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

{peer.name}: {trigger}; legacy compatibility is not allowlis

Error message

{peer.name}: {trigger}; legacy compatibility is not allowlisted

What it means

Raised when the MCP client's fallback path tries to downgrade a peer to the legacy protocol but the peer was not configured with allow_legacy=True. The client treats legacy compatibility as an opt-in trust decision, so it fails closed instead of silently negotiating an older, less-verified protocol revision.

Source

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

    ) -> dict[str, Any] | None:
        return peer.transport(message, timeout_ms)

    def _mutual_version(self, advertised: list[Any]) -> str | None:
        common = [version for version in advertised if version in self.supported_modern]
        return sorted(common, reverse=True)[0] if common else None

    def _activate_modern(self, peer: Peer, result: dict[str, Any], version: str) -> None:
        if result.get("resultType") != "complete":
            raise RuntimeError(f"{peer.name}: modern discovery omitted resultType")
        peer.era = "modern"
        peer.protocol_version = version
        peer.capabilities = result.get("capabilities", {})
        peer.server_info = result.get("_meta", {}).get(SERVER_INFO_KEY, {})
        peer.available = True

    def _probe_legacy(self, peer: Peer, trigger: str) -> None:
        if not peer.allow_legacy:
            raise RuntimeError(
                f"{peer.name}: {trigger}; legacy compatibility is not allowlisted"
            )
        request_id = self._new_id()
        initialize = legacy_request(
            request_id,
            "initialize",
            {
                "protocolVersion": self.supported_legacy[0],
                "capabilities": self.client_capabilities.copy(),
                "clientInfo": CLIENT_INFO.copy(),
            },
        )
        try:
            response = self._send(peer, initialize, self.legacy_probe_timeout_ms)
        except (TimeoutError, ConnectionError) as exc:
            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")

View on GitHub (pinned to 39ea8a1c6d)

Solutions

  1. Set allow_legacy=True on the Peer if you intentionally need to talk to the legacy server
  2. Upgrade the peer server to a modern protocol revision that answers server/discover
  3. Check why modern discovery failed (empty response, unrecognized error code) and fix the server side
  4. Keep legacy off in production and treat this error as a signal the server is outdated

Example fix

// before
peer = Peer(name="old-server", transport=t)  # allow_legacy defaults to False
client.connect_all()  # -> RuntimeError: legacy compatibility is not allowlisted

// after
peer = Peer(name="old-server", transport=t, allow_legacy=True)
client.connect_all()
Defensive patterns

Strategy: validation

Validate before calling

for peer in client.peers.values():
    if not peer.allow_legacy and peer_likely_legacy(peer):
        peer.allow_legacy = True  # explicit opt-in before connect_all

Type guard

null

Try / catch

try:
    client.connect_all()
except RuntimeError as e:
    if "legacy compatibility is not allowlisted" in str(e):
        # decide: opt in to legacy or upgrade the peer
        ...

Prevention

When it happens

Trigger: Modern discovery on a peer returns an empty/unrecognized result or an unrecognized discovery error code, _connect_peer calls _probe_legacy(peer, trigger), and peer.allow_legacy is falsy.

Common situations: Pointing the client at an old MCP server that only speaks a legacy initialize handshake; forgetting to set allow_legacy in the Peer config; server upgrade changing discovery behavior so the legacy fallback path suddenly engages.

Related errors


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