rohitg00/ai-engineering-from-scratch · error · RuntimeError

: unsupported legacy protocol revision

Error message

{peer.name}: unsupported legacy protocol revision

What it means

The legacy initialize succeeded but the protocolVersion in the result is not in the client's supported_legacy set. The client only speaks an explicit allowlist of legacy revisions and refuses to proceed with one it has not been configured to accept.

Solutions

  1. Check what protocolVersion the server returned and compare with client.supported_legacy
  2. Add the server's revision to supported_legacy if it is actually safe to use
  3. Upgrade the server to a revision the client already allows
  4. Disable legacy for this peer and require a modern handshake

Example fix

// before
client = McpClient(..., supported_legacy={"2024-11-05"})

// after
client = McpClient(..., supported_legacy={"2024-11-05", "2025-03-26"})
Defensive patterns

Strategy: validation

Validate before calling

advertised = probe_server_protocol_version(peer)
if advertised not in client.supported_legacy:
    align_versions(client, peer)  # widen set or upgrade server before connect

Type guard

null

Try / catch

try:
    client.connect_all()
except RuntimeError as e:
    if "unsupported legacy protocol revision" in str(e):
        client.supported_legacy.add(reported_version(peer))

Prevention

When it happens

Trigger: result.get('protocolVersion') from a successful legacy initialize returns a version string absent from self.supported_legacy.

Common situations: Server running a much older or newer legacy revision than the client anticipates; client's supported_legacy not updated after a server upgrade; server reporting an ad-hoc version string.

Related errors


AI-assisted analysis of rohitg00/ai-engineering-from-scratch@39ea8a1c6d (2026-08-26). Data as JSON: /api/errors/8702484ae488cad5. Report an issue: GitHub.

Appendix: source

Thrown at phases/13-tools-and-protocols/08-building-an-mcp-client/code/main.py:389

            raise RuntimeError(f"{peer.name}: bounded legacy probe failed closed") from exc
        if not isinstance(response, dict):
            raise RuntimeError(f"{peer.name}: bounded legacy probe returned no result")
        kind, payload = decode_rpc_response(response, request_id)
        if kind != "result":
            raise RuntimeError(f"{peer.name}: legacy initialize returned an error")
        result = payload
        version = result.get("protocolVersion")
        capabilities = result.get("capabilities")
        server_info = result.get("serverInfo")
        valid_server_info = (
            isinstance(server_info, dict)
            and isinstance(server_info.get("name"), str)
            and bool(server_info["name"])
            and isinstance(server_info.get("version"), str)
            and bool(server_info["version"])
        )
        if version not in self.supported_legacy:
            raise RuntimeError(f"{peer.name}: unsupported legacy protocol revision")
        if not isinstance(capabilities, dict) or not valid_server_info:
            raise RuntimeError(f"{peer.name}: malformed legacy initialize result")
        peer.era = "legacy"
        peer.protocol_version = version
        peer.capabilities = capabilities
        peer.server_info = server_info
        peer.available = True
        self._send(
            peer,
            {"jsonrpc": "2.0", "method": "notifications/initialized", "params": {}}
        )

    def _connect_peer(self, peer: Peer) -> None:
        if peer.available and peer.era in {"modern", "legacy"}:
            return
        request_id = self._new_id()
        probe = modern_request(
            request_id,

View on GitHub (pinned to 39ea8a1c6d)