Model Context Protocol
TL;DR

MCP shipped its biggest protocol revision since launch on July 28, 2026. The session handshake and the Mcp-Session-Id header are gone, making MCP stateless at the transport layer. Two experimental features, MCP Apps (interactive UIs) and Tasks (long-running work), graduate into official extensions, and OAuth authorization gets six hardening changes. The Python and C# SDKs already ship stable v2 releases that speak both the old and new wire formats from the same server, so most running MCP servers keep working without changes. But the wire format is incompatible with older clients once you fully adopt it, Python's SDK doesn't ship the Tasks extension yet, and enterprise SSO is still on the roadmap, not shipped. Here's what actually changed, how to hold state without sessions, and what we'd check before touching a production MCP server.

If you run any kind of MCP server or connector, something changed under you on July 28, 2026, and it's worth five minutes even if nothing looks broken yet. MCP's maintainers just shipped the biggest rework of the protocol since it existed: your server stops remembering things between requests, two experimental features became official building blocks anyone can use, and login checks just got stricter. Nothing breaks today. But the next time you scale your server, add a slow tool, or touch your login flow, this update decides how much that costs you.

Anthropic built MCP and handed it to the Linux Foundation in December 2025. The group that owns it now (Anthropic, OpenAI, Microsoft, and a mix of community engineers) doesn't version its releases the way most software does. There's no MCP 2.0. Instead, each release is just named after the day it shipped, so officially this one is called "2026-07-28." We'll mostly just call it the July update.

If you build or maintain MCP servers, this update changes how your server remembers things, how it handles slow jobs, and how its login flow gets checked. We read through the spec, the SDK release notes, and the GitHub threads where developers hit the sharp edges, so this covers what changed and how to actually build against it — starting with what each piece means in plain terms, then how you'd use it. If you're weighing whether to build this yourself, MCP server build vs. outsource covers that decision; for the deeper patterns once your server is running, how to design MCP tools your AI agent won't misuse is the companion piece.


What actually changed in MCP's July update?

Think of the old version of MCP like a phone call. When your AI tool connected to an MCP server, the two of them shook hands first, and the server kept that call open so it could remember who it was talking to. That's fine for one call at a time. It falls apart once you have thousands running at once, because now one specific machine has to keep picking up every time, or the conversation breaks.

The July update hangs up that phone call for good. Every message the AI tool sends now carries everything the server needs to understand it, the same way a text message includes enough context that the other person doesn't need to remember your last conversation. Any server in a whole group of them can now answer any message, because nothing lives in memory between requests. That's what "stateless" means here, and it's the one change everything else in this update is built on top of.

Two more things happened alongside it. MCP Apps let a server open an actual working screen inside the chat, like a chart you can click through, instead of the AI just describing numbers in text. And Tasks let a server say "give me a minute" on slow work, generating a report, say, instead of making the AI sit there waiting. Behind the scenes, login checks (OAuth) also got six specific fixes to close real security gaps.

A handful of smaller changes shipped alongside all this. Servers can now tell clients how long a list of available tools stays valid, so nothing has to ask again unnecessarily. A few older features (Roots, Sampling, and Logging, for the technically curious) got a 12-month retirement notice. And tool descriptions can now describe more complex inputs than before.

2026-07-28 SPEC DIFF vs 2025-11-25 - REMOVED initialize handshake Mcp-Session-Id + ADDED Mcp-Method Mcp-Name + tool-list expiry GRADUATED MCP Apps Tasks now official extensions ~ DEPRECATED Roots Sampling Logging 12-month window ! HARDENED OAuth 6 authorization changes

Why did MCP go stateless?

MCP went stateless so a remote server can run behind an ordinary round-robin load balancer instead of needing sticky sessions and a shared session store. Under the old model, a client called initialize, got back an Mcp-Session-Id, and every following request had to land on whichever server instance issued that ID. That meant Redis-backed session stores, sticky routing rules, and a whole class of "which pod has this session" bugs eating real engineering time. One MCP maintainer put it plainly on the Hacker News thread announcing the release: "this is an exciting change for those that wanted to roll out remote MCP servers into serverless hosts."

BEFORE · STICKY ROUTING CLIENT LB POD 1 POD 2 POD 3 sticky sessions · session store AFTER · ROUND-ROBIN CLIENT LB POD 1 POD 2 POD 3 any instance · no session store

