Skip to main content
株式会社オブライト
Software Development2026-07-299 min read

MCP 2026-07-28 Spec: Stateless Core, What Breaks

MCP 2026-07-28 explained: no initialize handshake, no session IDs, server/discover, MRTR, the Tasks extension, hardened OAuth, and what breaks in existing servers.


On 28 July 2026 the Model Context Protocol published its 2026-07-28 specification. The headline change from the previous revision, 2025-11-25, is that the protocol is now stateless: the initialize handshake and session IDs are gone, and every request is self-contained. That makes serverless and edge deployment straightforward, but it also guarantees work for existing MCP server implementations. This article walks through the changes from a migration standpoint.

Change summary

Area2025-11-252026-07-28
Connection startinitialize / notifications/initialized handshakeRemoved; each request carries version and capabilities in _meta
Version negotiationOnce, at initializeserver/discover (MUST implement), or per request
SessionsProtocol-level sessions via Mcp-Session-IdRemoved; state uses server-minted handles passed as tool arguments
Change notificationsHTTP GET plus resources/subscribeUnified into a single subscriptions/listen stream
Server-initiated requestssampling/createMessage, elicitation/create, roots/listReplaced by Multi Round-Trip Requests (resultType: "input_required")
Long-running workExperimental tasks in coreSplit into the io.modelcontextprotocol/tasks extension
Result shapeFree-formresultType required on every result (complete / input_required)

1. Stateless core: no more initialize handshake

Previously a connection opened with initialize, was confirmed by notifications/initialized, and subsequent requests assumed that context. The handshake is gone entirely; every request now carries its own context in _meta.

- io.modelcontextprotocol/protocolVersion — the protocol version for this request
- io.modelcontextprotocol/clientCapabilities — client capabilities
- io.modelcontextprotocol/clientInfo — client identity (SHOULD)
- Servers SHOULD identify themselves via io.modelcontextprotocol/serverInfo in each result's _meta

Version mismatches return UnsupportedProtocolVersionError. The implication is blunt: servers that assume state persists between requests will not work as written. Code that stashes handshake capabilities in an instance variable has to be rewritten to read them per request.

2. server/discover as the entry point

In place of the handshake, servers MUST implement the server/discover RPC, which advertises supported protocol versions, capabilities and identity. Clients MAY call it before anything else for up-front version selection, or use it over STDIO as a backward-compatibility probe. Because the call is optional for clients, a server must still answer correctly when it is never called.

3. No session IDs: where state goes now

The Mcp-Session-Id header is removed from the Streamable HTTP transport, and tools/list, resources/list and prompts/list must no longer vary per connection. The same request to the same server returns the same listing for everyone.

So where does cross-call state live? The spec's answer: server-minted handles passed as ordinary tool arguments. A post-login cursor, an in-flight job ID, a temp-file reference — all become explicit parts of the tool schema rather than implicit protocol state. The more your implementation leaned on the session, the larger the redesign.

4. Notifications consolidate into subscriptions/listen

The HTTP GET endpoint and resources/subscribe / resources/unsubscribe are replaced by subscriptions/listen, a single long-lived POST-response stream. Clients opt in to specific notification types (toolsListChanged, promptsListChanged, resourcesListChanged, resourceSubscriptions), and the server tags notifications with io.modelcontextprotocol/subscriptionId.

Watch one detail: request-scoped notifications such as notifications/progress and notifications/message do not flow on that stream. They continue to arrive on the response stream of the request they belong to. Moving progress handling to subscriptions/listen is a mistake. Separately, logging/setLevel is gone; log level is set per request via io.modelcontextprotocol/logLevel in _meta, and servers MUST NOT emit notifications/message for requests that omit that field.

5. Multi Round-Trip Requests (MRTR)

Statelessness makes server-initiated requests (roots/list, sampling/createMessage, elicitation/create) untenable. The replacement is the MRTR pattern.

- When a server needs more information mid-request, it returns an InputRequiredResult (resultType: "input_required") whose inputRequests field carries what it needs
- The client re-issues the original request with inputResponses attached
- Every result now carries a required resultType, either complete or input_required
- Results from earlier-protocol servers that omit the field MUST be treated as complete

As a consequence, notifications/elicitation/complete and the elicitationId field of URL-mode elicitation — both introduced in 2025-11-25 — are removed. With outcomes learned via retry, a completion signal and its correlation ID no longer fit. Servers that need to correlate across retries encode their own identifier in requestState.

6. The extensions framework and Tasks

A governed framework for official extensions arrives, with an extensions field added to ClientCapabilities and ServerCapabilities. The experimental tasks feature leaves core and is redesigned as the official io.modelcontextprotocol/tasks extension.

- The blocking tasks/result method is replaced by polling via tasks/get
- A new tasks/update carries client-to-server input
- tasks/list is removed
- Servers may return task handles unsolicited, without per-request opt-in

If you implemented long-running work as a core feature, you now declare it as an extension capability. For hands-on integration of MCP servers, see Claude Code and MCP integration guide.

7. Hardened authorization

The OAuth 2.0 / OIDC surface gets practical tightening so MCP servers connect to enterprise identity systems such as Entra or Okta without workarounds.

