A demo MCP server might expose one tool, add_to_cart, that a model calls a few times before checkout. With a single user on one machine, after a few seconds, it runs and the work looks done.
The same server in production is a different system, sitting behind a load balancer with a dozen agents hitting it at once. The cart has to survive across calls, and an auth token has to be scoped so that one user's agent cannot touch another user's cart. On top of that, the moment another team builds on the server, changing how it behaves starts to get expensive.
The 2026-07-28 MCP release addresses that difference. As a major revision of the protocol, its main operational change for server teams is that MCP is now stateless at the protocol layer, which means server teams can no longer rely on the protocol to hold the session. Almost everything else here follows from that.
This is written for people running or evaluating an MCP server that other teams may depend on. It assumes you know what a tool call is, but it is not a full spec walkthrough, a beginner's guide, or a line-by-line migration manual. We are publishing it because MCP is turning into shared infrastructure, and the operational questions that come with that are easier to answer early than to retrofit.
The 2026-07-28 specification is now final. The TypeScript, Python, Go, and C# Tier 1 SDKs support it, although implementations that relied on protocol sessions will still need SDK-specific migration work.
| Protocol change | What it means in production | What to check |
|---|---|---|
| Protocol sessions and the initialize handshake are removed | Any request can hit any instance; no protocol-session stickiness or shared session store | Drop session affinity from the load balancer |
| Protocol version and client capabilities move into _meta on every request; clients should also include clientInfo | Requests are self-contained; capabilities come per-call or via server/discover | Confirm your SDK and middleware put this in _meta |
| Cross-call state moves to explicit, server-minted handles | You own handle lifecycle, ownership and cleanup | Define expiry, ownership checks and a cleanup path |
| MCP-Protocol-Version and Mcp-Method are required on POSTs; Mcp-Name is required for named operations | Gateways route and rate-limit without reading the body | Route on the headers; reject header/body mismatches |
| ttlMs and cacheScope added to list and read results | Clients cache list and read results safely, with less reliance on open streams | Set TTLs; mark per-user data so it is not shared |
| W3C Trace Context documented in _meta | Traces correlate across host, client, server and downstream | Wire tool calls into your OpenTelemetry backend |
| Authorization aligned closer to OAuth and OIDC | Fewer mix-up attacks and fewer client registration failures | Validate iss; prefer CIMD; set application_type when using DCR; bind credentials to the issuer |
| Roots, Sampling and Logging deprecated | They keep working for at least twelve months; replacements are now the path | Plan the moves; send logs to stderr or OpenTelemetry |
| Tasks moved to an extension; tools use full JSON Schema 2020-12 | New task lifecycle; richer, more precise tool schemas | Migrate task code; bound schema depth, no automatic network $ref dereferencing |
The stateless core
Under the 2025-11-25 specification, Streamable HTTP used the initialize/initialized handshake. Sessionful implementations could then issue an Mcp-Session-Id, which later requests had to carry. In that pattern, the cart from the opening could live behind the protocol session, and horizontal deployments needed sticky routing or a shared session store for that session state.
The release removes both the handshake and the session. Protocol version and client capabilities now travel in _meta on every request, clients should also include clientInfo, and a new server/discover method lets a client fetch server capabilities when it wants them up front. With the session gone, each request carries everything a server needs to answer it, so any instance can. A deployment that needed sticky sessions and a shared store can run behind a plain round-robin load balancer.

Figure 1. The same deployment before and after the session is removed.
This is where "stateless" is easy to misread. The protocol core is stateless. Your application does not have to be, and a cart server clearly is not. The shift is about where the state lives, which the next section covers.
MCP-Protocol-Version and Mcp-Method are required on every Streamable HTTP POST, while Mcp-Name is required for tools/call, resources/read, and prompts/get. These headers let gateways route and validate requests without parsing the body, and servers reject header/body mismatches.
The ttlMs and cacheScope fields let clients cache list and read results with explicit freshness and sharing boundaries. Clients that want change notifications opt in through subscriptions/listen, which replaces the old long-lived GET stream and resource subscription methods.
Where your state lives now
Removing the protocol session does not delete your state. It moves the responsibility for it to you. The pattern the maintainers point to is the one HTTP APIs have always used. A tool mints an explicit handle and returns it, and the model passes that handle back as an ordinary argument on later calls. For the cart, create_cart returns a cart_id, and every following add_item or checkout call carries that cart_id.

