# dual-era One server factory, both protocol eras. A `mode="legacy"` client runs the `initialize` handshake; a `mode="auto"` client probes `server/discover` and adopts the 2026 stateless era — the same `greet` tool answers both and reports which era served it via `ctx.request_context.protocol_version`. **Start here** when migrating a v1 server: the entry owns the era decision, the server body stays era-agnostic. ## Run it ```bash # over HTTP — the same /mcp endpoint serves both eras; the client self-hosts # the server on a free port, runs, then tears it down uv run python -m stories.dual_era.client --http # same, against the lowlevel-API server variant uv run python -m stories.dual_era.client --http --server server_lowlevel ``` The bare stdio invocation (`uv run python -m stories.dual_era.client`) is legacy-only until the SDK's stdio entry can negotiate the era, so the modern leg fails there today — run over `--http`. ## What to look at - `client.py` — both connections are visible, against the same `targets()` factory: `Client(targets(), mode=mode)` (default `"auto"`, the discover-then-fallback ladder) and `Client(targets(), mode="legacy")` (forces the `initialize` handshake). The era decision is one explicit `mode=` argument at construction; no date strings appear in the body. - `client.py` — `client.protocol_version` / `client.server_info` / `client.server_capabilities` are era-neutral: populated by `initialize` *or* `server/discover`, whichever ran. - `server.py` — `ctx.request_context.protocol_version` is the era branch key (lowlevel: `ctx.protocol_version` directly). Compare against `MODERN_PROTOCOL_VERSIONS`, never a date literal. - **Where to read the negotiated version.** One value, three read paths: `client.protocol_version` on the client after connect; `ctx.protocol_version` inside a lowlevel handler; `ctx.request_context.protocol_version` inside an `MCPServer` handler. ## Caveats - `ctx.request_context.protocol_version` is the current way to read the negotiated version; a later release will shorten it to `ctx.transport.*`. - Over HTTP the built-in era branch is currently header-only — a 2026 client that omits the `MCP-Protocol-Version` header is mis-routed to the legacy path. The body-primary classifier lands in a later release. ## Spec - [Versioning — backward compatibility](https://modelcontextprotocol.io/specification/draft/basic/versioning) - [`server/discover`](https://modelcontextprotocol.io/specification/draft/server/discover) ## See also `legacy_routing/` (route eras yourself), `reconnect/` (persist `DiscoverResult` for zero-RTT reconnect).