sakutto
Generative AI

MCP Spec 2026-07-28: What Removing Sessions Means for Developers

MCPModel Context ProtocolAI agentsAPI integration
MCP Spec 2026-07-28: What Removing Sessions Means for Developers

What the MCP 2026-07-28 revision is

The MCP 2026-07-28 revision is the latest specification of the Model Context Protocol, the common standard that connects AI models to external tools. The official changelog states explicitly that it lists changes made since the previous revision, 2025-11-25.

MCP is a shared contract for how an AI model reaches external tools and data. Build a server that follows it and any compatible AI tool can call it the same way. Service providers increasingly ship their own, as with the official MCP server X released.

The theme is removing state

One principle runs through this revision: take the machinery for holding state across connections out of the protocol itself.

MCP used to keep conversational state between client and server. You connected, exchanged capabilities via initialize, and everything after that ran on top of that shared assumption, with a session ID carried along.

The 2026-07-28 revision drops that. Every request now stands on its own.

What was removed

The list of removals is long.

Main removals in the 2026-07-28 revision (per the official changelog)

RemovedReplacement
Protocol-level sessions / Mcp-Session-Id headerServer-minted handles passed as ordinary tool arguments
initialize / notifications/initialized handshakeEach request declares version and capabilities in _meta
HTTP GET endpoint / resources/subscribe and friendssubscriptions/listen (a single long-lived POST-response stream)
ping / logging/setLevel / notifications/roots/list_changedLog level set per request via _meta
Server-initiated requests (sampling/createMessage etc.)The MRTR pattern (return a result, receive the answer on retry)
SSE stream resumability / message redelivery (Last-Event-ID)Re-issue with a new request ID

All of these appear under "Major changes," and all of them touch existing implementations.

View official source →
This document lists changes made to the Model Context Protocol (MCP) specification since the previous revision, 2025-11-25. / Remove protocol-level sessions and the Mcp-Session-Id header from the Streamable HTTP transport. List endpoints (tools/list, resources/list, prompts/list) no longer vary per-connection. Servers that need cross-call state use explicit, server-minted handles passed as ordinary tool arguments (SEP-2567). — From the statements on which revision this covers, and on the removal of sessions

What removing sessions actually changes

This is the part that hits implementations hardest.

State travels as a tool argument

Once sessions are gone, you need somewhere to put state that has to survive across calls. The specification gives an answer.

Servers that need cross-call state mint explicit handles and pass them as ordinary tool arguments. Instead of the protocol quietly carrying state behind the scenes, the reference to that state becomes a visible argument.

Fewer implicit assumptions make it easier to see what is going on. In exchange, defining and moving those state values is now the responsibility of whoever designs the server and its tools.

Checking the structure of the values you pass as arguments saves time while you build.

Free ToolJSON Formatter & ValidatorPretty-print or minify JSON data. Catch syntax errors instantly with line numbers and tree view.Try it now →

View official source →
Servers that need cross-call state use explicit, server-minted handles passed as ordinary tool arguments (SEP-2567). — From the statement on passing cross-call state as handles in ordinary arguments

Lists no longer vary per connection

An easy one to miss: list behavior changed. tools/list, resources/list, and prompts/list no longer return different results depending on the connection.

That is a constraint and an advantage at the same time. If the same query returns the same answer, it can be cached. The specification leans into this: servers SHOULD return tools/list results in a deterministic order, and the stated reason is to enable client-side caching and improve LLM prompt cache hit rates.

On top of that, results from the list endpoints and resources/read now require two fields, ttlMs and cacheScope. ttlMs is a freshness hint in milliseconds so clients can cache responses and poll less. cacheScope is either public or private and controls whether shared intermediaries may store the response.

