DEV Community

minia2a
minia2a

Posted on

MCP 2026-07-28 Went Stateless — and Still Has No Way to Charge for a Call

MCP 2026-07-28 Went Stateless — and Still Has No Way to Charge for a Call

The Model Context Protocol's July 28 revision is the biggest architectural change since launch. It is not an incremental bump — it's a rewrite of the protocol's core assumption. And yet the one thing server authors keep asking for — a way to get paid — is still, deliberately, not there.

This post is two things: an accurate map of what actually changed (a lot of the summaries floating around get it wrong), and a concrete pattern for the payment gap, using HTTP 402.

What actually shipped: the protocol went stateless

The headline is statelessness. Prior revisions assumed a long-lived session: client calls initialize, server returns a session ID, both sides keep per-session state. The 2026-07-28 revision removes that assumption.

Change What it means
initialize handshake removed No session ID. Each request carries its own version + capabilities in _meta.io.modelcontextprotocol/{protocolVersion, clientInfo, clientCapabilities}
New server/discover RPC Clients learn supported versions and capabilities up front; SDKs fall back to legacy initialize for old servers
Stateless-only Streamable HTTP The streamable transport accepts 2026-07-28 only in stateless mode; stateful sessions negotiate down to 2025-11-25
Removed methods ping, logging/setLevel, resources/subscribe, resources/unsubscribe, and SSE resumability (Last-Event-ID, standalone GET) are gone — they reject with MethodNotFound
MRTR (SEP-2322) Multi-round-trip requests replace holding an SSE stream open. A server returns InputRequiredResult (resultType: "inputRequired") with an opaque requestState; the client re-issues the request with inputResponses + that same requestState. Any replica can pick up the retry — that's the point
Header routing (SEP-2243) Every Streamable HTTP POST carries Mcp-Method (mirroring the JSON-RPC method), plus Mcp-Name for tools/call, resources/read, prompts/get. Intermediaries can route without parsing bodies
Cacheable lists (SEP-2549) tools/list, prompts/list, resources/list carry ttlMs and cacheScope freshness hints
Real HTTP errors Transport failures return actual status codes instead of HTTP 200 with an in-body JSON-RPC error: unknown method → 404, unsupported version → 400 (-32022), missing capability → -32021

Equally important is what got deprecated: roots, sampling, and logging (SEP-2577), plus the legacy HTTP+SSE transport and Dynamic Client Registration (replaced by Client ID Metadata Documents). A lot of the "MCP added OAuth DCR and roots!" summaries have it exactly backwards — those were the old model, now on the way out.

The through-line: the protocol is being rebuilt so that any request can be routed to any replica, with no shared session state. That's a property you want if you're running an MCP gateway, a load balancer, or a fleet of servers behind one endpoint.

What did not change: there's still no payment layer

Search the spec for a way to charge for a tools/call and you won't find one. This is a deliberate design decision, not an omission. MCP solves discovery (what tools exist) and capability (what they do). Commerce — who owes whom — is a different protocol's job, and the maintainers have been consistent that it's out of scope.

The reasoning is sound. A tool-integration protocol should not bake in a pricing model, a settlement rail, or a currency. Those things rot fast. But the decision leaves a concrete problem for anyone running a non-free MCP server: when an agent calls your tool, how do you get paid?

The ecosystem's answer is converging on HTTP 402 Payment Required — the status code that's been reserved since 1997 and is finally being used for its intended purpose. The standard that operationalizes it is x402, which is now under the Linux Foundation.

How HTTP 402 gates an MCP server

The mechanism is refreshingly small. When a request arrives without proof of payment, you return 402 with payment instructions in the WWW-Authenticate header. The caller pays, retries with proof, and you verify. One request cycle, no account system.

POST /mcp HTTP/1.1
Content-Type: application/json
Mcp-Method: tools/call
Mcp-Name: lookup_contract

HTTP/1.1 402 Payment Required
WWW-Authenticate: Payment version="1.0", asset="USDC", chain="base", receiver="0x…", price="0.01"
Content-Type: application/json

{"error": "payment required", "price": "0.01 USDC"}
Enter fullscreen mode Exit fullscreen mode

A working Express middleware for the gate — placed before your tools/call handler:

app.use('/mcp', async (req, res, next) => {
  const isToolCall = req.headers['mcp-method'] === 'tools/call';
  if (!isToolCall) return next();

  const proof = req.headers['x-payment-proof'];

  if (!proof) {
    res.status(402).set({
      'WWW-Authenticate':
        'Payment version="1.0", asset="USDC", chain="base", ' +
        'receiver="<YOUR-RECEIVER>", price="0.01"',
    });
    return res.json({ error: 'payment required', price: '0.01 USDC' });
  }

  // Verify the proof against the facilitator that settled it.
  const ok = await verifyPayment(proof);
  if (!ok) return res.status(402).json({ error: 'payment invalid' });

  next();
});
Enter fullscreen mode Exit fullscreen mode

Two details worth getting right in production:

  1. Mcp-Method: tools/call is now a routing header (SEP-2243). You can gate only paid tools by matching on it, rather than intercepting every POST and inspecting the JSON-RPC body. Free tools (Mcp-Method: tools/list, etc.) pass straight through.
  2. Verification is delegated to a facilitator. You don't need to parse the chain or manage USDC custody yourself. The payment proof is a receipt a facilitator issued; you POST it back to the facilitator's verify endpoint and get a yes/no. Your server stays a stateless replica — which is exactly the property 2026-07-28 just worked to give you.

The stateless rewrite is a gift to paid endpoints

This is the part that connects the two halves. Payment gating and statelessness reinforce each other:

  • A 402 + retry pattern is naturally idempotent. If your server is a stateless replica, a client can hit any replica with the same paid request and get the same result. No session affinity to fight.
  • MRTR gives you requestState tokens, so a multi-step paid interaction (request → pay → confirm → result) can span replicas without sticky sessions.
  • Real HTTP status codes mean a 402 is a 402, not a JSON-RPC error wrapped in HTTP 200. Agents and gateways can branch on it cheaply.

If you're building a non-free MCP server right now, the stateless model removes most of the infrastructure reasons you couldn't charge before.

The other paths (and why they're complementary, not competitors)

HTTP 402 is not the only answer, and it's worth knowing the map:

  • MPP (Machine Payments Protocol) — a second standard (Stripe + Tempo + Visa) that also uses HTTP 402 and adds session/subscription semantics on top. Backward-compatible with x402 at the 402-challenge level.
  • MCP Billing Spec v1 — a community draft (noui.bot) that layers billing metadata over MCP's _meta namespace, closer to the tool protocol itself.
  • AWS AgentCore — wraps MCP servers behind a gateway that adds auth and metering; payment is handled by the platform, not the server.

None of these are in the MCP core spec, and none of them should be. The clean split — MCP for tools, 402 for payment, a facilitator for settlement — is the shape that's actually stabilizing.

Bottom line

MCP 2026-07-28 is a protocol-level bet that agents will talk to stateless fleets of tools, not stateful single servers. It removed the session assumption, standardized routing and multi-round-trip interaction, and got rid of the things that tied a server to a single machine.

It did not add payments — and that's correct. The payment layer lives one status code below the tool protocol. If you run an MCP server that should earn money, the path is: return 402 with payment instructions, verify the receipt through a facilitator, and let the stateless architecture you're now building on do the rest.

Top comments (0)