Dyer InnovationStart a project
Dyer InnovationHow-To Guides

Technical Research Report · Visual Edition

MCP Goes Stateless

The 2026-07-28 Model Context Protocol specification shipped today — the largest revision since launch

The handshake is gone, sessions are gone, server→client calls are gone. What that costs to migrate, and what it changes for MCP server builders, MCP Apps, and the MCPA exam.

TL;DR — it’s final, not an RC, and it lands today

Revision 2026-07-28 is published as a stable, final release — the RC was locked 21 May 2026 and shipped on schedule ten weeks later. The core protocol is now stateless request/response: no initialize handshake, no Mcp-Session-Id, no server-initiated calls. For most builders the practical news is narrower than the headline: a server that never leaned on the removed session machinery has little to unwind, but every SDK bump and every client is a dual-era problem for the next twelve months.

Version & status

2026‑07‑28 final

Stable tag on GitHub; supersedes 2025-11-25.

Breaking changes

9 major

Plus 12 minor and 6 newly-deprecated features.

Stream resumability

Removed

A dropped stream now loses the in-flight request.

Deprecation grace

12 months

Roots/Sampling/Logging/DCR removable 2027-07-28.

Stateless core server/discover mandatory MRTR replaces callbacks MCP Apps = official extension OAuth: CIMD over DCR Legacy client → modern server fails

Bottom line: make your MCP server dual-era (implement server/discover + accept per-request _meta while keeping initialize), and if you are studying for the MCPA exam, learn this delta alongside the 2025-11-25 baseline in the MCPA exam prep guide.

1

What Just Released

The spec the MCPA exam prep guide flagged as a locked release candidate is now the current protocol version — published final today, on the date it was named for.

25 Nov 2025

2025-11-25 ships

The stateful baseline: initialize handshake, Mcp-Session-Id, server→client sampling/elicitation/roots.

21 May 2026

RC locked

Announced as “the largest revision of the protocol since launch.” A ten-week window opens for SDK maintainers to validate against real workloads.

29 May 2026

Beta SDKs

Tier-1 SDK betas published against the RC tag.

28 Jul 2026

2026-07-28 final

Stable release. TypeScript, Python, Go and C# SDKs updated; Rust in beta. Claude product support “rolling out,” no forced migration date announced.

28 Jul 2027

Earliest removals

Roots, Sampling, Logging and DCR become eligible for removal under the new twelve-month lifecycle policy.

Two structural changes carry almost all the weight — and they pull in opposite directions.

Theme one

Stateless core

Version, identity and capabilities move into per-request _meta. A server can now sit behind a plain round-robin load balancer with no sticky routing and no shared session store — which is what makes serverless and edge deployment viable.

Theme two

Extensions framework

A formal {vendor}/{name} extension system (SEP-2133) that versions independently of the core. Tasks moved out of core into io.modelcontextprotocol/tasks; MCP Apps lands as io.modelcontextprotocol/ui.

The tension

A thinner core, a wider surface

Core shrinks — Roots, Sampling, Logging, ping all leave or deprecate. But extensions are opt-in and SDK support is discretionary, so real-world capability now varies by client in a way it didn’t before.


2

Stateless, Drawn

The old protocol made the first request special and every subsequent request dependent on it; the new one makes every request self-describing and independent.

Before — 2025-11-25, session-bound

1

initialize

version + capabilities negotiated once

→

2

Mcp-Session-Id

server mints, client echoes on every call

→

3

Sticky LB

affinity required, or shared session store

→

4

Open GET stream

server→client sampling / roots / elicitation

Four coupled requirements. Losing the stream lost the session; scaling out meant either sticky routing or externalising session state — the two things that kept MCP servers off serverless.

After — 2026-07-28, per-request

1

Any request

_meta carries protocolVersion, clientCapabilities, clientInfo

→

2

Round-robin LB

routes on Mcp-Method / Mcp-Name headers, no body parse

→

3

Any instance

accepts or returns UnsupportedProtocolVersionError

→

4

Result

resultType: complete — or input_required to ask for more

State that genuinely must persist is now the server’s problem, not the protocol’s: servers mint explicit handles and pass them back as ordinary tool arguments. subscriptions/listen remains for opted-in change notifications, but it is one long-lived POST stream, not the connection identity.
Sessions didn’t disappear — they stopped being the protocol’s job and became yours, as an explicit, server-minted handle you can see in the tool arguments. — SEP-2567, restated

3

The Delta, Line by Line

Every item below was verified against the published 2026-07-28 changelog today; three of the deltas carried in the earlier MCPA guide needed correction.

