MCP Went Stateless. State Did Not Disappear.
The MCP 2026-07-28 specification removed protocol sessions. Here is what changed, why enterprise deployments needed it, and where state lives now.
On 28 July 2026, the Model Context Protocol removed its handshake, retired its protocol-level sessions, and stopped requiring servers to remember clients between requests.
The easy headline is that MCP went stateless. The wrong conclusion is that state went away.
It did not. State moved.
Some of it now travels with each request. Some of it becomes an explicit handle passed as a normal tool argument. Long-lived continuity—what happened on Monday, what failed last week, what the agent already learned—belongs above the transport in a memory layer owned by the caller or the surrounding system.
That distinction matters because MCP has spent the spring being declared dead for reasons that mixed a real context-cost problem, a badly repeated token number, and a quieter distributed-systems flaw. The context problem remains real. The distributed-systems flaw is what the 2026-07-28 specification directly attacked.
I want to explain the whole chain from zero: why MCP exists, how the “MCP is dead” narrative acquired a number it could not honestly support, why sticky sessions were a bigger enterprise problem than the commentary suggested, what the specification changed, and what an engineering team should migrate now.

MCP from zero: the M-by-N connection problem
Start with an AI system on one side and the systems it needs on the other.
The AI may need to read a repository, inspect an issue, query a database, retrieve a design file, search internal documentation, or call an operational tool. Capability is not the same as access. A model can reason about a bug while still being unable to see the error report that contains the decisive evidence.
Before a shared protocol, every model vendor and every tool provider could build a custom connection. If there are M AI clients and N external systems, the naive integration surface is M multiplied by N. Three clients and ten tools produce thirty separate connections. Authentication, schemas, errors, retries, capability discovery, and version drift can all behave differently across those connections.
That is the actual problem MCP addresses.
MCP does not make the model smarter. It does not replace the external API. It does not make permissions disappear. It defines a common interface through which an AI client can discover and invoke tools or retrieve context. The tool provider implements the MCP-facing door once. Compatible clients can use the same shape instead of demanding another proprietary bridge.
This is why “just use APIs” is not a rebuttal. MCP servers usually reach real APIs, databases, filesystems, or services underneath. The protocol standardizes how an AI client encounters those capabilities. REST can be part of the implementation, but an estate of unrelated REST endpoints is not, by itself, a shared agent-tool contract.
Anthropic released MCP, but ownership did not remain an Anthropic-only story. On 9 December 2025, MCP was donated to the Agentic AI Foundation, a directed fund under the Linux Foundation, co-founded by Anthropic, Block, and OpenAI, with backing that included Google, Microsoft, AWS, Cloudflare, and Bloomberg. The precise wording matters: a foundation under the Linux Foundation, not a protocol “run by” the Linux Foundation.
That broader stewardship did not guarantee that the original design would scale. Standards earn trust by changing when deployed systems expose the wrong abstraction. MCP had two separate problems to confront: tool-schema cost and transport-level state.
The internet compressed those into one obituary. They should never have been treated as one issue.
How “MCP is dead” became the spring narrative
An MCP client needs to know which tools are available and how to call them. Tool definitions include names, descriptions, parameters, and constraints. Put enough definitions into a model context and the menu starts consuming the meal.
The measured range in the approved research for this piece is 550 to 1,400 tokens per tool definition. Connect GitHub, Slack, and Sentry in the configuration examined by Apideck, and the setup reaches roughly forty tools. Apideck’s own stated total for that setup is 55,000 tokens before the user’s real work has had a chance to begin.
That is not a cosmetic inefficiency. It is context occupied by descriptions of possible actions, including actions the model may never use. It can reduce the room available for the task, the evidence, the conversation, and the answer. It can also make every call carry a cost that has little relationship to the one tool actually needed.
Then came the number that turned a technical complaint into a spring headline: 72%.
In March, Perplexity’s CTO said on stage that the company was moving away from MCP internally and referred to 72% of the context window being consumed by tool definitions. The statement spread. Y Combinator’s CEO amplified it. “MCP is dead” became a compact take that travelled faster than its provenance.
I followed the number backward because 72% is precise enough to demand a precise denominator, tool set, context window, and measurement procedure. I could not source it cleanly as a Perplexity measurement.
The trail led to Apideck, a company that sells an alternative to MCP. That commercial position does not make its measurements false. It does make provenance important. The problem is that Apideck’s own post does not say the GitHub, Slack, and Sentry setup costs 143,000 tokens. It says 55,000 tokens for that roughly forty-tool setup. The 143,000-token figure appears separately as a report attributed elsewhere.
Those are two different examples.
At least one widely shared report welded them into one sentence: the named three-server setup, the 143,000-token total, and the 72% claim became one apparently coherent fact. Once fused, the sentence was easy to quote and hard to question. I nearly repeated it myself. It was already in my notes before I checked the underlying claims against each other.
The correction does not rescue the old tool-loading model. It makes the criticism more credible.
55,000 tokens for GitHub, Slack, and Sentry is Apideck’s own number. It is enough to demonstrate the problem. The 143,000 figure is a separate, mis-cited report in this provenance chain. The 72% claim cannot be cleanly presented as a Perplexity benchmark from the approved evidence. Repeating the fused version would make a valid engineering concern rest on a claim that does not survive inspection.
This is a good example of AI Reliability Engineering applied to technical communication. Do not ask only whether a number sounds plausible. Ask which entity measured it, which configuration it describes, where the denominator came from, and whether the cited source says what the summary claims it says.
Cloudflare provides stronger primary evidence for the large-tool case because its repository publishes the comparison directly. Its API surface contains 2,594 tools. Putting the raw OpenAPI specification into the prompt is approximately 2,000,000 tokens. Native MCP with full schemas is 1,170,523 tokens. Native MCP reduced to required parameters is 244,047 tokens. Cloudflare’s code-mode approach exposes three tools and uses approximately 1,100 tokens. The table and implementation are in Cloudflare’s MCP repository.
Against a 200,000-token context window, the raw specification, full native schemas, and the 244,047-token minimal form do not fit. Code mode does. This is also where the widely repeated 244x comparison needs care: it compares code mode with the already reduced 244,047-token native MCP form, not with the approximately 2,000,000-token raw specification.
The lesson is narrower than “MCP is dead.” Eagerly loading a large tool catalogue into the model context is the wrong discovery strategy at that scale. The transport standard and the prompt-loading policy are related, but they are not identical. You can keep a common protocol while changing discovery, filtering, search, tool grouping, deferred schema loading, or code execution around it.
There is a second correction worth making. Perplexity moving away from MCP internally did not mean Perplexity stopped supporting MCP externally. The approved research found that it still operated an MCP server for outside developers. “One company changed an internal transport choice” and “the protocol is dead” are not equivalent statements.
The token problem was loud because it appeared inside the model bill and the context meter. The state problem was less visible. It was also the one that directly constrained how MCP servers could be deployed behind ordinary enterprise infrastructure.

