🚀 New: chi (χ) — an open-source autoresearch harness for fleets of LLM coding agents. Read the announcement.

MCP Goes Stateless: What the 2026-07-28 Spec Changes For Anyone Building Agent Tools

The Model Context Protocol's 2026-07-28 spec drops session IDs for a stateless request/response core. What changed, why it matters for backend engineers, and a runnable demo of the new Multi Round-Trip Request flow.

The Model Context Protocol just shipped its most disruptive spec release yet. MCP 2026-07-28, published by the MCP steering group and adopted into Claude the same week, rips out the initialize/initialized handshake and the Mcp-Session-Id header that every MCP server has depended on since the protocol launched. In their place: a plain request/response model where every call carries its own context. Anthropic confirmed rollout across Claude products in a companion post, and SDKs for TypeScript, Python, Go, and C# already support it, with Rust in beta.

If you have built or run an MCP server, this is not a footnote release. It is the difference between a protocol that needs sticky sessions and shared memory, and one that behaves like any other HTTP API you already know how to scale.

Why this is the boring problem that matters again

MCP servers today mostly look like WebSocket servers wearing an HTTP costume: a client opens a session, the server pins state to that session in memory, and if your load balancer routes the next request to a different instance, the session is gone. That is exactly the constraint that pushed the industry toward stateless REST APIs and JWTs a decade ago, and MCP inherited a milder version of the same problem because it needed a way for servers to push follow-up questions back to the model mid-task. The 2026-07-28 spec solves that without a persistent session, and the mechanism is worth understanding even if you never write an MCP server yourself, because it is a clean answer to “how do you keep an API stateless when the client sometimes needs to ask a follow-up.”

Three changes matter most:

Stateless core. Every request now carries its protocol version, client identity, and capabilities in a _meta field instead of relying on a handshake tied to a session. Any state a tool actually needs, like a shopping cart ID or a paginated cursor, gets minted as an explicit handle that the tool hands back to the model as a plain argument. State becomes something the model can see and reason about, not something hidden in the transport layer. Servers can now sit behind a stock round-robin load balancer with zero shared storage, the same way you would deploy any stateless Django or FastAPI service.

Multi Round-Trip Requests (MRTR). This is the piece that replaces the old server-initiated push. Instead of holding a stream open while it waits for missing input, a tool call now just returns resultType: "input_required" with the questions it needs answered. The client resolves those, then retries the original call with an inputResponses argument attached. No open connection, no session to lose if the client crashes between round trips.

Header-based routing and cacheable lists. Requests carry Mcp-Method and Mcp-Name HTTP headers so gateways and WAFs can route on headers instead of parsing every JSON body, and tools/list, prompts/list, and resources/list responses now carry ttlMs and cacheScope, so clients stop re-fetching a tool catalog that has not changed. Auth got hardened too: RFC 9207 issuer validation before code redemption, and Dynamic Client Registration is deprecated in favor of Client ID Metadata Documents.

Try the MRTR flow below. It is a minimal simulation of a “book a meeting” tool that is missing the attendee’s timezone, one of the more common real reasons a tool call needs a follow-up.

About the demo

This simulates the request/response shapes from the spec; it does not call a real MCP server. Everything runs in your browser.
{
  "method": "tools/call",
  "params": {
    "name": "book_meeting",
    "arguments": { "attendee": "priya@acme.com", "duration_min": 30 }
  },
  "_meta": { "protocolVersion": "2026-07-28" }
}
Waiting for step 1…

No stream stayed open between step one and step two, and no session ID tied the two requests together. The second call is self-contained: same tool name, same original arguments, plus inputResponses. That is the whole trick, and it is why a plain load balancer can now sit in front of an MCP server the way it sits in front of any REST API.

Where I am taking this

I run MCP-adjacent tooling in chi, my multi-agent research harness, and until now every tool server there has quietly assumed one long-lived process per session. My next pass is to strip that assumption out: move any state chi’s tools need into explicit handles passed back through the model, exactly as the spec recommends, so a tool server can be redeployed or scaled horizontally without dropping an in-flight agent run. If you maintain an MCP server today, that is the audit worth doing this week: grep for anywhere you are keying state off a session, and ask whether it should be an argument instead.