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..1c112cdf7604224 --- /dev/null +++ b/src/content/docs/agents/x402/charge-for-http-content.mdx @@ -0,0 +1,109 @@ +--- +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 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 + +- 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` are directed to 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, refer to 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( + "0xYourWalletAddress" 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; +``` + +Refer to 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 +- [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..c2786a4d264e39f --- /dev/null +++ b/src/content/docs/agents/x402/charge-for-mcp-tools.mdx @@ -0,0 +1,93 @@ +--- +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. + +## 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: "0xYourWalletAddress", + 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, + ); + + 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, refer to [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..3d88887105b6091 --- /dev/null +++ b/src/content/docs/agents/x402/index.mdx @@ -0,0 +1,48 @@ +--- +title: x402 +pcx_content_type: navigation +sidebar: + order: 8 + 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..a032a7e3e89366e --- /dev/null +++ b/src/content/docs/agents/x402/pay-from-agents-sdk.mdx @@ -0,0 +1,64 @@ +--- +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 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"; +import { withX402Client } from "agents/x402"; +import { privateKeyToAccount } from "viem/accounts"; + +export class MyAgent extends Agent { + // Your Agent definitions... + + 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, { + network: "base-sepolia", + account, + }); + } + + 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, + }); + } +} +``` + +For a complete working example, see [x402-mcp on GitHub](https://github.com/cloudflare/agents/tree/main/examples/x402-mcp). + +## Environment setup + +Store your private key securely: + +```sh +# Local development (.dev.vars) +MY_PRIVATE_KEY="0x..." + +# Production +npx wrangler secret put MY_PRIVATE_KEY +``` + +Use `base-sepolia` for testing. Get test USDC from the [Circle faucet](https://faucet.circle.com/). + +## 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..91bf48f66bd8dd4 --- /dev/null +++ b/src/content/docs/agents/x402/pay-with-tool-plugins.mdx @@ -0,0 +1,162 @@ +--- +title: Pay from coding tools +pcx_content_type: how-to +sidebar: + order: 4 +description: Add x402 payment handling to OpenCode and Claude Code. +--- + +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. + +Both examples require: + +- 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. 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"; +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."); + } + + // 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 }); + 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. 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"; + +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 { + // 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 }); + 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)); + }); +} +``` + +Register the hook in `.claude/settings.json`: + +```json +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "WebFetch", + "hooks": [ + { + "type": "command", + "command": "node .claude/scripts/handle-x402.mjs", + "timeout": 30 + } + ] + } + ] + } +} +``` + +## 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