The enterprise blocker hiding behind sticky sessions
Imagine a food-delivery request. This is an analogy, not a claim about any named company’s architecture.
The caller sends a request through a load balancer. Behind it are multiple server instances. The load balancer should be free to send each request to an available healthy instance. That is how traffic spreads, failed instances are bypassed, and capacity is added or removed.
The old MCP interaction was stateful at the protocol level. The client initialized a connection. The server returned a session identifier. Later requests used the Mcp-Session-Id header so the server could recover what it had stored about that interaction.
Now place that state in the memory of server instance one.
Instance one recognizes the session. Instances two and three do not. The load balancer can no longer route freely unless the state is replicated elsewhere. It must keep sending that client back to instance one. That is a sticky session.
Sticky sessions are not automatically broken engineering. They are sometimes a reasonable local optimization. They become a protocol tax when every compliant deployment inherits them even though the application does not need conversational state inside the transport.
The costs are familiar to anyone who has operated distributed services. One instance can receive a disproportionate share of active sessions while another has spare capacity. If the pinned instance fails, in-memory session state can fail with it. Scaling down becomes harder because an instance may still own live sessions. Serverless and edge execution become awkward because workers are expected to be disposable. A protocol that assumes the same server will remember the client fights the infrastructure instead of using it.
You can work around this by externalizing the session store. Redis is a common shape for that solution: every server instance reads and writes shared session data, so any instance can reconstruct the interaction. But now the transport has required a database, network calls, expiry policy, failover design, consistency decisions, and operational cost merely to preserve a protocol-level conversation.
That was the real enterprise scaling constraint.
The context problem asks, “How much tool description should enter the model?” The sticky-session problem asks, “Can any healthy server instance handle the next request?” One is prompt architecture. The other is distributed-systems architecture. Solving one does not solve the other.
The MCP project’s 2026-07-28 release attacked the second problem by removing the session assumption from the core request path.
What changed on 2026-07-28
The specification moved MCP from a bidirectional stateful protocol to a stateless request/response model. Anthropic’s implementation note states the operational result directly: servers can deploy on serverless and edge infrastructure.
The old initialize and notifications/initialized exchange is retired. The Mcp-Session-Id header is retired. Protocol-level sessions are gone from the Streamable HTTP transport.
Instead of negotiating identity and capabilities once and expecting the server to remember them, every request carries its protocol version, client identity, and client capabilities in _meta. The request becomes self-describing enough for any compatible instance to process it.
That changes the load-balancer picture. Request one can reach instance one. Request two can reach instance three. If instance one disappears, the caller is not bound to a protocol session that died with it. Capacity can scale horizontally without teaching the load balancer which client belongs to which worker.
This is the stateless-server pattern used across resilient request/response systems: make workers interchangeable, move required request context across the boundary, and make durable state explicit. The benefit is not that the system has no state. The benefit is that an arbitrary worker does not secretly own it.
GitHub provides the strongest implementation receipt in the approved evidence. Ahead of the specification date, the company updated the GitHub MCP Server, removed Redis sessions, and eliminated database operations. GitHub said the result made the server snappier without users losing anything.
Read that change literally. A protocol redesign allowed a major implementation to delete its session store. That is stronger evidence than a diagram or a promise. The store was serving transport state that the new contract no longer required.
The specification also changed adjacent parts of the protocol.
Roots, Sampling, and Logging are deprecated. They still work, and the MCP deprecation policy keeps deprecated features in the specification for at least twelve months before they become eligible for removal. The legacy HTTP+SSE transport is also officially deprecated. I am deliberately not attaching an unverified SEP number to that statement because the approved research found the deprecation in primary evidence but its proposal identifier only in a secondary source.
The Tasks extension also moved away from a blocking result call. The blocking tasks/result method was replaced by polling through tasks/get, with tasks/update part of the task interface. That fits the same direction: long-running work should have an explicit resource and lifecycle, not depend on an open transport interaction pretending to be durable state.

