Serve the 2026-07-28 protocol over stdio: decide the era from the opening request - #3152
Code review found 2 important issues
Found 5 candidates, confirmed 4. See review comments for details.
Details
| Severity | Count |
|---|---|
| 🔴 Important | 2 |
| 🟡 Nit | 2 |
| 🟣 Pre-existing | 0 |
| Severity | File:Line | Issue |
|---|---|---|
| 🔴 Important | src/mcp/server/runner.py:658-666 |
Replay stream drops sender contextvars propagation (last_context) |
| 🔴 Important | docs/whats-new.md:194 |
Stale admonition: handlers/subscriptions.md still says listen is rejected over stdio |
| 🟡 Nit | src/mcp/server/runner.py:630-636 |
Stale _has_modern_envelope docstring describes removed lock-on-success model |
| 🟡 Nit | src/mcp/server/runner.py:658-687 |
Unbounded pre-request frame buffering in _replay_from_opening_request |
Annotations
Check failure on line 666 in src/mcp/server/runner.py
claude / Claude Code Review
Replay stream drops sender contextvars propagation (last_context)
The new replay indirection silently breaks sender contextvars propagation for every request served through `serve_dual_era_loop`: `replay_then_relay` iterates the original `ContextReceiveStream` (discarding the per-message context envelope) and re-sends bare items through a plain anyio memory stream that has no `last_context` attribute, so the dispatcher's `sender_ctx` is now always `None` on this path, in both eras. This regresses a deliberate, test-pinned feature (e.g. `Client(server, mode="le
Check failure on line 194 in docs/whats-new.md
claude / Claude Code Review
Stale admonition: handlers/subscriptions.md still says listen is rejected over stdio
This PR deletes the stdio refusal of `subscriptions/listen` (and correctly updates the caveat here in whats-new.md), but the canonical Subscriptions page still documents the removed behavior: `docs/handlers/subscriptions.md` lines 55-60 keep the "Streamable HTTP only, for now" admonition saying a 2026-07-28 stdio connection rejects the method with METHOD_NOT_FOUND. Please remove or rewrite that warning block in this PR — AGENTS.md requires the owning docs page to be updated in the same PR as a u
Check warning on line 636 in src/mcp/server/runner.py
claude / Claude Code Review
Stale _has_modern_envelope docstring describes removed lock-on-success model
The docstring on `_has_modern_envelope` still ends with "and, like every failed classification, locks no era" — a description of the first-success-locks model this PR removes. Under the new design this helper *is* the era decision (the `opens_modern` check on the opening request), and a half-built envelope on the opening frame now does permanently open a 2026 connection, as this PR's own tests pin. Trimming the stale clause keeps the one helper documenting the era rule from asserting the opposit
Check warning on line 687 in src/mcp/server/runner.py
claude / Claude Code Review
Unbounded pre-request frame buffering in _replay_from_opening_request
The peek loop in `_replay_from_opening_request` appends every non-request frame (notifications, responses, `Exception` items from unparseable input) into the unbounded `peeked` list until the first JSON-RPC request arrives, draining the transport's zero-buffer stream eagerly — so a peer that streams non-request frames without ever sending a request grows server memory without bound, where the pre-PR dispatcher consumed those frames incrementally under transport backpressure. A small cap on the p