MCP 2026-07-28: what changed, and what it means for anyone in the middle

The 2026-07-28 revision makes MCP stateless: no handshake, no sessions, metadata on every request. Here is each change, sourced to the spec, with what it means for gateways and logs.

The short answer

MCP revision 2026-07-28 makes the protocol stateless. The initialize handshake is gone, protocol-level sessions and the Mcp-Session-Id header are gone, and every request now carries its protocol version and client capabilities in _meta.11 Servers must implement a new server/discover method.1 On Streamable HTTP, each POST also mirrors the method and tool name into headers, and a server must reject the request with 400 if a header disagrees with the body.2

For anyone who sits between an agent and its tools, that last rule matters most. A proxy that routes or applies policy on the header, while the server runs what the body says, is exactly the split the spec is trying to close.2

The previous revision was 2025-11-25.1 This post walks through the changes that affect security, logging and gateways. It skips the schema details.

Every MCP tool call goes through your rules first. Here a delete that doesn’t say which rows is stopped before it reaches the database. These are the rules deedglass init starts with; the call is made up.

The changes in one table

Each row comes from the revision’s own changelog or its Streamable HTTP page.

Area Before (up to 2025-11-25) In 2026-07-28
Handshake initialize, then notifications/initialized None; version and capabilities in each request’s _meta1
Sessions Optional Mcp-Session-Id, ended with DELETE Removed; servers keep state in handles passed as tool arguments1
Discovery From the initialize result server/discover, which every server must implement1
Server-to-client asks Server sent its own requests (sampling, elicitation, roots) Result with resultType: "input_required"; client retries with the answers1
Result shape No result type Every result carries resultType1
Change notifications GET stream, resources/subscribe subscriptions/listen on a long-lived POST response1
Broken streams Resumable with Last-Event-ID Not resumable; re-issue with a new request ID1
List caching listChanged only tools/list results carry ttlMs and cacheScope1
HTTP headers MCP-Protocol-Version Plus Mcp-Method, and Mcp-Name for calls and reads2
Tasks Experimental, in core An official extension1

Some things are on their way out rather than gone. Roots, Sampling and Logging are deprecated, and so is OAuth Dynamic Client Registration, in favour of Client ID Metadata Documents. Deprecated features keep a deprecation window of at least twelve months.1

Metadata on every request

Without a handshake, nothing is negotiated once and remembered. Each request says which protocol version it speaks and what the client can do.1 If the server doesn’t support that version, it answers 400 with an UnsupportedProtocolVersionError that lists the versions it does support. An unknown method gets 404 with JSON-RPC -32601. A notification it accepts gets 202 with no body.2

This is good news for anyone keeping records. A log line for one request now holds everything needed to interpret it: the version, the client’s identity if it sent one, and the call itself. Under the old model, the meaning of a tools/call could depend on a handshake that happened minutes earlier on the same session, which a log of single requests didn’t capture.

It also changes how state works. Servers that need state across calls now mint their own handles and pass them as ordinary tool arguments.1 That means the state is visible in the arguments, where a policy engine can see it, rather than hidden in a session id.

Multi Round-Trip Requests change one more thing. When a server needs the user’s input, or a model completion, it no longer sends its own request back to the client. It returns a result with resultType: "input_required", and the client retries the original request with the answers attached.1 For an approval flow, that means the same tool call can arrive twice: once without the extra input, once with it. Anything counting calls should count the final one.

Headers that must match the body

Streamable HTTP now mirrors parts of the JSON-RPC body into headers so load balancers and gateways can route without parsing JSON. Every POST carries MCP-Protocol-Version, which must equal the version in _meta, plus Mcp-Method. Calls to tools/call, resources/read and prompts/get also carry Mcp-Name.2 A name that isn’t plain ASCII travels Base64-encoded between =?base64? and ?=.2

A server that processes the body must reject a missing or mismatched header with 400 and JSON-RPC error -32020, HeaderMismatch.2 The spec gives the reason plainly: different components can rely on different sources of truth, such as a load balancer routing on the header while the server executes the body.2

Here is what the attack looks like when nobody checks:

Code A request one program sends to another over the web.

POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: list_tables

