This article is published in English.
Stateless MCP: Scaling Servers Without Sessions or Handshakes
How a stateless Model Context Protocol drops sessions and handshakes, and how _meta, multi-round requests, routing headers, caching and tasks keep it usable.
A Model Context Protocol server that works flawlessly on one machine can start failing the moment you run three copies behind a load balancer, because each instance only remembers the sessions it created itself. The protocol's move toward a stateless core is aimed squarely at that problem. This article explains what changes when sessions and the initialization handshake go away, how requests carry their own context instead, and how multi-round interactions, header-based routing, cacheable lists and background tasks fit into the new model, so you can judge what it means for the servers and gateways you run.
A note on timing: the stateless design described here belongs to the 2026 revision of the protocol. Details such as exact method names, header names and result types may still shift, so confirm them against the current MCP specification before relying on them in code. If you need a refresher on the basics of how MCP clients discover and invoke tools, the blog's introduction to how the Model Context Protocol lets agents discover and call tools covers that ground.
What "state" means here
A system is stateful when it has to remember something between requests in order to handle the next one. A food delivery order is a good everyday example: it moves from accepted, to being prepared, to out for delivery, to delivered, and the service must keep track of which stage each order has reached so that it can answer "where is my food?" at any moment.
Earlier versions of MCP worked the same way at the connection level. When a client (the AI application) first connected to a server, the two ran an initialization handshake. The server then issued a session identifier, say abc123, and the client attached it to every following request so the server could associate each call with what had happened earlier in the conversation, including the capabilities the two sides had negotiated.
Why sessions break under horizontal scaling
With a single server instance and modest traffic, that design is perfectly adequate. Trouble starts when load grows and you add instances behind a load balancer:
- A user's client sends its first request, for example asking for the list of tools. The load balancer sends it to Server 1, which creates session
abc123. - The same client sends a second request, for example calling the weather tool. This time the load balancer picks Server 2.
- Server 2 has never heard of
abc123. It holds none of Server 1's in-memory state, so the request fails.
Stateful systems have two standard workarounds. Sticky sessions pin each client to one instance, which undermines even load distribution and complicates failover. Alternatively, a shared store such as Redis holds session data that every instance can read. That works, but it adds another piece of infrastructure to operate, secure and keep available, plus a network lookup on every call, all purely to preserve protocol bookkeeping.
The stateless model
The 2026 revision takes a different route: it turns MCP into a stateless request-response protocol and retires sessions. Each request stands alone and does not depend on anything the server remembers from a previous one. With no per-client memory on the server, any instance can answer any request, and scaling out becomes a matter of adding instances behind an ordinary load balancer.
That does not mean your application can have no state at all. A tool that manages a shopping cart or a long document edit still needs data somewhere. The difference is that such state becomes explicit application data, stored where you choose and referenced by identifiers in the request, rather than implicit protocol state tied to a connection.
How a request carries its own context
Without a handshake or a session ID, the server still needs to know which protocol version the client speaks, who the client is and what it can do. The answer is that every request brings this information with it.
The _meta object
Request payloads include an optional _meta object for this metadata. It can carry:
- The protocol version, so the server knows how to interpret the message.
- The client's identity, such as an application name and version like
MyAIApp v1.0. - The client's capabilities, so the server knows which features it can use in its response.
Because the context arrives inside the request, the server can process it immediately, with no lookup in a session table. The trade-off is a slightly larger payload on each call, which is usually negligible compared with the cost of a shared session store.
Multi-round interactions without an open connection
Stateful designs make back-and-forth easy: if the server needs more information, it can ask over the connection it already has open. Take a user who asks to book a flight to Delhi but forgets to mention the date. A stateful server could simply ask for the date and wait on the same connection for the reply.
A stateless protocol cannot hold connections open for that purpose, so MCP defines a structured multi-round flow instead:
- When a server receives a request that is missing essential parameters, it returns a dedicated
input requiredresult rather than failing or waiting. - The client application asks the user for the missing details.
- The client places the answers in an
input responsesobject and sends a new, fully independent request that the server can complete.
Because the second request contains everything needed, it can land on any server instance. The server does not have to remember that it asked a question; the client carries the thread forward. If the server needs to correlate the two requests (for instance to avoid repeating expensive work), it can return an opaque token for the client to echo back, rather than keeping hidden memory.
Routing on headers instead of bodies
The stateless shift also opens the door to performance and operational gains. Two changes stand out: header-based routing and cacheable list results.
Protocol details in HTTP headers
Previously, infrastructure in front of an MCP server, such as an API gateway, a web application firewall or a load balancer, had to parse the JSON body of each request just to discover which method or tool was being invoked. Parsing bodies at the edge costs CPU, adds latency and is awkward to configure in many gateways.
Under the new rules, HTTP requests must expose key protocol information in headers:
MCP-Method, for exampletools/call;MCP-Name, the name of the specific tool being called.
A gateway can read these headers and route, rate-limit or block traffic without touching the payload. That makes common policies straightforward to express, for example sending expensive tools to a dedicated pool of instances, applying a stricter rate limit to one tool, or blocking a tool entirely during an incident. As always with headers, the server should still validate that they match the body, so that a client cannot bypass a policy by sending a misleading header.
Cacheable tool and prompt lists
Clients ask servers the same questions constantly: which tools are available, which prompts are supported. Across thousands of users, a server may spend a surprising share of its capacity answering those identical requests.
Tool and prompt lists change rarely, so the new revision makes list results cacheable. A client can fetch the tool list once, keep it in memory and reuse it for later requests instead of asking again. The same property lets shared infrastructure, such as a gateway or HTTP cache in front of the servers, answer repeated list requests for many clients at once. Either way, far fewer requests reach the server. As with any cache, you need a way to invalidate it when a deployment changes the list, so plan for expiry or versioning rather than caching forever.
Long-running work with background tasks
Some tools answer in milliseconds, like a weather lookup. Others do not: asking an assistant to analyze 10,000 documents could take twenty minutes. In a plain request-response cycle, the client would have to hold the connection open for the whole time, tying up resources on both ends and leaving the user interface stuck waiting.
To handle this, the 2026 revision includes a redesigned Tasks framework:
- Creation. When a client triggers a heavy tool, the server responds at once with a task identifier, for example
task_abc123, and the initial request ends. - Background execution. The server performs the analysis in the background while the user carries on with other parts of the application.
- Progress checks. At any point the client can ask for the task's status and results with a
Task Getcall. - Updated inputs. If the job needs more information partway through, the client supplies it with
Task Update.
This is the familiar asynchronous job pattern from web APIs, applied to MCP. It keeps applications responsive regardless of how heavy the work is. In a multi-instance deployment, remember that the task's status must be stored somewhere every instance can reach, since the Task Get call may arrive at a different server from the one that created the task. The protocol no longer needs shared session state, but a durable job store is still your responsibility.
What this means for your servers
If you operate or build MCP servers, the practical checklist looks like this:
- Remove hidden per-connection memory. Anything a tool needs between calls should live in explicit storage, keyed by identifiers the client sends.
- Read context from each request. Take the protocol version, client identity and capabilities from
_meta, not from a session. - Design tools to ask, not wait. Return an input-required result when parameters are missing and expect the answers in a new request.
- Use the routing headers at the edge. Configure gateways to route and limit by
MCP-MethodandMCP-Name, and validate them against the body on the server. - Cache lists, and plan invalidation. Let clients and gateways cache tool and prompt lists, with a clear way to refresh them after deployments.
- Move slow tools to tasks. Return a task ID quickly and keep task state in a store shared by all instances.
Key takeaways
- The stateless revision replaces MCP's session-based design with independent request-response calls, removing the need for sticky sessions or a shared session store just to scale out.
- The handshake and session IDs are gone; each request carries its protocol version, client identity and capabilities in
_meta. - Missing information is handled with an
input requiredresult and a follow-up request carryinginput responses, instead of an open connection. MCP-MethodandMCP-Nameheaders let gateways route, rate-limit and block traffic without parsing JSON bodies.- Stable lists of tools, prompts and resources become cacheable, cutting repetitive load on servers.
- Long-running tools return a task ID immediately, and clients follow up with
Task GetandTask Update. - Statelessness moves state rather than eliminating it: application data and task progress still need a durable home that every instance can reach. Verify exact names against the current specification before you build on them.