Fix stale MRTR/elicitation framing in client and upgrade docs (#4551)

This commit is contained in:
Jeremiah Lowin 2026-07-19 18:10:15 -04:00 committed by GitHub
commit eee5e91334
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 10 additions and 8 deletions

View file

@ -146,7 +146,7 @@ client = Client(
<VersionBadge version="4.0.0" />
Modern-era servers (protocol version `2026-07-28` and later) can pause a `call_tool`, `get_prompt`, or `read_resource` call to ask for input before producing a final result. When this happens, the client answers each round automatically using the callbacks you already configured — your `elicitation_handler`, `sampling_handler`, and roots — and retries until the call reaches a terminal result. No extra wiring is needed beyond the handlers described above.
On modern-era connections (protocol version `2026-07-28` and later), a server can ask for input before it returns a final result. Nothing is held open: the tool *returns* a description of what it needs, which completes that round as an ordinary response, and the client answers by issuing a **new** `call_tool`, `get_prompt`, or `read_resource` request carrying the answer. `fastmcp.Client` drives that loop for you — it fulfils each round's requests using the callbacks you already configured (your `elicitation_handler`, `sampling_handler`, and roots) and repeats until the call reaches a terminal result. No extra wiring is needed beyond the handlers described above.
The `input_required_max_rounds` parameter caps how many rounds the client will answer before giving up, guarding against a server that never terminates. It defaults to `10`.

View file

@ -169,17 +169,17 @@ These warnings come from the MCP SDK, not from FastMCP. For logging they are ben
FastMCP servers built on the SDK v2 serve multiple protocol eras from the same server. The SDK negotiates the era each client speaks: the sessionless `2026-07-28` era (which discovers capabilities through `server/discover`) and earlier session-based handshake versions are all handled simultaneously. This formally supersedes FastMCP's earlier "latest protocol only" stance — a single server now works with clients across the protocol transition.
Not every Context feature is available on every era yet. The push-style interactions that require the server to call back into the client — elicitation, sampling, and listing roots — depend on the session-based request/response flow of the earlier eras. On a `2026-07-28` connection these raise a clear, era-aware error rather than reaching the client. Logging notifications and the request/response features flow on every era.
Not every Context feature is available on every era yet. The imperative push APIs that call back into the client mid-execution — `ctx.elicit`, `ctx.sample`, and `ctx.list_roots` — depend on the session-based back-channel of the earlier eras, so on a `2026-07-28` connection they raise a clear, era-aware error rather than reaching the client. Elicitation itself still reaches the user on the modern era, through the guard pattern: a tool *returns* an `InputRequiredResult` describing what it needs, and the client answers with a fresh call (see [Elicitation on the modern protocol](/servers/elicitation#elicitation-on-the-modern-protocol)). Logging notifications and the request/response features flow on every era.
Sampling is the exception that does not come back. `ctx.sample` and `ctx.sample_step` are **deprecated** and will be removed in a future FastMCP release: server-initiated sampling was removed from the wire by SEP-2577, and unlike elicitation it has no multi-round-trip replacement (the agentic loop would exhaust the round-trip budget). The migration is to call an LLM directly from your server rather than borrowing the client's model. See [Sampling](/servers/sampling) for details.
Sampling is the exception that does not come back, and the reason is the protocol rather than an unfinished FastMCP feature. SEP-2577 deprecated server-initiated sampling, so `ctx.sample` and `ctx.sample_step` are **deprecated** and will be removed in a future FastMCP release. Elicitation moved to the guard pattern because the modern protocol still carries elicitation requests; sampling has no equivalent path because the protocol deprecated the pattern itself. The migration is to call an LLM directly from your server rather than borrowing the client's model. See [Sampling](/servers/sampling) for details.
| Context feature | Earlier eras (session-based) | `2026-07-28` (sessionless) |
| --- | --- | --- |
| `ctx.info` / logging notifications | Supported | Supported |
| Tools, resources, prompts, completions | Supported | Supported |
| `ctx.elicit` | Supported | Not yet — MRTR rewrite pending |
| `ctx.elicit` | Supported | Use the guard pattern (return `InputRequiredResult`) |
| `ctx.sample` / `ctx.sample_step` | Supported (deprecated) | Removed — call an LLM server-side |
| `ctx.list_roots` | Supported | Not yet — MRTR rewrite pending |
| `ctx.list_roots` | Supported | Via the guard pattern (`input_requests` carries roots requests) |
| Tasks (via the FastMCP client) | Supported | Not yet |
If your tools rely on `ctx.elicit` or `ctx.list_roots`, they continue to work against clients on the earlier eras, and the sessionless replacements will expand this table as they land. Sampling is deprecated on every era and will not return on modern connections — migrate those tools to server-side LLM calls.
If your tools rely on `ctx.elicit` or `ctx.list_roots`, they continue to work against clients on the earlier eras; on the modern era, reach for the guard pattern instead (see [Elicitation on the modern protocol](/servers/elicitation#elicitation-on-the-modern-protocol)). Sampling is deprecated on every era and will not return on modern connections — migrate those tools to server-side LLM calls.

View file

@ -543,7 +543,9 @@ If you need to support both eras, branch on `ctx.protocol_version`: return an `I
### Sampling and roots
Elicitation is the most common request to carry this way, but the same mechanism carries **sampling** and **roots** requests too — the `input_requests` map can hold any of the three, and each answer comes back in `ctx.input_responses` under its key (an `ElicitResult`, `CreateMessageResult`, or `ListRootsResult`). See [Sampling](/servers/sampling) and [Client Roots](/clients/roots) for what those requests contain. `fastmcp.Client` answers all three from the handlers you already configured, so a guard tool that mixes them needs no extra client wiring.
Elicitation is the most common request to carry this way, and **roots** requests work identically — the `input_requests` map holds them the same way, and each answer comes back in `ctx.input_responses` under its key (an `ElicitResult` or `ListRootsResult`). See [Client Roots](/clients/roots) for what a roots request contains. `fastmcp.Client` answers both from the handlers you already configured, so a guard tool that mixes them needs no extra client wiring.
The map can structurally hold a **sampling** request too (its answer would be a `CreateMessageResult`), but SEP-2577 deprecated server-initiated sampling on the modern protocol, so reach for a direct server-side LLM call instead of routing generation through a guard round. See [Sampling](/servers/sampling).
### Middleware

View file

@ -24,7 +24,7 @@ answer.
</Warning>
<Note>
This page covers `ctx.sample()`, which requests generation over the handshake-era back-channel. On the modern protocol (2026-07-28) a tool can instead request a sampling round from the client as part of a multi-round guard call — the same mechanism that carries elicitation. See [Elicitation on the modern protocol](/servers/elicitation#elicitation-on-the-modern-protocol) for how a guard tool returns `input_requests` and reads the answers back off `ctx.input_responses`.
This page covers `ctx.sample()`, which requests generation over the handshake-era back-channel. The modern protocol's guard mechanism can structurally carry a sampling request in its `input_requests` map, but SEP-2577 deprecated server-initiated sampling as a pattern rather than only the `ctx.sample()` spelling of it, so that is not a supported migration path. Use the guard mechanism for [elicitation](/servers/elicitation#elicitation-on-the-modern-protocol) and roots, and call an LLM directly from your server for generation.
</Note>
LLM sampling allows your MCP tools to request text generation from an LLM during execution. This enables tools to leverage AI capabilities for analysis, generation, reasoning, and more—without the client needing to orchestrate multiple calls.