{"jsonrpc":"2.0","id":7,"method":"tools/call",
 "params":{"name":"drop_table","arguments":{"table":"orders"}, "_meta":{...}}}

A gateway with a rule “allow list_tables” that reads only the header lets this through. The server runs drop_table. Under 2026-07-28 a conforming server refuses it, but only if it processes the body and checks. A gateway that makes decisions should parse the body itself, or at least confirm the two agree before it allows anything.

The spec adds a second warning for intermediaries. One that enforces policy on mirrored headers should check that MCP-Protocol-Version names a version that requires header and body validation. If the version is older or the header is missing, it should reject the request rather than trust headers nobody validated.2

Servers can also ask clients to mirror chosen tool parameters into Mcp-Param- headers, using x-mcp-header in the tool’s input schema.1 The same rule applies: a header is a copy, and the body is what runs.

What breaks for older clients

A server that only speaks 2026-07-28 will see old-style traffic for a while. The spec says it should answer GET and DELETE on the endpoint with 405, and ignore Mcp-Session-Id and Last-Event-ID.2 Clients that support both eras try a modern request first and look inside any 400 before falling back to initialize, because modern servers also use 400 for version and header errors.2

Two changes can surprise code that worked before:

  1. Lost streams stay lost. With resumability gone, a dropped response stream loses the in-flight request, and the client must send it again with a new request ID.1 For a tool that writes, that retry is a second write unless the tool is idempotent. Our post on agents that retried and wrote twice covers the fix.
  2. Session-scoped rules lose their anchor. Any gateway rule keyed on Mcp-Session-Id, such as “this session already got approval”, needs a new key. The agent’s own identity, from authorization, is the obvious one.

The authorization changes are smaller. Authorization servers should include iss in their responses, and clients must check a present iss against the issuer they recorded before redeeming a code.1

A checklist for gateways and logs

If you run a proxy, a gateway or a logging layer in front of MCP servers, these are the checks the new revision calls for.

  1. Decide on the body, not the headers. Parse params.name and params.arguments. Use the headers for routing only after confirming they match.2
  2. Reject old or missing versions where you rely on headers.2
  3. Decode Base64 header values before comparing.2
  4. Log the _meta version and client info with each call. Each request now carries them.1
  5. Count the final round of a multi round-trip request, not every attempt.1
  6. Make writes safe to retry. Streams no longer resume.1
  7. Re-key anything that used session ids.1
  8. Keep validating Origin and answer 403 when it is present and invalid; bind local servers to localhost.2

The basics in MCP server security still apply on top of these. What an MCP gateway does, and how the options compare, is on the comparison page.

What this post doesn’t cover

  • The full schema. JSON Schema 2020-12 for tool schemas, error-code renumbering and the tasks extension’s methods are in the changelog.1
  • Which clients and servers have shipped it. Adoption moves weekly; check your SDK’s release notes rather than a blog post.
  • Whether a given gateway implements the header check. Test it: send a mismatched Mcp-Name and see whether you get 400 and -32020.

If you want to know which MCP servers your own machine is configured to run, npx deedglass scan reads the config files locally; the scan reads files on your machine and sends nothing to us.3 For tool calls themselves, a tool-call policy decides on the arguments in the body, which is where 2026-07-28 says the truth is. To try the checks without installing anything, open the playground; to grade what an agent can reach on your own machine, run npx deedglass scan (the grade, explained).

Written with AI assistance and checked against our claims register.

The takeaway

MCP 2026-07-28 drops initialize and sessions; a gateway must now check that mirrored headers match the body, or it routes on one value and runs another.

Read nextMCP security checklist: what to check before an agent gets a tool Try it: install in 7 s

Related on this site

More posts

Sources

  1. 1MCP specification 2026-07-28: Key Changes · modelcontextprotocol.io · checked 26 October 2026
  2. 2MCP specification 2026-07-28: Streamable HTTP · modelcontextprotocol.io · checked 26 October 2026
  3. 3Deedglass docs: the scan · docs.deedglass.com · checked 26 October 2026

Published 26 October 2026, sources last checked 26 October 2026. If something here is out of date, write to hello@deedglass.com.

Try it on your own setup

npx deedglass scan

Grades only your own setup. Nothing is uploaded.

Pricing