Skip to content

Commit 409f98b

Browse files
committed
docs: address minor migration guide gaps from upgrade eval
Follow-up to the previous commit covering the lower-severity gaps that caused hesitation but not failures during the automated upgrade eval. - Note that sse_client retains headers/timeout/auth params (only streamable HTTP changed) - Add Iterable[ReadResourceContents] to the read_resource wrapping-removed example (was the recommended v1 return type) - Note that mcp.settings no longer holds transport fields; debug and log_level remain on the constructor - Document Context generic params change (3→2, drop ServerSessionT) with before/after - Note there is currently no public handler introspection API - Show RootModel construction direction (drop wrapper call), not just parsing direction - Add follow_redirects=True to the primary streamable_http_client example and explain v1 set it internally
1 parent 3a52324 commit 409f98b

File tree

1 file changed

+50
-2
lines changed

1 file changed

+50
-2
lines changed

docs/migration.md

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ http_client = httpx.AsyncClient(
3838
headers={"Authorization": "Bearer token"},
3939
timeout=httpx.Timeout(30, read=300),
4040
auth=my_auth,
41+
follow_redirects=True,
4142
)
4243

4344
async with http_client:
@@ -48,6 +49,8 @@ async with http_client:
4849
...
4950
```
5051

52+
v1's internal client set `follow_redirects=True`; set it explicitly when supplying your own `httpx.AsyncClient` to preserve that behavior.
53+
5154
### `get_session_id` callback removed from `streamable_http_client`
5255

5356
The `get_session_id` callback (third element of the returned tuple) has been removed from `streamable_http_client`. The function now returns a 2-tuple `(read_stream, write_stream)` instead of a 3-tuple.
@@ -100,6 +103,8 @@ async with http_client:
100103

101104
The `headers`, `timeout`, `sse_read_timeout`, and `auth` parameters have been removed from `StreamableHTTPTransport`. Configure these on the `httpx.AsyncClient` instead (see example above).
102105

106+
Note: `sse_client` retains its `headers`, `timeout`, `sse_read_timeout`, and `auth` parameters — only the streamable HTTP transport changed.
107+
103108
### Removed type aliases and classes
104109

105110
The following deprecated type aliases and classes have been removed from `mcp.types`:
@@ -388,6 +393,8 @@ app = Starlette(routes=[Mount("/", app=mcp.streamable_http_app(json_response=Tru
388393

389394
**Note:** DNS rebinding protection is automatically enabled when `host` is `127.0.0.1`, `localhost`, or `::1`. This now happens in `sse_app()` and `streamable_http_app()` instead of the constructor.
390395

396+
If you were mutating these via `mcp.settings` after construction (e.g., `mcp.settings.port = 9000`), pass them to `run()` / `sse_app()` / `streamable_http_app()` instead — these fields no longer exist on `Settings`. The `debug` and `log_level` parameters remain on the constructor.
397+
391398
### `MCPServer.get_context()` removed
392399

393400
`MCPServer.get_context()` has been removed. Context is now injected by the framework and passed explicitly — there is no ambient ContextVar to read from.
@@ -500,6 +507,22 @@ notification = server_notification_adapter.validate_python(data)
500507
# No .root access needed - notification is the actual type
501508
```
502509

510+
The same applies when constructing values — the wrapper call is no longer needed:
511+
512+
**Before (v1):**
513+
514+
```python
515+
await session.send_notification(ClientNotification(InitializedNotification()))
516+
await session.send_request(ClientRequest(PingRequest()), EmptyResult)
517+
```
518+
519+
**After (v2):**
520+
521+
```python
522+
await session.send_notification(InitializedNotification())
523+
await session.send_request(PingRequest(), EmptyResult)
524+
```
525+
503526
**Available adapters:**
504527

505528
| Union Type | Adapter |
@@ -577,6 +600,22 @@ ctx: ClientRequestContext
577600
server_ctx: ServerRequestContext[LifespanContextT, RequestT]
578601
```
579602

603+
The high-level `Context` class (injected into `@mcp.tool()` etc.) similarly dropped its `ServerSessionT` parameter: `Context[ServerSessionT, LifespanContextT, RequestT]``Context[LifespanContextT, RequestT]`. Both remaining parameters have defaults, so bare `Context` is usually sufficient:
604+
605+
**Before (v1):**
606+
607+
```python
608+
async def my_tool(ctx: Context[ServerSession, None]) -> str: ...
609+
```
610+
611+
**After (v2):**
612+
613+
```python
614+
async def my_tool(ctx: Context) -> str: ...
615+
# or, with an explicit lifespan type:
616+
async def my_tool(ctx: Context[MyLifespanState]) -> str: ...
617+
```
618+
580619
### `ProgressContext` and `progress()` context manager removed
581620

582621
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.
@@ -752,6 +791,8 @@ if ListToolsRequest in server.request_handlers:
752791
server = Server("my-server", on_list_tools=handle_list_tools)
753792
```
754793

794+
If you need to check whether a handler is registered, track this yourself — there is currently no public introspection API.
795+
755796
### Lowlevel `Server`: decorator-based handlers replaced with constructor `on_*` params
756797

757798
The lowlevel `Server` class no longer uses decorator methods for handler registration. Instead, handlers are passed as `on_*` keyword arguments to the constructor.
@@ -876,10 +917,17 @@ Note: `params.arguments` can be `None` (the old decorator defaulted it to `{}`).
876917

877918
**`read_resource()` — content type wrapping removed:**
878919

879-
The old decorator auto-wrapped `str` into `TextResourceContents` and `bytes` into `BlobResourceContents` (with base64 encoding), and applied a default mime type of `text/plain`:
920+
The old decorator auto-wrapped `Iterable[ReadResourceContents]` (and the deprecated `str`/`bytes` shorthand) into `TextResourceContents`/`BlobResourceContents`, handling base64 encoding and mime-type defaulting:
880921

881922
```python
882-
# Before (v1) — str/bytes auto-wrapped with mime type defaulting
923+
# Before (v1) — Iterable[ReadResourceContents] auto-wrapped
924+
from mcp.server.lowlevel.helper_types import ReadResourceContents
925+
926+
@server.read_resource()
927+
async def handle(uri: AnyUrl) -> Iterable[ReadResourceContents]:
928+
return [ReadResourceContents(content="file contents", mime_type="text/plain")]
929+
930+
# Before (v1) — str/bytes shorthand (already deprecated in v1)
883931
@server.read_resource()
884932
async def handle(uri: str) -> str:
885933
return "file contents"

0 commit comments

Comments
 (0)