View official source →
Servers SHOULD return tools from tools/list in a deterministic order to enable client-side caching and improve LLM prompt cache hit rates. / Require ttlMs and cacheScope fields on results returned by tools/list, prompts/list, resources/list, resources/read, and resources/templates/list via a new CacheableResult interface. ttlMs is a freshness hint (in milliseconds) allowing clients to cache responses and reduce polling; cacheScope ("public" or "private") controls whether shared intermediaries may cache the response. — From the statements on deterministic list ordering and the new caching fields

Version negotiation happens on every request

With initialize gone, version negotiation had to move somewhere.

Every request carries its protocol version and client capabilities in _meta. Clients SHOULD identify themselves on each request, and servers SHOULD identify themselves in each result's _meta. A mismatch returns UnsupportedProtocolVersionError.

A new call, server/discover, was added alongside it. Servers MUST implement it. It advertises supported protocol versions, capabilities, and identity. Clients may call it before anything else to pick a version up front, or use it as a backward-compatibility probe on STDIO (the transport that talks over standard input and output).

View official source →
Make MCP stateless: remove the initialize/notifications/initialized handshake. Every request now carries its protocol version and client capabilities in _meta (io.modelcontextprotocol/protocolVersion, io.modelcontextprotocol/clientCapabilities). Clients SHOULD identify themselves on each request (io.modelcontextprotocol/clientInfo), and servers SHOULD identify themselves in each result's _meta (io.modelcontextprotocol/serverInfo). Version mismatches return UnsupportedProtocolVersionError (SEP-2575). / Add server/discover: servers MUST implement this RPC to advertise their supported protocol versions, capabilities, and identity. Clients MAY call it before any other request for up-front version selection, or use it as a backward-compatibility probe on STDIO (SEP-2575). — From the statements on going stateless and on adding server/discover

How servers ask questions now

The other major design shift is the removal of server-initiated requests.

MRTR, the new shape of the exchange

Previously, when a server needed extra information mid-processing, it sent its own request back to the client: roots/list, sampling/createMessage, or elicitation/create. That is replaced by a pattern called MRTR (Multi Round-Trip Requests).

The new flow is different. The server returns a result. But that result's resultType is input_required, and its inputRequests field carries the requests for what it needs in order to continue. The client reads that and re-sends the original request with inputResponses attached.

Rather than the server seizing the conversation and interrupting, it hands back control and waits to be asked again with the missing pieces. The effect is that the mapping between request and response stays simple.

Asking for more information: the old way versus MRTR

StagePreviously (server-initiated)MRTR
TriggerServer pauses and sends a requestServer returns a result (resultType is input_required)
Where the ask livesroots/list, sampling/createMessage, elicitation/createThe result's inputRequests field
Client's answerReply to the server's requestRe-send the original request with inputResponses
MappingServer-initiated round trips nest inside the callStill one response per request

To support this, every result now requires a resultType field. Ordinary results are complete; MRTR interim results are input_required. If a result comes back from an earlier-protocol server without the field, clients MUST treat it as complete.

View official source →
Multi Round-Trip Requests (MRTR) pattern introduced which replaces the previous approach of sending server-initiated requests, such as roots/list, sampling/createMessage, or elicitation/create. Servers return an InputRequiredResult (resultType: "input_required") whose inputRequests field carries the requests for the additional information needed to process the request. Clients respond with inputResponses on a retry of the original request providing the requested information. (SEP-2322). / All results now carry a required resultType field: "complete" for ordinary results and "input_required" for multi round-trip request interim results. Clients MUST treat results from earlier-protocol servers that omit the field as "complete" (SEP-2322). — From the statements introducing MRTR and requiring the resultType field

Notifications were reorganized too

The mechanism for announcing changes was rebuilt. The HTTP GET endpoint and resources/subscribe / resources/unsubscribe were replaced by subscriptions/listen, a single long-lived POST-response stream.

Clients opt in to the types they want: tools list changed, prompts list changed, resources list changed, and resource subscriptions. The server acknowledges and tags notifications with a subscription ID.

