Development Choices

MCP 2026-07-28’s Stateless Request Model

Author
Joseph TrasattiMember of technical staff
Published
Section
MCP
Length
11 min read3 sources cited
Self-contained request capsules distributing cleanly across several identical server cells

MCP revision 2026-07-28 makes each request self-describing: it removes the initialization handshake and protocol session identifier, and sends version and client capabilities with every call. Requests can reach any server instance. Stateful applications still work by passing server-minted handles explicitly in normal tool arguments.

Stateless at the protocol layer

A self-contained MCP request passes through a load balancer to any server instance and returns a response
The modern protocol removes affinity without forbidding application state.

MCP revision 2026-07-28 changes the protocol core from a stateful connection lifecycle to self-describing requests. The protocol no longer depends on an initialization exchange or a transport session identifier, but applications can still keep state by exposing that state through explicit handles.

The July 28, 2026 specification release post describes the result as a stateless request-and-response core: every request carries enough protocol and client information to be processed without relying on an earlier handshake. The revision’s normative changelog records the two removals that make this possible: the initialize/notifications/initialized exchange and the protocol-level session represented by Mcp-Session-Id.

“Stateless” has a precise boundary here. It means the MCP protocol does not require a server to remember transport-scoped facts from one request so that it can interpret the next request. It does not mean a tool must forget an ongoing job, document, transaction, or other application object. That distinction decides how clients, servers, load balancers, and application data stores should be designed.

No initialization precondition

Earlier revisions established protocol version, capabilities, and implementation identity through the initialization lifecycle. A client sent initialize, received the server’s answer, sent notifications/initialized, and then entered normal operation. Later calls depended on facts established during that exchange.

Revision 2026-07-28 removes that lifecycle from the protocol. There is no initialization phase that must complete before an ordinary RPC is valid, and there is no initialized state for a server instance to retain. A client may send a compliant tool, resource, prompt, or discovery request without first preparing that particular instance through an earlier exchange.

This is a breaking change, not a shorter spelling of the old sequence. A 2026-07-28 client should not assume that sending initialize opens a modern protocol session. A 2026-07-28 server should not wait for notifications/initialized before accepting ordinary requests. Implementations that still need to communicate with older revisions need an explicit compatibility path; the detailed transition belongs in the migration from MCP 2025-11-25 to 2026-07-28, not in hidden lifecycle state inside the new revision.

Removing initialization also removes one place where clients previously obtained a server’s capabilities before calling it. The replacement is not another mandatory handshake. Capability information needed to interpret a request travels with that request, while optional server discovery provides an up-front view when a client needs one.

What every request carries

Each request carries its selected protocol version and the client capabilities that apply to that call in _meta. The specified keys are io.modelcontextprotocol/protocolVersion and io.modelcontextprotocol/clientCapabilities. The client should also identify its implementation on each request with io.modelcontextprotocol/clientInfo, containing its name and version.

These fields make the request self-describing. The receiving server instance does not need to recover a capability snapshot negotiated by another instance, infer a version from a connection’s history, or look up which initialization exchange preceded the call. It reads the request’s metadata and handles the call under the stated protocol version and capabilities.

The distinction between required request context and recommended identity matters. Protocol version and client capabilities are part of the per-request contract. Client identity is also sent per request, but the specification uses SHOULD for clientInfo, not MUST. A server result likewise should identify the server implementation in io.modelcontextprotocol/serverInfo. That identity is useful for display, logging, and debugging; it is not a replacement for protocol version metadata.

The direct cost of this design is repetition. Metadata that was once exchanged at the start of a session now accompanies every request. The supplied sources do not provide a byte count, latency measurement, or throughput benchmark for that trade, so no performance effect can be quantified here. The operational benefit that the specification does establish is independence: a request is not tied to whichever instance handled an earlier lifecycle message.

Per-request metadata also moves version errors to the request boundary. If the server does not support the version named by the client, it returns UnsupportedProtocolVersionError. A client can therefore attempt an ordinary RPC directly and handle a version mismatch, or learn the supported versions first through discovery. The complete selection and mismatch rules are covered under MCP protocol-version negotiation.

Discovery without a session

Servers implementing revision 2026-07-28 must implement server/discover. Clients may call it before another RPC, but they are not required to do so. The 2026-07-28 discovery specification defines the method as a query for the server’s supported protocol versions, capabilities, and identity.

A discovery request is itself an ordinary self-describing request. It carries the standard _meta, including the client’s protocol version and capabilities. Its result returns supportedVersions, server capabilities, and server information. It may also include natural-language instructions, and the result supports caching through its cache metadata.

Discovery therefore does not recreate initialization under another method name. It does not open a session, produce a session identifier, or place the connection into a new state. Calling it gives the client a server description that can guide later calls; skipping it leaves the client free to invoke another RPC and deal with an unsupported-version response if necessary.

The method is useful when a client needs to present server information or choose a version before making a functional call. It can return the server’s identity and its tool, resource, or prompt capabilities in one response rather than requiring separate probes. On stdio, it also provides the specified backward-compatibility probe for a client that supports both the new per-request model and legacy servers based on initialize.

Discovery data and client capability metadata travel in opposite directions and answer different questions. The request tells the server which protocol version and client features apply now. The discovery result tells the client which versions and server features are available. Neither direction creates durable protocol state. For the broader change from connection-established capabilities to request metadata, see capability negotiation between MCP clients and servers.

