Address review: drop tasks from modern capability table; note the reentrancy blocker too

This commit is contained in:
Jeremiah Lowin 2026-07-20 20:28:23 -04:00
commit 07fa270652
No known key found for this signature in database
2 changed files with 2 additions and 3 deletions

View file

@ -98,7 +98,7 @@ The migration already routed `initialize` interception through the SDK's `Server
`fastmcp.Client` now defaults to `mode="auto"` (#4572): it probes `server/discover`, falls back to the classic handshake, and answers multi-round-trip `input_required` requests through its existing handlers. The same PR surfaced `extensions=` and `result_claims=` (SEP-2133). The client also dropped its forked protocol helpers — extension folding, the evicting message handler, discover synthesis — in favor of the SDK's own (#4574).
The decision here was **compose, not wrap** (D16): rebuild `fastmcp.Client` on the SDK's high-level `mcp.Client` rather than wrapping `mcp.ClientSession`. The parts that compose cleanly have shipped. The rest is **blocked upstream**: `mcp.Client` constructs its `ClientSession` at a single hardcoded site with no injection hook, while FastMCP's `session_class` is load-bearing (`ProxyClient` substitutes a session that skips result validation so a backend's schema violation surfaces at the end client rather than becoming a proxy error). A `session_factory=` hook on `mcp.Client` — the same shape as the `notification_bindings=` parameter added earlier — would unblock the full rebuild.
The decision here was **compose, not wrap** (D16): rebuild `fastmcp.Client` on the SDK's high-level `mcp.Client` rather than wrapping `mcp.ClientSession`. The parts that compose cleanly have shipped. The rest is **blocked upstream on two counts**. First, `mcp.Client` constructs its `ClientSession` at a single hardcoded site with no injection hook, while FastMCP's `session_class` is load-bearing (`ProxyClient` substitutes a session that skips result validation so a backend's schema violation surfaces at the end client rather than becoming a proxy error) — a `session_factory=` hook on `mcp.Client`, the same shape as the `notification_bindings=` parameter added earlier, would solve this. Second, `mcp.Client.__aenter__` refuses reentry, but FastMCP's client is deliberately reentrant (its refcounted context manager exists to fix a proxy session-reuse deadlock), so the rebuild also needs the SDK client to tolerate reentrant entry. Both must land upstream before the full rebuild is possible; `session_factory=` alone is necessary but not sufficient.
This workstream also owns the server-side statelessness design holes — `ctx.session_id` / `set_state` round-tripping and stateful-proxy affinity — since they turn on the same "what is a session without a session?" question. See [Statelessness on 2026-07-28](/development/v4-notes/known-gaps#statelessness-on-2026-07-28) for the full accounting.

View file

@ -42,13 +42,12 @@ The complete picture of what a FastMCP v4 server and client provide on the `2026
| **Client protocol negotiation** | `Client(mode="auto")` — the default as of v4 — probes `server/discover` and falls back to the classic handshake; the client answers multi-round-trip `input_required` requests through its existing handlers. Pin `mode="legacy"` to force the handshake. |
| **Elicitation on the modern protocol (SEP-2322)** | Tools request user input via multi-round trips: a tool returns an `InputRequiredResult` and re-runs per round, reading the client's answers off `ctx.input_responses` / `ctx.request_state` (the [guard pattern](/servers/elicitation#elicitation-on-the-modern-protocol)). Each round is a complete request→response cycle; the framework seals `request_state` on the wire and unseals it before the tool runs, and a shared-key `request_state_security` policy carries state across replicas. On handshake-era connections returning this result produces a clear era error. |
| **Spec-standard errors (SEP-2164)** | Missing-resource reads return `-32602`; push-feature calls on modern connections fail with clear era-specific errors rather than generic method-not-found. |
| **Background tasks (2025 protocol — slated for removal)** | `@mcp.tool(task=True)` runs on a Redis-backed distributed runtime (Docket) with cross-replica notifications. This implements the 2025 task wire protocol (SEP-1686), which has since been **removed from the MCP spec**; FastMCP's implementation is slated for removal too — see the note below. |
| **Middleware** | Typed per-method hooks (`on_call_tool`, `on_list_tools`, …) and a suite of built-ins (auth, rate limiting, caching, error handling, logging, timing, and more). |
| **Composition** | `mount()`, providers, proxying, and tool transforms compose servers dynamically at runtime, with lifespans and middleware driven through the SDK session manager. |
| **Pagination** | Declarative `FastMCP(list_page_size=...)` paginates all list operations in the high-level server; the client auto-paginates with cycle detection. |
| **Telemetry** | OpenTelemetry spans on by default (no-op without an exporter), SDK-aligned attributes (`mcp.method.name`, `mcp.protocol.version`, `gen_ai.*`), plus auth and provider-delegation spans; `FASTMCP_ENABLE_TELEMETRY=false` disables cleanly. |
The **background tasks** row above needs a caveat. The `@mcp.tool(task=True)` runtime implements the 2025 task wire protocol (SEP-1686), which was **removed from the MCP spec entirely** — on `2026-07-28`, tasks left the core protocol and became the separate `io.modelcontextprotocol/tasks` extension. FastMCP's 2025 task machinery (`fastmcp_slim/fastmcp/server/tasks/`) is slated for removal rather than being carried forward. Users who need background tasks today should stay on FastMCP 3; the 2026 tasks extension will be the modern replacement, built later through the extensions mechanism. See [Known Gaps](/development/v4-notes/known-gaps#the-xfail-register) for the deletion tracking.
**Background tasks are not in the table because they do not work on `2026-07-28`.** The `@mcp.tool(task=True)` runtime implements the 2025 task wire protocol (SEP-1686), which was **removed from the MCP spec entirely** — on `2026-07-28`, task submission is not part of the core protocol, and tasks became the separate `io.modelcontextprotocol/tasks` extension. So `task=True` completes only on handshake-era connections, and FastMCP's 2025 task machinery (`fastmcp_slim/fastmcp/server/tasks/`) is slated for removal rather than being carried forward. Users who need background tasks today should stay on FastMCP 3; the 2026 tasks extension will be the modern replacement, built later through the extensions mechanism. See [Known Gaps](/development/v4-notes/known-gaps#the-xfail-register) for the deletion tracking.
## Still in the program