One distinction matters: request-scoped notifications do not flow on that stream. Progress and message notifications continue to travel on the response stream of the request they belong to. Monitoring notifications and in-flight notifications now take separate paths.

View official source →
Replace the HTTP GET endpoint and resources/subscribe/resources/unsubscribe with subscriptions/listen: a single long-lived POST-response stream for opted-in server-to-client change notifications. Clients opt in to specific types (toolsListChanged, promptsListChanged, resourcesListChanged, resourceSubscriptions); the server acknowledges and tags notifications with io.modelcontextprotocol/subscriptionId. Request-scoped notifications such as notifications/progress and notifications/message continue to flow on the response stream of the request they relate to, not the subscriptions/listen stream (SEP-2575). — From the statements on the new notification path and on request-scoped notifications

Long-running work moved to an official extension

Tasks, the experimental mechanism for long-running work, moved out of the core protocol and into an official extension.

The design was reworked as well. The blocking tasks/result method is gone, replaced by polling via tasks/get. A new tasks/update carries client-to-server input, tasks/list was removed, and servers may now return task handles unsolicited, without per-request opt-in.

View official source →
Move experimental tasks out of the core protocol and into an official extension (io.modelcontextprotocol/tasks). The redesigned extension replaces the blocking tasks/result method with polling via tasks/get and a new tasks/update for client-to-server input, removes tasks/list, and allows servers to return task handles unsolicited without per-request opt-in (SEP-2663). — From the statement on moving tasks into an extension and redesigning it

What to check if you already run an implementation

If you have an MCP server or client in production, you need to scope the impact.

The parts most likely to break

Start with anything that depends on the session ID. A server that reads the Mcp-Session-Id header to look up state will not work as written. If you need state, rewrite it so the server mints a handle and the tool takes it as an argument.

Next, recovery from a dropped connection. SSE stream resumability was removed, so you cannot resume mid-stream with Last-Event-ID. A break loses the in-flight request, so you have to issue it again with a new request ID. Check whether your design assumes retries are possible.

Then the removed calls. ping, logging/setLevel, and notifications/roots/list_changed are all gone. Log level moved to a per-request _meta field, and servers MUST NOT emit message notifications for requests that did not include it.

View official source →
Remove ping, logging/setLevel, and notifications/roots/list_changed. Log level is now set per-request via io.modelcontextprotocol/logLevel in _meta; servers MUST NOT emit notifications/message for requests that did not include this field (SEP-2575). / Remove SSE stream resumability and message redelivery (the Last-Event-ID header and SSE event IDs) from the Streamable HTTP transport. A broken response stream loses the in-flight request; clients MUST re-issue it as a new request with a new request ID (SEP-2575). — From the statements on removed calls and on dropping stream resumability

Smaller changes that still bite

Beyond the major changes, several adjustments affect implementations.

The error code for a missing resource changed from -32002 to -32602 (Invalid Params), aligning with the JSON-RPC specification (the JSON-based remote procedure call convention MCP is built on). An allocation policy for error codes was also defined: -32000 to -32019 stays implementation-defined, while -32020 to -32099 is reserved for the MCP specification.

Schema constraints were loosened, too. inputSchema and outputSchema now accept any JSON Schema 2020-12 keyword, and structuredContent accepts any JSON value.

When you are working through schemas and error payloads, it helps to check the structure as you go.

Free ToolJSON Formatter & ValidatorPretty-print or minify JSON data. Catch syntax errors instantly with line numbers and tree view.Try it now →

View official source →
Change resource not found error code from -32002 to -32602 (Invalid Params) to align with JSON-RPC specification. / Define an error code allocation policy partitioning the JSON-RPC server-error range: -32000 to -32019 remains implementation-defined (existing SDK usage is grandfathered), -32020 to -32099 is reserved for the MCP specification. / Loosen inputSchema and outputSchema to allow any JSON Schema 2020-12 keywords, and structuredContent to allow any JSON value. — From the statements on the error code change, the allocation policy, and looser schema constraints

