← Read

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

WasIs
initialize / notifications/initialized handshakeEvery request carries its own version and capabilities in _meta
Mcp-Session-Id header, protocol-level sessionsNo 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/subscribeOne subscriptions/listen POST stream you opt into by notification type
ping, logging/setLevel, notifications/roots/list_changedRemoved
Tasks in the core protocolAn 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):

  1. The client sends a request.
  2. The server replies with an InputRequiredResult — resultType: "input_required" — whose inputRequests field says what it needs.
  3. The client retries the same request, now with inputResponses attached.

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 stderr on 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/read and resources/templates/list now return ttlMs and cacheScope ("public" or "private") via a CacheableResult interface, so clients can cache instead of polling.
  • Servers should return tools from tools/list in 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-Method and Mcp-Name headers.
  • The JSON-RPC server-error range is partitioned: -32000 to -32019 stays implementation-defined, -32020 to -32099 is reserved for the spec. The errors introduced in this draft were renumbered accordingly — HeaderMismatch to -32020, MissingRequiredClientCapability to -32021, UnsupportedProtocolVersion to -32022.
  • inputSchema and outputSchema accept any JSON Schema 2020-12 keywords, and structuredContent any 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:

  1. Don't start anything new on Roots, Sampling or Logging.
  2. Sort your tools/list output.
  3. Stop relying on session state held in the connection. Move it to handles you pass back as tool arguments.
  4. 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.
  5. Stay on the HTTP+SSE transport only if you must; it is now formally deprecated.
  6. If you depend on @modelcontextprotocol/sdk, note that the 2026-07-28 implementation lives in the new @modelcontextprotocol/server and @modelcontextprotocol/client packages, 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

More to read