DEV Community

Debbie Shapiro
Debbie Shapiro

Posted on Originally published at labyrinthanalyticsconsulting.com

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.

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 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 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. For broader context on where MCP fits in a data architecture, see MCP Servers Explained and MCP vs Function Calling vs Plugins.

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.

Top comments (1)

Collapse
 
codearchitect_j profile image
CodeArchitect_J •

Going stateless is a big shift for anyone who keyed things off Mcp-Session-Id. The explicit-handle pattern through tool arguments feels cleaner, but it pushes state management onto every server author. Did you see any guidance on how long those handles should live, or on migrating servers that relied on session state?