Skip to content

Migrate from 2025-11-25 to 2026-07-28

Spec
2025-11-25 → 2026-07-28
Evidence
E2Reproducedarm64
TypeScript SDK (server)
@modelcontextprotocol/server@2.4.0
TypeScript SDK (client)
@modelcontextprotocol/client@2.4.0
Python SDK
mcp@2.3.0
Java SDK
io.modelcontextprotocol.sdk:mcp@2.1.0
Go SDK
github.com/modelcontextprotocol/go-sdk@v1.8.0
Verified

Most of the work is done by the SDKs. Upgrading the package is not always enough, though: in our tests, common setups kept speaking 2025-11-25 after an upgrade. This page lists what to check.

If you have not read it yet, How 2026-07-28 works explains the concepts used here.

The specification calls a client or server that only speaks 2025 versions legacy, one that only speaks 2026-07-28 modern, and one that speaks both dual-era:

Client Server Result
Dual-era Modern or dual-era Works on 2026-07-28
Dual-era Legacy Works: the client falls back to initialize
Legacy Dual-era Works on the old protocol
Legacy Modern only Fails. Old clients have no way forward
Modern only Legacy Fails

So the safe path is to make servers dual-era first, then move clients. The TypeScript, Python and Go SDKs we tested serve both generations from the same server code, as long as you use the right entry point. The Java SDK 2.1.0 only serves 2025-11-25.

Each example server was called by the TypeScript client SDK 2.4.0 three ways:

Server Default client versionNegotiation: auto Pinned to 2026-07-28
TypeScript 2.4.0, serveStdio() 2025-11-25 2026-07-28 2026-07-28
TypeScript 2.4.0, server.connect(new StdioServerTransport()) 2025-11-25 2025-11-25 —
Python 2.3.0 2025-11-25 2026-07-28 2026-07-28
Go v1.8.0 2025-11-25 2026-07-28 2026-07-28
Java 2.1.0 2025-11-25 2025-11-25 fails: Version negotiation failed

Two things stand out. The default TypeScript client always used 2025-11-25. And the TypeScript server only spoke 2026-07-28 when started with serveStdio().

  • Upgrade the SDK to a version that implements 2026-07-28. Of the official SDKs we tested, the Java SDK 2.1.0 does not yet; keep it on 2025-11-25.
  • TypeScript: change the entry point. A v2 server that still calls server.connect(new StdioServerTransport()), or hands requests to a …StreamableHTTPServerTransport, serves 2025 clients only. Use serveStdio(() => createServer()) for stdio and createMcpHandler(() => createServer()) for HTTP. The official upgrade guide says the same.
  • Remove per-connection state. Anything you kept in a session (a logged-in user, an open cart) must become a handle passed as a tool argument, or come from the auth token.
  • Move server-to-client requests to multi round-trip requests. In TypeScript, return inputRequired(...). In Python, replace await ctx.elicit(...) with a Resolve(...) parameter: ctx.elicit() failed on 2026-07-28 in our test.
  • Set cache hints for lists that rarely change (cacheHints in TypeScript, cache_hints in Python). Without them the SDKs send ttlMs: 0.
  • Stop relying on removed methods. ping and logging/setLevel return -32601. The log level now arrives per request in _meta as io.modelcontextprotocol/logLevel; without it, the server must not send log notifications for that request.
  • Resource not found is now -32602 (Invalid params) instead of -32002. Update clients or tests that match the old code.
  • HTTP servers: expect MCP-Protocol-Version, Mcp-Method and Mcp-Name headers, return 405 for GET, and ignore Mcp-Session-Id from new clients.
  • Turn negotiation on. With the TypeScript client SDK v2, set versionNegotiation: { mode: 'auto' }. The default is 'legacy', the plain 2025 handshake.
  • Send _meta on every request: protocol version and capabilities. The SDKs do this once negotiation picked 2026-07-28.
  • Declare capabilities you can fulfil. A server that wants a confirmation returns -32021 if your request does not declare elicitation.
  • Handle input_required: ask the user, then retry with a new id, the same arguments, inputResponses and the unchanged requestState. The TypeScript client does this automatically through the handlers you register with setRequestHandler.
  • Treat a missing resultType as "complete": older servers do not send it.
  • Use subscriptions/listen for change notifications instead of the GET stream or resources/subscribe.
  • On stdio, probe with server/discover before falling back to initialize.
  • On a broken HTTP stream, resend the request with a new id. Streams can no longer be resumed with Last-Event-ID.

Run the Inspector against your server once per protocol era:

Terminal window
npx -y @modelcontextprotocol/inspector@2.12.0 --cli <your command or URL> --protocol-era modern --method tools/list
npx -y @modelcontextprotocol/inspector@2.12.0 --cli <your command or URL> --protocol-era legacy --method tools/list

Both should list your tools. If modern fails with Version negotiation failed, the server still speaks only 2025-11-25: check the SDK version and, for TypeScript, the entry point.

Sources

MCPDev is community documentation. It is not affiliated with the Model Context Protocol project or with the vendors of the clients it covers.