Client-side 2026-07-28 support: .discover()/.adopt() + Client(mode=); request-metadata green (#2950)
This commit is contained in:
parent
ae13ede143
commit
f226d00d0a
1 changed files with 2533 additions and 993 deletions
|
|
@ -159,6 +159,12 @@ The `headers`, `timeout`, `sse_read_timeout`, and `auth` parameters have been re
|
|||
|
||||
Note: `sse_client` retains its `headers`, `timeout`, `sse_read_timeout`, and `auth` parameters — only the streamable HTTP transport changed.
|
||||
|
||||
### `StreamableHTTPTransport.protocol_version` attribute removed
|
||||
|
||||
The transport no longer holds per-connection protocol state; era-dependent headers (e.g. `MCP-Protocol-Version`) are now supplied per-message by the session. If you were reading `transport.protocol_version` to learn the negotiated version, read `session.protocol_version` (or `client.protocol_version` on the high-level `Client`) instead.
|
||||
|
||||
The `MCP_PROTOCOL_VERSION` header-name constant has moved: import `MCP_PROTOCOL_VERSION_HEADER` from `mcp.shared.inbound` instead of `MCP_PROTOCOL_VERSION` from `mcp.client.streamable_http`.
|
||||
|
||||
### `terminate_windows_process` removed
|
||||
|
||||
The deprecated `mcp.os.win32.utilities.terminate_windows_process` function has been
|
||||
|
|
@ -326,9 +332,9 @@ result = await session.list_resources(params=PaginatedRequestParams(cursor="next
|
|||
result = await session.list_tools(params=PaginatedRequestParams(cursor="next_page_token"))
|
||||
```
|
||||
|
||||
### `ClientSession.get_server_capabilities()` replaced by `initialize_result` property
|
||||
### `ClientSession.get_server_capabilities()` replaced by era-neutral accessors
|
||||
|
||||
`ClientSession` now stores the full `InitializeResult` via an `initialize_result` property. This provides access to `server_info`, `capabilities`, `instructions`, and the negotiated `protocol_version` through a single property. The `get_server_capabilities()` method has been removed.
|
||||
`ClientSession` now exposes the negotiated server metadata as properties: `server_capabilities`, `server_info`, `instructions`, and `protocol_version`. These are populated by whichever connection step ran (`initialize()` for ≤2025-11-25 servers, `discover()` for 2026-07-28+), and are `None` if none has — matching v1's `get_server_capabilities()`. The `get_server_capabilities()` method has been removed.
|
||||
|
||||
**Before (v1):**
|
||||
|
||||
|
|
@ -340,15 +346,23 @@ capabilities = session.get_server_capabilities()
|
|||
**After (v2):**
|
||||
|
||||
```python
|
||||
result = session.initialize_result
|
||||
if result is not None:
|
||||
capabilities = result.capabilities
|
||||
server_info = result.server_info
|
||||
instructions = result.instructions
|
||||
version = result.protocol_version
|
||||
capabilities = session.server_capabilities
|
||||
server_info = session.server_info
|
||||
instructions = session.instructions
|
||||
version = session.protocol_version
|
||||
```
|
||||
|
||||
The high-level `Client.initialize_result` returns the same `InitializeResult` but is non-nullable — initialization is guaranteed inside the context manager, so no `None` check is needed. This replaces v1's `Client.server_capabilities`; use `client.initialize_result.capabilities` instead.
|
||||
The raw handshake result is also retained: `session.initialize_result` is set after `initialize()` (≤2025-11-25 servers — including `stateless_http=True` servers, which still answer `initialize`); `session.discover_result` is set after `discover()` (2026-07-28+ servers). At most one is non-`None`.
|
||||
|
||||
On the high-level `Client`, `client.server_capabilities`, `client.server_info`, and `client.protocol_version` are non-nullable inside the context manager. `client.instructions` remains `str | None` since the server may omit it. (The lowlevel `ClientSession` still lets you call methods before any handshake, as in v1; `Client` always connects on enter — by default it probes `server/discover` and falls back to the initialize handshake.)
|
||||
|
||||
### `Client` defaults to `mode='auto'`
|
||||
|
||||
In v1, connecting to a server always performed the `initialize` handshake. In v2, `Client` defaults to `mode='auto'`: on enter it probes `server/discover` and, if the server doesn't support it, falls back to the `initialize` handshake. Pass `mode='legacy'` to force the initialize handshake and reproduce v1's byte-identical pre-2026 behavior, or pass a modern protocol-version string (e.g. `mode='2026-07-28'`) to pin a version without probing.
|
||||
|
||||
For an in-process `Client(server)` (where `server` is a `Server` or `MCPServer` instance), `mode='auto'` dispatches calls directly through `DirectDispatcher` with no JSON-RPC framing. Pass `mode='legacy'` if you need the in-memory JSON-RPC transport that v1 used.
|
||||
|
||||
`Client.send_ping()` is deprecated (ping is removed in 2026-07-28); pin `mode='legacy'` if you need it.
|
||||
|
||||
### `McpError` renamed to `MCPError`
|
||||
|
||||
|
|
@ -764,6 +778,10 @@ async def my_tool(ctx: Context) -> str: ...
|
|||
async def my_tool(ctx: Context[MyLifespanState]) -> str: ...
|
||||
```
|
||||
|
||||
### Version constants
|
||||
|
||||
`SUPPORTED_PROTOCOL_VERSIONS` is deprecated — it's now the union of `HANDSHAKE_PROTOCOL_VERSIONS` (initialize-handshake versions) and `MODERN_PROTOCOL_VERSIONS` (per-request-envelope versions). If you were using it to mean "versions the initialize handshake accepts", switch to `HANDSHAKE_PROTOCOL_VERSIONS`. Named scalars derived from these tuples are now exported alongside them — `LATEST_HANDSHAKE_VERSION`, `LATEST_MODERN_VERSION`, `OLDEST_SUPPORTED_VERSION` — so prefer those over indexing the tuples directly.
|
||||
|
||||
### `ProgressContext` and `progress()` context manager removed
|
||||
|
||||
The `mcp.shared.progress` module (`ProgressContext`, `Progress`, and the `progress()` context manager) has been removed. This module had no real-world adoption — all users send progress notifications via `Context.report_progress()` or `session.send_progress_notification()` directly.
|
||||
|
|
@ -796,6 +814,12 @@ await session.send_progress_notification(
|
|||
)
|
||||
```
|
||||
|
||||
### Handler progress reporting: prefer `ctx.report_progress()` over manual `progress_token`
|
||||
|
||||
Reading `ctx.meta["progress_token"]` and calling `session.send_progress_notification(token, ...)` is specific to the JSON-RPC transport path. On the in-process modern path (`DirectDispatcher` / `Client(server)`), there is no wire token in `_meta`, so handlers that gate progress on the token's presence go silent.
|
||||
|
||||
`ctx.report_progress(progress, total, message)` works on every dispatcher: it sends a progress notification when a token is present and routes the update through the dispatcher's progress channel otherwise, no-opping only when the caller did not request progress at all. `session.send_progress_notification(progress_token, ...)` is unchanged and still works on JSON-RPC transports for code that already holds a token.
|
||||
|
||||
### `create_connected_server_and_client_session` removed
|
||||
|
||||
The `create_connected_server_and_client_session` helper in `mcp.shared.memory` has been removed. Use `mcp.client.Client` instead — it accepts a `Server` or `MCPServer` instance directly and handles the in-memory transport and session setup for you.
|
||||
|
|
@ -818,7 +842,7 @@ async with Client(server) as client:
|
|||
result = await client.call_tool("my_tool", {"x": 1})
|
||||
```
|
||||
|
||||
`Client` accepts the same callback parameters the old helper did (`sampling_callback`, `list_roots_callback`, `logging_callback`, `message_handler`, `elicitation_callback`, `client_info`) plus `raise_exceptions` to surface server-side errors.
|
||||
`Client` accepts the same callback parameters the old helper did (`sampling_callback`, `list_roots_callback`, `logging_callback`, `message_handler`, `elicitation_callback`, `client_info`) plus `raise_exceptions` to surface server-side errors and `mode` to control version negotiation (`'auto'` by default; `'legacy'` reproduces v1's initialize-only handshake).
|
||||
|
||||
If you need direct access to the underlying `ClientSession` and memory streams (e.g., for low-level transport testing), `create_client_server_memory_streams` is still available in `mcp.shared.memory`:
|
||||
|
||||
|
|
@ -1289,6 +1313,12 @@ warnings.filterwarnings("ignore", category=MCPDeprecationWarning)
|
|||
|
||||
No migration is required during the deprecation window. New code should avoid building on these features, since they may be removed in a future spec version.
|
||||
|
||||
### Client-to-server progress deprecated (2026-07-28)
|
||||
|
||||
The 2026-07-28 spec restricts `notifications/progress` to the server-to-client direction only — `ProgressNotification` is no longer in `ClientNotification`. `Client.send_progress_notification()` and `ClientSession.send_progress_notification()` now carry `typing_extensions.deprecated` and emit `mcp.MCPDeprecationWarning` at runtime. They continue to work against servers negotiating 2025-11-25 or earlier.
|
||||
|
||||
On the server side, prefer the new dispatcher-agnostic `ServerSession.report_progress(progress, total, message)` (and `Context.report_progress()` on `MCPServer`) over the raw `ServerSession.send_progress_notification(progress_token, …)`. `report_progress` encapsulates the "no-op when the caller did not request progress" rule and works on every dispatcher; the raw token-taking form remains for handlers that read `_meta.progressToken` directly.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
### OAuth metadata URLs no longer gain a trailing slash
|
||||
|
|
|
|||
Loading…
Reference in a new issue