We'd go further than that. A tool call is really just a question and an answer: the model asks for something, gets a response, and decides what to do next. Making a server remember a session for that kind of exchange was awkward from the start, the same awkwardness REST APIs solved years ago by not depending on server memory at all (see our MCP vs REST API comparison for how the two differ under the hood). The team behind the change pointed out a real cost too: because a session could technically change which tools were available, every client had to keep re-checking, even though almost no server actually behaved that way. The old model also allowed exactly one session per client, which got in the way of things like sharing a cart across parts of a system while keeping other state separate. MCP's C# SDK already made statelessness the default, switched on out of the box in its newest release.

How do you handle state in a stateless MCP server?

This only matters if your server does more than answer single, self-contained questions. A tool that looks up a stock price doesn't need to remember anything between calls; a tool that walks a user through a multi-step checkout does. For that second kind, you mint an explicit handle from a tool call and have the model pass it back as an ordinary argument on later calls, the same pattern REST APIs have used for years. A tool like start_checkout returns a basket_id, and every following call (add_item, apply_discount, checkout) takes that basket_id as a normal parameter, the way a coat-check ticket carries everything needed to get your coat back from any attendant, not just the one who took it. The model composes that handle across tool calls the way it composes any other value, and that turns out to be more capable than hidden session state ever was: it can reason about the handle, hand it to a different tool, or drop it and start over.

start_checkout mints basket_id add_item basket_id apply_discount basket_id checkout basket_id PASSED BACK AS A NORMAL PARAMETER — NOT A SESSION

Sometimes a server needs to ask the user something mid-task, like confirming a deletion. Since a server can no longer keep a connection open and wait for an answer, MCP handles this differently now too: the server sends back a note saying it needs more information, the client asks the user, and then re-sends the original request with the answer attached.

{
  "resultType": "input_required",
  "inputRequests": {
    "confirm": {
      "type": "elicitation",
      "message": "Delete 3 files?",
      "schema": { "type": "boolean" }
    }
  },
  "requestState": "eyJzdGVwIjoxLCJmaWxlcyI6WyJhIiwiYiIsImMiXX0="
}

The client collects the answer and re-sends the original call with inputResponses and the echoed requestState. Any server instance can pick that retry up, because everything it needs travels in the payload. One thing to get right the first time: requestState is opaque to the client, but it isn't secret. Sign or encrypt it before handing it back, the way you'd treat a JWT. Store it server-side keyed to a session instead, and you've quietly rebuilt the statefulness this whole redesign was meant to remove.

Will your existing MCP server break under the new spec?

Not immediately, if you're on an official SDK, but plan the migration deliberately rather than assuming nothing changes. Python, TypeScript, and C# all shipped v2 releases that serve both protocol eras from the same running server, and on the TypeScript SDK the new wire format is opt-in: nothing changes until you configure version negotiation or move to the new HTTP entry point. Upgrading the SDK alone doesn't silently flip your server's wire format.

63,000 open-source MCP servers indexed wire-incompatible in both directions ~30% · pushed to in last 30 days 70% · long tail

What does change is anything downstream. The operator of Glama, an MCP registry and gateway, shared numbers on the same Hacker News thread: of roughly 63,000 open-source MCP servers indexed, only about 30% had been pushed to in the last 30 days, and the new protocol is "wire-incompatible in both directions." Most of that long tail won't pick up dual-era support on its own, so test any third-party server rather than assume it negotiates gracefully. And once you do send the new Mcp-Method/Mcp-Name headers, a strict gateway sitting in front of an old, unpatched server can reject the request outright. SDK maturity varies by language too: Java is officially a Tier 2 SDK, which gives it up to six months to catch up under MCP's own tiering policy.

SDKStateless coreTasks extensionMCP AppsNotes
Python (mcp)✅ v2.0.0 stable❌ not in v2.0.0✅ built-in extension APIpip install mcp now installs 2.x; v1.x is maintenance-only
TypeScript✅ v2 (beta at RC time)Ships via extensions framework✅ via @modelcontextprotocol/ext-appsSplit into /core, /client, /server packages in v2
C#✅ v2.0.0 stable, stateless by defaultModelContextProtocol.Extensions.Tasks package✅ dedicated Apps packageEmits build-time deprecation warnings for old patterns
JavaTier 2 (in progress)Tier 2 (in progress)Tier 2 (in progress)Up to 6 months per the SDK tier policy

What is the MCP Tasks extension?

