GeezerVison is showing github.com — [v2.0.x] Point imports of mcp.server.fastmcp at the migration guide by maxisbey · Pull Request #3393 · modelcontextprotocol/python-sdk
Conversation
Backport of #3388 onto v2.0.0 so it can ship as 2.0.1. v2.0.x was cut at the v2.0.0 tag; this PR is the only thing on top of it.
Three cherry-picks (-x, all applied cleanly):
Motivation and Context
See #3388. A 2.0.1 reaches installs pinned to mcp==2.0.* / <2.1; everyone unpinned gets it from the next release off main.
How Has This Been Tested?
On this branch: ./scripts/test (5585 passed, 100% coverage, strict-no-cover clean), pyright, ruff, scripts/docs/build_config.py (no fastmcp page), and the import by hand. uv-dynamic-versioning resolves the branch as 2.0.1.devN, so a v2.0.1 tag here yields 2.0.1.
Breaking Changes
None.
Types of changes
- Bug fix (non-breaking change which fixes an issue)
- New feature (non-breaking change which adds functionality)
- Breaking change (fix or feature that would cause existing functionality to change)
- Documentation update
Checklist
- I am assigned to the linked issue (or it is labeled
help wanted, or I'm a maintainer)
- I have disclosed any AI assistance and can explain the change in my own words
- I have read the MCP Documentation
- My code follows the repository's style guidelines
- New and existing tests pass locally
- I have added appropriate error handling
- I have added or updated documentation as needed
Additional context
Release: gh release create v2.0.1 --target <merge sha on v2.0.x> --latest=false so 2.1.0 stays "Latest". deploy-docs.yml only fires from main, so the docs site is unaffected by this branch. If you'd rather keep #3380 as its own commit on v2.0.x, rebase-merge instead of squash.
AI Disclaimer
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
…cepts Metadata 2.5 (#3380)
(cherry picked from commit 4d6f87e)
v1 code running against mcp 2 fails with a bare "No module named
'mcp.server.fastmcp'", which reads like a broken install and gives no
hint that the package was renamed in a new major version. Add a plain
module at the old path whose only statement raises ModuleNotFoundError
with a message that keeps the canonical prefix, names the replacement
import, links the migration guide, and mentions pinning mcp<2.
The exception type and its `name` attribute match what a genuinely
missing module produces, so existing `except ImportError`,
`except ModuleNotFoundError`, and `exc.name` fallbacks keep working and
nothing is re-exported or warned about. The module is a file rather
than a package so tools that walk packages do not execute it, and it is
excluded from the generated API reference since it carries no API.
(cherry picked from commit b189452)
The first-symptom row keeps the verbatim 2.0/2.1 text people search
for while staying true once the message carries a pointer, and the
FastMCP section notes that the old path raises ModuleNotFoundError and
that dual-version import fallbacks continue to work.
(cherry picked from commit 3330346)
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.
Beyond the inline finding, I also checked whether shipping the tombstone as a real module file breaks spec-based introspection — importlib.util.find_spec("mcp.server.fastmcp") now returns a ModuleSpec where it previously returned None, but this is an inherent, low-impact tradeoff of the tombstone approach (find_spec does not execute the module) and was ruled out as a bug.
Extended reasoning...
A finding on docs/migration.md's new claim about except ImportError fallbacks is posted inline, so approval is off the table. Separately, two candidate issues about the stub file's visibility to importlib.util.find_spec were investigated and ruled out: find_spec locating the module without executing it means code probing for the v1 module via spec lookup sees it as present, but that is a known cost of any tombstone module rather than a defect in this change, and the common dual-major pattern (try-import with a ModuleNotFoundError fallback, covered by the new tests in tests/server/test_fastmcp.py) works as intended.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
|
- `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 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 fastmcp spelling (valid in v1, whose mcp/server/__init__.py imported .fastmcp): CPython's _handle_fromlist swallows a fromlist submodule's ModuleNotFoundError exactly when exc.name matches the dotted path and the module is absent from sys.modules — both true for the shim — so the user gets a plain ImportError: cannot import name 'fastmcp' from 'mcp.server' with no migration pointer, and an except ModuleNotFoundError fallback 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 to import mcp.server.fastmcp / from mcp.server.fastmcp import ..., or note that only except ImportError covers every spelling.
Extended reasoning...
Path: from mcp.server import fastmcp compiles to import('mcp.server', fromlist=['fastmcp']). mcp.server (v2) has no fastmcp attribute, so _handle_fromlist runs import_('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 guard try: 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 2
Verification: 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, whose mcp/server/__init__.py did from .fastmcp import FastMCP, binding the submodule attribute) compiles to __import__('mcp.server', fromlist=('fastmcp',)). v2's src/mcp/server/__init__.py binds no `f
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
claude[bot] reacted with thumbs up emoji
claude[bot] reacted with thumbs down emoji
All reactions
-
👍
1 reaction
-
👎
1 reaction
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
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.
1 issue found across 5 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="docs/migration.md">
<violation number="1" location="docs/migration.md:670">
P2: Scope this fallback claim to `import mcp.server.fastmcp` and `from mcp.server.fastmcp import ...`, or tell users to catch `ImportError`: `from mcp.server import fastmcp` suppresses the shim's `ModuleNotFoundError` and raises `ImportError` instead.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
|
- `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 comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Scope this fallback claim to import mcp.server.fastmcp and from mcp.server.fastmcp import ..., or tell users to catch ImportError: from mcp.server import fastmcp suppresses the shim's ModuleNotFoundError and raises ImportError instead.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/migration.md, line 670:
<comment>Scope this fallback claim to `import mcp.server.fastmcp` and `from mcp.server.fastmcp import ...`, or tell users to catch `ImportError`: `from mcp.server import fastmcp` suppresses the shim's `ModuleNotFoundError` and raises `ImportError` instead.</comment>
<file context>
@@ -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.
+
### What is unchanged on `MCPServer`
</file context>
|
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. |
|
+Importing `mcp.server.fastmcp`, or anything below it, raises `ModuleNotFoundError` (newer 2.x releases include a link to this section in its message), so fallbacks around `import mcp.server.fastmcp` or `from mcp.server.fastmcp import ...` keep working. |
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Milestone
No milestone
Development
Successfully merging this pull request may close these issues.
Uh oh!
There was an error while loading. Please reload this page.
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.
|