You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
Copy file name to clipboardExpand all lines: docs/migration.md
+15-22Lines changed: 15 additions & 22 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -634,8 +634,6 @@ server = Server("my-server", on_call_tool=handle_call_tool)
634
634
635
635
The `mcp.shared.context` module has been removed. `RequestContext` is now split into `ClientRequestContext` (in `mcp.client.context`) and `ServerRequestContext` (in `mcp.server.context`).
636
636
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
-
639
637
**`RequestContext` changes:**
640
638
641
639
- 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
1168
1166
1169
1167
### `ClientSession` now runs on `JSONRPCDispatcher`; `BaseSession` removed
1170
1168
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).
1174
1170
1175
1171
Behavior changes:
1176
1172
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.
0 commit comments