○Removed outright ◐Deprecated (12-month grace) ●New / changed in place
Table 1 — 2025-11-25 → 2026-07-28, the exam- and implementation-relevant deltas
AreaWas (2025-11-25)Is (2026-07-28)KindSEP
Handshakeinitialize + notifications/initializedNone. Per-request _meta: protocolVersion, clientCapabilities, clientInfo; servers echo serverInfo in result _meta○2575
SessionsMcp-Session-Id header; per-connection list resultsRemoved. Cross-call state = explicit server-minted handles passed as tool arguments○2567
DiscoveryCapabilities learned from initialize responseserver/discover — servers MUST implement; clients MAY call. Returns supported versions, capabilities, identity●2575
Server→clientsampling/createMessage, roots/list, elicitation/create over an open streamMRTR: server returns InputRequiredResult (resultType:"input_required") with inputRequests; client retries the original request carrying inputResponses●2322
Result envelopeNo type discriminatorRequired resultType on every result. Results from earlier-protocol servers that omit it MUST be read as "complete"●2322
SubscriptionsHTTP GET endpoint + resources/subscribe / unsubscribesubscriptions/listen — one long-lived POST stream, opt-in per notification type, tagged with io.modelcontextprotocol/subscriptionId●2575
ResumabilityLast-Event-ID + SSE event IDs; redelivery on reconnectRemoved. A broken stream loses the in-flight request; client re-issues with a new request ID○2575
Utilitiesping, logging/setLevel, notifications/roots/list_changedAll removed. Log level per-request via _meta["io.modelcontextprotocol/logLevel"]; no notifications/message unless that field was sent○2575
Transport headersOptionalMcp-Method and Mcp-Name required on Streamable HTTP POST; x-mcp-header allows custom headers from tool parameters●2243
CachingOnly listChanged notificationsCacheableResult: ttlMs + cacheScope (public/private) required on the four list methods, resources/read, and resources/templates/list. Tools SHOULD be returned in deterministic order●2549
TasksExperimental, in core; blocking tasks/result; tasks/listOfficial extension io.modelcontextprotocol/tasks. Polling via tasks/get, new tasks/update for mid-flight input, tasks/list removed, unsolicited task handles allowed●2663
MCP AppsSEP-1865, first announced extension (Jan 2026)Official extension io.modelcontextprotocol/ui in the ext-apps repo, negotiated via capabilities.extensions with a mimeTypes settings object●2133
Roots / Sampling / LoggingCore client featuresDeprecated. Migrate: tool params or resource URIs (Roots) · direct LLM provider API (Sampling) · stderr or OpenTelemetry (Logging)◐2577
HTTP+SSE transportDeprecated since 2025-03-26, informallyReclassified Deprecated under the formal policy — earliest removal three months after SEP-2596 reaches Final, the shortest fuse on the board◐2596
Error codesResource-not-found -32002; ad-hoc server-error rangeResource-not-found → -32602 (Invalid Params). Range policy: -32000..-32019 implementation-defined, -32020..-32099 reserved to the spec. New codes renumbered: HeaderMismatch -32020, MissingRequiredClientCapability -32021, UnsupportedProtocolVersion -32022●—
SchemaConstrained subsetinputSchema/outputSchema accept any JSON Schema 2020-12 keyword; structuredContent accepts any JSON value; $ref resolution rules and composition-keyword bounds added●2106
AuthorizationOAuth 2.1 + DCR (RFC 7591)OAuth 2.1 retained, hardened: iss validation per RFC 9207 (clients MUST validate); credentials keyed by issuer and never reused across authorization servers; application_type required at DCR; DCR deprecated in favour of Client ID Metadata Documents◐2468 / 2352 / 837
Elicitationnotifications/elicitation/complete + elicitationIdBoth removed. Outcome is learned by retrying the original request; correlate with your own identifier in requestState○2322
ObservabilityUndefinedOpenTelemetry trace-context conventions documented for _meta: traceparent, tracestate, baggage●414

Corrections against earlier draft coverage, including the MCPA exam prep guide, all verified today: (1) the new error codes were themselves renumbered during the RC period — UnsupportedProtocolVersion is -32022, not -32004; (2) DCR is deprecated, not removed, and still required reading because application_type became mandatory within it; (3) SSE resumability removal and the loss of ping/logging/setLevel did not appear in the earlier delta list and are the changes most likely to break a long-running client.


4

Hard Cut vs. Grace

The single most important operational fact: a legacy client talking to a modern-only server fails, and legacy clients have no fall-forward mechanism — so the compatibility burden sits entirely on servers.

