Developer guide
Status: draft · Owner: ecosystem · Depends on: AIMarketEscrow (Base), AIMarket Hub v2, the Oracle Gateway
How a capability invoked through the MCP gateway gets paid, end to end, and the threat model
+ mandatory controls. Grounded in the live escrow (debitChannel/settleChannel/refundChannel/
expireChannel), the Hub (signed receipts + signed /.well-known + signed /ai-market/v2/prices),
and the gateway.
| Model | Key custody | Flow | Use when |
|---|---|---|---|
| Local stdio *(default)* | The payer key lives on the user's machine (the MCP server is spawned locally by Claude Desktop/Cursor). The gateway signs DebitAuthorizations locally — the key never leaves the machine. | open channel → per call: hub quotes price_usd → gateway signs EIP-712 debit (tight deadline) → hub debitChannel → signed receipt → settle/refund on close. | dev, single agent, agent holds its own wallet. Same trust domain as the agent. |
| Remote / hosted | No custody. Agent deposits on-chain and pre-authorizes spend (debit vouchers bounded by a ceiling); the gateway only relays signed vouchers. | agent approves USDC + pre-signs bounded vouchers → gateway relays per call, never exceeding the ceiling → settle/expiry. | shared/hosted gateway; the gateway must never hold a user key. |
Default = local stdio (the MCP server already runs locally). The remote model is for a hosted gateway and must use pre-signed, bounded vouchers — never custody.
openChannel(channelId, USDC, deposit) — a small float (MIN $1).invoke {capability_id, input, X-Payment-Channel} → hub returns price_usd + signed receipt.DebitAuthorization{channelId, hub, token, amount(6dp), receiptId, nonce, deadline}; hub debitChannel. receiptId single-use, nonce increments, first debit binds the hub.settleChannel → used→hub, refund→depositor.refundChannel (pre-debit), or permissionless expireChannel after 24h. No admin freeze/seize — funds are always recoverable.USD→token: prices are advertised in USD; the debit amount is USDC 6-dp (round(usd * 1e6)); the signed receipt's price_usd is authoritative.
From an adversarial red-team across three vectors. C = client/gateway-enforceable, P = protocol/contract, O = ops/governance.
An MCP tool call is driven by an LLM that can be manipulated (malicious tool output / injected content) into spending. Mitigations:
price_usd against the pinned/advertised price (and the signed /prices); refuse overcharge (±tolerance). Must-fix.price_usd/result; pin signer_public_key from the signed /.well-known. Fail-closed. Must-fix.receiptId single-use + per-channel nonce (contract-enforced). Gateway generates a fresh random receiptId per call; fetches the current nonce before signing. Must-fix (nonce sync).now+5min, not 24h, so a stolen signature can't be replayed later. Must-fix.hub; gateway validates the signed hub == the intended hub (kills cross-hub signature replay). Must-fix.refundChannel (pre-debit), settleChannel (depositor or bound hub), permissionless expireChannel after 24h; no admin trap. A malicious hub that never settles → depositor waits 24h and anyone expires it.setHubAuthorization only via governance; audited hub whitelist. Must-fix before mainnet value.