Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/advanced/middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions examples/stories/legacy_routing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
11 changes: 6 additions & 5 deletions examples/stories/middleware/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
4 changes: 2 additions & 2 deletions examples/stories/middleware/server.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
1 change: 0 additions & 1 deletion examples/stories/roots/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion examples/stories/sampling/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions examples/stories/serve_one/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion examples/stories/sse_polling/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions examples/stories/starlette_mount/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion examples/stories/streaming/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/mcp/server/lowlevel/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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=...)`)
Expand Down
4 changes: 2 additions & 2 deletions src/mcp/server/mcpserver/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 2 additions & 1 deletion src/mcp/shared/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion tests/interaction/_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -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=(
Expand Down
Loading