From a4824bf3873dcd50d253965717c48315ac18e550 Mon Sep 17 00:00:00 2001 From: Cameron Whiteside Date: Tue, 17 Feb 2026 13:20:48 -0600 Subject: [PATCH 1/9] [Agents] Expand x402 documentation into dedicated section - Split single x402.mdx page into dedicated /agents/x402/ section - Add pages for charging (HTTP content, MCP tools) and paying (Agents SDK, coding tools) - Remove marketing language and question-based framing - Tighten content, remove redundant examples and ASCII diagrams - Link to GitHub templates for detailed configuration --- src/content/docs/agents/x402.mdx | 173 ----------------- .../agents/x402/charge-for-http-content.mdx | 76 ++++++++ .../docs/agents/x402/charge-for-mcp-tools.mdx | 98 ++++++++++ src/content/docs/agents/x402/index.mdx | 48 +++++ .../docs/agents/x402/pay-from-agents-sdk.mdx | 103 ++++++++++ .../agents/x402/pay-with-tool-plugins.mdx | 180 ++++++++++++++++++ .../reference/worker-templates.mdx | 2 +- 7 files changed, 506 insertions(+), 174 deletions(-) delete mode 100644 src/content/docs/agents/x402.mdx create mode 100644 src/content/docs/agents/x402/charge-for-http-content.mdx create mode 100644 src/content/docs/agents/x402/charge-for-mcp-tools.mdx create mode 100644 src/content/docs/agents/x402/index.mdx create mode 100644 src/content/docs/agents/x402/pay-from-agents-sdk.mdx create mode 100644 src/content/docs/agents/x402/pay-with-tool-plugins.mdx diff --git a/src/content/docs/agents/x402.mdx b/src/content/docs/agents/x402.mdx deleted file mode 100644 index 5591050f3b67ef7..000000000000000 --- a/src/content/docs/agents/x402.mdx +++ /dev/null @@ -1,173 +0,0 @@ ---- -title: x402 -pcx_content_type: navigation -sidebar: - order: 8 ---- - -## What is x402? - -[x402](https://www.x402.org/) is an open payment standard that enables services to charge for access to their APIs and content directly over HTTP. It is built around the HTTP 402 Payment Required status code and allows clients to programmatically pay for resources without accounts, sessions, or credential management. - -## x402 Workers and Agents - -You can create paywalled endpoints in your Workers and query any x402 server from your agent by wrapping the `fetch` with x402 and your account. - -```ts -import { Hono } from "hono"; -import { Agent, getAgentByName } from "agents"; -import { wrapFetchWithPayment } from "x402-fetch"; -import { paymentMiddleware } from "x402-hono"; - -// This allows us to derive an account from just the private key -import { privateKeyToAccount } from "viem/accounts"; - -// The code below creates an Agent that can fetch the protected route and automatically pay. -// The agent's account must not be empty! You can get test credits -// for base-sepolia here: https://faucet.circle.com/ -export class PayAgent extends Agent { - fetchWithPay!: ReturnType; - - onStart() { - // We derive the account from which the agent will pay - const privateKey = process.env.CLIENT_TEST_PK as `0x${string}`; - const account = privateKeyToAccount(privateKey); - console.log("Agent will pay from this address:", account.address); - this.fetchWithPay = wrapFetchWithPayment(fetch, account); - } - - async onRequest(req: Request) { - const url = new URL(req.url); - console.log("Trying to fetch paid API"); - - // Use the x402 compatible fetch (fetchWithPay) to access the paid endpoint - // Note: this could be any paid endpoint, on any server - const paidUrl = new URL("/protected-route", url.origin).toString(); - return this.fetchWithPay(paidUrl, {}); - } -} - -const app = new Hono<{ Bindings: Env }>(); - -// Configure the middleware. -// Only gate the `protected-route` endpoint, everything else we keep free. -app.use( - paymentMiddleware( - process.env.SERVER_ADDRESS as `0x${string}`, // our server's public address - { - "/protected-route": { - price: "$0.10", - network: "base-sepolia", - config: { - description: "Access to premium content", - }, - }, - }, - { url: "https://x402.org/facilitator" }, // Payment facilitator URL - // To learn more about facilitators https://x402.gitbook.io/x402/core-concepts/facilitator - ), -); - -// Our paid endpoint will return some premium content. -app.get("/protected-route", (c) => { - return c.json({ - message: "This content is behind a paywall. Thanks for paying!", - }); -}); - -// The agent will fetch our own protected route and automatically pay. -app.get("/agent", async (c) => { - const agent = await getAgentByName(c.env.PAY_AGENT, "1234"); - return agent.fetch(c.req.raw); -}); - -export default app; -``` - -Check out the [complete example](https://github.com/cloudflare/agents/tree/main/examples/x402). - -## MCP servers with paid tools - -x402 supercharges your MCP servers so they can include paid tools. - -```ts -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { McpAgent } from "agents/mcp"; -import { withX402, type X402Config } from "agents/x402"; -import { z } from "zod"; - -const X402_CONFIG: X402Config = { - network: "base", - recipient: env.MCP_ADDRESS, - facilitator: { url: "https://x402.org/facilitator" }, // Payment facilitator URL - // To learn more about facilitators https://x402.gitbook.io/x402/core-concepts/facilitator -}; - -export class PaidMCP extends McpAgent { - server = withX402( - new McpServer({ name: "PaidMCP", version: "1.0.0" }), - X402_CONFIG, - ); // That's it! - - async init() { - // Paid tool - this.server.paidTool( - "square", - "Squares a number", - 0.01, // USD - { number: z.number() }, - {}, - async ({ number }) => { - return { content: [{ type: "text", text: String(number ** 2) }] }; - }, - ); - - // Free tool - this.server.tool( - "echo", - "Echo a message", - { message: z.string() }, - async ({ message }) => { - return { content: [{ type: "text", text: message }] }; - }, - ); - } -} -``` - -We also include an MCP client that you can use from anywhere (not just your Agents!) to pay for these tools. - -```ts -import { Agent } from "agents"; -import { withX402Client } from "agents/x402"; -import { privateKeyToAccount } from "viem/accounts"; - -export class MyAgent extends Agent { - // Your Agent definitions... - - onStart() { - const { id } = await this.mcp.connect(`${env.WORKER_URL}/mcp`); - const account = privateKeyToAccount(this.env.MY_PRIVATE_KEY); - - this.x402Client = withX402Client(this.mcp.mcpConnections[id].client, { - network: "base-sepolia", - account, - }); - } - - onPaymentRequired(paymentRequirements): Promise { - // Your human-in-the-loop confirmation flow... - } - - async onToolCall(toolName: string, toolArgs: unknown) { - // The first parameter becomes the confirmation callback. - // We can set it to `null` if we want the agent to pay automatically. - return await this.x402Client.callTool(this.onPaymentRequired, { - name: toolName, - arguments: toolArgs, - }); - } -} -``` - -Check out the [complete example](https://github.com/cloudflare/agents/tree/main/examples/x402-mcp). diff --git a/src/content/docs/agents/x402/charge-for-http-content.mdx b/src/content/docs/agents/x402/charge-for-http-content.mdx new file mode 100644 index 000000000000000..2ecb07a81e59bdf --- /dev/null +++ b/src/content/docs/agents/x402/charge-for-http-content.mdx @@ -0,0 +1,76 @@ +--- +title: Charge for HTTP content +pcx_content_type: how-to +sidebar: + order: 1 +description: Gate HTTP endpoints with x402 payments using a Cloudflare Worker proxy. +--- + +The x402-proxy template is a Cloudflare Worker that sits in front of any HTTP backend. When a request hits a protected route, the proxy returns a 402 response with payment instructions. After the client pays, the proxy verifies the payment and forwards the request to your origin. + +[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/templates/tree/main/x402-proxy-template) + +## Prerequisites + +- A [Cloudflare account](https://dash.cloudflare.com/sign-up) +- An HTTP backend to gate +- A wallet address to receive payments + +## Configuration + +Define protected routes in `wrangler.jsonc`: + +```json +{ + "vars": { + "PAY_TO": "0xYourWalletAddress", + "NETWORK": "base-sepolia", + "PROTECTED_PATTERNS": [ + { + "pattern": "/api/premium/*", + "price": "$0.10", + "description": "Premium API access" + } + ] + } +} +``` + +:::note +`base-sepolia` is a test network. Change to `base` for production. +::: + +## Selective gating with Bot Management + +With [Bot Management](/bots/), the proxy can charge crawlers while keeping the site free for humans: + +```json +{ + "pattern": "/content/*", + "price": "$0.10", + "description": "Content access", + "bot_score_threshold": 30, + "except_detection_ids": [117479730] +} +``` + +Requests with a bot score below `bot_score_threshold` see the paywall. Use `except_detection_ids` to allowlist specific crawlers by [detection ID](/ai-crawl-control/reference/bots/). + +## Deploy + +Clone the template, edit `wrangler.jsonc`, and deploy: + +```sh +git clone https://github.com/cloudflare/templates +cd templates/x402-proxy-template +npm install +npx wrangler deploy +``` + +For full configuration options and Bot Management examples, see the [template README](https://github.com/cloudflare/templates/tree/main/x402-proxy-template). + +## Related + +- [Pay Per Crawl](/ai-crawl-control/features/pay-per-crawl/) — Native Cloudflare monetization without custom code +- [Charge for MCP tools](/agents/x402/charge-for-mcp-tools/) — Charge per tool call instead of per request +- [x402.org](https://x402.org) — Protocol specification diff --git a/src/content/docs/agents/x402/charge-for-mcp-tools.mdx b/src/content/docs/agents/x402/charge-for-mcp-tools.mdx new file mode 100644 index 000000000000000..1dd9845aa39f818 --- /dev/null +++ b/src/content/docs/agents/x402/charge-for-mcp-tools.mdx @@ -0,0 +1,98 @@ +--- +title: Charge for MCP tools +pcx_content_type: how-to +sidebar: + order: 2 +description: Charge per tool call in an MCP server using paidTool. +--- + +The Agents SDK provides `paidTool`, a drop-in replacement for `tool` that adds x402 payment requirements. Clients pay per tool call, and you can mix free and paid tools in the same server. + +## Prerequisites + +- A [Cloudflare account](https://dash.cloudflare.com/sign-up) +- An MCP server project +- A wallet address to receive payments + +## Setup + +Wrap your `McpServer` with `withX402` and use `paidTool` for tools you want to charge for: + +```ts +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { McpAgent } from "agents/mcp"; +import { withX402, type X402Config } from "agents/x402"; +import { z } from "zod"; + +const X402_CONFIG: X402Config = { + network: "base", + recipient: env.MCP_ADDRESS, + facilitator: { url: "https://x402.org/facilitator" }, +}; + +export class PaidMCP extends McpAgent { + server = withX402( + new McpServer({ name: "PaidMCP", version: "1.0.0" }), + X402_CONFIG, + ); + + async init() { + // Paid tool — $0.01 per call + this.server.paidTool( + "square", + "Squares a number", + 0.01, // USD + { number: z.number() }, + {}, + async ({ number }) => { + return { content: [{ type: "text", text: String(number ** 2) }] }; + }, + ); + + // Free tool + this.server.tool( + "echo", + "Echo a message", + { message: z.string() }, + async ({ message }) => { + return { content: [{ type: "text", text: message }] }; + }, + ); + } +} +``` + +## Configuration + +| Field | Description | +| ------------- | ------------------------------------------------------------ | +| `network` | `base` for production, `base-sepolia` for testing | +| `recipient` | Wallet address to receive payments | +| `facilitator` | Payment facilitator URL (use `https://x402.org/facilitator`) | + +## paidTool signature + +```ts +this.server.paidTool( + name, // Tool name + description, // Tool description + price, // Price in USD (e.g., 0.01) + inputSchema, // Zod schema for inputs + annotations, // MCP annotations + handler, // Async function that executes the tool +); +``` + +When a client calls a paid tool without payment, the server returns 402 with payment requirements. The client pays via x402, retries with payment proof, and receives the result. + +## Testing + +Use `base-sepolia` and get test USDC from the [Circle faucet](https://faucet.circle.com/). + +For a complete working example, see [x402-mcp on GitHub](https://github.com/cloudflare/agents/tree/main/examples/x402-mcp). + +## Related + +- [Pay from Agents SDK](/agents/x402/pay-from-agents-sdk/) — Build clients that pay for tools +- [Charge for HTTP content](/agents/x402/charge-for-http-content/) — Gate HTTP endpoints +- [MCP server guide](/agents/guides/remote-mcp-server/) — Build your first MCP server diff --git a/src/content/docs/agents/x402/index.mdx b/src/content/docs/agents/x402/index.mdx new file mode 100644 index 000000000000000..e6acf40984a5295 --- /dev/null +++ b/src/content/docs/agents/x402/index.mdx @@ -0,0 +1,48 @@ +--- +title: x402 +pcx_content_type: navigation +sidebar: + order: 5 + group: + hideIndex: false +--- + +import { LinkCard, CardGrid } from "~/components"; + +[x402](https://www.x402.org/) is an open payment standard built around HTTP 402 (Payment Required). Services return a 402 response with payment instructions, and clients pay programmatically without accounts, sessions, or API keys. + +## Charge for resources + + + + + + +## Pay for resources + + + + + + +## Related + +- [x402.org](https://x402.org) — Protocol specification +- [Pay Per Crawl](/ai-crawl-control/features/pay-per-crawl/) — Cloudflare-native monetization +- [x402 examples](https://github.com/cloudflare/agents/tree/main/examples) — Complete working code diff --git a/src/content/docs/agents/x402/pay-from-agents-sdk.mdx b/src/content/docs/agents/x402/pay-from-agents-sdk.mdx new file mode 100644 index 000000000000000..c0323da2784e75b --- /dev/null +++ b/src/content/docs/agents/x402/pay-from-agents-sdk.mdx @@ -0,0 +1,103 @@ +--- +title: Pay from Agents SDK +pcx_content_type: how-to +sidebar: + order: 3 +description: Use withX402Client to pay for resources from a Cloudflare Agent. +--- + +The Agents SDK includes `withX402Client`, which wraps an MCP client to handle x402 payments automatically or with human approval. + +## Prerequisites + +- A [Cloudflare account](https://dash.cloudflare.com/sign-up) +- An Agent project +- A wallet with USDC (test on `base-sepolia`, production on `base`) + +## Setup + +Wrap your MCP client connection with `withX402Client`: + +```ts +import { Agent } from "agents"; +import { withX402Client } from "agents/x402"; +import { privateKeyToAccount } from "viem/accounts"; + +export class MyAgent extends Agent { + x402Client!: ReturnType; + + async onStart() { + const { id } = await this.mcp.connect(`${env.WORKER_URL}/mcp`); + const account = privateKeyToAccount(this.env.MY_PRIVATE_KEY); + + this.x402Client = withX402Client(this.mcp.mcpConnections[id].client, { + network: "base-sepolia", + account, + }); + } + + async callTool(toolName: string, toolArgs: unknown) { + return await this.x402Client.callTool(null, { + name: toolName, + arguments: toolArgs, + }); + } +} +``` + +## Payment confirmation + +### Auto-pay + +Pass `null` as the first argument to `callTool`. The agent pays without prompting: + +```ts +const result = await this.x402Client.callTool(null, { + name: "expensive-tool", + arguments: { data: "..." }, +}); +``` + +### Human-in-the-loop + +Pass a callback that receives payment requirements and returns whether to proceed: + +```ts +async callToolWithApproval(toolName: string, toolArgs: unknown) { + return await this.x402Client.callTool( + this.onPaymentRequired.bind(this), + { name: toolName, arguments: toolArgs } + ); +} + +async onPaymentRequired(requirements: PaymentRequirements): Promise { + console.log(`Tool costs ${requirements.amount} ${requirements.asset}`); + return await this.askUserForApproval(requirements); +} +``` + +The `PaymentRequirements` object contains `resource`, `amount`, `asset`, `network`, and `recipient`. + +## Environment setup + +Store your private key securely: + +```sh +# Local development (.dev.vars) +MY_PRIVATE_KEY="0x..." + +# Production +npx wrangler secret put MY_PRIVATE_KEY +``` + +## Testing + +Use `base-sepolia` and get test USDC from the [Circle faucet](https://faucet.circle.com/). + +For a complete working example, see [x402-mcp on GitHub](https://github.com/cloudflare/agents/tree/main/examples/x402-mcp). + +## Related + +- [Charge for MCP tools](/agents/x402/charge-for-mcp-tools/) — Build servers that charge for tools +- [Pay from coding tools](/agents/x402/pay-with-tool-plugins/) — Add payments to OpenCode or Claude Code +- [Human-in-the-loop guide](/agents/guides/human-in-the-loop/) — Implement approval workflows diff --git a/src/content/docs/agents/x402/pay-with-tool-plugins.mdx b/src/content/docs/agents/x402/pay-with-tool-plugins.mdx new file mode 100644 index 000000000000000..e40339cfa78c747 --- /dev/null +++ b/src/content/docs/agents/x402/pay-with-tool-plugins.mdx @@ -0,0 +1,180 @@ +--- +title: Pay from coding tools +pcx_content_type: how-to +sidebar: + order: 4 +description: Add x402 payment handling to OpenCode and Claude Code. +--- + +Add x402 payment handling to AI coding tools so they can pay for paywalled resources automatically. + +## Prerequisites + +- OpenCode or Claude Code installed +- A wallet with USDC (test on `base-sepolia`, production on `base`) + +## Setup + +Both implementations use the same dependencies and environment variable. + +Install the x402 packages: + +```sh +npm install @x402/fetch @x402/evm viem +``` + +Set your wallet private key: + +```sh +export X402_PRIVATE_KEY="0xYourPrivateKey" +``` + +Get test USDC from the [Circle faucet](https://faucet.circle.com/). + +## OpenCode plugin + +OpenCode plugins expose tools to the agent. Create an `x402-fetch` tool that handles 402 responses. + +Create `.opencode/plugins/x402-payment.ts`: + +```ts +import type { Plugin } from "@opencode-ai/plugin"; +import { tool } from "@opencode-ai/plugin"; +import { x402Client, wrapFetchWithPayment } from "@x402/fetch"; +import { registerExactEvmScheme } from "@x402/evm/exact/client"; +import { privateKeyToAccount } from "viem/accounts"; + +export const X402PaymentPlugin: Plugin = async () => ({ + tool: { + "x402-fetch": tool({ + description: + "Fetch a URL with x402 payment. Use when webfetch returns 402.", + args: { + url: tool.schema.string().describe("The URL to fetch"), + timeout: tool.schema.number().optional().describe("Timeout in seconds"), + }, + async execute(args) { + const privateKey = process.env.X402_PRIVATE_KEY; + if (!privateKey) { + throw new Error("X402_PRIVATE_KEY environment variable is not set."); + } + + const account = privateKeyToAccount(privateKey as `0x${string}`); + const client = new x402Client(); + registerExactEvmScheme(client, { signer: account }); + const paidFetch = wrapFetchWithPayment(fetch, client); + + const response = await paidFetch(args.url, { + method: "GET", + signal: args.timeout + ? AbortSignal.timeout(args.timeout * 1000) + : undefined, + }); + + if (!response.ok) { + throw new Error(`${response.status} ${response.statusText}`); + } + + return await response.text(); + }, + }), + }, +}); +``` + +When the built-in `webfetch` returns a 402, the agent calls `x402-fetch` to retry with payment. + +## Claude Code hook + +Claude Code hooks intercept tool results. Hook into `WebFetch` responses to handle 402s transparently. + +Create `.claude/settings.json`: + +```json +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "WebFetch", + "hooks": [ + { + "type": "command", + "command": "node .claude/dist/handle-x402.mjs", + "timeout": 30 + } + ] + } + ] + } +} +``` + +Create `.claude/scripts/handle-x402.mjs`: + +```js +import { x402Client, wrapFetchWithPayment } from "@x402/fetch"; +import { registerExactEvmScheme } from "@x402/evm/exact/client"; +import { privateKeyToAccount } from "viem/accounts"; + +const input = JSON.parse(await readStdin()); + +const haystack = JSON.stringify(input.tool_response ?? input.error ?? ""); +if (!haystack.includes("402")) process.exit(0); + +const url = input.tool_input?.url; +if (!url) process.exit(0); + +const privateKey = process.env.X402_PRIVATE_KEY; +if (!privateKey) { + console.error("X402_PRIVATE_KEY not set."); + process.exit(2); +} + +try { + const account = privateKeyToAccount(privateKey); + const client = new x402Client(); + registerExactEvmScheme(client, { signer: account }); + const paidFetch = wrapFetchWithPayment(fetch, client); + + const res = await paidFetch(url, { method: "GET" }); + const text = await res.text(); + + if (!res.ok) { + console.error(`Paid fetch failed: ${res.status}`); + process.exit(2); + } + + console.log( + JSON.stringify({ + hookSpecificOutput: { + hookEventName: "PostToolUse", + additionalContext: `Paid for "${url}" via x402:\n${text}`, + }, + }), + ); +} catch (err) { + console.error(`x402 payment failed: ${err.message}`); + process.exit(2); +} + +function readStdin() { + return new Promise((resolve) => { + let data = ""; + process.stdin.on("data", (chunk) => (data += chunk)); + process.stdin.on("end", () => resolve(data)); + }); +} +``` + +Build the hook: + +```sh +cd .claude && npm run build +``` + +## Related + +- [Pay from Agents SDK](/agents/x402/pay-from-agents-sdk/) — Use the Agents SDK for more control +- [Charge for HTTP content](/agents/x402/charge-for-http-content/) — Build the server side +- [Human-in-the-loop guide](/agents/guides/human-in-the-loop/) — Implement approval workflows +- [x402.org](https://x402.org) — Protocol specification diff --git a/src/content/docs/ai-crawl-control/reference/worker-templates.mdx b/src/content/docs/ai-crawl-control/reference/worker-templates.mdx index 1fb290df965241b..ee67f8aa13d32df 100644 --- a/src/content/docs/ai-crawl-control/reference/worker-templates.mdx +++ b/src/content/docs/ai-crawl-control/reference/worker-templates.mdx @@ -22,4 +22,4 @@ For setup instructions and Bot Management integration examples, see the [templat - [Cloudflare Workers](/workers/) — Build and deploy serverless applications - [Workers templates](https://github.com/cloudflare/templates) — More templates on GitHub - [Pay Per Crawl](/ai-crawl-control/features/pay-per-crawl/what-is-pay-per-crawl/) — Native Cloudflare integration for monetizing crawler access -- [x402 with Agents](/agents/x402/) — Use x402 in Cloudflare Agents +- [x402 payments](/agents/x402/) — Gate resources, charge for MCP tools, add payments to coding agents From ffc510fb3140a5a3ee3a6e58f0c1a17075de7728 Mon Sep 17 00:00:00 2001 From: Cameron Whiteside Date: Tue, 17 Feb 2026 14:35:24 -0600 Subject: [PATCH 2/9] [Agents] Add custom Worker endpoints section to x402 HTTP content page --- .../agents/x402/charge-for-http-content.mdx | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/content/docs/agents/x402/charge-for-http-content.mdx b/src/content/docs/agents/x402/charge-for-http-content.mdx index 2ecb07a81e59bdf..a89ffc5b2169996 100644 --- a/src/content/docs/agents/x402/charge-for-http-content.mdx +++ b/src/content/docs/agents/x402/charge-for-http-content.mdx @@ -69,6 +69,37 @@ npx wrangler deploy For full configuration options and Bot Management examples, see the [template README](https://github.com/cloudflare/templates/tree/main/x402-proxy-template). +## Custom Worker endpoints + +For more control, add x402 middleware directly to your Worker using Hono: + +```ts +import { Hono } from "hono"; +import { paymentMiddleware } from "x402-hono"; + +const app = new Hono<{ Bindings: Env }>(); + +app.use( + paymentMiddleware( + process.env.SERVER_ADDRESS as `0x${string}`, + { + "/premium": { + price: "$0.10", + network: "base-sepolia", + config: { description: "Premium content" }, + }, + }, + { url: "https://x402.org/facilitator" }, + ), +); + +app.get("/premium", (c) => c.json({ message: "Thanks for paying!" })); + +export default app; +``` + +See the [x402 Workers example](https://github.com/cloudflare/agents/tree/main/examples/x402) for a complete implementation. + ## Related - [Pay Per Crawl](/ai-crawl-control/features/pay-per-crawl/) — Native Cloudflare monetization without custom code From 3c258d1a968b3681cb2a16c78e96ce5642141f9f Mon Sep 17 00:00:00 2001 From: Cameron Whiteside Date: Tue, 17 Feb 2026 14:36:26 -0600 Subject: [PATCH 3/9] [Agents] Remove redundant prerequisites from MCP tools page --- src/content/docs/agents/x402/charge-for-mcp-tools.mdx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/content/docs/agents/x402/charge-for-mcp-tools.mdx b/src/content/docs/agents/x402/charge-for-mcp-tools.mdx index 1dd9845aa39f818..db41d8c8adf8ee4 100644 --- a/src/content/docs/agents/x402/charge-for-mcp-tools.mdx +++ b/src/content/docs/agents/x402/charge-for-mcp-tools.mdx @@ -8,12 +8,6 @@ description: Charge per tool call in an MCP server using paidTool. The Agents SDK provides `paidTool`, a drop-in replacement for `tool` that adds x402 payment requirements. Clients pay per tool call, and you can mix free and paid tools in the same server. -## Prerequisites - -- A [Cloudflare account](https://dash.cloudflare.com/sign-up) -- An MCP server project -- A wallet address to receive payments - ## Setup Wrap your `McpServer` with `withX402` and use `paidTool` for tools you want to charge for: From 1ca40ecabf5ed46107184b75a2b2b6cb1d8d0acc Mon Sep 17 00:00:00 2001 From: Cameron Whiteside Date: Tue, 17 Feb 2026 14:37:01 -0600 Subject: [PATCH 4/9] [Agents] Add facilitator documentation link to x402 config --- src/content/docs/agents/x402/charge-for-mcp-tools.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/content/docs/agents/x402/charge-for-mcp-tools.mdx b/src/content/docs/agents/x402/charge-for-mcp-tools.mdx index db41d8c8adf8ee4..ff3e583cf6834b2 100644 --- a/src/content/docs/agents/x402/charge-for-mcp-tools.mdx +++ b/src/content/docs/agents/x402/charge-for-mcp-tools.mdx @@ -21,7 +21,8 @@ import { z } from "zod"; const X402_CONFIG: X402Config = { network: "base", recipient: env.MCP_ADDRESS, - facilitator: { url: "https://x402.org/facilitator" }, + facilitator: { url: "https://x402.org/facilitator" }, // Payment facilitator URL + // To learn more about facilitators: https://x402.gitbook.io/x402/core-concepts/facilitator }; export class PaidMCP extends McpAgent { From b01c8399a7d31a7eaf8c55e97a4ca6746337518d Mon Sep 17 00:00:00 2001 From: Cameron Whiteside Date: Tue, 17 Feb 2026 14:39:31 -0600 Subject: [PATCH 5/9] [Agents] Simplify pay-from-agents-sdk to match production style --- .../docs/agents/x402/pay-from-agents-sdk.mdx | 65 ++++--------------- 1 file changed, 13 insertions(+), 52 deletions(-) diff --git a/src/content/docs/agents/x402/pay-from-agents-sdk.mdx b/src/content/docs/agents/x402/pay-from-agents-sdk.mdx index c0323da2784e75b..3f6fbeab8a203d2 100644 --- a/src/content/docs/agents/x402/pay-from-agents-sdk.mdx +++ b/src/content/docs/agents/x402/pay-from-agents-sdk.mdx @@ -6,17 +6,7 @@ sidebar: description: Use withX402Client to pay for resources from a Cloudflare Agent. --- -The Agents SDK includes `withX402Client`, which wraps an MCP client to handle x402 payments automatically or with human approval. - -## Prerequisites - -- A [Cloudflare account](https://dash.cloudflare.com/sign-up) -- An Agent project -- A wallet with USDC (test on `base-sepolia`, production on `base`) - -## Setup - -Wrap your MCP client connection with `withX402Client`: +The Agents SDK includes an MCP client that can pay for x402-protected tools. Use it from your Agents or any MCP client connection. ```ts import { Agent } from "agents"; @@ -24,9 +14,9 @@ import { withX402Client } from "agents/x402"; import { privateKeyToAccount } from "viem/accounts"; export class MyAgent extends Agent { - x402Client!: ReturnType; + // Your Agent definitions... - async onStart() { + onStart() { const { id } = await this.mcp.connect(`${env.WORKER_URL}/mcp`); const account = privateKeyToAccount(this.env.MY_PRIVATE_KEY); @@ -36,8 +26,14 @@ export class MyAgent extends Agent { }); } - async callTool(toolName: string, toolArgs: unknown) { - return await this.x402Client.callTool(null, { + onPaymentRequired(paymentRequirements): Promise { + // Your human-in-the-loop confirmation flow... + } + + async onToolCall(toolName: string, toolArgs: unknown) { + // The first parameter is the confirmation callback. + // Set to `null` for the agent to pay automatically. + return await this.x402Client.callTool(this.onPaymentRequired, { name: toolName, arguments: toolArgs, }); @@ -45,38 +41,7 @@ export class MyAgent extends Agent { } ``` -## Payment confirmation - -### Auto-pay - -Pass `null` as the first argument to `callTool`. The agent pays without prompting: - -```ts -const result = await this.x402Client.callTool(null, { - name: "expensive-tool", - arguments: { data: "..." }, -}); -``` - -### Human-in-the-loop - -Pass a callback that receives payment requirements and returns whether to proceed: - -```ts -async callToolWithApproval(toolName: string, toolArgs: unknown) { - return await this.x402Client.callTool( - this.onPaymentRequired.bind(this), - { name: toolName, arguments: toolArgs } - ); -} - -async onPaymentRequired(requirements: PaymentRequirements): Promise { - console.log(`Tool costs ${requirements.amount} ${requirements.asset}`); - return await this.askUserForApproval(requirements); -} -``` - -The `PaymentRequirements` object contains `resource`, `amount`, `asset`, `network`, and `recipient`. +For a complete working example, see [x402-mcp on GitHub](https://github.com/cloudflare/agents/tree/main/examples/x402-mcp). ## Environment setup @@ -90,11 +55,7 @@ MY_PRIVATE_KEY="0x..." npx wrangler secret put MY_PRIVATE_KEY ``` -## Testing - -Use `base-sepolia` and get test USDC from the [Circle faucet](https://faucet.circle.com/). - -For a complete working example, see [x402-mcp on GitHub](https://github.com/cloudflare/agents/tree/main/examples/x402-mcp). +Use `base-sepolia` for testing. Get test USDC from the [Circle faucet](https://faucet.circle.com/). ## Related From 5c827e0d9a6b1124f9770ca1607c91ecefa601ee Mon Sep 17 00:00:00 2001 From: Cameron Whiteside Date: Tue, 17 Feb 2026 14:44:04 -0600 Subject: [PATCH 6/9] [Agents] Add human-in-the-loop placeholders to coding tools page --- src/content/docs/agents/x402/pay-with-tool-plugins.mdx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/content/docs/agents/x402/pay-with-tool-plugins.mdx b/src/content/docs/agents/x402/pay-with-tool-plugins.mdx index e40339cfa78c747..afebd4a0c4f7cf9 100644 --- a/src/content/docs/agents/x402/pay-with-tool-plugins.mdx +++ b/src/content/docs/agents/x402/pay-with-tool-plugins.mdx @@ -59,6 +59,10 @@ export const X402PaymentPlugin: Plugin = async () => ({ throw new Error("X402_PRIVATE_KEY environment variable is not set."); } + // Your human-in-the-loop confirmation flow... + // const approved = await confirmPayment(args.url, estimatedCost); + // if (!approved) throw new Error("Payment declined by user"); + const account = privateKeyToAccount(privateKey as `0x${string}`); const client = new x402Client(); registerExactEvmScheme(client, { signer: account }); @@ -131,6 +135,10 @@ if (!privateKey) { } try { + // Your human-in-the-loop confirmation flow... + // const approved = await confirmPayment(url); + // if (!approved) process.exit(0); + const account = privateKeyToAccount(privateKey); const client = new x402Client(); registerExactEvmScheme(client, { signer: account }); From a3cba089d4bbc827cefc24e1b3922122df30bf8e Mon Sep 17 00:00:00 2001 From: "ask-bonk[bot]" Date: Wed, 18 Feb 2026 02:24:46 +0000 Subject: [PATCH 7/9] 7 issues: code bugs, path mismatches Co-authored-by: threepointone --- src/content/docs/agents/x402/charge-for-http-content.mdx | 2 +- src/content/docs/agents/x402/charge-for-mcp-tools.mdx | 2 +- src/content/docs/agents/x402/pay-from-agents-sdk.mdx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/content/docs/agents/x402/charge-for-http-content.mdx b/src/content/docs/agents/x402/charge-for-http-content.mdx index a89ffc5b2169996..ec2b7a38d9eeb87 100644 --- a/src/content/docs/agents/x402/charge-for-http-content.mdx +++ b/src/content/docs/agents/x402/charge-for-http-content.mdx @@ -81,7 +81,7 @@ const app = new Hono<{ Bindings: Env }>(); app.use( paymentMiddleware( - process.env.SERVER_ADDRESS as `0x${string}`, + "0xYourWalletAddress" as `0x${string}`, { "/premium": { price: "$0.10", diff --git a/src/content/docs/agents/x402/charge-for-mcp-tools.mdx b/src/content/docs/agents/x402/charge-for-mcp-tools.mdx index ff3e583cf6834b2..780714109121437 100644 --- a/src/content/docs/agents/x402/charge-for-mcp-tools.mdx +++ b/src/content/docs/agents/x402/charge-for-mcp-tools.mdx @@ -20,7 +20,7 @@ import { z } from "zod"; const X402_CONFIG: X402Config = { network: "base", - recipient: env.MCP_ADDRESS, + recipient: "0xYourWalletAddress", facilitator: { url: "https://x402.org/facilitator" }, // Payment facilitator URL // To learn more about facilitators: https://x402.gitbook.io/x402/core-concepts/facilitator }; diff --git a/src/content/docs/agents/x402/pay-from-agents-sdk.mdx b/src/content/docs/agents/x402/pay-from-agents-sdk.mdx index 3f6fbeab8a203d2..a032a7e3e89366e 100644 --- a/src/content/docs/agents/x402/pay-from-agents-sdk.mdx +++ b/src/content/docs/agents/x402/pay-from-agents-sdk.mdx @@ -16,8 +16,8 @@ import { privateKeyToAccount } from "viem/accounts"; export class MyAgent extends Agent { // Your Agent definitions... - onStart() { - const { id } = await this.mcp.connect(`${env.WORKER_URL}/mcp`); + async onStart() { + const { id } = await this.mcp.connect(`${this.env.WORKER_URL}/mcp`); const account = privateKeyToAccount(this.env.MY_PRIVATE_KEY); this.x402Client = withX402Client(this.mcp.mcpConnections[id].client, { From c538c1c79c421f356e53ffb65b837ab98ccca35f Mon Sep 17 00:00:00 2001 From: Jun Lee Date: Wed, 18 Feb 2026 09:33:12 +0000 Subject: [PATCH 8/9] Apply suggestions from code review --- src/content/docs/agents/x402/charge-for-http-content.mdx | 6 +++--- src/content/docs/agents/x402/charge-for-mcp-tools.mdx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/content/docs/agents/x402/charge-for-http-content.mdx b/src/content/docs/agents/x402/charge-for-http-content.mdx index ec2b7a38d9eeb87..d4afac87b814a5f 100644 --- a/src/content/docs/agents/x402/charge-for-http-content.mdx +++ b/src/content/docs/agents/x402/charge-for-http-content.mdx @@ -54,7 +54,7 @@ With [Bot Management](/bots/), the proxy can charge crawlers while keeping the s } ``` -Requests with a bot score below `bot_score_threshold` see the paywall. Use `except_detection_ids` to allowlist specific crawlers by [detection ID](/ai-crawl-control/reference/bots/). +Requests with a bot score below `bot_score_threshold` are directed to the paywall. Use `except_detection_ids` to allowlist specific crawlers by [detection ID](/ai-crawl-control/reference/bots/). ## Deploy @@ -67,7 +67,7 @@ npm install npx wrangler deploy ``` -For full configuration options and Bot Management examples, see the [template README](https://github.com/cloudflare/templates/tree/main/x402-proxy-template). +For full configuration options and Bot Management examples, refer to the [template README](https://github.com/cloudflare/templates/tree/main/x402-proxy-template). ## Custom Worker endpoints @@ -98,7 +98,7 @@ app.get("/premium", (c) => c.json({ message: "Thanks for paying!" })); export default app; ``` -See the [x402 Workers example](https://github.com/cloudflare/agents/tree/main/examples/x402) for a complete implementation. +Refer to the [x402 Workers example](https://github.com/cloudflare/agents/tree/main/examples/x402) for a complete implementation. ## Related diff --git a/src/content/docs/agents/x402/charge-for-mcp-tools.mdx b/src/content/docs/agents/x402/charge-for-mcp-tools.mdx index 780714109121437..c2786a4d264e39f 100644 --- a/src/content/docs/agents/x402/charge-for-mcp-tools.mdx +++ b/src/content/docs/agents/x402/charge-for-mcp-tools.mdx @@ -84,7 +84,7 @@ When a client calls a paid tool without payment, the server returns 402 with pay Use `base-sepolia` and get test USDC from the [Circle faucet](https://faucet.circle.com/). -For a complete working example, see [x402-mcp on GitHub](https://github.com/cloudflare/agents/tree/main/examples/x402-mcp). +For a complete working example, refer to [x402-mcp on GitHub](https://github.com/cloudflare/agents/tree/main/examples/x402-mcp). ## Related From 1d2d3466a0fa3bcc1f18bd640b47eff2e213fdfa Mon Sep 17 00:00:00 2001 From: Cameron Whiteside Date: Wed, 18 Feb 2026 11:41:45 -0600 Subject: [PATCH 9/9] [Agents] Address x402 docs review feedback - Fix code bugs: async onStart, this.env access, placeholder addresses - Restore sidebar order to 8 (was incorrectly changed to 5) - Add deploy button description - Simplify pay-with-tool-plugins: remove build step, reorder sections - Streamline intro text throughout --- .../agents/x402/charge-for-http-content.mdx | 2 + src/content/docs/agents/x402/index.mdx | 2 +- .../agents/x402/pay-with-tool-plugins.mdx | 78 +++++++------------ 3 files changed, 29 insertions(+), 53 deletions(-) diff --git a/src/content/docs/agents/x402/charge-for-http-content.mdx b/src/content/docs/agents/x402/charge-for-http-content.mdx index d4afac87b814a5f..1c112cdf7604224 100644 --- a/src/content/docs/agents/x402/charge-for-http-content.mdx +++ b/src/content/docs/agents/x402/charge-for-http-content.mdx @@ -8,6 +8,8 @@ description: Gate HTTP endpoints with x402 payments using a Cloudflare Worker pr The x402-proxy template is a Cloudflare Worker that sits in front of any HTTP backend. When a request hits a protected route, the proxy returns a 402 response with payment instructions. After the client pays, the proxy verifies the payment and forwards the request to your origin. +Deploy the x402-proxy template to your Cloudflare account: + [![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/templates/tree/main/x402-proxy-template) ## Prerequisites diff --git a/src/content/docs/agents/x402/index.mdx b/src/content/docs/agents/x402/index.mdx index e6acf40984a5295..3d88887105b6091 100644 --- a/src/content/docs/agents/x402/index.mdx +++ b/src/content/docs/agents/x402/index.mdx @@ -2,7 +2,7 @@ title: x402 pcx_content_type: navigation sidebar: - order: 5 + order: 8 group: hideIndex: false --- diff --git a/src/content/docs/agents/x402/pay-with-tool-plugins.mdx b/src/content/docs/agents/x402/pay-with-tool-plugins.mdx index afebd4a0c4f7cf9..91bf48f66bd8dd4 100644 --- a/src/content/docs/agents/x402/pay-with-tool-plugins.mdx +++ b/src/content/docs/agents/x402/pay-with-tool-plugins.mdx @@ -6,38 +6,19 @@ sidebar: description: Add x402 payment handling to OpenCode and Claude Code. --- -Add x402 payment handling to AI coding tools so they can pay for paywalled resources automatically. +The following examples show how to add x402 payment handling to AI coding tools. When the tool encounters a 402 response, it pays automatically and retries. -## Prerequisites +Both examples require: -- OpenCode or Claude Code installed -- A wallet with USDC (test on `base-sepolia`, production on `base`) - -## Setup - -Both implementations use the same dependencies and environment variable. - -Install the x402 packages: - -```sh -npm install @x402/fetch @x402/evm viem -``` - -Set your wallet private key: - -```sh -export X402_PRIVATE_KEY="0xYourPrivateKey" -``` - -Get test USDC from the [Circle faucet](https://faucet.circle.com/). +- A wallet private key (set as `X402_PRIVATE_KEY` environment variable) +- The x402 packages: `@x402/fetch`, `@x402/evm`, and `viem` ## OpenCode plugin -OpenCode plugins expose tools to the agent. Create an `x402-fetch` tool that handles 402 responses. - -Create `.opencode/plugins/x402-payment.ts`: +OpenCode plugins expose tools to the agent. To create an `x402-fetch` tool that handles 402 responses, create `.opencode/plugins/x402-payment.ts`: ```ts +// Use base-sepolia for testing. Get test USDC from https://faucet.circle.com/ import type { Plugin } from "@opencode-ai/plugin"; import { tool } from "@opencode-ai/plugin"; import { x402Client, wrapFetchWithPayment } from "@x402/fetch"; @@ -90,32 +71,10 @@ When the built-in `webfetch` returns a 402, the agent calls `x402-fetch` to retr ## Claude Code hook -Claude Code hooks intercept tool results. Hook into `WebFetch` responses to handle 402s transparently. - -Create `.claude/settings.json`: - -```json -{ - "hooks": { - "PostToolUse": [ - { - "matcher": "WebFetch", - "hooks": [ - { - "type": "command", - "command": "node .claude/dist/handle-x402.mjs", - "timeout": 30 - } - ] - } - ] - } -} -``` - -Create `.claude/scripts/handle-x402.mjs`: +Claude Code hooks intercept tool results. To handle 402s transparently, create a script at `.claude/scripts/handle-x402.mjs`: ```js +// Use base-sepolia for testing. Get test USDC from https://faucet.circle.com/ import { x402Client, wrapFetchWithPayment } from "@x402/fetch"; import { registerExactEvmScheme } from "@x402/evm/exact/client"; import { privateKeyToAccount } from "viem/accounts"; @@ -174,10 +133,25 @@ function readStdin() { } ``` -Build the hook: +Register the hook in `.claude/settings.json`: -```sh -cd .claude && npm run build +```json +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "WebFetch", + "hooks": [ + { + "type": "command", + "command": "node .claude/scripts/handle-x402.mjs", + "timeout": 30 + } + ] + } + ] + } +} ``` ## Related