Blog, by , , 8 min read

Paid MCP tools: how x402 over MCP works

How an MCP tool asks for payment with x402, where the price and the receipt travel inside tools/call, and how to charge for tools or pay for them.

8min read
On this page
  1. The loop in six steps
  2. Step 2: the price arrives as a tool result, not a JSON-RPC error
  3. Step 4: the payment rides in _meta
  4. Step 6: the receipt comes back in _meta too
  5. Charging for a tool
  6. Paying for a tool
  7. Frequently asked questions

Short answer: a paid MCP tool runs the same loop as a paid HTTP endpoint, carried in MCP's own messages. The first tools/call without payment returns a tool result with isError: true whose content is an x402 PaymentRequired object, the MCP equivalent of HTTP 402. The client signs a payment and retries the same call with the payload in _meta["x402/payment"]. The server verifies, runs the tool, settles, and returns the result with the receipt in _meta["x402/payment-response"]. There is no HTTP 402, no extra endpoint and no API key; a client that can't pay gets the price instead of the result and is not charged. ToolVet's own MCP server works this way.

Some background: x402 lets a client, usually an AI agent, pay for an HTTP request in stablecoins. The Model Context Protocol is how agents discover and call tools on remote servers, and it isn't HTTP-shaped: a tool call is a JSON-RPC request inside a transport, and the response is a result object, not a status code. So the x402 authors wrote a separate transport spec for MCP that maps each step of the payment loop onto MCP's result and _meta fields. As of 18 October 2026, ToolVet lists 28,955 remote MCP servers next to 51,684 paid x402 APIs (data); the paid tools among those servers are what this post is about, as the transport spec reads on that date.

The loop in six steps

The MCP transport spec describes the flow as a numbered list:

  1. The client calls a paid tool without payment.
  2. The server returns a tool result with isError: true and PaymentRequired data.
  3. The client reads the payment requirements and creates a PaymentPayload.
  4. The client retries the tool call with the payment in _meta["x402/payment"].
  5. The server verifies the payment, executes the tool and settles the payment.
  6. The server returns the tool result with settlement information in _meta["x402/payment-response"].

The objects are the same as over HTTP; only their envelope changes.

Step 2: the price arrives as a tool result, not a JSON-RPC error

