|
| 1 | +--- |
| 2 | +name: agent-payment-x402 |
| 3 | +description: Add x402 payment execution to AI agents — per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents need to pay for APIs, services, or other agents. |
| 4 | +origin: community |
| 5 | +--- |
| 6 | + |
| 7 | +# Agent Payment Execution (x402) |
| 8 | + |
| 9 | +Enable AI agents to make autonomous payments with built-in spending controls. Uses the x402 HTTP payment protocol and MCP tools so agents can pay for external services, APIs, or other agents without custodial risk. |
| 10 | + |
| 11 | +## When to Use |
| 12 | + |
| 13 | +Use when: your agent needs to pay for an API call, purchase a service, settle with another agent, enforce per-task spending limits, or manage a non-custodial wallet. Pairs naturally with cost-aware-llm-pipeline and security-review skills. |
| 14 | + |
| 15 | +## How It Works |
| 16 | + |
| 17 | +### x402 Protocol |
| 18 | +x402 extends HTTP 402 (Payment Required) into a machine-negotiable flow. When a server returns `402`, the agent's payment tool automatically negotiates price, checks budget, signs a transaction, and retries — no human in the loop. |
| 19 | + |
| 20 | +### Spending Controls |
| 21 | +Every payment tool call enforces a `SpendingPolicy`: |
| 22 | +- **Per-task budget** — max spend for a single agent action |
| 23 | +- **Per-session budget** — cumulative limit across an entire session |
| 24 | +- **Allowlisted recipients** — restrict which addresses/services the agent can pay |
| 25 | +- **Rate limits** — max transactions per minute/hour |
| 26 | + |
| 27 | +### Non-Custodial Wallets |
| 28 | +Agents hold their own keys via ERC-4337 smart accounts. The orchestrator sets policy before delegation; the agent can only spend within bounds. No pooled funds, no custodial risk. |
| 29 | + |
| 30 | +## MCP Integration |
| 31 | + |
| 32 | +The payment layer exposes standard MCP tools that slot into any Claude Code or agent harness setup. |
| 33 | + |
| 34 | +> **Security note**: Always pin the package version. This tool manages private keys — unpinned `npx` installs introduce supply-chain risk. |
| 35 | +
|
| 36 | +```json |
| 37 | +{ |
| 38 | + "mcpServers": { |
| 39 | + "agentpay": { |
| 40 | + "command": "npx", |
| 41 | + |
| 42 | + } |
| 43 | + } |
| 44 | +} |
| 45 | +``` |
| 46 | + |
| 47 | +### Available Tools (agent-callable) |
| 48 | + |
| 49 | +| Tool | Purpose | |
| 50 | +|------|---------| |
| 51 | +| `get_balance` | Check agent wallet balance | |
| 52 | +| `send_payment` | Send payment to address or ENS | |
| 53 | +| `check_spending` | Query remaining budget | |
| 54 | +| `list_transactions` | Audit trail of all payments | |
| 55 | + |
| 56 | +> **Note**: Spending policy is set by the **orchestrator** before delegating to the agent — not by the agent itself. This prevents agents from escalating their own spending limits. Configure policy via `set_policy` in your orchestration layer or pre-task hook, never as an agent-callable tool. |
| 57 | +
|
| 58 | +## Examples |
| 59 | + |
| 60 | +### Budget enforcement in an MCP client |
| 61 | + |
| 62 | +When building an orchestrator that calls the agentpay MCP server, enforce budgets before dispatching paid tool calls. |
| 63 | + |
| 64 | +> **Prerequisites**: Install the package before adding the MCP config — `npx` without `-y` will prompt for confirmation in non-interactive environments, causing the server to hang: `npm install -g [email protected]` |
| 65 | +
|
| 66 | +```typescript |
| 67 | +import { Client } from "@modelcontextprotocol/sdk/client/index.js"; |
| 68 | +import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; |
| 69 | + |
| 70 | +async function main() { |
| 71 | + // 1. Validate credentials before constructing the transport. |
| 72 | + // A missing key must fail immediately — never let the subprocess start without auth. |
| 73 | + const walletKey = process.env.WALLET_PRIVATE_KEY; |
| 74 | + if (!walletKey) { |
| 75 | + throw new Error("WALLET_PRIVATE_KEY is not set — refusing to start payment server"); |
| 76 | + } |
| 77 | + |
| 78 | + // Connect to the agentpay MCP server via stdio transport. |
| 79 | + // Whitelist only the env vars the server needs — never forward all of process.env |
| 80 | + // to a third-party subprocess that manages private keys. |
| 81 | + const transport = new StdioClientTransport({ |
| 82 | + command: "npx", |
| 83 | + |
| 84 | + env: { |
| 85 | + PATH: process.env.PATH ?? "", |
| 86 | + NODE_ENV: process.env.NODE_ENV ?? "production", |
| 87 | + WALLET_PRIVATE_KEY: walletKey, |
| 88 | + }, |
| 89 | + }); |
| 90 | + const agentpay = new Client({ name: "orchestrator", version: "1.0.0" }); |
| 91 | + await agentpay.connect(transport); |
| 92 | + |
| 93 | + // 2. Set spending policy before delegating to the agent. |
| 94 | + // Always verify success — a silent failure means no controls are active. |
| 95 | + const policyResult = await agentpay.callTool({ |
| 96 | + name: "set_policy", |
| 97 | + arguments: { |
| 98 | + per_task_budget: 0.50, |
| 99 | + per_session_budget: 5.00, |
| 100 | + allowlisted_recipients: ["api.example.com"], |
| 101 | + }, |
| 102 | + }); |
| 103 | + if (policyResult.isError) { |
| 104 | + throw new Error( |
| 105 | + `Failed to set spending policy — do not delegate: ${JSON.stringify(policyResult.content)}` |
| 106 | + ); |
| 107 | + } |
| 108 | + |
| 109 | + // 3. Use preToolCheck before any paid action |
| 110 | + await preToolCheck(agentpay, 0.01); |
| 111 | +} |
| 112 | + |
| 113 | +// Pre-tool hook: fail-closed budget enforcement with four distinct error paths. |
| 114 | +async function preToolCheck(agentpay: Client, apiCost: number): Promise<void> { |
| 115 | + // Path 1: Reject invalid input (NaN/Infinity bypass the < comparison) |
| 116 | + if (!Number.isFinite(apiCost) || apiCost < 0) { |
| 117 | + throw new Error(`Invalid apiCost: ${apiCost} — action blocked`); |
| 118 | + } |
| 119 | + |
| 120 | + // Path 2: Transport/connectivity failure |
| 121 | + let result; |
| 122 | + try { |
| 123 | + result = await agentpay.callTool({ name: "check_spending" }); |
| 124 | + } catch (err) { |
| 125 | + throw new Error(`Payment service unreachable — action blocked: ${err}`); |
| 126 | + } |
| 127 | + |
| 128 | + // Path 3: Tool returned an error (e.g., auth failure, wallet not initialised) |
| 129 | + if (result.isError) { |
| 130 | + throw new Error( |
| 131 | + `check_spending failed — action blocked: ${JSON.stringify(result.content)}` |
| 132 | + ); |
| 133 | + } |
| 134 | + |
| 135 | + // Path 4: Parse and validate the response shape |
| 136 | + let remaining: number; |
| 137 | + try { |
| 138 | + const parsed = JSON.parse( |
| 139 | + (result.content as Array<{ text: string }>)[0].text |
| 140 | + ); |
| 141 | + if (!Number.isFinite(parsed?.remaining)) { |
| 142 | + throw new TypeError("missing or non-finite 'remaining' field"); |
| 143 | + } |
| 144 | + remaining = parsed.remaining; |
| 145 | + } catch (err) { |
| 146 | + throw new Error( |
| 147 | + `check_spending returned unexpected format — action blocked: ${err}` |
| 148 | + ); |
| 149 | + } |
| 150 | + |
| 151 | + // Path 5: Budget exceeded |
| 152 | + if (remaining < apiCost) { |
| 153 | + throw new Error( |
| 154 | + `Budget exceeded: need $${apiCost} but only $${remaining} remaining` |
| 155 | + ); |
| 156 | + } |
| 157 | +} |
| 158 | + |
| 159 | +main().catch((err) => { |
| 160 | + console.error(err); |
| 161 | + process.exitCode = 1; |
| 162 | +}); |
| 163 | +``` |
| 164 | + |
| 165 | +## Best Practices |
| 166 | + |
| 167 | +- **Set budgets before delegation**: When spawning sub-agents, attach a SpendingPolicy via your orchestration layer. Never give an agent unlimited spend. |
| 168 | +- **Pin your dependencies **: Always specify an exact version in your MCP config (e.g., `[email protected]`). Verify package integrity before deploying to production. |
| 169 | +- **Audit trails**: Use `list_transactions` in post-task hooks to log what was spent and why. |
| 170 | +- **Fail closed**: If the payment tool is unreachable, block the paid action — don't fall back to unmetered access. |
| 171 | +- **Pair with security-review**: Payment tools are high-privilege. Apply the same scrutiny as shell access. |
| 172 | +- **Test with testnets first**: Use Base Sepolia for development; switch to Base mainnet for production. |
| 173 | + |
| 174 | +## Production Reference |
| 175 | + |
| 176 | +- **npm**: [`agentwallet-sdk`](https://www.npmjs.com/package/agentwallet-sdk) |
| 177 | +- **Merged into NVIDIA NeMo Agent Toolkit**: [PR #17](https://github.com/NVIDIA/NeMo-Agent-Toolkit-Examples/pull/17) — x402 payment tool for NVIDIA's agent examples |
| 178 | +- **Protocol spec**: [x402.org](https://x402.org) |
0 commit comments