-
Notifications
You must be signed in to change notification settings - Fork 3.8k
[v2.0.x] Point imports of mcp.server.fastmcp at the migration guide #3393
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+82
−8
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -12,7 +12,7 @@ Every section heading below names the API it affects, so searching this page for | |||||
|
|
||||||
| | Change | First symptom | Section | | ||||||
| |---|---|---| | ||||||
| | `FastMCP` renamed to `MCPServer` | `ModuleNotFoundError: No module named 'mcp.server.fastmcp'` | [`FastMCP` renamed](#fastmcp-renamed-to-mcpserver) | | ||||||
| | `FastMCP` renamed to `MCPServer` | `ModuleNotFoundError: No module named 'mcp.server.fastmcp'` (newer 2.x releases follow it with a pointer to this guide) | [`FastMCP` renamed](#fastmcp-renamed-to-mcpserver) | | ||||||
| | Fields renamed from camelCase to snake_case | `AttributeError: 'Tool' object has no attribute 'inputSchema'` | [snake_case fields](#field-names-changed-from-camelcase-to-snake_case) | | ||||||
| | `mcp.types` names removed | `ImportError: cannot import name 'Content' from 'mcp.types'` | [Removed types](#removed-type-aliases-and-classes) | | ||||||
| | `McpError` renamed to `MCPError` | `ImportError: cannot import name 'McpError' from 'mcp'` | [`McpError` renamed](#mcperror-renamed-to-mcperror) | | ||||||
|
|
@@ -667,6 +667,8 @@ All submodules under `mcp.server.fastmcp.*` are now under `mcp.server.mcpserver. | |||||
| - `ToolError`, `ResourceError` — from `mcp.server.mcpserver.exceptions` | ||||||
| - `MCPServerError` (renamed from `FastMCPError`) — from `mcp.server.mcpserver.exceptions` | ||||||
|
|
||||||
| Importing `mcp.server.fastmcp`, or anything below it, raises `ModuleNotFoundError` (newer 2.x releases include a link to this section in its message), so existing `except ImportError` or `except ModuleNotFoundError` fallbacks around the v1 import keep working. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Scope this fallback claim to Prompt for AI agents
Suggested change
|
||||||
|
|
||||||
| ### What is unchanged on `MCPServer` | ||||||
|
|
||||||
| Beyond the changes covered in this section, the everyday `FastMCP` surface carries over to `MCPServer` as-is: | ||||||
|
|
||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| """Removed in mcp 2: `FastMCP` is now `mcp.server.mcpserver.MCPServer`. | ||
| This module has no API. Importing it, or anything below it, raises | ||
| `ModuleNotFoundError` with a message that points at the migration guide. It | ||
| exists only because the bare "No module named 'mcp.server.fastmcp'" gave v1 | ||
| code no hint that the installed SDK is a different major version. | ||
| """ | ||
|
|
||
| _MESSAGE = ( | ||
| "No module named 'mcp.server.fastmcp'. This is mcp 2.x, where FastMCP was renamed to MCPServer " | ||
| "(from mcp.server.mcpserver import MCPServer) and other APIs changed; see the migration guide at " | ||
| "https://py.sdk.modelcontextprotocol.io/v2/migration/#fastmcp-renamed-to-mcpserver " | ||
| "or pin 'mcp<2' to keep running v1 code." | ||
| ) | ||
|
|
||
| raise ModuleNotFoundError(_MESSAGE, name=__name__) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| """The removed v1 import path `mcp.server.fastmcp` fails with a pointer to the migration guide.""" | ||
|
|
||
| import importlib | ||
| import sys | ||
|
|
||
| import pytest | ||
| from inline_snapshot import snapshot | ||
|
|
||
| import mcp.server | ||
| from mcp.server.mcpserver import MCPServer | ||
|
|
||
|
|
||
| def test_importing_fastmcp_raises_module_not_found_that_points_at_the_migration_guide() -> None: | ||
| """SDK-defined: the v1 path fails with the same exception type and `.name` as a module | ||
| that genuinely does not exist, but the message names the replacement and the guide.""" | ||
| with pytest.raises(ModuleNotFoundError) as exc_info: | ||
| importlib.import_module("mcp.server.fastmcp") | ||
|
|
||
| assert exc_info.value.name == "mcp.server.fastmcp" | ||
| assert str(exc_info.value) == snapshot( | ||
| "No module named 'mcp.server.fastmcp'. This is mcp 2.x, where FastMCP was renamed to MCPServer " | ||
| "(from mcp.server.mcpserver import MCPServer) and other APIs changed; see the migration guide at " | ||
| "https://py.sdk.modelcontextprotocol.io/v2/migration/#fastmcp-renamed-to-mcpserver " | ||
| "or pin 'mcp<2' to keep running v1 code." | ||
| ) | ||
| # A module that raises while executing is never cached, so nothing is left behind. | ||
| assert "mcp.server.fastmcp" not in sys.modules | ||
| assert not hasattr(mcp.server, "fastmcp") | ||
|
|
||
|
|
||
| def test_importing_a_fastmcp_submodule_raises_the_parent_pointer() -> None: | ||
| """SDK-defined: a deep v1 path executes `mcp.server.fastmcp` first, so it fails with that | ||
| module's message and `.name` rather than a bare error for the leaf.""" | ||
| with pytest.raises(ModuleNotFoundError) as parent: | ||
| importlib.import_module("mcp.server.fastmcp") | ||
| with pytest.raises(ModuleNotFoundError) as exc_info: | ||
| importlib.import_module("mcp.server.fastmcp.utilities.types") | ||
|
|
||
| assert exc_info.value.name == "mcp.server.fastmcp" | ||
| assert str(exc_info.value) == str(parent.value) | ||
|
|
||
|
|
||
| def test_v1_first_import_shim_falls_back_to_mcpserver() -> None: | ||
| """SDK-defined: projects that support both majors try the v1 import and fall back on | ||
| `ModuleNotFoundError` (the narrowest guard seen in the wild), which is why the pointer is | ||
| raised as exactly that type and not as a bare `ImportError` or after a warning.""" | ||
| fell_back = False | ||
| try: | ||
| server_class: type = importlib.import_module("mcp.server.fastmcp").FastMCP | ||
| except ModuleNotFoundError: | ||
| fell_back = True | ||
| server_class = MCPServer | ||
|
|
||
| assert fell_back | ||
| assert server_class is MCPServer |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 This new claim is false for the
from mcp.server import fastmcpspelling (valid in v1, whosemcp/server/__init__.pyimported.fastmcp): CPython's_handle_fromlistswallows a fromlist submodule's ModuleNotFoundError exactly whenexc.namematches the dotted path and the module is absent from sys.modules — both true for the shim — so the user gets a plainImportError: cannot import name 'fastmcp' from 'mcp.server'with no migration pointer, and anexcept ModuleNotFoundErrorfallback does NOT catch it (behavior itself is unchanged from base; the new doc sentence and the shim docstring in src/mcp/server/fastmcp.py are what overclaim). Scope the claim toimport mcp.server.fastmcp/from mcp.server.fastmcp import ..., or note that onlyexcept ImportErrorcovers every spelling.Extended reasoning...
Path:
from mcp.server import fastmcpcompiles to import('mcp.server', fromlist=['fastmcp']). mcp.server (v2) has nofastmcpattribute, so _handle_fromlist runsimport_('mcp.server.fastmcp'); the shim at src/mcp/server/fastmcp.py:16 raises ModuleNotFoundError(name='mcp.server.fastmcp'), and the failed exec removes the entry from sys.modules. _handle_fromlist's backwards-compat guard —if exc.name == from_ and sys.modules.get(from_, _ERR_MSG_PREFIX) is not None: continue— matches on both conditions (name set to name on purpose, module absent so .get returns the non-None sentinel), so the shim's exception is silently discarded. IMPORT_FROM then raises ImportError('cannot import name fastmcp from mcp.server'), which is not a ModuleNotFoundError and carries no migration pointer. Consequence: a v1-compat guardtry: from mcp.server import fastmcp / except ModuleNotFoundError:crashes despite migration.md:670's assurance that such fallbacks 'keep working', and the whole point of the shim (the pointer message) never surfaces for this spelling. Same final exception as base 2Verification: nit — The mechanism is real and the new doc sentence is false for one valid v1 spelling, but runtime behavior is unchanged from the base branch.
from mcp.server import fastmcp(valid in v1, whosemcp/server/__init__.pydidfrom .fastmcp import FastMCP, binding the submodule attribute) compiles to__import__('mcp.server', fromlist=('fastmcp',)). v2'ssrc/mcp/server/__init__.pybinds no `f