AI · · 7 min read
MCP's 2026-07-28 revision is close to a rewrite. What it changes
The Model Context Protocol dropped sessions, the initialize handshake, ping and server-initiated requests. What changed, and why nothing breaks today.
The Model Context Protocol — the way AI assistants connect to external tools — published a new specification revision, 2026-07-28, on 28 July 2026. It removes the initialize handshake, removes protocol-level sessions, replaces server-initiated requests with a retry pattern, and deprecates three whole features.
Nothing you have configured changes today: MCP clients and servers negotiate a protocol version, and both ends have to support 2026-07-28 before any of it applies. But the official TypeScript SDK shipped support on the same day, under new package names, so the migration is real and already available.
This is what changed, in plain terms, split by whether you use MCP servers or write them.
The short version
| Was | Is |
|---|---|
initialize / notifications/initialized handshake | Every request carries its own version and capabilities in _meta |
Mcp-Session-Id header, protocol-level sessions | No sessions; servers mint their own handles and pass them as tool arguments |
Server-initiated requests (roots/list, sampling/createMessage, elicitation/create) | Multi Round-Trip Requests: the server returns "input required", the client retries with answers |
HTTP GET stream plus resources/subscribe | One subscriptions/listen POST stream you opt into by notification type |
ping, logging/setLevel, notifications/roots/list_changed | Removed |
| Tasks in the core protocol | An official extension, io.modelcontextprotocol/tasks |
Resumable SSE streams (Last-Event-ID) | Removed; a broken stream means re-issuing the request |
Stateless, and what that buys
The headline change is that MCP is now stateless. There is no handshake to complete before a request counts, and no session identifier binding a series of requests together. Instead, every request carries io.modelcontextprotocol/protocolVersion and io.modelcontextprotocol/clientCapabilities in its _meta field, clients should identify themselves with io.modelcontextprotocol/clientInfo, and servers should identify themselves in each result's _meta with io.modelcontextprotocol/serverInfo. A version mismatch returns UnsupportedProtocolVersionError.
Servers must now implement a server/discover RPC that advertises supported protocol versions, capabilities and identity. Clients may call it up front to pick a version, or use it as a backwards-compatibility probe.
The practical consequence: an MCP server becomes an ordinary stateless HTTP service. It can sit behind a load balancer without sticky sessions, scale horizontally, and be restarted without dropping clients. Where a server genuinely needs cross-call state, the spec's answer is explicit, server-minted handles passed as ordinary tool arguments — state you can see in the tool call rather than state hidden in a connection.
The cost is paid in the transport. SSE stream resumability and message redelivery are gone: if a response stream breaks, the in-flight request is lost and the client must re-issue it as a new request with a new ID.
Multi Round-Trip Requests replace server-initiated calls
Previously a server could turn around mid-request and ask the client something — for a list of roots, for a model completion, for user input. That is gone. In its place is a pattern the spec calls Multi Round-Trip Requests (MRTR):
- The client sends a request.
- The server replies with an
InputRequiredResult—resultType: "input_required"— whoseinputRequestsfield says what it needs. - The client retries the same request, now with
inputResponsesattached.
Every result now carries a required resultType field: "complete" or "input_required". Clients must treat a result from an older server that omits the field as "complete".
This is the change that most affects how a server is written. Anything modelled as "pause and ask" becomes "return, then get called again", so a server needs to encode enough context to resume — the spec's guidance for correlating an elicitation across retries is for the server to put its own identifier in requestState.
Three features are now deprecated
Roots, Sampling and Logging are deprecated. They still work during the deprecation window, but new implementations should not adopt them. The spec's suggested migrations:
- Roots → pass directories or files as tool parameters, resource URIs, or server configuration.
- Sampling → integrate directly with the LLM provider's API.
- Logging → log to
stderron stdio, or use OpenTelemetry.
Log level is no longer a stateful setting either. It is set per request via io.modelcontextprotocol/logLevel in _meta, and servers must not emit notifications/message for requests that did not include the field.
Also deprecated: the old HTTP+SSE transport (migrate to Streamable HTTP), the includeContext values "thisServer" and "allServers", and OAuth 2.0 Dynamic Client Registration in favour of Client ID Metadata Documents.
Alongside these, MCP adopted a formal feature lifecycle policy: features are Active, Deprecated or Removed, deprecation lasts a minimum of twelve months, and there is a registry of everything currently deprecated. That is the most reassuring part of the revision: from here on, a feature has to spend at least twelve months in the Deprecated state before it can be removed.
Caching, ordering and other quieter wins
tools/list,prompts/list,resources/list,resources/readandresources/templates/listnow returnttlMsandcacheScope("public"or"private") via aCacheableResultinterface, so clients can cache instead of polling.- Servers should return tools from
tools/listin a deterministic order, explicitly to improve LLM prompt cache hit rates. If your server builds its tool list from a hash map, sort it. - Streamable HTTP POSTs now require
Mcp-MethodandMcp-Nameheaders. - The JSON-RPC server-error range is partitioned:
-32000to-32019stays implementation-defined,-32020to-32099is reserved for the spec. The errors introduced in this draft were renumbered accordingly —HeaderMismatchto-32020,MissingRequiredClientCapabilityto-32021,UnsupportedProtocolVersionto-32022. inputSchemaandoutputSchemaaccept any JSON Schema 2020-12 keywords, andstructuredContentany JSON value.
What to actually do
If you only use MCP servers — connecting tools to an assistant through a config file — nothing. Version negotiation means a client and server that both speak 2025-11-25 carry on speaking it. The behaviour changes when both ends have moved to 2026-07-28, not when the spec is published.
If you write an MCP server:
- Don't start anything new on Roots, Sampling or Logging.
- Sort your
tools/listoutput. - Stop relying on session state held in the connection. Move it to handles you pass back as tool arguments.
- If you use server-initiated requests, plan the MRTR rewrite — it is the biggest change and the one that alters your server's control flow.
- Stay on the HTTP+SSE transport only if you must; it is now formally deprecated.
- If you depend on
@modelcontextprotocol/sdk, note that the2026-07-28implementation lives in the new@modelcontextprotocol/serverand@modelcontextprotocol/clientpackages, not in a minor release of the old one.
The SDK shipped with it, under new names
This is the part that catches people, and it is a packaging change as much as a protocol one.
The official TypeScript SDK repository now publishes two packages, @modelcontextprotocol/server and @modelcontextprotocol/client. Both are at 2.0.0 and both were published on 28 July 2026, the same day as the revision. Their compiled bundles carry the 2026-07-28 wire format and also a compatibility path the code calls the legacy protocol versions, which is how a 2.0 server goes on talking to clients still on older revisions.
The older, single @modelcontextprotocol/sdk package is a different lineage. Its latest release at the time of writing is 1.30.0, published on 27 July 2026 — the day before the revision — and its compiled source sets LATEST_PROTOCOL_VERSION = '2025-11-25', with a supported list of 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05 and 2024-10-07.
So there are two decisions, not one. Staying on the old package is fine and keeps working through version negotiation. Adopting 2026-07-28 means moving to the new packages and absorbing the protocol changes above — which is why it is worth reading the changelog before you start, rather than treating it as a version bump.
FAQ
Will my existing MCP setup break?
No. Clients and servers negotiate a protocol version, and both ends have to support 2026-07-28 before any of this applies. Older revisions remain valid.
Why remove sessions at all?
Sessions forced every server to be a stateful service and every deployment to keep a client pinned to one instance. Removing them means a server can be a plain, horizontally scalable HTTP endpoint. The trade-off is that anything genuinely stateful has to be modelled explicitly, as handles in tool arguments.
Is ping really gone?
Yes — removed, along with logging/setLevel and notifications/roots/list_changed. The changelog names no replacement, so liveness is whatever your transport already gives you.
Where do I see everything that changed?
The specification repository publishes a per-revision changelog, and links a full commit comparison between 2025-11-25 and 2026-07-28. Both are in the sources below.
Sources
- Model Context Protocol specification — 2026-07-28 key changes (the revision's own changelog)
- modelcontextprotocol/modelcontextprotocol releases — revision dates
- Full comparison, 2025-11-25 to 2026-07-28
- modelcontextprotocol/typescript-sdk — the official SDK repository and the packages it publishes
- The published npm packages themselves:
@modelcontextprotocol/serverand@modelcontextprotocol/client2.0.0 (publish dates and the protocol revision in their bundles), and@modelcontextprotocol/sdk1.30.0 (publish date and itsLATEST_PROTOCOL_VERSIONandSUPPORTED_PROTOCOL_VERSIONSconstants)
More to read
- Tech
Tech · · 8 min read
Why the page jumps when an image loads, and the browser feature that stops it
Safari 27 enabled scroll anchoring, so every engine now has it. How the browser picks an anchor, the eight things that switch it off, and when to opt out.
- Design
Design · · 7 min read
The missing space between 日本語 and English, and the CSS that adds it
One declaration adds the thin space typographers put between ideographs and Latin letters. Why it is off by default in every engine, and which keywords actually work.