On this page
Import fails after MCP Python SDK 2.0
ModuleNotFoundError: No module named 'mcp.server.fastmcp' Your server code imports mcp.server.fastmcp, but the installed mcp package is 2.x, where that module no longer exists — so the process dies at import, before it speaks any MCP.
- MCP specification
- 2026-07-28
Is this you?
Your client shows a transport error rather than the traceback, so read the installed version and the module layout directly. Together these separate the three things that look identical from the client's side: the mcp package on 2.x under 1.x-era code, a 1.x pin that never took effect, and the separately published fastmcp package, which is not this problem.
- This error
mcp 2.0.0or later withmcp.server.fastmcp False, while your code still imports it. Step 3 then shows the requirement that let it through, and it is usually not your own — a dependency of a dependency declaringmcp>=1.0.0resolves the same way. APackageNotFoundErrorfrom step 2 means the standalonefastmcpdistribution is not installed, which rules it out.- Then the fix is the next section.
- Working
- Either
mcp 1.xwithmcp.server.fastmcp Trueandmcp.server.mcpserver False, which is the 1.x line your imports expect, ormcp 2.xwith those two reversed and code that importsMCPServer. Both are working states. In step 3, the requirement that namesmcpcarries an upper bound. - Then this isn't your problem — try the error reference.
3 other strings clients print for this
- MCP error -32000: Connection closed
- Failed to reconnect to plugin:<plugin>:<server>: -32000
- AttributeError: 'Server' object has no attribute 'list_tools'
Dotted spans such as <url> are placeholders — your client prints your own value there.
Fix it
Decide whether you are staying on 1.x or moving to 2.x; the fix is one line either way. To stay, bound the requirement to mcp>=1.28,<2 and re-resolve, so the lockfile actually moves back. To migrate, change from mcp.server.fastmcp import FastMCP to from mcp.server.mcpserver import MCPServer and rename the constructor call — your decorators keep the same arguments and handler signatures.
Have your agent fix this
Copy a prompt that sends your coding agent to this page to find and apply the right fix for your project.
The cause depends on your platform. Pick yours.
Are you pinning or migrating?
Pin to mcp 1.x
If you need the server starting again and are not ready to change code
- 1 Add the upper bound to the requirement itself, not just to the lockfile. The SDK's migration guide states the rule plainly: "If your package depends on
mcp, keep a<2upper bound until you've migrated." The v2.0.0 release notes givemcp>=1.28,<2as the worked example. - 2 Re-resolve afterwards, because the bound alone does not move an environment that already installed 2.x. Run
uv lockand thenuv sync, orpip install -e .for a pip project. Confirm you landed on 1.x rather than assuming it. - 3 Find the requirement that actually pulled
mcpin before you edit anything.uv tree --invertnames it, and when the unbounded requirement belongs to a dependency rather than to you, editing your ownpyproject.tomlchanges nothing — pin it in that project, or constrain it from yours. - 4 Treat the pin as temporary. The 1.x branch receives security fixes and critical bug fixes only, so the migration is still ahead of you; the pin buys the time to schedule it rather than removing the work.
- 5 For a launcher you do not control the manifest of, bound it at the launch command. Adding
"--with", "mcp<2"to auvxserver's arguments is the documented shape of this workaround, and it can be dropped once the package itself declares the bound. For apipxinstall,pipx runpip <venv> install 'mcp<2'does the same to an existing environment.
MCP Python SDK (migrate to 2.x)
If you would rather move to the 2.x line than carry a pin
- 1 Change the import and the class name.
FastMCPis nowMCPServer, and the migration guide states that "All submodules undermcp.server.fastmcp.*are now undermcp.server.mcpserver.*with the same structure" — so a submodule import is the same rewrite one level deeper. - 2 Leave your handlers alone. Under what is unchanged on
MCPServer, the guide is explicit: "@mcp.tool(),@mcp.resource(),@mcp.prompt(), and@mcp.completion()take the same arguments and handler signatures as v1." For most servers the whole diff is the import line and the constructor. - 3 Set the floor to 2 and keep an upper bound —
mcp>=2,<3— so the next major cannot do this to you again. - 4 Expect to serve older clients without configuring anything. The v2.0.0 release notes say v2 "still serves every 2025-era client from the same
MCPServer, over Streamable HTTP and stdio, with nothing to configure", so migrating the server does not require your callers to move first. - 5 Read the known gaps before you commit to a date. The release notes exclude the tasks extension (SEP-2663) from 2.0.0, and on the client leave out DPoP proof binding (SEP-1932) and the workload-identity
jwt-bearergrant. If you depend on one of those, the pin is the better answer this month.
FastMCP (the standalone package)
If your imports read from fastmcp import FastMCP rather than naming mcp
- 1 Check which FastMCP you have, because there are two and only one is affected. The class this error is about lived at
mcp.server.fastmcpinside themcppackage. The separately publishedfastmcpdistribution has its own import root, and afrom fastmcp import FastMCPline cannot raise this error. - 2 Expect
fastmcp3.x not to dragmcp2.x in either. On PyPI today,fastmcp3.4.5 requiresfastmcp-slim[client,server]==3.4.5, whoseclientandserverextras both declaremcp<2.0,>=1.24.0— already bounded, so a fresh resolve of that line cannot reach 2.x. - 3 Look elsewhere in the tree if you see this error in a project that also uses
fastmcp. Something else is requiringmcpwithout a bound;uv tree --invertnames it. - 4 Know which line is built on SDK v2 before you upgrade to reach it. The
fastmcp4.0.0b1 release, published 2026-07-28, requiresmcp>=2.0.0,<3.0.0andmcp-types>=2.0.0,<3.0.0, and its notes state that MCP Python SDK v2 "rewrote the protocol layer end to end". It is a beta, so treat it as one.
Something else
This failure has one cause and two exits. An unbounded mcp requirement resolved to 2.x under code written for 1.x, so either bound the requirement below 2 and re-resolve, or move the import to mcp.server.mcpserver and the class to MCPServer. Establish which requirement pulled mcp in first: when it belongs to a dependency rather than to your project, the pin has to go where that requirement is declared, or be applied as a constraint from yours. Check the version before you debug anything else in the launch path — the process dies at import, so a token, a wrapper script, or a transport setting cannot be the cause no matter how recently you touched it.
Why it happens
MCP Python SDK 2.0.0, published 2026-07-28, moved every submodule under mcp.server.fastmcp.* to mcp.server.mcpserver.* and renamed the FastMCP class to MCPServer. Its release notes state that pip install mcp now installs 2.x.
So a project that declared mcp>=1.0.0 or mcp>=1.28.1 with no upper bound resolves straight through a breaking major the first time anything re-resolves. That is why the failure looks intermittent and machine-specific when it is neither: a warm cache keeps working, and the next lock refresh, fresh clone, or container build breaks.
The 1.x line is maintenance-only — the release notes say it "will only receive security fixes from now on" and lives on the v1.x branch.
The same resolution breaks servers built on the low-level API with a different string: those raise AttributeError: 'Server' object has no attribute 'list_tools' from the decorator call instead of failing the import.
The exchange
- MCP client Claude Code, VS Code
- Server process your MCP server
- Package index PyPI
-
This is the whole cause, and it is not in your repository. The requirement was already unbounded; 2.0.0 becoming the newest release is what changed.
Server process to Package index
resolve mcp>=1.28.1 no upper bound declared -
Package index to Server process
mcp 2.0.0 published 2026-07-28 -
The process exits before the first MCP message, so the client has no protocol error to report — only that the transport closed. The traceback is on the subprocess stderr.
MCP client to Server process
spawn stdio subprocess uvx your-mcp-server -
Inside Server process
import mcp.server.fastmcp (this step fails) module removed in 2.0.0Flow stops here
A version bound got the process to start; nothing about who may call it is in the SDK.
-
The server starts. What stops an unauthenticated caller reaching it?
-
mcp-oauth-inboundThe route in front of it: the gateway acts as the OAuth 2.1 resource server and authorization server, delegating browser login to an OIDC provider, so the server process never implements a token flow.
-
Which of our tools can a caller that did authenticate actually call?
-
mcp-capability-filter-inboundThe ones the route allows and no others: anything outside its allowlist is refused with
MethodNotFoundbefore the request reaches your handler, andaccessControl.mode: rolesAndGroupsnarrows the list per caller. -
An agent is looping on one tool. Can we cap it before it costs us?
-
rate-limit-inboundYes — per caller, on the route: the looping client runs out of its own budget without the tool being taken away from everyone else.
-
After the next SDK bump, which callers stopped succeeding?
-
capability_invocationEach tool call emits its own event with the caller, the capability, and the outcome, so a regression resolves to a named tool rather than to a drop in traffic.
All of these attach to one MCP route's policies.inbound — the same policy engine on the way in and on the way out. Your MCP server keeps
the code and the authentication it has today.
Stop debugging auth on every MCP server
A gateway in front of your MCP servers handles discovery, audience binding, and token validation once — for every server and every client.