From 4bf4934b0eab46f6bed4dca1f6f44d969ef5395f Mon Sep 17 00:00:00 2001 From: Max Isbey <224885523+maxisbey@users.noreply.github.com> Date: Wed, 29 Jul 2026 13:32:55 +0000 Subject: [PATCH] Retire wording tied to pre-2.0 milestones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Several docstrings, comments, and story READMEs still described work as "expected before v2 is final", "planned before beta", or "revisit before beta" — milestones that passed with the 2.0 release. Reword the provisional middleware/dispatcher notes as "may change in a 2.x minor release", drop the stale beta TODOs, and correct the middleware story: MCPServer now exposes the same middleware list as the lowlevel Server. No-Verification-Needed: doc, comment, and docstring wording only --- docs/advanced/middleware.md | 4 ++-- examples/stories/legacy_routing/README.md | 3 +-- examples/stories/middleware/README.md | 11 ++++++----- examples/stories/middleware/server.py | 4 ++-- examples/stories/roots/README.md | 1 - examples/stories/sampling/README.md | 1 - examples/stories/serve_one/README.md | 8 ++++---- examples/stories/sse_polling/README.md | 2 +- examples/stories/starlette_mount/README.md | 3 +-- examples/stories/streaming/README.md | 2 +- src/mcp/server/lowlevel/server.py | 6 +++--- src/mcp/server/mcpserver/server.py | 4 ++-- src/mcp/shared/dispatcher.py | 3 ++- tests/interaction/_requirements.py | 2 +- 14 files changed, 26 insertions(+), 28 deletions(-) diff --git a/docs/advanced/middleware.md b/docs/advanced/middleware.md index 3ad58ce05d..5d80927441 100644 --- a/docs/advanced/middleware.md +++ b/docs/advanced/middleware.md @@ -5,8 +5,8 @@ A **middleware** is one async function that wraps every message your server rece You write it as `async (ctx, call_next)` and append it to `server.middleware`. That is the whole API. !!! warning - The middleware list is marked **provisional** in the source. The signature and semantics are - expected to change before v2 is final. Use it to *observe* (timing, logging, tracing) and to + The middleware list is marked **provisional** in the source: its signature and semantics may + change in a 2.x minor release. Use it to *observe* (timing, logging, tracing) and to *refuse* messages; do not make it the foundation your server stands on. `MCPServer` takes the list at construction (`MCPServer(name, middleware=[...])`) and exposes it as diff --git a/examples/stories/legacy_routing/README.md b/examples/stories/legacy_routing/README.md index 84a4528c96..c36352aec9 100644 --- a/examples/stories/legacy_routing/README.md +++ b/examples/stories/legacy_routing/README.md @@ -94,8 +94,7 @@ eras need different auth, rate limits, or scaling. - DNS-rebinding protection is on by default; the harness disables it (`NO_DNS_REBIND`) because the in-process httpx2 client sends no `Origin`. Drop the kwarg for a real deployment. -- `mcp.shared.inbound` is a deep import path — a shorter re-export is planned - before beta. +- `mcp.shared.inbound` is a deep import path; there is no shorter re-export. ## Spec diff --git a/examples/stories/middleware/README.md b/examples/stories/middleware/README.md index 599f890f80..cfce502f11 100644 --- a/examples/stories/middleware/README.md +++ b/examples/stories/middleware/README.md @@ -31,12 +31,13 @@ uv run python -m stories.middleware.client --http ## Caveats -- **Lowlevel-only.** `Server.middleware` on `mcp.server.lowlevel.Server` is the - one public hook; `MCPServer` has no public accessor for it yet (a - `MCPServer.middleware` accessor is planned before beta). +- **One list, two accessors.** `Server.middleware` on + `mcp.server.lowlevel.Server` is the hook this story uses; `MCPServer` + exposes the same list as `MCPServer.middleware` (or takes it at + construction as `MCPServer(name, middleware=[...])`). - The middleware signature is **provisional** (see the TODO in - `src/mcp/server/lowlevel/server.py`): it tightens to a covariant `Context[L]` - and gains an outbound seam before v2 final. + `src/mcp/server/lowlevel/server.py`): it may change in a 2.x minor release, + tightening to a covariant `Context[L]` and gaining an outbound seam. - `ServerMiddleware` / `CallNext` / `HandlerResult` are imported from `mcp.server.context` (helper tier); not re-exported at `mcp.server.lowlevel`. - Do **not** `await ctx.session.send_request(...)` while wrapping `initialize` diff --git a/examples/stories/middleware/server.py b/examples/stories/middleware/server.py index e2164eb668..42df3e8c6e 100644 --- a/examples/stories/middleware/server.py +++ b/examples/stories/middleware/server.py @@ -1,7 +1,7 @@ """Dispatch-layer middleware: `Server.middleware` is the public hook. -A lowlevel-only story: `MCPServer` has no public middleware accessor yet, so the -one supported registration point is the `middleware` list on `lowlevel.Server`. +This story registers on the lowlevel `Server`; `MCPServer` exposes the same +list as `MCPServer.middleware`, so the recipe carries over unchanged. """ import json diff --git a/examples/stories/roots/README.md b/examples/stories/roots/README.md index d11bf8845e..c0aa34c421 100644 --- a/examples/stories/roots/README.md +++ b/examples/stories/roots/README.md @@ -3,7 +3,6 @@ > **Deprecated** in the 2026-07-28 protocol (SEP-2577); functional through the > deprecation window. Migration: accept directory paths as ordinary tool > parameters or resource URIs instead of relying on `roots/list`. -> TODO(maxisbey): revisit before beta. The client passes a `list_roots_callback` returning the filesystem locations it is willing to expose; a server tool calls `ctx.session.list_roots()` mid-request diff --git a/examples/stories/sampling/README.md b/examples/stories/sampling/README.md index 1c4a9bf794..3c68be8511 100644 --- a/examples/stories/sampling/README.md +++ b/examples/stories/sampling/README.md @@ -3,7 +3,6 @@ > **Deprecated** in the 2026-07-28 protocol (SEP-2577); functional through the > deprecation window. Migration: call your LLM provider directly from the > server instead of requesting completions through the client. -> TODO(maxisbey): revisit before beta. A tool that asks the **client's** LLM for a completion mid-call — the inverted MCP direction. The server holds no model API key; it awaits diff --git a/examples/stories/serve_one/README.md b/examples/stories/serve_one/README.md index dd75486164..b71b07e566 100644 --- a/examples/stories/serve_one/README.md +++ b/examples/stories/serve_one/README.md @@ -34,15 +34,15 @@ uv run python -m stories.serve_one.client ## Caveats - **Deep imports** — `serve_one`, `serve_connection`, and `Connection` are only - reachable at `mcp.server.runner` / `mcp.server.connection` today; a shorter - `mcp.server.*` re-export is tracked for beta. + reachable at `mcp.server.runner` / `mcp.server.connection`; there is no + shorter `mcp.server.*` re-export. - **Lowlevel-only.** The drivers take a `lowlevel.Server` and `MCPServer` has no public accessor for its underlying one (`_lowlevel_server` is private), so there is no `MCPServer`-tier variant of this story. Build the lowlevel `Server` directly until that accessor lands. - **No public `DispatchContext`** — `SingleExchangeContext` is hand-rolled - boilerplate; a public helper (or a `serve_one` overload that builds one) is - tracked for beta. + boilerplate; there is no public helper (or `serve_one` overload) that builds + one. - **Lifespan** — the transport entry enters `server.lifespan(server)` **once** and threads `lifespan_state` to every `handle_one()` call; never enter it per-request. diff --git a/examples/stories/sse_polling/README.md b/examples/stories/sse_polling/README.md index 026ba3ce07..0a54a9fe18 100644 --- a/examples/stories/sse_polling/README.md +++ b/examples/stories/sse_polling/README.md @@ -4,7 +4,7 @@ > the sessionful transport are removed in the 2026-07-28 protocol (SEP-2575) > with no modern-era equivalent; the closest 2026-era pattern is client-side > reconnection over a persisted `DiscoverResult` — -> [`reconnect/`](../reconnect/). TODO(maxisbey): revisit before beta. +> [`reconnect/`](../reconnect/). SEP-1699 server-initiated SSE disconnection with `Last-Event-ID` replay. The server's `EventStore` stamps every SSE event with an ID and opens each response diff --git a/examples/stories/starlette_mount/README.md b/examples/stories/starlette_mount/README.md index 97b3a84bbe..0aa5a8ef51 100644 --- a/examples/stories/starlette_mount/README.md +++ b/examples/stories/starlette_mount/README.md @@ -45,8 +45,7 @@ kill "$SERVER_PID" no `Origin` header. Remove it (or configure allowed hosts) for a real deployment. - The parent-lifespan dance is a known SDK ergonomics gap (other SDKs mount - with no extra ceremony); tracked for the beta reshape. The recipe shown here - is what works today. + with no extra ceremony). The recipe shown here is what works today. ## Spec diff --git a/examples/stories/streaming/README.md b/examples/stories/streaming/README.md index c485399b71..a7a37cab97 100644 --- a/examples/stories/streaming/README.md +++ b/examples/stories/streaming/README.md @@ -55,7 +55,7 @@ uv run python -m stories.streaming.client --http --server server_lowlevel through the deprecation window. Migration: write to stderr or emit OpenTelemetry instead of `notifications/message`. It is shown here because servers still need to support 2025-era clients during that window. Progress - and cancellation are **not** deprecated. TODO(maxisbey): revisit before beta. + and cancellation are **not** deprecated. - A cancelled request is not answered: no response follows `notifications/cancelled`. (The 2025-era streamable HTTP transport is the one exception - its wire ends a request only with a response, so it terminates diff --git a/src/mcp/server/lowlevel/server.py b/src/mcp/server/lowlevel/server.py index 1cbd3f2bd6..efdf4b216e 100644 --- a/src/mcp/server/lowlevel/server.py +++ b/src/mcp/server/lowlevel/server.py @@ -433,9 +433,9 @@ def __init__( # `OpenTelemetryMiddleware` ships on by default so every server emits a # SERVER span per message; it is a no-op until an OTel exporter is # installed. Drop it from this list to opt out. - # TODO(L54): provisional - signature and semantics change with the - # Context/middleware rework (covariant `Context[L]`, outbound seam) before - # v2 final. + # TODO(L54): provisional - signature and semantics may change in a 2.x + # minor release with the Context/middleware rework (covariant + # `Context[L]`, outbound seam). self.middleware: list[ServerMiddleware[LifespanResultT]] = [OpenTelemetryMiddleware()] # SEP-2133 extension settings advertised under `ServerCapabilities.extensions` # (identifier -> settings). Higher layers (e.g. `MCPServer(extensions=...)`) diff --git a/src/mcp/server/mcpserver/server.py b/src/mcp/server/mcpserver/server.py index 7bf1993e29..bc79c44a36 100644 --- a/src/mcp/server/mcpserver/server.py +++ b/src/mcp/server/mcpserver/server.py @@ -267,8 +267,8 @@ def middleware(self) -> list[ServerMiddleware[Any]]: The same list as the low-level `Server.middleware`: append an `async (ctx, call_next)` callable to observe, refuse, or rewrite - messages before they reach a handler. Provisional - the signature is - expected to change before v2 is final; see the middleware guide. + messages before they reach a handler. Provisional - the signature may + change in a 2.x minor release; see the middleware guide. """ return self._lowlevel_server.middleware diff --git a/src/mcp/shared/dispatcher.py b/src/mcp/shared/dispatcher.py index f109638f2a..f2ff96e7d5 100644 --- a/src/mcp/shared/dispatcher.py +++ b/src/mcp/shared/dispatcher.py @@ -250,7 +250,8 @@ class Dispatcher(Outbound, Protocol[TransportT_co]): Implementations own correlation of outbound requests to inbound results, the receive loop, per-request concurrency, and cancellation/progress wiring. - The lifecycle surface is provisional; `run()` may change before v2 stable. + The lifecycle surface is provisional; `run()` may change in a 2.x minor + release. """ async def run( diff --git a/tests/interaction/_requirements.py b/tests/interaction/_requirements.py index 6406bdb8bc..964a1829d2 100644 --- a/tests/interaction/_requirements.py +++ b/tests/interaction/_requirements.py @@ -660,7 +660,7 @@ def __post_init__(self) -> None: "The dispatcher drops null-id error responses with a debug log; in v1, JSONRPCError.id was " "non-nullable, so a null-id error response failed transport validation and the resulting " "ValidationError was surfaced to message_handler as an exception. A typed fault channel " - "restoring visibility is planned before v2 stable." + "restoring visibility is planned." ), ), deferred=(