Skip to main content
MCP INTEGRATION

MCP server payment integration.

Add USDC payments to any MCP server in 10 minutes. Drop-in middleware for Node and Python. x402-compatible. Works with Claude Desktop, Cursor, Cline.

SHORT ANSWER

Blockchain0x provides drop-in requirePayment middleware for MCP servers. Install @blockchain0x/mcp (Node) or blockchain0x-mcp (Python), wrap your paid tool handlers, and they return 402 Payment Required with a hosted_url until paid. AI clients (or their humans) pay USDC on Base; the next call succeeds. x402-compatible.

WHY MCP NEEDS PAYMENT

MCP makes tool invocation trivial. That is the problem.

MCP made it trivial for AI agents to call external tools. That is the protocol's whole achievement - and it is also the reason MCP servers run up infrastructure costs without revenue. Once your server is listed in a registry or shared in a config snippet, any agent with the right client (Claude Desktop, Cursor, Cline) can call your tools thousands of times a day with zero friction and zero compensation. Your egress, your LLM tokens, your third-party API calls; their free service.

The 402 Payment Required pattern was designed for exactly this shape. The middleware intercepts paid tool calls, returns a structured 402 with a hosted payment URL, and lets the next call through after payment confirms. AI clients that understand x402 pay automatically; clients that do not surface the URL to the user for one-click payment. Either way, your server only does the expensive work after the call has been paid for.

INSTALLATION

One package install. Three environment variables.

Node and Python are first-class. Node targets Node 18+ and the official @modelcontextprotocol/sdk; Python targets 3.10+ and the mcp library. Both expose identical middleware shapes.

INSTALL - NODE
npm install @blockchain0x/mcp
INSTALL - PYTHON
pip install blockchain0x-mcp
ENVIRONMENT VARIABLES
export BLOCKCHAIN0X_API_KEY=sk_live_...
export BLOCKCHAIN0X_AGENT_ID=agt_abc123
export BLOCKCHAIN0X_SIGNING_SECRET=whsec_...

BLOCKCHAIN0X_API_KEY and BLOCKCHAIN0X_AGENT_ID come from the agent's settings page in the Blockchain0x dashboard. BLOCKCHAIN0X_SIGNING_SECRET is needed in the process that handles webhooks. For production deployments where MCP server and webhook are separate processes, both processes need REDIS_URL set so the payment cache is shared.

FULL MCP SERVER EXAMPLE

A working MCP server with paid + free tools.

Below is a complete MCP server in Node with one paid tool (search_docs at $0.02 per call, 60-second cache window) and one free tool (ping). Paid tools are wrapped with requirePayment; free tools are not. Drop into your codebase, set env vars, and your server is monetized.

SERVER.TS
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { requirePayment } from "@blockchain0x/mcp";

const server = new McpServer({ name: "docs-search-mcp", version: "1.0.0" });

server.tool(
  "search_docs",
  "Semantic search across our private documentation",
  { query: { type: "string", description: t("Search query") } },
  requirePayment({
    amountUsdc: "0.02",
    reason: "docs search",
    cacheWindowSeconds: 60,
  })(async ({ query }) => {
    const results = await runVectorSearch(query);
    return { content: [{ type: "text", text: JSON.stringify(results) }] };
  }),
);

// Free tools work alongside paid ones
server.tool(
  "ping",
  "Health check",
  {},
  async () => ({ content: [{ type: "text", text: "pong" }] }),
);

await server.connect(new StdioServerTransport());

When an AI client calls search_docs without prior payment, the middleware intercepts, calls our API to create a payment request, and returns 402 with the hosted_url in the standard MCP error shape. The client surfaces the URL (Claude Desktop shows it as a clickable link; programmatic x402 clients pay automatically). The payment confirms, the cache updates via webhook, and the next call to search_docs goes through to your handler.

WEBHOOK HANDLING

Webhook updates the shared payment cache.

The webhook handler receives a signed POST when the chain confirms payment, verifies the signature, and writes the (client_address, tool_name) pair into the shared cache. The MCP server reads from the same cache on each tool call to decide whether to charge or pass through.

WEBHOOK.TS
import express from "express";
import { verifyWebhook, recordPayment } from "@blockchain0x/mcp";

const app = express();

app.post(
  "/webhooks/payment",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    const signature = req.header("X-Blockchain0x-Signature") ?? "";
    if (!verifyWebhook(req.body, signature, process.env.BLOCKCHAIN0X_SIGNING_SECRET!)) {
      return res.status(401).send("Invalid signature");
    }
    const event = JSON.parse(req.body.toString());
    if (event.type === "payment.confirmed") {
      // Mark the (client_address, tool_name) pair paid in the shared cache
      await recordPayment({
        clientAddress: event.data.payer_address,
        toolName: event.data.reason,
        confirmedAt: event.data.confirmed_at,
      });
    }
    res.status(200).send("ok");
  },
);

