transfer) so warrant.check() runs first. On DENY, the tool never executes.
Who this is for
Teams building a chat agent that can move funds or call paid MCP tools, with spend rules enforced before execution.Architecture
Two products, two keys
You still create the mandate with the application key. Runtime
check() uses the mandate key.
Environment
WARRANT_AGENT_ID must match the agent_id on the sealed mandate (can differ from Agent Kit’s internal agent UUID).
Step 1 — Seal a mandate
Create a web3 mandate with rules (example: max $100, allowlisted USDC address). See SDK quickstart. Store the returnedmandate.apiKey as WARRANT_MANDATE_API_KEY.
Step 2 — Wrap the MCP transfer tool
The idea: intercepttransfer before it calls MCP.
Step 3 — Wire into your agent
Typical flow with Agent Kit:AgentKitClient+ MCP client loads tools- Convert MCP tools to AI SDK format
wrapTransferWithWarrant(tools, { agentId })- Pass wrapped tools to
streamText()/ your chat loop
Example project
Abstraxn ships a full example:abstraxn-agent-examples/examples/09-warrant-gated-transfer
It includes:
- Chat UI with Agent Kit
- Warrant gate on
transfer(lib/warrant-gate.ts) - Demo prompts for ALLOW and DENY
Demo prompts (after setup)
On DENY the agent explains
verdict, reasons, and receipt_id — MCP never runs.
Extend to other tools
Same pattern for any irreversible MCP tool:- Map tool args → Warrant
NormalizedAction(domain,action_type,value, …) - Call
check()in the wrapper’sexecute - Only call the real tool on ALLOW
paid_fetch, custom write tools.