MCP Went Stateless: What the 2026-07-28 Spec Changes for Your Server
The Model Context Protocol spec dated 2026-07-28 is the largest revision since the protocol launched, and the headline is the one server operators have been asking for: MCP is now a stateless request/response protocol. No initialize, no notifications/initialized, no Mcp-Session-Id. Every request carries what the server needs to answer it. That is what lets a server sit behind an ordinary load balancer without sticky sessions.
If you wrote a server against 2025-11-25, nothing breaks today. Clients that speak the older revision still work, and the spec says to keep accepting the old error codes they send. The changes below are what to do when you move.
Each request now carries its own context
The handshake is gone. In its place, every request includes _meta fields under the io.modelcontextprotocol/* namespace:
| Key | Required | What it carries |
|---|---|---|
io.modelcontextprotocol/protocolVersion | Yes | The version for this request, for example "2026-07-28" |
io.modelcontextprotocol/clientCapabilities | Yes | The client capabilities relevant to this request |
io.modelcontextprotocol/clientInfo | No | Client name and version |
io.modelcontextprotocol/logLevel | No | Minimum log level the server should emit for this request |
Servers identify themselves in each result's _meta with io.modelcontextprotocol/serverInfo. A version the server does not support returns UnsupportedProtocolVersion (-32022). There is an optional server/discover for clients that want to learn about a server before calling anything, but a client no longer has to.
What this changes in code: any per-connection state your server kept between initialize and the first tools/call has to go. The spec is explicit that tools/list, resources/list, and prompts/list no longer vary per connection. If a tool needs state across calls, the server mints a handle and the client passes it back as an ordinary tool argument.
Two headers your gateway can route on
Streamable HTTP requests must include Mcp-Method and Mcp-Name. The method is the JSON-RPC method; the name is the tool, prompt, or resource being addressed. That is a small change with a large operational payoff. A rate limiter, WAF, or gateway can meter tools/call on delete_account differently from tools/list without parsing JSON bodies. If you were doing body inspection in a proxy to get the same effect, you can delete it.
List results are cacheable on purpose
tools/list, prompts/list, resources/list, and resources/read results now carry ttlMs and cacheScope. Clients use them to stop re-fetching catalogs they already have. For a server, this means two things. Set a ttlMs that reflects how often your catalog actually changes. And stop assuming a client has fetched the list recently; it may be serving a cached one, which is another reason lists cannot vary per connection.
Change notifications moved to a single stream
The old HTTP GET endpoint and resources/subscribe are replaced by subscriptions/listen: one long-lived POST response stream the client opts into, per notification type (toolsListChanged, promptsListChanged, resourcesListChanged, resourceSubscriptions). The server acknowledges and tags each notification with io.modelcontextprotocol/subscriptionId.
Request-scoped notifications — progress and log messages — still flow on the response stream of the request they belong to, not on the listen stream. If your server emitted progress on a side channel, it moves back to the request.
Tasks are an extension now
Long-running work moves out of the core into io.modelcontextprotocol/tasks. The server decides when a tools/call should become a task and returns a handle. The client polls with tasks/get, sends mid-flight input with tasks/update, and cancels with tasks/cancel. The blocking tasks/result is gone. tasks/list is gone too, because it cannot be scoped safely without sessions.
The extensions framework is the bigger structural point. New capability ships as an opt-in extension, negotiated explicitly, and stabilizes there before it is ever considered for the core. MCP Apps (server-rendered UI in a sandboxed iframe, talking to the host over the same JSON-RPC) and Enterprise Managed Authorization are extensions in the same sense.
Authorization: validate iss, plan to leave DCR
Authorization servers must return the iss parameter per RFC 9207 and clients must validate it before redeeming a code. That closes an authorization-server mix-up hole. Dynamic Client Registration is formally deprecated in favor of client metadata documents (CIMD). DCR keeps working for now; the spec has a formal deprecation policy with a minimum window between deprecation and removal, so you have time, but new servers should not start on DCR.
Also deprecated: Roots, Sampling, Logging as a negotiated capability (log level now rides on each request), the older HTTP+SSE transport, and the resource-not-found code -32002, replaced by -32602. Accept the old codes from old clients. Do not emit them.
A migration order that does not break clients
- Add the required
_metafields to your client and theserverInfofield to your server results. Keep answeringinitializefor older clients. - Remove any per-connection state behind
tools/listand friends. Mint handles for cross-call state. - Emit and require
Mcp-MethodandMcp-Nameon Streamable HTTP. Update the gateway rules. - Add
ttlMsandcacheScopeto list results. - Move progress and log notifications onto the request stream; move list-changed notifications to
subscriptions/listen. - If you have long-running tools, adopt the Tasks extension instead of a home-grown polling endpoint.
- Start new auth integrations on CIMD and validate
iss.
The payoff is a server you can scale horizontally without a session store, and a catalog clients stop hammering. How you describe the tools in that catalog still matters as much as before — the routing problem is covered in MCP tool descriptions that route the model. The spec and changelog are at modelcontextprotocol.io.
Keep reading
MCP Tool Descriptions That Route the Model to the Right Call
How to write MCP tool names, descriptions, and schemas so the model picks one tool on purpose, and what to do when two tools sound the same.
MCP Resources vs Tools vs Prompts: Stop Putting Everything in a Tool
The three primitives in the Model Context Protocol, what the model actually sees, and a rule of thumb for Dataverse, files, and internal APIs.
An MCP Server for Dynamics 365 Finance and Operations: Natural-Language Access to ERP
How to expose Dynamics 365 Finance and Operations to Claude through MCP — the architecture, the data entity choices, the auth model, and the operations that should never be one prompt away.
Building Your First MCP Server in Python: A Hands-On Walkthrough
From zero to a working Model Context Protocol server in about 100 lines — tools, resources, a local client test, and the traps that will bite you on day one.
Indirect Prompt Injection: Tool Output Is Not Instructions
A retrieved document, an email, or a tool result can tell the model to take an action. Delimiters do not stop it. The tool allowlist after untrusted text does.
Copilot Auto Model Selection Now Has Tiers: Efficiency, Balance, Intelligence
GitHub Copilot's Auto picker gained efficiency, balance, and intelligence tiers in September 2026, alongside GPT-6 Astra and GPT-6.1 Sol. How the tiers change the cost-quality trade, and what admins control.
Newsletter
New posts, straight to your inbox
One email per post. No spam, no tracking pixels, unsubscribe anytime.
Comments
- No comments yet. Be the first.