x402-wallet-mcp
v0.6.7
Published
Self-custodial USDC wallet + x402 payment signing as an MCP server for AI agents
Downloads
2,142
Readme
Give Claude Code, Cursor, or any MCP client its own wallet. It discovers x402 endpoints, signs USDC payments on Base, and handles the full HTTP 402 negotiation — so your AI agent can call paid APIs without human intervention.
You: "Get me the top Hacker News stories from the x402 API"
Claude: Using call_endpoint to pay $0.002 USDC...
✓ Payment signed (EIP-3009 TransferWithAuthorization)
✓ Got 10 stories from https://x402.onchainexpat.com/api/x402-tools/hackernews/topWhy This Exists
AI agents need to spend money. Today that means hardcoded API keys, credit cards on file, or manual approval for every request. The x402 protocol fixes this: servers return HTTP 402 with a price, clients sign a USDC payment, and the request goes through. No API keys. No subscriptions. Pay per call.
This project is the missing piece: an open-source MCP server that gives any AI agent a USDC wallet, spending controls, and the ability to pay for x402 APIs autonomously.
Features
- Zero-config wallets — works out of the box with no API keys or signup required
- Privy HSM-backed keys — keys never leave Privy's HSM/TEE infrastructure
- Three wallet modes — Proxy (zero-config default), Linked (email-recoverable), or Privy direct (BYOK)
- Email recovery — link your wallet to an email, recover it on any device via OTP
- Full x402 negotiation — handles 402 → sign → retry automatically
- EVM exact + escrow — EIP-3009 TransferWithAuthorization and ReceiveWithAuthorization
- Endpoint discovery — fetches
.well-known/x402documents and searches x402scan.com - Spending controls — per-call maximum and daily cap with automatic enforcement
- Transaction history — append-only log of every payment
- Coinbase Onramp — buy USDC with a debit card or Apple Pay, no crypto experience needed
- 13 MCP tools — everything an agent needs to discover, query, pay, and audit
Quick Start
No API keys or signup required. Just install and go:
Claude Code
claude mcp add x402-wallet -- npx x402-wallet-mcpCursor / Windsurf / Claude Desktop
Add to your MCP config file (.mcp.json, ~/.cursor/mcp.json, or Claude Desktop settings):
{
"mcpServers": {
"x402-wallet": {
"command": "npx",
"args": ["x402-wallet-mcp"]
}
}
}On first run an HSM-backed wallet is automatically provisioned via the x402 provisioning service. Fund your wallet using the fund_wallet tool (generates a Coinbase Onramp link) or send USDC on Base directly to the wallet address.
Link Your Wallet to Email (Recommended)
After setup, use wallet_link to connect your wallet to an email address. This enables recovery on any device:
You: "Link my wallet to [email protected]"
Claude: Verification code sent! Enter the 6-digit code from your email.
You: "123456"
Claude: ✓ Wallet linked to [email protected]. You can recover this wallet on any device.Recover on Another Device
You: "Recover my wallet using [email protected]"
Claude: ✓ Wallet recovered — same address, same balance.If you have multiple wallets linked to the same email, you'll be shown each wallet's address and USDC balance so you can choose which one to load.
Your Keys Are Always Yours
No private key is ever stored on your machine. All keys live in Privy's HSM/TEE secure enclaves — hardware security modules that never expose raw key material.
Once you link your email with wallet_link, you can:
- Export your private key at home.privy.io — log in with your email, verify via OTP, and click "Export keys"
- Recover your wallet on any device using
wallet_recoverwith the same email - Access your funds even if this package, the x402 provisioning service, or our website disappears entirely
This works for all wallet modes (proxy, linked, and BYOK). You do not need your own Privy credentials — the default setup is fully recoverable and exportable after linking your email.
Power Users: Bring Your Own Privy Credentials
For full control, sign up for a Privy account and pass your own credentials. This bypasses the proxy and talks directly to Privy:
claude mcp add x402-wallet \
-e PRIVY_APP_ID=your-app-id \
-e PRIVY_APP_SECRET=your-app-secret \
-- npx x402-wallet-mcpHow It Works
┌──────────────┐ 1. POST /api/data ┌──────────────┐
│ │ ──────────────────────────────→ │ │
│ AI Agent │ 2. 402 + price: $0.002 │ x402 Server │
│ (via MCP) │ ←────────────────────────────── │ │
│ │ 3. POST + X-PAYMENT header │ │
│ │ ──────────────────────────────→ │ │
│ │ 4. 200 + data │ │
│ │ ←────────────────────────────── │ │
└──────────────┘ └──────────────┘
│ │
│ sign EIP-3009 │ verify signature
│ TransferWithAuthorization │ settle USDC on Base
▼ ▼
┌──────────────┐ ┌──────────────┐
│ x402-wallet │ │ USDC on │
│ (Privy HSM) │ │ Base │
└──────────────┘ └──────────────┘- The agent calls
call_endpointwith a URL - The server returns HTTP 402 with payment requirements (
acceptsarray) - x402-wallet-mcp picks the best payment option, checks spending limits, signs an EIP-3009 authorization
- Retries the request with the signed payment in the
X-PAYMENTheader - Returns the API response to the agent and logs the transaction
MCP Tools
The server exposes 13 tools that any MCP client can call:
| Tool | Description | Key Parameters |
|------|-------------|----------------|
| call_endpoint | Make a paid API call (handles full 402 flow) | url, method?, body?, headers?, prefer_escrow? |
| query_endpoint | Probe pricing without paying | url, method? |
| discover_endpoints | Search for available x402 APIs | query?, source? |
| check_balance | USDC balance on Base + deposit address | — |
| wallet_info | Wallet mode, addresses, recovery status | — |
| fund_wallet | Buy USDC via Coinbase Onramp (debit card / Apple Pay) | amount? |
| transaction_history | Recent payment log | limit? |
| configure_spending | Set per-call max and daily cap | per_call_max?, daily_cap? |
| add_endpoint_source | Register a .well-known/x402 source | base_url?, endpoint_url? |
| manage_allowlist | Add/remove merchant allowlist entries | allow?, remove?, mode? |
| wallet_link | Link wallet to email for recovery | email?, session_token?, code? |
| wallet_recover | Recover a linked wallet on any device | email?, session_token?, code?, wallet_id? |
| export_key | Instructions for exporting private key via Privy | — |
Example: Paid API Call
When an agent calls call_endpoint:
{
"url": "https://x402.onchainexpat.com/api/x402-tools/hackernews/top",
"method": "POST",
"body": "{\"num_stories\": 5}"
}The tool returns:
{
"success": true,
"status": 200,
"amountPaid": "$0.01",
"scheme": "exact",
"network": "eip155:8453",
"data": {
"stories": ["..."]
}
}Spending Controls
Built-in safeguards prevent runaway spending:
| Control | Default | Override Env Var |
|---------|---------|------------------|
| Per-call maximum | $5.00 USDC | X402_PER_CALL_MAX |
| Daily cap | $50.00 USDC | X402_DAILY_CAP |
The daily cap resets at midnight UTC. Both limits can also be changed at runtime using the configure_spending tool.
If a payment would exceed either limit, the tool returns an error explaining why — the agent can then ask the user for approval or skip the call.
Payment Schemes
Exact (EIP-3009 TransferWithAuthorization)
The default and most common scheme. Signs a one-time USDC transfer authorization:
- Domain: USDC contract on Base (
0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) - Type:
TransferWithAuthorization(from, to, value, validAfter, validBefore, nonce) - Expiry: Configurable via
maxTimeoutSeconds(default 60s) - Nonce: Random 32 bytes (one-time use)
Escrow (EIP-3009 ReceiveWithAuthorization)
For endpoints that use the x402r escrow middleware. Funds are held in escrow and settled after the API response:
- Type:
ReceiveWithAuthorization(from, to, value, validAfter, validBefore, nonce) - to: Token collector contract (not the final recipient)
- Nonce: Deterministic — computed from
keccak256(chainId, escrowAddress, paymentInfoHash) - Expiry:
validAfter=0,validBefore=MAX_UINT48
The tool auto-detects which scheme to use based on the server's accepts array. By default it prefers exact; pass prefer_escrow: true to prefer escrow when both are available.
Configuration
Environment Variables
| Variable | Purpose | Required |
|----------|---------|----------|
| PRIVY_APP_ID | Privy application ID (enables direct Privy mode) | No |
| PRIVY_APP_SECRET | Privy application secret | No |
| X402_PROXY_URL | Custom proxy URL (default: https://x402.onchainexpat.com/api/wallet) | No |
| X402_PER_CALL_MAX | Max USDC per API call (e.g. "10.00") | No |
| X402_DAILY_CAP | Max USDC per day (e.g. "100.00") | No |
| X402_RPC_URL | Custom Base RPC endpoint | No |
| X402_SKIP_LINKING | Skip email link prompt on first run | No |
No environment variables are required. The wallet works zero-config out of the box, including Coinbase Onramp for buying USDC.
Wallet mode priority: If PRIVY_APP_ID and PRIVY_APP_SECRET are set, the wallet connects directly to Privy. Otherwise, it uses the hosted proxy for zero-config operation.
Data Directory
All persistent data is stored in ~/.x402-wallet/:
~/.x402-wallet/
├── config.json # Settings, wallet ID, endpoint sources
├── history.jsonl # Transaction log (append-only)
├── spending.json # Daily spending tracker
└── endpoints-cache.json # Discovery cache (1hr TTL)Default Configuration
{
"version": 2,
"wallet": { "mode": "proxy" },
"spending": { "perCallMaxUsdc": "5.00", "dailyCapUsdc": "50.00" },
"endpointSources": [
"https://x402.onchainexpat.com",
"https://padelmaps.org",
"https://stableenrich.dev",
"https://stablestudio.dev",
"https://x402.twit.sh"
],
"preferences": { "preferEscrow": false, "preferredNetwork": "evm" }
}Note: The default mode is
"proxy"(zero-config). Usewallet_linkto upgrade to"linked"mode with email recovery and key export. WhenPRIVY_APP_IDandPRIVY_APP_SECRETenv vars are set, the wallet automatically switches to"privy"mode.
Architecture
x402-wallet-mcp/
├── bin/
│ └── x402-wallet-mcp.ts # CLI entry point
├── src/
│ ├── index.ts # Main: wallet + MCP server + stdio
│ ├── server.ts # McpServer with 13 tools
│ ├── wallet/
│ │ ├── types.ts # WalletProvider interface
│ │ ├── factory.ts # Wallet creation (proxy/linked/privy)
│ │ ├── proxy-wallet.ts # Zero-config wallet via hosted proxy
│ │ ├── proxy-api.ts # REST client for proxy service
│ │ ├── privy-wallet.ts # Direct Privy server wallets (HSM/TEE)
│ │ ├── privy-api.ts # REST client for Privy API
│ │ ├── link-api.ts # Email linking & recovery API client
│ │ └── null-wallet.ts # Placeholder for unconfigured state
│ ├── payment/
│ │ ├── evm-exact.ts # EIP-3009 TransferWithAuthorization
│ │ ├── evm-escrow.ts # ReceiveWithAuthorization + nonce
│ │ ├── negotiator.ts # 402 → sign → retry orchestrator
│ │ ├── types.ts # AcceptEntry, PaymentRequired, etc.
│ │ └── constants.ts # USDC addresses, chain IDs
│ ├── discovery/
│ │ ├── well-known.ts # Fetch .well-known/x402
│ │ ├── x402scan.ts # Query x402scan.com
│ │ └── registry.ts # Merge + deduplicate + cache
│ ├── spending/
│ │ ├── tracker.ts # Per-call + daily cap enforcement
│ │ ├── allowlist.ts # Merchant allowlist validation
│ │ └── store.ts # Persist daily spend totals
│ ├── tools/ # 13 MCP tool implementations
│ ├── store/
│ │ ├── config.ts # ~/.x402-wallet/config.json
│ │ ├── history.ts # Append-only JSONL transaction log
│ │ └── paths.ts # Cross-platform path resolution
│ └── utils/
│ ├── logger.ts # stderr-only (stdout = MCP JSON-RPC)
│ ├── http.ts # Fetch with timeout + retries
│ ├── format.ts # USDC atomic ↔ human-readable
│ ├── deposit-qr.ts # QR code generation for deposits
│ └── onramp.ts # Coinbase Onramp URL generation
└── tests/
├── unit/ # 127 tests across 16 files
├── integration/ # Live endpoint tests (costs real USDC)
└── e2e/ # Full MCP server over stdioDevelopment
Prerequisites
- Node.js >= 18
- npm
Setup
git clone https://github.com/onchainexpat/x402-wallet-mcp.git
cd x402-wallet-mcp
npm installBuild
npm run build # TypeScript → dist/
npm run lint # Type-check without emittingRun in Development
npm run dev # Run with tsx (auto-reloads)Testing
# Unit tests (127 tests, no network calls, no USDC spent)
npm test
# Watch mode
npm run test:watch
# Integration tests (hits real x402 endpoints, costs real USDC)
# Requires a funded wallet
RUN_LIVE_TESTS=1 npm run test:live
# E2E tests (spawns MCP server over stdio, calls all 13 tools)
RUN_E2E_TESTS=1 npx vitest run tests/e2eThe unit test suite covers:
- Wallet: Proxy + Privy + Linked API mocking, factory routing, email linking/recovery
- Payment: EIP-3009 exact/escrow signing, escrow nonce determinism, full negotiator flow (402 → sign → retry), edge cases (double-402, empty accepts, spending limits)
- Spending: per-call max, daily cap, midnight reset, env var overrides, merchant allowlist
- Discovery: endpoint merging, deduplication, cache behavior, fetch failure handling
- Store: config defaults/persistence, history append/query, USDC formatting, Coinbase onramp URLs
Local Testing with an MCP Client
# Build and run
npm run build
node dist/bin/x402-wallet-mcp.js
# Or use npx to test the published package experience
npx .The server communicates over stdio (JSON-RPC), so you need an MCP client to interact with it. The easiest way is to add it to Claude Code's config and test through the chat.
Terminology
| Term | Definition | |------|------------| | x402 | Protocol for HTTP 402 payments — servers price API calls, clients pay with crypto | | MCP | Model Context Protocol — standard for AI tool servers | | EIP-3009 | Ethereum standard for gasless USDC transfers via signed authorizations | | Base | Coinbase's L2 network where USDC payments settle | | Exact | Direct payment scheme — USDC transfers immediately to the server | | Escrow | Protected payment scheme — funds held in smart contract, settled after API response |
Security Considerations
[!IMPORTANT] This software manages real cryptocurrency. Review the security policy before using in production.
- No keys on your machine: Private keys are never stored locally — not in plaintext, not encrypted, not anywhere on disk. All keys live in Privy's HSM/TEE secure enclaves.
- Survivable: If this package, the x402 provisioning service, or our website disappears, go to home.privy.io, log in with your linked email, and export your private key. Your funds are always accessible.
- HSM-backed signing: Whether using proxy or direct Privy mode, keys never leave Privy's hardware security modules.
- Proxy signing validation: The hosted proxy validates every signing request — only USDC transfers on Base, capped at 100 USDC per transaction.
- Spending limits: Enforced locally before signing. Cannot be bypassed by the AI agent.
- No stdout leaks: All logging goes to stderr. stdout is reserved for MCP JSON-RPC. Private keys never appear in logs.
- Email recovery: OTP-verified, rate-limited, HMAC-derived codes with 10-minute expiry.
Roadmap
- [ ] Multi-chain support (Ethereum mainnet, Arbitrum, Optimism)
- [ ] Payment receipts and on-chain verification
- [ ] Webhook notifications for payments
- [ ] Rate limiting and circuit breaker patterns
- [ ] Dashboard UI for spending analytics
Contributing
Contributions are welcome. See CONTRIBUTING.md for guidelines.
Before submitting a PR:
- Run
npm testand ensure all 127 tests pass - Run
npm run lintfor type checking - Add tests for new functionality
- Keep PRs focused — one feature or fix per PR
Related Projects
- x402 — The x402 protocol specification by Coinbase
- MCP — Model Context Protocol by Anthropic
- viem — TypeScript library for Ethereum (used for signing)
- x402scan.com — Directory of x402-enabled endpoints
