Tomorrow, July 28th, 2026, the Model Context Protocol maintainers finalize what Anthropic's David Soria Parra called "the most substantial changes we have made to the specification, probably since adding authorization." The Register's writeup is a good summary of what's landing, but the more interesting story is why — and it's a story every infra team building on MCP servers should understand before they get surprised by a breaking client.

The short version: MCP is giving up the thing it was originally built around — a stateful, per-client session — in favor of looking a lot more like a stateless HTTP API. If you've ever scaled a stateful service behind a load balancer, you already know why this had to happen.


The Problem: MCP Was Designed for One Client, One Server

MCP shipped in late 2024 as a protocol for connecting an AI client — Claude Desktop, an IDE, a coding agent — to a tool server running on the same machine or close to it. That context matters. The original spec assumed a session: a client connects, negotiates capabilities, and the server holds state about who it's talking to and what it's allowed to do for the lifetime of that connection.

That model works fine when "the server" is a Python process running on your laptop. It stops working the moment "the server" is a fleet of pods behind a load balancer serving thousands of concurrent enterprise clients — which is exactly where MCP ended up. By the time it was donated to the Agentic AI Foundation in 2025, MCP was seeing 97 million-plus downloads a month with over 10,000 servers in the wild. That's not a "runs on your laptop" adoption curve anymore.

Stateful sessions and horizontal scaling don't mix well. A session pins a client to whichever server instance it first negotiated capabilities with — the classic sticky-session problem. Add more instances behind a load balancer and you either need session affinity (which undermines the point of load balancing) or a shared session store (which is one more piece of stateful infrastructure to run, secure, and keep consistent). Neither is what you want when the whole reason you're scaling out is to not think about individual server instances anymore.

The Fix: Push State Onto the Wire, Not Into the Server

The core move in the July 28th revision is deceptively simple: stop tracking sessions at the protocol level. Instead of a server remembering "client X negotiated capability Y three requests ago," every request carries what it needs — protocol version, client identity, capabilities — in a _meta parameter.

What Moved: Server Memory → Wire Protocol

Old: state in the server
session.clientId
session.capabilities
session.authContext
session.rateLimit
New: state in the request
request._meta.clientId
request._meta.capabilities
request._meta.authContext
request._meta.rateLimitKey

Parra's framing is the whole idea in one line: this moves "state away from the server onto the wire protocol." Each request becomes self-describing and independently handleable. Any server instance behind the load balancer can pick up any request without needing to have seen the client before. That's not a novel pattern — it's exactly how Claude's own Messages API works, and exactly how you'd design any service you expect to run at cloud scale. MCP is just catching up to conventions that REST APIs settled a decade ago.

The routing story changes too. Previously, everything — including the operation itself — was buried inside a JSON-RPC payload sent over HTTP POST, which meant a load balancer or gateway couldn't route intelligently without parsing the body. The revised spec mirrors routing-relevant information into HTTP headers, so infrastructure can route requests without inspecting JSON-RPC internals or tracking any session state at all. If you've built an API gateway in front of anything else, this is a familiar and welcome change.

Stateful vs Stateless MCP Session

What You Actually Gain From This

None of this is change for its own sake. Going stateless buys you a specific, concrete set of properties that stateful protocols structurally can't offer:

  • Trivial horizontal scaling. Any server instance can answer any request. No sticky routing, no session affinity rules in the load balancer, no shared session store to keep consistent across replicas. Scaling out is just "add more instances" again, instead of "add more instances and now go solve session distribution."
  • No more sticky-session failure mode. When a session-holding instance dies, restarts, or gets rescheduled by an orchestrator, every client pinned to it used to lose state mid-conversation. Stateless requests don't have that failure mode at all — a request either succeeds or it doesn't, and the next one doesn't care which instance handled the last one.
  • Simpler infrastructure, full stop. No session store, no session expiry/cleanup logic, no distributed session replication across regions. That's a whole category of operational surface area — and a whole category of bugs — that stops existing.
  • Real infrastructure-level routing. With routing-relevant data mirrored into HTTP headers instead of buried in a JSON-RPC body, standard gateways, API management layers, and load balancers can make routing decisions the way they already do for every other HTTP API — without special-casing MCP traffic or parsing request bodies to figure out where a request should go.
  • Cleaner multi-tenant isolation. Per-request identity via _meta means auth and tenant context travel with the request itself rather than living in server-side session memory. That's a smaller blast radius if something goes wrong, and it maps naturally onto how most auth systems (bearer tokens, short-lived credentials) already work.
  • Easier debugging and reasoning. A stateless request is fully explained by its own contents — no need to reconstruct "what did this session negotiate three requests ago" to understand why a call behaved the way it did. That's a real win for anyone who's had to debug a stuck or corrupted session state at 2am.
  • A smaller, more honest core protocol. Retiring sampling, roots, and verbose logging alongside the statelessness shift isn't incidental — it's the same instinct applied twice: keep only what's proven to earn its complexity, push everything else to extensions or existing tooling that already does it better.

