Skip to content

Commit 1672788

Browse files
committed
Trim the dispatcher-swap migration notes
Cut the ClientSession section by about a third: merged single-concern bullets (stray responses; the timeout exemptions), dropped rationale padding, reframed the shutdown bullet as new-versus-v1 (v1 never answered at shutdown), and removed the REQUEST_CANCELLED bullet entirely - the constant never existed in v1, so it has no place in a v1-to-v2 guide.
1 parent 1703d42 commit 1672788

1 file changed

Lines changed: 15 additions & 22 deletions

File tree

docs/migration.md

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -634,8 +634,6 @@ server = Server("my-server", on_call_tool=handle_call_tool)
634634

635635
The `mcp.shared.context` module has been removed. `RequestContext` is now split into `ClientRequestContext` (in `mcp.client.context`) and `ServerRequestContext` (in `mcp.server.context`).
636636

637-
The split separates shared fields from server-specific fields. There is no shared `RequestContext` generic anymore — each concrete class fixes its session type.
638-
639637
**`RequestContext` changes:**
640638

641639
- The `RequestContext[SessionT, LifespanContextT, RequestT]` generic no longer exists; use `ClientRequestContext` or `ServerRequestContext[LifespanContextT, RequestT]`
@@ -1168,29 +1166,24 @@ In practice, replace direct `ServerSession` use with `Server.run(read_stream, wr
11681166

11691167
### `ClientSession` now runs on `JSONRPCDispatcher`; `BaseSession` removed
11701168

1171-
`ClientSession` keeps its public surface — the `(read_stream, write_stream, ...)` constructor, every typed method, manual `initialize()`, and the async context-manager lifecycle — but the v1 receive loop (`BaseSession`) underneath it is gone. A new keyword-only `dispatcher=` constructor argument accepts a pre-built dispatcher instead of the stream pair (for example a `DirectDispatcher` for in-process embedding).
1172-
1173-
Code that imported or subclassed `BaseSession` directly has no shim — the class is removed outright. The receive-loop engine it implemented now lives in `JSONRPCDispatcher` (`mcp.shared.jsonrpc_dispatcher`); to customize client behavior, use the `ClientSession` constructor callbacks, or supply your own engine through the `dispatcher=` keyword.
1169+
`ClientSession`'s public surface is unchanged — same constructor, typed methods, manual `initialize()`, and async context-manager lifecycle — but `BaseSession`, the v1 receive loop underneath it, is removed with no shim. The engine now lives in `JSONRPCDispatcher` (`mcp.shared.jsonrpc_dispatcher`). To customize client behavior, use the `ClientSession` constructor callbacks, or pass a pre-built dispatcher via the new keyword-only `dispatcher=` constructor argument (e.g. a `DirectDispatcher` for in-process embedding).
11741170

11751171
Behavior changes:
11761172

1177-
- **Request ids count from 1** (previously 0). Progress tokens, which reuse the request id, shift the same way. Ids are opaque per JSON-RPC; do not assign meaning to them.
1178-
- **Timeouts**: the error message is now `Request 'tools/call' timed out` (previously `Timed out while waiting for response to CallToolRequest. Waited N seconds.`), and a timed-out or abandoned request is followed by `notifications/cancelled` on the wire, so the server stops the handler instead of leaving it running. The `initialize` request is never cancelled this way, and requests sent with resumption metadata are also exempt so they stay resumable.
1179-
- **No cancellation for requests that never reached the wire.** A timed-out or caller-cancelled request whose initial write never completed is failed locally without `notifications/cancelled` — the peer never saw the id, so there is nothing to cancel.
1180-
- **The resumption exemption applies only when the hints reach the transport.** A request sent from inside a request callback carries stream-routing metadata that takes precedence, so its resumption hints are dropped — and an abandoned one gets the courtesy `notifications/cancelled` like any other request.
1181-
- **Server-initiated requests run concurrently.** Sampling/elicitation/roots callbacks no longer serialize the receive loop: a slow callback does not block other traffic, a callback may itself send requests without deadlocking, and a server's `notifications/cancelled` now actually interrupts the callback (the request is then answered with an error response).
1182-
- **Session shutdown answers in-flight server-initiated requests with `CONNECTION_CLOSED`** (-32000, `Connection closed`) instead of -32002. The write is bounded (about one second), so closing a session stays fast even when the transport has stopped accepting writes.
1183-
- **The `REQUEST_CANCELLED` constant is removed from `mcp.types`.** Its value (-32002) collided with the spec's resource-not-found error code, and the shutdown response above was its only use.
1184-
- **Notification callbacks are concurrent.** `logging_callback`, `progress_callback`, and `message_handler` start in arrival order, but each delivery runs as its own task with no completion-before-response guarantee (matching the TypeScript, C#, and Go SDKs): deliveries may interleave, and a `progress_callback` delivery may finish after the request it reports on has returned. Callbacks that need strict sequencing must coordinate themselves.
1185-
- **Transport-level `Exception` items are delivered concurrently too.** An `Exception` the transport places on the read stream is dispatched to `message_handler` as its own task, like notification callbacks, instead of blocking the receive loop — and a `message_handler` that raises on it is logged, not fatal to the session.
1186-
- **Unknown-id responses are ignored**, as the spec asks. v1 surfaced them to `message_handler` as a `RuntimeError`; nothing is surfaced now.
1187-
- **Error responses with a null `id`** — the JSON-RPC shape for a peer reporting a parse error — are now dropped with a debug log. v1 surfaced them to `message_handler` as an `MCPError`.
1188-
- **A raising request callback** is answered with `code=0` and the exception text. v1 flattened every callback exception to `INVALID_PARAMS`. Callbacks that want a specific error response should return `ErrorData` (unchanged) or raise `MCPError`. One carve-out: a callback that raises pydantic's `ValidationError` is still answered with `INVALID_PARAMS` (`"Invalid request parameters"`, empty `data`) because the dispatcher cannot distinguish it from inbound-params validation — this conflation is pre-existing v1 behavior, and a revisit is pending.
1189-
- **`send_request` before entering the context manager** raises `RuntimeError` immediately; v1 wrote to the transport and hung until the timeout. After the connection has closed, `send_request` instead raises `MCPError` (`CONNECTION_CLOSED`), matching what an in-flight request receives — `RuntimeError` remains only for calls before entry. `send_notification` before entry still works.
1190-
- **`send_notification` no longer takes `related_request_id`, and `send_request` no longer accepts `ServerMessageMetadata`.** The hint was never serialized by any client transport in v1 or v2 — it exists for the server's streamable-HTTP stream routing. Progress and response correlation via `progressToken` and the request id is unaffected.
1191-
- **The private `mcp.shared._context.RequestContext` generic is deleted.** Client callbacks now receive the concrete `mcp.client.ClientRequestContext`, whose `request_id` is always populated (the client only builds a context for inbound requests). Annotations spelled `RequestContext[ClientSession]` become `ClientRequestContext`.
1192-
1193-
`mcp.shared.session` is now a compatibility module: `ProgressFnT` is re-exported (its home is `mcp.shared.dispatcher`), and `RequestResponder` remains as a typing-only stub so `MessageHandlerFnT` annotations keep importing — it has been unreachable at runtime since the server-side swap. `RequestResponder.respond()` no longer exists.
1173+
- **Request ids count from 1** (previously 0); progress tokens, which reuse the id, shift too. Ids are opaque per JSON-RPC — do not assign meaning to them.
1174+
- **Timeouts**: the error message is now `Request 'tools/call' timed out`, and a timed-out or abandoned request is followed by `notifications/cancelled` so the server stops the handler instead of leaving it running. Exempt: `initialize`, requests sent with resumption metadata (so they stay resumable), and requests whose initial write never completed (the peer never saw the id).
1175+
- **Resumption hints sent from inside a request callback are dropped** (stream-routing metadata takes precedence there), so those requests are cancelled like any other.
1176+
- **Server-initiated requests run concurrently.** A slow sampling/elicitation/roots callback no longer blocks other traffic, a callback may itself send requests without deadlocking, and a server's `notifications/cancelled` now interrupts the callback (the request is then answered with an error).
1177+
- **Session shutdown now answers in-flight server-initiated requests with `CONNECTION_CLOSED` (-32000)**; v1 left them unanswered. The write is bounded (~1s) so closing stays fast.
1178+
- **Notification callbacks are concurrent.** `logging_callback`, `progress_callback`, and `message_handler` deliveries start in arrival order but each runs as its own task: they may interleave, and a `progress_callback` delivery may finish after the request it reports on has returned. Callbacks that need strict sequencing must coordinate themselves.
1179+
- **Transport-level `Exception` items are delivered to `message_handler` the same way** — as their own task, without blocking the receive loop — and a `message_handler` that raises on one is logged, not fatal to the session.
1180+
- **Stray responses are no longer surfaced to `message_handler`.** Responses with an unknown id are ignored (as the spec asks; v1 surfaced a `RuntimeError`), and error responses with a null `id` — a peer reporting a parse error — are dropped with a debug log (v1 surfaced an `MCPError`).
1181+
- **A raising request callback** is answered with `code=0` and the exception text; v1 flattened every callback exception to `INVALID_PARAMS`. For a specific error response, return `ErrorData` (unchanged) or raise `MCPError`. One carve-out: pydantic's `ValidationError` is still answered with `INVALID_PARAMS`, as in v1.
1182+
- **`send_request` before entering the context manager** raises `RuntimeError` immediately; v1 wrote to the transport and hung until the timeout. After the connection has closed it raises `MCPError` (`CONNECTION_CLOSED`) instead. `send_notification` before entry still works.
1183+
- **`send_notification` no longer takes `related_request_id`, and `send_request` no longer accepts `ServerMessageMetadata`.** No client transport ever serialized these hints; progress and response correlation via `progressToken` and the request id is unaffected.
1184+
- **Client callbacks now receive `mcp.client.ClientRequestContext`** (its `request_id` is always populated); the private `mcp.shared._context.RequestContext` generic is deleted. Annotations spelled `RequestContext[ClientSession]` become `ClientRequestContext`.
1185+
1186+
`mcp.shared.session` is now a compatibility module: `ProgressFnT` is re-exported (its home is `mcp.shared.dispatcher`), and `RequestResponder` remains as a typing-only stub so `MessageHandlerFnT` annotations keep importing. `RequestResponder.respond()` no longer exists.
11941187

11951188
### Experimental Tasks support removed
11961189

0 commit comments

Comments
 (0)