# What Changed in the MCP Spec

![Two-row flow diagram. Top row, before: a client performs an initialize handshake, an Mcp-Session-Id holds state on the server, and every call is tied to that session. Bottom row, after the 2026-07-28 revision: each request carries the protocol version and client capabilities in _meta; with Multi Round-Trip Requests the server asks for input and the client retries with answers; list results carry ttlMs and cacheScope cache hints.](https://labyrinthanalyticsconsulting.com/images/blog/hero_labyrinth_mcp-stateless-update.png?utm_source=hashnode&utm_medium=referral&utm_campaign=blog_what-changed-mcp-spec-stateless-update)

The MCP specification revision dated 2026-07-28 makes the Model Context Protocol stateless: the `initialize` handshake and the `Mcp-Session-Id` session header are gone, and every request now carries the context the server needs. If you run an MCP server or client in a data pipeline, the practical question is whether your code has to change. For most simple request-and-response integrations the answer is "a little." For anything that leaned on sessions or on the server calling back into the client, the answer is "yes, plan for it."

The revision lands four proposals together: SEP-2567 (sessionless MCP), SEP-2575 (a stateless core with no initialize step), SEP-2322 (Multi Round-Trip Requests), and SEP-2549 (cacheable results). The official [changelog](https://modelcontextprotocol.io/specification/2026-07-28/changelog) is the authoritative list. This post walks through what each change means for a working integration.

## What replaced the initialize handshake?

Under earlier revisions, a client opened a connection with `initialize`, the two sides negotiated a protocol version and capabilities, the client sent `notifications/initialized`, and on Streamable HTTP the server issued an `Mcp-Session-Id` that every later request had to echo.

In 2026-07-28 that whole exchange is removed. Each request carries its own context in `_meta`, under namespaced keys: `io.modelcontextprotocol/protocolVersion` and `io.modelcontextprotocol/clientCapabilities` on every request, with `clientInfo` recommended. A client that wants to know what a server supports before calling it uses the new `server/discover` method, which servers must implement. If the client and server disagree on version, the server answers with an `UnsupportedProtocolVersionError` rather than failing a handshake.

The consequence for you: any code that waits for an initialize response, caches a session ID, or retries "session expired" failures is now code for a protocol that no longer exists. Connection setup collapses into "build the `_meta` block once, attach it to every call."

## Where does state go without sessions?

Removing the session header does not mean servers can no longer keep state. It means state is no longer implicit in the connection. When a server genuinely needs continuity across calls (an open cursor, a staged transaction, a long-running job), the pattern is an explicit handle: the server mints an identifier, returns it in a tool result, and the client passes it back as an ordinary tool argument.

This is the change most likely to touch real pipeline code. If your server kept per-session state in memory keyed by `Mcp-Session-Id`, that state needs a new home and a handle the model can see. The upside is that explicit handles survive load balancers, restarts, and horizontal scaling in a way connection-bound sessions never did.

## What are Multi Round-Trip Requests?

Multi Round-Trip Requests (MRTR) replace the old pattern where a server, mid-request, sent its own requests back to the client: sampling, elicitation, and roots queries. In a stateless protocol the server cannot hold a request open and wait.

Instead, when a server needs something from the client to finish, it returns a result with `resultType: "input_required"` and a list of `inputRequests`. The client gathers the answers (asks the user, runs a sampling call, and so on) and retries the original request with `inputResponses` attached. Each round trip is a complete, independent request, which is what lets the rest of the protocol stay stateless.

MRTR is not request batching. It does not bundle a sequence of dependent client calls into one message. If your client used server-initiated sampling or elicitation, MRTR is your migration; if it never did, MRTR mostly does not affect you. Note also that Roots, Sampling, and Logging are deprecated as standalone features in this revision.

## How does caching work now?

SEP-2549 lets servers mark results as cacheable. The hint lives in the result body, not in an HTTP header: a cacheable result carries `ttlMs` (time-to-live in milliseconds) and `cacheScope` (`public` or `private`). It applies to `tools/list`, `prompts/list`, `resources/list`, `resources/templates/list`, and `resources/read`.

For pipelines that rediscover a server's tools on every run, honoring `ttlMs` is a cheap win: fewer list calls and less token spend re-describing tools the model already knows. List-change notifications now arrive through the new `subscriptions/listen` method rather than over a session.

## What else breaks?

The headline changes come with a set of smaller ones that will show up as errors if you upgrade a client or server without reading the changelog:

1. Results carry a required `resultType` field.
2. HTTP requests must send `Mcp-Method` and `Mcp-Name` headers.
3. `ping` and `logging/setLevel` are removed, and SSE resumability is gone.
4. Error codes moved: resource-not-found is now `-32602` instead of `-32002`, and protocol-specific codes are renumbered into the `-32020` to `-32099` range.
5. Tasks moved out of the core spec into an extension.
6. On the auth side, Dynamic Client Registration is deprecated in favor of Client ID Metadata Documents, and clients must validate the `iss` of the authorization server.

MCP is still JSON-RPC 2.0 over stdio or Streamable HTTP, so failures surface as JSON-RPC error codes, not HTTP status codes. If your monitoring matches on specific error numbers, update those patterns.

## How should a data team migrate?

A pragmatic path has three steps.

**Audit.** Search your code for `initialize`, `Mcp-Session-Id`, sampling or elicitation handlers, `ping`, and any hard-coded MCP error numbers. Each hit is a migration point. Check which protocol version your SDK targets; most of this work belongs to the SDK, and upgrading it may handle the envelope changes for you.

**Refactor the stateful parts.** Replace session-keyed server state with explicit handles passed as tool arguments. Replace server-initiated requests with MRTR's `input_required` and retry loop.

**Adopt caching deliberately.** Respect `ttlMs` on list results, and be careful with `cacheScope: private` results in any shared cache.

The versioning page describes backward compatibility with initialization-based versions, so clients and servers on different revisions can coexist during a rollout. Roll the server and client forward separately in a low-traffic environment first, and watch for `UnsupportedProtocolVersionError` before promoting.

## Bottom line

MCP 2026-07-28 trades implicit connection state for explicit, per-request context. For a client that sends independent tool calls, the migration is mostly an SDK upgrade and a `_meta` block. For servers that kept session state or called back into the client, it is a real refactor: explicit handles for state, MRTR for callbacks. Either way, read the [changelog](https://modelcontextprotocol.io/specification/2026-07-28/changelog) before you upgrade; the smaller breaking changes are the ones that surprise you in production.

If your team runs MCP integrations that touch session state and you want a second set of eyes on the migration, [get in touch](https://www.labyrinthanalyticsconsulting.com/contact?utm_source=hashnode&utm_medium=referral&utm_campaign=blog_what-changed-mcp-spec-stateless-update). For broader context on where MCP fits in a data architecture, see [MCP Servers Explained](https://www.labyrinthanalyticsconsulting.com/blog/mcp-servers-explained-bridge-ai-data?utm_source=hashnode&utm_medium=referral&utm_campaign=blog_what-changed-mcp-spec-stateless-update) and [MCP vs Function Calling vs Plugins](https://www.labyrinthanalyticsconsulting.com/blog/mcp-vs-function-calling-vs-plugins-2026-decision-tree?utm_source=hashnode&utm_medium=referral&utm_campaign=blog_what-changed-mcp-spec-stateless-update).

*Correction, 2026-10-13: an earlier version of this post described several mechanisms that are not part of the MCP specification and mischaracterized Multi Round-Trip Requests and result caching. It has been rewritten against the official 2026-07-28 changelog.*
