On July 28, 2026, the Model Context Protocol ships its biggest spec revision since launch — the handshake is gone, sessions are gone, and the stateful backbone MCP has run on since day one gets torn out. Here's the full architecture breakdown: what MCP looks like today, why it broke at scale, and exactly what replaces it.

If you've deployed an MCP server behind a load balancer, you've probably already felt the pain this release fixes. Ten weeks from the release candidate locking, MCP 2026-07-28 becomes final — and the MCP maintainers themselves are calling it the largest revision of the protocol since launch. Six Specification Enhancement Proposals (SEPs) came together just to deliver the headline change: a fully stateless protocol core.

The Deadline

Mark the calendar: the release candidate for MCP 2026-07-28 locked on May 21, 2026, kicking off a ten-week validation window for SDK maintainers and client implementers to test against real workloads. Tier 1 SDKs are expected to ship support inside that window. The final spec publishes July 28, 2026.

The good news buried in all this churn: going forward, every deprecated feature gets a minimum twelve-month runway before it can be removed, under a new formal feature lifecycle policy. This kind of breaking change is meant to be the last one of its size.

The Basics: Host, Client, Server

MCP standardizes how LLM applications plug into external tools and data — conceptually similar to how the Language Server Protocol standardized language tooling across editors. Three roles make up the topology:

  • Host — the LLM application that initiates connections (an IDE, a chat client, an agent runtime).
  • Client — a connector living inside the host, maintaining a strict one-to-one relationship with a server.
  • Server — the service exposing tools, data, and prompt templates back to the model.
HOST LLM application CLIENT Connector SERVER Tools, data, prompts 1:1 JSON-RPC 2.0

All three talk over JSON-RPC 2.0. A single host isn't limited to one server — it can spin up many isolated client↔server sessions in parallel, one per connected server, each fully sandboxed from the others.

The Payload: What a Server Offers

Once connected, a server can expose three capability types to the client, negotiated at connection time:

  • Resources — context and data for the model or user to read.
  • Prompts — templated messages and workflows.
  • Tools — functions the model can actually execute.

A real tools/call request looks like this:

{"jsonrpc":"2.0","id":1,"method":"tools/call",
 "params":{"name":"search","arguments":{"q":"otters"}}}

Clients can offer capabilities back to servers too — historically, sampling, roots, and elicitation. Two of those three are being deprecated in this release, which we'll get to.

Before: The Stateful Handshake

Under the outgoing 2025-11-25 spec, calling a tool over Streamable HTTP means establishing a session first via an initialize handshake:

POST /mcp HTTP/1.1
Content-Type: application/json

{"jsonrpc":"2.0","id":1,"method":"initialize",
 "params":{"protocolVersion":"2025-11-25","capabilities":{},
           "clientInfo":{"name":"my-app","version":"1.0"}}}

The server responds with an Mcp-Session-Id that every subsequent request must carry — pinning the client to whichever server instance issued it:

POST /mcp HTTP/1.1
Mcp-Session-Id: 1868a90c-3a3f-4f5b
Content-Type: application/json

{"jsonrpc":"2.0","id":2,"method":"tools/call",
 "params":{"name":"search","arguments":{"q":"otters"}}}
CLIENT SERVER A holds the session SERVER B unreachable SESSION STORE initialize pinned

The Problem: Sticky Sessions Don't Scale Quietly

That single pinned session creates three concrete operational costs for anyone running MCP servers at any real scale:

CostWhat it means
Sticky routingThe load balancer must pin every client to one instance — no plain round-robin.
Shared session storeEvery server instance needs access to the same session state to stay consistent, usually meaning new infrastructure (Redis, a database) just for protocol bookkeeping.
Deep packet inspectionGateways and rate-limiters must read into the message body just to figure out what operation is even happening.

None of that is what anyone signs up for when standing up a tool server. This is exactly the pain the 2026 rewrite targets.

After: The Stateless Core

In the new spec, the initialize/initialized handshake is removed entirely (SEP-2575), and the Mcp-Session-Id header and protocol-level session go with it (SEP-2567). Client info, protocol version, and capabilities now travel inside _meta on every single request instead of being negotiated once up front:

POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
Content-Type: application/json

{"jsonrpc":"2.0","id":1,"method":"tools/call",
 "params":{"name":"search","arguments":{"q":"otters"},
           "_meta":{"io.modelcontextprotocol/clientInfo":{"name":"my-app","version":"1.0"}}}}

