Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` | `MCPDeprecationWarning: mcp.server.fastmcp is deprecated`, or `ModuleNotFoundError` for its submodules | [`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` moved to the `mcp-types` package | `ModuleNotFoundError: No module named 'mcp.types'` | [`mcp.types` moved](#mcptypes-moved-to-the-mcp-types-package) |
| `McpError` renamed to `MCPError` | `ImportError: cannot import name 'McpError' from 'mcp'` | [`McpError` renamed](#mcperror-renamed-to-mcperror) |
Expand Down Expand Up @@ -532,7 +532,7 @@ raise MCPError.from_error_data(error_data)

### `FastMCP` renamed to `MCPServer`

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.
The `FastMCP` class has been renamed to `MCPServer` to better reflect its role as the main server class in the SDK. The rename itself is mechanical, but the class is not behaviourally identical to v1's — the rest of this section covers what changed.

**Before (v1):**

Expand All @@ -552,6 +552,8 @@ mcp = MCPServer("Demo")

`Context` is the type annotation for the `ctx` parameter injected into tools, resources, and prompts (see [`get_context()` removed](#mcpserverget_context-removed) below). The `ctx.fastmcp` property is now `ctx.mcp_server`.

The old import path is not gone entirely: `from mcp.server.fastmcp import FastMCP` (and `Context`, `Image`, `Audio`, `Icon` — the names v1 exported there) still resolves, to the v2 objects, and emits an `MCPDeprecationWarning` at import. That is a bridge, not a compatibility layer: `FastMCP` is `MCPServer` under its old name, with every v2 change on this page in effect, and the warning points back here for the ones that don't announce themselves. The path is removed in v3. Nothing beneath it is shimmed, so `mcp.server.fastmcp.server`, `mcp.server.fastmcp.prompts.base`, and the rest raise `ModuleNotFoundError`.

All submodules under `mcp.server.fastmcp.*` are now under `mcp.server.mcpserver.*` with the same structure. Common imports:

- `Image`, `Audio` — from `mcp.server.mcpserver` (or `.utilities.types`)
Expand Down Expand Up @@ -581,11 +583,13 @@ mcp = MCPServer() # serverInfo.name == "mcp-server"

If test suites assert on the initialize result, or anything keys configuration or allow-lists off `serverInfo.name`, pass a name explicitly: `MCPServer("FastMCP")` preserves the old value, though a real name for your server is better.

### `MCPServer` constructor: `title`, `description`, and `version` added to the positional parameters
### `MCPServer` constructor: parameters after `name` are now keyword-only

The constructor's positional parameter order changed. v2 inserts `title` and `description` before `instructions`, and `version` after `icons`, so the order is now `name`, `title`, `description`, `instructions`, `website_url`, `icons`, `version`. In v1 the order was `name`, `instructions`, `website_url`, `icons`.
As with the [lowlevel `Server`](#lowlevel-server-constructor-parameters-are-now-keyword-only), only `name` is positional. `title` and `description` were added to the constructor and `version` moved off the installed package (see below), which reshuffled the parameter order relative to v1's `name`, `instructions`, `website_url`, `icons`. Rather than let a v1 call that passed `instructions` positionally quietly land its text in `title`, v2 makes every parameter after `name` keyword-only, so that call raises instead:

A v1 call that passed `instructions` positionally still runs without error on v2, because both slots are `str | None`. The text silently lands in `title` instead: the server sends it as `serverInfo.title` and stops sending `instructions` in the initialize result, which clients feed to the model.
```text
TypeError: MCPServer.__init__() takes from 1 to 2 positional arguments but 3 were given
```

**Before (v1):**

Expand All @@ -604,8 +608,6 @@ from mcp.server.mcpserver import MCPServer
mcp = MCPServer("Demo", instructions="You answer questions about the weather.")
```

Keep `name` positional and pass everything else by keyword.

### Unversioned servers report an empty version

In v1, a server constructed without a `version` reported the installed `mcp`
Expand Down
6 changes: 6 additions & 0 deletions scripts/docs/gen_ref_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
# it from `src/` would emit the unimportable `mcp-types.mcp_types.*`.
PACKAGES = (ROOT / "src" / "mcp", ROOT / "src" / "mcp-types" / "mcp_types")

# Deprecated import shims that warn on import and only re-export names whose
# canonical documentation lives elsewhere. They get no API page of their own.
DEPRECATED_MODULES = frozenset({"mcp.server.fastmcp"})