Figure 2. The explicit-handle pattern, shown with a shopping cart.
The argument for this is that the handle is visible to the model. The model can hold it, reason about it, pass it between tools, and thread it through a sequence of steps, which session state hidden in transport metadata could not support.
The cost is that handle lifecycle becomes a production concern you design rather than one the protocol manages. A cart_id needs an owner, a lifetime, and a way to be cleaned up, and none of that happens for you anymore. There is no session timeout to lean on and no automatic teardown when a client disconnects. Handles also arrive as arguments the model can see and pass around, so they are untrusted input: an expired cart_id should fail cleanly, and a cart_id from one user should never resolve for another.
Auth, permissions, and scoping
A tool server others depend on acts with its own credentials on behalf of many callers, which is the classic confused-deputy position. The handle question from the last section is where this bites first. A cart_id on a request is an identifier, and it is not evidence that the agent presenting it is allowed to use it. Ownership has to be checked on every call, on your side, against the caller's identity.
The baseline below that is ordinary least privilege. The server's token should reach only what its tools need, write paths should be separated from read paths, and scope should be enforced somewhere you control rather than left to a calling agent's good behavior. As one public example, GitHub's agentic-workflows setup runs its MCP server read-only, sends write operations through a separate permission-controlled job, and uses a gateway allow-list to decide which tools an agent can see at all.
The release hardens the authorization specification to match OAuth and OpenID Connect deployments more closely. Authorization servers should include the iss parameter, and clients must validate it when present; a future revision is expected to make its inclusion mandatory. Clients using Dynamic Client Registration must declare an appropriate application_type, although DCR is now deprecated in favor of Client ID Metadata Documents. Client credentials are bound to the authorization server that issued them, so clients must re-register when that authorization server changes.
Tool descriptions, naming, and precision
The model picks a tool from its name, its description and its schema. In a demo with one obvious tool, a loose description does no harm. In production, with several servers and overlapping tools in context, a vague description is how a model reaches for the wrong tool, or the right tool with the wrong arguments. The model uses names and descriptions to choose routes, so teams should review them like API contract details.
The release lifts tool inputSchema and outputSchema to full JSON Schema 2020-12. inputSchema still requires an object at the root, but can now use composition, conditionals, and references; outputSchema can describe any JSON value. A tighter schema means fewer malformed argument blocks reaching your logic. Implementations must not automatically dereference network $ref URIs and should bound schema depth and validation time, because tool schemas are attacker-reachable input.
When tools are too broad
Tool definitions are a running cost. Because LLM APIs are stateless, agent runtimes typically include the available tool names and schemas with each model request. (That is a different sense of statelessness from the protocol change above, sitting at the model API rather than the transport.) A broad toolset adds cost even when the tools are not used, and models tend to handle broad tools less reliably.
The clearest public numbers come from GitHub, measuring its own agentic CI workflows. A GitHub MCP server with 40 tools added roughly 10 to 15 KB of schema per turn, and pruning the unused ones cut per-call context by 8 to 12 KB, saving thousands of tokens per run with no change in behavior. The cost is not only unused tools sitting in context, it is irrelevant ones getting called. In one GitHub workflow that only scanned local file changes, the search_repositories tool was invoked 342 times in a single run, accounting for 58 percent of all tool calls, despite having no role in the job, until the optimizer flagged it for removal.
The same logic applies to tools that are broad rather than just unnecessary. A manage_cart tool that creates, edits, empties and checks out a cart is harder to scope, harder to audit, and harder for a model to route to correctly than four narrow tools with clear jobs. Breadth costs tokens on every call, blurs what an invocation is permitted to do, and gives the model more room to choose wrong. The fixes are the ones that also make a server safer: expose the smallest set of tools a caller needs, keep each tool's job narrow and its description specific, and move predictable data-fetching out of the tool-call loop where you can, since every tool call costs a model reasoning step on top of the data it returns.
Logging, tracing, and debugging
MCP's Logging capability is deprecated, with stderr for stdio transports and OpenTelemetry for structured observability as the replacements. This is an annotation-only deprecation, so Logging keeps working in this release and for at least twelve months, and removing it would need a separate proposal. Treating deprecated Logging as the main debugging path for a new production server is a slow mistake rather than a fast one.
Debugging a shared server belongs in your observability stack, where you can see which tool was called, the shape of its arguments, its latency, what it called downstream, and how it failed. The documented W3C Trace Context keys are what make that work across boundaries. A single cart operation can be traced from the host application, through the client SDK, into your server, and out to whatever the server calls, and arrive as one correlated trace instead of disconnected log lines in four places.
Extensions, MCP Apps, and Tasks
The release makes extensions a formal part of the protocol. Capabilities can ship as opt-in extensions, identified by reverse-DNS IDs and negotiated through a capabilities map, and they version on their own timeline. For a server others depend on, that is the mechanism by which new capability arrives without breaking what you already built.
Tasks joins existing extensions including MCP Apps and Enterprise-Managed Authorization. Two especially relevant to production server teams are MCP Apps and Tasks. MCP Apps lets a server provide an interactive HTML interface that a host renders in a sandboxed iframe, with UI templates declared ahead of time so a host can prefetch, cache and security-review them. Every action the UI takes goes back through the same JSON-RPC base protocol as a direct tool call, so it travels the same audit and consent path rather than a side channel.
Tasks moves from an experimental core feature to an extension, reshaped around the stateless model. A server can answer tools/call with a task handle, and the client drives the task through task-specific operations. tasks/list is gone, because it cannot be scoped safely without sessions. Anything built on the earlier experimental Tasks lifecycle has to migrate.
Migrating from 2025-11-25
The maintainers describe these as breaking changes, so migrations should be tested rather than assumed. It helps to sort the work by what kind of change it is.
The changes that need code are the ones touching the request lifecycle. The initialize/initialized handshake and the Mcp-Session-Id session are gone, so protocol version and client capabilities move into _meta per request, clients should also include clientInfo, and any session-derived state moves to explicit handles. Sampling, elicitation, and roots now use Multi Round-Trip Requests: the server returns an input_required result and the client retries the original request with inputResponses. All results now carry a resultType, and long-lived change notifications move to subscriptions/listen. The Tasks lifecycle is new. Tool schema handling changes with JSON Schema 2020-12. And a client matching on the literal -32002 error code for a missing resource has to update it.
The changes that are operational rather than code are about how you run the server. Load balancing stops assuming session affinity. Gateways start routing on the new headers. Caching becomes a deliberate ttlMs and cacheScope decision. Logging moves to stderr or OpenTelemetry. And the deprecations of Roots, Sampling and Logging need a plan, though not an urgent one, given the twelve-month window.
Common traps
- Reading protocol statelessness as application statelessness, and dropping state your application still needs.
- Treating a handle as proof that the caller is allowed to use it.
- Shipping one broad tool where a few narrow ones would be safer, cheaper and easier to route to.
- Leaning on deprecated Logging as the main debugging path for a new server.
- Assuming an installed SDK speaks 2026-07-28 without an upgrade, migration, or explicit opt-in.
- Caching a user-specific result with a shareable cache scope.
Before you let others depend on it
Routing and scaling
- Load balancing assumes any instance can serve any request.
- Gateways route on
Mcp-MethodandMcp-Name, and servers reject header/body mismatches.
State
- Every piece of cross-call state is an explicit handle with an owner, a lifetime and a cleanup path.
- Handles are validated and scoped to the caller, and an expired or foreign handle fails cleanly.
Auth
- The server's token reaches only what its tools need, with read and write paths separated.
- Authorization servers emit
iss; clients validate it, prefer CIMD over DCR, and bind stored credentials to the issuer.
Tools
- Each tool has a stable, specific name and a precise description, and the exposed set is the smallest a caller needs.
- Tool schemas are depth-bounded and do not automatically dereference network
$refURIs.
Observability
- Tool calls emit traces using the documented W3C Trace Context keys, and logging goes to
stderror OpenTelemetry. - List and read responses set
ttlMsandcacheScope, with per-user data marked unshareable.
Migration
- Anything built on the old handshake, sessions, experimental Tasks, Roots, Sampling, Logging or the
-32002code has a migration plan, tested against the final spec.
The protocol can take a lot of infrastructure work off your plate. It does not take the operational ownership with it, and that ownership is what someone else is depending on when they build on your server.
Sources
- The 2026-07-28 MCP Specification, Model Context Protocol blog: https://blog.modelcontextprotocol.io/posts/2026-07-28/
- MCP 2026-07-28 specification: https://modelcontextprotocol.io/specification/2026-07-28
- MCP key changes (changelog): https://modelcontextprotocol.io/specification/2026-07-28/changelog
- Improving token efficiency in GitHub Agentic Workflows, GitHub blog (one public engineering example): https://github.blog/ai-and-ml/github-copilot/improving-token-efficiency-in-github-agentic-workflows/
Share
Author

Steve Kearns


