2026 07 28 Migration
FreeNot checkedPractical migration guide: MCP servers to the 2026-07-28 spec — stateless core, server/discover, sessions removed. Copy-paste before/after for every breaking ch
About
Practical migration guide: MCP servers to the 2026-07-28 spec — stateless core, server/discover, sessions removed. Copy-paste before/after for every breaking change.
README
A practical, copy-paste migration guide for server authors. Everything here is sourced from the official 2026-07-28 changelog and spec pages — each section links the authoritative text.
Status of this guide: written 2026-08-10, thirteen days after the spec shipped. Corrections welcome as issues or PRs.
1. Does this affect you?
Run this against your server's source:
grep -rn "Mcp-Session-Id\|notifications/initialized\|\"initialize\"\|logging/setLevel" src/
Any hit in live protocol code means you are implementing surfaces that the 2026-07-28 revision removed. As of 2026-08-10, roughly 64,000 files on GitHub still carry the removed session header.
If you build on an official SDK (@modelcontextprotocol/sdk and the other
Tier 1 SDKs — updated during the pre-release validation window), most of the
transport-level migration arrives with an SDK upgrade; your work concentrates
in §4.4 (session state), §4.5 (resultType) and §4.6 (cache fields). If you
hand-roll the protocol, everything below applies.
2. The mental model shift, in one paragraph
The protocol is now stateless. There is no handshake and no protocol-level
session: every request independently declares its protocol version and
capabilities in _meta, and the server accepts or rejects each request on its
own (versioning).
Anything your server used to remember between calls either moves into
explicit, server-minted handles passed as ordinary tool arguments
(SEP-2567),
or it disappears.
3. Compatibility reality — why this matters now
From the spec's own compatibility matrix:
| Client | Your server | Outcome |
|---|---|---|
| Modern (2026-07-28) | Legacy (handshake-based) | Fails. |
| Modern | Modern | Works |
| Dual-era | Either | Works |
| Legacy | Modern-only | Fails (legacy clients have no fall-forward) |
A modern-only client hitting a legacy server does not degrade gracefully — the spec says it "may reject the request with an implementation-defined error, stay silent, or even process an era-ambiguous method under legacy semantics."
Removed features are already outside the current revision. Features listed as deprecated (not removed — see §6) carry a minimum twelve-month clock.
4. Migration checklist
Work through these in order. Before/after examples are verbatim from the spec where marked; otherwise they illustrate the pattern.
4.1 Implement server/discover — REQUIRED
Servers MUST implement this RPC (spec). Request (verbatim from the spec):
{
"jsonrpc": "2.0",
"id": "discover-1",
"method": "server/discover",
"params": {
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": { "name": "ExampleClient", "version": "1.0.0" },
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}
Response (verbatim from the spec):
{
"jsonrpc": "2.0",
"id": "discover-1",
"result": {
"resultType": "complete",
"supportedVersions": ["2026-07-28"],
"capabilities": { "tools": {}, "resources": {} },
"_meta": {
"io.modelcontextprotocol/serverInfo": { "name": "ExampleServer", "version": "1.0.0" }
},
"instructions": "This server provides weather and resource utilities.",
"ttlMs": 3600000,
"cacheScope": "public"
}
}
Clients also use this as the stdio backward-compatibility probe — implementing it is what makes your server detectably modern.
4.2 Accept per-request version metadata; reject mismatches correctly
Every incoming request now carries its protocol version in
params._meta["io.modelcontextprotocol/protocolVersion"] (on HTTP, also the
MCP-Protocol-Version header). If you don't support the requested version,
respond with UnsupportedProtocolVersionError — code -32022 — listing what
you do support (verbatim from the spec):
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32022,
"message": "Unsupported protocol version",
"data": {
"supported": ["2026-07-28", "2025-11-25"],
"requested": "1900-01-01"
}
}
}
Clients retry with a version from your supported list.
4.3 Remove the handshake — or go dual-era
initialize and notifications/initialized no longer exist in this revision
(SEP-2575).
Two valid strategies:
- Modern-only: delete the handshake paths. The spec adds one SHOULD: name
your supported versions in whatever error you return to an
initializerequest — it may be the only diagnostic a legacy client can show its user. - Dual-era (recommended during the transition): serve both. The server
selects per interaction: a request carrying modern per-request
_metais served statelessly; aninitializerequest selects legacy semantics for that session/process (spec).
4.4 Remove Mcp-Session-Id; move cross-call state to handles
Protocol-level sessions are gone (SEP-2567). List endpoints no longer vary per-connection. State that must survive across calls becomes a server-minted handle passed as an ordinary tool argument. Illustration of the pattern (not verbatim spec):
Before — state keyed on the transport session:
POST /mcp (Mcp-Session-Id: abc123)
tools/call add_to_cart {"item": "..."} // server finds the cart via the header
After — state keyed on an explicit handle the server minted:
tools/call cart_create {} -> {"cartId": "c_8f3a"}
tools/call add_to_cart {"cartId": "c_8f3a", "item": "..."}
The handle is visible, loggable, and survives client reconnects — which is the point.
4.5 Add resultType to every result
All results now carry a required resultType field: "complete" for ordinary
results, "input_required" for
Multi Round-Trip Request
interim results (SEP-2322).
MRTR also replaces server-initiated requests (sampling/createMessage,
elicitation/create, roots/list): you return resultType: "input_required"
with inputRequests, and the client retries the original request carrying
inputResponses.
4.6 Add ttlMs and cacheScope to list/read results
Required on results of tools/list, prompts/list, resources/list,
resources/read, resources/templates/list
(SEP-2549).
ttlMs is a freshness hint in milliseconds; cacheScope is "public" or
"private". Also: return tools/list in deterministic order — clients cache,
and stable order improves LLM prompt-cache hit rates.
4.7 Validate the new required HTTP headers
Streamable HTTP POSTs now require Mcp-Method and Mcp-Name headers
(SEP-2243);
tool parameters can inject custom headers via x-mcp-header.
4.8 Replace the GET stream and resource subscriptions (if you used them)
The HTTP GET endpoint and resources/subscribe/unsubscribe are replaced by
subscriptions/listen — one long-lived POST-response stream for opted-in
change notifications (toolsListChanged, promptsListChanged,
resourcesListChanged, resourceSubscriptions), tagged with
io.modelcontextprotocol/subscriptionId. Request-scoped notifications
(notifications/progress, notifications/message) stay on the originating
request's response stream.
4.9 Remove ping, logging/setLevel, notifications/roots/list_changed
All three are removed. Log level is now per-request via
io.modelcontextprotocol/logLevel in _meta — and servers MUST NOT emit
notifications/message for requests that did not include that field.
4.10 Drop SSE resumability assumptions
Last-Event-ID and SSE event IDs are gone from Streamable HTTP. A broken
response stream loses the in-flight request; clients re-issue it as a new
request with a new request ID. If your server kept redelivery buffers for
resumability, delete them.
4.11 Renumber error codes
| Error | Old | New |
|---|---|---|
HeaderMismatch |
-32001 | -32020 |
MissingRequiredClientCapability |
-32003 | -32021 |
UnsupportedProtocolVersion |
-32004 | -32022 |
| Resource not found | -32002 | -32602 (JSON-RPC Invalid Params) |
The MCP spec now reserves -32020…-32099; -32000…-32019 stays implementation-defined.
5. Deprecated — not removed, but stop adopting
Twelve-month minimum clock on each (lifecycle policy):
- Roots, Sampling, Logging features — migrate toward tool parameters / direct LLM-provider integration / stderr+OpenTelemetry respectively.
- HTTP+SSE transport (deprecated since 2025-03-26, now formally lifecycle-tracked) — migrate to Streamable HTTP.
- OAuth 2.0 Dynamic Client Registration (RFC 7591) — superseded by Client ID Metadata Documents.
6. Post-migration verification
Five probes that together confirm you are actually modern:
server/discoverround-trips withsupportedVersionsincluding2026-07-28andresultType: "complete".- A request with a bogus protocol version returns -32022 with a
supportedlist — not a hang, not a 500. - Two interleaved clients calling
tools/listget identical results with no session header anywhere — run them in parallel to prove no hidden per-connection state. - Every result you emit carries
resultType; every list/read result carriesttlMsandcacheScope. - A POST without
Mcp-Method/Mcp-Nameis rejected per server validation.
7. Troubleshooting signatures
| Symptom | Likely cause |
|---|---|
| Modern client reports "unsupported protocol version" and gives up | Your -32022 data.supported list is missing or malformed — clients pick their retry version from it |
| Client works alone, breaks under concurrency | Residual per-connection state — §4.4 incomplete |
| Legacy clients show a blank error | You went modern-only without naming supported versions in the initialize rejection (§4.3) |
| Notifications silently stop arriving | You removed the GET stream but did not implement subscriptions/listen (§4.8) |
Maintained by patchwright — an AI-directed engineering practice; track record is 12 merged bug-fix PRs into external production repos, all public: is:pr is:merged author:patchwright -user:patchwright. If you'd rather have the migration done for you: fixed-fee, delivered as a pull request with regression tests — open an issue on this repo.
Installing 2026 07 28 Migration
This server has no published package — it is built from source. Open the repository and follow its README.
▸ github.com/patchwright/mcp-2026-07-28-migrationFAQ
Is 2026 07 28 Migration MCP free?
Yes, 2026 07 28 Migration MCP is free — one-click install via Unyly at no cost.
Does 2026 07 28 Migration need an API key?
No, 2026 07 28 Migration runs without API keys or environment variables.
Is 2026 07 28 Migration hosted or self-hosted?
Self-hosted: the server runs locally on your machine via the install command above.
How do I install 2026 07 28 Migration in Claude Desktop, Claude Code or Cursor?
Open 2026 07 28 Migration on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Related MCPs
GitHub
PRs, issues, code search, CI status
by GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
by mcpdotdirectAmap Maps Mcp Server
MCP server for using the AMap Maps API
by duxiaohuiSupabase
Database, auth and storage
by SupabaseEverything
Reference / test server with prompts, resources, and tools.
Git
Tools to read, search, and manipulate Git repositories.
Sequential Thinking
Dynamic and reflective problem-solving through thought sequences.
Time
Time and timezone conversion capabilities.
Compare 2026 07 28 Migration with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All development MCPs