Tasks is an official MCP extension for tool calls that take too long to answer in one request-response cycle: generating a report, running a batch job, waiting on a slow upstream system. Instead of the model sitting on an open connection waiting, the server hands back a ticket it can check on later, the way a busy kitchen hands you a buzzer instead of making you stand at the counter. Task creation is server-directed: your client doesn't ask for a task, it signals it can handle one, and the server decides when a tools/call is slow enough to warrant a task handle instead of a direct result.

How do you build with the Tasks extension?

You drive a task forward with tasks/get, tasks/update, and tasks/cancel instead of waiting on the original request. tasks/list is gone entirely in this revision, because a server can't scope it safely without the session state the protocol just removed. Check your SDK before you build against this: Tasks moved from an experimental core feature into its own extension because the redesign was substantial, so anyone who built against the old API has to migrate to the new lifecycle, not just bump a version.

tools/call server decides TASK HANDLE check on it later client drives tasks/get tasks/update tasks/cancel

The gap that actually bites right now is Python. SDK v2.0.0 doesn't include Tasks at all, and the release notes say it won't land in the 2.x line until later. C# ships it as a separate package with its own registration call. And if you're building a server that emits tasks, design for retries early: reviewers flagged during the spec review that a retried tasks/get carrying inputResponses has no defined idempotency guarantee, so a network retry can double-execute a side effect if you're not careful.

What is an MCP App?

An MCP App is a small, sandboxed web page your server can pop directly into the chat: a chart, a form, a dashboard, instead of the model describing the same data in text. The tool declares a UI resource in its metadata, the host renders it inside a sandboxed iframe, and the app talks back to the host over the same JSON-RPC protocol MCP uses everywhere else, so every UI-initiated action still goes through the same consent path as a direct tool call.

Claude · Copilot · ChatGPT · Goose show me the dashboard APP · ui:// resource SANDBOXED IFRAME live-updating

How do you build an MCP App?

You declare a _meta.ui.resourceUri pointing to a ui:// resource on the tool, and serve an HTML page from that resource that the host renders inside the iframe. The tool description tells the host what to preload; when the model calls the tool, the host fetches the UI resource and opens a postMessage channel so the app and host can talk. The app can call server tools, and the host can push fresh results in without another prompt round-trip.

import { App } from "@modelcontextprotocol/ext-apps";

const app = new App();
await app.connect();

app.ontoolresult = (result) => {
  renderChart(result.data);
};

const response = await app.callServerTool({
  name: "fetch_details",
  arguments: { id: "123" },
});

The security model is why this beats a plain web app when the use case fits: the iframe can't touch the parent page's DOM, cookies, or local storage, and every UI-initiated tool call goes through the same audit and consent path as a direct call from the model. It's overkill for a static form, but for data exploration, configuration wizards with dependent fields, or anything with live-updating state, it beats a back-and-forth text exchange. Client support already spans Claude, VS Code's GitHub Copilot, ChatGPT, and Goose, and the ext-apps repository ships starter templates for React, Vue, Svelte, and vanilla JavaScript.

What changed in MCP's authorization rules?

Six changes harden MCP's login flow (OAuth) to match how it's actually used in the real world. In plain terms, this batch is about making sure your login flow can't be tricked into talking to the wrong server, and about not breaking auth for developers running local tools with checks that were built for web apps.

What do you need to check in your OAuth flow?

Start with confirming who actually issued a login token. A new required field called iss tells the client which server a token really came from, closing off a trick where a fake login server could pose as your real one. This matters more for MCP than most systems, because one AI tool often talks to many independent servers, each with its own login system, and a future MCP version will simply refuse tokens that skip this field. If you run a login server, add it now.

A second fix solves an annoying, recurring bug: login servers used to assume every client was a website, which broke local development tools trying to log in from your own laptop. Now a client tells the server upfront what kind of app it is, so that assumption stops causing problems.

None of this replaces a full enterprise SSO story. Cross-App Access and SSO-integrated auth are still a forming Enterprise Readiness priority on MCP's own roadmap, not something this release ships. If a vendor tells you MCP now has enterprise auth, ask what specifically shipped — it's OAuth hardening, not centralized SSO management.

SHIPPED · OAUTH HARDENING iss validation PKCE requirements DCR fixes +3 more login flow can't be tricked into the wrong server NOT SHIPPED enterprise SSO still on the roadmap

What should you watch for when migrating to the July update?

