{"slug": "i-put-trading-tools-behind-mcp-with-per-call-pricing-and-fixed-the-one-bug-that", "title": "I Put Trading Tools Behind MCP With Per-Call Pricing — and Fixed the One Bug That Breaks x402 for MCP Builders", "summary": "ScriptMasterLabs shipped a paid MCP gateway where tools/list is free and tools/call costs $0.05 in USDC per call, settled via x402 v2 on Base with no signup or API keys. The developer reports that MCP clients swallow JSON-RPC errors before the model sees them, so the payment challenge never reaches the agent; the fix is to return the challenge as a successful result with isError: true and the x402 accepts array in structuredContent. The gateway exposes trading and telemetry tools (get_market_cycle, scrape_url) with fail-closed commerce, returning no-charge errors on failed delivery.", "body_md": "I shipped a paid MCP gateway today: `tools/list` is free, `tools/call` costs $0.05 in USDC per call, settled via x402 v2 on Base. It's live right now — no signup, no API keys. The payment is the credential.\n\nBut the real story isn't the pricing. It's the implementation detail that every MCP builder attempting x402 will hit — and that quietly breaks the entire payment flow if you miss it.\n\nHere's what happens when you bolt x402 onto an MCP server the naive way. The agent calls `tools/call` without payment. Your server returns a JSON-RPC error: `-32000, Payment Required`. Done, right?\n\nWrong. **MCP clients swallow JSON-RPC errors before the model ever sees them.** The client sees an error code, the model sees \"tool call failed,\" and the payment challenge — the `accepts` array, the price, the `payTo` — never reaches the agent. The agent can't pay because it never learned the price.\n\nThis is documented by anchor-x402 (18 paid x402 services, MIT-licensed), and I hit it myself. The fix is simple but non-obvious:\n\n**Return the payment challenge as a successful result with `isError: true`, not as a JSON-RPC error.**\n\n```\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 3,\n  \"result\": {\n    \"content\": [{\n      \"type\": \"text\",\n      \"text\": \"Payment required: $0.05 USDC. Sign and retry with PAYMENT-SIGNATURE.\"\n    }],\n    \"structuredContent\": {\n      \"x402Version\": 2,\n      \"accepts\": [{\n        \"scheme\": \"exact\",\n        \"network\": \"eip155:8453\",\n        \"amount\": \"50000\",\n        \"asset\": \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\",\n        \"payTo\": \"0xc29185fa176357612f3194735753e520e91adc46\"\n      }]\n    },\n    \"isError\": true\n  }\n}\n```\n\nThe agent model reads this, sees the price and the challenge, signs an EIP-3009 `transferWithAuthorization`, and retries. The flow works end-to-end. `isError: true` means the client passes it through to the model instead of eating it.\n\nThe second design decision I copied from anchor-x402: **`tools/list` and discovery are always free.** This is correct by construction — agents can't discover what they can't see. Charging for `tools/list` is a paywall on your own storefront.\n\nThe full pattern:\n\n`tools/list` → free, always`tools/call` → paid, `isError: true` result on unpaid attempts`/.well-known/x402` → free, machine-readable manifest\nEvery byte of actual data sits behind a 402. Everything the agent needs to *decide* to buy is free. That's the whole distribution model for machine commerce.\n\nThe x402 MCP space is filling fast at the utility layer: converters, validators, formatters at $0.001–$0.02/call (anchor-x402, agent402). Real numbers from the wild: an MCPize author reports ~$8,500/mo net on a security-auditor server; self-hosted x402 did 3.3M transactions in 30 days at ~$0.46 average.\n\nBut **nobody does trading or telemetry MCP tools.** That's the lane I took:\n\n`get_market_cycle`` scrape_url`\nBoth follow fail-closed commerce: bot-blocked pages, bad symbols, garbled output return no-charge errors. **Failed delivery never earns payment.** Firecrawl's charge-on-4xx is a cited complaint in the ecosystem; I went the other way.\n\n```\n# Free: list the tools\ncurl -X POST https://squeezeos-api.onrender.com/v1/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\",\"params\":{}}'\n\n# Paid: this returns the challenge as an isError result\ncurl -X POST https://squeezeos-api.onrender.com/v1/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/call\",\n       \"params\":{\"name\":\"get_market_cycle\",\"arguments\":{\"symbol\":\"AAPL\"}}}'\n```\n\nThe manifest is at `https://squeezeos-api.onrender.com/.well-known/x402`. x402 v2, USDC on Base, CDP facilitator sponsors gas.\n\nIf you're putting x402 payments on your MCP server: **never return the challenge as a JSON-RPC error.** `isError: true` result, challenge in `structuredContent`. That's the difference between a payment flow that works and one that silently dies in the client.\n\nBuilt by ScriptMasterLabs (service-disabled veteran-owned small business). Happy to answer questions about the x402 integration or the verify→execute→accept→settle flow.", "url": "https://wpnews.pro/news/i-put-trading-tools-behind-mcp-with-per-call-pricing-and-fixed-the-one-bug-that", "canonical_source": "https://dev.to/scriptmasterlabs01/i-put-trading-tools-behind-mcp-with-per-call-pricing-and-fixed-the-one-bug-that-breaks-x402-for-1956", "published_at": "2026-10-02 19:58:46+00:00", "updated_at": "2026-10-02 20:07:32.861307+00:00", "lang": "en", "topics": ["ai-agents", "agent-protocols", "ai-tools", "ai-infrastructure"], "entities": ["ScriptMasterLabs", "MCP", "x402", "Base", "USDC", "anchor-x402", "agent402", "MCPize"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/i-put-trading-tools-behind-mcp-with-per-call-pricing-and-fixed-the-one-bug-that", "markdown": "https://wpnews.pro/news/i-put-trading-tools-behind-mcp-with-per-call-pricing-and-fixed-the-one-bug-that.md", "text": "https://wpnews.pro/news/i-put-trading-tools-behind-mcp-with-per-call-pricing-and-fixed-the-one-bug-that.txt", "jsonld": "https://wpnews.pro/news/i-put-trading-tools-behind-mcp-with-per-call-pricing-and-fixed-the-one-bug-that.jsonld"}}