ChangeDetail
iss validationAuthorization servers SHOULD include iss in authorization responses per RFC 9207; clients MUST validate it against the recorded issuer before redeeming the code
Dynamic Client RegistrationRFC 7591 DCR is deprecated in favour of Client ID Metadata Documents, retained only for authorization servers that lack support
application_typeRequired during dynamic client registration, avoiding OIDC redirect URI conflicts
Credential bindingCredentials are keyed by issuer, MUST NOT be reused with a different authorization server, and MUST be re-registered when it changes

8. Newly deprecated features

This revision formalises a feature lifecycle and deprecation policy: Active, Deprecated and Removed states, a minimum twelve-month deprecation window, and a registry of deprecated features. The following remain in the spec but should not be adopted by new implementations.

DeprecatedSuggested migration
RootsPass directories and files via tool parameters, resource URIs, or server configuration
SamplingIntegrate directly with the LLM provider's API
LoggingLog to stderr on stdio, or use OpenTelemetry
HTTP+SSE transport (deprecated since 2025-03-26)Migrate to Streamable HTTP
includeContext values "thisServer" / "allServers"Omit the field or use "none"

9. Smaller changes that are easy to miss

- Cache hints required on listings: tools/list and friends must return ttlMs and cacheScope (public / private) to cut polling
- Deterministic tool order: tools/list SHOULD return a stable order to help client-side caching and prompt cache hit rates
- Request headers: Mcp-Method and Mcp-Name are required on Streamable HTTP POSTs, and x-mcp-header allows custom headers from tool parameters
- SSE resumability removed: Last-Event-ID and event IDs are gone; a broken stream means re-issuing the request with a new request ID (MUST)
- Error code policy: -32000 to -32019 stays implementation-defined, -32020 to -32099 is reserved for the spec; resource-not-found moves from -32002 to -32602 (Invalid Params)
- Looser schemas: inputSchema / outputSchema accept any JSON Schema 2020-12 keywords, and structuredContent accepts any JSON value
- Tracing: OpenTelemetry trace context propagation via _meta keys traceparent, tracestate and baggage is now documented convention
- ping removed: implementations that relied on it for liveness need another mechanism

Migration checklist for existing servers

- Have you replaced every dependency on initialize data with per-request _meta reads?
- Have you implemented server/discover (MUST)?
- Has session-based state moved to server-minted handles passed as tool arguments?
- Do tools/list and friends return identical results regardless of connection?
- Are ttlMs and cacheScope present on listing results?
- Does every result carry resultType?
- Are server-initiated requests (sampling / elicitation / roots) reworked as MRTR?
- Is task support re-declared as the io.modelcontextprotocol/tasks extension?
- Have subscriptions moved to subscriptions/listen, with progress notifications left on the response stream?
- Is log level read from _meta, with no notifications/message emitted for requests that omit it?
- Are credentials keyed by issuer, and is iss validated?
- Do any clients still rely on SSE resumability instead of re-issuing broken requests?

When to migrate

The twelve-month minimum deprecation window means implementations using Roots, Sampling or Logging will not break immediately. Statelessness itself, however, is a breaking change: adding resultType and implementing server/discover are unavoidable the moment you target the new revision. The pragmatic order is to handle the core first — statelessness, resultType, server/discover — and replace deprecated features over the grace period. If you are writing a new MCP server today, design it without the deprecated features from the start.

FAQ

Will existing MCP servers stop working with new-spec clients?

Not immediately. Clients can use server/discover over STDIO as a backward-compatibility probe, and the spec requires them to treat results without resultType as complete. That said, features such as MRTR, extensions and cache hints only work when both sides speak the new revision, so migration is the practical expectation.

What is the biggest practical benefit of going stateless?

Servers can be deployed on serverless and edge platforms as-is. Previously per-connection state had to live somewhere, which meant session affinity or a session store when scaling horizontally. Self-contained requests let MCP be operated like any ordinary HTTP API.

With sampling deprecated, how does a server call an LLM?

The suggested migration is to integrate directly with the LLM provider's API, making the MCP server itself a client of the model API. Requests for additional user input, previously elicitation, become MRTR: return input_required and wait for the client to retry.

Without session IDs, how is authenticated state preserved?

Authorization still travels as an OAuth access token on each request. Only protocol-level sessions are gone, not credentials. Business state that spans calls, such as an in-flight job, moves to server-minted handles exchanged explicitly as tool arguments.

Are the dates effectively version numbers?

Yes. MCP specification revisions are identified by date: this one is 2026-07-28 and the previous is 2025-11-25. Implementations put that string in protocolVersion, so the date functions as the version identifier.

Summary

The 2026-07-28 revision moves MCP from a bidirectional stateful protocol toward something that behaves like an ordinary request/response HTTP API. In exchange for easy serverless deployment and scaling, the initialize handshake, session IDs, server-initiated requests and SSE resumability are all gone. For existing implementations, the two design decisions that matter most are where state now lives — server-minted handles — and how server-side questions fold into MRTR.

Related free tools (no sign-up, instant results)

Feel free to contact us

Contact Us