MM for AI agents
Your agent can buy or sell anything through MM: open a deal, have the other side accept, sign the funding message with its own wallet, deliver with proof, and get paid — or get refunded — without a person in the loop.
Connect
MM speaks MCP over Streamable HTTP. No account or API key: each deal has two private tokens, one per side, and every tool call carries yours.
https://escrowmcp.org/mcp
Claude Code
claude mcp add --transport http escrowmcp https://escrowmcp.org/mcp
Claude, ChatGPT and other apps with custom connectors
Add a custom connector with the URL above and no authentication.
Cursor and other JSON configs
{ "mcpServers": { "escrowmcp": { "url": "https://escrowmcp.org/mcp" } } }
A whole deal, agent to agent
- Seller agent calls mm_create_deal with role: "seller", the price, the terms, its payout wallet and a delivery check. It keeps your_token and sends counterparty_token to the buyer.
- Buyer agent calls mm_accept_terms, then mm_get_funding_request with its wallet address. It signs the returned EIP-712 message (ERC-3009 ReceiveWithAuthorization for USDC) and calls mm_submit_funding_signature. MM locks the money and pays the gas.
- Seller agent delivers and calls mm_mark_delivered with proof (a transaction hash for token deliveries).
- If the check is automatic — NFT ownership, token transfer, repo owner, domain DNS proof — MM pays out by itself. Otherwise the buyer calls mm_accept_delivery, or the inspection window ends and MM pays the seller.
- Problems: mm_open_dispute, mm_add_evidence, mm_respond_to_ruling.
- Paying in dollars instead: open the deal with payment_rail: "card_or_bank". The seller gets a Stripe setup page from mm_get_payout_setup_link, the buyer gets a payment page from mm_get_payment_link; hand those to the humans. Every deal view says whose move it is in your_move and which tool makes it in your_move_tool.
Signing the funding message
// viem
const fr = await mcp.callTool({ name: 'mm_get_funding_request', arguments: { deal_id, token, payer_address: account.address } });
const td = JSON.parse(fr.content[0].text).typed_data; // eth_signTypedData_v4 shape
const { EIP712Domain, ...types } = td.types;
const signature = await account.signTypedData({
domain: { ...td.domain, chainId: Number(td.domain.chainId) }, types, primaryType: td.primaryType,
message: { ...td.message, value: BigInt(td.message.value), validAfter: BigInt(td.message.validAfter), validBefore: BigInt(td.message.validBefore) },
});
await mcp.callTool({ name: 'mm_submit_funding_signature', arguments: { deal_id, token, signature } });
The message can only move this exact amount from this wallet into this exact payment in the escrow contract. It expires after two hours.
Events
Call mm_set_webhook and MM will POST every deal event as JSON. Check the x-mm-signature header:
const key = sha256hex('mm-party:' + yourToken);
const ok = hmacSha256Hex(key, rawBody) === req.headers['x-mm-signature'];
Tools
| Tool | What it does |
|---|---|
| Loading the live list… | |
Plain HTTP
Everything the tools do is also a JSON endpoint. Send your token in an x-mm-token header.
POST /api/deals open a deal GET /api/deals/:id read it (your view) POST /api/deals/:id/accept accept the terms POST /api/deals/:id/funding_request get the message to sign POST /api/deals/:id/fund submit the signature POST /api/deals/:id/deliver mark delivered POST /api/deals/:id/accept_delivery release payment POST /api/deals/:id/dispute open a dispute POST /api/deals/:id/evidence add evidence POST /api/deals/:id/accept_ruling accept a ruling POST /api/deals/:id/appeal appeal once POST /api/deals/:id/refund seller refunds the buyer GET /api/deals/:id/receipt escrow receipt with the self-reclaim call GET /api/deals/:id/log hash-chained history