Protocol specification
MCP spec 2026-07-28: the seven changes, and what to do about them
- Published on
- 2026-07-28
- Previous revision
- 2025-11-25
This page is a technical reference, not a sales page. It lists what changes between revision 2025-11-25 and revision 2026-07-28, what that actually breaks in an existing server, and what needs redoing. The SEP numbers are there so you can check everything at the source.
The seven changes at a glance
- Effort Heavy
The server keeps nothing between two calls
Your server can no longer remember who is talking between two calls.
- Effort Moderate
Two pieces of information become mandatory on every request
Two pieces of information become mandatory on every request.
- Effort Heavy
The server can no longer speak when nothing was asked
The server can no longer send a message outside a request.
- Effort Light
One error code changes number
One error code changes number.
- Effort Light
Long-running tasks leave the core
Long-running tasks become a separate extension.
- Effort Moderate
Authentication gets stricter
Authentication requires more checks on the server side.
- Effort Moderate
Four features enter end of life
Four features enter end of life, with 12 months ahead of them.
The detail, change by change
The server keeps nothing between two calls
SEP-2575SEP-2567- What breaks
- Today your server opens a session on first contact and uses it to remember who is talking and where they left off. That session goes away.
- What we do
- Every tool becomes self-contained and receives what it needs on each call. It is the heaviest part of the migration, and the only one that really touches your architecture.
Before
const sessions = new Map<string, AuthContext>();// The handshake seeds everything that follows.onInitialize((req) => { const id = randomUUID(); sessions.set(id, authFrom(req)); return { sessionId: id };});tool("list_forms", (args, ctx) => listForms(sessions.get(ctx.sessionId)));After
// No handshake, no session map, no Mcp-Session-Id.// Each handler resolves its own auth, and paging// state travels as an explicit argument.tool("list_forms", ({ cursor }, req) => listForms(authFrom(req), cursor));Two pieces of information become mandatory on every request
SEP-2243- What breaks
- The server has to require them, and reject requests that contradict themselves.
- What we do
- We check the whole network path. A firewall or proxy that strips these headers breaks the server silently, and the symptom looks like a client-side bug. It is the most expensive failure to diagnose if you have never seen it.
The server can no longer speak when nothing was asked
SEP-2260SEP-2322- What breaks
- Progress bars and notifications pushed continuously during a long job stop working.
- What we do
- The tool asks its question, hands control back, and the client returns with the answer. Your interactive flows need rethinking rather than porting.
Before
// Pushing on the long-lived GET channel.tool("import_listings", async (args, ctx) => { for (const row of args.rows) { await ctx.notify({ progress: row.index }); } return { imported: args.rows.length };});After
// The server cannot push outside an active// request. Ask, return, let the client come back.tool("import_listings", (args) => { if (!args.confirmed) { return InputRequiredResult({ prompt: "Import 412 listings?", }); } return { imported: args.rows.length };});// The client replies with inputResponses.One error code changes number
SEP-2164- What breaks
- Resource not found moves from -32002 to -32602.
- What we do
- We track down every test that compares that number literally. It is the quietest line in the revision, and the one that breaks your tests and in-house clients most silently.
Before
if (!form) { throw new McpError(-32002, "Form not found");}After
if (!form) { throw new McpError(-32602, "Form not found");}Long-running tasks leave the core
SEP-2663- What breaks
- Tasks becomes a separate extension, with its own rules.
- What we do
- We rewire onto the extension, or drop the dependency. Plenty of servers declare Tasks without ever using it, and this is when you find out.
Authentication gets stricter
RFC 9207RFC 9728- What breaks
- The server has to check more about the tokens it receives, and publish its connection details in exactly the expected format.
- What we do
- We go through the OAuth setup end to end. The old way of registering clients is deprecated: it still works, and it will go.
Four features enter end of life
SEP-2577SEP-2596- What breaks
- Roots, Sampling, Logging and the legacy SSE transport are deprecated.
- What we do
- Each has a simple replacement: tool parameters, a direct model call, ordinary logs, the new transport. Nothing insurmountable, but four clocks running at once.
What does not break
No server stopped on 28 July 2026. A publication date is not a switch, and a server written for revision 2025-11-25 still answers normally today. The protocol policy guarantees at least 12 months before anything is actually removed, so nothing deprecated here can disappear before July 2027. What does move before that date is directory reviews and client compatibility testing, which already line up with the new revision.
Want to know what this looks like on your server?
The audit compares your server line by line against this revision, ranks what breaks by severity and prices each item. 950 EUR, two days, a written document. If the finding is that you have nothing to migrate, you keep the document and I sell you nothing else.
Get my MCP server audited