Payment required is not a JSON-RPC error with a numeric code; the spec never uses a JSON-RPC error for it. It is an ordinary tool result flagged with isError: true, and the spec says servers must put the PaymentRequired object in two places at once: in structuredContent as the object itself, and in content[0].text as the same object JSON-encoded. Clients should prefer structuredContent when available (the spec's sample checks it for x402Version and accepts) and fall back to parsing content[0].text.

The object is the familiar x402 v2 challenge: x402Version (2), error, a resource with url, description and mimeType, and accepts, an array of payment options with scheme, network, amount, asset, payTo, maxTimeoutSeconds and extra. The spec's example uses exact as the scheme, eip155:84532 (Base Sepolia) as the network, "10000" as the amount, which is 0.01 USDC in atomic units, and mcp://tool/financial_analysis as the resource URL. ToolVet puts its own MCP URL there instead, so a client knows which server asked.

Two consequences follow from "it's just a tool result". A model can read it: an agent whose MCP client can't pay doesn't crash; it sees a text result saying what the tool costs, which is why ToolVet's paid tools "reply with their price" to clients without a wallet, as the agents page puts it, while the free tools (list_categories, get_service, get_service_status, scan_mcp_server) answer anyone. And nothing is charged at this step: no payment has been signed, the challenge is a quote.

Step 4: the payment rides in _meta

The retry is a tools/call with the same tool name and arguments plus one _meta key, x402/payment, holding a PaymentPayload: x402Version, the resource, the accepted option the client chose, and a payload with the signature and the signed authorization (from, to, value, validAfter, validBefore, nonce). With the exact scheme on EVM chains that authorization is an EIP-3009 transferWithAuthorization, and the scheme spec says the facilitator "cannot modify the amount or destination" and serves "only as the transaction broadcaster".

A note on the key name. MCP's 2026-07-28 specification defines a format for _meta keys: an optional prefix of dot-separated labels ending in a slash, then a name, and it says implementations should use reverse-DNS prefixes such as com.example/. Prefixes whose second label is modelcontextprotocol or mcp are reserved. x402/payment has a single-label prefix, which the format allows; it just isn't reverse-DNS. If you write your own client, treat the key as a literal string from the x402 spec; don't rewrite it into reverse-DNS form or validate it against your own prefix rules.

Step 6: the receipt comes back in _meta too

On success the tool result is the tool's normal content, plus _meta["x402/payment-response"] with a SettlementResponse: success, transaction (the on-chain hash), network and payer. Log it with every paid call; it is the receipt, the same way the PAYMENT-RESPONSE header is over HTTP.

The spec handles three failures, and all three use the same shape, a tool result with isError: true:

Case What comes back
No payment attached PaymentRequired
Verification failed PaymentRequired with a reason
Settlement failed after the tool ran A payment error; the spec says the server "should not return the tool's content - only the payment error"

That last row matters for server authors: the tool executes before settlement, so a tool with side effects (sending an email, placing an order) has already acted when settlement fails. Keep paid tools idempotent or deliver their effect only after a successful settlement. ToolVet's own server runs settlement after the handler, and a failed payment turns the result into an error; the agents page states the rule from the buyer's side: you are charged only for successful responses. What to do when settlement fails or hangs covers the facilitator side.

Charging for a tool

With the official TypeScript packages, a paid tool is a wrapped handler. Coinbase's seller guide for MCP uses x402ResourceServer from @x402/core/server, createPaymentWrapper from @x402/mcp, the ExactEvmScheme, and a facilitator client, then wraps each handler and registers it with mcpServer.tool(...). Python has the same pieces as x402[evm,mcp] and a @paid decorator. The price is a string such as "$0.01", and payTo is any address that can receive the asset. The guide also notes that the CDP SDK's createX402Server helper is HTTP-only, so for MCP you build the resource server yourself.

ToolVet does exactly this: one createPaymentWrapper per paid tool, each with its own price, resource description and discovery extension, so a catalog that reads the challenge sees what the tool does and what it costs. If you run on Cloudflare, the Agents SDK folds the same loop into withX402(new McpServer(...), config) and a paidTool(name, description, price, inputSchema, annotations, handler) method, with the price as a number in USD (0.01), that sits next to the free tool(...); free and paid tools can share a server.

Whichever you use, the facilitator question is the same as for HTTP endpoints. The facilitator comparison has the fees, and ToolVet's facilitator status pages show whether each one's /supported endpoint is answering right now.

Paying for a tool

On the client side, Coinbase's buyer guide wraps any MCP SDK Client with wrapMCPClientWithPayment from @x402/mcp. Every method other than the tool call passes through unchanged. The autoPayment option decides what happens on a payment-required result: true, the default, pays and retries by itself; false throws so your code can look at the price first. Python's create_x402_mcp_client(payment, url) does the same. Spend controls apply as they do for HTTP: by default the reference client only pays recognized USD-pegged assets and caps each payment at $1 (buyer quickstart), and those controls run before anything is signed.

Three things to check before you let autoPayment pay for you:

  • Read the challenge once by hand. Call the tool without payment, print structuredContent, and confirm the network, the asset and the amount are what the listing promised. The checks in our challenge post apply unchanged.
  • Know what you're connecting to. A paid tool is still a tool definition the model reads. Run the server's URL through the free MCP security scanner before you connect it, for the reasons in our tool-poisoning scan.
  • Separate the wallet from the agent's judgment. The agent decides whether to call a tool; the spend controls decide how much it may pay. Set maxAmountPerPayment near the prices you expect, not at the default.

If you don't want to run a paying MCP client at all, ToolVet's HTTP API is the other route: GET /api/v1/search with free=1 gives 10 searches a day per IP address without a wallet, and the paid calls use plain x402 headers, which the toolvet npm package wires into the Vercel AI SDK, LangChain and the OpenAI Agents SDK (agents page).

Frequently asked questions

How does an MCP server charge for a tool call?

With x402 over MCP, the server answers an unpaid tools/call with a tool result marked isError: true that contains an x402 PaymentRequired object in structuredContent and in content[0].text. The client signs a payment for one of the accepts options and retries the same call with the payload in _meta["x402/payment"]. The server verifies and settles the payment through a facilitator and returns the result with the receipt in _meta["x402/payment-response"].

Is a paid MCP tool a JSON-RPC error?

No. Payment required is a normal tool result flagged with isError: true, so a model or a client without a wallet can read the price as text. The x402 MCP transport spec never uses a JSON-RPC error for it.

Can Claude or Cursor pay for MCP tools?

An MCP client pays only if it has an x402 wallet wired in, for example through wrapMCPClientWithPayment from @x402/mcp in your own code. Desktop clients without a wallet get the free tools and see the price of the paid ones. That is why ToolVet keeps list_categories, get_service, get_service_status and scan_mcp_server free and gives search_services a free daily allowance.

What happens if settlement fails after the tool already ran?

The transport spec says the server should return only the payment error, not the tool's content. Since the tool has executed by then, server authors should keep paid tools idempotent or apply their side effects only after settlement succeeds, and clients should log every x402/payment-response so a disputed call can be checked on-chain.

Does ToolVet list paid MCP servers?

Yes. ToolVet lists remote MCP servers and checks each one on a schedule by running the handshake and fetching its tool list; servers that answer with a payment or sign-in request count as reachable. How we check and score explains the checks and the trust score.