Watch for SDK-level bugs filed since the release and the parts of the spec that are still explicitly unfinished. Two filed, reproducible bugs are worth knowing before you ship. On the TypeScript SDK, a server factory that returns the same instance across requests (an easy mistake, and the natural workaround once you've read that per-request allocation has a cost) crashes with an uncatchable stack-overflow error after roughly 19,000–25,000 requests, and it surfaces after the handler's close has already resolved, so your error handling never sees it. On the Python SDK, the default stateful mode registers a new session before validating the incoming request at all, so a rejected request still leaks a live session. Neither is a reason to avoid the SDKs; both were reported with working patches attached, and they're the kind of thing that only surfaces under real request volume, not in normal testing.

Binary and file handling remains an open gap by design, not oversight. On the Hacker News thread, a developer asked directly about clients still fumbling base64-encoded files into the context window, and the lead maintainer's reply confirmed it's deferred for this release, with the relevant proposal still in draft. If your server pushes anything larger than small text payloads, that's still your problem to solve. And not everyone thinks this round of churn was worth it: the strongest pushback on that same thread argued that plain HTTP calls with good documentation beat hand-rolled MCP tool semantics for backend-to-backend work, since models are trained deeply enough on raw HTTP that the structured layer adds overhead without matching benefit. The counter-argument is that MCP's value shows up for less technical end users and for tools that need explicit, auditable semantics a bare HTTP call doesn't give you. Worth deciding deliberately for what you're building, not by default.


If your MCP server still runs on the old spec, migrating it (dropping sessions, picking up Tasks, meeting the new auth rules) is exactly the kind of work our MCP server development team takes on. And if you want to test the rewritten server against a real enterprise system before it goes anywhere near production, we can run that testing on our own maintained sandboxes for major ERPs (SAP S/4HANA, NetSuite, D365, Coupa, Workday) instead of you provisioning access yourself.

Frequently asked questions

Is MCP still stateful?

No. As of MCP's July 2026 update, the protocol is stateless by default — the initialize handshake and Mcp-Session-Id header are removed, and every request is self-contained. Servers that need to track state across calls do it explicitly, by minting a handle like a basket_id and having the model pass it back, not through a protocol-managed session.

What happened to Mcp-Session-Id?

It's gone as of this update, along with the initialize/initialized handshake it depended on. Any MCP request can now land on any server instance, which is what lets a remote MCP server run behind a plain round-robin load balancer instead of sticky routing.

Do I need to rewrite my MCP server for the new spec?

Not urgently if you're on an official SDK. The Python, TypeScript, and C# v2 SDKs serve both the 2025-11-25 and 2026-07-28 wire formats from the same running server, so existing deployments keep working. Test against the exact SDK version you ship, since third-party servers that haven't updated won't negotiate the new wire format on their own.

What is the MCP Tasks extension?

Tasks is an official MCP extension for long-running work: a server can answer a tools/call with a task handle instead of a result, and the client drives it forward with tasks/get, tasks/update, and tasks/cancel. It graduated from an experimental core feature to a standalone extension in this release, and Python's SDK doesn't include it yet.

How do MCP Apps work?

A tool declares a ui:// resource in its metadata; the host fetches that resource, renders it as HTML inside a sandboxed iframe, and opens a postMessage-based JSON-RPC channel so the app can call server tools and receive live results. The sandbox keeps the app from touching the host page's DOM or cookies, and every UI-initiated tool call still goes through the same consent path as a direct call.

Is MCP an Anthropic product or an open standard?

It's an open standard now. Anthropic created MCP and donated it to the Linux Foundation in December 2025; MCP's July 2026 update comes from that multi-vendor steering committee, with Anthropic, OpenAI, Microsoft, and others contributing.

Does MCP support enterprise SSO now?

Not fully. This release hardens OAuth authorization (issuer validation, PKCE requirements, Dynamic Client Registration fixes), but full enterprise-managed SSO is still a forming priority on MCP's roadmap, not something this release ships.

When should I upgrade my MCP server to 2026-07-28?

It depends on your SDK. Python and C# already have stable v2 releases built for it; TypeScript's v2 line is close behind. On Java, budget for a lag — it's officially a Tier 2 SDK with up to six months to catch up. Pin the exact SDK version rather than a floating range while the ecosystem is still mid-transition.

What's still missing from the 2026-07-28 spec?

Binary and file handling is the clearest gap. MCP's own lead maintainer confirmed on Hacker News that proper file-transfer support was deferred for this release. Enterprise-managed SSO auth is also not shipped, just OAuth hardening. If your server needs either today, plan around the gap rather than the roadmap.