x402dispatcher
Local x402 Bazaar Aggregator for AI agents: discover paid APIs from the Coinbase x402 Bazaar, wrap them as Model Context Protocol (MCP) tools, settle micropayments from a CDP treasury wallet, and return the upstream data to the agent.
This repo is currently at V4.
Why this exists
AI agents are good at reasoning and tool use, but bad at paying for APIs. The x402 protocol turns HTTP 402 Payment Required into a programmable stablecoin micropayment rail (typically USDC).
x402dispatcher sits in the middle as a solo-operator friendly aggregator:
| Idea | What it means |
|---|---|
| Discovery | Query the public Coinbase x402 Bazaar catalog |
| MCP integration | Expose discovered APIs as MCP tools for Cursor / agents |
| Dispatch | Sign and settle payment from a treasury wallet via @coinbase/cdp-sdk |
| Monetization | Apply a micro-markup on top of upstream cost and retain the spread |
Funds move wallet → merchant. The platform does not custody buyer funds.
Roadmap
| Version | Status | Goal |
|---|---|---|
| V1 | Done | Manually wrap one paid-style flow (MBTA demo + $0.01 USDC testnet settle) |
| V2 | Done | Auto-discover Base Sepolia Bazaar APIs and wrap many as MCP tools with real x402 payment |
| V3 | Done | Smart arbitrage: search, compare prices, pick cheapest API for a task (with failover) |
| V4 | Current | Track success/latency; economy vs verified routing tiers |
| V5 | Planned | Cloud host, public registries, agent.json for crawlers |
What V4 does
On top of V3 routing, V4 records every paid call’s success and latency in data/api-stats.json, then offers two tiers:
| Tier | Behavior |
|---|---|
economy |
Cheapest first (V3 behavior) |
verified |
Only APIs with enough successful history; ranked by reliability/latency/price score |
Thresholds (env): VERIFIED_MIN_SAMPLES (default 2), VERIFIED_MIN_SUCCESS_RATE (default 0.8).
New tools: get_api_stats, list_verified_apis. quote_route / route_and_call accept optional tier.
What V3 does
On top of V2 discovery + payment, V3 adds a router:
quote_route— search Bazaar for a natural-language task, rank candidates by total price (upstream + markup), return the plan without payingroute_and_call— same ranking, pay and call the cheapest; on failure, try the next-cheapest (up tomax_attempts)
All spends remain gated by MAX_PRICE_USD.
What V2 does
On startup the MCP server:
- Loads credentials from
.env - Resolves a CDP Treasury payer wallet
- Searches / lists the Coinbase Bazaar for Base Sepolia (
eip155:84532) HTTP resources priced at or belowMAX_PRICE_USD - Registers each match as an MCP tool
- Also registers helper tools:
search_bazaar,list_discovered_apis,call_x402_api - Keeps the V1 demo tool
get_mbta_predictions
When an agent calls a discovered tool (or call_x402_api):
- Enforce
MAX_PRICE_USDon upstream price + markup - Pay the real x402 endpoint with
CdpX402Client+wrapFetchWithPaymentfrom@x402/fetch - Collect the markup spread (Treasury → Merchant USDC transfer when possible)
- Return
{ payment, data }to the agent
V1 get_mbta_predictions still proves a fixed $0.01 USDC Base Sepolia transfer, then fetches free public MBTA prediction data.
Architecture
Agent / Cursor
│ MCP (stdio)
▼
x402dispatcher MCP server (src/index.ts)
│
├─ Discovery → listX402DiscoveryResources / searchX402Resources (@coinbase/cdp-sdk)
├─ Payment → CdpX402Client + wrapFetchWithPayment (@coinbase/cdp-sdk/x402, @x402/fetch)
├─ Routing → economy (price) / verified (stats score) with failover
├─ Stats → data/api-stats.json success + latency history
├─ Guardrails → MAX_PRICE_USD (+ SDK spend controls)
└─ Markup → MARKUP_BPS applied; optional USDC transfer to Merchant account
│
▼
Upstream x402 HTTP API (Bazaar listing)
Key packages
@coinbase/cdp-sdk— wallets, Bazaar discovery,CdpX402Client@x402/fetch/@x402/core/@x402/evm— HTTP 402 payment loop@modelcontextprotocol/sdk— MCP server + toolsdotenv,zod,viem
Requirements
- Node.js 19+ (CDP SDK requirement; 22 LTS recommended)
- Coinbase Developer Platform credentials:
CDP_API_KEY_IDCDP_API_KEY_SECRETCDP_WALLET_SECRET(Wallet Secret from CDP Portal → Non-custodial Wallet → Security — not a MetaMask private key)
- Base Sepolia USDC (+ a little ETH for gas) on the Treasury address
Setup
git clone https://github.com/jegamboafuentes/x402dispatcher.git
cd x402dispatcher
npm install
cp .env.example .env
# edit .env with your CDP credentials
Environment variables
| Variable | Required | Description |
|---|---|---|
CDP_API_KEY_ID |
Yes | CDP API key ID |
CDP_API_KEY_SECRET |
Yes | CDP API key secret |
CDP_WALLET_SECRET |
Yes | CDP Wallet Secret (base64 P-256 key from Portal) |
MAX_PRICE_USD |
Recommended | Hard cap before any automated spend (e.g. 0.01) |
MARKUP_BPS |
Optional | Markup in basis points (default 1000 = 10%) |
DISCOVERY_LIMIT |
Optional | Max Bazaar tools to register at startup (default 40, max 100) |
VERIFIED_MIN_SAMPLES |
Optional | Min successful-history calls for Verified (default 2) |
VERIFIED_MIN_SUCCESS_RATE |
Optional | Min success rate 0–1 for Verified (default 0.8) |
CDP_PRIVATE_KEY |
Optional | Only if you import a specific EOA into CDP (not used by default V2+ payer path) |
Never commit .env. Only .env.example is tracked.
Fund the treasury
npx tsx -e "import 'dotenv/config'; import { CdpX402Client } from '@coinbase/cdp-sdk/x402'; const c = new CdpX402Client({ environment: 'development', walletConfig: { type: 'eoa', accountName: 'Treasury' } }); console.log(await c.getAddresses());"
Send Base Sepolia USDC (and a little ETH) to the printed evmAddress.
Run
MCP server (stdio)
npm start
Cursor MCP config
Project file: .cursor/mcp.json (already included). Cursor should spawn:
{
"mcpServers": {
"x402dispatcher": {
"command": "npx",
"args": ["tsx", "src/index.ts"],
"cwd": "${workspaceFolder}"
}
}
}
Reload MCP in Cursor after clone/install. If ${workspaceFolder} is not expanded on your Cursor build, set cwd to the absolute path of this repo and optionally point command at your Node 22 binary.
MCP tools
Core
| Tool | Purpose |
|---|---|
quote_route |
Rank matching APIs; tier=economy|verified; no payment |
route_and_call |
Pay/call best match for tier; failover; records stats |
get_api_stats |
V4 — local success/latency history |
list_verified_apis |
V4 — APIs that currently qualify as Verified |
search_bazaar |
Semantic/text search of Base Sepolia Bazaar APIs under MAX_PRICE_USD |
list_discovered_apis |
List APIs currently cached/registered |
call_x402_api |
Pay + call by tool_name or full resource URL |
get_mbta_predictions |
V1 demo: $0.01 USDC settle + live MBTA predictions |
Dynamic tools
At startup, x402dispatcher also registers one MCP tool per discovered Bazaar resource (names like x402_<host>_<path>_<n>). Each accepts optional query / body and pays the upstream URL.
Testing
V4 end-to-end (recommended)
Seeds two economy weather calls, promotes the winner into Verified, then quotes/routes with tier=verified:
npm run test:v4
Expect: V4 SMOKE TEST PASSED
Earlier versions
npm run test:v3
npm run test:v2
Manual checks in Cursor
- Reload the
x402dispatcherMCP server - Ask for weather with economy routing a couple of times (builds stats)
- Ask: “List verified APIs” / “Get API stats”
- Ask: “Use the verified tier to get weather for Boston”
- Confirm
chosen.verifiedis true anddata/api-stats.jsongrew
Guardrail check
Set MAX_PRICE_USD below a listing’s total and confirm quote/route refuse or return zero candidates.
Project layout
x402dispatcher/
├── src/
│ ├── index.ts # MCP server, tool registration
│ ├── discovery.ts # Bazaar list/search → DiscoveredApi
│ ├── payment.ts # CdpX402Client, markup, MBTA settle
│ ├── routing.ts # quote + economy/verified route + failover
│ ├── stats.ts # V4 local success/latency store
│ └── config.ts # MAX_PRICE_USD, MARKUP_BPS, verified thresholds
├── scripts/
│ ├── v4-smoke-test.ts
│ ├── v3-smoke-test.ts
│ ├── v2-smoke-test.ts
│ ├── mcp-test.ts
│ └── smoke-test.ts
├── data/ # local api-stats.json (gitignored)
├── .cursor/
│ ├── mcp.json
│ └── rules/ # security + x402-stack agent rules
├── AGENTS.md # product / roadmap context for agents
├── .env.example
└── package.json
Security notes
- Wallet credentials load only from
.env— never hardcode secrets. - Every automated spend is gated by
MAX_PRICE_USDbefore signing. - V2 also configures CDP x402 spend controls (
maxAmountPerPayment+ Base Sepolia network allowlist). - Treat the Bazaar as a catalog, not an endorsement. Prefer small caps on testnet first.
CDP_WALLET_SECRETmust be the Portal Wallet Secret (long base64), not a MetaMask hex key.
Stack references
License
ISC