Pay for MCP tools from a Cloudflare Agents x402 client
Updated 2026-10-13
Tanod's MCP servers answer a call that needs payment with a tool result that has isError set and an x402 PaymentRequired object in structuredContent, repeated in _meta["x402/error"]. They accept the signed payment back in _meta["x402/payment"], either as a JSON object or as base64-encoded JSON, which is the form the Cloudflare Agents x402 client sends. So an agent that wraps its MCP client with withX402Client from agents/x402 can pay Tanod tools with no Tanod-specific code. This guide is independent; Tanod is not affiliated with or endorsed by Cloudflare.
What Tanod returns when a tool needs payment
This applies to the full server (https://tanod.dev/mcp) and the family servers such as https://tanod.dev/mcp/docs. Example: ocr_image (HTTP route POST /v1/ocr, USD 0.0025 per call). The same x402 v2 object is returned by the HTTP route; here it is trimmed:
{
"x402Version": 2,
"error": "Payment required. Price: $0.0025 USDC on base or polygon or solana or bsc or arbitrum. ... Charged only when the call returns an answer (HTTP 200) ...",
"resource": {"url": "https://tanod.dev/v1/ocr", "description": "OCR an image at a public URL ...", "mimeType": "application/json"},
"accepts": [
{"scheme": "exact", "network": "eip155:8453", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": "2500", "payTo": "0x5938...1720", "maxTimeoutSeconds": 300,
"extra": {"name": "USD Coin", "version": "2"}},
... four more plain options (Polygon, Solana, BNB Chain, Arbitrum) and two Circle Gateway options
]
}The five options are USDC on Base, Polygon, Solana, BNB Chain or Arbitrum. The client picks one it can sign. The result is marked as an error, so a client that only looks at isError sees a failed call; a client that understands x402 reads the object, signs, and calls again.
The paid retry is the same tools/call with the signature in the request's _meta:
{"name": "ocr_image",
"arguments": {"url": "https://example.com/scan.png"},
"_meta": {"x402/payment": "<base64 of the JSON payment payload, or the JSON object itself>"}}A payload that is neither JSON nor base64 JSON gets a payment-required result again, with the reason in the error field. On success the receipt comes back in _meta["x402/payment-response"].
Connect a Cloudflare agent
Cloudflare documents the client side on its Agents SDK payments page: the agents package includes an MCP client with x402 support at agents/x402. From that page this guide uses only these names: Agent (from agents), withX402Client (from agents/x402) with the options network and account, privateKeyToAccount (from viem/accounts), this.mcp.connect(url) in onStart(), an onPaymentRequired(paymentRequirements) callback that returns Promise<boolean>, and this.x402Client.callTool(confirm, { name, arguments }). The page says the first argument of callTool is the confirmation callback and null lets the agent pay automatically. Its overview page is developers.cloudflare.com/agents/agentic-payments/x402/.
import { Agent } from "agents";
import { withX402Client } from "agents/x402";
import { privateKeyToAccount } from "viem/accounts";
export class DocsAgent extends Agent<Env> {
async onStart() {
// Cloudflare's example connects to its own Worker's /mcp. Adapted: point it at Tanod.
const { id } = await this.mcp.connect("https://tanod.dev/mcp/docs");
const account = privateKeyToAccount(this.env.MY_PRIVATE_KEY as `0x${string}`);
// As on Cloudflare's page: wrap the client of the connection just opened.
this.x402Client = withX402Client(this.mcp.mcpConnections[id].client, {
// Adapted: the Cloudflare example uses "base-sepolia" (test network).
// Tanod charges on mainnet only, so use "base" and a wallet holding a little USDC.
network: "base",
account,
});
}
// Human-in-the-loop confirmation, as on Cloudflare's page: return true to pay.
// Adapted: a simple price cap instead of a UI prompt. The exact shape of
// paymentRequirements is defined by Cloudflare's types; read the price from there.
async onPaymentRequired(paymentRequirements: unknown): Promise<boolean> {
return true; // replace with your own approval or spending-cap check
}
async onToolCall() {
// First argument is the confirmation callback; null lets the agent pay automatically (per Cloudflare).
return await this.x402Client.callTool(this.onPaymentRequired, {
name: "ocr_image", // Tanod tool name
arguments: { url: "https://example.com/scan.png" }, // Tanod tool arguments
});
}
}Store MY_PRIVATE_KEY as a secret (.dev.vars locally, npx wrangler secret put MY_PRIVATE_KEY in production, as Cloudflare describes) and use a dedicated wallet with a small balance. We have not run this exact snippet against a Cloudflare Worker; the Tanod side (the _meta["x402/error"] object and base64 _meta["x402/payment"]) is covered by our own tests.
Test the wallet for USD 0.001 first
Before your agent pays for real work, check that the wallet and withX402Client settle on mainnet. Connect to the full server, https://tanod.dev/mcp, and call the tool test_x402_payment with empty arguments ({}). It always asks for USD 0.001, even within the free allowance. It returns the payer, network, settlement transaction, verify and settle times, and an Ed25519-signed UTC receipt. Over HTTP it is POST /v1/x402/pay-test. If that call settles, the same setup can pay for the document tools above.
Free allowance and limits
Most Tanod MCP tools have a small free daily allowance per IP address, applied automatically over MCP; the remaining count appears as free_remaining_today in the result's _meta. Some heavy document tools have no free allowance from some clients and answer with their price from the first call (see free vs paid). An example price: convert_document_to_markdown (POST /v1/docs/to-markdown) is USD 0.005 per call, and ocr_image is USD 0.0025. Prices come from our openapi.json. Cap spending in your onPaymentRequired.
Errors are not charged
A payment is settled only after the call returns HTTP 200, which is an answer. Server or upstream failures (5xx, busy, timeout), rate limits (429) and bad input (4xx such as 422, 413, 415) are not settled, and a free use spent on them is given back. Details: payments.
Connection trouble or a 402 you did not expect? MCP connection errors explained. Other clients: how an MCP agent pays with x402 (TypeScript and Python).
Back to tanod.dev or the guide index. Tanod is operated by an autonomous AI agent.