Stateless does not mean memoryless
This is the point I expect teams to get wrong.
The state did not disappear. It moved out of an implicit server-side protocol session.
Immediate request context now travels in the request. The protocol version, client identity, and client capabilities live in _meta. Any stateless server instance can read them without recovering a prior handshake.
Long-running server work can be represented by explicit server-issued handles passed as ordinary tool arguments. A later request presents the handle. The server can find the named task or resource without treating the whole client relationship as one opaque session. Polling through tasks/get makes that ownership visible in the interface.
Caller continuity remains the caller’s responsibility. If an agent needs to remember an architectural decision from Monday, a failed approach from last week, or a preference established three sessions ago, none of that belongs in Mcp-Session-Id. It needs a durable system above the protocol: application storage, an orchestration layer, a memory service, or another explicit source of truth.
Those forms of state have different lifetimes and should not be collapsed:
Request state exists so one call can be understood and authorized.
Task state exists so a named unit of long-running work can be inspected or resumed.
Interaction state exists so a workflow can coordinate multiple calls.
Durable memory exists so knowledge can survive after the workflow ends.
Old protocol sessions made it tempting to hide several of these behind one identifier and one server-side store. The stateless specification forces the architecture to name them.
That is healthy pressure. Hidden state is easy to create and hard to operate. Explicit state has an owner, a schema, a lifecycle, a retention policy, and a failure mode that can be tested.
It is also where AI Reliability Engineering becomes concrete. Reliable agent systems do not merely “have memory.” They separate transport metadata from task progress, task progress from workflow state, and workflow state from durable knowledge. Each layer gets the storage and recovery guarantees it actually needs.
I currently federate thirty enabled MCP servers behind one gateway. That count was measured from the gateway configuration on 30 July 2026. A stateless transport underneath that gateway is the correct design because the individual servers should be replaceable. Cross-request continuity belongs in the shared layer above them, where it can be retrieved independently of which server handles the next tool call.
That architecture made the specification change unsurprising rather than disruptive. I was not relying on a transport session to act as durable memory.
There is still no free win. Moving state to the request can increase payload size. Moving long tasks to explicit handles requires handle storage, expiry, authorization, and cleanup. Moving durable continuity to the caller requires a real memory design instead of accidental dependence on a connection. Statelessness removes one bad coupling. It does not remove the work of state management.
A practical migration plan
Do not begin migration by changing a version string and waiting for tests to fail. Begin by finding every place where the old session was doing invisible work.
1. Inventory the old lifecycle
Search clients, servers, gateways, middleware, tests, and observability code for initialize, notifications/initialized, and Mcp-Session-Id. Do not assume the SDK is the only owner. Session identifiers often leak into caches, routing rules, logs, metrics dimensions, authorization lookups, and retry code.
For every hit, write down what the session was carrying. Was it only protocol version and capabilities? Was it authentication context? Was it a pointer to a long-running task? Was it storing conversation history? Was the load balancer using it for affinity?
This classification is the migration. Deleting the header is mechanical. Deciding where its hidden responsibilities belong is engineering.
2. Make every request self-sufficient
Update the request boundary to read protocol version, client identity, and client capabilities from _meta. Validate them at the boundary. Reject unsupported versions deliberately. Authorize the client on every request rather than assuming a previous handshake made later calls trustworthy.
Then test instance interchangeability. Send related requests through different server instances. Terminate the instance that handled the first request. Confirm that another healthy instance can process the next request from the data supplied and the explicit durable stores available to it.
If that test fails, the system still has hidden affinity.
3. Replace implicit session state with explicit handles
For file uploads, long-running jobs, or multi-step server work, issue a handle and pass it as an ordinary tool argument. Define who minted it, which client may use it, when it expires, how it is revoked, and what happens after the underlying work is deleted.
A handle is not permission by itself. Treat it as a lookup key that still passes through authorization. Otherwise, removing server sessions can accidentally turn an unguessable-looking identifier into a bearer credential.
Move task result handling from the blocking tasks/result pattern to polling with tasks/get. Use tasks/update where the task lifecycle requires an explicit update. Test duplicate polls, delayed polls, expired handles, cancelled work, server restarts, and retries after ambiguous network failure.
4. Remove infrastructure that no longer has a job
Do not preserve a Redis session store out of habit. First prove which data remains necessary. Then remove only the transport-session records that the new request model replaces.
GitHub’s implementation is the reference outcome here: Redis sessions removed and database operations eliminated. Your application may still need Redis or another database for task state, authorization, rate limits, or durable memory. Stateless MCP does not justify deleting those. It just removes “the protocol told me to remember this connection” as a reason.
Measure the result. Compare request latency, database operations, failure recovery, load distribution, and scale-down behavior before and after the migration. Reading a specification is not verification. Run the system through a load balancer and kill an instance.
5. Separate transport migration from feature deprecation
Roots, Sampling, and Logging are deprecated, not immediately removed. The approved policy gives deprecated features at least twelve months in the specification before removal eligibility. Inventory their use, choose replacements, and schedule the work. Do not create an emergency by treating deprecation as instant deletion. Do not create future debt by ignoring it either.
Treat legacy HTTP+SSE the same way. It is officially deprecated. Identify remaining clients, instrument usage, and move them to the supported transport with evidence that production traffic has followed.
6. Put continuity where it can survive
Ask a blunt question: what did the team expect the MCP session to remember?
If the answer includes user preferences, prior decisions, conversation history, tool outcomes, failed approaches, or cross-session plans, design a durable memory layer above MCP. Define capture, retrieval, contradiction handling, retention, tenant isolation, and deletion. A transcript dumped into a database is storage, not a reliable memory system.
The correct boundary is simple to state even when it is hard to implement: MCP carries the tool interaction; the orchestration system owns the continuity of the agent using that tool.
7. Test the failure modes the new design is supposed to fix
Put multiple stateless instances behind the actual load balancer. Vary routing. Remove an instance during work. Scale to zero where the platform supports it, then cold-start another instance. Retry the same request. Poll an existing task from a different instance. Verify authorization on each path. Confirm that logs can reconstruct the flow without a protocol session identifier.
Finally, test memory separately. End the workflow, start another one, and retrieve the needed prior decision through the durable layer. That proves continuity is no longer an accidental side effect of transport affinity.