Put together, this is MCP trading "convenient on day one" for "operable at day 1,000." That trade almost always looks unnecessary right up until the moment you're the one holding the pager for a service that won't scale past a few thousand concurrent sessions.

What's Getting Cut, and Why It's Not a Loss

Three features are being deprecated or downgraded, and all three read like the maintainers being honest about what actually got adopted versus what sounded good on paper:

  • Sampling — letting a server ask the client's model to generate a completion. Confusing semantics, minimal real-world adoption. Cut.
  • Roots — a client telling the server which filesystem paths are relevant. Described plainly as "a very niche thing." Cut.
  • Logging — protocol-level logging that turned out to be excessive verbosity nobody needed once stderr, stdio, and OpenTelemetry already do this job better.

None of this is really a subtraction. It's MCP admitting that a chunk of its original surface area was speculative design that never earned its keep, and choosing to remove it rather than let it calcify into permanent baggage. Deprecated features get a 12-month grace period before they stop working entirely — generous, but not infinite.

The bigger structural change is that MCP is explicitly no longer trying to grow its core to cover every use case. Domain-specific needs now go into extensions with independent release cycles — MCP Apps (interactive JS-based UI beyond plain text/images) and a Tasks extension (long-running operation management, pulled out of core) are the first two official ones. That's a healthier shape for a protocol at this stage: a small, stable core plus an extension surface that can move faster and fail cheaper than the core ever could.

The Part That Actually Matters to You: This Breaks Things

This is not additive. Stacklok's Enterprise Readiness Guide put it plainly: "compatibility requires both sides to share a supported protocol era, or for one side to implement deliberate fallback or translation." A server built against the new revision may not speak to an old client, and vice versa.

If you're consuming MCP through an official SDK, this should mostly be absorbed for you — that's the entire point of not hand-rolling protocol handling. If you've built your own client or server implementation directly against the wire protocol — which describes a fair number of the 10,000+ servers out there — Parra's own words are the warning: "it's going to be a lot of uplift to make this correct."

Practical read for anyone running MCP servers in production right now — and the answer genuinely splits depending on whether you own the server or just consume one:

If you're running third-party MCP servers — Grafana, Elastic, MongoDB, Atlassian, and similar vendor-maintained servers — you're not the one who has to rewrite the session handling. That's the maintainer's problem, and it's exactly the kind of uplift the official SDKs are designed to absorb quietly. Your real exposure is entirely on the client side: whatever is orchestrating those servers (your agent runtime, your MCP gateway, your client SDK version) needs to speak the same protocol era as whatever revision each vendor ships next. Watch changelogs, pin client SDK versions deliberately, and don't assume "it worked yesterday" survives an unannounced server-side upgrade.

If you're running custom MCP servers with your own session/state management, this lands directly on you. Any logic that assumes a session persists across requests — auth context cached per-connection, per-client rate limits keyed off a session ID, in-memory conversation or tool-call state tied to a session lifetime — has to move to being derived from _meta on each request instead. That's a real architectural change, not a config flag, and it's the difference between "reads about this" and "spends a sprint on this."

Concretely:

  1. Check your transport layer, not just your tool definitions. If your server infrastructure or gateway does anything session-aware — sticky routing, per-connection state, session-keyed rate limiting — that logic needs to change regardless of whether your tools themselves need touching.
  2. Audit for sampling, roots, and logging usage. If you're leaning on any of these, you have 12 months before they stop working, not before you should start planning.
  3. Pin your protocol version explicitly on both client and server during the transition window. Silent version drift between an old client and a new server is exactly the failure mode this changelog exists to warn you about — this matters even more when you're mixing custom servers with several vendor-maintained ones, since each may migrate on its own schedule.

Why This Is the Right Call

It's easy to read "breaking change to a widely-adopted protocol" as a red flag. It isn't one here. MCP was designed fast, for a narrower use case than the one it ended up serving, and every mature protocol eventually has to reckon with the gap between its original assumptions and its actual deployment reality. HTTP did it. gRPC did it. Kubernetes' own API conventions went through multiple stateful-to-stateless corrections as the project scaled. Stacklok's Craig McLuckie — who co-created Kubernetes — backing the stateless direction here isn't a coincidence; it's someone who's already lived through this exact architectural argument once, at a different layer of the stack.

The uncomfortable but correct read is: MCP had to choose between staying backward-compatible and being usable at the scale it already reached. It's choosing scale. Anyone running MCP infrastructure — including the MCP work happening on this site's AI Chat tool — should treat July 28th as the day to start testing against the new revision, not the day to start worrying about it.