From ac6a957de08edef89d37eac142fdd07bb428b55c Mon Sep 17 00:00:00 2001 From: Max Isbey <224885523+maxisbey@users.noreply.github.com> Date: Mon, 27 Jul 2026 20:03:02 +0000 Subject: [PATCH 1/2] Restore mcp.server.fastmcp as a deprecated import alias for MCPServer `from mcp.server.fastmcp import FastMCP` (and the Context, Image, Audio, Icon names v1 exported alongside it) resolves again, to the v2 objects, and emits MCPDeprecationWarning at import. Only the package init is shimmed; the fastmcp submodule tree still raises ModuleNotFoundError. The alias is removed in v3. MCPServer's constructor now takes everything after `name` as keyword-only, matching the lowlevel Server. A v1 call that passed `instructions` positionally previously landed the text in the new `title` parameter silently; it now raises TypeError, which matters more once the old import path stops forcing users through the guide. --- docs/migration.md | 14 ++++++------ scripts/docs/gen_ref_pages.py | 6 ++++++ src/mcp/server/fastmcp/__init__.py | 34 ++++++++++++++++++++++++++++++ src/mcp/server/mcpserver/server.py | 2 +- tests/server/test_fastmcp.py | 33 +++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 src/mcp/server/fastmcp/__init__.py create mode 100644 tests/server/test_fastmcp.py diff --git a/docs/migration.md b/docs/migration.md index 5eb7659c23..435eec1389 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -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) | @@ -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 names 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`) @@ -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):** @@ -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` diff --git a/scripts/docs/gen_ref_pages.py b/scripts/docs/gen_ref_pages.py index 2340e18466..340923dd1c 100644 --- a/scripts/docs/gen_ref_pages.py +++ b/scripts/docs/gen_ref_pages.py @@ -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", @@ -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 diff --git a/src/mcp/server/fastmcp/__init__.py b/src/mcp/server/fastmcp/__init__.py new file mode 100644 index 0000000000..1f41e5d521 --- /dev/null +++ b/src/mcp/server/fastmcp/__init__.py @@ -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. +""" + +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"] diff --git a/src/mcp/server/mcpserver/server.py b/src/mcp/server/mcpserver/server.py index 33a0b08c84..1b367d4719 100644 --- a/src/mcp/server/mcpserver/server.py +++ b/src/mcp/server/mcpserver/server.py @@ -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, @@ -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, diff --git a/tests/server/test_fastmcp.py b/tests/server/test_fastmcp.py new file mode 100644 index 0000000000..b1d8d664e5 --- /dev/null +++ b/tests/server/test_fastmcp.py @@ -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 From 5f87d3974959b66bd601d5755db168518d9fe7db Mon Sep 17 00:00:00 2001 From: Max Isbey <224885523+maxisbey@users.noreply.github.com> Date: Mon, 27 Jul 2026 20:12:28 +0000 Subject: [PATCH 2/2] Fix contradictions in the FastMCP rename migration prose The rename section still called it a simple rename with no functional changes, contradicting the bridge paragraph and the runtime warning, and it claimed the warning enumerates the silent behaviour changes when it points at the guide instead. No-Verification-Needed: doc-only change --- docs/migration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/migration.md b/docs/migration.md index 435eec1389..e906e93034 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -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):** @@ -552,7 +552,7 @@ 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 names 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`. +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: