What’s actually changing
The specification changelog states it directly:
“Removed
initialize/notifications/initializedhandshake; requests now carry protocol version and capabilities in_meta(io.modelcontextprotocol/protocolVersion,io.modelcontextprotocol/clientCapabilities); version mismatches returnUnsupportedProtocolVersionError”
This follows directly from the removal of protocol-level sessions: if there’s no persistent session, there’s no connection-scoped handshake result to hold onto either. Every request has to be self-describing.
A real request under the new shape:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search",
"arguments": { "query": "stateless MCP" },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": {
"name": "example-client",
"version": "1.0.0"
},
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {}
}
}
}
}Structural Comparison Matrix
| Operational Aspect | Before (initialize handshake) | After (2026-07-28+) |
|---|---|---|
| When negotiation happens | Once, at connection start | Every request |
| Where version/capabilities live | Handshake response, held for the session | _meta.io.modelcontextprotocol/protocolVersion and clientCapabilities on each call |
| Version mismatch behavior | Handshake failure | UnsupportedProtocolVersionError, listing supported revisions |
Fix it: move negotiation into per-request _meta
Handle UnsupportedProtocolVersionError explicitly
Since the server’s error response lists the revisions it actually supports, a client should retry with a mutually supported version rather than treating any mismatch as a hard failure. Skipping that retry logic means a client that only speaks an older revision fails outright against a server that could have served it a compatible version.
Any code that previously stored a “session established” flag after a successful initialize call, then skipped re-sending capabilities on later calls, needs that flag and the associated shortcut removed. Every outgoing request needs the _meta block populated fresh, not conditionally based on connection state that no longer exists.
Confirmed version
Sourced from the official Model Context Protocol specification changelog, 2026-07-28 revision, published 2026-07-28, including the real example request shape from the spec’s own migration documentation. Browse more posts like this in the AI Productivity archive.







