Community-maintained FDE reference. Not an official Vercel or Anthropic project. About this project
The 2026-07-28 stateless revision
Published and verified. The 2026-07-28 revision is final and published at
modelcontextprotocol.io/specification/2026-07-28/. Every claim on this page has been re-verified against the published specification and its changelog (an earlier version of this page was sourced from the draft changelog and carriedstatus: draft). The rest of this reference now speaks 2026-07-28 natively, so this page serves as the revision-history record: what changed relative to 2025-11-25, and why it changed that way.
Plain-language explanation
TL;DR: This revision made the protocol stateless. The initialization handshake is gone, the session id header is gone, and every request is self-contained: it carries its own protocol version and capabilities in _meta, and anything a server used to ask the client for mid-request (an elicitation answer, a sampling completion) is now returned as an “input required” result that the client satisfies by retrying the original request with the answers attached. If that sounds like it was designed for a Vercel Function, that is because serverless deployments spent two years demonstrating that MCP’s stateful session model was the part that did not survive contact with reality. This repo’s page on that friction, Serverless sessions, catalogued the workarounds this revision existed to delete; the spec has now deleted them.
One caution before building: the published spec, what the deployed handler serves, and what a default SDK client sends are three different things. See SDK status below.
What changed
All items below are the published changelog’s own change set, with RFC-2119 language and SEP numbers as it states them.
Sessions and the handshake are removed
- Protocol-level sessions and the
Mcp-Session-Idheader are removed from Streamable HTTP. List endpoints (tools/list,resources/list,prompts/list) no longer vary per connection. Servers needing cross-call state use explicit, server-minted handles passed as ordinary tool arguments (SEP-2567). - The
initialize/notifications/initializedhandshake is removed. Every request carries its protocol version and client capabilities in_meta(io.modelcontextprotocol/protocolVersion,io.modelcontextprotocol/clientCapabilities); clients SHOULD sendio.modelcontextprotocol/clientInfoper request and servers SHOULD returnio.modelcontextprotocol/serverInfoper result. Version mismatches return anUnsupportedProtocolVersionError(SEP-2575). - A new
server/discoverRPC: servers MUST implement it to advertise supported versions, capabilities, and identity; clients MAY call it up front for version selection or as a backward-compatibility probe on stdio (SEP-2575).
Server push is reshaped
- The HTTP GET listening stream and
resources/subscribe/resources/unsubscribeare replaced bysubscriptions/listen: one long-lived POST-response stream for opted-in change notifications (toolsListChanged,promptsListChanged,resourcesListChanged,resourceSubscriptions), tagged withio.modelcontextprotocol/subscriptionId. Request-scoped notifications such as progress and log messages stay on the response stream of the request they relate to (SEP-2575). - SSE resumability and redelivery are removed (
Last-Event-ID, SSE event ids). A broken response stream loses the in-flight request; clients MUST re-issue it as a new request with a new id (SEP-2575). ping,logging/setLevel, andnotifications/roots/list_changedare removed. Log level becomes per-request viaio.modelcontextprotocol/logLevelin_meta, and servers MUST NOT emitnotifications/messagefor requests that did not opt in (SEP-2575).
Server-initiated requests become MRTR
- The Multi Round-Trip Requests (MRTR) pattern replaces server-initiated requests (
roots/list,sampling/createMessage,elicitation/create). A server needing input returns anInputRequiredResult(resultType: "input_required") whoseinputRequestscarry what it needs; the client retries the original request withinputResponsesattached (SEP-2322). - Every result now carries a required
resultTypefield:"complete"normally,"input_required"for MRTR interim results. Clients MUST treat results from earlier-protocol servers that omit the field as"complete"(SEP-2322). - Consequently the 2025-11-25 URL-mode elicitation additions (
notifications/elicitation/complete,elicitationId) are removed; correlation across retries moves intorequestState.
Tasks move out of core
- Experimental tasks leave the core protocol for an official extension,
io.modelcontextprotocol/tasks. The redesign replaces the blockingtasks/resultwith polling viatasks/get, addstasks/updatefor client-to-server input, removestasks/list, and lets servers return task handles unsolicited, without per-request opt-in (SEP-2663). The extension is documented outside the core spec, at the extensions section of the site.
Deprecations
- Roots, Sampling, and Logging are deprecated (SEP-2577), remaining functional during a minimum twelve-month window under the new feature-lifecycle policy (SEP-2596). Suggested migrations: pass directories via tool parameters or configuration instead of roots; integrate directly with LLM provider APIs instead of sampling; log to
stderror OpenTelemetry instead of MCP logging. - The HTTP+SSE transport (deprecated since 2025-03-26) is formally reclassified as Deprecated, and OAuth Dynamic Client Registration (RFC 7591) is deprecated in favor of Client ID Metadata Documents; it remains available for backwards compatibility with authorization servers that do not support CIMD.
- The
includeContextvalues"thisServer"and"allServers"(soft-deprecated in 2025-11-25) are formally Deprecated; omit the field or use"none".
Transport, caching, and auth details
- Streamable HTTP POSTs must carry standard
Mcp-MethodandMcp-Namerequest headers, withx-mcp-headersupport for custom headers from tool parameters (SEP-2243). The method and tool name become visible to the HTTP layer before body parsing. - List and read results gain a
CacheableResultinterface with requiredttlMs(freshness hint, ms) andcacheScope("public"or"private") fields ontools/list,prompts/list,resources/list,resources/read, andresources/templates/list; servers SHOULD return tools in deterministic order to help client-side and LLM prompt caching (SEP-2549). ClientCapabilitiesandServerCapabilitiesgain anextensionsfield; OpenTelemetry trace-context_metakeys (traceparent,tracestate,baggage) are documented (SEP-414).- Auth hardening: authorization servers SHOULD send the RFC 9207
issparameter and clients MUST validate a present one against the recorded issuer before redeeming the code (SEP-2468); registered credentials are bound to their issuing authorization server (SEP-2352); clients must set an appropriateapplication_typeduring registration (SEP-837). - Housekeeping: resource-not-found moves from
-32002to-32602; an error-code allocation policy keeps-32000to-32019implementation-defined (existing SDK usage grandfathered) and reserves-32020to-32099for the spec. The codes introduced in this revision are renumbered accordingly (HeaderMismatch-32020,MissingRequiredClientCapability-32021,UnsupportedProtocolVersion-32022), andHeaderMismatchErroris added to the schema itself, where it previously existed only in transport prose. Tool schemas loosen to full JSON Schema 2020-12, andstructuredContentmay be any JSON value (SEP-2106).
Governance
- The revision ships with a formal feature lifecycle and deprecation policy (SEP-2596): features are Active, Deprecated, or Removed, deprecation windows last at least twelve months, and a published registry tracks every feature currently in the Deprecated state. The deprecations above are the policy’s first occupants.
A stateless tool call
What the change set adds up to on the wire:
Diagram source (Mermaid)
sequenceDiagram
participant C as Client
participant S as Server on Vercel
Note over C,S: no initialize, no session id
C->>S: POST tools/call, Mcp-Method and Mcp-Name headers
Note over C: _meta carries protocolVersion and capabilities
alt server needs user input
S-->>C: resultType input_required, inputRequests
C->>S: retry tools/call with inputResponses
end
S-->>C: resultType complete, serverInfo in _metaThe annotated message trace walks this exchange frame by frame; under 2025-11-25 the same trace needed three extra handshake frames before the first useful request.
Key deltas at a glance
- Removed:
initialize,notifications/initialized,Mcp-Session-Id,ping,logging/setLevel,notifications/roots/list_changed,resources/subscribe,resources/unsubscribe, the GET listening stream, SSE resumability, coretasks/*. - Added:
server/discover(client → server),subscriptions/listen(client → server, long-lived response stream),resultTypeon every result,_metaprotocol envelope keys,Mcp-Method/Mcp-Nameheaders,ttlMs/cacheScope, theextensionscapability field. - Moved: tasks to the
io.modelcontextprotocol/tasksextension; server-initiated requests into MRTR results. - Deprecated: roots, sampling, MCP logging, HTTP+SSE, RFC 7591 dynamic client registration, the
"thisServer"/"allServers"values ofincludeContext.
Why serverless is its happy path
Each removal deletes a workaround this repo used to have to teach:
- No session, no session store. Under 2025-11-25, a stateful server on Vercel needed Redis or had to run sessionless and hope clients coped; Serverless sessions is a whole page about that gap. Under this revision, statelessness is not a degraded mode, it is the mode, and cross-call state becomes explicit handles in tool arguments, which serialize, shard, and survive redeploys.
- Every request is routable. With version, capabilities, and identity in
_meta, and the method and tool name inMcp-Method/Mcp-Nameheaders, any invocation on any instance can serve any request, and Routing Middleware or a WAF can route and rate-limit per method without parsing bodies. - No held-open streams to babysit. MRTR turns “server keeps a stream open waiting for the user” into “server returns immediately, client retries when ready”. A
maxDurationbudget stops being a mid-elicitation death sentence. - CDN-shaped caching.
ttlMspluscacheScope: "public"ontools/listis exactly the contract an edge cache in front of a function wants. - Deprecating sampling, roots, and logging removes the three features that assumed a long-lived, bidirectional channel between peers, the assumption a function invocation never satisfied.
SDK status
The deployable stack has moved to the v2 package line: mcp-handler 2.1.1 (peer @modelcontextprotocol/server ^2.0.0) with @modelcontextprotocol/server 2.0.0 and, for tests, @modelcontextprotocol/client 2.0.0. This repo’s examples run on those pins; examples/minimal-server (in the repository) is the template.
Wire status: the pinned stack (mcp-handler 2.1.1 on @modelcontextprotocol/server 2.0.0) serves the 2026-07-28 contract natively over Streamable HTTP and falls back to stateless 2025-11-25 Streamable HTTP for legacy clients; the SDK Client defaults to that legacy handshake unless you opt in to modern version negotiation, so the examples’ in-memory test suites exercise only the legacy path. Concretely, verified against the installed packages on 2026-08-26:
- Server side, over HTTP: modern.
mcp-handler2.1.1 serves 2026-07-28 natively:server/discover, the per-request_metaenvelope,resultTypeon every result,ttlMs/cacheScopeon list results (0and"private"unless you configure cache hints),Mcp-Method/Mcp-Namevalidation with-32020, MRTRinput_requiredresults, andsubscriptions/listen.@modelcontextprotocol/server2.0.0 declaresSUPPORTED_MODERN_PROTOCOL_VERSIONS = ["2026-07-28"]; itsLATEST_PROTOCOL_VERSION = "2025-11-25"constant only names the version answered on the legacyinitializepath. A 2025-era client gets that stateless legacy fallback from the same handler:initializeanswered at 2025-11-25, noMcp-Session-Idissued, GET and DELETE answered405. HTTP+SSE and the Redis dependency are gone from 2.x. - Client side: legacy by default.
@modelcontextprotocol/client2.0.0 defaultsversionNegotiation.modeto'legacy', so a plainconnect()performs theinitializehandshake at 2025-11-25 and its results carry noresultType,ttlMs, orcacheScope. Opt in withversionNegotiation: { mode: 'auto' }(probesserver/discoverand falls back on-32601) or pin a version. - In-memory tests: legacy only. A bare
McpServeroverInMemoryTransportanswersserver/discoverwith-32601, so the examples’ vitest suites cannot observe the modern fields; those belong in an HTTP-level check against the handler. See Testing.
The annotated message trace shows both shapes and the curl that produces the modern one.
Common misconceptions
- “The spec is published, so my stack already speaks it.” Check, do not assume: publication and implementation are separate events. This repo’s pinned handler does serve 2026-07-28 natively, but a default SDK client still opens with the legacy handshake, so a claim about what your deployment emits must be checked against a capture with the client you actually use. See SDK status.
- “Stateless means no state anywhere.” No: it means state is explicit. Servers mint handles and pass them as tool arguments; the protocol layer stops pretending to remember things for you.
- “MRTR is just elicitation renamed.” No: it inverts the direction. There are no server-initiated requests at all; the server returns its questions inside a result, and the client retries with answers. Consent UX still applies, at the retry boundary; see Consent UX.
- “Sampling being deprecated means my server loses LLM access.” No: the suggested migration is calling an LLM provider directly from the server, rather than round-tripping through the client.
- “My 2025-11-25 client breaks immediately.” No: results from earlier-protocol servers without
resultTypeMUST be treated as"complete", and deprecated features remain functional through a minimum twelve-month lifecycle window.
Debugging notes
- How to tell which revision a server speaks: probe with
server/discover; a-32601back strongly suggests a 2025-11-25 (or older) server expecting aninitializehandshake. In the other direction, a stateless-revision server receivinginitializewill not know the method. - Symptom:
400complaining about missing headers on a new-revision server → the client did not sendMcp-Method/Mcp-Name. Likely cause: an old SDK. Where to look: request headers, then theHeaderMismatchError(-32020) body. - Symptom: an “interim” result you did not expect → you received
resultType: "input_required"and treated it as final. Where to look: your result-handling switch; it needs an MRTR retry path. - Symptom: reconnect-and-resume stops working → SSE resumability is removed in this revision; re-issue the request with a new id instead of replaying
Last-Event-ID.
Security implications
- No session id, no session hijacking, which retires a whole checklist row; but server-minted handles inherit the threat model instead: they must be unguessable and bound to the verified principal, exactly like task ids. The gate’s default must be denial.
- Per-request auth is unchanged in shape: OAuth 2.1 protected-resource metadata still fronts the endpoint, and the revision tightens the client side (RFC 9207
issvalidation, issuer-bound credentials, CIMD over dynamic registration). See Authorization. Mcp-MethodandMcp-Nameheaders are a gift to your firewall: per-tool WAF rules and rate limits without body inspection. They are also a new spoofing surface; the spec defines aHeaderMismatchErrorprecisely because servers must verify the headers against the body rather than trust either alone.cacheScopeis a data-leak control: mark anything principal-dependent"private"or a shared cache will happily serve one user’s tool catalog to another. See Output trust.
Runnable example
Every example in this repo builds on the v2 stack (examples/minimal-server (in the repository) is the smallest), and all of them are stateless by design, which is exactly the shape this revision rewards. Deployed (or under npm run dev), each serves the new frames (server/discover, resultType, the _meta envelope) to any client that sends modern headers; the message trace has the curl. What the examples’ vitest suites do not show is those same frames, because the in-memory transport stays on the legacy handshake; see SDK status.
Related
- Serverless sessions - the 2025-11-25 friction this revision was designed to delete, and what replaced it.
- Annotated message trace - the current wire exchange, frame by frame.
- Tasks (extension) - the utility that left core for an official extension, redesigned on the way out.
- Authorization - the auth model, which survives the statelessness change intact.
- Transports - Streamable HTTP under this revision.
Bibliography
- Model Context Protocol Specification, Key Changes, version 2026-07-28 - https://modelcontextprotocol.io/specification/2026-07-28/changelog
- Model Context Protocol Specification, Multi Round-Trip Requests, version 2026-07-28 - https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/mrtr
- Model Context Protocol Specification, Transports, version 2026-07-28 - https://modelcontextprotocol.io/specification/2026-07-28/basic/transports
- Model Context Protocol Specification, Versioning, version 2026-07-28 - https://modelcontextprotocol.io/specification/2026-07-28/basic/versioning
- Model Context Protocol Specification, Error codes (allocation policy), version 2026-07-28 - https://modelcontextprotocol.io/specification/2026-07-28/basic/index#error-codes
- Model Context Protocol Specification, Deprecated features registry, version 2026-07-28 - https://modelcontextprotocol.io/specification/2026-07-28/deprecated
- Model Context Protocol, Feature lifecycle and deprecation policy - https://modelcontextprotocol.io/community/feature-lifecycle
- Model Context Protocol, Tasks extension overview - https://modelcontextprotocol.io/extensions/tasks/overview
- SEP-2567, Remove protocol-level sessions - https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2567
- SEP-2575, Stateless protocol changes - https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575
- SEP-2322, Multi Round-Trip Requests - https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2322
- SEP-2663, Tasks as an extension - https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2663
- SEP-2549, Cacheable list results - https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2549
- SEP-2243, Standard MCP request headers - https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2243
- SEP-2577, Deprecate Roots, Sampling, and Logging - https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577