Table 2 — Compatibility matrix (from the spec’s versioning page)
Client eraServer eraOutcome
ModernModernWorks. server/discover optional; mismatches surface as UnsupportedProtocolVersionError and the client retries.
ModernLegacyFails. Server may error, stay silent, or process an era-ambiguous method under legacy semantics. On stdio, probe with server/discover first to fail deterministically.
Dual-eraModernWorks. Client stays modern.
Dual-eraLegacyWorks. Falls back to initialize on a non-modern error or a 4xx without a modern error body.
LegacyModernFails. On HTTP the request lacks required headers → 400. No fall-forward exists.
LegacyDual-eraWorks, on the negotiated legacy revision.
Era is a property of the server, not of a request. Clients SHOULD cache the determination for the lifetime of the process (stdio) or origin (HTTP). A modern-only server SHOULD name its supported versions in the error it returns to initialize — that error string may be the only diagnostic a legacy user ever sees.

5

Migrating

For anyone running MCP servers today the safe path is a single decision — go dual-era — followed by six mechanical steps.

Do you control every client that talks to this server, and can you upgrade them together?
Yes → modern-only
▼ no
Is it reached by third-party hosts — Claude, ChatGPT, VS Code, Goose — on their own upgrade clocks?
Yes → dual-era
▼ no
Default — serve both eras on one endpoint, and delete the legacy branch once telemetry shows no legacy opens
Dual-era

A dual-era server selects its behaviour from how the client opens: a request carrying modern _meta is served statelessly; an initialize request selects legacy semantics for that process or session.

The steps, in dependency order — one through three are required for any modern request to succeed at all.

1

Implement server/discover

Mandatory. Return supported versions, capabilities (including extensions), identity, plus ttlMs/cacheScope.

2

Read version and capabilities from _meta, per request

Stop trusting handshake state. Return UnsupportedProtocolVersionError (-32022) with a supported list on mismatch.

3

Stamp resultType on every result

And on the client side, treat a missing resultType from an older server as "complete".

4

Convert server→client calls to MRTR

Any sampling, roots or elicitation call becomes an InputRequiredResult plus idempotent handling of the client’s retry. Carry your own correlation id in requestState.

5

Re-home session state into explicit handles

Anything that lived behind Mcp-Session-Id becomes a server-minted token in the tool arguments — visible, auditable, and yours to expire.

6

Transport, caching, errors, auth

Require Mcp-Method/Mcp-Name; add ttlMs/cacheScope to list and read results; move resource-not-found to -32602; validate iss; key OAuth credentials by issuer; plan the CIMD move off DCR.

The quiet one that bites: stream resumability is gone. Any client that relied on Last-Event-ID to survive a dropped connection must now re-issue the request under a new request ID — which means server-side handlers for long operations need to be idempotent or task-backed, or a reconnect silently double-executes.


6

Source Notes & Premise Checks

Table 4 — Where a claim needed a note, and where a premise didn’t hold
ItemResolution
“It may now have shipped”Confirmed shipped. GitHub tag 2026-07-28 is marked stable release (28 Jul 2026), preceded by 2026-07-28 RC (29 May 2026). The blog post is titled “The 2026-07-28 Specification” and states Final Release.
Versioning page says “current is 2025-11-25”Stale render, caught and discounted. The same page’s negotiation section, its deprecated-features link, and every substantive reference point at /specification/2026-07-28/. Treated as a docs-site propagation lag on release day; the GitHub release tag and the blog post are the authorities used here.
“UnsupportedProtocolVersion is -32004”Superseded. The error-code allocation policy renumbered the draft codes: -32020 / -32021 / -32022. The earlier MCPA guide carried the draft numbers.
“DCR removed”Not removed — deprecated. Remains available for authorization servers that don’t support CIMD; and application_type became a new requirement within DCR (SEP-837), so it still needs implementing.
Claude-side migration deadlineNone published. The Anthropic post says support is “rolling out” with no forced cutoff — which is why dual-era, not a hard cut, is the recommendation.
MCP Apps extension identifierRegistry page names the extension “MCP Apps” in ext-apps; the negotiation examples on both the extensions overview and the versioning page use the identifier io.modelcontextprotocol/ui. Both cited.

The protocol got simpler; the compatibility surface got wider. Plan for the second, not the first.

Go dual-era on your MCP server, add iss validation to the auth path, advertise the MCP Apps extension if you ship UI, and re-learn MCPA Domain 3 and 4 material against the delta table above. The one condition on all of it: none of the deprecated features (Roots, Sampling, Logging, DCR) should appear in anything built from release day onward, because the twelve-month clock has started.

References

All URLs accessed 28 July 2026. primary = specification / vendor · secondary = independent.

Specification — primary

Announcements & releases — primary

Key SEPs — primary

Related How-To Guide

How-To Guides · Published 28 July 2026

Built from public sources. Plans, prices and versions change: check the linked sources before you rely on them.

Get new guides by emailOne short note when a new How-To Guide lands. Free.

Get new guides by emailBrowse all guides