Because nothing is pinned to a session anymore, that request can land on any server instance — plain round-robin, no sticky routing, no shared session store. The Streamable HTTP transport now requires Mcp-Method and Mcp-Name headers (SEP-2243), so load balancers and gateways can route on the operation without inspecting the body at all. There's also a new server/discover method for clients that want to fetch server capabilities on demand, plus ttlMs/cacheScope fields on list and resource results (SEP-2549) so clients know exactly how long a tools/list response stays fresh and whether it's safe to share across users.

CLIENT ROUND ROBIN SERVER A SERVER B SERVER C

The Handle Pattern: State Without Sessions

A stateless protocol doesn't mean a stateless application. Servers that need to carry state across calls do what HTTP APIs have always done: mint an explicit handle and have the model pass it back as an ordinary argument on later calls.

Model  -> create_basket()
Server -> { "basket_id": "b_9x2" }
Model  -> add_item(basket_id: "b_9x2", sku: "...")
Server -> { "ok": true, "total": 2 }
MODEL MCP SERVER create_basket() → basket_id: "b_9x2" add_item(b_9x2, sku) → ok, total: 2 items

In practice this turns out to be more powerful than hidden session state ever was — the model can compose handles across tools, reason about them explicitly, and hand them off between steps in ways that transport-level metadata never allowed.

Server-initiated requests (like an elicitation prompt) still needed a rework for a world with no persistent connection. They may now only be issued while the server is actively processing a client request (SEP-2260), and Multi Round-Trip Requests (SEP-2322) replace a held-open SSE stream with an explicit InputRequiredResult that any server instance can pick back up on retry.

New Capabilities: MCP Apps & Tasks

New capabilities no longer get bolted directly onto the core spec — they ship as Extensions: reverse-DNS IDs, their own repositories and maintainers, versioned independently (SEP-2133). Two official extensions ship with this release.

  • MCP Apps — servers ship interactive HTML interfaces that hosts render in a sandboxed iframe. Tools declare their UI templates ahead of time so hosts can prefetch and security-review before anything runs — every UI action still routes through the same JSON-RPC audit path as a direct tool call.
  • Tasks — a tools/call can now return a task handle instead of an immediate result. The client drives it forward with tasks/get, tasks/update, and tasks/cancel — task creation is server-directed. tasks/list is gone, since it can't be scoped safely without sessions.

Security: Authorization Gets Hardened

Six SEPs harden MCP's authorization spec to align more closely with how OAuth 2.0 and OpenID Connect actually get deployed in practice:

  • Clients must validate the iss parameter on authorization responses per RFC 9207 — closing off a mix-up attack class especially relevant to MCP's single-client, many-server pattern.
  • Clients declare their OpenID Connect application_type during Dynamic Client Registration, fixing the common bug where a desktop or CLI client defaults to "web" and gets its localhost redirect URI rejected.
  • Registered credentials bind to the issuing authorization server's issuer, with re-registration required when a resource migrates between auth servers.
  • The spec now documents requesting refresh tokens from OpenID-Connect-style servers, and clarifies scope accumulation during step-up auth.

What's Being Deprecated

Three core features are formally deprecated under the new lifecycle policy (SEP-2577) — annotation-only for now, guaranteed to keep working for at least twelve months:

DeprecatedReplacement
RootsTool parameters, resource URIs, or server configuration
SamplingDirect integration with LLM provider APIs
Loggingstderr for stdio transports / OpenTelemetry for structured observability

There's also a smaller but very real breaking change: the error code for a missing resource moves from the MCP-custom -32002 to the standard JSON-RPC -32602 Invalid Params. If your client matches on the literal -32002 value anywhere, update it before July 28.

The Rollout Timeline

RC locked May 21, 2026 → ten-week SDK validation window → final spec July 28, 2026. Across the release: six SEPs delivered the stateless core, two official extensions shipped alongside it, another six SEPs hardened authorization, and every future deprecation now carries a twelve-month-minimum runway. That last number is arguably the real point of this release — not just this one breaking change, but making sure it's the last time MCP has to break this hard.

Three Pillars to Remember

  • Stateless Core — no handshake, no session — any instance can handle any request.
  • Extensions — MCP Apps and Tasks ship and evolve independently of the core spec.
  • Hardened Auth — OAuth/OIDC-aligned, resistant to mix-up attacks.

If you're building or operating MCP servers, don't wait for July 28 — the release candidate is available now. Test against it.

Sources