verifyWebhook does HMAC-SHA256 in constant time. recordPayment writes the cache entry that the MCP server will check on subsequent calls. In production, configure REDIS_URL on both the MCP server process and the webhook handler so the cache is shared; with in-memory only, every tool call after a payment still returns 402 because the MCP process never saw the cache update.

STARTER REPOSITORY

Working examples for stdio and HTTP transports.

A complete MCP server repository at the GitHub link below. Includes the server example above, an HTTP-transport variant for browser-based clients, a Python equivalent, the Express webhook handler, a Redis-backed shared cache, and a docker-compose stack with all four services.

github.com/blockchain0x/agent-wallet-mcp-server

Repository structure: stdio-server.ts (Node + stdio transport for Claude Desktop / Cursor / Cline), http-server.ts (Node + HTTP transport for web clients), python/server.py (Python equivalent), webhook.ts (Express), docker-compose.yml (full stack with Redis), README walking through installation in Claude Desktop and deployment to Fly.io.

COMMON PITFALLS

Five MCP-specific traps to avoid.

These come from operators running paid MCP servers in production. Most relate to the cache architecture and the MCP protocol's specific error format.

PITFALL 1

Shared cache between MCP server and webhook handler

The middleware records paid (client, tool) pairs in a cache; the webhook handler updates the cache when a payment confirms. If the MCP server and webhook handler are different processes, they need to share the cache (Redis is the recommended store). Using in-memory cache between two processes will cause every call after a payment to still return 402 because the MCP process never saw the cache update. Configure REDIS_URL on both processes; the SDK handles the rest.

PITFALL 2

Stdio transport vs HTTP transport

Claude Desktop, Cursor, and Cline use stdio transport (the MCP server is a subprocess). Other clients (browser extensions, web UIs) use HTTP transport. Both transports work with requirePayment, but the deployment differs: stdio servers ship as installable packages or binaries; HTTP servers are long-running web services. The starter repo includes templates for both. Most paid MCP servers are HTTP for hosting reasons.

PITFALL 3

Tool naming consistency between cache and webhook

The cache uses the (client_address, tool_name) pair as the lookup key. The tool_name in the cache is what you put in the requirePayment reason field (default: the MCP tool name). If you set reason to something different from the tool name, the webhook's recordPayment call must use the same string. Mismatches cause clients to be charged but their payments never satisfy the cache for any tool. Keep reason equal to the tool name unless you have a specific reason to differ.

PITFALL 4

MCP protocol-level error format

MCP tool calls expect errors in a specific JSON-RPC shape: { content: [{ type: 'text', text: '...' }], isError: true }. The middleware returns 402 responses in this shape automatically, but if you wrap requirePayment with your own error handler, make sure your handler preserves the isError flag. Without it, the MCP client treats your 402 as a successful response with the payment URL as a regular result, which confuses the LLM.

PITFALL 5

Caching window per tool, not per server

Each requirePayment() call has its own cacheWindowSeconds. If you have 5 paid tools all configured with 60 seconds, a payment to tool A does not grant access to tool B - each tool is independently paid. Some operators get this wrong on first integration and assume a payment to any tool unlocks all of them. To explicitly grant cross-tool access, share a scope by passing the same cacheKey: 'docs' (instead of per-tool default) to all tools that should share payments.

FREQUENTLY ASKED

Three MCP-specific questions.

Does this work with Claude Desktop, Cursor, and Cline out of the box?

Yes. All three are standard MCP clients using stdio transport. Your paid MCP server, once deployed (e.g. as an npm package or installable binary), gets configured in the client's settings JSON like any other MCP server. When the client invokes a paid tool, the user (or the agent) sees a 402 response containing the hosted_url. Claude Desktop and Cursor surface this as a clickable link in the chat; the user pays and the next call succeeds. Cline behavior is similar with a slightly different surfacing.

How does this interact with MCP's resources and prompts (not just tools)?

MCP servers expose three primitive types: tools, resources, and prompts. The middleware gates tools (requirePayment wraps a tool handler). For resources, use requirePaidResource which gates resource access behind the same payment flow. Prompts are typically free and used for client-side prompt templating, so we do not provide a paid-prompt wrapper. If you have a use case where prompts should be paid, contact us - we can add that.

Can paid MCP servers be listed in public MCP server registries?

Yes. Public registries (the MCP server directory, GitHub topics like #mcp-server) list both free and paid servers. The fact that your server is paid is part of its public description. Some registries flag paid servers visually, which is fine - if anything, the flag helps the right clients find your server. We do not have a private registry; the open MCP ecosystem is the discovery surface.

Monetize your MCP server.

Ten minutes from npm install to your first paid tool call. Pro at $9/agent/month.