Routing without protocol affinity

Because every compliant request includes the protocol context needed to process it, any such request can reach any server instance. A plain round-robin load balancer can distribute successive calls across instances without first finding the instance that handled an initialization exchange.

Sticky sessions are therefore not required for protocol routing. There is no Mcp-Session-Id for a router to inspect, preserve, or map to an origin. A server fleet also does not need a shared session store merely to recover protocol version, client capability, or initialization status. Those facts arrive with the call.

The qualification “for protocol routing” is important. The revision does not claim that an application never needs shared data. A tool may work with an application object that must be visible to more than one instance. A server may also rely on data that exists independently of MCP. The stateless core removes the protocol’s hidden routing dependency; it does not dictate where an application stores its own objects.

A useful deployment test follows directly from the contract: two consecutive valid requests from one client must not depend on reaching the same instance merely because they belong to the same MCP interaction. If the second instance needs the previous instance’s negotiated version, remembered client capabilities, or stored initialized flag, the implementation is retaining state that revision 2026-07-28 moved into each request.

That does not prohibit an infrastructure layer from using affinity for a separate application reason. It means the MCP protocol no longer requires that affinity. If affinity remains, its condition and failure behavior belong to the application architecture rather than to an implicit MCP session.

The same boundary applies to shared storage. Removing the session identifier eliminates the need for a store whose purpose is to map an MCP transport session to protocol context. It does not eliminate a database or other store holding application objects. Calling the whole deployment stateless when tool results still depend on instance-local application data would blur the exact guarantee the revision provides.

This differs materially from the session mechanics documented for older Streamable HTTP implementations. Readers maintaining both generations should keep the new request model separate from MCP session management over Streamable HTTP, where session identifiers and routing affinity may still describe a legacy revision.

Application state uses explicit handles

An MCP server may still support work that spans calls. Under revision 2026-07-28, the server represents that continuity explicitly: a tool mints a handle, returns it through the normal tool interface, and the client or model passes it back as an ordinary argument to the next tool call.

The handle is application data, not protocol metadata. It names whatever ongoing object the server has chosen to expose. The protocol does not treat possession of that handle as evidence of an initialized transport session, and it does not route the request according to a hidden session record. Each call still carries its own protocol version and client capabilities.

This mechanism makes the dependency visible in the tool exchange. A later call that needs earlier application state includes the handle in its arguments instead of relying on an unstated relationship between two connections or two requests. The model can see the handle and thread it between tool calls because it appears in the same ordinary data path as other tool outputs and arguments.

The server remains responsible for making the handle useful wherever a compliant request lands. If only the instance that minted a handle can resolve it, the application has recreated instance affinity at the application layer. That may describe the implementation, but it is not protocol-level session management, and the routing requirement must be documented as an application constraint. If every instance can resolve the handle, requests remain freely distributable while the application retains continuity.

Explicit handles also define the scope of migration work for stateful servers. Code that indexed state by Mcp-Session-Id cannot keep doing so under the new protocol because that header no longer exists. The state must be addressed by a value carried in an ordinary request, and the relevant tool contracts must accept and return that value. This is an engineering cost even though the revision removes protocol session infrastructure: schemas, handlers, persistence lookups, and client call sequences may all need to change where they previously relied on a hidden session.

The supplied sources do not prescribe one storage mechanism, handle format, lifetime, or persistence policy. Those are application decisions and should not be presented as requirements of revision 2026-07-28. The protocol-level requirement is narrower: cross-call application state is expressed through an explicit server-minted handle passed in tool arguments, not through transport session state.

List methods reflect the same separation. The revision’s changelog states that tools/list, resources/list, and prompts/list no longer vary by connection. A server should not use a remembered transport session to decide which catalog a request sees. Any application-specific input needed to select or operate on state must be made explicit rather than inherited from an earlier connection event.

The compatibility boundary

The clean implementation boundary is the protocol revision named by the request. Under 2026-07-28, the server receives version and client capability metadata on every call, does not require initialization, does not issue or consume Mcp-Session-Id, and implements server/discover. Under an older revision, the lifecycle and session rules of that revision still apply.

A mixed-version client cannot safely treat both models as one lifecycle. On HTTP it can use the revision’s version-error behavior to select a compatible path. On stdio, the discovery specification says a client supporting modern and legacy servers should send server/discover first as the compatibility probe. That probe determines which protocol behavior follows; it does not initialize a 2026-07-28 session.

For server operators, the central audit question is whether any ordinary call depends on protocol context that is absent from the call itself. Stored initialization results, connection-scoped capability flags, and routing keyed by the removed session header all cross the new boundary. Application objects referenced by explicit tool arguments do not: they remain stateful application data reached through a stateless protocol request.

What to check next

When checking an implementation, inspect the request _meta, confirm that no initialize exchange is required, verify that Mcp-Session-Id is absent from the new-revision path, and identify every piece of cross-call state. Protocol version and client capabilities belong on each request; application continuity belongs behind explicit handles in tool arguments.

Then check discovery and compatibility separately. server/discover must exist on a 2026-07-28 server, but a client may call another RPC first. Any support for older servers should follow the version-selection rules for the relevant transport rather than quietly restoring a protocol session to the new revision.

Sources

  1. July 28, 2026 specification release postblog.modelcontextprotocol.io
  2. revision’s normative changelogmodelcontextprotocol.io
  3. 2026-07-28 discovery specificationmodelcontextprotocol.io

See also