Wrapping up: complexity the protocol absorbed has moved to implementations

The MCP 2026-07-28 revision comes down to one sentence: the protocol stopped managing state. Sessions and the initialization handshake are gone, and every request now stands on its own.

The upside is easy to see. Independent requests are easier to cache and easier to load-balance. Lists that no longer vary per connection, a recommended deterministic order, and cache lifetimes carried in the results themselves all push in that direction.

The flip side is that the complexity the protocol quietly absorbed did not disappear — it moved into implementations. If you need state, design a handle and pass it as an argument. If a connection drops, send it again. Things you never had to think about are now part of your server and client design.

If you already run an implementation, the starting point is your dependence on session IDs and your recovery path after a dropped connection. The full list of removed calls lives in the official changelog, which is the reliable place to check.

The same theme — that the plumbing decides the outcome — runs through the case where harness settings tripled a benchmark score and how context engineering is approached. If you want to try an MCP server first-hand, the official MCP server X released is an approachable entry point.

Free ToolJSON Formatter & ValidatorPretty-print or minify JSON data. Catch syntax errors instantly with line numbers and tree view.Try it now →

FAQ

Q. What changed in the MCP 2026-07-28 revision?
The biggest change is that protocol-level sessions are gone and MCP is now stateless. The Mcp-Session-Id header was removed from the Streamable HTTP transport, and list endpoints such as tools/list no longer return different results per connection. Servers that need state across calls now pass server-minted handles as ordinary tool arguments.
MCP Official Changelog — Major changes
Remove protocol-level sessions and the Mcp-Session-Id header from the Streamable HTTP transport. List endpoints (tools/list, resources/list, prompts/list) no longer vary per-connection. Servers that need cross-call state use explicit, server-minted handles passed as ordinary tool arguments (SEP-2567). MCP Official Changelog — Major changes
Q. Is the initialize handshake no longer required?
It is gone. The initialize and notifications/initialized exchange was removed, and every request now carries its own protocol version and client capabilities in _meta. If the versions do not line up, the server returns UnsupportedProtocolVersionError.
MCP Official Changelog — Major changes
Make MCP stateless: remove the initialize/notifications/initialized handshake. Every request now carries its protocol version and client capabilities in _meta (io.modelcontextprotocol/protocolVersion, io.modelcontextprotocol/clientCapabilities). … Version mismatches return UnsupportedProtocolVersionError (SEP-2575). MCP Official Changelog — Major changes
Q. How do servers ask the client for something now?
Server-initiated requests were replaced by a pattern called MRTR (Multi Round-Trip Requests). Instead of the server sending its own request, it returns a result whose resultType is input_required, with the information it needs listed in inputRequests. The client then re-sends the original request with inputResponses attached.
MCP Official Changelog — Major changes
Multi Round-Trip Requests (MRTR) pattern introduced which replaces the previous approach of sending server-initiated requests, such as roots/list, sampling/createMessage, or elicitation/create. Servers return an InputRequiredResult (resultType: "input_required") whose inputRequests field carries the requests for the additional information needed to process the request. Clients respond with inputResponses on a retry of the original request providing the requested information. (SEP-2322). MCP Official Changelog — Major changes
Q. Can a broken connection be resumed?
No. SSE stream resumability and message redelivery were removed from the Streamable HTTP transport. When a response stream breaks, the in-flight request is lost, and the client has to issue it again as a new request with a new request ID.
MCP Official Changelog — Major changes
Remove SSE stream resumability and message redelivery (the Last-Event-ID header and SSE event IDs) from the Streamable HTTP transport. A broken response stream loses the in-flight request; clients MUST re-issue it as a new request with a new request ID (SEP-2575). MCP Official Changelog — Major changes

Related Tools

Related Tool Categories

Articles