_KIND_SECTIONS = {
griffe.Kind.MODULE: "Modules",
griffe.Kind.CLASS: "Classes",
Expand Down Expand Up @@ -185,6 +189,8 @@ def generate() -> list[NavItem]:
continue

ident = ".".join(parts)
if ident in DEPRECATED_MODULES:
continue
documented.add(ident)
stubs[API_DIR / doc_path] = _stub(parts[-1], f"::: {ident}")
pages[ident] = API_DIR / doc_path
Expand Down
34 changes: 34 additions & 0 deletions src/mcp/server/fastmcp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Deprecated import path: `FastMCP` was renamed to `MCPServer` in v2.

This module keeps `from mcp.server.fastmcp import FastMCP` working so v1 code
runs, but importing it emits an `MCPDeprecationWarning`. It is not a
behaviour-preserving alias: v2 changed defaults and semantics beyond the name
(the default server name, transport parameters moving off the constructor,
sync handlers running on a worker thread). Read the migration guide before
relying on it:

https://py.sdk.modelcontextprotocol.io/v2/migration/

Only this package init is shimmed. Submodules such as
`mcp.server.fastmcp.server` or `mcp.server.fastmcp.prompts.base` no longer
exist; their contents live under `mcp.server.mcpserver`. This module is removed
in v3.
"""
Comment on lines +1 to +16

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 docs/whats-new.md line 19 still says the old mcp.server.fastmcp import path "is gone rather than deprecated" — after this PR that sentence is wrong on both counts, since the path exists again and emits an MCPDeprecationWarning (only its submodules still raise ModuleNotFoundError). A one-sentence edit to whats-new.md (mirroring the updated symptom row in docs/migration.md) brings it in line.

Extended reasoning...

What's wrong: docs/whats-new.md line 19, in the "FastMCP is now MCPServer" section, reads: "This is the first thing every v1 server hits, because the old import path is gone rather than deprecated." This PR's headline change is precisely that the old import path is no longer gone and is now deprecated: the new src/mcp/server/fastmcp/__init__.py shim makes from mcp.server.fastmcp import FastMCP (plus Context, Image, Audio, Icon) resolve to the v2 objects while emitting an MCPDeprecationWarning. The sentence is therefore false on both counts once this PR merges.

Why it slipped through: the PR did update the docs for this behaviour change — docs/migration.md's quick-reference table now says MCPDeprecationWarning: mcp.server.fastmcp is deprecated, or ModuleNotFoundError for its submodules, and a new paragraph in the "FastMCP renamed" section describes the bridge. But docs/whats-new.md covers the same rename in its own words and wasn't touched, so the two pages now contradict each other about the PR's central feature. AGENTS.md asks that changes affecting user-visible behaviour update the relevant docs/ pages in the same PR, which suggests this is an oversight rather than a scoping decision.

Concrete walkthrough: a v1 user upgrading to v2 reads whats-new.md, which tells them their first symptom will be an import error because "the old import path is gone." They then run their server: from mcp.server.fastmcp import FastMCP succeeds with a warning, and FastMCP("Demo") works (it's MCPServer under the old name). The doc's claim doesn't match what they observe — and worse, a user who trusts the doc might not realize the warning-emitting shim is a temporary bridge removed in v3. Meanwhile migration.md (correctly) tells the same reader the path "is not gone entirely."

Impact: documentation-only. Nothing breaks at runtime; the shim, warning, and tests in this PR all behave as intended. The cost is reader confusion between two docs pages describing the same rename, on the exact behaviour this PR changes.

Fix: a one-sentence edit to docs/whats-new.md line 19, e.g.: "This is the first thing every v1 server hits — the top-level import now resolves with an MCPDeprecationWarning (removed in v3), while submodules like mcp.server.fastmcp.server still raise ModuleNotFoundError." That matches the framing the PR already adopted in docs/migration.md.

All three verifiers confirmed the finding; two rated it nit and one normal. Since the code is correct and only a doc sentence is stale, this doesn't warrant blocking the merge — nit severity.


import warnings

from mcp.server.mcpserver import Audio, Context, Icon, Image
from mcp.server.mcpserver import MCPServer as FastMCP
from mcp.shared.exceptions import MCPDeprecationWarning

warnings.warn(
"mcp.server.fastmcp is deprecated: FastMCP was renamed to MCPServer "
"(from mcp.server.mcpserver import MCPServer). This is not just a rename; "
"defaults and behaviour changed in v2, see the migration guide: "
"https://py.sdk.modelcontextprotocol.io/v2/migration/. "
"This import path will be removed in v3.",
MCPDeprecationWarning,
stacklevel=2,
)

__all__ = ["FastMCP", "Context", "Image", "Audio", "Icon"]
2 changes: 1 addition & 1 deletion src/mcp/server/mcpserver/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ class MCPServer(Generic[LifespanResultT]):
def __init__(
self,
name: str | None = None,
*,
title: str | None = None,
description: str | None = None,
instructions: str | None = None,
Expand All @@ -156,7 +157,6 @@ def __init__(
version: str = "",
auth_server_provider: OAuthAuthorizationServerProvider[Any, Any, Any] | None = None,
token_verifier: TokenVerifier | None = None,
*,
tools: list[Tool] | None = None,
resources: list[Resource] | None = None,
extensions: Sequence[Extension] | None = None,
Expand Down
33 changes: 33 additions & 0 deletions tests/server/test_fastmcp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""The `mcp.server.fastmcp` import path is a deprecated alias for `mcp.server.mcpserver`."""

import importlib
import sys
from types import ModuleType

import pytest

from mcp import MCPDeprecationWarning
from mcp.server.mcpserver import Audio, Context, Icon, Image, MCPServer


def _import_fastmcp_fresh() -> ModuleType:
# The warning fires on module execution, so drop any cached module first.
sys.modules.pop("mcp.server.fastmcp", None)
return importlib.import_module("mcp.server.fastmcp")


def test_importing_fastmcp_warns_and_names_the_replacement():
with pytest.warns(MCPDeprecationWarning, match="renamed to MCPServer"):
_import_fastmcp_fresh()


def test_fastmcp_reexports_the_v1_surface_as_the_v2_objects():
with pytest.warns(MCPDeprecationWarning):
fastmcp = _import_fastmcp_fresh()

assert fastmcp.__all__ == ["FastMCP", "Context", "Image", "Audio", "Icon"]
assert fastmcp.FastMCP is MCPServer
assert fastmcp.Context is Context
assert fastmcp.Image is Image
assert fastmcp.Audio is Audio
assert fastmcp.Icon is Icon
Loading