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
docs: fill migration guide gaps surfaced by upgrade eval
Adds missing migration coverage identified by running 24 automated upgrade
workers across 8 v1 codebases using only docs/migration.md as reference.
- Show Context import path in FastMCP→MCPServer section and get_context() example
- Document camelCase→snake_case field rename with common-field table
- Add complete on_* handler reference table (decorator → kwarg → params → return)
- Note that all mcp.server.fastmcp.* submodules moved to mcp.server.mcpserver.*
- Document create_connected_server_and_client_session removal (use Client instead)
- Add raise-side example for MCPError constructor signature change
- State explicitly that mcp.shared.context module was removed
- Fix _meta example to use arbitrary keys (avoid progressToken alias confusion)
- Document private _add_request_handler workaround for handlers MCPServer
doesn't expose (subscribe/unsubscribe/set_logging_level)
Copy file name to clipboardExpand all lines: docs/migration.md
+185-3Lines changed: 185 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -126,6 +126,52 @@ from mcp.types import ContentBlock, ResourceTemplateReference
126
126
# Use `str` instead of `Cursor` for pagination cursors
127
127
```
128
128
129
+
### Field names changed from camelCase to snake_case
130
+
131
+
All Pydantic model fields in `mcp.types` now use snake_case names for Python attribute access. The JSON wire format is unchanged — serialization still uses camelCase via Pydantic aliases.
132
+
133
+
**Before (v1):**
134
+
135
+
```python
136
+
result =await session.call_tool("my_tool", {"x": 1})
137
+
if result.isError:
138
+
...
139
+
140
+
tools =await session.list_tools()
141
+
cursor = tools.nextCursor
142
+
schema = tools.tools[0].inputSchema
143
+
```
144
+
145
+
**After (v2):**
146
+
147
+
```python
148
+
result =await session.call_tool("my_tool", {"x": 1})
149
+
if result.is_error:
150
+
...
151
+
152
+
tools =await session.list_tools()
153
+
cursor = tools.next_cursor
154
+
schema = tools.tools[0].input_schema
155
+
```
156
+
157
+
Common renames:
158
+
159
+
| v1 (camelCase) | v2 (snake_case) |
160
+
|----------------|-----------------|
161
+
|`inputSchema`|`input_schema`|
162
+
|`outputSchema`|`output_schema`|
163
+
|`isError`|`is_error`|
164
+
|`nextCursor`|`next_cursor`|
165
+
|`mimeType`|`mime_type`|
166
+
|`structuredContent`|`structured_content`|
167
+
|`serverInfo`|`server_info`|
168
+
|`protocolVersion`|`protocol_version`|
169
+
|`uriTemplate`|`uri_template`|
170
+
|`listChanged`|`list_changed`|
171
+
|`progressToken`|`progress_token`|
172
+
173
+
Because `populate_by_name=True` is set, the old camelCase names still work as constructor kwargs (e.g., `Tool(inputSchema={...})` is accepted), but attribute access must use snake_case (`tool.input_schema`).
174
+
129
175
### `args` parameter removed from `ClientSessionGroup.call_tool()`
130
176
131
177
The deprecated `args` parameter has been removed from `ClientSessionGroup.call_tool()`. Use `arguments` instead.
@@ -225,6 +271,28 @@ except MCPError as e:
225
271
from mcp import MCPError
226
272
```
227
273
274
+
The constructor signature also changed — it now takes `code`, `message`, and optional `data` directly instead of wrapping an `ErrorData`:
The `FastMCP` class has been renamed to `MCPServer` to better reflect its role as the main server class in the SDK. This is a simple rename with no functional changes to the class itself.
@@ -240,11 +308,19 @@ mcp = FastMCP("Demo")
240
308
**After (v2):**
241
309
242
310
```python
243
-
from mcp.server.mcpserver import MCPServer
311
+
from mcp.server.mcpserver import MCPServer, Context
244
312
245
313
mcp = MCPServer("Demo")
246
314
```
247
315
316
+
`Context` is the type annotation for the `ctx` parameter injected into tools, resources, and prompts (see [`get_context()` removed](#mcpserverget_context-removed) below).
317
+
318
+
All submodules under `mcp.server.fastmcp.*` are now under `mcp.server.mcpserver.*` with the same structure. Common imports:
319
+
320
+
-`Image`, `Audio` — from `mcp.server.mcpserver` (or `.utilities.types`)
321
+
-`UserMessage`, `AssistantMessage` — from `mcp.server.mcpserver.prompts.base`
322
+
-`ToolError`, `ResourceError` — from `mcp.server.mcpserver.exceptions`
323
+
248
324
### `mount_path` parameter removed from MCPServer
249
325
250
326
The `mount_path` parameter has been removed from `MCPServer.__init__()`, `MCPServer.run()`, `MCPServer.run_sse_async()`, and `MCPServer.sse_app()`. It was also removed from the `Settings` class.
The internal layers (`ToolManager.call_tool`, `Tool.run`, `Prompt.render`, `ResourceTemplate.create_resource`, etc.) now require `context` as a positional argument.
345
423
424
+
### Registering lowlevel handlers on `MCPServer` (workaround)
425
+
426
+
`MCPServer` does not expose public APIs for `subscribe_resource`, `unsubscribe_resource`, or `set_logging_level` handlers. In v1, the workaround was to reach into the private lowlevel server and use its decorator methods:
This is a private API and may change. A public way to register these handlers on `MCPServer` is planned; until then, use this workaround or use the lowlevel `Server` directly.
462
+
346
463
### Replace `RootModel` by union types with `TypeAdapter` validation
347
464
348
465
The following union types are no longer `RootModel` subclasses:
@@ -428,6 +545,8 @@ server = Server("my-server", on_call_tool=handle_call_tool)
428
545
429
546
### `RequestContext` type parameters simplified
430
547
548
+
The `mcp.shared.context` module has been removed. `RequestContext` is now split into `ClientRequestContext` (in `mcp.client.context`) and `ServerRequestContext` (in `mcp.server.context`).
549
+
431
550
The `RequestContext` class has been split to separate shared fields from server-specific fields. The shared `RequestContext` now only takes 1 type parameter (the session type) instead of 3.
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.
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.
615
+
616
+
**Before (v1):**
617
+
618
+
```python
619
+
from mcp.shared.memory import create_connected_server_and_client_session
620
+
621
+
asyncwith create_connected_server_and_client_session(server) as session:
622
+
result =await session.call_tool("my_tool", {"x": 1})
623
+
```
624
+
625
+
**After (v2):**
626
+
627
+
```python
628
+
from mcp.client import Client
629
+
630
+
asyncwith Client(server) as client:
631
+
result =await client.call_tool("my_tool", {"x": 1})
632
+
```
633
+
634
+
`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.
635
+
636
+
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`:
637
+
638
+
```python
639
+
import anyio
640
+
from mcp.client.session import ClientSession
641
+
from mcp.shared.memory import create_client_server_memory_streams
642
+
643
+
asyncwith create_client_server_memory_streams() as (client_streams, server_streams):
asyncwith ClientSession(*client_streams) as session:
647
+
await session.initialize()
648
+
...
649
+
tg.cancel_scope.cancel()
650
+
```
651
+
493
652
### Resource URI type changed from `AnyUrl` to `str`
494
653
495
654
The `uri` field on resource-related types now uses `str` instead of Pydantic's `AnyUrl`. This aligns with the [MCP specification schema](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/draft/schema.ts) which defines URIs as plain strings (`uri: string`) without strict URL validation. This change allows relative paths like `users/me` that were previously rejected.
@@ -645,6 +804,29 @@ server = Server("my-server", on_list_tools=handle_list_tools, on_call_tool=handl
645
804
- Handlers return the full result type (e.g. `ListToolsResult`) rather than unwrapped values (e.g. `list[Tool]`).
646
805
- The automatic `jsonschema` input/output validation that the old `call_tool()` decorator performed has been removed. There is no built-in replacement — if you relied on schema validation in the lowlevel server, you will need to validate inputs yourself in your handler.
647
806
807
+
**Complete handler reference:**
808
+
809
+
All handlers receive `ctx: ServerRequestContext` as the first argument. The second argument and return type are:
810
+
811
+
| v1 decorator | v2 constructor kwarg |`params` type | return type |
0 commit comments