Documentation Index
Fetch the complete documentation index at: https://docs.faremeter.xyz/llms.txt
Use this file to discover all available pages before exploring further.
Most MCP clients (Claude Desktop, Cursor, etc.) don’t have x402 payment support built in. This recipe creates a local proxy that sits between your MCP client and a paywalled MCP server, automatically handling payments on each tool call.
How it works
MCP Client → Local Proxy (:8402) → Paywalled MCP Server
(pays via x402)
The proxy intercepts outgoing requests and routes them along two paths:
- Free path — protocol-level MCP methods (
initialize, tools/list, prompts/list, resources/list) are forwarded directly using plain fetch. These are handshake/discovery calls that never cost anything.
- Paid path — everything else (
tools/call, prompts/get, resources/read, etc.) is sent through payer.fetch, which automatically detects a 402 Payment Required response, settles the x402 payment on-chain, and retries the request with proof of payment.
Installation
pnpm add express dotenv @faremeter/rides @faremeter/logs
The proxy
import "dotenv/config"
import express from "express"
import { payer } from "@faremeter/rides"
import { getLogger } from "@faremeter/logs"
const logger = await getLogger(["mcp-proxy"])
await payer.addLocalWallet(process.env.PAYER_KEYPAIR_PATH)
const app = express()
app.use(express.json())
const MCP_SERVER_URL = process.env.MCP_SERVER_URL ?? "https://your-mcp-server.com/mcp"
// ── Free path ────────────────────────────────────────────────
// Protocol-level methods: forwarded with plain fetch (no payment)
const FREE_METHODS = ["initialize", "tools/list", "prompts/list", "resources/list"]
// ── Paid path ────────────────────────────────────────────────
// Everything else (tools/call, prompts/get, resources/read, …)
// goes through payer.fetch, which handles the 402 → pay → retry flow
app.all("/mcp", async (req, res) => {
const method = req.body?.method
const shouldPay = !FREE_METHODS.includes(method)
logger.info(`${method} → ${shouldPay ? "paying" : "free"}`)
// Free methods use plain fetch; paid methods use payer.fetch
const fetchFn = shouldPay ? payer.fetch : fetch
const response = await fetchFn(MCP_SERVER_URL, {
method: req.method,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(req.body),
})
const data = await response.text()
res.status(response.status).send(data)
})
app.listen(8402, () => {
logger.info("MCP proxy running on http://localhost:8402/mcp")
})
What each path looks like
Running it
PAYER_KEYPAIR_PATH=~/.config/solana/id.json \
MCP_SERVER_URL=https://your-mcp-server.com/mcp \
pnpm tsx mcp-proxy.ts
Point your MCP client to http://localhost:8402/mcp — the proxy handles payments transparently.
Claude Desktop configuration
Add the proxy as a custom MCP server in your Claude Desktop config:
{
"mcpServers": {
"paid-api": {
"url": "http://localhost:8402/mcp"
}
}
}