Protocol statelessness is not “no state”
Protocol statelessness is a design constraint: a server must be able to understand and process a request without recovering hidden conversational context from a protocol session on a particular machine. The request carries what the protocol needs, or it explicitly refers to durable state elsewhere.
That last part matters. “Stateless” is routinely misread as “the application stores nothing.” That is neither realistic nor desirable for most systems. Orders, user records, long-running jobs, approvals, and agent workflows all have state. Protocol statelessness asks a narrower question: does this request depend on private protocol context held by one connection, one process, or one sticky load-balancer route?
Plain English: A stateless protocol does not forget your workflow. It stops hiding the workflow’s context inside one server’s session memory.
MCP makes this distinction timely. Its 2026-07-28 revision removes the initialize/initialized exchange and the Mcp-Session-Id protocol session from the newer Streamable HTTP wire format. That means an MCP request can be handled by any suitable server instance rather than being tied, at the protocol layer, to a prior session route. MCP release candidate Application state can still cross calls through explicit handles passed as ordinary Tool arguments.
For engineers building remote MCP services, this is more than a tidier lifecycle. It changes where responsibility sits. The edge no longer implicitly owns conversational continuity. Your durable application components do: a database, a workflow engine, an object store, or another authoritative state service.
Why this matters now
Stateful protocol sessions make early systems easy to sketch. A client connects, negotiates capabilities, and the selected process retains what follows. But that convenience quietly creates operational dependencies: session affinity, session replication, coordinated draining during deploys, and special recovery logic when the chosen instance disappears.
A sticky session means a load balancer repeatedly directs a client to the same backend instance. It can be useful, but it ties availability and scaling behavior to a route that carries history. If an autoscaler replaces that instance, the system must either restore the session, fail the work, or make the client start again.
A protocol where requests are independently interpretable fits more naturally behind ordinary layer-7 balancing and short-lived compute. HTTP defines itself as stateless because a request’s semantics can be understood in isolation; the standard specifically notes benefits for proxy reuse and dynamic load balancing. RFC 9110 That does not magically solve distributed systems problems. It does remove one unnecessary place for them to hide.
MCP’s revision is therefore an instance of a broader agent-infrastructure pattern: make transport and routing boring; make workflow state explicit, durable, authorized, and observable. This is particularly useful when many agents, clients, and tenants share a remote Tool service and no one worker should be privileged merely because it saw an earlier request.
How it works: turn hidden context into an explicit contract
First, decide what a request needs in order to be processed. That commonly includes authentication, tenant identity, the selected Tool, arguments, a trace identifier, and an explicit reference to any earlier work. It may also require a version or capability choice that a session handshake previously established once.
Second, move cross-request information into an authoritative store. A workflow record, for example, might contain a job’s owner, current phase, allowed transitions, results, and expiry. The request does not carry the whole record; it carries an opaque identifier—an explicit state handle—that lets the service find it.
Third, validate that handle on every use. “Opaque” should not mean “trusted.” The service needs to establish that the handle exists, is unexpired, belongs to the authenticated tenant, and permits the requested transition. A handle is a reference, not an authorization decision.
Fourth, make the operation’s retry behavior deliberate. Network failures leave a client with an awkward but normal uncertainty: perhaps its request never arrived; perhaps it completed just before the response was lost. A stateless server cannot rely on a private session to resolve that uncertainty. For side effects, give the operation an idempotency key, an identifier used to recognize a repeat of the same logical request. Store the result or outcome associated with that key, then return the same outcome for a legitimate retry.
Plain English: Stateless routing makes retries easier to send, not automatically safe to perform. Safety comes from the operation’s own duplicate-handling rules.
Finally, trace at the request boundary. A request-level trace ID, authenticated principal, state-handle ID, and operation ID make it possible to answer useful questions without excavating per-process session memory: which tenant changed this workflow, which request attempted it twice, and which backend processed each attempt?
This is also where Multi Round-Trip Requests fit. MCP describes them as the replacement for several server-initiated request patterns in stateless operation. The client and server can still complete a multi-step interaction, but each step is an independently routable request rather than an exchange anchored in a protocol session. MCP specification
What it is not
Protocol statelessness is not a stateless application. A shopping service can be protocol-stateless while retaining inventory, carts, payments, and audit history. The difference is that the server handling request two does not need private memory from the server that handled request one. It can retrieve the necessary business state through an explicit reference and shared authority.
It is not synonymous with HTTP either. HTTP’s stateless semantics allow applications to layer cookies, bearer tokens, server-side sessions, and resource identifiers on top. HTTP does not prevent hidden state; it provides a request-oriented substrate on which you can either introduce or avoid it.
Nor is it the same as idempotency. Idempotency describes the effect of repeating an operation. A set status to approved operation can be idempotent while still requiring a stateful protocol session. Conversely, a stateless create payment endpoint can produce duplicate charges if it lacks deduplication. The concepts frequently appear together because independently routed requests make duplicate delivery and retry uncertainty impossible to ignore.
Finally, it is not a claim that every feature should become sessionless. The MCP C# SDK documents stateful sessions for unsolicited notifications, resource subscriptions, and per-client isolation, while also offering a hybrid migration mode. MCP C# SDK documentation A connection-scoped feature may need a stateful mechanism. The engineering decision is to identify that need honestly, not to retain a session by default because it is familiar.
A practical example: a remote code-review Tool
Consider a hypothetical MCP Tool that starts a repository review and later returns findings. In a session-oriented design, the first request may create in-memory review context on worker A. Later requests assume worker A still has the repository selection, policy, and partial results. Scaling means preserving affinity; a restart means recovering or losing that context.
A protocol-stateless version makes the workflow visible. startReview creates a durable review record and returns a handle. The record binds the review to a tenant, repository revision, selected policy, and expiry. getReview accepts that handle and fetches the record. applyFinding accepts the handle, a finding identifier, and an idempotency key.
Any healthy worker can serve each call. Workers may cache immutable repository data for performance, but correctness does not depend on cache survival. The workflow store decides whether a finding exists, whether it has already been applied, and whether the caller is authorized. A deploy can drain workers without migrating protocol sessions because the workflow is not theirs to own.
That design has costs. Every request performs handle validation and possibly a storage lookup. You must choose handle lifetimes and revocation behavior. If the Tool returns sensitive output, you must ensure that a guessed, leaked, or cross-tenant handle cannot retrieve it. But those are valuable costs: they describe the real security and consistency requirements that the implicit session previously obscured.
Where it breaks
The most common mistake is to externalize state without defining its lifecycle. A database row that never expires is not a workflow design. Define creation, permitted transitions, timeout, cancellation, retention, and deletion. Decide what happens when two requests attempt incompatible transitions at once. The durable authority must enforce those rules, not merely record events after the fact.
Another mistake is treating a handle as a bearer Credential without acknowledging it. If possession of a handle grants access, protect it from logs, URLs, telemetry, and accidental cross-tenant reuse. If it is only a locator, require normal authorization alongside it. Either way, validate scope and expiry on the server.
Repeated metadata is a genuine trade-off. A negotiated session can avoid sending some information again. Stateless requests may spend more bytes and parsing time. In many remote agent systems, that overhead is worth the simpler failure model, but it is not free. Measure it where latency or request volume makes it material.
There is also no universal answer for subscriptions and server-initiated activity. You might retain a stateful channel, establish a separate notification system, or use explicit polling over a durable workflow record. The right choice depends on delivery guarantees and product requirements, not on an architectural slogan.
Plain English: Statelessness shifts responsibility; it does not erase it. If a workflow has rules, retries, ownership, and expiry, some durable component must enforce them.
What a senior engineer can do this week
Start by drawing the current request path for one remote Tool. Mark every dependency on process memory, connection identity, negotiated session data, and sticky routing. Then ask of each item: is it necessary protocol context, cacheable optimization, or actual application state?
Choose one multi-step operation and give it an explicit workflow record. Include tenant binding, ownership, status, expiry, and an operation identifier. Add an idempotency key to its side-effecting transition. Test the uncomfortable cases: duplicate delivery, response loss after completion, worker restart between calls, and a caller attempting to reuse another tenant’s handle.
Next, remove session affinity in a non-production environment and run the same tests through multiple instances. The goal is not merely that requests succeed; it is that the audit trail explains exactly what happened. Ensure your observability system correlates the state handle and operation ID without exposing sensitive values.
For MCP specifically, review whether your clients and servers use the newer Streamable HTTP lifecycle and whether any deployment assumption still depends on Mcp-Session-Id. Do not simply delete session storage. First replace each legitimate use with explicit state, authorization, and retry semantics. If you need subscriptions or unsolicited notifications, use the documented stateful or hybrid option deliberately rather than accidentally. MCP C# SDK documentation
The result is not an application with less state. It is an application where state has a clear owner and a request can be understood without asking a particular server what happened earlier. That is a much better foundation for scaling agent infrastructure—and for debugging it when the network behaves exactly as networks do.