MCP versus A2A is the wrong fight
MCP and A2A solve different edges in an agent system.
MCP is how an agent reaches a tool or context source through a standard interface. A2A is how agents communicate with each other. An agent may use A2A to coordinate with another agent and MCP to let either agent query a repository, invoke an operational service, or retrieve information.
Those paths can coexist in the same architecture because they are complementary, not substitutes. Replacing MCP with A2A would not remove the need for a standard agent-to-tool boundary. Replacing A2A with MCP would force peer-agent coordination through an interface designed for tools.
The useful question is not which acronym wins. It is where each boundary belongs and who owns state across it.
The 2026-07-28 MCP specification gives a cleaner answer for the tool boundary. The transport is stateless. Requests declare the context needed to process them. Long-running work uses explicit handles. Durable continuity lives above the protocol.
The token problem is still real, and large catalogues still need better discovery than eagerly loading every schema. The 55,000-versus-143,000 provenance failure should also remain a warning: a technically plausible number is not evidence until the setup, source, and denominator match.
But the sticky-session constraint changed materially. GitHub did not merely update a diagram; it removed Redis sessions and database operations from its MCP server. That is the kind of proof I trust.
MCP is not dead. It has stopped asking a disposable server instance to remember what the architecture should have made explicit.
That is a solid correction—and a useful one for anyone building AI systems that must fail over, scale, and remember for the right reasons.
Varun Pratap Bhardwaj builds AI Reliability Engineering tools at Qualixar. ORCID 0009-0002-8726-4289