Framework integrations
Gating a sensitive agent action with Authorize should be a few lines inside the framework you already use — not hand-written authorize/verify plumbing around every tool. @provenance/agent-adapters provides a tiny framework-agnostic core, gate(), plus first-class drop-ins for MCP, the Vercel AI SDK, and LangChain.
The optional framework packages are peer dependencies — the adapters never import them, so the package installs and builds whether or not you have them. Install only the one you use.
MCP (the hero)
Wrap the tools on an MCP server so a call from any MCP client (Claude, etc.) round-trips through Authorize before executing. Call withAuthorize before registering the named tools:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { withAuthorize } from "@provenance/agent-adapters/mcp";
import { Provenance } from "@provenance/sdk";
const prov = new Provenance({ apiKey: process.env.PRV_KEY! });
const server = new McpServer({ name: "wallet", version: "1.0.0" });
withAuthorize(server, {
tools: {
wire_transfer: {
client: prov, deviceId, agentSessionId,
describe: (a) => `Wire $${a.amount} to ${a.to}`,
payload: (a) => a,
context: (a) => ({ action_type: "wire", transaction_amount: a.amount }),
onPending: "fail", // MCP never blocks — pending returns a structured result
},
},
});
server.registerTool("wire_transfer", schema, async ({ amount, to }) => wire(amount, to));
server.registerTool("read_balance", {}, async () => readBalance()); // ungated
When the account's policy requires human step-up approval, the gated tool returns a structured "awaiting human approval (id …)" result instead of hanging past the client's timeout — and succeeds on a retry once approved. A denial returns an isError result naming the reason. A runnable server is in packages/agent-adapters/examples/mcp-demo/.
Vercel AI SDK
import { tool } from "ai";
import { gatedTool } from "@provenance/agent-adapters/ai-sdk";
const wireTool = tool(gatedTool({
description: "Wire funds",
parameters: z.object({ amount: z.number(), to: z.string() }),
execute: async (a) => wire(a.amount, a.to),
}, { client: prov, deviceId, agentSessionId, describe: (a) => `Wire $${a.amount} to ${a.to}`, payload: (a) => a }));
LangChain
import { DynamicStructuredTool } from "@langchain/core/tools";
import { gatedTool } from "@provenance/agent-adapters/langchain";
const wireTool = new DynamicStructuredTool(gatedTool({
name: "wire_transfer", description: "Wire funds", schema,
func: async (a) => wire(a.amount, a.to),
}, { client: prov, deviceId, agentSessionId, describe: (a) => `Wire $${a.amount} to ${a.to}`, payload: (a) => a }));
Anything else — the gate() recipe
The adapters are thin wrappers over the core. For any framework (OpenAI Agents SDK, a bespoke tool loop), wrap the function directly:
import { gate, IntentDeniedError } from "@provenance/agent-adapters";
const gatedWire = gate(executeWire, {
client: prov, deviceId, agentSessionId,
describe: (a) => `Wire $${a.amount} to ${a.to}`,
payload: (a) => a,
context: (a) => ({ action_type: "wire", transaction_amount: a.amount }),
onPending: "poll", // block on the approval loop; or "fail" to throw immediately
});
try {
await gatedWire({ amount: 500, to: "acct-1" });
} catch (e) {
if (e instanceof IntentDeniedError) {
// e.reason, e.approvalId, e.pending (a still-open step-up you can retry)
}
}
The invariant: gate derives the description + payload from the call's args once, authorizes and verifies that exact payload, then runs the wrapped function with the same args. There's no seam between verify and execute for anything to mutate the payload — and the API's payload_hash binding enforces it cryptographically too.
Offline verification for relying parties
A downstream service that receives an intent token can verify it offline against the published JWKS, with no call back to Provenance, to prove authenticity and expiry. Remember that offline verification sees authenticity and expiry only, not revocation; for high-value actions, check /v1/intent/status/:jti or run the full verify (which the adapters do).