Command Palette

Search for a command to run...

UnylyUnyly
Browse all

2026 07 28 Migration

FreeNot checked

Practical migration guide: MCP servers to the 2026-07-28 spec — stateless core, server/discover, sessions removed. Copy-paste before/after for every breaking ch

GitHubEmbed

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 initialize request — 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 _meta is served statelessly; an initialize request 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:

  1. server/discover round-trips with supportedVersions including 2026-07-28 and resultType: "complete".
  2. A request with a bogus protocol version returns -32022 with a supported list — not a hang, not a 500.
  3. Two interleaved clients calling tools/list get identical results with no session header anywhere — run them in parallel to prove no hidden per-connection state.
  4. Every result you emit carries resultType; every list/read result carries ttlMs and cacheScope.
  5. A POST without Mcp-Method/Mcp-Name is 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.

from github.com/patchwright/mcp-2026-07-28-migration

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-migration

FAQ

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

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