chore: import upstream snapshot with attribution
Deploy Docs / deploy-docs (push) Failing after 1s
Conformance Tests / client-conformance (push) Failing after 3s
Conformance Tests / server-conformance (push) Failing after 1s
GitHub Actions Security Analysis / zizmor (push) Failing after 1s
CI / checks (push) Failing after 59m20s
CI / all-green (push) Has been cancelled
Deploy Docs / deploy-docs (push) Failing after 1s
Conformance Tests / client-conformance (push) Failing after 3s
Conformance Tests / server-conformance (push) Failing after 1s
GitHub Actions Security Analysis / zizmor (push) Failing after 1s
CI / checks (push) Failing after 59m20s
CI / all-green (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# 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).
|
||||
Reference in New Issue
Block a user