Lets an MCP client stuck on the pre-2026-07-28 initialize handshake talk to a 2026-07-28 MCP server, with no changes to either side.
The 2026-07-28 MCP specification removed the initialize/initialized handshake entirely, along with the Mcp-Session-Id header it used. Every prior spec version (2024-11-05, 2025-03-26, 2025-06-18) negotiates one protocol version for an entire session via that handshake. Under 2026-07-28, there is no handshake — every request carries its own MCP-Protocol-Version HTTP header, and client identity travels per-request in params._meta["io.modelcontextprotocol/clientInfo"] instead of being negotiated once.
This is a clean break, not a superset: a client written against 2026-07-28 cannot talk to a pre-2026-07-28 server at all, and a client that only knows the old handshake cannot talk to a 2026-07-28 server, because that server never implements initialize in the first place. As the ecosystem migrates unevenly, that's a real, immediate interoperability wall. A few projects have started patching this inside their own codebases as one-off fixes; there wasn't an independent, reusable bridge.
mcp-versionbridge runs as a small HTTP proxy that looks like an old-style MCP server to an old-style client, and talks to a real 2026-07-28 server behind it:
[old client, 2025-06-18 handshake] <--http--> [mcp-versionbridge] <--http--> [2026-07-28 server]
- The old client sends
initializeas it always has. The bridge answers it locally — it is never forwarded, since the new server doesn't implement that method — capturing the client's requested protocol version, capabilities, andclientInfo, and issuing anMcp-Session-Idthe old client will echo back. - Every subsequent request from that client is forwarded to the real server with
MCP-Protocol-Versionset andparams._meta["io.modelcontextprotocol/clientInfo"]injected (merged with, not overwriting, any_metathe client already sent) — exactly what a native 2026-07-28 client would send. - Responses are relayed back unchanged.
Directionality: v1 only bridges old client → new server. The reverse (a 2026-07-28-native client reaching an old server) is a real, distinct need but isn't implemented here yet — tracked as a v1.1 direction, not silently half-supported.
Transport: HTTP only, since the per-request MCP-Protocol-Version mechanism the 2026-07-28 spec documents is HTTP-header-based. stdio transport is out of scope for v1.
Capabilities: the bridge passes through whatever capabilities the old client declared at initialize time rather than querying the new server's actual capabilities via server/discover — that RPC's exact request/response schema isn't in the public spec write-up yet, and guessing at it would make this tool look more authoritative than it is. Pass-through is honest and functional; discovery-based negotiation is a natural, clearly-scoped follow-up once that schema is documented.
npm install mcp-versionbridgeimport { startVersionBridge } from "mcp-versionbridge";
const bridge = await startVersionBridge({
newServerUrl: "https://my-2026-07-28-server.example.com/mcp",
});
console.log(`Point your old MCP client at ${bridge.url} instead of the real server.`);
// later
await bridge.stop();Run the annotated demo (a fixture 2026-07-28 server + a real old-style handshake, showing exactly what crosses the wire):
npx tsx examples/demo.ts- New-client → old-server direction (planned, not yet implemented).
- stdio transport.
server/discover-based capability negotiation (schema not yet public).- Authentication/authorization pass-through beyond whatever headers you add yourself — this bridges protocol-version mechanics, not auth.
npm install
npm run typecheck
npm test
npm run buildMIT
This is one of 8 independent open-source infrastructure tools, each built against a real, researched gap:
- embedguard -- catches silent embedding-model swaps under a vector index
- agora -- propose/vote/veto/quorum for multi-agent systems
- yjs-lens -- verifies Yjs convergence and summarizes causal history
- gatewayproof -- conformance-tests OpenAI-compatible LLM gateways
- wit-breaking -- detects breaking changes in WIT interfaces
- automerge-lens -- explains Automerge merge conflicts and verifies convergence
- pglite-diff -- diffs migration query results on isolated Postgres
Built by ArkNet Digital.