From 49b0ca4c12f90e3e04c72011b366636e58bbae34 Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Sat, 24 Jan 2026 14:55:02 +0100 Subject: [PATCH 1/3] Rename FastMCP to MCPServer --- .github/ISSUE_TEMPLATE/bug.yaml | 2 +- README.md | 184 +++++++++--------- docs/index.md | 4 +- docs/installation.md | 2 +- docs/migration.md | 28 +-- docs/testing.md | 4 +- .../{fastmcp => mcpserver}/complex_inputs.py | 6 +- examples/{fastmcp => mcpserver}/desktop.py | 6 +- .../direct_call_tool_result_return.py | 6 +- examples/{fastmcp => mcpserver}/echo.py | 6 +- examples/{fastmcp => mcpserver}/icons_demo.py | 8 +- .../logging_and_progress.py | 6 +- examples/{fastmcp => mcpserver}/mcp.png | Bin examples/{fastmcp => mcpserver}/memory.py | 8 +- .../parameter_descriptions.py | 6 +- .../readme-quickstart.py | 4 +- examples/{fastmcp => mcpserver}/screenshot.py | 8 +- .../{fastmcp => mcpserver}/simple_echo.py | 6 +- examples/{fastmcp => mcpserver}/text_me.py | 8 +- .../{fastmcp => mcpserver}/unicode_example.py | 6 +- .../weather_structured.py | 6 +- .../mcp_everything_server/server.py | 16 +- .../mcp_simple_auth/legacy_as_server.py | 8 +- .../simple-auth/mcp_simple_auth/server.py | 8 +- .../snippets/clients/display_utilities.py | 2 +- examples/snippets/clients/stdio_client.py | 8 +- examples/snippets/servers/__init__.py | 2 +- examples/snippets/servers/basic_prompt.py | 6 +- examples/snippets/servers/basic_resource.py | 4 +- examples/snippets/servers/basic_tool.py | 4 +- examples/snippets/servers/completion.py | 4 +- .../servers/direct_call_tool_result.py | 4 +- examples/snippets/servers/direct_execution.py | 4 +- examples/snippets/servers/elicitation.py | 4 +- examples/snippets/servers/images.py | 6 +- examples/snippets/servers/lifespan_example.py | 6 +- ..._quickstart.py => mcpserver_quickstart.py} | 8 +- examples/snippets/servers/notifications.py | 4 +- examples/snippets/servers/oauth_server.py | 6 +- examples/snippets/servers/sampling.py | 4 +- .../snippets/servers/streamable_config.py | 4 +- .../servers/streamable_http_basic_mounting.py | 4 +- .../servers/streamable_http_host_mounting.py | 4 +- .../streamable_http_multiple_servers.py | 6 +- .../servers/streamable_http_path_config.py | 8 +- .../servers/streamable_starlette_mount.py | 6 +- .../snippets/servers/structured_output.py | 4 +- examples/snippets/servers/tool_progress.py | 4 +- pyproject.toml | 6 +- src/mcp/cli/__init__.py | 2 +- src/mcp/cli/claude.py | 6 +- src/mcp/cli/cli.py | 18 +- src/mcp/client/_memory.py | 14 +- src/mcp/client/client.py | 14 +- src/mcp/client/session_group.py | 8 +- src/mcp/server/__init__.py | 4 +- src/mcp/server/auth/provider.py | 2 +- src/mcp/server/fastmcp/__init__.py | 8 - src/mcp/server/fastmcp/exceptions.py | 21 -- src/mcp/server/fastmcp/utilities/__init__.py | 1 - src/mcp/server/mcpserver/__init__.py | 8 + src/mcp/server/mcpserver/exceptions.py | 21 ++ .../prompts/__init__.py | 0 .../{fastmcp => mcpserver}/prompts/base.py | 8 +- .../{fastmcp => mcpserver}/prompts/manager.py | 8 +- .../resources/__init__.py | 0 .../{fastmcp => mcpserver}/resources/base.py | 2 +- .../resources/resource_manager.py | 10 +- .../resources/templates.py | 8 +- .../{fastmcp => mcpserver}/resources/types.py | 2 +- .../server/{fastmcp => mcpserver}/server.py | 127 ++++++------ .../{fastmcp => mcpserver}/tools/__init__.py | 0 .../{fastmcp => mcpserver}/tools/base.py | 8 +- .../tools/tool_manager.py | 10 +- .../server/mcpserver/utilities/__init__.py | 1 + .../utilities/context_injection.py | 4 +- .../utilities/func_metadata.py | 14 +- .../utilities/logging.py | 6 +- .../{fastmcp => mcpserver}/utilities/types.py | 2 +- src/mcp/shared/context.py | 2 +- tests/client/test_client.py | 30 +-- tests/client/test_list_methods_cursor.py | 8 +- tests/client/test_list_roots_callback.py | 6 +- tests/client/test_logging_callback.py | 4 +- tests/client/test_sampling_callback.py | 6 +- tests/client/transports/test_memory.py | 30 +-- tests/issues/test_100_tool_listing.py | 4 +- .../test_1027_win_unreachable_cleanup.py | 10 +- tests/issues/test_129_resource_templates.py | 6 +- tests/issues/test_1338_icons_and_metadata.py | 8 +- tests/issues/test_141_resource_templates.py | 6 +- tests/issues/test_152_resource_mime_type.py | 6 +- .../issues/test_1754_mime_type_parameters.py | 10 +- tests/issues/test_176_progress_token.py | 4 +- tests/issues/test_188_concurrency.py | 6 +- tests/issues/test_355_type_error.py | 8 +- tests/issues/test_973_url_decoding.py | 2 +- tests/server/auth/test_error_handling.py | 2 +- .../server/{fastmcp => mcpserver}/__init__.py | 0 .../{fastmcp => mcpserver}/auth/__init__.py | 0 .../auth/test_auth_integration.py | 0 .../prompts/__init__.py | 0 .../prompts/test_base.py | 2 +- .../prompts/test_manager.py | 4 +- .../resources/__init__.py | 0 .../resources/test_file_resources.py | 2 +- .../resources/test_function_resources.py | 2 +- .../resources/test_resource_manager.py | 2 +- .../resources/test_resource_template.py | 10 +- .../resources/test_resources.py | 12 +- .../servers/__init__.py | 0 .../servers/test_file_server.py | 20 +- .../test_elicitation.py | 18 +- .../test_func_metadata.py | 4 +- .../test_integration.py | 20 +- .../test_parameter_descriptions.py | 4 +- .../{fastmcp => mcpserver}/test_server.py | 184 +++++++++--------- .../{fastmcp => mcpserver}/test_title.py | 12 +- .../test_tool_manager.py | 30 +-- .../test_url_elicitation.py | 22 +-- .../test_url_elicitation_error_throw.py | 8 +- tests/server/test_lifespan.py | 16 +- tests/test_examples.py | 55 +++--- 123 files changed, 693 insertions(+), 697 deletions(-) rename examples/{fastmcp => mcpserver}/complex_inputs.py (84%) rename examples/{fastmcp => mcpserver}/desktop.py (80%) rename examples/{fastmcp => mcpserver}/direct_call_tool_result_return.py (76%) rename examples/{fastmcp => mcpserver}/echo.py (79%) rename examples/{fastmcp => mcpserver}/icons_demo.py (86%) rename examples/{fastmcp => mcpserver}/logging_and_progress.py (80%) rename examples/{fastmcp => mcpserver}/mcp.png (100%) rename examples/{fastmcp => mcpserver}/memory.py (98%) rename examples/{fastmcp => mcpserver}/parameter_descriptions.py (76%) rename examples/{fastmcp => mcpserver}/readme-quickstart.py (82%) rename examples/{fastmcp => mcpserver}/screenshot.py (78%) rename examples/{fastmcp => mcpserver}/simple_echo.py (50%) rename examples/{fastmcp => mcpserver}/text_me.py (89%) rename examples/{fastmcp => mcpserver}/unicode_example.py (91%) rename examples/{fastmcp => mcpserver}/weather_structured.py (98%) rename examples/snippets/servers/{fastmcp_quickstart.py => mcpserver_quickstart.py} (84%) delete mode 100644 src/mcp/server/fastmcp/__init__.py delete mode 100644 src/mcp/server/fastmcp/exceptions.py delete mode 100644 src/mcp/server/fastmcp/utilities/__init__.py create mode 100644 src/mcp/server/mcpserver/__init__.py create mode 100644 src/mcp/server/mcpserver/exceptions.py rename src/mcp/server/{fastmcp => mcpserver}/prompts/__init__.py (100%) rename src/mcp/server/{fastmcp => mcpserver}/prompts/base.py (96%) rename src/mcp/server/{fastmcp => mcpserver}/prompts/manager.py (88%) rename src/mcp/server/{fastmcp => mcpserver}/resources/__init__.py (100%) rename src/mcp/server/{fastmcp => mcpserver}/resources/base.py (96%) rename src/mcp/server/{fastmcp => mcpserver}/resources/resource_manager.py (93%) rename src/mcp/server/{fastmcp => mcpserver}/resources/templates.py (94%) rename src/mcp/server/{fastmcp => mcpserver}/resources/types.py (99%) rename src/mcp/server/{fastmcp => mcpserver}/server.py (92%) rename src/mcp/server/{fastmcp => mcpserver}/tools/__init__.py (100%) rename src/mcp/server/{fastmcp => mcpserver}/tools/base.py (94%) rename src/mcp/server/{fastmcp => mcpserver}/tools/tool_manager.py (91%) create mode 100644 src/mcp/server/mcpserver/utilities/__init__.py rename src/mcp/server/{fastmcp => mcpserver}/utilities/context_injection.py (95%) rename src/mcp/server/{fastmcp => mcpserver}/utilities/func_metadata.py (97%) rename src/mcp/server/{fastmcp => mcpserver}/utilities/logging.py (84%) rename src/mcp/server/{fastmcp => mcpserver}/utilities/types.py (98%) rename tests/server/{fastmcp => mcpserver}/__init__.py (100%) rename tests/server/{fastmcp => mcpserver}/auth/__init__.py (100%) rename tests/server/{fastmcp => mcpserver}/auth/test_auth_integration.py (100%) rename tests/server/{fastmcp => mcpserver}/prompts/__init__.py (100%) rename tests/server/{fastmcp => mcpserver}/prompts/test_base.py (98%) rename tests/server/{fastmcp => mcpserver}/prompts/test_manager.py (96%) rename tests/server/{fastmcp => mcpserver}/resources/__init__.py (100%) rename tests/server/{fastmcp => mcpserver}/resources/test_file_resources.py (98%) rename tests/server/{fastmcp => mcpserver}/resources/test_function_resources.py (98%) rename tests/server/{fastmcp => mcpserver}/resources/test_resource_manager.py (98%) rename tests/server/{fastmcp => mcpserver}/resources/test_resource_template.py (97%) rename tests/server/{fastmcp => mcpserver}/resources/test_resources.py (96%) rename tests/server/{fastmcp => mcpserver}/servers/__init__.py (100%) rename tests/server/{fastmcp => mcpserver}/servers/test_file_server.py (86%) rename tests/server/{fastmcp => mcpserver}/test_elicitation.py (97%) rename tests/server/{fastmcp => mcpserver}/test_func_metadata.py (99%) rename tests/server/{fastmcp => mcpserver}/test_integration.py (98%) rename tests/server/{fastmcp => mcpserver}/test_parameter_descriptions.py (91%) rename tests/server/{fastmcp => mcpserver}/test_server.py (95%) rename tests/server/{fastmcp => mcpserver}/test_title.py (97%) rename tests/server/{fastmcp => mcpserver}/test_tool_manager.py (97%) rename tests/server/{fastmcp => mcpserver}/test_url_elicitation.py (96%) rename tests/server/{fastmcp => mcpserver}/test_url_elicitation_error_throw.py (95%) diff --git a/.github/ISSUE_TEMPLATE/bug.yaml b/.github/ISSUE_TEMPLATE/bug.yaml index e52277a2a7..617ff9872e 100644 --- a/.github/ISSUE_TEMPLATE/bug.yaml +++ b/.github/ISSUE_TEMPLATE/bug.yaml @@ -39,7 +39,7 @@ body: demonstrating the bug. placeholder: | - from mcp.server.fastmcp import FastMCP + from mcp.server.mcpserver import MCPServer ... render: Python diff --git a/README.md b/README.md index b6f8087ab8..0f0468a192 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ - [Sampling](#sampling) - [Logging and Notifications](#logging-and-notifications) - [Authentication](#authentication) - - [FastMCP Properties](#fastmcp-properties) + - [MCPServer Properties](#mcpserver-properties) - [Session Properties and Methods](#session-properties-and-methods) - [Request Context Properties](#request-context-properties) - [Running Your Server](#running-your-server) @@ -134,18 +134,18 @@ uv run mcp Let's create a simple MCP server that exposes a calculator tool and some data: - + ```python -"""FastMCP quickstart example. +"""MCPServer quickstart example. Run from the repository root: - uv run examples/snippets/servers/fastmcp_quickstart.py + uv run examples/snippets/servers/mcpserver_quickstart.py """ -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Create an MCP server -mcp = FastMCP("Demo") +mcp = MCPServer("Demo") # Add an addition tool @@ -180,13 +180,13 @@ if __name__ == "__main__": mcp.run(transport="streamable-http", json_response=True) ``` -_Full example: [examples/snippets/servers/fastmcp_quickstart.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/fastmcp_quickstart.py)_ +_Full example: [examples/snippets/servers/mcpserver_quickstart.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/mcpserver_quickstart.py)_ You can install this server in [Claude Code](https://docs.claude.com/en/docs/claude-code/mcp) and interact with it right away. First, run the server: ```bash -uv run --with mcp examples/snippets/servers/fastmcp_quickstart.py +uv run --with mcp examples/snippets/servers/mcpserver_quickstart.py ``` Then add it to Claude Code: @@ -216,7 +216,7 @@ The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) lets you bui ### Server -The FastMCP server is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing: +The MCPServer server is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing: ```python @@ -226,7 +226,7 @@ from collections.abc import AsyncIterator from contextlib import asynccontextmanager from dataclasses import dataclass -from mcp.server.fastmcp import Context, FastMCP +from mcp.server.mcpserver import Context, MCPServer from mcp.server.session import ServerSession @@ -256,7 +256,7 @@ class AppContext: @asynccontextmanager -async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]: +async def app_lifespan(server: MCPServer) -> AsyncIterator[AppContext]: """Manage application lifecycle with type-safe context.""" # Initialize on startup db = await Database.connect() @@ -268,7 +268,7 @@ async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]: # Pass lifespan to server -mcp = FastMCP("My App", lifespan=app_lifespan) +mcp = MCPServer("My App", lifespan=app_lifespan) # Access type-safe lifespan context in tools @@ -288,9 +288,9 @@ Resources are how you expose data to LLMs. They're similar to GET endpoints in a ```python -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer -mcp = FastMCP(name="Resource Example") +mcp = MCPServer(name="Resource Example") @mcp.resource("file://documents/{name}") @@ -319,9 +319,9 @@ Tools let LLMs take actions through your server. Unlike resources, tools are exp ```python -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer -mcp = FastMCP(name="Tool Example") +mcp = MCPServer(name="Tool Example") @mcp.tool() @@ -340,14 +340,14 @@ def get_weather(city: str, unit: str = "celsius") -> str: _Full example: [examples/snippets/servers/basic_tool.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/basic_tool.py)_ -Tools can optionally receive a Context object by including a parameter with the `Context` type annotation. This context is automatically injected by the FastMCP framework and provides access to MCP capabilities: +Tools can optionally receive a Context object by including a parameter with the `Context` type annotation. This context is automatically injected by the MCPServer framework and provides access to MCP capabilities: ```python -from mcp.server.fastmcp import Context, FastMCP +from mcp.server.mcpserver import Context, MCPServer from mcp.server.session import ServerSession -mcp = FastMCP(name="Progress Example") +mcp = MCPServer(name="Progress Example") @mcp.tool() @@ -395,7 +395,7 @@ validated data that clients can easily process. **Note:** For backward compatibility, unstructured results are also returned. Unstructured results are provided for backward compatibility with previous versions of the MCP specification, and are quirks-compatible -with previous versions of FastMCP in the current version of the SDK. +with previous versions of MCPServer in the current version of the SDK. **Note:** In cases where a tool function's return type annotation causes the tool to be classified as structured _and this is undesirable_, @@ -414,10 +414,10 @@ from typing import Annotated from pydantic import BaseModel -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer from mcp.types import CallToolResult, TextContent -mcp = FastMCP("CallToolResult Example") +mcp = MCPServer("CallToolResult Example") class ValidationModel(BaseModel): @@ -465,9 +465,9 @@ from typing import TypedDict from pydantic import BaseModel, Field -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer -mcp = FastMCP("Structured Output Example") +mcp = MCPServer("Structured Output Example") # Using Pydantic models for rich structured data @@ -567,10 +567,10 @@ Prompts are reusable templates that help LLMs interact with your server effectiv ```python -from mcp.server.fastmcp import FastMCP -from mcp.server.fastmcp.prompts import base +from mcp.server.mcpserver import MCPServer +from mcp.server.mcpserver.prompts import base -mcp = FastMCP(name="Prompt Example") +mcp = MCPServer(name="Prompt Example") @mcp.prompt(title="Code Review") @@ -595,7 +595,7 @@ _Full example: [examples/snippets/servers/basic_prompt.py](https://github.com/mo MCP servers can provide icons for UI display. Icons can be added to the server implementation, tools, resources, and prompts: ```python -from mcp.server.fastmcp import FastMCP, Icon +from mcp.server.mcpserver import MCPServer, Icon # Create an icon from a file path or URL icon = Icon( @@ -605,7 +605,7 @@ icon = Icon( ) # Add icons to server -mcp = FastMCP( +mcp = MCPServer( "My Server", website_url="https://example.com", icons=[icon] @@ -623,21 +623,21 @@ def my_resource(): return "content" ``` -_Full example: [examples/fastmcp/icons_demo.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/fastmcp/icons_demo.py)_ +_Full example: [examples/mcpserver/icons_demo.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/mcpserver/icons_demo.py)_ ### Images -FastMCP provides an `Image` class that automatically handles image data: +MCPServer provides an `Image` class that automatically handles image data: ```python -"""Example showing image handling with FastMCP.""" +"""Example showing image handling with MCPServer.""" from PIL import Image as PILImage -from mcp.server.fastmcp import FastMCP, Image +from mcp.server.mcpserver import Image, MCPServer -mcp = FastMCP("Image Example") +mcp = MCPServer("Image Example") @mcp.tool() @@ -660,9 +660,9 @@ The Context object is automatically injected into tool and resource functions th To use context in a tool or resource function, add a parameter with the `Context` type annotation: ```python -from mcp.server.fastmcp import Context, FastMCP +from mcp.server.mcpserver import Context, MCPServer -mcp = FastMCP(name="Context Example") +mcp = MCPServer(name="Context Example") @mcp.tool() @@ -678,7 +678,7 @@ The Context object provides the following capabilities: - `ctx.request_id` - Unique ID for the current request - `ctx.client_id` - Client ID if available -- `ctx.fastmcp` - Access to the FastMCP server instance (see [FastMCP Properties](#fastmcp-properties)) +- `ctx.mcp_server` - Access to the MCPServer server instance (see [MCPServer Properties](#mcpserver-properties)) - `ctx.session` - Access to the underlying session for advanced communication (see [Session Properties and Methods](#session-properties-and-methods)) - `ctx.request_context` - Access to request-specific data and lifespan resources (see [Request Context Properties](#request-context-properties)) - `await ctx.debug(message)` - Send debug log message @@ -692,10 +692,10 @@ The Context object provides the following capabilities: ```python -from mcp.server.fastmcp import Context, FastMCP +from mcp.server.mcpserver import Context, MCPServer from mcp.server.session import ServerSession -mcp = FastMCP(name="Progress Example") +mcp = MCPServer(name="Progress Example") @mcp.tool() @@ -824,12 +824,12 @@ import uuid from pydantic import BaseModel, Field -from mcp.server.fastmcp import Context, FastMCP +from mcp.server.mcpserver import Context, MCPServer from mcp.server.session import ServerSession from mcp.shared.exceptions import UrlElicitationRequiredError from mcp.types import ElicitRequestURLParams -mcp = FastMCP(name="Elicitation Example") +mcp = MCPServer(name="Elicitation Example") class BookingPreferences(BaseModel): @@ -931,11 +931,11 @@ Tools can interact with LLMs through sampling (generating text): ```python -from mcp.server.fastmcp import Context, FastMCP +from mcp.server.mcpserver import Context, MCPServer from mcp.server.session import ServerSession from mcp.types import SamplingMessage, TextContent -mcp = FastMCP(name="Sampling Example") +mcp = MCPServer(name="Sampling Example") @mcp.tool() @@ -968,10 +968,10 @@ Tools can send logs and notifications through the context: ```python -from mcp.server.fastmcp import Context, FastMCP +from mcp.server.mcpserver import Context, MCPServer from mcp.server.session import ServerSession -mcp = FastMCP(name="Notifications Example") +mcp = MCPServer(name="Notifications Example") @mcp.tool() @@ -1010,7 +1010,7 @@ from pydantic import AnyHttpUrl from mcp.server.auth.provider import AccessToken, TokenVerifier from mcp.server.auth.settings import AuthSettings -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer class SimpleTokenVerifier(TokenVerifier): @@ -1020,8 +1020,8 @@ class SimpleTokenVerifier(TokenVerifier): pass # This is where you would implement actual token validation -# Create FastMCP instance as a Resource Server -mcp = FastMCP( +# Create MCPServer instance as a Resource Server +mcp = MCPServer( "Weather Service", # Token verifier for authentication token_verifier=SimpleTokenVerifier(), @@ -1062,15 +1062,15 @@ For a complete example with separate Authorization Server and Resource Server im See [TokenVerifier](src/mcp/server/auth/provider.py) for more details on implementing token validation. -### FastMCP Properties +### MCPServer Properties -The FastMCP server instance accessible via `ctx.fastmcp` provides access to server configuration and metadata: +The MCPServer server instance accessible via `ctx.mcp_server` provides access to server configuration and metadata: -- `ctx.fastmcp.name` - The server's name as defined during initialization -- `ctx.fastmcp.instructions` - Server instructions/description provided to clients -- `ctx.fastmcp.website_url` - Optional website URL for the server -- `ctx.fastmcp.icons` - Optional list of icons for UI display -- `ctx.fastmcp.settings` - Complete server configuration object containing: +- `ctx.mcp_server.name` - The server's name as defined during initialization +- `ctx.mcp_server.instructions` - Server instructions/description provided to clients +- `ctx.mcp_server.website_url` - Optional website URL for the server +- `ctx.mcp_server.icons` - Optional list of icons for UI display +- `ctx.mcp_server.settings` - Complete server configuration object containing: - `debug` - Debug mode flag - `log_level` - Current logging level - `host` and `port` - Server network configuration @@ -1083,12 +1083,12 @@ The FastMCP server instance accessible via `ctx.fastmcp` provides access to serv def server_info(ctx: Context) -> dict: """Get information about the current server.""" return { - "name": ctx.fastmcp.name, - "instructions": ctx.fastmcp.instructions, - "debug_mode": ctx.fastmcp.settings.debug, - "log_level": ctx.fastmcp.settings.log_level, - "host": ctx.fastmcp.settings.host, - "port": ctx.fastmcp.settings.port, + "name": ctx.mcp_server.name, + "instructions": ctx.mcp_server.instructions, + "debug_mode": ctx.mcp_server.settings.debug, + "log_level": ctx.mcp_server.settings.log_level, + "host": ctx.mcp_server.settings.host, + "port": ctx.mcp_server.settings.port, } ``` @@ -1203,9 +1203,9 @@ cd to the `examples/snippets` directory and run: python servers/direct_execution.py """ -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer -mcp = FastMCP("My App") +mcp = MCPServer("My App") @mcp.tool() @@ -1234,7 +1234,7 @@ python servers/direct_execution.py uv run mcp run servers/direct_execution.py ``` -Note that `uv run mcp run` or `uv run mcp dev` only supports server using FastMCP and not the low-level server variant. +Note that `uv run mcp run` or `uv run mcp dev` only supports server using MCPServer and not the low-level server variant. ### Streamable HTTP Transport @@ -1246,9 +1246,9 @@ Note that `uv run mcp run` or `uv run mcp dev` only supports server using FastMC uv run examples/snippets/servers/streamable_config.py """ -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer -mcp = FastMCP("StatelessServer") +mcp = MCPServer("StatelessServer") # Add a simple tool to demonstrate the server @@ -1275,7 +1275,7 @@ if __name__ == "__main__": _Full example: [examples/snippets/servers/streamable_config.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_config.py)_ -You can mount multiple FastMCP servers in a Starlette application: +You can mount multiple MCPServer servers in a Starlette application: ```python @@ -1288,10 +1288,10 @@ import contextlib from starlette.applications import Starlette from starlette.routing import Mount -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Create the Echo server -echo_mcp = FastMCP(name="EchoServer") +echo_mcp = MCPServer(name="EchoServer") @echo_mcp.tool() @@ -1301,7 +1301,7 @@ def echo(message: str) -> str: # Create the Math server -math_mcp = FastMCP(name="MathServer") +math_mcp = MCPServer(name="MathServer") @math_mcp.tool() @@ -1400,10 +1400,10 @@ import contextlib from starlette.applications import Starlette from starlette.routing import Mount -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Create MCP server -mcp = FastMCP("My App") +mcp = MCPServer("My App") @mcp.tool() @@ -1447,10 +1447,10 @@ import contextlib from starlette.applications import Starlette from starlette.routing import Host -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Create MCP server -mcp = FastMCP("MCP Host App") +mcp = MCPServer("MCP Host App") @mcp.tool() @@ -1494,11 +1494,11 @@ import contextlib from starlette.applications import Starlette from starlette.routing import Mount -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Create multiple MCP servers -api_mcp = FastMCP("API Server") -chat_mcp = FastMCP("Chat Server") +api_mcp = MCPServer("API Server") +chat_mcp = MCPServer("Chat Server") @api_mcp.tool() @@ -1540,7 +1540,7 @@ _Full example: [examples/snippets/servers/streamable_http_multiple_servers.py](h ```python -"""Example showing path configuration when mounting FastMCP. +"""Example showing path configuration when mounting MCPServer. Run from the repository root: uvicorn examples.snippets.servers.streamable_http_path_config:app --reload @@ -1549,10 +1549,10 @@ Run from the repository root: from starlette.applications import Starlette from starlette.routing import Mount -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer -# Create a simple FastMCP server -mcp_at_root = FastMCP("My Server") +# Create a simple MCPServer server +mcp_at_root = MCPServer("My Server") @mcp_at_root.tool() @@ -1585,10 +1585,10 @@ You can mount the SSE server to an existing ASGI server using the `sse_app` meth ```python from starlette.applications import Starlette from starlette.routing import Mount, Host -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer -mcp = FastMCP("My App") +mcp = MCPServer("My App") # Mount the SSE server to the existing ASGI server app = Starlette( @@ -1606,12 +1606,12 @@ You can also mount multiple MCP servers at different sub-paths. The SSE transpor ```python from starlette.applications import Starlette from starlette.routing import Mount -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Create multiple MCP servers -github_mcp = FastMCP("GitHub API") -browser_mcp = FastMCP("Browser") -search_mcp = FastMCP("Search") +github_mcp = MCPServer("GitHub API") +browser_mcp = MCPServer("Browser") +search_mcp = MCPServer("Search") # Mount each server at its own sub-path # The SSE transport automatically uses ASGI's root_path to construct @@ -2126,7 +2126,7 @@ from mcp.shared.context import RequestContext # Create server parameters for stdio connection server_params = StdioServerParameters( command="uv", # Using uv to run the server - args=["run", "server", "fastmcp_quickstart", "stdio"], # We're already in snippets dir + args=["run", "server", "mcpserver_quickstart", "stdio"], # We're already in snippets dir env={"UV_INDEX": os.environ.get("UV_INDEX", "")}, ) @@ -2157,7 +2157,7 @@ async def run(): prompts = await session.list_prompts() print(f"Available prompts: {[p.name for p in prompts.prompts]}") - # Get a prompt (greet_user prompt from fastmcp_quickstart) + # Get a prompt (greet_user prompt from mcpserver_quickstart) if prompts.prompts: prompt = await session.get_prompt("greet_user", arguments={"name": "Alice", "style": "friendly"}) print(f"Prompt result: {prompt.messages[0].content}") @@ -2170,13 +2170,13 @@ async def run(): tools = await session.list_tools() print(f"Available tools: {[t.name for t in tools.tools]}") - # Read a resource (greeting resource from fastmcp_quickstart) + # Read a resource (greeting resource from mcpserver_quickstart) resource_content = await session.read_resource("greeting://World") content_block = resource_content.contents[0] if isinstance(content_block, types.TextContent): print(f"Resource content: {content_block.text}") - # Call a tool (add tool from fastmcp_quickstart) + # Call a tool (add tool from mcpserver_quickstart) result = await session.call_tool("add", arguments={"a": 5, "b": 3}) result_unstructured = result.content[0] if isinstance(result_unstructured, types.TextContent): @@ -2254,7 +2254,7 @@ from mcp.shared.metadata_utils import get_display_name # Create server parameters for stdio connection server_params = StdioServerParameters( command="uv", # Using uv to run the server - args=["run", "server", "fastmcp_quickstart", "stdio"], + args=["run", "server", "mcpserver_quickstart", "stdio"], env={"UV_INDEX": os.environ.get("UV_INDEX", "")}, ) diff --git a/docs/index.md b/docs/index.md index eb5ddf4000..e096d910b4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -15,9 +15,9 @@ If you want to read more about the specification, please visit the [MCP document Here's a simple MCP server that exposes a tool, resource, and prompt: ```python title="server.py" -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer -mcp = FastMCP("Test Server", json_response=True) +mcp = MCPServer("Test Server", json_response=True) @mcp.tool() diff --git a/docs/installation.md b/docs/installation.md index 6e20706a84..f398462353 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -21,7 +21,7 @@ The following dependencies are automatically installed: - [`starlette`](https://pypi.org/project/starlette/): Web framework used to build the HTTP transport endpoints. - [`python-multipart`](https://pypi.org/project/python-multipart/): Handle HTTP body parsing. - [`sse-starlette`](https://pypi.org/project/sse-starlette/): Server-Sent Events for Starlette, used to build the SSE transport endpoint. -- [`pydantic-settings`](https://pypi.org/project/pydantic-settings/): Settings management used in FastMCP. +- [`pydantic-settings`](https://pypi.org/project/pydantic-settings/): Settings management used in MCPServer. - [`uvicorn`](https://pypi.org/project/uvicorn/): ASGI server used to run the HTTP transport endpoints. - [`jsonschema`](https://pypi.org/project/jsonschema/): JSON schema validation. - [`pywin32`](https://pypi.org/project/pywin32/): Windows specific dependencies for the CLI tools. diff --git a/docs/migration.md b/docs/migration.md index 3f92fe0bf1..9a34ef732e 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -116,15 +116,15 @@ result = await session.list_resources(params=PaginatedRequestParams(cursor="next result = await session.list_tools(params=PaginatedRequestParams(cursor="next_page_token")) ``` -### `mount_path` parameter removed from FastMCP +### `mount_path` parameter removed from MCPServer -The `mount_path` parameter has been removed from `FastMCP.__init__()`, `FastMCP.run()`, `FastMCP.run_sse_async()`, and `FastMCP.sse_app()`. It was also removed from the `Settings` class. +The `mount_path` parameter has been removed from `MCPServer.__init__()`, `MCPServer.run()`, `MCPServer.run_sse_async()`, and `MCPServer.sse_app()`. It was also removed from the `Settings` class. This parameter was redundant because the SSE transport already handles sub-path mounting via ASGI's standard `root_path` mechanism. When using Starlette's `Mount("/path", app=mcp.sse_app())`, Starlette automatically sets `root_path` in the ASGI scope, and the `SseServerTransport` uses this to construct the correct message endpoint path. -### Transport-specific parameters moved from FastMCP constructor to run()/app methods +### Transport-specific parameters moved from MCPServer constructor to run()/app methods -Transport-specific parameters have been moved from the `FastMCP` constructor to the `run()`, `sse_app()`, and `streamable_http_app()` methods. This provides better separation of concerns - the constructor now only handles server identity and authentication, while transport configuration is passed when starting the server. +Transport-specific parameters have been moved from the `MCPServer` constructor to the `run()`, `sse_app()`, and `streamable_http_app()` methods. This provides better separation of concerns - the constructor now only handles server identity and authentication, while transport configuration is passed when starting the server. **Parameters moved:** @@ -138,42 +138,42 @@ Transport-specific parameters have been moved from the `FastMCP` constructor to **Before (v1):** ```python -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Transport params in constructor -mcp = FastMCP("Demo", json_response=True, stateless_http=True) +mcp = MCPServer("Demo", json_response=True, stateless_http=True) mcp.run(transport="streamable-http") # Or for SSE -mcp = FastMCP("Server", host="0.0.0.0", port=9000, sse_path="/events") +mcp = MCPServer("Server", host="0.0.0.0", port=9000, sse_path="/events") mcp.run(transport="sse") ``` **After (v2):** ```python -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Transport params passed to run() -mcp = FastMCP("Demo") +mcp = MCPServer("Demo") mcp.run(transport="streamable-http", json_response=True, stateless_http=True) # Or for SSE -mcp = FastMCP("Server") +mcp = MCPServer("Server") mcp.run(transport="sse", host="0.0.0.0", port=9000, sse_path="/events") ``` **For mounted apps:** -When mounting FastMCP in a Starlette app, pass transport params to the app methods: +When mounting MCPServer in a Starlette app, pass transport params to the app methods: ```python # Before (v1) -mcp = FastMCP("App", json_response=True) +mcp = MCPServer("App", json_response=True) app = Starlette(routes=[Mount("/", app=mcp.streamable_http_app())]) # After (v2) -mcp = FastMCP("App") +mcp = MCPServer("App") app = Starlette(routes=[Mount("/", app=mcp.streamable_http_app(json_response=True))]) ``` @@ -349,7 +349,7 @@ params = CallToolRequestParams( ### `streamable_http_app()` available on lowlevel Server -The `streamable_http_app()` method is now available directly on the lowlevel `Server` class, not just `FastMCP`. This allows using the streamable HTTP transport without the FastMCP wrapper. +The `streamable_http_app()` method is now available directly on the lowlevel `Server` class, not just `MCPServer`. This allows using the streamable HTTP transport without the MCPServer wrapper. ```python from mcp.server.lowlevel.server import Server diff --git a/docs/testing.md b/docs/testing.md index f869873608..9a222c9067 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -8,9 +8,9 @@ This makes it easy to write tests without network overhead. Let's assume you have a simple server with a single tool: ```python title="server.py" -from mcp.server import FastMCP +from mcp.server import MCPServer -app = FastMCP("Calculator") +app = MCPServer("Calculator") @app.tool() def add(a: int, b: int) -> int: diff --git a/examples/fastmcp/complex_inputs.py b/examples/mcpserver/complex_inputs.py similarity index 84% rename from examples/fastmcp/complex_inputs.py rename to examples/mcpserver/complex_inputs.py index b55d4f725b..93a42d1c89 100644 --- a/examples/fastmcp/complex_inputs.py +++ b/examples/mcpserver/complex_inputs.py @@ -1,4 +1,4 @@ -"""FastMCP Complex inputs Example +"""MCPServer Complex inputs Example Demonstrates validation via pydantic with complex models. """ @@ -7,9 +7,9 @@ from pydantic import BaseModel, Field -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer -mcp = FastMCP("Shrimp Tank") +mcp = MCPServer("Shrimp Tank") class ShrimpTank(BaseModel): diff --git a/examples/fastmcp/desktop.py b/examples/mcpserver/desktop.py similarity index 80% rename from examples/fastmcp/desktop.py rename to examples/mcpserver/desktop.py index 8ea62c70f8..804184516d 100644 --- a/examples/fastmcp/desktop.py +++ b/examples/mcpserver/desktop.py @@ -1,14 +1,14 @@ -"""FastMCP Desktop Example +"""MCPServer Desktop Example A simple example that exposes the desktop directory as a resource. """ from pathlib import Path -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Create server -mcp = FastMCP("Demo") +mcp = MCPServer("Demo") @mcp.resource("dir://desktop") diff --git a/examples/fastmcp/direct_call_tool_result_return.py b/examples/mcpserver/direct_call_tool_result_return.py similarity index 76% rename from examples/fastmcp/direct_call_tool_result_return.py rename to examples/mcpserver/direct_call_tool_result_return.py index 2218af49b4..44a316bc6b 100644 --- a/examples/fastmcp/direct_call_tool_result_return.py +++ b/examples/mcpserver/direct_call_tool_result_return.py @@ -1,13 +1,13 @@ -"""FastMCP Echo Server with direct CallToolResult return""" +"""MCPServer Echo Server with direct CallToolResult return""" from typing import Annotated from pydantic import BaseModel -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer from mcp.types import CallToolResult, TextContent -mcp = FastMCP("Echo Server") +mcp = MCPServer("Echo Server") class EchoResponse(BaseModel): diff --git a/examples/fastmcp/echo.py b/examples/mcpserver/echo.py similarity index 79% rename from examples/fastmcp/echo.py rename to examples/mcpserver/echo.py index 9f01e60ca2..501c47069b 100644 --- a/examples/fastmcp/echo.py +++ b/examples/mcpserver/echo.py @@ -1,9 +1,9 @@ -"""FastMCP Echo Server""" +"""MCPServer Echo Server""" -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Create server -mcp = FastMCP("Echo Server") +mcp = MCPServer("Echo Server") @mcp.tool() diff --git a/examples/fastmcp/icons_demo.py b/examples/mcpserver/icons_demo.py similarity index 86% rename from examples/fastmcp/icons_demo.py rename to examples/mcpserver/icons_demo.py index 47601c0356..f50389f32f 100644 --- a/examples/fastmcp/icons_demo.py +++ b/examples/mcpserver/icons_demo.py @@ -1,4 +1,4 @@ -"""FastMCP Icons Demo Server +"""MCPServer Icons Demo Server Demonstrates using icons with tools, resources, prompts, and implementation. """ @@ -6,7 +6,7 @@ import base64 from pathlib import Path -from mcp.server.fastmcp import FastMCP, Icon +from mcp.server.mcpserver import Icon, MCPServer # Load the icon file and convert to data URI icon_path = Path(__file__).parent / "mcp.png" @@ -16,7 +16,9 @@ icon_data = Icon(src=icon_data_uri, mime_type="image/png", sizes=["64x64"]) # Create server with icons in implementation -mcp = FastMCP("Icons Demo Server", website_url="https://github.com/modelcontextprotocol/python-sdk", icons=[icon_data]) +mcp = MCPServer( + "Icons Demo Server", website_url="https://github.com/modelcontextprotocol/python-sdk", icons=[icon_data] +) @mcp.tool(icons=[icon_data]) diff --git a/examples/fastmcp/logging_and_progress.py b/examples/mcpserver/logging_and_progress.py similarity index 80% rename from examples/fastmcp/logging_and_progress.py rename to examples/mcpserver/logging_and_progress.py index 016155233a..b157f9dd05 100644 --- a/examples/fastmcp/logging_and_progress.py +++ b/examples/mcpserver/logging_and_progress.py @@ -1,11 +1,11 @@ -"""FastMCP Echo Server that sends log messages and progress updates to the client""" +"""MCPServer Echo Server that sends log messages and progress updates to the client""" import asyncio -from mcp.server.fastmcp import Context, FastMCP +from mcp.server.mcpserver import Context, MCPServer # Create server -mcp = FastMCP("Echo Server with logging and progress updates") +mcp = MCPServer("Echo Server with logging and progress updates") @mcp.tool() diff --git a/examples/fastmcp/mcp.png b/examples/mcpserver/mcp.png similarity index 100% rename from examples/fastmcp/mcp.png rename to examples/mcpserver/mcp.png diff --git a/examples/fastmcp/memory.py b/examples/mcpserver/memory.py similarity index 98% rename from examples/fastmcp/memory.py rename to examples/mcpserver/memory.py index cc87ea930c..fd0bd93627 100644 --- a/examples/fastmcp/memory.py +++ b/examples/mcpserver/memory.py @@ -24,7 +24,7 @@ from pydantic import BaseModel, Field from pydantic_ai import Agent -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer MAX_DEPTH = 5 SIMILARITY_THRESHOLD = 0.7 @@ -36,11 +36,11 @@ T = TypeVar("T") -mcp = FastMCP("memory") +mcp = MCPServer("memory") DB_DSN = "postgresql://postgres:postgres@localhost:54320/memory_db" -# reset memory with rm ~/.fastmcp/{USER}/memory/* -PROFILE_DIR = (Path.home() / ".fastmcp" / os.environ.get("USER", "anon") / "memory").resolve() +# reset memory with rm ~/.mcp/{USER}/memory/* +PROFILE_DIR = (Path.home() / ".mcp" / os.environ.get("USER", "anon") / "memory").resolve() PROFILE_DIR.mkdir(parents=True, exist_ok=True) diff --git a/examples/fastmcp/parameter_descriptions.py b/examples/mcpserver/parameter_descriptions.py similarity index 76% rename from examples/fastmcp/parameter_descriptions.py rename to examples/mcpserver/parameter_descriptions.py index 307ae5cedd..59a1caf3f6 100644 --- a/examples/fastmcp/parameter_descriptions.py +++ b/examples/mcpserver/parameter_descriptions.py @@ -1,11 +1,11 @@ -"""FastMCP Example showing parameter descriptions""" +"""MCPServer Example showing parameter descriptions""" from pydantic import Field -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Create server -mcp = FastMCP("Parameter Descriptions Server") +mcp = MCPServer("Parameter Descriptions Server") @mcp.tool() diff --git a/examples/fastmcp/readme-quickstart.py b/examples/mcpserver/readme-quickstart.py similarity index 82% rename from examples/fastmcp/readme-quickstart.py rename to examples/mcpserver/readme-quickstart.py index e1abf7c518..864b774a9e 100644 --- a/examples/fastmcp/readme-quickstart.py +++ b/examples/mcpserver/readme-quickstart.py @@ -1,7 +1,7 @@ -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Create an MCP server -mcp = FastMCP("Demo") +mcp = MCPServer("Demo") # Add an addition tool diff --git a/examples/fastmcp/screenshot.py b/examples/mcpserver/screenshot.py similarity index 78% rename from examples/fastmcp/screenshot.py rename to examples/mcpserver/screenshot.py index 2c73c9847c..e7b3ee6fbd 100644 --- a/examples/fastmcp/screenshot.py +++ b/examples/mcpserver/screenshot.py @@ -1,15 +1,15 @@ -"""FastMCP Screenshot Example +"""MCPServer Screenshot Example Give Claude a tool to capture and view screenshots. """ import io -from mcp.server.fastmcp import FastMCP -from mcp.server.fastmcp.utilities.types import Image +from mcp.server.mcpserver import MCPServer +from mcp.server.mcpserver.utilities.types import Image # Create server -mcp = FastMCP("Screenshot Demo") +mcp = MCPServer("Screenshot Demo") @mcp.tool() diff --git a/examples/fastmcp/simple_echo.py b/examples/mcpserver/simple_echo.py similarity index 50% rename from examples/fastmcp/simple_echo.py rename to examples/mcpserver/simple_echo.py index d0fa597004..3d8142a665 100644 --- a/examples/fastmcp/simple_echo.py +++ b/examples/mcpserver/simple_echo.py @@ -1,9 +1,9 @@ -"""FastMCP Echo Server""" +"""MCPServer Echo Server""" -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Create server -mcp = FastMCP("Echo Server") +mcp = MCPServer("Echo Server") @mcp.tool() diff --git a/examples/fastmcp/text_me.py b/examples/mcpserver/text_me.py similarity index 89% rename from examples/fastmcp/text_me.py rename to examples/mcpserver/text_me.py index 8a8dea351a..7aeb543621 100644 --- a/examples/fastmcp/text_me.py +++ b/examples/mcpserver/text_me.py @@ -2,9 +2,9 @@ # dependencies = [] # /// -"""FastMCP Text Me Server +"""MCPServer Text Me Server -------------------------------- -This defines a simple FastMCP server that sends a text message to a phone number via https://surgemsg.com/. +This defines a simple MCPServer server that sends a text message to a phone number via https://surgemsg.com/. To run this example, create a `.env` file with the following values: @@ -23,7 +23,7 @@ from pydantic import BeforeValidator from pydantic_settings import BaseSettings, SettingsConfigDict -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer class SurgeSettings(BaseSettings): @@ -37,7 +37,7 @@ class SurgeSettings(BaseSettings): # Create server -mcp = FastMCP("Text me") +mcp = MCPServer("Text me") surge_settings = SurgeSettings() # type: ignore diff --git a/examples/fastmcp/unicode_example.py b/examples/mcpserver/unicode_example.py similarity index 91% rename from examples/fastmcp/unicode_example.py rename to examples/mcpserver/unicode_example.py index a598397845..012633ec76 100644 --- a/examples/fastmcp/unicode_example.py +++ b/examples/mcpserver/unicode_example.py @@ -1,10 +1,10 @@ -"""Example FastMCP server that uses Unicode characters in various places to help test +"""Example MCPServer server that uses Unicode characters in various places to help test Unicode handling in tools and inspectors. """ -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer -mcp = FastMCP() +mcp = MCPServer() @mcp.tool(description="🌟 A tool that uses various Unicode characters in its description: á é í ó ú ñ 漢字 🎉") diff --git a/examples/fastmcp/weather_structured.py b/examples/mcpserver/weather_structured.py similarity index 98% rename from examples/fastmcp/weather_structured.py rename to examples/mcpserver/weather_structured.py index af4e435dfd..958c7d3197 100644 --- a/examples/fastmcp/weather_structured.py +++ b/examples/mcpserver/weather_structured.py @@ -1,4 +1,4 @@ -"""FastMCP Weather Example with Structured Output +"""MCPServer Weather Example with Structured Output Demonstrates how to use structured output with tools to return well-typed, validated data that clients can easily process. @@ -14,10 +14,10 @@ from pydantic import BaseModel, Field from mcp.client import Client -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Create server -mcp = FastMCP("Weather Service") +mcp = MCPServer("Weather Service") # Example 1: Using a Pydantic model for structured output diff --git a/examples/servers/everything-server/mcp_everything_server/server.py b/examples/servers/everything-server/mcp_everything_server/server.py index 341fa1197f..4fb7d9a1d3 100644 --- a/examples/servers/everything-server/mcp_everything_server/server.py +++ b/examples/servers/everything-server/mcp_everything_server/server.py @@ -10,8 +10,8 @@ import logging import click -from mcp.server.fastmcp import Context, FastMCP -from mcp.server.fastmcp.prompts.base import UserMessage +from mcp.server.mcpserver import Context, MCPServer +from mcp.server.mcpserver.prompts.base import UserMessage from mcp.server.session import ServerSession from mcp.server.streamable_http import EventCallback, EventMessage, EventStore from mcp.types import ( @@ -80,7 +80,7 @@ async def replay_events_after(self, last_event_id: EventId, send_callback: Event # Create event store for SSE resumability (SEP-1699) event_store = InMemoryEventStore() -mcp = FastMCP( +mcp = MCPServer( name="mcp-conformance-test-server", ) @@ -391,9 +391,9 @@ def test_prompt_with_image() -> list[UserMessage]: # Custom request handlers -# TODO(felix): Add public APIs to FastMCP for subscribe_resource, unsubscribe_resource, -# and set_logging_level to avoid accessing protected _mcp_server attribute. -@mcp._mcp_server.set_logging_level() # pyright: ignore[reportPrivateUsage] +# TODO(felix): Add public APIs to MCPServer for subscribe_resource, unsubscribe_resource, +# and set_logging_level to avoid accessing protected _lowlevel_server attribute. +@mcp._lowlevel_server.set_logging_level() # pyright: ignore[reportPrivateUsage] async def handle_set_logging_level(level: str) -> None: """Handle logging level changes""" logger.info(f"Log level set to: {level}") @@ -413,8 +413,8 @@ async def handle_unsubscribe(uri: str) -> None: logger.info(f"Unsubscribed from resource: {uri}") -mcp._mcp_server.subscribe_resource()(handle_subscribe) # pyright: ignore[reportPrivateUsage] -mcp._mcp_server.unsubscribe_resource()(handle_unsubscribe) # pyright: ignore[reportPrivateUsage] +mcp._lowlevel_server.subscribe_resource()(handle_subscribe) # pyright: ignore[reportPrivateUsage] +mcp._lowlevel_server.unsubscribe_resource()(handle_unsubscribe) # pyright: ignore[reportPrivateUsage] @mcp.completion() diff --git a/examples/servers/simple-auth/mcp_simple_auth/legacy_as_server.py b/examples/servers/simple-auth/mcp_simple_auth/legacy_as_server.py index ac9dfcb571..ab7773b5bb 100644 --- a/examples/servers/simple-auth/mcp_simple_auth/legacy_as_server.py +++ b/examples/servers/simple-auth/mcp_simple_auth/legacy_as_server.py @@ -19,7 +19,7 @@ from starlette.responses import Response from mcp.server.auth.settings import AuthSettings, ClientRegistrationOptions -from mcp.server.fastmcp.server import FastMCP +from mcp.server.mcpserver.server import MCPServer from .simple_auth_provider import SimpleAuthSettings, SimpleOAuthProvider @@ -43,8 +43,8 @@ def __init__(self, auth_settings: SimpleAuthSettings, auth_callback_path: str, s super().__init__(auth_settings, auth_callback_path, server_url) -def create_simple_mcp_server(server_settings: ServerSettings, auth_settings: SimpleAuthSettings) -> FastMCP: - """Create a simple FastMCP server with simple authentication.""" +def create_simple_mcp_server(server_settings: ServerSettings, auth_settings: SimpleAuthSettings) -> MCPServer: + """Create a simple MCPServer server with simple authentication.""" oauth_provider = LegacySimpleOAuthProvider( auth_settings, server_settings.auth_callback_path, str(server_settings.server_url) ) @@ -61,7 +61,7 @@ def create_simple_mcp_server(server_settings: ServerSettings, auth_settings: Sim resource_server_url=None, ) - app = FastMCP( + app = MCPServer( name="Simple Auth MCP Server", instructions="A simple MCP server with simple credential authentication", auth_server_provider=oauth_provider, diff --git a/examples/servers/simple-auth/mcp_simple_auth/server.py b/examples/servers/simple-auth/mcp_simple_auth/server.py index 28d2565429..0320871b12 100644 --- a/examples/servers/simple-auth/mcp_simple_auth/server.py +++ b/examples/servers/simple-auth/mcp_simple_auth/server.py @@ -16,7 +16,7 @@ from pydantic_settings import BaseSettings, SettingsConfigDict from mcp.server.auth.settings import AuthSettings -from mcp.server.fastmcp.server import FastMCP +from mcp.server.mcpserver.server import MCPServer from .token_verifier import IntrospectionTokenVerifier @@ -45,7 +45,7 @@ class ResourceServerSettings(BaseSettings): oauth_strict: bool = False -def create_resource_server(settings: ResourceServerSettings) -> FastMCP: +def create_resource_server(settings: ResourceServerSettings) -> MCPServer: """Create MCP Resource Server with token introspection. This server: @@ -60,8 +60,8 @@ def create_resource_server(settings: ResourceServerSettings) -> FastMCP: validate_resource=settings.oauth_strict, # Only validate when --oauth-strict is set ) - # Create FastMCP server as a Resource Server - app = FastMCP( + # Create MCPServer server as a Resource Server + app = MCPServer( name="MCP Resource Server", instructions="Resource Server that validates tokens via Authorization Server introspection", debug=True, diff --git a/examples/snippets/clients/display_utilities.py b/examples/snippets/clients/display_utilities.py index 40e31cf2b1..baa2765a8f 100644 --- a/examples/snippets/clients/display_utilities.py +++ b/examples/snippets/clients/display_utilities.py @@ -12,7 +12,7 @@ # Create server parameters for stdio connection server_params = StdioServerParameters( command="uv", # Using uv to run the server - args=["run", "server", "fastmcp_quickstart", "stdio"], + args=["run", "server", "mcpserver_quickstart", "stdio"], env={"UV_INDEX": os.environ.get("UV_INDEX", "")}, ) diff --git a/examples/snippets/clients/stdio_client.py b/examples/snippets/clients/stdio_client.py index e4d4303975..f096a2649a 100644 --- a/examples/snippets/clients/stdio_client.py +++ b/examples/snippets/clients/stdio_client.py @@ -12,7 +12,7 @@ # Create server parameters for stdio connection server_params = StdioServerParameters( command="uv", # Using uv to run the server - args=["run", "server", "fastmcp_quickstart", "stdio"], # We're already in snippets dir + args=["run", "server", "mcpserver_quickstart", "stdio"], # We're already in snippets dir env={"UV_INDEX": os.environ.get("UV_INDEX", "")}, ) @@ -43,7 +43,7 @@ async def run(): prompts = await session.list_prompts() print(f"Available prompts: {[p.name for p in prompts.prompts]}") - # Get a prompt (greet_user prompt from fastmcp_quickstart) + # Get a prompt (greet_user prompt from mcpserver_quickstart) if prompts.prompts: prompt = await session.get_prompt("greet_user", arguments={"name": "Alice", "style": "friendly"}) print(f"Prompt result: {prompt.messages[0].content}") @@ -56,13 +56,13 @@ async def run(): tools = await session.list_tools() print(f"Available tools: {[t.name for t in tools.tools]}") - # Read a resource (greeting resource from fastmcp_quickstart) + # Read a resource (greeting resource from mcpserver_quickstart) resource_content = await session.read_resource("greeting://World") content_block = resource_content.contents[0] if isinstance(content_block, types.TextContent): print(f"Resource content: {content_block.text}") - # Call a tool (add tool from fastmcp_quickstart) + # Call a tool (add tool from mcpserver_quickstart) result = await session.call_tool("add", arguments={"a": 5, "b": 3}) result_unstructured = result.content[0] if isinstance(result_unstructured, types.TextContent): diff --git a/examples/snippets/servers/__init__.py b/examples/snippets/servers/__init__.py index b9865e822a..f132f875f5 100644 --- a/examples/snippets/servers/__init__.py +++ b/examples/snippets/servers/__init__.py @@ -22,7 +22,7 @@ def run_server(): print("Usage: server [transport]") print("Available servers: basic_tool, basic_resource, basic_prompt, tool_progress,") print(" sampling, elicitation, completion, notifications,") - print(" fastmcp_quickstart, structured_output, images") + print(" mcpserver_quickstart, structured_output, images") print("Available transports: stdio (default), sse, streamable-http") sys.exit(1) diff --git a/examples/snippets/servers/basic_prompt.py b/examples/snippets/servers/basic_prompt.py index 40f606ba69..d2eee9729e 100644 --- a/examples/snippets/servers/basic_prompt.py +++ b/examples/snippets/servers/basic_prompt.py @@ -1,7 +1,7 @@ -from mcp.server.fastmcp import FastMCP -from mcp.server.fastmcp.prompts import base +from mcp.server.mcpserver import MCPServer +from mcp.server.mcpserver.prompts import base -mcp = FastMCP(name="Prompt Example") +mcp = MCPServer(name="Prompt Example") @mcp.prompt(title="Code Review") diff --git a/examples/snippets/servers/basic_resource.py b/examples/snippets/servers/basic_resource.py index 5c19730595..38d96b491f 100644 --- a/examples/snippets/servers/basic_resource.py +++ b/examples/snippets/servers/basic_resource.py @@ -1,6 +1,6 @@ -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer -mcp = FastMCP(name="Resource Example") +mcp = MCPServer(name="Resource Example") @mcp.resource("file://documents/{name}") diff --git a/examples/snippets/servers/basic_tool.py b/examples/snippets/servers/basic_tool.py index 550e240808..7648024bc6 100644 --- a/examples/snippets/servers/basic_tool.py +++ b/examples/snippets/servers/basic_tool.py @@ -1,6 +1,6 @@ -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer -mcp = FastMCP(name="Tool Example") +mcp = MCPServer(name="Tool Example") @mcp.tool() diff --git a/examples/snippets/servers/completion.py b/examples/snippets/servers/completion.py index d7626f0b4b..47accffa3b 100644 --- a/examples/snippets/servers/completion.py +++ b/examples/snippets/servers/completion.py @@ -1,4 +1,4 @@ -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer from mcp.types import ( Completion, CompletionArgument, @@ -7,7 +7,7 @@ ResourceTemplateReference, ) -mcp = FastMCP(name="Example") +mcp = MCPServer(name="Example") @mcp.resource("github://repos/{owner}/{repo}") diff --git a/examples/snippets/servers/direct_call_tool_result.py b/examples/snippets/servers/direct_call_tool_result.py index 3dfff91f12..4c98c358ee 100644 --- a/examples/snippets/servers/direct_call_tool_result.py +++ b/examples/snippets/servers/direct_call_tool_result.py @@ -4,10 +4,10 @@ from pydantic import BaseModel -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer from mcp.types import CallToolResult, TextContent -mcp = FastMCP("CallToolResult Example") +mcp = MCPServer("CallToolResult Example") class ValidationModel(BaseModel): diff --git a/examples/snippets/servers/direct_execution.py b/examples/snippets/servers/direct_execution.py index 65a6fbbf39..acf7151d3b 100644 --- a/examples/snippets/servers/direct_execution.py +++ b/examples/snippets/servers/direct_execution.py @@ -7,9 +7,9 @@ python servers/direct_execution.py """ -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer -mcp = FastMCP("My App") +mcp = MCPServer("My App") @mcp.tool() diff --git a/examples/snippets/servers/elicitation.py b/examples/snippets/servers/elicitation.py index 34921aa4b3..70e515c75f 100644 --- a/examples/snippets/servers/elicitation.py +++ b/examples/snippets/servers/elicitation.py @@ -9,12 +9,12 @@ from pydantic import BaseModel, Field -from mcp.server.fastmcp import Context, FastMCP +from mcp.server.mcpserver import Context, MCPServer from mcp.server.session import ServerSession from mcp.shared.exceptions import UrlElicitationRequiredError from mcp.types import ElicitRequestURLParams -mcp = FastMCP(name="Elicitation Example") +mcp = MCPServer(name="Elicitation Example") class BookingPreferences(BaseModel): diff --git a/examples/snippets/servers/images.py b/examples/snippets/servers/images.py index 9e0262c853..b30c9a8f78 100644 --- a/examples/snippets/servers/images.py +++ b/examples/snippets/servers/images.py @@ -1,10 +1,10 @@ -"""Example showing image handling with FastMCP.""" +"""Example showing image handling with MCPServer.""" from PIL import Image as PILImage -from mcp.server.fastmcp import FastMCP, Image +from mcp.server.mcpserver import Image, MCPServer -mcp = FastMCP("Image Example") +mcp = MCPServer("Image Example") @mcp.tool() diff --git a/examples/snippets/servers/lifespan_example.py b/examples/snippets/servers/lifespan_example.py index 62278b6aac..2925f10602 100644 --- a/examples/snippets/servers/lifespan_example.py +++ b/examples/snippets/servers/lifespan_example.py @@ -4,7 +4,7 @@ from contextlib import asynccontextmanager from dataclasses import dataclass -from mcp.server.fastmcp import Context, FastMCP +from mcp.server.mcpserver import Context, MCPServer from mcp.server.session import ServerSession @@ -34,7 +34,7 @@ class AppContext: @asynccontextmanager -async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]: +async def app_lifespan(server: MCPServer) -> AsyncIterator[AppContext]: """Manage application lifecycle with type-safe context.""" # Initialize on startup db = await Database.connect() @@ -46,7 +46,7 @@ async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]: # Pass lifespan to server -mcp = FastMCP("My App", lifespan=app_lifespan) +mcp = MCPServer("My App", lifespan=app_lifespan) # Access type-safe lifespan context in tools diff --git a/examples/snippets/servers/fastmcp_quickstart.py b/examples/snippets/servers/mcpserver_quickstart.py similarity index 84% rename from examples/snippets/servers/fastmcp_quickstart.py rename to examples/snippets/servers/mcpserver_quickstart.py index f762e908ab..70a83a56e4 100644 --- a/examples/snippets/servers/fastmcp_quickstart.py +++ b/examples/snippets/servers/mcpserver_quickstart.py @@ -1,13 +1,13 @@ -"""FastMCP quickstart example. +"""MCPServer quickstart example. Run from the repository root: - uv run examples/snippets/servers/fastmcp_quickstart.py + uv run examples/snippets/servers/mcpserver_quickstart.py """ -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Create an MCP server -mcp = FastMCP("Demo") +mcp = MCPServer("Demo") # Add an addition tool diff --git a/examples/snippets/servers/notifications.py b/examples/snippets/servers/notifications.py index 833bc89053..5579af510f 100644 --- a/examples/snippets/servers/notifications.py +++ b/examples/snippets/servers/notifications.py @@ -1,7 +1,7 @@ -from mcp.server.fastmcp import Context, FastMCP +from mcp.server.mcpserver import Context, MCPServer from mcp.server.session import ServerSession -mcp = FastMCP(name="Notifications Example") +mcp = MCPServer(name="Notifications Example") @mcp.tool() diff --git a/examples/snippets/servers/oauth_server.py b/examples/snippets/servers/oauth_server.py index 226a3ec6e1..962ef0615e 100644 --- a/examples/snippets/servers/oauth_server.py +++ b/examples/snippets/servers/oauth_server.py @@ -6,7 +6,7 @@ from mcp.server.auth.provider import AccessToken, TokenVerifier from mcp.server.auth.settings import AuthSettings -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer class SimpleTokenVerifier(TokenVerifier): @@ -16,8 +16,8 @@ async def verify_token(self, token: str) -> AccessToken | None: pass # This is where you would implement actual token validation -# Create FastMCP instance as a Resource Server -mcp = FastMCP( +# Create MCPServer instance as a Resource Server +mcp = MCPServer( "Weather Service", # Token verifier for authentication token_verifier=SimpleTokenVerifier(), diff --git a/examples/snippets/servers/sampling.py b/examples/snippets/servers/sampling.py index ae78a74ace..4ffeeda726 100644 --- a/examples/snippets/servers/sampling.py +++ b/examples/snippets/servers/sampling.py @@ -1,8 +1,8 @@ -from mcp.server.fastmcp import Context, FastMCP +from mcp.server.mcpserver import Context, MCPServer from mcp.server.session import ServerSession from mcp.types import SamplingMessage, TextContent -mcp = FastMCP(name="Sampling Example") +mcp = MCPServer(name="Sampling Example") @mcp.tool() diff --git a/examples/snippets/servers/streamable_config.py b/examples/snippets/servers/streamable_config.py index ca68102242..622e67063c 100644 --- a/examples/snippets/servers/streamable_config.py +++ b/examples/snippets/servers/streamable_config.py @@ -2,9 +2,9 @@ uv run examples/snippets/servers/streamable_config.py """ -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer -mcp = FastMCP("StatelessServer") +mcp = MCPServer("StatelessServer") # Add a simple tool to demonstrate the server diff --git a/examples/snippets/servers/streamable_http_basic_mounting.py b/examples/snippets/servers/streamable_http_basic_mounting.py index 7a32dbef96..9a53034f16 100644 --- a/examples/snippets/servers/streamable_http_basic_mounting.py +++ b/examples/snippets/servers/streamable_http_basic_mounting.py @@ -9,10 +9,10 @@ from starlette.applications import Starlette from starlette.routing import Mount -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Create MCP server -mcp = FastMCP("My App") +mcp = MCPServer("My App") @mcp.tool() diff --git a/examples/snippets/servers/streamable_http_host_mounting.py b/examples/snippets/servers/streamable_http_host_mounting.py index 57da57f7b7..2a41f74a59 100644 --- a/examples/snippets/servers/streamable_http_host_mounting.py +++ b/examples/snippets/servers/streamable_http_host_mounting.py @@ -9,10 +9,10 @@ from starlette.applications import Starlette from starlette.routing import Host -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Create MCP server -mcp = FastMCP("MCP Host App") +mcp = MCPServer("MCP Host App") @mcp.tool() diff --git a/examples/snippets/servers/streamable_http_multiple_servers.py b/examples/snippets/servers/streamable_http_multiple_servers.py index cf6c6985d2..71217bdfed 100644 --- a/examples/snippets/servers/streamable_http_multiple_servers.py +++ b/examples/snippets/servers/streamable_http_multiple_servers.py @@ -9,11 +9,11 @@ from starlette.applications import Starlette from starlette.routing import Mount -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Create multiple MCP servers -api_mcp = FastMCP("API Server") -chat_mcp = FastMCP("Chat Server") +api_mcp = MCPServer("API Server") +chat_mcp = MCPServer("Chat Server") @api_mcp.tool() diff --git a/examples/snippets/servers/streamable_http_path_config.py b/examples/snippets/servers/streamable_http_path_config.py index 1dcceeeeb7..4c65ffdd79 100644 --- a/examples/snippets/servers/streamable_http_path_config.py +++ b/examples/snippets/servers/streamable_http_path_config.py @@ -1,4 +1,4 @@ -"""Example showing path configuration when mounting FastMCP. +"""Example showing path configuration when mounting MCPServer. Run from the repository root: uvicorn examples.snippets.servers.streamable_http_path_config:app --reload @@ -7,10 +7,10 @@ from starlette.applications import Starlette from starlette.routing import Mount -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer -# Create a simple FastMCP server -mcp_at_root = FastMCP("My Server") +# Create a simple MCPServer server +mcp_at_root = MCPServer("My Server") @mcp_at_root.tool() diff --git a/examples/snippets/servers/streamable_starlette_mount.py b/examples/snippets/servers/streamable_starlette_mount.py index c33dc71bc7..eb6f1b8093 100644 --- a/examples/snippets/servers/streamable_starlette_mount.py +++ b/examples/snippets/servers/streamable_starlette_mount.py @@ -7,10 +7,10 @@ from starlette.applications import Starlette from starlette.routing import Mount -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer # Create the Echo server -echo_mcp = FastMCP(name="EchoServer") +echo_mcp = MCPServer(name="EchoServer") @echo_mcp.tool() @@ -20,7 +20,7 @@ def echo(message: str) -> str: # Create the Math server -math_mcp = FastMCP(name="MathServer") +math_mcp = MCPServer(name="MathServer") @math_mcp.tool() diff --git a/examples/snippets/servers/structured_output.py b/examples/snippets/servers/structured_output.py index 50ee130c7e..bea7b22c16 100644 --- a/examples/snippets/servers/structured_output.py +++ b/examples/snippets/servers/structured_output.py @@ -4,9 +4,9 @@ from pydantic import BaseModel, Field -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer -mcp = FastMCP("Structured Output Example") +mcp = MCPServer("Structured Output Example") # Using Pydantic models for rich structured data diff --git a/examples/snippets/servers/tool_progress.py b/examples/snippets/servers/tool_progress.py index 2ac458f6aa..0b283cb1f5 100644 --- a/examples/snippets/servers/tool_progress.py +++ b/examples/snippets/servers/tool_progress.py @@ -1,7 +1,7 @@ -from mcp.server.fastmcp import Context, FastMCP +from mcp.server.mcpserver import Context, MCPServer from mcp.server.session import ServerSession -mcp = FastMCP(name="Progress Example") +mcp = MCPServer(name="Progress Example") @mcp.tool() diff --git a/pyproject.toml b/pyproject.toml index f3f80e4e91..e3faf687e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -149,7 +149,7 @@ max-complexity = 24 # Default is 10 [tool.ruff.lint.per-file-ignores] "__init__.py" = ["F401"] -"tests/server/fastmcp/test_func_metadata.py" = ["E501"] +"tests/server/mcpserver/test_func_metadata.py" = ["E501"] "tests/shared/test_progress_notifications.py" = ["PLW0603"] [tool.ruff.lint.pylint] @@ -230,3 +230,7 @@ source = [ "/home/runner/work/python-sdk/python-sdk/src/", 'D:\a\python-sdk\python-sdk\src', ] + +[tool.inline-snapshot] +default-flags = ["disable"] +format-command = "ruff format --stdin-filename {filename}" diff --git a/src/mcp/cli/__init__.py b/src/mcp/cli/__init__.py index b29bce8878..daf07c0a13 100644 --- a/src/mcp/cli/__init__.py +++ b/src/mcp/cli/__init__.py @@ -1,4 +1,4 @@ -"""FastMCP CLI package.""" +"""MCP CLI package.""" from .cli import app diff --git a/src/mcp/cli/claude.py b/src/mcp/cli/claude.py index 7c2f650edd..27e782033e 100644 --- a/src/mcp/cli/claude.py +++ b/src/mcp/cli/claude.py @@ -7,7 +7,7 @@ from pathlib import Path from typing import Any -from mcp.server.fastmcp.utilities.logging import get_logger +from mcp.server.mcpserver.utilities.logging import get_logger logger = get_logger(__name__) @@ -49,7 +49,7 @@ def update_claude_config( with_packages: list[str] | None = None, env_vars: dict[str, str] | None = None, ) -> bool: - """Add or update a FastMCP server in Claude's configuration. + """Add or update a MCP server in Claude's configuration. Args: file_spec: Path to the server file, optionally with :object suffix @@ -121,7 +121,7 @@ def update_claude_config( else: # pragma: no cover file_spec = str(Path(file_spec).resolve()) - # Add fastmcp run command + # Add mcp run command args.extend(["mcp", "run", file_spec]) server_config: dict[str, Any] = {"command": uv_path, "args": args} diff --git a/src/mcp/cli/cli.py b/src/mcp/cli/cli.py index 5e1e641ac4..858ab7db29 100644 --- a/src/mcp/cli/cli.py +++ b/src/mcp/cli/cli.py @@ -8,7 +8,7 @@ from pathlib import Path from typing import Annotated, Any -from mcp.server import FastMCP +from mcp.server import MCPServer from mcp.server import Server as LowLevelServer try: @@ -19,9 +19,9 @@ try: from mcp.cli import claude - from mcp.server.fastmcp.utilities.logging import get_logger + from mcp.server.mcpserver.utilities.logging import get_logger except ImportError: # pragma: no cover - print("Error: mcp.server.fastmcp is not installed or not in PYTHONPATH") + print("Error: mcp.server is not installed or not in PYTHONPATH") sys.exit(1) try: @@ -149,12 +149,10 @@ def _check_server_object(server_object: Any, object_name: str): Returns: True if it's supported. """ - if not isinstance(server_object, FastMCP): - logger.error(f"The server object {object_name} is of type {type(server_object)} (expecting {FastMCP}).") + if not isinstance(server_object, MCPServer): + logger.error(f"The server object {object_name} is of type {type(server_object)} (expecting {MCPServer}).") if isinstance(server_object, LowLevelServer): - logger.warning( - "Note that only FastMCP server is supported. Low level Server class is not yet supported." - ) + logger.warning("Note that only MCPServer is supported. Low level Server class is not yet supported.") return False return True @@ -172,8 +170,8 @@ def _check_server_object(server_object: Any, object_name: str): f"No server object found in {file}. Please either:\n" "1. Use a standard variable name (mcp, server, or app)\n" "2. Specify the object name with file:object syntax" - "3. If the server creates the FastMCP object within main() " - " or another function, refactor the FastMCP object to be a " + "3. If the server creates the MCPServer object within main() " + " or another function, refactor the MCPServer object to be a " " global variable named mcp, server, or app.", extra={"file": str(file)}, ) diff --git a/src/mcp/client/_memory.py b/src/mcp/client/_memory.py index 3589d0da75..33a1a186f2 100644 --- a/src/mcp/client/_memory.py +++ b/src/mcp/client/_memory.py @@ -10,7 +10,7 @@ from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream from mcp.server import Server -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer from mcp.shared.memory import create_client_server_memory_streams from mcp.shared.message import SessionMessage @@ -23,7 +23,7 @@ class InMemoryTransport: stopped when the context manager exits. Example: - server = FastMCP("test") + server = MCPServer("test") transport = InMemoryTransport(server) async with transport.connect() as (read_stream, write_stream): @@ -36,11 +36,11 @@ class InMemoryTransport: result = await client.call_tool("my_tool", {...}) """ - def __init__(self, server: Server[Any] | FastMCP, *, raise_exceptions: bool = False) -> None: + def __init__(self, server: Server[Any] | MCPServer, *, raise_exceptions: bool = False) -> None: """Initialize the in-memory transport. Args: - server: The MCP server to connect to (Server or FastMCP instance) + server: The MCP server to connect to (Server or MCPServer instance) raise_exceptions: Whether to raise exceptions from the server """ self._server = server @@ -61,10 +61,10 @@ async def connect( Yields: A tuple of (read_stream, write_stream) for bidirectional communication """ - # Unwrap FastMCP to get underlying Server + # Unwrap MCPServer to get underlying Server actual_server: Server[Any] - if isinstance(self._server, FastMCP): - actual_server = self._server._mcp_server # type: ignore[reportPrivateUsage] + if isinstance(self._server, MCPServer): + actual_server = self._server._lowlevel_server # type: ignore[reportPrivateUsage] else: actual_server = self._server diff --git a/src/mcp/client/client.py b/src/mcp/client/client.py index 1738c12def..14a230aa30 100644 --- a/src/mcp/client/client.py +++ b/src/mcp/client/client.py @@ -8,7 +8,7 @@ from mcp.client._memory import InMemoryTransport from mcp.client.session import ClientSession, ElicitationFnT, ListRootsFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT from mcp.server import Server -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer from mcp.shared.session import ProgressFnT from mcp.types import ( CallToolResult, @@ -34,14 +34,14 @@ class Client: """A high-level MCP client for connecting to MCP servers. Currently supports in-memory transport for testing. Pass a Server or - FastMCP instance directly to the constructor. + MCPServer instance directly to the constructor. Example: ```python from mcp.client import Client - from mcp.server.fastmcp import FastMCP + from mcp.server.mcpserver import MCPServer - server = FastMCP("test") + server = MCPServer("test") @server.tool() def add(a: int, b: int) -> int: @@ -55,7 +55,7 @@ async def main(): ``` """ - # TODO(felixweinberger): Expand to support all transport types (like FastMCP 2): + # TODO(felixweinberger): Expand to support all transport types: # - Add ClientTransport base class with connect_session() method # - Add StreamableHttpTransport, SSETransport, StdioTransport # - Add infer_transport() to auto-detect transport from input type @@ -64,7 +64,7 @@ async def main(): def __init__( self, - server: Server[Any] | FastMCP, + server: Server[Any] | MCPServer, *, # TODO(Marcelo): When do `raise_exceptions=True` actually raises? raise_exceptions: bool = False, @@ -79,7 +79,7 @@ def __init__( """Initialize the client with a server. Args: - server: The MCP server to connect to (Server or FastMCP instance) + server: The MCP server to connect to (Server or MCPServer instance) raise_exceptions: Whether to raise exceptions from the server read_timeout_seconds: Timeout for read operations sampling_callback: Callback for handling sampling requests diff --git a/src/mcp/client/session_group.py b/src/mcp/client/session_group.py index 32395829f2..4c09d92d72 100644 --- a/src/mcp/client/session_group.py +++ b/src/mcp/client/session_group.py @@ -15,7 +15,7 @@ import anyio import httpx -from pydantic import BaseModel +from pydantic import BaseModel, Field from typing_extensions import Self import mcp @@ -103,9 +103,9 @@ class ClientSessionGroup: class _ComponentNames(BaseModel): """Used for reverse index to find components.""" - prompts: set[str] = set() - resources: set[str] = set() - tools: set[str] = set() + prompts: set[str] = Field(default_factory=set) + resources: set[str] = Field(default_factory=set) + tools: set[str] = Field(default_factory=set) # Standard MCP components. _prompts: dict[str, types.Prompt] diff --git a/src/mcp/server/__init__.py b/src/mcp/server/__init__.py index 0feed368e4..a2dada3af7 100644 --- a/src/mcp/server/__init__.py +++ b/src/mcp/server/__init__.py @@ -1,5 +1,5 @@ -from .fastmcp import FastMCP from .lowlevel import NotificationOptions, Server +from .mcpserver import MCPServer from .models import InitializationOptions -__all__ = ["Server", "FastMCP", "NotificationOptions", "InitializationOptions"] +__all__ = ["Server", "MCPServer", "NotificationOptions", "InitializationOptions"] diff --git a/src/mcp/server/auth/provider.py b/src/mcp/server/auth/provider.py index 9fb30c1406..5eb577fd43 100644 --- a/src/mcp/server/auth/provider.py +++ b/src/mcp/server/auth/provider.py @@ -96,7 +96,7 @@ async def verify_token(self, token: str) -> AccessToken | None: """Verify a bearer token and return access info if valid.""" -# NOTE: FastMCP doesn't render any of these types in the user response, so it's +# NOTE: MCPServer doesn't render any of these types in the user response, so it's # OK to add fields to subclasses which should not be exposed externally. AuthorizationCodeT = TypeVar("AuthorizationCodeT", bound=AuthorizationCode) RefreshTokenT = TypeVar("RefreshTokenT", bound=RefreshToken) diff --git a/src/mcp/server/fastmcp/__init__.py b/src/mcp/server/fastmcp/__init__.py deleted file mode 100644 index 2feecf1e9b..0000000000 --- a/src/mcp/server/fastmcp/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -"""FastMCP - A more ergonomic interface for MCP servers.""" - -from mcp.types import Icon - -from .server import Context, FastMCP -from .utilities.types import Audio, Image - -__all__ = ["FastMCP", "Context", "Image", "Audio", "Icon"] diff --git a/src/mcp/server/fastmcp/exceptions.py b/src/mcp/server/fastmcp/exceptions.py deleted file mode 100644 index fb5bda106b..0000000000 --- a/src/mcp/server/fastmcp/exceptions.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Custom exceptions for FastMCP.""" - - -class FastMCPError(Exception): - """Base error for FastMCP.""" - - -class ValidationError(FastMCPError): - """Error in validating parameters or return values.""" - - -class ResourceError(FastMCPError): - """Error in resource operations.""" - - -class ToolError(FastMCPError): - """Error in tool operations.""" - - -class InvalidSignature(Exception): - """Invalid signature for use with FastMCP.""" diff --git a/src/mcp/server/fastmcp/utilities/__init__.py b/src/mcp/server/fastmcp/utilities/__init__.py deleted file mode 100644 index be448f97ad..0000000000 --- a/src/mcp/server/fastmcp/utilities/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""FastMCP utility modules.""" diff --git a/src/mcp/server/mcpserver/__init__.py b/src/mcp/server/mcpserver/__init__.py new file mode 100644 index 0000000000..f51c0b0ed8 --- /dev/null +++ b/src/mcp/server/mcpserver/__init__.py @@ -0,0 +1,8 @@ +"""MCPServer - A more ergonomic interface for MCP servers.""" + +from mcp.types import Icon + +from .server import Context, MCPServer +from .utilities.types import Audio, Image + +__all__ = ["MCPServer", "Context", "Image", "Audio", "Icon"] diff --git a/src/mcp/server/mcpserver/exceptions.py b/src/mcp/server/mcpserver/exceptions.py new file mode 100644 index 0000000000..dd1b75e829 --- /dev/null +++ b/src/mcp/server/mcpserver/exceptions.py @@ -0,0 +1,21 @@ +"""Custom exceptions for MCPServer.""" + + +class MCPServerError(Exception): + """Base error for MCPServer.""" + + +class ValidationError(MCPServerError): + """Error in validating parameters or return values.""" + + +class ResourceError(MCPServerError): + """Error in resource operations.""" + + +class ToolError(MCPServerError): + """Error in tool operations.""" + + +class InvalidSignature(Exception): + """Invalid signature for use with MCPServer.""" diff --git a/src/mcp/server/fastmcp/prompts/__init__.py b/src/mcp/server/mcpserver/prompts/__init__.py similarity index 100% rename from src/mcp/server/fastmcp/prompts/__init__.py rename to src/mcp/server/mcpserver/prompts/__init__.py diff --git a/src/mcp/server/fastmcp/prompts/base.py b/src/mcp/server/mcpserver/prompts/base.py similarity index 96% rename from src/mcp/server/fastmcp/prompts/base.py rename to src/mcp/server/mcpserver/prompts/base.py index 48c65b57c5..751733f9c5 100644 --- a/src/mcp/server/fastmcp/prompts/base.py +++ b/src/mcp/server/mcpserver/prompts/base.py @@ -1,4 +1,4 @@ -"""Base classes for FastMCP prompts.""" +"""Base classes for MCPServer prompts.""" from __future__ import annotations @@ -9,12 +9,12 @@ import pydantic_core from pydantic import BaseModel, Field, TypeAdapter, validate_call -from mcp.server.fastmcp.utilities.context_injection import find_context_parameter, inject_context -from mcp.server.fastmcp.utilities.func_metadata import func_metadata +from mcp.server.mcpserver.utilities.context_injection import find_context_parameter, inject_context +from mcp.server.mcpserver.utilities.func_metadata import func_metadata from mcp.types import ContentBlock, Icon, TextContent if TYPE_CHECKING: - from mcp.server.fastmcp.server import Context + from mcp.server.mcpserver.server import Context from mcp.server.session import ServerSessionT from mcp.shared.context import LifespanContextT, RequestT diff --git a/src/mcp/server/fastmcp/prompts/manager.py b/src/mcp/server/mcpserver/prompts/manager.py similarity index 88% rename from src/mcp/server/fastmcp/prompts/manager.py rename to src/mcp/server/mcpserver/prompts/manager.py index 6d032c73a0..34d4c7e94e 100644 --- a/src/mcp/server/fastmcp/prompts/manager.py +++ b/src/mcp/server/mcpserver/prompts/manager.py @@ -4,11 +4,11 @@ from typing import TYPE_CHECKING, Any -from mcp.server.fastmcp.prompts.base import Message, Prompt -from mcp.server.fastmcp.utilities.logging import get_logger +from mcp.server.mcpserver.prompts.base import Message, Prompt +from mcp.server.mcpserver.utilities.logging import get_logger if TYPE_CHECKING: - from mcp.server.fastmcp.server import Context + from mcp.server.mcpserver.server import Context from mcp.server.session import ServerSessionT from mcp.shared.context import LifespanContextT, RequestT @@ -16,7 +16,7 @@ class PromptManager: - """Manages FastMCP prompts.""" + """Manages MCPServer prompts.""" def __init__(self, warn_on_duplicate_prompts: bool = True): self._prompts: dict[str, Prompt] = {} diff --git a/src/mcp/server/fastmcp/resources/__init__.py b/src/mcp/server/mcpserver/resources/__init__.py similarity index 100% rename from src/mcp/server/fastmcp/resources/__init__.py rename to src/mcp/server/mcpserver/resources/__init__.py diff --git a/src/mcp/server/fastmcp/resources/base.py b/src/mcp/server/mcpserver/resources/base.py similarity index 96% rename from src/mcp/server/fastmcp/resources/base.py rename to src/mcp/server/mcpserver/resources/base.py index b91a0e1203..d3ccc425ef 100644 --- a/src/mcp/server/fastmcp/resources/base.py +++ b/src/mcp/server/mcpserver/resources/base.py @@ -1,4 +1,4 @@ -"""Base classes and interfaces for FastMCP resources.""" +"""Base classes and interfaces for MCPServer resources.""" import abc from typing import Any diff --git a/src/mcp/server/fastmcp/resources/resource_manager.py b/src/mcp/server/mcpserver/resources/resource_manager.py similarity index 93% rename from src/mcp/server/fastmcp/resources/resource_manager.py rename to src/mcp/server/mcpserver/resources/resource_manager.py index 20f67bbe42..a855fb5f55 100644 --- a/src/mcp/server/fastmcp/resources/resource_manager.py +++ b/src/mcp/server/mcpserver/resources/resource_manager.py @@ -7,13 +7,13 @@ from pydantic import AnyUrl -from mcp.server.fastmcp.resources.base import Resource -from mcp.server.fastmcp.resources.templates import ResourceTemplate -from mcp.server.fastmcp.utilities.logging import get_logger +from mcp.server.mcpserver.resources.base import Resource +from mcp.server.mcpserver.resources.templates import ResourceTemplate +from mcp.server.mcpserver.utilities.logging import get_logger from mcp.types import Annotations, Icon if TYPE_CHECKING: - from mcp.server.fastmcp.server import Context + from mcp.server.mcpserver.server import Context from mcp.server.session import ServerSessionT from mcp.shared.context import LifespanContextT, RequestT @@ -21,7 +21,7 @@ class ResourceManager: - """Manages FastMCP resources.""" + """Manages MCPServer resources.""" def __init__(self, warn_on_duplicate_resources: bool = True): self._resources: dict[str, Resource] = {} diff --git a/src/mcp/server/fastmcp/resources/templates.py b/src/mcp/server/mcpserver/resources/templates.py similarity index 94% rename from src/mcp/server/fastmcp/resources/templates.py rename to src/mcp/server/mcpserver/resources/templates.py index 14e2ca4bc5..698ac36825 100644 --- a/src/mcp/server/fastmcp/resources/templates.py +++ b/src/mcp/server/mcpserver/resources/templates.py @@ -10,13 +10,13 @@ from pydantic import BaseModel, Field, validate_call -from mcp.server.fastmcp.resources.types import FunctionResource, Resource -from mcp.server.fastmcp.utilities.context_injection import find_context_parameter, inject_context -from mcp.server.fastmcp.utilities.func_metadata import func_metadata +from mcp.server.mcpserver.resources.types import FunctionResource, Resource +from mcp.server.mcpserver.utilities.context_injection import find_context_parameter, inject_context +from mcp.server.mcpserver.utilities.func_metadata import func_metadata from mcp.types import Annotations, Icon if TYPE_CHECKING: - from mcp.server.fastmcp.server import Context + from mcp.server.mcpserver.server import Context from mcp.server.session import ServerSessionT from mcp.shared.context import LifespanContextT, RequestT diff --git a/src/mcp/server/fastmcp/resources/types.py b/src/mcp/server/mcpserver/resources/types.py similarity index 99% rename from src/mcp/server/fastmcp/resources/types.py rename to src/mcp/server/mcpserver/resources/types.py index 683886fd8b..64e6338060 100644 --- a/src/mcp/server/fastmcp/resources/types.py +++ b/src/mcp/server/mcpserver/resources/types.py @@ -13,7 +13,7 @@ import pydantic_core from pydantic import Field, ValidationInfo, validate_call -from mcp.server.fastmcp.resources.base import Resource +from mcp.server.mcpserver.resources.base import Resource from mcp.types import Annotations, Icon diff --git a/src/mcp/server/fastmcp/server.py b/src/mcp/server/mcpserver/server.py similarity index 92% rename from src/mcp/server/fastmcp/server.py rename to src/mcp/server/mcpserver/server.py index 71cd81eb27..600f392458 100644 --- a/src/mcp/server/fastmcp/server.py +++ b/src/mcp/server/mcpserver/server.py @@ -1,4 +1,4 @@ -"""FastMCP - A more ergonomic interface for MCP servers.""" +"""MCPServer - A more ergonomic interface for MCP servers.""" from __future__ import annotations @@ -27,16 +27,15 @@ from mcp.server.auth.settings import AuthSettings from mcp.server.elicitation import ElicitationResult, ElicitSchemaModelT, UrlElicitationResult, elicit_with_validation from mcp.server.elicitation import elicit_url as _elicit_url -from mcp.server.fastmcp.exceptions import ResourceError -from mcp.server.fastmcp.prompts import Prompt, PromptManager -from mcp.server.fastmcp.resources import FunctionResource, Resource, ResourceManager -from mcp.server.fastmcp.tools import Tool, ToolManager -from mcp.server.fastmcp.utilities.context_injection import find_context_parameter -from mcp.server.fastmcp.utilities.logging import configure_logging, get_logger from mcp.server.lowlevel.helper_types import ReadResourceContents -from mcp.server.lowlevel.server import LifespanResultT -from mcp.server.lowlevel.server import Server as MCPServer +from mcp.server.lowlevel.server import LifespanResultT, Server from mcp.server.lowlevel.server import lifespan as default_lifespan +from mcp.server.mcpserver.exceptions import ResourceError +from mcp.server.mcpserver.prompts import Prompt, PromptManager +from mcp.server.mcpserver.resources import FunctionResource, Resource, ResourceManager +from mcp.server.mcpserver.tools import Tool, ToolManager +from mcp.server.mcpserver.utilities.context_injection import find_context_parameter +from mcp.server.mcpserver.utilities.logging import configure_logging, get_logger from mcp.server.session import ServerSession, ServerSessionT from mcp.server.sse import SseServerTransport from mcp.server.stdio import stdio_server @@ -57,14 +56,14 @@ class Settings(BaseSettings, Generic[LifespanResultT]): - """FastMCP server settings. + """MCPServer settings. - All settings can be configured via environment variables with the prefix FASTMCP_. - For example, FASTMCP_DEBUG=true will set debug=True. + All settings can be configured via environment variables with the prefix MCP_. + For example, MCP_DEBUG=true will set debug=True. """ model_config = SettingsConfigDict( - env_prefix="FASTMCP_", + env_prefix="MCP_", env_file=".env", env_nested_delimiter="__", nested_model_default_partial_update=True, @@ -84,27 +83,25 @@ class Settings(BaseSettings, Generic[LifespanResultT]): # prompt settings warn_on_duplicate_prompts: bool - lifespan: Callable[[FastMCP[LifespanResultT]], AbstractAsyncContextManager[LifespanResultT]] | None + lifespan: Callable[[MCPServer[LifespanResultT]], AbstractAsyncContextManager[LifespanResultT]] | None """A async context manager that will be called when the server is started.""" auth: AuthSettings | None def lifespan_wrapper( - app: FastMCP[LifespanResultT], - lifespan: Callable[[FastMCP[LifespanResultT]], AbstractAsyncContextManager[LifespanResultT]], -) -> Callable[[MCPServer[LifespanResultT, Request]], AbstractAsyncContextManager[LifespanResultT]]: + app: MCPServer[LifespanResultT], + lifespan: Callable[[MCPServer[LifespanResultT]], AbstractAsyncContextManager[LifespanResultT]], +) -> Callable[[Server[LifespanResultT, Request]], AbstractAsyncContextManager[LifespanResultT]]: @asynccontextmanager - async def wrap( - _: MCPServer[LifespanResultT, Request], - ) -> AsyncIterator[LifespanResultT]: + async def wrap(_: Server[LifespanResultT, Request]) -> AsyncIterator[LifespanResultT]: async with lifespan(app) as context: yield context return wrap -class FastMCP(Generic[LifespanResultT]): +class MCPServer(Generic[LifespanResultT]): def __init__( self, name: str | None = None, @@ -123,7 +120,7 @@ def __init__( warn_on_duplicate_resources: bool = True, warn_on_duplicate_tools: bool = True, warn_on_duplicate_prompts: bool = True, - lifespan: Callable[[FastMCP[LifespanResultT]], AbstractAsyncContextManager[LifespanResultT]] | None = None, + lifespan: Callable[[MCPServer[LifespanResultT]], AbstractAsyncContextManager[LifespanResultT]] | None = None, auth: AuthSettings | None = None, ): self.settings = Settings( @@ -136,15 +133,15 @@ def __init__( auth=auth, ) - self._mcp_server = MCPServer( - name=name or "FastMCP", + self._lowlevel_server = Server( + name=name or "mcp-server", title=title, description=description, instructions=instructions, website_url=website_url, icons=icons, version=version, - # TODO(Marcelo): It seems there's a type mismatch between the lifespan type from an FastMCP and Server. + # TODO(Marcelo): It seems there's a type mismatch between the lifespan type from an MCPServer and Server. # We need to create a Lifespan type that is a generic on the server type, like Starlette does. lifespan=(lifespan_wrapper(self, self.settings.lifespan) if self.settings.lifespan else default_lifespan), # type: ignore ) @@ -176,43 +173,43 @@ def __init__( @property def name(self) -> str: - return self._mcp_server.name + return self._lowlevel_server.name @property def title(self) -> str | None: - return self._mcp_server.title + return self._lowlevel_server.title @property def description(self) -> str | None: - return self._mcp_server.description + return self._lowlevel_server.description @property def instructions(self) -> str | None: - return self._mcp_server.instructions + return self._lowlevel_server.instructions @property def website_url(self) -> str | None: - return self._mcp_server.website_url + return self._lowlevel_server.website_url @property def icons(self) -> list[Icon] | None: - return self._mcp_server.icons + return self._lowlevel_server.icons @property def version(self) -> str | None: - return self._mcp_server.version + return self._lowlevel_server.version @property def session_manager(self) -> StreamableHTTPSessionManager: """Get the StreamableHTTP session manager. This is exposed to enable advanced use cases like mounting multiple - FastMCP servers in a single FastAPI application. + MCPServer instances in a single FastAPI application. Raises: RuntimeError: If called before streamable_http_app() has been called. """ - return self._mcp_server.session_manager # pragma: no cover + return self._lowlevel_server.session_manager # pragma: no cover @overload def run(self, transport: Literal["stdio"] = ...) -> None: ... @@ -249,7 +246,7 @@ def run( transport: Literal["stdio", "sse", "streamable-http"] = "stdio", **kwargs: Any, ) -> None: - """Run the FastMCP server. Note this is a synchronous function. + """Run the MCP server. Note this is a synchronous function. Args: transport: Transport protocol to use ("stdio", "sse", or "streamable-http") @@ -269,16 +266,16 @@ def run( def _setup_handlers(self) -> None: """Set up core MCP protocol handlers.""" - self._mcp_server.list_tools()(self.list_tools) + self._lowlevel_server.list_tools()(self.list_tools) # Note: we disable the lowlevel server's input validation. - # FastMCP does ad hoc conversion of incoming data before validating - + # MCPServer does ad hoc conversion of incoming data before validating - # for now we preserve this for backwards compatibility. - self._mcp_server.call_tool(validate_input=False)(self.call_tool) - self._mcp_server.list_resources()(self.list_resources) - self._mcp_server.read_resource()(self.read_resource) - self._mcp_server.list_prompts()(self.list_prompts) - self._mcp_server.get_prompt()(self.get_prompt) - self._mcp_server.list_resource_templates()(self.list_resource_templates) + self._lowlevel_server.call_tool(validate_input=False)(self.call_tool) + self._lowlevel_server.list_resources()(self.list_resources) + self._lowlevel_server.read_resource()(self.read_resource) + self._lowlevel_server.list_prompts()(self.list_prompts) + self._lowlevel_server.get_prompt()(self.get_prompt) + self._lowlevel_server.list_resource_templates()(self.list_resource_templates) async def list_tools(self) -> list[MCPTool]: """List all available tools.""" @@ -302,10 +299,10 @@ def get_context(self) -> Context[ServerSession, LifespanResultT, Request]: during a request; outside a request, most methods will error. """ try: - request_context = self._mcp_server.request_context + request_context = self._lowlevel_server.request_context except LookupError: request_context = None - return Context(request_context=request_context, fastmcp=self) + return Context(request_context=request_context, mcp_server=self) async def call_tool(self, name: str, arguments: dict[str, Any]) -> Sequence[ContentBlock] | dict[str, Any]: """Call a tool by name with arguments.""" @@ -488,7 +485,7 @@ async def handle_completion(ref, argument, context): return Completion(values=["option1", "option2"]) return None """ - return self._mcp_server.completion() + return self._lowlevel_server.completion() def add_resource(self, resource: Resource) -> None: """Add a resource to the server. @@ -676,7 +673,7 @@ def custom_route( name: str | None = None, include_in_schema: bool = True, ): - """Decorator to register a custom HTTP route on the FastMCP server. + """Decorator to register a custom HTTP route on the MCP server. Allows adding arbitrary HTTP endpoints outside the standard MCP protocol, which can be useful for OAuth callbacks, health checks, or admin APIs. @@ -704,13 +701,7 @@ def decorator( # pragma: no cover func: Callable[[Request], Awaitable[Response]], ) -> Callable[[Request], Awaitable[Response]]: self._custom_starlette_routes.append( - Route( - path, - endpoint=func, - methods=methods, - name=name, - include_in_schema=include_in_schema, - ) + Route(path, endpoint=func, methods=methods, name=name, include_in_schema=include_in_schema) ) return func @@ -719,10 +710,10 @@ def decorator( # pragma: no cover async def run_stdio_async(self) -> None: """Run the server using stdio transport.""" async with stdio_server() as (read_stream, write_stream): - await self._mcp_server.run( + await self._lowlevel_server.run( read_stream, write_stream, - self._mcp_server.create_initialization_options(), + self._lowlevel_server.create_initialization_options(), ) async def run_sse_async( # pragma: no cover @@ -810,7 +801,9 @@ async def handle_sse(scope: Scope, receive: Receive, send: Send): # pragma: no # Add client ID from auth context into request context if available async with sse.connect_sse(scope, receive, send) as streams: - await self._mcp_server.run(streams[0], streams[1], self._mcp_server.create_initialization_options()) + await self._lowlevel_server.run( + streams[0], streams[1], self._lowlevel_server.create_initialization_options() + ) return Response() # Create routes @@ -923,7 +916,7 @@ def streamable_http_app( host: str = "127.0.0.1", ) -> Starlette: """Return an instance of the StreamableHTTP server app.""" - return self._mcp_server.streamable_http_app( + return self._lowlevel_server.streamable_http_app( streamable_http_path=streamable_http_path, json_response=json_response, stateless_http=stateless_http, @@ -1012,25 +1005,25 @@ def my_tool(x: int, ctx: Context) -> str: """ _request_context: RequestContext[ServerSessionT, LifespanContextT, RequestT] | None - _fastmcp: FastMCP | None + _mcp_server: MCPServer | None def __init__( self, *, request_context: (RequestContext[ServerSessionT, LifespanContextT, RequestT] | None) = None, - fastmcp: FastMCP | None = None, + mcp_server: MCPServer | None = None, **kwargs: Any, ): super().__init__(**kwargs) self._request_context = request_context - self._fastmcp = fastmcp + self._mcp_server = mcp_server @property - def fastmcp(self) -> FastMCP: - """Access to the FastMCP server.""" - if self._fastmcp is None: # pragma: no cover + def mcp_server(self) -> MCPServer: + """Access to the MCPServer instance.""" + if self._mcp_server is None: # pragma: no cover raise ValueError("Context is not available outside of a request") - return self._fastmcp # pragma: no cover + return self._mcp_server # pragma: no cover @property def request_context( @@ -1070,8 +1063,8 @@ async def read_resource(self, uri: str | AnyUrl) -> Iterable[ReadResourceContent Returns: The resource content as either text or bytes """ - assert self._fastmcp is not None, "Context is not available outside of a request" - return await self._fastmcp.read_resource(uri) + assert self._mcp_server is not None, "Context is not available outside of a request" + return await self._mcp_server.read_resource(uri) async def elicit( self, diff --git a/src/mcp/server/fastmcp/tools/__init__.py b/src/mcp/server/mcpserver/tools/__init__.py similarity index 100% rename from src/mcp/server/fastmcp/tools/__init__.py rename to src/mcp/server/mcpserver/tools/__init__.py diff --git a/src/mcp/server/fastmcp/tools/base.py b/src/mcp/server/mcpserver/tools/base.py similarity index 94% rename from src/mcp/server/fastmcp/tools/base.py rename to src/mcp/server/mcpserver/tools/base.py index e6a64f4d96..9798fd96e5 100644 --- a/src/mcp/server/fastmcp/tools/base.py +++ b/src/mcp/server/mcpserver/tools/base.py @@ -8,15 +8,15 @@ from pydantic import BaseModel, Field -from mcp.server.fastmcp.exceptions import ToolError -from mcp.server.fastmcp.utilities.context_injection import find_context_parameter -from mcp.server.fastmcp.utilities.func_metadata import FuncMetadata, func_metadata +from mcp.server.mcpserver.exceptions import ToolError +from mcp.server.mcpserver.utilities.context_injection import find_context_parameter +from mcp.server.mcpserver.utilities.func_metadata import FuncMetadata, func_metadata from mcp.shared.exceptions import UrlElicitationRequiredError from mcp.shared.tool_name_validation import validate_and_warn_tool_name from mcp.types import Icon, ToolAnnotations if TYPE_CHECKING: - from mcp.server.fastmcp.server import Context + from mcp.server.mcpserver.server import Context from mcp.server.session import ServerSessionT from mcp.shared.context import LifespanContextT, RequestT diff --git a/src/mcp/server/fastmcp/tools/tool_manager.py b/src/mcp/server/mcpserver/tools/tool_manager.py similarity index 91% rename from src/mcp/server/fastmcp/tools/tool_manager.py rename to src/mcp/server/mcpserver/tools/tool_manager.py index 0d3d9d52a4..5decefb7ef 100644 --- a/src/mcp/server/fastmcp/tools/tool_manager.py +++ b/src/mcp/server/mcpserver/tools/tool_manager.py @@ -3,21 +3,21 @@ from collections.abc import Callable from typing import TYPE_CHECKING, Any -from mcp.server.fastmcp.exceptions import ToolError -from mcp.server.fastmcp.tools.base import Tool -from mcp.server.fastmcp.utilities.logging import get_logger +from mcp.server.mcpserver.exceptions import ToolError +from mcp.server.mcpserver.tools.base import Tool +from mcp.server.mcpserver.utilities.logging import get_logger from mcp.shared.context import LifespanContextT, RequestT from mcp.types import Icon, ToolAnnotations if TYPE_CHECKING: - from mcp.server.fastmcp.server import Context + from mcp.server.mcpserver.server import Context from mcp.server.session import ServerSessionT logger = get_logger(__name__) class ToolManager: - """Manages FastMCP tools.""" + """Manages MCPServer tools.""" def __init__( self, diff --git a/src/mcp/server/mcpserver/utilities/__init__.py b/src/mcp/server/mcpserver/utilities/__init__.py new file mode 100644 index 0000000000..b9d00d54c5 --- /dev/null +++ b/src/mcp/server/mcpserver/utilities/__init__.py @@ -0,0 +1 @@ +"""MCPServer utility modules.""" diff --git a/src/mcp/server/fastmcp/utilities/context_injection.py b/src/mcp/server/mcpserver/utilities/context_injection.py similarity index 95% rename from src/mcp/server/fastmcp/utilities/context_injection.py rename to src/mcp/server/mcpserver/utilities/context_injection.py index 80923e873f..9cba83e860 100644 --- a/src/mcp/server/fastmcp/utilities/context_injection.py +++ b/src/mcp/server/mcpserver/utilities/context_injection.py @@ -1,4 +1,4 @@ -"""Context injection utilities for FastMCP.""" +"""Context injection utilities for MCPServer.""" from __future__ import annotations @@ -20,7 +20,7 @@ def find_context_parameter(fn: Callable[..., Any]) -> str | None: Returns: The name of the context parameter, or None if not found """ - from mcp.server.fastmcp.server import Context + from mcp.server.mcpserver.server import Context # Get type hints to properly resolve string annotations try: diff --git a/src/mcp/server/fastmcp/utilities/func_metadata.py b/src/mcp/server/mcpserver/utilities/func_metadata.py similarity index 97% rename from src/mcp/server/fastmcp/utilities/func_metadata.py rename to src/mcp/server/mcpserver/utilities/func_metadata.py index 95b3a3274b..4b539ce1f2 100644 --- a/src/mcp/server/fastmcp/utilities/func_metadata.py +++ b/src/mcp/server/mcpserver/utilities/func_metadata.py @@ -21,9 +21,9 @@ is_union_origin, ) -from mcp.server.fastmcp.exceptions import InvalidSignature -from mcp.server.fastmcp.utilities.logging import get_logger -from mcp.server.fastmcp.utilities.types import Audio, Image +from mcp.server.mcpserver.exceptions import InvalidSignature +from mcp.server.mcpserver.utilities.logging import get_logger +from mcp.server.mcpserver.utilities.types import Audio, Image from mcp.types import CallToolResult, ContentBlock, TextContent logger = get_logger(__name__) @@ -98,8 +98,8 @@ def convert_result(self, result: Any) -> Any: Note: we return unstructured content here **even though the lowlevel server tool call handler provides generic backwards compatibility serialization of - structured content**. This is for FastMCP backwards compatibility: we need to - retain FastMCP's ad hoc conversion logic for constructing unstructured output + structured content**. This is for MCPServer backwards compatibility: we need to + retain MCPServer's ad hoc conversion logic for constructing unstructured output from function return values, whereas the lowlevel server simply serializes the structured output. """ @@ -213,7 +213,7 @@ def func_metadata( try: sig = inspect.signature(func, eval_str=True) except NameError as e: # pragma: no cover - # This raise could perhaps be skipped, and we (FastMCP) just call + # This raise could perhaps be skipped, and we (MCPServer) just call # model_rebuild right before using it 🤷 raise InvalidSignature(f"Unable to evaluate type annotations for callable {func.__name__!r}") from e params = sig.parameters @@ -494,7 +494,7 @@ class DictModel(RootModel[dict_annotation]): def _convert_to_content(result: Any) -> Sequence[ContentBlock]: """Convert a result to a sequence of content objects. - Note: This conversion logic comes from previous versions of FastMCP and is being + Note: This conversion logic comes from previous versions of MCPServer and is being retained for purposes of backwards compatibility. It produces different unstructured output than the lowlevel server tool call handler, which just serializes structured content verbatim. diff --git a/src/mcp/server/fastmcp/utilities/logging.py b/src/mcp/server/mcpserver/utilities/logging.py similarity index 84% rename from src/mcp/server/fastmcp/utilities/logging.py rename to src/mcp/server/mcpserver/utilities/logging.py index 43c4d9ba77..c394f2bfaf 100644 --- a/src/mcp/server/fastmcp/utilities/logging.py +++ b/src/mcp/server/mcpserver/utilities/logging.py @@ -1,14 +1,14 @@ -"""Logging utilities for FastMCP.""" +"""Logging utilities for MCPServer.""" import logging from typing import Literal def get_logger(name: str) -> logging.Logger: - """Get a logger nested under MCPnamespace. + """Get a logger nested under MCP namespace. Args: - name: the name of the logger, which will be prefixed with 'FastMCP.' + name: the name of the logger Returns: a configured logger instance diff --git a/src/mcp/server/fastmcp/utilities/types.py b/src/mcp/server/mcpserver/utilities/types.py similarity index 98% rename from src/mcp/server/fastmcp/utilities/types.py rename to src/mcp/server/mcpserver/utilities/types.py index a1445de196..f092b245a8 100644 --- a/src/mcp/server/fastmcp/utilities/types.py +++ b/src/mcp/server/mcpserver/utilities/types.py @@ -1,4 +1,4 @@ -"""Common types used across FastMCP.""" +"""Common types used across MCPServer.""" import base64 from pathlib import Path diff --git a/src/mcp/shared/context.py b/src/mcp/shared/context.py index b140f9a77a..890536a5de 100644 --- a/src/mcp/shared/context.py +++ b/src/mcp/shared/context.py @@ -22,7 +22,7 @@ class RequestContext(Generic[SessionT, LifespanContextT, RequestT]): lifespan_context: LifespanContextT # NOTE: This is typed as Any to avoid circular imports. The actual type is # mcp.server.experimental.request_context.Experimental, but importing it here - # triggers mcp.server.__init__ -> fastmcp -> tools -> back to this module. + # triggers mcp.server.__init__ -> mcpserver -> tools -> back to this module. # The Server sets this to an Experimental instance at runtime. experimental: Any = field(default=None) request: RequestT | None = None diff --git a/tests/client/test_client.py b/tests/client/test_client.py index 97319861bd..943c74c0ca 100644 --- a/tests/client/test_client.py +++ b/tests/client/test_client.py @@ -9,7 +9,7 @@ import mcp.types as types from mcp.client.client import Client from mcp.server import Server -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer from mcp.types import ( CallToolResult, EmptyResult, @@ -68,9 +68,9 @@ async def handle_completion( @pytest.fixture -def app() -> FastMCP: - """Create a FastMCP server for testing.""" - server = FastMCP("test") +def app() -> MCPServer: + """Create a MCPServer server for testing.""" + server = MCPServer("test") @server.tool() def greet(name: str) -> str: @@ -90,7 +90,7 @@ def greeting_prompt(name: str) -> str: return server -async def test_client_is_initialized(app: FastMCP): +async def test_client_is_initialized(app: MCPServer): """Test that the client is initialized after entering context.""" async with Client(app) as client: assert client.server_capabilities == snapshot( @@ -114,13 +114,13 @@ async def test_client_with_simple_server(simple_server: Server): ) -async def test_client_send_ping(app: FastMCP): +async def test_client_send_ping(app: MCPServer): async with Client(app) as client: result = await client.send_ping() assert result == snapshot(EmptyResult()) -async def test_client_list_tools(app: FastMCP): +async def test_client_list_tools(app: MCPServer): async with Client(app) as client: result = await client.list_tools() assert result == snapshot( @@ -147,7 +147,7 @@ async def test_client_list_tools(app: FastMCP): ) -async def test_client_call_tool(app: FastMCP): +async def test_client_call_tool(app: MCPServer): async with Client(app) as client: result = await client.call_tool("greet", {"name": "World"}) assert result == snapshot( @@ -158,7 +158,7 @@ async def test_client_call_tool(app: FastMCP): ) -async def test_read_resource(app: FastMCP): +async def test_read_resource(app: MCPServer): """Test reading a resource.""" async with Client(app) as client: result = await client.read_resource("test://resource") @@ -169,7 +169,7 @@ async def test_read_resource(app: FastMCP): ) -async def test_get_prompt(app: FastMCP): +async def test_get_prompt(app: MCPServer): """Test getting a prompt.""" async with Client(app) as client: result = await client.get_prompt("greeting_prompt", {"name": "Alice"}) @@ -181,14 +181,14 @@ async def test_get_prompt(app: FastMCP): ) -def test_client_session_property_before_enter(app: FastMCP): +def test_client_session_property_before_enter(app: MCPServer): """Test that accessing session before context manager raises RuntimeError.""" client = Client(app) with pytest.raises(RuntimeError, match="Client must be used within an async context manager"): client.session -async def test_client_reentry_raises_runtime_error(app: FastMCP): +async def test_client_reentry_raises_runtime_error(app: MCPServer): """Test that reentering a client raises RuntimeError.""" async with Client(app) as client: with pytest.raises(RuntimeError, match="Client is already entered"): @@ -237,7 +237,7 @@ async def test_client_set_logging_level(simple_server: Server): assert result == snapshot(EmptyResult()) -async def test_client_list_resources_with_params(app: FastMCP): +async def test_client_list_resources_with_params(app: MCPServer): """Test listing resources with params parameter.""" async with Client(app) as client: result = await client.list_resources() @@ -255,14 +255,14 @@ async def test_client_list_resources_with_params(app: FastMCP): ) -async def test_client_list_resource_templates(app: FastMCP): +async def test_client_list_resource_templates(app: MCPServer): """Test listing resource templates with params parameter.""" async with Client(app) as client: result = await client.list_resource_templates() assert result == snapshot(ListResourceTemplatesResult(resource_templates=[])) -async def test_list_prompts(app: FastMCP): +async def test_list_prompts(app: MCPServer): """Test listing prompts with params parameter.""" async with Client(app) as client: result = await client.list_prompts() diff --git a/tests/client/test_list_methods_cursor.py b/tests/client/test_list_methods_cursor.py index 7d4124bbd5..1e547afed6 100644 --- a/tests/client/test_list_methods_cursor.py +++ b/tests/client/test_list_methods_cursor.py @@ -5,7 +5,7 @@ import mcp.types as types from mcp import Client from mcp.server import Server -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer from mcp.types import ListToolsRequest, ListToolsResult from .conftest import StreamSpyCollection @@ -16,7 +16,7 @@ @pytest.fixture async def full_featured_server(): """Create a server with tools, resources, prompts, and templates.""" - server = FastMCP("test") + server = MCPServer("test") # pragma: no cover on handlers below - these exist only to register items with the # server so list_* methods return results. The handlers themselves are never called @@ -55,7 +55,7 @@ def greeting_prompt(name: str) -> str: # pragma: no cover ) async def test_list_methods_params_parameter( stream_spy: Callable[[], StreamSpyCollection], - full_featured_server: FastMCP, + full_featured_server: MCPServer, method_name: str, request_method: str, ): @@ -95,7 +95,7 @@ async def test_list_methods_params_parameter( async def test_list_tools_with_strict_server_validation( - full_featured_server: FastMCP, + full_featured_server: MCPServer, ): """Test pagination with a server that validates request format strictly.""" async with Client(full_featured_server) as client: diff --git a/tests/client/test_list_roots_callback.py b/tests/client/test_list_roots_callback.py index a8f8823fe5..6919624c7d 100644 --- a/tests/client/test_list_roots_callback.py +++ b/tests/client/test_list_roots_callback.py @@ -3,8 +3,8 @@ from mcp import Client from mcp.client.session import ClientSession -from mcp.server.fastmcp import FastMCP -from mcp.server.fastmcp.server import Context +from mcp.server.mcpserver import MCPServer +from mcp.server.mcpserver.server import Context from mcp.server.session import ServerSession from mcp.shared.context import RequestContext from mcp.types import ListRootsResult, Root, TextContent @@ -12,7 +12,7 @@ @pytest.mark.anyio async def test_list_roots_callback(): - server = FastMCP("test") + server = MCPServer("test") callback_return = ListRootsResult( roots=[ diff --git a/tests/client/test_logging_callback.py b/tests/client/test_logging_callback.py index 687efca71e..e90dac2c47 100644 --- a/tests/client/test_logging_callback.py +++ b/tests/client/test_logging_callback.py @@ -4,7 +4,7 @@ import mcp.types as types from mcp import Client -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer from mcp.shared.session import RequestResponder from mcp.types import ( LoggingMessageNotificationParams, @@ -22,7 +22,7 @@ async def __call__(self, params: LoggingMessageNotificationParams) -> None: @pytest.mark.anyio async def test_logging_callback(): - server = FastMCP("test") + server = MCPServer("test") logging_collector = LoggingCollector() # Create a simple test tool diff --git a/tests/client/test_sampling_callback.py b/tests/client/test_sampling_callback.py index 1394e665ca..78d7ba688d 100644 --- a/tests/client/test_sampling_callback.py +++ b/tests/client/test_sampling_callback.py @@ -2,7 +2,7 @@ from mcp import Client from mcp.client.session import ClientSession -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer from mcp.shared.context import RequestContext from mcp.types import ( CreateMessageRequestParams, @@ -16,7 +16,7 @@ @pytest.mark.anyio async def test_sampling_callback(): - server = FastMCP("test") + server = MCPServer("test") callback_return = CreateMessageResult( role="assistant", @@ -60,7 +60,7 @@ async def test_sampling_tool(message: str): @pytest.mark.anyio async def test_create_message_backwards_compat_single_content(): """Test backwards compatibility: create_message without tools returns single content.""" - server = FastMCP("test") + server = MCPServer("test") # Callback returns single content (text) callback_return = CreateMessageResult( diff --git a/tests/client/transports/test_memory.py b/tests/client/transports/test_memory.py index 942ff6c8e0..70f842040a 100644 --- a/tests/client/transports/test_memory.py +++ b/tests/client/transports/test_memory.py @@ -5,7 +5,7 @@ from mcp import Client from mcp.client._memory import InMemoryTransport from mcp.server import Server -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer from mcp.types import Resource @@ -30,9 +30,9 @@ async def handle_list_resources(): # pragma: no cover @pytest.fixture -def fastmcp_server() -> FastMCP: - """Create a FastMCP server for testing.""" - server = FastMCP("test") +def mcpserver_server() -> MCPServer: + """Create a MCPServer server for testing.""" + server = MCPServer("test") @server.tool() def greet(name: str) -> str: @@ -58,40 +58,40 @@ async def test_with_server(simple_server: Server): assert write_stream is not None -async def test_with_fastmcp(fastmcp_server: FastMCP): - """Test creating transport with a FastMCP instance.""" - transport = InMemoryTransport(fastmcp_server) +async def test_with_mcpserver(mcpserver_server: MCPServer): + """Test creating transport with a MCPServer instance.""" + transport = InMemoryTransport(mcpserver_server) async with transport.connect() as (read_stream, write_stream): assert read_stream is not None assert write_stream is not None -async def test_server_is_running(fastmcp_server: FastMCP): +async def test_server_is_running(mcpserver_server: MCPServer): """Test that the server is running and responding to requests.""" - async with Client(fastmcp_server) as client: + async with Client(mcpserver_server) as client: assert client.server_capabilities is not None -async def test_list_tools(fastmcp_server: FastMCP): +async def test_list_tools(mcpserver_server: MCPServer): """Test listing tools through the transport.""" - async with Client(fastmcp_server) as client: + async with Client(mcpserver_server) as client: tools_result = await client.list_tools() assert len(tools_result.tools) > 0 tool_names = [t.name for t in tools_result.tools] assert "greet" in tool_names -async def test_call_tool(fastmcp_server: FastMCP): +async def test_call_tool(mcpserver_server: MCPServer): """Test calling a tool through the transport.""" - async with Client(fastmcp_server) as client: + async with Client(mcpserver_server) as client: result = await client.call_tool("greet", {"name": "World"}) assert result is not None assert len(result.content) > 0 assert "Hello, World!" in str(result.content[0]) -async def test_raise_exceptions(fastmcp_server: FastMCP): +async def test_raise_exceptions(mcpserver_server: MCPServer): """Test that raise_exceptions parameter is passed through.""" - transport = InMemoryTransport(fastmcp_server, raise_exceptions=True) + transport = InMemoryTransport(mcpserver_server, raise_exceptions=True) async with transport.connect() as (read_stream, _write_stream): assert read_stream is not None diff --git a/tests/issues/test_100_tool_listing.py b/tests/issues/test_100_tool_listing.py index 9e3447b741..e59fb632d7 100644 --- a/tests/issues/test_100_tool_listing.py +++ b/tests/issues/test_100_tool_listing.py @@ -1,12 +1,12 @@ import pytest -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer pytestmark = pytest.mark.anyio async def test_list_tools_returns_all_tools(): - mcp = FastMCP("TestTools") + mcp = MCPServer("TestTools") # Create 100 tools with unique names num_tools = 100 diff --git a/tests/issues/test_1027_win_unreachable_cleanup.py b/tests/issues/test_1027_win_unreachable_cleanup.py index 78ff54070f..c59c5aecae 100644 --- a/tests/issues/test_1027_win_unreachable_cleanup.py +++ b/tests/issues/test_1027_win_unreachable_cleanup.py @@ -43,13 +43,13 @@ async def test_lifespan_cleanup_executed(): Path(startup_marker).unlink() Path(cleanup_marker).unlink() - # Create a minimal MCP server using FastMCP that tracks lifecycle + # Create a minimal MCP server using MCPServer that tracks lifecycle server_code = textwrap.dedent(f""" import asyncio import sys from pathlib import Path from contextlib import asynccontextmanager - from mcp.server.fastmcp import FastMCP + from mcp.server.mcpserver import MCPServer STARTUP_MARKER = {escape_path_for_python(startup_marker)} CLEANUP_MARKER = {escape_path_for_python(cleanup_marker)} @@ -64,7 +64,7 @@ async def lifespan(server): # This cleanup code now runs properly during shutdown Path(CLEANUP_MARKER).write_text("cleaned up") - mcp = FastMCP("test-server", lifespan=lifespan) + mcp = MCPServer("test-server", lifespan=lifespan) @mcp.tool() def echo(text: str) -> str: @@ -156,7 +156,7 @@ async def test_stdin_close_triggers_cleanup(): import sys from pathlib import Path from contextlib import asynccontextmanager - from mcp.server.fastmcp import FastMCP + from mcp.server.mcpserver import MCPServer STARTUP_MARKER = {escape_path_for_python(startup_marker)} CLEANUP_MARKER = {escape_path_for_python(cleanup_marker)} @@ -171,7 +171,7 @@ async def lifespan(server): # This cleanup code runs when stdin closes, enabling graceful shutdown Path(CLEANUP_MARKER).write_text("cleaned up") - mcp = FastMCP("test-server", lifespan=lifespan) + mcp = MCPServer("test-server", lifespan=lifespan) @mcp.tool() def echo(text: str) -> str: diff --git a/tests/issues/test_129_resource_templates.py b/tests/issues/test_129_resource_templates.py index 2411210675..39e2c6f2ad 100644 --- a/tests/issues/test_129_resource_templates.py +++ b/tests/issues/test_129_resource_templates.py @@ -1,13 +1,13 @@ import pytest from mcp import types -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer @pytest.mark.anyio async def test_resource_templates(): # Create an MCP server - mcp = FastMCP("Demo") + mcp = MCPServer("Demo") # Add a dynamic greeting resource @mcp.resource("greeting://{name}") @@ -23,7 +23,7 @@ def get_user_profile(user_id: str) -> str: # pragma: no cover # Get the list of resource templates using the underlying server # Note: list_resource_templates() returns a decorator that wraps the handler # The handler returns a ServerResult with a ListResourceTemplatesResult inside - result = await mcp._mcp_server.request_handlers[types.ListResourceTemplatesRequest]( + result = await mcp._lowlevel_server.request_handlers[types.ListResourceTemplatesRequest]( types.ListResourceTemplatesRequest(params=None) ) assert isinstance(result, types.ListResourceTemplatesResult) diff --git a/tests/issues/test_1338_icons_and_metadata.py b/tests/issues/test_1338_icons_and_metadata.py index 41df47ee4f..a003f75b8b 100644 --- a/tests/issues/test_1338_icons_and_metadata.py +++ b/tests/issues/test_1338_icons_and_metadata.py @@ -2,7 +2,7 @@ import pytest -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer from mcp.types import Icon pytestmark = pytest.mark.anyio @@ -19,7 +19,7 @@ async def test_icons_and_website_url(): ) # Create server with website URL and icon - mcp = FastMCP("TestServer", website_url="https://example.com", icons=[test_icon]) + mcp = MCPServer("TestServer", website_url="https://example.com", icons=[test_icon]) # Create tool with icon @mcp.tool(icons=[test_icon]) @@ -100,7 +100,7 @@ async def test_multiple_icons(): icon2 = Icon(src="data:image/png;base64,icon2", mime_type="image/png", sizes=["32x32"]) icon3 = Icon(src="data:image/png;base64,icon3", mime_type="image/png", sizes=["64x64"]) - mcp = FastMCP("MultiIconServer") + mcp = MCPServer("MultiIconServer") # Create tool with multiple icons @mcp.tool(icons=[icon1, icon2, icon3]) @@ -122,7 +122,7 @@ def multi_icon_tool() -> str: # pragma: no cover async def test_no_icons_or_website(): """Test that server works without icons or websiteUrl.""" - mcp = FastMCP("BasicServer") + mcp = MCPServer("BasicServer") @mcp.tool() def basic_tool() -> str: # pragma: no cover diff --git a/tests/issues/test_141_resource_templates.py b/tests/issues/test_141_resource_templates.py index be99e75837..57e8040df8 100644 --- a/tests/issues/test_141_resource_templates.py +++ b/tests/issues/test_141_resource_templates.py @@ -1,7 +1,7 @@ import pytest from mcp import Client -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer from mcp.types import ( ListResourceTemplatesResult, TextResourceContents, @@ -11,7 +11,7 @@ @pytest.mark.anyio async def test_resource_template_edge_cases(): """Test server-side resource template validation""" - mcp = FastMCP("Demo") + mcp = MCPServer("Demo") # Test case 1: Template with multiple parameters @mcp.resource("resource://users/{user_id}/posts/{post_id}") @@ -64,7 +64,7 @@ def get_user_profile_missing(user_id: str) -> str: # pragma: no cover @pytest.mark.anyio async def test_resource_template_client_interaction(): """Test client-side resource template interaction""" - mcp = FastMCP("Demo") + mcp = MCPServer("Demo") # Register some templated resources @mcp.resource("resource://users/{user_id}/posts/{post_id}") diff --git a/tests/issues/test_152_resource_mime_type.py b/tests/issues/test_152_resource_mime_type.py index 7d1ac00c78..e738017f85 100644 --- a/tests/issues/test_152_resource_mime_type.py +++ b/tests/issues/test_152_resource_mime_type.py @@ -3,16 +3,16 @@ import pytest from mcp import Client, types -from mcp.server.fastmcp import FastMCP from mcp.server.lowlevel import Server from mcp.server.lowlevel.helper_types import ReadResourceContents +from mcp.server.mcpserver import MCPServer pytestmark = pytest.mark.anyio -async def test_fastmcp_resource_mime_type(): +async def test_mcpserver_resource_mime_type(): """Test that mime_type parameter is respected for resources.""" - mcp = FastMCP("test") + mcp = MCPServer("test") # Create a small test image as bytes image_bytes = b"fake_image_data" diff --git a/tests/issues/test_1754_mime_type_parameters.py b/tests/issues/test_1754_mime_type_parameters.py index 6ad7bdee86..7903fd5603 100644 --- a/tests/issues/test_1754_mime_type_parameters.py +++ b/tests/issues/test_1754_mime_type_parameters.py @@ -7,14 +7,14 @@ import pytest from mcp import Client -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer pytestmark = pytest.mark.anyio async def test_mime_type_with_parameters(): """Test that MIME types with parameters are accepted (RFC 2045).""" - mcp = FastMCP("test") + mcp = MCPServer("test") # This should NOT raise a validation error @mcp.resource("ui://widget", mime_type="text/html;profile=mcp-app") @@ -28,7 +28,7 @@ def widget() -> str: async def test_mime_type_with_parameters_and_space(): """Test MIME type with space after semicolon.""" - mcp = FastMCP("test") + mcp = MCPServer("test") @mcp.resource("data://json", mime_type="application/json; charset=utf-8") def data() -> str: @@ -41,7 +41,7 @@ def data() -> str: async def test_mime_type_with_multiple_parameters(): """Test MIME type with multiple parameters.""" - mcp = FastMCP("test") + mcp = MCPServer("test") @mcp.resource("data://multi", mime_type="text/plain; charset=utf-8; format=fixed") def data() -> str: @@ -54,7 +54,7 @@ def data() -> str: async def test_mime_type_preserved_in_read_resource(): """Test that MIME type with parameters is preserved when reading resource.""" - mcp = FastMCP("test") + mcp = MCPServer("test") @mcp.resource("ui://my-widget", mime_type="text/html;profile=mcp-app") def my_widget() -> str: diff --git a/tests/issues/test_176_progress_token.py b/tests/issues/test_176_progress_token.py index 2dba0c6751..db0a66f9b3 100644 --- a/tests/issues/test_176_progress_token.py +++ b/tests/issues/test_176_progress_token.py @@ -2,7 +2,7 @@ import pytest -from mcp.server.fastmcp import Context +from mcp.server.mcpserver import Context from mcp.shared.context import RequestContext pytestmark = pytest.mark.anyio @@ -24,7 +24,7 @@ async def test_progress_token_zero_first_call(): ) # Create context with our mocks - ctx = Context(request_context=request_context, fastmcp=MagicMock()) + ctx = Context(request_context=request_context, mcp_server=MagicMock()) # Test progress reporting await ctx.report_progress(0, 10) # First call with 0 diff --git a/tests/issues/test_188_concurrency.py b/tests/issues/test_188_concurrency.py index 61a9b2c6bb..0e11f61482 100644 --- a/tests/issues/test_188_concurrency.py +++ b/tests/issues/test_188_concurrency.py @@ -2,12 +2,12 @@ import pytest from mcp import Client -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer @pytest.mark.anyio async def test_messages_are_executed_concurrently_tools(): - server = FastMCP("test") + server = MCPServer("test") event = anyio.Event() tool_started = anyio.Event() call_order: list[str] = [] @@ -48,7 +48,7 @@ async def trigger(): @pytest.mark.anyio async def test_messages_are_executed_concurrently_tools_and_resources(): - server = FastMCP("test") + server = MCPServer("test") event = anyio.Event() tool_started = anyio.Event() call_order: list[str] = [] diff --git a/tests/issues/test_355_type_error.py b/tests/issues/test_355_type_error.py index 63ed803846..33d6b455b2 100644 --- a/tests/issues/test_355_type_error.py +++ b/tests/issues/test_355_type_error.py @@ -2,7 +2,7 @@ from contextlib import asynccontextmanager from dataclasses import dataclass -from mcp.server.fastmcp import Context, FastMCP +from mcp.server.mcpserver import Context, MCPServer from mcp.server.session import ServerSession @@ -19,7 +19,7 @@ def query(self): # pragma: no cover # Create a named server -mcp = FastMCP("My App") +mcp = MCPServer("My App") @dataclass @@ -28,7 +28,7 @@ class AppContext: @asynccontextmanager -async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]: # pragma: no cover +async def app_lifespan(server: MCPServer) -> AsyncIterator[AppContext]: # pragma: no cover """Manage application lifecycle with type-safe context""" # Initialize on startup db = await Database.connect() @@ -40,7 +40,7 @@ async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]: # pragma: # Pass lifespan to server -mcp = FastMCP("My App", lifespan=app_lifespan) +mcp = MCPServer("My App", lifespan=app_lifespan) # Access type-safe lifespan context in tools diff --git a/tests/issues/test_973_url_decoding.py b/tests/issues/test_973_url_decoding.py index 32d5a16cc5..01cf222b92 100644 --- a/tests/issues/test_973_url_decoding.py +++ b/tests/issues/test_973_url_decoding.py @@ -3,7 +3,7 @@ Regression test for https://github.com/modelcontextprotocol/python-sdk/issues/973 """ -from mcp.server.fastmcp.resources import ResourceTemplate +from mcp.server.mcpserver.resources import ResourceTemplate def test_template_matches_decodes_space(): diff --git a/tests/server/auth/test_error_handling.py b/tests/server/auth/test_error_handling.py index 8eafbcdbbc..7c5c435825 100644 --- a/tests/server/auth/test_error_handling.py +++ b/tests/server/auth/test_error_handling.py @@ -16,7 +16,7 @@ from mcp.server.auth.provider import AuthorizeError, RegistrationError, TokenError from mcp.server.auth.routes import create_auth_routes from mcp.server.auth.settings import ClientRegistrationOptions, RevocationOptions -from tests.server.fastmcp.auth.test_auth_integration import MockOAuthProvider +from tests.server.mcpserver.auth.test_auth_integration import MockOAuthProvider @pytest.fixture diff --git a/tests/server/fastmcp/__init__.py b/tests/server/mcpserver/__init__.py similarity index 100% rename from tests/server/fastmcp/__init__.py rename to tests/server/mcpserver/__init__.py diff --git a/tests/server/fastmcp/auth/__init__.py b/tests/server/mcpserver/auth/__init__.py similarity index 100% rename from tests/server/fastmcp/auth/__init__.py rename to tests/server/mcpserver/auth/__init__.py diff --git a/tests/server/fastmcp/auth/test_auth_integration.py b/tests/server/mcpserver/auth/test_auth_integration.py similarity index 100% rename from tests/server/fastmcp/auth/test_auth_integration.py rename to tests/server/mcpserver/auth/test_auth_integration.py diff --git a/tests/server/fastmcp/prompts/__init__.py b/tests/server/mcpserver/prompts/__init__.py similarity index 100% rename from tests/server/fastmcp/prompts/__init__.py rename to tests/server/mcpserver/prompts/__init__.py diff --git a/tests/server/fastmcp/prompts/test_base.py b/tests/server/mcpserver/prompts/test_base.py similarity index 98% rename from tests/server/fastmcp/prompts/test_base.py rename to tests/server/mcpserver/prompts/test_base.py index afc1ec6ea8..035e1cc81d 100644 --- a/tests/server/fastmcp/prompts/test_base.py +++ b/tests/server/mcpserver/prompts/test_base.py @@ -2,7 +2,7 @@ import pytest -from mcp.server.fastmcp.prompts.base import AssistantMessage, Message, Prompt, TextContent, UserMessage +from mcp.server.mcpserver.prompts.base import AssistantMessage, Message, Prompt, TextContent, UserMessage from mcp.types import EmbeddedResource, TextResourceContents diff --git a/tests/server/fastmcp/prompts/test_manager.py b/tests/server/mcpserver/prompts/test_manager.py similarity index 96% rename from tests/server/fastmcp/prompts/test_manager.py rename to tests/server/mcpserver/prompts/test_manager.py index 950ffddd1a..0e30b2e697 100644 --- a/tests/server/fastmcp/prompts/test_manager.py +++ b/tests/server/mcpserver/prompts/test_manager.py @@ -1,7 +1,7 @@ import pytest -from mcp.server.fastmcp.prompts.base import Prompt, TextContent, UserMessage -from mcp.server.fastmcp.prompts.manager import PromptManager +from mcp.server.mcpserver.prompts.base import Prompt, TextContent, UserMessage +from mcp.server.mcpserver.prompts.manager import PromptManager class TestPromptManager: diff --git a/tests/server/fastmcp/resources/__init__.py b/tests/server/mcpserver/resources/__init__.py similarity index 100% rename from tests/server/fastmcp/resources/__init__.py rename to tests/server/mcpserver/resources/__init__.py diff --git a/tests/server/fastmcp/resources/test_file_resources.py b/tests/server/mcpserver/resources/test_file_resources.py similarity index 98% rename from tests/server/fastmcp/resources/test_file_resources.py rename to tests/server/mcpserver/resources/test_file_resources.py index 39272b051b..94885113a9 100644 --- a/tests/server/fastmcp/resources/test_file_resources.py +++ b/tests/server/mcpserver/resources/test_file_resources.py @@ -4,7 +4,7 @@ import pytest -from mcp.server.fastmcp.resources import FileResource +from mcp.server.mcpserver.resources import FileResource @pytest.fixture diff --git a/tests/server/fastmcp/resources/test_function_resources.py b/tests/server/mcpserver/resources/test_function_resources.py similarity index 98% rename from tests/server/fastmcp/resources/test_function_resources.py rename to tests/server/mcpserver/resources/test_function_resources.py index 61ed44f6c6..5f5c216ed1 100644 --- a/tests/server/fastmcp/resources/test_function_resources.py +++ b/tests/server/mcpserver/resources/test_function_resources.py @@ -1,7 +1,7 @@ import pytest from pydantic import BaseModel -from mcp.server.fastmcp.resources import FunctionResource +from mcp.server.mcpserver.resources import FunctionResource class TestFunctionResource: diff --git a/tests/server/fastmcp/resources/test_resource_manager.py b/tests/server/mcpserver/resources/test_resource_manager.py similarity index 98% rename from tests/server/fastmcp/resources/test_resource_manager.py rename to tests/server/mcpserver/resources/test_resource_manager.py index 3d6dd9be91..eb9b355aaf 100644 --- a/tests/server/fastmcp/resources/test_resource_manager.py +++ b/tests/server/mcpserver/resources/test_resource_manager.py @@ -4,7 +4,7 @@ import pytest from pydantic import AnyUrl -from mcp.server.fastmcp.resources import FileResource, FunctionResource, ResourceManager, ResourceTemplate +from mcp.server.mcpserver.resources import FileResource, FunctionResource, ResourceManager, ResourceTemplate @pytest.fixture diff --git a/tests/server/fastmcp/resources/test_resource_template.py b/tests/server/mcpserver/resources/test_resource_template.py similarity index 97% rename from tests/server/fastmcp/resources/test_resource_template.py rename to tests/server/mcpserver/resources/test_resource_template.py index 022e86db82..246398def2 100644 --- a/tests/server/fastmcp/resources/test_resource_template.py +++ b/tests/server/mcpserver/resources/test_resource_template.py @@ -4,8 +4,8 @@ import pytest from pydantic import BaseModel -from mcp.server.fastmcp import FastMCP -from mcp.server.fastmcp.resources import FunctionResource, ResourceTemplate +from mcp.server.mcpserver import MCPServer +from mcp.server.mcpserver.resources import FunctionResource, ResourceTemplate from mcp.types import Annotations @@ -219,10 +219,10 @@ def get_user_data(user_id: str) -> str: # pragma: no cover assert template.annotations is None @pytest.mark.anyio - async def test_template_annotations_in_fastmcp(self): - """Test template annotations via FastMCP decorator.""" + async def test_template_annotations_in_mcpserver(self): + """Test template annotations via MCPServer decorator.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.resource("resource://dynamic/{id}", annotations=Annotations(audience=["user"], priority=0.7)) def get_dynamic(id: str) -> str: # pragma: no cover diff --git a/tests/server/fastmcp/resources/test_resources.py b/tests/server/mcpserver/resources/test_resources.py similarity index 96% rename from tests/server/fastmcp/resources/test_resources.py rename to tests/server/mcpserver/resources/test_resources.py index 6d346786dc..93dc438d5d 100644 --- a/tests/server/fastmcp/resources/test_resources.py +++ b/tests/server/mcpserver/resources/test_resources.py @@ -1,7 +1,7 @@ import pytest -from mcp.server.fastmcp import FastMCP -from mcp.server.fastmcp.resources import FunctionResource, Resource +from mcp.server.mcpserver import MCPServer +from mcp.server.mcpserver.resources import FunctionResource, Resource from mcp.types import Annotations @@ -130,10 +130,10 @@ def get_data() -> str: # pragma: no cover assert resource.annotations is None @pytest.mark.anyio - async def test_resource_annotations_in_fastmcp(self): - """Test resource annotations via FastMCP decorator.""" + async def test_resource_annotations_in_mcpserver(self): + """Test resource annotations via MCPServer decorator.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.resource("resource://annotated", annotations=Annotations(audience=["assistant"], priority=0.5)) def get_annotated() -> str: # pragma: no cover @@ -150,7 +150,7 @@ def get_annotated() -> str: # pragma: no cover async def test_resource_annotations_with_both_audiences(self): """Test resource with both user and assistant audience.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.resource("resource://both", annotations=Annotations(audience=["user", "assistant"], priority=1.0)) def get_both() -> str: # pragma: no cover diff --git a/tests/server/fastmcp/servers/__init__.py b/tests/server/mcpserver/servers/__init__.py similarity index 100% rename from tests/server/fastmcp/servers/__init__.py rename to tests/server/mcpserver/servers/__init__.py diff --git a/tests/server/fastmcp/servers/test_file_server.py b/tests/server/mcpserver/servers/test_file_server.py similarity index 86% rename from tests/server/fastmcp/servers/test_file_server.py rename to tests/server/mcpserver/servers/test_file_server.py index b8c9ad3d6a..9c3fe265c2 100644 --- a/tests/server/fastmcp/servers/test_file_server.py +++ b/tests/server/mcpserver/servers/test_file_server.py @@ -3,7 +3,7 @@ import pytest -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer @pytest.fixture() @@ -20,14 +20,14 @@ def test_dir(tmp_path_factory: pytest.TempPathFactory) -> Path: @pytest.fixture -def mcp() -> FastMCP: - mcp = FastMCP() +def mcp() -> MCPServer: + mcp = MCPServer() return mcp @pytest.fixture(autouse=True) -def resources(mcp: FastMCP, test_dir: Path) -> FastMCP: +def resources(mcp: MCPServer, test_dir: Path) -> MCPServer: @mcp.resource("dir://test_dir") def list_test_dir() -> list[str]: """List the files in the test directory""" @@ -61,7 +61,7 @@ def read_config_json() -> str: @pytest.fixture(autouse=True) -def tools(mcp: FastMCP, test_dir: Path) -> FastMCP: +def tools(mcp: MCPServer, test_dir: Path) -> MCPServer: @mcp.tool() def delete_file(path: str) -> bool: # ensure path is in test_dir @@ -74,7 +74,7 @@ def delete_file(path: str) -> bool: @pytest.mark.anyio -async def test_list_resources(mcp: FastMCP): +async def test_list_resources(mcp: MCPServer): resources = await mcp.list_resources() assert len(resources) == 4 @@ -87,7 +87,7 @@ async def test_list_resources(mcp: FastMCP): @pytest.mark.anyio -async def test_read_resource_dir(mcp: FastMCP): +async def test_read_resource_dir(mcp: MCPServer): res_iter = await mcp.read_resource("dir://test_dir") res_list = list(res_iter) assert len(res_list) == 1 @@ -104,7 +104,7 @@ async def test_read_resource_dir(mcp: FastMCP): @pytest.mark.anyio -async def test_read_resource_file(mcp: FastMCP): +async def test_read_resource_file(mcp: MCPServer): res_iter = await mcp.read_resource("file://test_dir/example.py") res_list = list(res_iter) assert len(res_list) == 1 @@ -113,13 +113,13 @@ async def test_read_resource_file(mcp: FastMCP): @pytest.mark.anyio -async def test_delete_file(mcp: FastMCP, test_dir: Path): +async def test_delete_file(mcp: MCPServer, test_dir: Path): await mcp.call_tool("delete_file", arguments={"path": str(test_dir / "example.py")}) assert not (test_dir / "example.py").exists() @pytest.mark.anyio -async def test_delete_file_and_check_resources(mcp: FastMCP, test_dir: Path): +async def test_delete_file_and_check_resources(mcp: MCPServer, test_dir: Path): await mcp.call_tool("delete_file", arguments={"path": str(test_dir / "example.py")}) res_iter = await mcp.read_resource("file://test_dir/example.py") res_list = list(res_iter) diff --git a/tests/server/fastmcp/test_elicitation.py b/tests/server/mcpserver/test_elicitation.py similarity index 97% rename from tests/server/fastmcp/test_elicitation.py rename to tests/server/mcpserver/test_elicitation.py index 587dcebb06..5a55592abb 100644 --- a/tests/server/fastmcp/test_elicitation.py +++ b/tests/server/mcpserver/test_elicitation.py @@ -7,7 +7,7 @@ from mcp import Client, types from mcp.client.session import ClientSession, ElicitationFnT -from mcp.server.fastmcp import Context, FastMCP +from mcp.server.mcpserver import Context, MCPServer from mcp.server.session import ServerSession from mcp.shared.context import RequestContext from mcp.types import ElicitRequestParams, ElicitResult, TextContent @@ -18,7 +18,7 @@ class AnswerSchema(BaseModel): answer: str = Field(description="The user's answer to the question") -def create_ask_user_tool(mcp: FastMCP): +def create_ask_user_tool(mcp: MCPServer): """Create a standard ask_user tool that handles all elicitation responses.""" @mcp.tool(description="A tool that uses elicitation") @@ -36,7 +36,7 @@ async def ask_user(prompt: str, ctx: Context[ServerSession, None]) -> str: async def call_tool_and_assert( - mcp: FastMCP, + mcp: MCPServer, elicitation_callback: ElicitationFnT, tool_name: str, args: dict[str, Any], @@ -61,7 +61,7 @@ async def call_tool_and_assert( @pytest.mark.anyio async def test_stdio_elicitation(): """Test the elicitation feature using stdio transport.""" - mcp = FastMCP(name="StdioElicitationServer") + mcp = MCPServer(name="StdioElicitationServer") create_ask_user_tool(mcp) # Create a custom handler for elicitation requests @@ -79,7 +79,7 @@ async def elicitation_callback(context: RequestContext[ClientSession, None], par @pytest.mark.anyio async def test_stdio_elicitation_decline(): """Test elicitation with user declining.""" - mcp = FastMCP(name="StdioElicitationDeclineServer") + mcp = MCPServer(name="StdioElicitationDeclineServer") create_ask_user_tool(mcp) async def elicitation_callback(context: RequestContext[ClientSession, None], params: ElicitRequestParams): @@ -93,7 +93,7 @@ async def elicitation_callback(context: RequestContext[ClientSession, None], par @pytest.mark.anyio async def test_elicitation_schema_validation(): """Test that elicitation schemas must only contain primitive types.""" - mcp = FastMCP(name="ValidationTestServer") + mcp = MCPServer(name="ValidationTestServer") def create_validation_tool(name: str, schema_class: type[BaseModel]): @mcp.tool(name=name, description=f"Tool testing {name}") @@ -138,7 +138,7 @@ async def elicitation_callback( @pytest.mark.anyio async def test_elicitation_with_optional_fields(): """Test that Optional fields work correctly in elicitation schemas.""" - mcp = FastMCP(name="OptionalFieldServer") + mcp = MCPServer(name="OptionalFieldServer") class OptionalSchema(BaseModel): required_name: str = Field(description="Your name (required)") @@ -253,7 +253,7 @@ async def optional_multiselect_callback(context: RequestContext[ClientSession, A @pytest.mark.anyio async def test_elicitation_with_default_values(): """Test that default values work correctly in elicitation schemas and are included in JSON.""" - mcp = FastMCP(name="DefaultValuesServer") + mcp = MCPServer(name="DefaultValuesServer") class DefaultsSchema(BaseModel): name: str = Field(default="Guest", description="User name") @@ -309,7 +309,7 @@ async def callback_override(context: RequestContext[ClientSession, None], params @pytest.mark.anyio async def test_elicitation_with_enum_titles(): """Test elicitation with enum schemas using oneOf/anyOf for titles.""" - mcp = FastMCP(name="ColorPreferencesApp") + mcp = MCPServer(name="ColorPreferencesApp") # Test single-select with titles using oneOf class FavoriteColorSchema(BaseModel): diff --git a/tests/server/fastmcp/test_func_metadata.py b/tests/server/mcpserver/test_func_metadata.py similarity index 99% rename from tests/server/fastmcp/test_func_metadata.py rename to tests/server/mcpserver/test_func_metadata.py index ba1d30d26d..c57d1ee9f0 100644 --- a/tests/server/fastmcp/test_func_metadata.py +++ b/tests/server/mcpserver/test_func_metadata.py @@ -12,8 +12,8 @@ from dirty_equals import IsPartialDict from pydantic import BaseModel, Field -from mcp.server.fastmcp.exceptions import InvalidSignature -from mcp.server.fastmcp.utilities.func_metadata import func_metadata +from mcp.server.mcpserver.exceptions import InvalidSignature +from mcp.server.mcpserver.utilities.func_metadata import func_metadata from mcp.types import CallToolResult diff --git a/tests/server/fastmcp/test_integration.py b/tests/server/mcpserver/test_integration.py similarity index 98% rename from tests/server/fastmcp/test_integration.py rename to tests/server/mcpserver/test_integration.py index a7f945f782..4d624c68e6 100644 --- a/tests/server/fastmcp/test_integration.py +++ b/tests/server/mcpserver/test_integration.py @@ -1,6 +1,6 @@ -"""Integration tests for FastMCP server functionality. +"""Integration tests for MCPServer server functionality. -These tests validate the proper functioning of FastMCP features using focused, +These tests validate the proper functioning of MCPServer features using focused, single-feature servers across different transports (SSE and StreamableHTTP). """ # TODO(Marcelo): The `examples` package is not being imported as package. We need to solve this. @@ -25,7 +25,7 @@ basic_tool, completion, elicitation, - fastmcp_quickstart, + mcpserver_quickstart, notifications, sampling, structured_output, @@ -121,8 +121,8 @@ def run_server_with_transport(module_name: str, port: int, transport: str) -> No mcp = completion.mcp elif module_name == "notifications": mcp = notifications.mcp - elif module_name == "fastmcp_quickstart": - mcp = fastmcp_quickstart.mcp + elif module_name == "mcpserver_quickstart": + mcp = mcpserver_quickstart.mcp elif module_name == "structured_output": mcp = structured_output.mcp else: @@ -608,18 +608,18 @@ async def test_completion(server_transport: str, server_url: str) -> None: assert all(lang.startswith("py") for lang in completion_result.completion.values) -# Test FastMCP quickstart example +# Test MCPServer quickstart example @pytest.mark.anyio @pytest.mark.parametrize( "server_transport", [ - ("fastmcp_quickstart", "sse"), - ("fastmcp_quickstart", "streamable-http"), + ("mcpserver_quickstart", "sse"), + ("mcpserver_quickstart", "streamable-http"), ], indirect=True, ) -async def test_fastmcp_quickstart(server_transport: str, server_url: str) -> None: - """Test FastMCP quickstart example.""" +async def test_mcpserver_quickstart(server_transport: str, server_url: str) -> None: + """Test MCPServer quickstart example.""" transport = server_transport client_cm = create_client_for_transport(transport, server_url) diff --git a/tests/server/fastmcp/test_parameter_descriptions.py b/tests/server/mcpserver/test_parameter_descriptions.py similarity index 91% rename from tests/server/fastmcp/test_parameter_descriptions.py rename to tests/server/mcpserver/test_parameter_descriptions.py index 340ca71603..ec9f22c259 100644 --- a/tests/server/fastmcp/test_parameter_descriptions.py +++ b/tests/server/mcpserver/test_parameter_descriptions.py @@ -3,12 +3,12 @@ import pytest from pydantic import Field -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer @pytest.mark.anyio async def test_parameter_descriptions(): - mcp = FastMCP("Test Server") + mcp = MCPServer("Test Server") @mcp.tool() def greet( diff --git a/tests/server/fastmcp/test_server.py b/tests/server/mcpserver/test_server.py similarity index 95% rename from tests/server/fastmcp/test_server.py rename to tests/server/mcpserver/test_server.py index 5b6030be96..9dfaefebf2 100644 --- a/tests/server/fastmcp/test_server.py +++ b/tests/server/mcpserver/test_server.py @@ -9,11 +9,11 @@ from starlette.routing import Mount, Route from mcp.client import Client -from mcp.server.fastmcp import Context, FastMCP -from mcp.server.fastmcp.exceptions import ToolError -from mcp.server.fastmcp.prompts.base import Message, UserMessage -from mcp.server.fastmcp.resources import FileResource, FunctionResource -from mcp.server.fastmcp.utilities.types import Audio, Image +from mcp.server.mcpserver import Context, MCPServer +from mcp.server.mcpserver.exceptions import ToolError +from mcp.server.mcpserver.prompts.base import Message, UserMessage +from mcp.server.mcpserver.resources import FileResource, FunctionResource +from mcp.server.mcpserver.utilities.types import Audio, Image from mcp.server.session import ServerSession from mcp.server.transport_security import TransportSecuritySettings from mcp.shared.exceptions import McpError @@ -32,16 +32,16 @@ class TestServer: @pytest.mark.anyio async def test_create_server(self): - mcp = FastMCP( - title="FastMCP Server", + mcp = MCPServer( + title="MCPServer Server", description="Server description", instructions="Server instructions", website_url="https://example.com/mcp_server", version="1.0", icons=[Icon(src="https://example.com/icon.png", mime_type="image/png", sizes=["48x48", "96x96"])], ) - assert mcp.name == "FastMCP" - assert mcp.title == "FastMCP Server" + assert mcp.name == "mcp-server" + assert mcp.title == "MCPServer Server" assert mcp.description == "Server description" assert mcp.instructions == "Server instructions" assert mcp.website_url == "https://example.com/mcp_server" @@ -53,7 +53,7 @@ async def test_create_server(self): @pytest.mark.anyio async def test_sse_app_returns_starlette_app(self): """Test that sse_app returns a Starlette application with correct routes.""" - mcp = FastMCP("test") + mcp = MCPServer("test") # Use host="0.0.0.0" to avoid auto DNS protection app = mcp.sse_app(host="0.0.0.0") @@ -70,8 +70,8 @@ async def test_sse_app_returns_starlette_app(self): @pytest.mark.anyio async def test_non_ascii_description(self): - """Test that FastMCP handles non-ASCII characters in descriptions correctly""" - mcp = FastMCP() + """Test that MCPServer handles non-ASCII characters in descriptions correctly""" + mcp = MCPServer() @mcp.tool(description=("🌟 This tool uses emojis and UTF-8 characters: á é í ó ú ñ 漢字 🎉")) def hello_world(name: str = "世界") -> str: @@ -94,7 +94,7 @@ def hello_world(name: str = "世界") -> str: @pytest.mark.anyio async def test_add_tool_decorator(self): - mcp = FastMCP() + mcp = MCPServer() @mcp.tool() def sum(x: int, y: int) -> int: # pragma: no cover @@ -104,7 +104,7 @@ def sum(x: int, y: int) -> int: # pragma: no cover @pytest.mark.anyio async def test_add_tool_decorator_incorrect_usage(self): - mcp = FastMCP() + mcp = MCPServer() with pytest.raises(TypeError, match="The @tool decorator was used incorrectly"): @@ -114,7 +114,7 @@ def sum(x: int, y: int) -> int: # pragma: no cover @pytest.mark.anyio async def test_add_resource_decorator(self): - mcp = FastMCP() + mcp = MCPServer() @mcp.resource("r://{x}") def get_data(x: str) -> str: # pragma: no cover @@ -124,7 +124,7 @@ def get_data(x: str) -> str: # pragma: no cover @pytest.mark.anyio async def test_add_resource_decorator_incorrect_usage(self): - mcp = FastMCP() + mcp = MCPServer() with pytest.raises(TypeError, match="The @resource decorator was used incorrectly"): @@ -142,7 +142,7 @@ class TestDnsRebindingProtection: def test_auto_enabled_for_127_0_0_1_sse(self): """DNS rebinding protection should auto-enable for host=127.0.0.1 in SSE app.""" - mcp = FastMCP() + mcp = MCPServer() # Call sse_app with host=127.0.0.1 to trigger auto-config # We can't directly inspect the transport_security, but we can verify # the app is created without error @@ -151,25 +151,25 @@ def test_auto_enabled_for_127_0_0_1_sse(self): def test_auto_enabled_for_127_0_0_1_streamable_http(self): """DNS rebinding protection should auto-enable for host=127.0.0.1 in StreamableHTTP app.""" - mcp = FastMCP() + mcp = MCPServer() app = mcp.streamable_http_app(host="127.0.0.1") assert app is not None def test_auto_enabled_for_localhost_sse(self): """DNS rebinding protection should auto-enable for host=localhost in SSE app.""" - mcp = FastMCP() + mcp = MCPServer() app = mcp.sse_app(host="localhost") assert app is not None def test_auto_enabled_for_ipv6_localhost_sse(self): """DNS rebinding protection should auto-enable for host=::1 (IPv6 localhost) in SSE app.""" - mcp = FastMCP() + mcp = MCPServer() app = mcp.sse_app(host="::1") assert app is not None def test_not_auto_enabled_for_other_hosts_sse(self): """DNS rebinding protection should NOT auto-enable for other hosts in SSE app.""" - mcp = FastMCP() + mcp = MCPServer() app = mcp.sse_app(host="0.0.0.0") assert app is not None @@ -178,7 +178,7 @@ def test_explicit_settings_not_overridden_sse(self): custom_settings = TransportSecuritySettings( enable_dns_rebinding_protection=False, ) - mcp = FastMCP() + mcp = MCPServer() # Explicit transport_security passed to sse_app should be used as-is app = mcp.sse_app(host="127.0.0.1", transport_security=custom_settings) assert app is not None @@ -188,7 +188,7 @@ def test_explicit_settings_not_overridden_streamable_http(self): custom_settings = TransportSecuritySettings( enable_dns_rebinding_protection=False, ) - mcp = FastMCP() + mcp = MCPServer() # Explicit transport_security passed to streamable_http_app should be used as-is app = mcp.streamable_http_app(host="127.0.0.1", transport_security=custom_settings) assert app is not None @@ -221,14 +221,14 @@ def mixed_content_tool_fn() -> list[ContentBlock]: class TestServerTools: @pytest.mark.anyio async def test_add_tool(self): - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(tool_fn) mcp.add_tool(tool_fn) assert len(mcp._tool_manager.list_tools()) == 1 @pytest.mark.anyio async def test_list_tools(self): - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(tool_fn) async with Client(mcp) as client: tools = await client.list_tools() @@ -236,7 +236,7 @@ async def test_list_tools(self): @pytest.mark.anyio async def test_call_tool(self): - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(tool_fn) async with Client(mcp) as client: result = await client.call_tool("my_tool", {"arg1": "value"}) @@ -245,7 +245,7 @@ async def test_call_tool(self): @pytest.mark.anyio async def test_tool_exception_handling(self): - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(error_tool_fn) async with Client(mcp) as client: result = await client.call_tool("error_tool_fn", {}) @@ -257,7 +257,7 @@ async def test_tool_exception_handling(self): @pytest.mark.anyio async def test_tool_error_handling(self): - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(error_tool_fn) async with Client(mcp) as client: result = await client.call_tool("error_tool_fn", {}) @@ -270,7 +270,7 @@ async def test_tool_error_handling(self): @pytest.mark.anyio async def test_tool_error_details(self): """Test that exception details are properly formatted in the response""" - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(error_tool_fn) async with Client(mcp) as client: result = await client.call_tool("error_tool_fn", {}) @@ -282,7 +282,7 @@ async def test_tool_error_details(self): @pytest.mark.anyio async def test_tool_return_value_conversion(self): - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(tool_fn) async with Client(mcp) as client: result = await client.call_tool("tool_fn", {"x": 1, "y": 2}) @@ -300,7 +300,7 @@ async def test_tool_image_helper(self, tmp_path: Path): image_path = tmp_path / "test.png" image_path.write_bytes(b"fake png data") - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(image_tool_fn) async with Client(mcp) as client: result = await client.call_tool("image_tool_fn", {"path": str(image_path)}) @@ -321,7 +321,7 @@ async def test_tool_audio_helper(self, tmp_path: Path): audio_path = tmp_path / "test.wav" audio_path.write_bytes(b"fake wav data") - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(audio_tool_fn) async with Client(mcp) as client: result = await client.call_tool("audio_tool_fn", {"path": str(audio_path)}) @@ -351,7 +351,7 @@ async def test_tool_audio_helper(self, tmp_path: Path): @pytest.mark.anyio async def test_tool_audio_suffix_detection(self, tmp_path: Path, filename: str, expected_mime_type: str): """Test that Audio helper correctly detects MIME types from file suffixes""" - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(audio_tool_fn) # Create a test audio file with the specific extension @@ -371,7 +371,7 @@ async def test_tool_audio_suffix_detection(self, tmp_path: Path, filename: str, @pytest.mark.anyio async def test_tool_mixed_content(self): - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(mixed_content_tool_fn) async with Client(mcp) as client: result = await client.call_tool("mixed_content_tool_fn", {}) @@ -423,7 +423,7 @@ def mixed_list_fn() -> list: # type: ignore TextContent(type="text", text="direct content"), ] - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(mixed_list_fn) # type: ignore async with Client(mcp) as client: result = await client.call_tool("mixed_list_fn", {}) @@ -466,7 +466,7 @@ def get_user(user_id: int) -> UserOutput: """Get user by ID""" return UserOutput(name="John Doe", age=30) - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(get_user) async with Client(mcp) as client: @@ -496,7 +496,7 @@ def calculate_sum(a: int, b: int) -> int: """Add two numbers""" return a + b - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(calculate_sum) async with Client(mcp) as client: @@ -523,7 +523,7 @@ def get_numbers() -> list[int]: """Get a list of numbers""" return [1, 2, 3, 4, 5] - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(get_numbers) async with Client(mcp) as client: @@ -539,7 +539,7 @@ async def test_tool_structured_output_server_side_validation_error(self): def get_numbers() -> list[int]: return [1, 2, 3, 4, [5]] # type: ignore - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(get_numbers) async with Client(mcp) as client: @@ -563,7 +563,7 @@ def get_metadata() -> dict[str, Any]: "config": {"nested": {"value": 123}}, } - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(get_metadata) async with Client(mcp) as client: @@ -599,7 +599,7 @@ def get_settings() -> dict[str, str]: """Get settings as string dictionary""" return {"theme": "dark", "language": "en", "timezone": "UTC"} - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(get_settings) async with Client(mcp) as client: @@ -618,7 +618,7 @@ def get_settings() -> dict[str, str]: @pytest.mark.anyio async def test_remove_tool(self): """Test removing a tool from the server.""" - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(tool_fn) # Verify tool exists @@ -633,7 +633,7 @@ async def test_remove_tool(self): @pytest.mark.anyio async def test_remove_nonexistent_tool(self): """Test that removing a non-existent tool raises ToolError.""" - mcp = FastMCP() + mcp = MCPServer() with pytest.raises(ToolError, match="Unknown tool: nonexistent"): mcp.remove_tool("nonexistent") @@ -641,7 +641,7 @@ async def test_remove_nonexistent_tool(self): @pytest.mark.anyio async def test_remove_tool_and_list(self): """Test that a removed tool doesn't appear in list_tools.""" - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(tool_fn) mcp.add_tool(error_tool_fn) @@ -665,7 +665,7 @@ async def test_remove_tool_and_list(self): @pytest.mark.anyio async def test_remove_tool_and_call(self): """Test that calling a removed tool fails appropriately.""" - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(tool_fn) # Verify tool works before removal @@ -691,7 +691,7 @@ async def test_remove_tool_and_call(self): class TestServerResources: @pytest.mark.anyio async def test_text_resource(self): - mcp = FastMCP() + mcp = MCPServer() def get_text(): return "Hello, world!" @@ -710,7 +710,7 @@ def get_text(): @pytest.mark.anyio async def test_binary_resource(self): - mcp = FastMCP() + mcp = MCPServer() def get_binary(): return b"Binary data" @@ -734,7 +734,7 @@ def get_binary(): @pytest.mark.anyio async def test_file_resource_text(self, tmp_path: Path): - mcp = FastMCP() + mcp = MCPServer() # Create a text file text_file = tmp_path / "test.txt" @@ -754,7 +754,7 @@ async def test_file_resource_text(self, tmp_path: Path): @pytest.mark.anyio async def test_file_resource_binary(self, tmp_path: Path): - mcp = FastMCP() + mcp = MCPServer() # Create a binary file binary_file = tmp_path / "test.bin" @@ -779,7 +779,7 @@ async def test_file_resource_binary(self, tmp_path: Path): @pytest.mark.anyio async def test_function_resource(self): - mcp = FastMCP() + mcp = MCPServer() @mcp.resource("function://test", name="test_get_data") def get_data() -> str: # pragma: no cover @@ -801,7 +801,7 @@ class TestServerResourceTemplates: async def test_resource_with_params(self): """Test that a resource with function parameters raises an error if the URI parameters don't match""" - mcp = FastMCP() + mcp = MCPServer() with pytest.raises(ValueError, match="Mismatch between URI parameters"): @@ -812,7 +812,7 @@ def get_data_fn(param: str) -> str: # pragma: no cover @pytest.mark.anyio async def test_resource_with_uri_params(self): """Test that a resource with URI parameters is automatically a template""" - mcp = FastMCP() + mcp = MCPServer() with pytest.raises(ValueError, match="Mismatch between URI parameters"): @@ -823,7 +823,7 @@ def get_data() -> str: # pragma: no cover @pytest.mark.anyio async def test_resource_with_untyped_params(self): """Test that a resource with untyped parameters raises an error""" - mcp = FastMCP() + mcp = MCPServer() @mcp.resource("resource://{param}") def get_data(param) -> str: # type: ignore # pragma: no cover @@ -832,7 +832,7 @@ def get_data(param) -> str: # type: ignore # pragma: no cover @pytest.mark.anyio async def test_resource_matching_params(self): """Test that a resource with matching URI and function parameters works""" - mcp = FastMCP() + mcp = MCPServer() @mcp.resource("resource://{name}/data") def get_data(name: str) -> str: @@ -850,7 +850,7 @@ def get_data(name: str) -> str: @pytest.mark.anyio async def test_resource_mismatched_params(self): """Test that mismatched parameters raise an error""" - mcp = FastMCP() + mcp = MCPServer() with pytest.raises(ValueError, match="Mismatch between URI parameters"): @@ -861,25 +861,25 @@ def get_data(user: str) -> str: # pragma: no cover @pytest.mark.anyio async def test_resource_multiple_params(self): """Test that multiple parameters work correctly""" - mcp = FastMCP() + mcp = MCPServer() @mcp.resource("resource://{org}/{repo}/data") def get_data(org: str, repo: str) -> str: return f"Data for {org}/{repo}" async with Client(mcp) as client: - result = await client.read_resource("resource://cursor/fastmcp/data") + result = await client.read_resource("resource://cursor/myrepo/data") async with Client(mcp) as client: - result = await client.read_resource("resource://cursor/fastmcp/data") + result = await client.read_resource("resource://cursor/myrepo/data") assert isinstance(result.contents[0], TextResourceContents) - assert result.contents[0].text == "Data for cursor/fastmcp" + assert result.contents[0].text == "Data for cursor/myrepo" @pytest.mark.anyio async def test_resource_multiple_mismatched_params(self): """Test that mismatched parameters raise an error""" - mcp = FastMCP() + mcp = MCPServer() with pytest.raises(ValueError, match="Mismatch between URI parameters"): @@ -888,7 +888,7 @@ def get_data_mismatched(org: str, repo_2: str) -> str: # pragma: no cover return f"Data for {org}" """Test that a resource with no parameters works as a regular resource""" - mcp = FastMCP() + mcp = MCPServer() @mcp.resource("resource://static") def get_static_data() -> str: @@ -906,7 +906,7 @@ def get_static_data() -> str: @pytest.mark.anyio async def test_template_to_resource_conversion(self): """Test that templates are properly converted to resources when accessed""" - mcp = FastMCP() + mcp = MCPServer() @mcp.resource("resource://{name}/data") def get_data(name: str) -> str: @@ -925,7 +925,7 @@ def get_data(name: str) -> str: @pytest.mark.anyio async def test_resource_template_includes_mime_type(self): """Test that list resource templates includes the correct mimeType.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.resource("resource://{user}/csv", mime_type="text/csv") def get_csv(user: str) -> str: @@ -949,7 +949,7 @@ def get_csv(user: str) -> str: class TestServerResourceMetadata: - """Test FastMCP @resource decorator meta parameter for list operations. + """Test MCPServer @resource decorator meta parameter for list operations. Meta flows: @resource decorator -> resource/template storage -> list_resources/list_resource_templates. Note: read_resource does NOT pass meta to protocol response (lowlevel/server.py only extracts content/mime_type). @@ -959,7 +959,7 @@ class TestServerResourceMetadata: async def test_resource_decorator_with_metadata(self): """Test that @resource decorator accepts and passes meta parameter.""" # Tests static resource flow: decorator -> FunctionResource -> list_resources (server.py:544,635,361) - mcp = FastMCP() + mcp = MCPServer() metadata = {"ui": {"component": "file-viewer"}, "priority": "high"} @@ -978,7 +978,7 @@ def get_config() -> str: # pragma: no cover async def test_resource_template_decorator_with_metadata(self): """Test that @resource decorator passes meta to templates.""" # Tests template resource flow: decorator -> add_template() -> list_resource_templates (server.py:544,622,377) - mcp = FastMCP() + mcp = MCPServer() metadata = {"api_version": "v2", "deprecated": False} @@ -996,7 +996,7 @@ def get_weather(city: str) -> str: # pragma: no cover async def test_read_resource_returns_meta(self): """Test that read_resource includes meta in response.""" # Tests end-to-end: Resource.meta -> ReadResourceContents.meta -> protocol _meta (lowlevel/server.py:341,371) - mcp = FastMCP() + mcp = MCPServer() metadata = {"version": "1.0", "category": "config"} @@ -1025,7 +1025,7 @@ class TestContextInjection: @pytest.mark.anyio async def test_context_detection(self): """Test that context parameters are properly detected.""" - mcp = FastMCP() + mcp = MCPServer() def tool_with_context(x: int, ctx: Context[ServerSession, None]) -> str: # pragma: no cover return f"Request {ctx.request_id}: {x}" @@ -1036,7 +1036,7 @@ def tool_with_context(x: int, ctx: Context[ServerSession, None]) -> str: # prag @pytest.mark.anyio async def test_context_injection(self): """Test that context is properly injected into tool calls.""" - mcp = FastMCP() + mcp = MCPServer() def tool_with_context(x: int, ctx: Context[ServerSession, None]) -> str: assert ctx.request_id is not None @@ -1054,7 +1054,7 @@ def tool_with_context(x: int, ctx: Context[ServerSession, None]) -> str: @pytest.mark.anyio async def test_async_context(self): """Test that context works in async functions.""" - mcp = FastMCP() + mcp = MCPServer() async def async_tool(x: int, ctx: Context[ServerSession, None]) -> str: assert ctx.request_id is not None @@ -1072,7 +1072,7 @@ async def async_tool(x: int, ctx: Context[ServerSession, None]) -> str: @pytest.mark.anyio async def test_context_logging(self): """Test that context logging methods work.""" - mcp = FastMCP() + mcp = MCPServer() async def logging_tool(msg: str, ctx: Context[ServerSession, None]) -> str: await ctx.debug("Debug message") @@ -1120,7 +1120,7 @@ async def logging_tool(msg: str, ctx: Context[ServerSession, None]) -> str: @pytest.mark.anyio async def test_optional_context(self): """Test that context is optional.""" - mcp = FastMCP() + mcp = MCPServer() def no_context(x: int) -> int: return x * 2 @@ -1136,7 +1136,7 @@ def no_context(x: int) -> int: @pytest.mark.anyio async def test_context_resource_access(self): """Test that context can access resources.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.resource("test://data") def test_resource() -> str: @@ -1160,7 +1160,7 @@ async def tool_with_resource(ctx: Context[ServerSession, None]) -> str: @pytest.mark.anyio async def test_resource_with_context(self): """Test that resources can receive context parameter.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.resource("resource://context/{name}") def resource_with_context(name: str, ctx: Context[ServerSession, None]) -> str: @@ -1192,7 +1192,7 @@ def resource_with_context(name: str, ctx: Context[ServerSession, None]) -> str: @pytest.mark.anyio async def test_resource_without_context(self): """Test that resources without context work normally.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.resource("resource://nocontext/{name}") def resource_no_context(name: str) -> str: @@ -1221,7 +1221,7 @@ def resource_no_context(name: str) -> str: @pytest.mark.anyio async def test_resource_context_custom_name(self): """Test resource context with custom parameter name.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.resource("resource://custom/{id}") def resource_custom_ctx(id: str, my_ctx: Context[ServerSession, None]) -> str: @@ -1251,7 +1251,7 @@ def resource_custom_ctx(id: str, my_ctx: Context[ServerSession, None]) -> str: @pytest.mark.anyio async def test_prompt_with_context(self): """Test that prompts can receive context parameter.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.prompt("prompt_with_ctx") def prompt_with_context(text: str, ctx: Context[ServerSession, None]) -> str: @@ -1276,7 +1276,7 @@ def prompt_with_context(text: str, ctx: Context[ServerSession, None]) -> str: @pytest.mark.anyio async def test_prompt_without_context(self): """Test that prompts without context work normally.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.prompt("prompt_no_ctx") def prompt_no_context(text: str) -> str: @@ -1294,12 +1294,12 @@ def prompt_no_context(text: str) -> str: class TestServerPrompts: - """Test prompt functionality in FastMCP server.""" + """Test prompt functionality in MCPServer server.""" @pytest.mark.anyio async def test_prompt_decorator(self): """Test that the prompt decorator registers prompts correctly.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.prompt() def fn() -> str: @@ -1316,7 +1316,7 @@ def fn() -> str: @pytest.mark.anyio async def test_prompt_decorator_with_name(self): """Test prompt decorator with custom name.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.prompt(name="custom_name") def fn() -> str: @@ -1332,7 +1332,7 @@ def fn() -> str: @pytest.mark.anyio async def test_prompt_decorator_with_description(self): """Test prompt decorator with custom description.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.prompt(description="A custom description") def fn() -> str: @@ -1347,7 +1347,7 @@ def fn() -> str: def test_prompt_decorator_error(self): """Test error when decorator is used incorrectly.""" - mcp = FastMCP() + mcp = MCPServer() with pytest.raises(TypeError, match="decorator was used incorrectly"): @mcp.prompt # type: ignore @@ -1357,7 +1357,7 @@ def fn() -> str: # pragma: no cover @pytest.mark.anyio async def test_list_prompts(self): """Test listing prompts through MCP protocol.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.prompt() def fn(name: str, optional: str = "default") -> str: # pragma: no cover @@ -1379,7 +1379,7 @@ def fn(name: str, optional: str = "default") -> str: # pragma: no cover @pytest.mark.anyio async def test_get_prompt(self): """Test getting a prompt through MCP protocol.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.prompt() def fn(name: str) -> str: @@ -1397,7 +1397,7 @@ def fn(name: str) -> str: @pytest.mark.anyio async def test_get_prompt_with_description(self): """Test getting a prompt through MCP protocol.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.prompt(description="Test prompt description") def fn(name: str) -> str: @@ -1410,7 +1410,7 @@ def fn(name: str) -> str: @pytest.mark.anyio async def test_get_prompt_without_description(self): """Test getting a prompt without description returns empty string.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.prompt() def fn(name: str) -> str: @@ -1423,7 +1423,7 @@ def fn(name: str) -> str: @pytest.mark.anyio async def test_get_prompt_with_docstring_description(self): """Test prompt uses docstring as description when not explicitly provided.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.prompt() def fn(name: str) -> str: @@ -1437,7 +1437,7 @@ def fn(name: str) -> str: @pytest.mark.anyio async def test_get_prompt_with_resource(self): """Test getting a prompt that returns resource content.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.prompt() def fn() -> Message: @@ -1467,7 +1467,7 @@ def fn() -> Message: @pytest.mark.anyio async def test_get_unknown_prompt(self): """Test error when getting unknown prompt.""" - mcp = FastMCP() + mcp = MCPServer() async with Client(mcp) as client: with pytest.raises(McpError, match="Unknown prompt"): await client.get_prompt("unknown") @@ -1475,7 +1475,7 @@ async def test_get_unknown_prompt(self): @pytest.mark.anyio async def test_get_prompt_missing_args(self): """Test error when required arguments are missing.""" - mcp = FastMCP() + mcp = MCPServer() @mcp.prompt() def prompt_fn(name: str) -> str: # pragma: no cover @@ -1488,7 +1488,7 @@ def prompt_fn(name: str) -> str: # pragma: no cover def test_streamable_http_no_redirect() -> None: """Test that streamable HTTP routes are correctly configured.""" - mcp = FastMCP() + mcp = MCPServer() # streamable_http_path defaults to "/mcp" app = mcp.streamable_http_app() diff --git a/tests/server/fastmcp/test_title.py b/tests/server/mcpserver/test_title.py similarity index 97% rename from tests/server/fastmcp/test_title.py rename to tests/server/mcpserver/test_title.py index 2cb1173b3e..6624647572 100644 --- a/tests/server/fastmcp/test_title.py +++ b/tests/server/mcpserver/test_title.py @@ -3,8 +3,8 @@ import pytest from mcp import Client -from mcp.server.fastmcp import FastMCP -from mcp.server.fastmcp.resources import FunctionResource +from mcp.server.mcpserver import MCPServer +from mcp.server.mcpserver.resources import FunctionResource from mcp.shared.metadata_utils import get_display_name from mcp.types import Prompt, Resource, ResourceTemplate, Tool, ToolAnnotations @@ -12,7 +12,7 @@ @pytest.mark.anyio async def test_server_name_title_description_version(): """Test that server title and description are set and retrievable correctly.""" - mcp = FastMCP( + mcp = MCPServer( name="TestServer", title="Test Server Title", description="This is a test server description.", @@ -37,7 +37,7 @@ async def test_server_name_title_description_version(): async def test_tool_title_precedence(): """Test that tool title precedence works correctly: title > annotations.title > name.""" # Create server with various tool configurations - mcp = FastMCP(name="TitleTestServer") + mcp = MCPServer(name="TitleTestServer") # Tool with only name @mcp.tool(description="Basic tool") @@ -90,7 +90,7 @@ def tool_with_both(message: str) -> str: # pragma: no cover @pytest.mark.anyio async def test_prompt_title(): """Test that prompt titles work correctly.""" - mcp = FastMCP(name="PromptTitleServer") + mcp = MCPServer(name="PromptTitleServer") # Prompt with only name @mcp.prompt(description="Basic prompt") @@ -123,7 +123,7 @@ def titled_prompt(topic: str) -> str: # pragma: no cover @pytest.mark.anyio async def test_resource_title(): """Test that resource titles work correctly.""" - mcp = FastMCP(name="ResourceTitleServer") + mcp = MCPServer(name="ResourceTitleServer") # Static resource without title def get_basic_data() -> str: # pragma: no cover diff --git a/tests/server/fastmcp/test_tool_manager.py b/tests/server/mcpserver/test_tool_manager.py similarity index 97% rename from tests/server/fastmcp/test_tool_manager.py rename to tests/server/mcpserver/test_tool_manager.py index 5ac1bf282f..42cac073c7 100644 --- a/tests/server/fastmcp/test_tool_manager.py +++ b/tests/server/mcpserver/test_tool_manager.py @@ -6,10 +6,10 @@ import pytest from pydantic import BaseModel -from mcp.server.fastmcp import Context, FastMCP -from mcp.server.fastmcp.exceptions import ToolError -from mcp.server.fastmcp.tools import Tool, ToolManager -from mcp.server.fastmcp.utilities.func_metadata import ArgModelBase, FuncMetadata +from mcp.server.mcpserver import Context, MCPServer +from mcp.server.mcpserver.exceptions import ToolError +from mcp.server.mcpserver.tools import Tool, ToolManager +from mcp.server.mcpserver.utilities.func_metadata import ArgModelBase, FuncMetadata from mcp.server.session import ServerSessionT from mcp.shared.context import LifespanContextT, RequestT from mcp.types import TextContent, ToolAnnotations @@ -366,7 +366,7 @@ def tool_with_context(x: int, ctx: Context[ServerSessionT, None]) -> str: manager = ToolManager() manager.add_tool(tool_with_context) - mcp = FastMCP() + mcp = MCPServer() ctx = mcp.get_context() result = await manager.call_tool("tool_with_context", {"x": 42}, context=ctx) assert result == "42" @@ -382,7 +382,7 @@ async def async_tool(x: int, ctx: Context[ServerSessionT, None]) -> str: manager = ToolManager() manager.add_tool(async_tool) - mcp = FastMCP() + mcp = MCPServer() ctx = mcp.get_context() result = await manager.call_tool("async_tool", {"x": 42}, context=ctx) assert result == "42" @@ -410,7 +410,7 @@ def tool_with_context(x: int, ctx: Context[ServerSessionT, None]) -> str: manager = ToolManager() manager.add_tool(tool_with_context) - mcp = FastMCP() + mcp = MCPServer() ctx = mcp.get_context() with pytest.raises(ToolError, match="Error executing tool tool_with_context"): await manager.call_tool("tool_with_context", {"x": 42}, context=ctx) @@ -439,10 +439,10 @@ def read_data(path: str) -> str: # pragma: no cover assert tool.annotations.open_world_hint is False @pytest.mark.anyio - async def test_tool_annotations_in_fastmcp(self): + async def test_tool_annotations_in_mcpserver(self): """Test that tool annotations are included in MCPTool conversion.""" - app = FastMCP() + app = MCPServer() @app.tool(annotations=ToolAnnotations(title="Echo Tool", read_only_hint=True)) def echo(message: str) -> str: # pragma: no cover @@ -670,10 +670,10 @@ def simple_tool(x: int) -> int: # pragma: no cover assert tool.meta is None @pytest.mark.anyio - async def test_metadata_in_fastmcp_decorator(self): - """Test that metadata is correctly added via FastMCP.tool decorator.""" + async def test_metadata_in_mcpserver_decorator(self): + """Test that metadata is correctly added via MCPServer.tool decorator.""" - app = FastMCP() + app = MCPServer() metadata = {"client": {"ui_component": "file_picker"}, "priority": "high"} @@ -694,7 +694,7 @@ def upload_file(filename: str) -> str: # pragma: no cover async def test_metadata_in_list_tools(self): """Test that metadata is included in MCPTool when listing tools.""" - app = FastMCP() + app = MCPServer() metadata = { "ui": {"input_type": "textarea", "rows": 5}, @@ -715,7 +715,7 @@ def analyze_text(text: str) -> dict[str, Any]: # pragma: no cover async def test_multiple_tools_with_different_metadata(self): """Test multiple tools with different metadata values.""" - app = FastMCP() + app = MCPServer() metadata1 = {"ui": "form", "version": 1} metadata2 = {"ui": "picker", "experimental": True} @@ -791,7 +791,7 @@ def tool_with_empty_meta(x: int) -> int: # pragma: no cover async def test_metadata_with_annotations(self): """Test that metadata and annotations can coexist.""" - app = FastMCP() + app = MCPServer() metadata = {"custom": "value"} annotations = ToolAnnotations(title="Combined Tool", read_only_hint=True) diff --git a/tests/server/fastmcp/test_url_elicitation.py b/tests/server/mcpserver/test_url_elicitation.py similarity index 96% rename from tests/server/fastmcp/test_url_elicitation.py rename to tests/server/mcpserver/test_url_elicitation.py index cade2aa564..45ec40a370 100644 --- a/tests/server/fastmcp/test_url_elicitation.py +++ b/tests/server/mcpserver/test_url_elicitation.py @@ -7,7 +7,7 @@ from mcp import Client, types from mcp.client.session import ClientSession from mcp.server.elicitation import CancelledElicitation, DeclinedElicitation, elicit_url -from mcp.server.fastmcp import Context, FastMCP +from mcp.server.mcpserver import Context, MCPServer from mcp.server.session import ServerSession from mcp.shared.context import RequestContext from mcp.types import ElicitRequestParams, ElicitResult, TextContent @@ -16,7 +16,7 @@ @pytest.mark.anyio async def test_url_elicitation_accept(): """Test URL mode elicitation with user acceptance.""" - mcp = FastMCP(name="URLElicitationServer") + mcp = MCPServer(name="URLElicitationServer") @mcp.tool(description="A tool that uses URL elicitation") async def request_api_key(ctx: Context[ServerSession, None]) -> str: @@ -46,7 +46,7 @@ async def elicitation_callback(context: RequestContext[ClientSession, None], par @pytest.mark.anyio async def test_url_elicitation_decline(): """Test URL mode elicitation with user declining.""" - mcp = FastMCP(name="URLElicitationDeclineServer") + mcp = MCPServer(name="URLElicitationDeclineServer") @mcp.tool(description="A tool that uses URL elicitation") async def oauth_flow(ctx: Context[ServerSession, None]) -> str: @@ -72,7 +72,7 @@ async def elicitation_callback(context: RequestContext[ClientSession, None], par @pytest.mark.anyio async def test_url_elicitation_cancel(): """Test URL mode elicitation with user cancelling.""" - mcp = FastMCP(name="URLElicitationCancelServer") + mcp = MCPServer(name="URLElicitationCancelServer") @mcp.tool(description="A tool that uses URL elicitation") async def payment_flow(ctx: Context[ServerSession, None]) -> str: @@ -98,7 +98,7 @@ async def elicitation_callback(context: RequestContext[ClientSession, None], par @pytest.mark.anyio async def test_url_elicitation_helper_function(): """Test the elicit_url helper function.""" - mcp = FastMCP(name="URLElicitationHelperServer") + mcp = MCPServer(name="URLElicitationHelperServer") @mcp.tool(description="Tool using elicit_url helper") async def setup_credentials(ctx: Context[ServerSession, None]) -> str: @@ -124,7 +124,7 @@ async def elicitation_callback(context: RequestContext[ClientSession, None], par @pytest.mark.anyio async def test_url_no_content_in_response(): """Test that URL mode elicitation responses don't include content field.""" - mcp = FastMCP(name="URLContentCheckServer") + mcp = MCPServer(name="URLContentCheckServer") @mcp.tool(description="Check URL response format") async def check_url_response(ctx: Context[ServerSession, None]) -> str: @@ -158,7 +158,7 @@ async def elicitation_callback(context: RequestContext[ClientSession, None], par @pytest.mark.anyio async def test_form_mode_still_works(): """Ensure form mode elicitation still works after SEP 1036.""" - mcp = FastMCP(name="FormModeBackwardCompatServer") + mcp = MCPServer(name="FormModeBackwardCompatServer") class NameSchema(BaseModel): name: str = Field(description="Your name") @@ -189,7 +189,7 @@ async def elicitation_callback(context: RequestContext[ClientSession, None], par @pytest.mark.anyio async def test_elicit_complete_notification(): """Test that elicitation completion notifications can be sent and received.""" - mcp = FastMCP(name="ElicitCompleteServer") + mcp = MCPServer(name="ElicitCompleteServer") # Track if the notification was sent notification_sent = False @@ -235,7 +235,7 @@ async def test_url_elicitation_required_error_code(): @pytest.mark.anyio async def test_elicit_url_typed_results(): """Test that elicit_url returns properly typed result objects.""" - mcp = FastMCP(name="TypedResultsServer") + mcp = MCPServer(name="TypedResultsServer") @mcp.tool(description="Test declined result") async def test_decline(ctx: Context[ServerSession, None]) -> str: @@ -287,7 +287,7 @@ async def cancel_callback(context: RequestContext[ClientSession, None], params: @pytest.mark.anyio async def test_deprecated_elicit_method(): """Test the deprecated elicit() method for backward compatibility.""" - mcp = FastMCP(name="DeprecatedElicitServer") + mcp = MCPServer(name="DeprecatedElicitServer") class EmailSchema(BaseModel): email: str = Field(description="Email address") @@ -320,7 +320,7 @@ async def elicitation_callback(context: RequestContext[ClientSession, None], par @pytest.mark.anyio async def test_ctx_elicit_url_convenience_method(): """Test the ctx.elicit_url() convenience method (vs ctx.session.elicit_url()).""" - mcp = FastMCP(name="CtxElicitUrlServer") + mcp = MCPServer(name="CtxElicitUrlServer") @mcp.tool(description="A tool that uses ctx.elicit_url() directly") async def direct_elicit_url(ctx: Context[ServerSession, None]) -> str: diff --git a/tests/server/fastmcp/test_url_elicitation_error_throw.py b/tests/server/mcpserver/test_url_elicitation_error_throw.py similarity index 95% rename from tests/server/fastmcp/test_url_elicitation_error_throw.py rename to tests/server/mcpserver/test_url_elicitation_error_throw.py index cacc0b741c..36caa11523 100644 --- a/tests/server/fastmcp/test_url_elicitation_error_throw.py +++ b/tests/server/mcpserver/test_url_elicitation_error_throw.py @@ -3,7 +3,7 @@ import pytest from mcp import Client, types -from mcp.server.fastmcp import Context, FastMCP +from mcp.server.mcpserver import Context, MCPServer from mcp.server.session import ServerSession from mcp.shared.exceptions import McpError, UrlElicitationRequiredError @@ -11,7 +11,7 @@ @pytest.mark.anyio async def test_url_elicitation_error_thrown_from_tool(): """Test that UrlElicitationRequiredError raised from a tool is received as McpError by client.""" - mcp = FastMCP(name="UrlElicitationErrorServer") + mcp = MCPServer(name="UrlElicitationErrorServer") @mcp.tool(description="A tool that raises UrlElicitationRequiredError") async def connect_service(service_name: str, ctx: Context[ServerSession, None]) -> str: @@ -50,7 +50,7 @@ async def connect_service(service_name: str, ctx: Context[ServerSession, None]) @pytest.mark.anyio async def test_url_elicitation_error_from_error(): """Test that client can reconstruct UrlElicitationRequiredError from McpError.""" - mcp = FastMCP(name="UrlElicitationErrorServer") + mcp = MCPServer(name="UrlElicitationErrorServer") @mcp.tool(description="A tool that raises UrlElicitationRequiredError with multiple elicitations") async def multi_auth(ctx: Context[ServerSession, None]) -> str: @@ -91,7 +91,7 @@ async def multi_auth(ctx: Context[ServerSession, None]) -> str: @pytest.mark.anyio async def test_normal_exceptions_still_return_error_result(): """Test that normal exceptions still return CallToolResult with is_error=True.""" - mcp = FastMCP(name="NormalErrorServer") + mcp = MCPServer(name="NormalErrorServer") @mcp.tool(description="A tool that raises a normal exception") async def failing_tool(ctx: Context[ServerSession, None]) -> str: diff --git a/tests/server/test_lifespan.py b/tests/server/test_lifespan.py index caeb0530d5..a303664a54 100644 --- a/tests/server/test_lifespan.py +++ b/tests/server/test_lifespan.py @@ -1,4 +1,4 @@ -"""Tests for lifespan functionality in both low-level and FastMCP servers.""" +"""Tests for lifespan functionality in both low-level and MCPServer servers.""" from collections.abc import AsyncIterator from contextlib import asynccontextmanager @@ -8,8 +8,8 @@ import pytest from pydantic import TypeAdapter -from mcp.server.fastmcp import Context, FastMCP from mcp.server.lowlevel.server import NotificationOptions, Server +from mcp.server.mcpserver import Context, MCPServer from mcp.server.models import InitializationOptions from mcp.server.session import ServerSession from mcp.shared.message import SessionMessage @@ -120,11 +120,11 @@ async def run_server(): @pytest.mark.anyio -async def test_fastmcp_server_lifespan(): - """Test that lifespan works in FastMCP server.""" +async def test_mcpserver_server_lifespan(): + """Test that lifespan works in MCPServer server.""" @asynccontextmanager - async def test_lifespan(server: FastMCP) -> AsyncIterator[dict[str, bool]]: + async def test_lifespan(server: MCPServer) -> AsyncIterator[dict[str, bool]]: """Test lifespan context that tracks startup/shutdown.""" context = {"started": False, "shutdown": False} try: @@ -133,7 +133,7 @@ async def test_lifespan(server: FastMCP) -> AsyncIterator[dict[str, bool]]: finally: context["shutdown"] = True - server = FastMCP("test", lifespan=test_lifespan) + server = MCPServer("test", lifespan=test_lifespan) # Create memory streams for testing send_stream1, receive_stream1 = anyio.create_memory_object_stream[SessionMessage](100) @@ -152,10 +152,10 @@ def check_lifespan(ctx: Context[ServerSession, None]) -> bool: async with anyio.create_task_group() as tg, send_stream1, receive_stream1, send_stream2, receive_stream2: async def run_server(): - await server._mcp_server.run( + await server._lowlevel_server.run( receive_stream1, send_stream2, - server._mcp_server.create_initialization_options(), + server._lowlevel_server.create_initialization_options(), raise_exceptions=True, ) diff --git a/tests/test_examples.py b/tests/test_examples.py index 6f390e33f6..86057af1c1 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -9,57 +9,59 @@ from pathlib import Path import pytest +from inline_snapshot import snapshot from pytest_examples import CodeExample, EvalExample, find_examples from mcp import Client -from mcp.types import TextContent, TextResourceContents +from mcp.types import CallToolResult, TextContent, TextResourceContents @pytest.mark.anyio async def test_simple_echo(): """Test the simple echo server""" - from examples.fastmcp.simple_echo import mcp + from examples.mcpserver.simple_echo import mcp async with Client(mcp) as client: result = await client.call_tool("echo", {"text": "hello"}) - assert len(result.content) == 1 - content = result.content[0] - assert isinstance(content, TextContent) - assert content.text == "hello" + assert result == snapshot( + CallToolResult(content=[TextContent(text="hello")], structured_content={"result": "hello"}) + ) @pytest.mark.anyio async def test_complex_inputs(): """Test the complex inputs server""" - from examples.fastmcp.complex_inputs import mcp + from examples.mcpserver.complex_inputs import mcp async with Client(mcp) as client: tank = {"shrimp": [{"name": "bob"}, {"name": "alice"}]} result = await client.call_tool("name_shrimp", {"tank": tank, "extra_names": ["charlie"]}) - assert len(result.content) == 3 - assert isinstance(result.content[0], TextContent) - assert isinstance(result.content[1], TextContent) - assert isinstance(result.content[2], TextContent) - assert result.content[0].text == "bob" - assert result.content[1].text == "alice" - assert result.content[2].text == "charlie" + assert result == snapshot( + CallToolResult( + content=[ + TextContent(text="bob"), + TextContent(text="alice"), + TextContent(text="charlie"), + ], + structured_content={"result": ["bob", "alice", "charlie"]}, + ) + ) @pytest.mark.anyio async def test_direct_call_tool_result_return(): """Test the CallToolResult echo server""" - from examples.fastmcp.direct_call_tool_result_return import mcp + from examples.mcpserver.direct_call_tool_result_return import mcp async with Client(mcp) as client: result = await client.call_tool("echo", {"text": "hello"}) - assert len(result.content) == 1 - content = result.content[0] - assert isinstance(content, TextContent) - assert content.text == "hello" - assert result.structured_content - assert result.structured_content["text"] == "hello" - assert isinstance(result.meta, dict) - assert result.meta["some"] == "metadata" + assert result == snapshot( + CallToolResult( + meta={"some": "metadata"}, # type: ignore[reportUnknownMemberType] + content=[TextContent(text="hello")], + structured_content={"text": "hello"}, + ) + ) @pytest.mark.anyio @@ -70,15 +72,12 @@ async def test_desktop(monkeypatch: pytest.MonkeyPatch): monkeypatch.setattr(Path, "iterdir", lambda self: mock_files) # type: ignore[reportUnknownArgumentType] monkeypatch.setattr(Path, "home", lambda: Path("/fake/home")) - from examples.fastmcp.desktop import mcp + from examples.mcpserver.desktop import mcp async with Client(mcp) as client: # Test the sum function result = await client.call_tool("sum", {"a": 1, "b": 2}) - assert len(result.content) == 1 - content = result.content[0] - assert isinstance(content, TextContent) - assert content.text == "3" + assert result == snapshot(CallToolResult(content=[TextContent(text="3")], structured_content={"result": 3})) # Test the desktop resource result = await client.read_resource("dir://desktop") From ab4f998e477fea211c16975e350c8b7e46ded46d Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Sat, 24 Jan 2026 21:34:06 +0100 Subject: [PATCH 2/3] update migration --- docs/migration.md | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/docs/migration.md b/docs/migration.md index 9a34ef732e..6b0de378be 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -116,6 +116,26 @@ result = await session.list_resources(params=PaginatedRequestParams(cursor="next result = await session.list_tools(params=PaginatedRequestParams(cursor="next_page_token")) ``` +### `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. + +**Before (v1):** + +```python +from mcp.server.fastmcp import FastMCP + +mcp = FastMCP("Demo") +``` + +**After (v2):** + +```python +from mcp.server.mcpserver import MCPServer + +mcp = MCPServer("Demo") +``` + ### `mount_path` parameter removed from MCPServer The `mount_path` parameter has been removed from `MCPServer.__init__()`, `MCPServer.run()`, `MCPServer.run_sse_async()`, and `MCPServer.sse_app()`. It was also removed from the `Settings` class. @@ -138,14 +158,14 @@ Transport-specific parameters have been moved from the `MCPServer` constructor t **Before (v1):** ```python -from mcp.server.mcpserver import MCPServer +from mcp.server.fastmcp import FastMCP # Transport params in constructor -mcp = MCPServer("Demo", json_response=True, stateless_http=True) +mcp = FastMCP("Demo", json_response=True, stateless_http=True) mcp.run(transport="streamable-http") # Or for SSE -mcp = MCPServer("Server", host="0.0.0.0", port=9000, sse_path="/events") +mcp = FastMCP("Server", host="0.0.0.0", port=9000, sse_path="/events") mcp.run(transport="sse") ``` @@ -165,14 +185,18 @@ mcp.run(transport="sse", host="0.0.0.0", port=9000, sse_path="/events") **For mounted apps:** -When mounting MCPServer in a Starlette app, pass transport params to the app methods: +When mounting in a Starlette app, pass transport params to the app methods: ```python # Before (v1) -mcp = MCPServer("App", json_response=True) +from mcp.server.fastmcp import FastMCP + +mcp = FastMCP("App", json_response=True) app = Starlette(routes=[Mount("/", app=mcp.streamable_http_app())]) # After (v2) +from mcp.server.mcpserver import MCPServer + mcp = MCPServer("App") app = Starlette(routes=[Mount("/", app=mcp.streamable_http_app(json_response=True))]) ``` From dcca179eaabf1ef95c4c3d9e0da65883f3d29253 Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Sat, 24 Jan 2026 21:36:15 +0100 Subject: [PATCH 3/3] add an --- src/mcp/cli/claude.py | 2 +- tests/client/test_client.py | 2 +- tests/client/transports/test_memory.py | 4 ++-- tests/server/mcpserver/resources/test_resource_template.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/mcp/cli/claude.py b/src/mcp/cli/claude.py index 27e782033e..071b4b6fbb 100644 --- a/src/mcp/cli/claude.py +++ b/src/mcp/cli/claude.py @@ -49,7 +49,7 @@ def update_claude_config( with_packages: list[str] | None = None, env_vars: dict[str, str] | None = None, ) -> bool: - """Add or update a MCP server in Claude's configuration. + """Add or update an MCP server in Claude's configuration. Args: file_spec: Path to the server file, optionally with :object suffix diff --git a/tests/client/test_client.py b/tests/client/test_client.py index 943c74c0ca..44d0a20377 100644 --- a/tests/client/test_client.py +++ b/tests/client/test_client.py @@ -69,7 +69,7 @@ async def handle_completion( @pytest.fixture def app() -> MCPServer: - """Create a MCPServer server for testing.""" + """Create an MCPServer server for testing.""" server = MCPServer("test") @server.tool() diff --git a/tests/client/transports/test_memory.py b/tests/client/transports/test_memory.py index 70f842040a..0336aea0aa 100644 --- a/tests/client/transports/test_memory.py +++ b/tests/client/transports/test_memory.py @@ -31,7 +31,7 @@ async def handle_list_resources(): # pragma: no cover @pytest.fixture def mcpserver_server() -> MCPServer: - """Create a MCPServer server for testing.""" + """Create an MCPServer server for testing.""" server = MCPServer("test") @server.tool() @@ -59,7 +59,7 @@ async def test_with_server(simple_server: Server): async def test_with_mcpserver(mcpserver_server: MCPServer): - """Test creating transport with a MCPServer instance.""" + """Test creating transport with an MCPServer instance.""" transport = InMemoryTransport(mcpserver_server) async with transport.connect() as (read_stream, write_stream): assert read_stream is not None diff --git a/tests/server/mcpserver/resources/test_resource_template.py b/tests/server/mcpserver/resources/test_resource_template.py index 246398def2..08f2033bff 100644 --- a/tests/server/mcpserver/resources/test_resource_template.py +++ b/tests/server/mcpserver/resources/test_resource_template.py @@ -220,7 +220,7 @@ def get_user_data(user_id: str) -> str: # pragma: no cover @pytest.mark.anyio async def test_template_annotations_in_mcpserver(self): - """Test template annotations via MCPServer decorator.""" + """Test template annotations via an MCPServer decorator.""" mcp = MCPServer()