x402-mcp-proxy
Lets an MCP client that has no wallet use an x402-gated MCP server.
MCP client ──stdio, free──> x402-mcp-proxy ──HTTP + X-PAYMENT──> paid MCP server
(holds wallet)
Why this exists
MCP clients can't pay. claude mcp add supports OAuth and static headers —
no wallet, no signer, no 402 handling. And a pinned -H "X-PAYMENT: ..." is
not a workaround: the header carries a per-challenge nonce and signature, so a
fixed value is a replay, and any correctly built server refuses it.
So the wallet has to live somewhere else. This proxy is that somewhere.
Note the distinction between the two x402 + MCP patterns:
- Server pays upstream — the MCP server holds a wallet and pays third-party APIs. Agents see free tools. No proxy needed.
- Server charges the client — the MCP server sells its own tools. The client must hold a wallet. This is the case this proxy solves.
How it works
It is a transparent forwarder, not an MCP implementation. stdio MCP is newline-delimited JSON-RPC, so each line is POSTed upstream as-is and the reply is written back verbatim. There is no method table to keep in sync — new tools on the upstream server work without touching this code.
Everything x402 (reading the challenge, signing a USDC authorization, retrying)
is handled by x402-reqwest. What this
crate adds is the plumbing MCP needs — session-id propagation, SSE unwrapping —
and a spend cap.
Protocol version
x402 v2 (V2SolanaExactClient), which identifies networks by CAIP-2 chain
id rather than v1's "solana-devnet" name.
All three parties must agree on the version: the upstream server's price tag,
this client, and the facilitator's schemes entry ("v2-solana-exact"). A
mismatch shows up as the client declining to pay a challenge it doesn't
recognise, not as a clear error.
Install
cargo install x402-mcp-proxy
Or from a clone: cargo install --path .
Usage
Create a payer wallet and fund it with devnet USDC:
solana-keygen new --derivation-path -o payer.json
solana address -k payer.json
# fund at https://faucet.circle.com → "Solana Devnet"
--derivation-path matters. Without it the key comes from the raw seed and the
phrase only recovers with the ASK keyword; with it you get the standard
m/44'/501'/0'/0' that Phantom, Solflare and Ledger use, recoverable with
'prompt://?key=0/0'. Pairing the wrong one silently yields a different,
valid-looking, empty address.
Mainnet is the same flow with two changes: fund with real USDC
(EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v), and set
X402_PROXY_RPC=https://api.mainnet-beta.solana.com. It defaults to devnet,
and paying a mainnet challenge against a devnet RPC fails in a way that looks
like a broken server rather than a misconfigured client.
Register with Claude Code:
claude mcp add my-server -- x402-mcp-proxy \
--url https://your-server.example.com/mcp \
--keypair /absolute/path/to/payer.json
All flags have environment-variable equivalents (X402_PROXY_URL,
X402_PROXY_KEYPAIR, X402_PROXY_RPC, X402_PROXY_MAX_PAYMENTS).
Spend cap
The proxy holds a funded wallet and an agent can call tools autonomously, so an agent stuck in a retry loop is a way to drain a wallet unattended.
--max-payments (default 10) bounds this. Once the cap is reached, paid
calls are refused at the proxy with a JSON-RPC error; discovery calls stay
free. --max-payments 0 refuses all paid calls, which is a useful way to see
which calls actually cost money.
The counter tracks settlements, not requests — so free calls, and calls whose work failed upstream (which a correct server declines to settle), don't consume budget.
The cap resets when the proxy restarts. Treat the payer as a hot wallet: fund it with what you're willing to lose, not with your balance.
Wallet backup warning
solana-keygen recover silently returns the wrong address if you use the
wrong derivation. There is no error — it looks exactly like a lost wallet:
Use --derivation-path (as shown above) — that path is m/44'/501'/0'/0',
which is also what Phantom, Solflare, and Ledger use, so the seed phrase stays
portable. Verify recovery reproduces the address before funding the wallet.
Status
Early, but proven end to end. Verified on Solana mainnet with real USDC
against a self-hosted facilitator: payer 1.498953 → 1.298953, merchant
0 → 0.20, settlement fee 0.000010001 SOL (~0.38% of the $0.20 price).
Also verified on devnet against a second, unrelated server
(examples/coinflip-server) to show the proxy isn't coupled to the one it was
written for.
One caveat worth knowing before you point this at mainnet: the public
facilitator.x402.rs settles testnets only — its /supported has no entry
for solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp. Mainnet needs a facilitator that
does, self-hosted or otherwise.
License
Licensed under either of
- MIT license (LICENSE-MIT)
- Apache License, Version 2.0 (LICENSE-APACHE)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.