diff --git a/.github/ISSUE_TEMPLATE/bug.yaml b/.github/ISSUE_TEMPLATE/bug.yaml index e52277a2..617ff987 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 b6f8087a..0f0468a1 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 eb5ddf40..e096d910 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 6e20706a..f3984623 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 79470085..63828f48 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -121,15 +121,35 @@ 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 +### `FastMCP` renamed to `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 `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. 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:** @@ -157,28 +177,32 @@ 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 in a Starlette app, pass transport params to the app methods: ```python # Before (v1) +from mcp.server.fastmcp import FastMCP + mcp = FastMCP("App", json_response=True) app = Starlette(routes=[Mount("/", app=mcp.streamable_http_app())]) # After (v2) -mcp = FastMCP("App") +from mcp.server.mcpserver import MCPServer + +mcp = MCPServer("App") app = Starlette(routes=[Mount("/", app=mcp.streamable_http_app(json_response=True))]) ``` @@ -354,7 +378,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 f8698736..9a222c90 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 b55d4f72..93a42d1c 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 typing import Annotated 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 8ea62c70..80418451 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 2218af49..44a316bc 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 9f01e60c..501c4706 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 47601c03..f50389f3 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 @@ Demonstrates using icons with tools, resources, prompts, and implementation. 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_uri = f"data:image/png;base64,{icon_data}" 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 01615523..b157f9dd 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 cc87ea93..fd0bd936 100644 --- a/examples/fastmcp/memory.py +++ b/examples/mcpserver/memory.py @@ -24,7 +24,7 @@ from pgvector.asyncpg import register_vector # Import register_vector 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 @@ DEFAULT_EMBEDDING_MODEL = "text-embedding-3-small" 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 307ae5ce..59a1caf3 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 e1abf7c5..864b774a 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 2c73c984..e7b3ee6f 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 d0fa5970..3d8142a6 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 8a8dea35..7aeb5436 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 @@ import httpx 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 a5983978..012633ec 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 af4e435d..958c7d31 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 typing import TypedDict 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 341fa119..4fb7d9a1 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 json 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 @@ watched_resource_content = "Watched resource content" # 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 ac9dfcb5..ab7773b5 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.requests import Request 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 @@ class LegacySimpleOAuthProvider(SimpleOAuthProvider): 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 28d25654..0320871b 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 import AnyHttpUrl 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 40e31cf2..baa2765a 100644 --- a/examples/snippets/clients/display_utilities.py +++ b/examples/snippets/clients/display_utilities.py @@ -12,7 +12,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/examples/snippets/clients/stdio_client.py b/examples/snippets/clients/stdio_client.py index e4d43039..f096a264 100644 --- a/examples/snippets/clients/stdio_client.py +++ b/examples/snippets/clients/stdio_client.py @@ -12,7 +12,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", "")}, ) @@ -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 b9865e82..f132f875 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 40f606ba..d2eee972 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 5c197305..38d96b49 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 550e2408..7648024b 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 d7626f0b..47accffa 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 @@ from mcp.types import ( 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 3dfff91f..4c98c358 100644 --- a/examples/snippets/servers/direct_call_tool_result.py +++ b/examples/snippets/servers/direct_call_tool_result.py @@ -4,10 +4,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): diff --git a/examples/snippets/servers/direct_execution.py b/examples/snippets/servers/direct_execution.py index 65a6fbbf..acf7151d 100644 --- a/examples/snippets/servers/direct_execution.py +++ b/examples/snippets/servers/direct_execution.py @@ -7,9 +7,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() diff --git a/examples/snippets/servers/elicitation.py b/examples/snippets/servers/elicitation.py index 34921aa4..70e515c7 100644 --- a/examples/snippets/servers/elicitation.py +++ b/examples/snippets/servers/elicitation.py @@ -9,12 +9,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): diff --git a/examples/snippets/servers/images.py b/examples/snippets/servers/images.py index 9e0262c8..b30c9a8f 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 62278b6a..2925f106 100644 --- a/examples/snippets/servers/lifespan_example.py +++ b/examples/snippets/servers/lifespan_example.py @@ -4,7 +4,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 @@ -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 f762e908..70a83a56 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 833bc890..5579af51 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 226a3ec6..962ef061 100644 --- a/examples/snippets/servers/oauth_server.py +++ b/examples/snippets/servers/oauth_server.py @@ -6,7 +6,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): @@ -16,8 +16,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(), diff --git a/examples/snippets/servers/sampling.py b/examples/snippets/servers/sampling.py index ae78a74a..4ffeeda7 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 ca681022..622e6706 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 7a32dbef..9a53034f 100644 --- a/examples/snippets/servers/streamable_http_basic_mounting.py +++ b/examples/snippets/servers/streamable_http_basic_mounting.py @@ -9,10 +9,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() diff --git a/examples/snippets/servers/streamable_http_host_mounting.py b/examples/snippets/servers/streamable_http_host_mounting.py index 57da57f7..2a41f74a 100644 --- a/examples/snippets/servers/streamable_http_host_mounting.py +++ b/examples/snippets/servers/streamable_http_host_mounting.py @@ -9,10 +9,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() diff --git a/examples/snippets/servers/streamable_http_multiple_servers.py b/examples/snippets/servers/streamable_http_multiple_servers.py index cf6c6985..71217bdf 100644 --- a/examples/snippets/servers/streamable_http_multiple_servers.py +++ b/examples/snippets/servers/streamable_http_multiple_servers.py @@ -9,11 +9,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() diff --git a/examples/snippets/servers/streamable_http_path_config.py b/examples/snippets/servers/streamable_http_path_config.py index 1dcceeee..4c65ffdd 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 @@ 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() diff --git a/examples/snippets/servers/streamable_starlette_mount.py b/examples/snippets/servers/streamable_starlette_mount.py index c33dc71b..eb6f1b80 100644 --- a/examples/snippets/servers/streamable_starlette_mount.py +++ b/examples/snippets/servers/streamable_starlette_mount.py @@ -7,10 +7,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() @@ -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 50ee130c..bea7b22c 100644 --- a/examples/snippets/servers/structured_output.py +++ b/examples/snippets/servers/structured_output.py @@ -4,9 +4,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 diff --git a/examples/snippets/servers/tool_progress.py b/examples/snippets/servers/tool_progress.py index 2ac458f6..0b283cb1 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 f3f80e4e..e3faf687 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 b29bce88..daf07c0a 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 7c2f650e..071b4b6f 100644 --- a/src/mcp/cli/claude.py +++ b/src/mcp/cli/claude.py @@ -7,7 +7,7 @@ import sys 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 an 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 5e1e641a..858ab7db 100644 --- a/src/mcp/cli/cli.py +++ b/src/mcp/cli/cli.py @@ -8,7 +8,7 @@ import sys 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 @@ except ImportError: # pragma: no cover 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 _import_server(file: Path, server_object: str | None = None): # pragma: no 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 _import_server(file: Path, server_object: str | None = None): # pragma: no 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 3589d0da..33a1a186 100644 --- a/src/mcp/client/_memory.py +++ b/src/mcp/client/_memory.py @@ -10,7 +10,7 @@ import anyio 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 @@ class InMemoryTransport: 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 1738c12d..14a230aa 100644 --- a/src/mcp/client/client.py +++ b/src/mcp/client/client.py @@ -8,7 +8,7 @@ from typing import Any 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 @@ class Client: ``` """ - # 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 @@ class Client: 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 @@ class Client: """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 32395829..4c09d92d 100644 --- a/src/mcp/client/session_group.py +++ b/src/mcp/client/session_group.py @@ -15,7 +15,7 @@ from typing import Any, TypeAlias 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 0feed368..a2dada3a 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 9fb30c14..5eb577fd 100644 --- a/src/mcp/server/auth/provider.py +++ b/src/mcp/server/auth/provider.py @@ -96,7 +96,7 @@ class TokenVerifier(Protocol): """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 2feecf1e..00000000 --- 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 fb5bda10..00000000 --- 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 be448f97..00000000 --- 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 00000000..f51c0b0e --- /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 00000000..dd1b75e8 --- /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 48c65b57..751733f9 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 @@ from typing import TYPE_CHECKING, Any, Literal 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 6d032c73..34d4c7e9 100644 --- a/src/mcp/server/fastmcp/prompts/manager.py +++ b/src/mcp/server/mcpserver/prompts/manager.py @@ -4,11 +4,11 @@ from __future__ import annotations 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 @@ logger = get_logger(__name__) 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 b91a0e12..d3ccc425 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 20f67bbe..a855fb5f 100644 --- a/src/mcp/server/fastmcp/resources/resource_manager.py +++ b/src/mcp/server/mcpserver/resources/resource_manager.py @@ -7,13 +7,13 @@ from typing import TYPE_CHECKING, Any 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 @@ logger = get_logger(__name__) 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 14e2ca4b..698ac368 100644 --- a/src/mcp/server/fastmcp/resources/templates.py +++ b/src/mcp/server/mcpserver/resources/templates.py @@ -10,13 +10,13 @@ from urllib.parse import unquote 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 683886fd..64e63380 100644 --- a/src/mcp/server/fastmcp/resources/types.py +++ b/src/mcp/server/mcpserver/resources/types.py @@ -13,7 +13,7 @@ import pydantic 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 71cd81eb..600f3924 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.provider import OAuthAuthorizationServerProvider, ProviderT 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 @@ _CallableT = TypeVar("_CallableT", bound=Callable[..., Any]) 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 @@ class FastMCP(Generic[LifespanResultT]): 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 @@ class FastMCP(Generic[LifespanResultT]): 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 @@ class FastMCP(Generic[LifespanResultT]): @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 @@ class FastMCP(Generic[LifespanResultT]): 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 @@ class FastMCP(Generic[LifespanResultT]): 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 @@ class FastMCP(Generic[LifespanResultT]): 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 @@ class FastMCP(Generic[LifespanResultT]): 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 @@ class FastMCP(Generic[LifespanResultT]): 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 @@ class FastMCP(Generic[LifespanResultT]): 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 @@ class FastMCP(Generic[LifespanResultT]): 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 @@ class FastMCP(Generic[LifespanResultT]): # 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 @@ class FastMCP(Generic[LifespanResultT]): 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 @@ class Context(BaseModel, Generic[ServerSessionT, LifespanContextT, RequestT]): """ _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 @@ class Context(BaseModel, Generic[ServerSessionT, LifespanContextT, RequestT]): 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 e6a64f4d..9798fd96 100644 --- a/src/mcp/server/fastmcp/tools/base.py +++ b/src/mcp/server/mcpserver/tools/base.py @@ -8,15 +8,15 @@ from typing import TYPE_CHECKING, Any 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 0d3d9d52..5decefb7 100644 --- a/src/mcp/server/fastmcp/tools/tool_manager.py +++ b/src/mcp/server/mcpserver/tools/tool_manager.py @@ -3,21 +3,21 @@ from __future__ import annotations 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 00000000..b9d00d54 --- /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 80923e87..9cba83e8 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 95b3a327..4b539ce1 100644 --- a/src/mcp/server/fastmcp/utilities/func_metadata.py +++ b/src/mcp/server/mcpserver/utilities/func_metadata.py @@ -21,9 +21,9 @@ from typing_inspection.introspection import ( 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 @@ class FuncMetadata(BaseModel): 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 @@ def _create_dict_model(func_name: str, dict_annotation: Any) -> type[BaseModel]: 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 43c4d9ba..c394f2bf 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 a1445de1..f092b245 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 b140f9a7..890536a5 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 97319861..44d0a203 100644 --- a/tests/client/test_client.py +++ b/tests/client/test_client.py @@ -9,7 +9,7 @@ from inline_snapshot import snapshot 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 @@ def simple_server() -> Server: @pytest.fixture -def app() -> FastMCP: - """Create a FastMCP server for testing.""" - server = FastMCP("test") +def app() -> MCPServer: + """Create an MCPServer server for testing.""" + server = MCPServer("test") @server.tool() def greet(name: str) -> str: @@ -90,7 +90,7 @@ def app() -> FastMCP: 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 7d4124bb..1e547afe 100644 --- a/tests/client/test_list_methods_cursor.py +++ b/tests/client/test_list_methods_cursor.py @@ -5,7 +5,7 @@ import pytest 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 @@ pytestmark = pytest.mark.anyio @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 @@ async def full_featured_server(): ) 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 a8f8823f..6919624c 100644 --- a/tests/client/test_list_roots_callback.py +++ b/tests/client/test_list_roots_callback.py @@ -3,8 +3,8 @@ from pydantic import FileUrl 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 @@ from mcp.types import ListRootsResult, Root, TextContent @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 687efca7..e90dac2c 100644 --- a/tests/client/test_logging_callback.py +++ b/tests/client/test_logging_callback.py @@ -4,7 +4,7 @@ import pytest 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 @@ class LoggingCollector: @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 1394e665..78d7ba68 100644 --- a/tests/client/test_sampling_callback.py +++ b/tests/client/test_sampling_callback.py @@ -2,7 +2,7 @@ import pytest 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 @@ from mcp.types import ( @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_callback(): @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 942ff6c8..0336aea0 100644 --- a/tests/client/transports/test_memory.py +++ b/tests/client/transports/test_memory.py @@ -5,7 +5,7 @@ import pytest 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 @@ def simple_server() -> Server: @pytest.fixture -def fastmcp_server() -> FastMCP: - """Create a FastMCP server for testing.""" - server = FastMCP("test") +def mcpserver_server() -> MCPServer: + """Create an 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 an 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 9e3447b7..e59fb632 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 78ff5407..c59c5aec 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 test_lifespan_cleanup_executed(): # 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 test_stdin_close_triggers_cleanup(): # 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 24112106..39e2c6f2 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 @@ async def test_resource_templates(): # 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 41df47ee..a003f75b 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 @@ async def test_multiple_icons(): 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 be99e758..57e8040d 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 @@ from mcp.types import ( @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 @@ async def test_resource_template_edge_cases(): @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 7d1ac00c..e738017f 100644 --- a/tests/issues/test_152_resource_mime_type.py +++ b/tests/issues/test_152_resource_mime_type.py @@ -3,16 +3,16 @@ import base64 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 6ad7bdee..7903fd56 100644 --- a/tests/issues/test_1754_mime_type_parameters.py +++ b/tests/issues/test_1754_mime_type_parameters.py @@ -7,14 +7,14 @@ with parameters like 'text/html;profile=mcp-app' which are valid per RFC 2045. 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 @@ async def test_mime_type_with_parameters(): 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 @@ async def test_mime_type_with_parameters_and_space(): 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 @@ async def test_mime_type_with_multiple_parameters(): 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 2dba0c67..db0a66f9 100644 --- a/tests/issues/test_176_progress_token.py +++ b/tests/issues/test_176_progress_token.py @@ -2,7 +2,7 @@ from unittest.mock import AsyncMock, MagicMock 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 61a9b2c6..0e11f614 100644 --- a/tests/issues/test_188_concurrency.py +++ b/tests/issues/test_188_concurrency.py @@ -2,12 +2,12 @@ import anyio 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 test_messages_are_executed_concurrently_tools(): @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 63ed8038..33d6b455 100644 --- a/tests/issues/test_355_type_error.py +++ b/tests/issues/test_355_type_error.py @@ -2,7 +2,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 @@ -19,7 +19,7 @@ class Database: # Replace with your actual DB type # 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 32d5a16c..01cf222b 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 8eafbcdb..7c5c4358 100644 --- a/tests/server/auth/test_error_handling.py +++ b/tests/server/auth/test_error_handling.py @@ -16,7 +16,7 @@ from starlette.applications import Starlette 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 afc1ec6e..035e1cc8 100644 --- a/tests/server/fastmcp/prompts/test_base.py +++ b/tests/server/mcpserver/prompts/test_base.py @@ -2,7 +2,7 @@ from typing import Any 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 950ffddd..0e30b2e6 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 39272b05..94885113 100644 --- a/tests/server/fastmcp/resources/test_file_resources.py +++ b/tests/server/mcpserver/resources/test_file_resources.py @@ -4,7 +4,7 @@ from tempfile import NamedTemporaryFile 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 61ed44f6..5f5c216e 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 3d6dd9be..eb9b355a 100644 --- a/tests/server/fastmcp/resources/test_resource_manager.py +++ b/tests/server/mcpserver/resources/test_resource_manager.py @@ -4,7 +4,7 @@ from tempfile import NamedTemporaryFile 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 022e86db..08f2033b 100644 --- a/tests/server/fastmcp/resources/test_resource_template.py +++ b/tests/server/mcpserver/resources/test_resource_template.py @@ -4,8 +4,8 @@ from typing import Any 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 @@ class TestResourceTemplateAnnotations: 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 an 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 6d346786..93dc438d 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 @@ class TestResourceAnnotations: 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 @@ class TestResourceAnnotations: 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 b8c9ad3d..9c3fe265 100644 --- a/tests/server/fastmcp/servers/test_file_server.py +++ b/tests/server/mcpserver/servers/test_file_server.py @@ -3,7 +3,7 @@ from pathlib import Path 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 resources(mcp: FastMCP, test_dir: Path) -> FastMCP: @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 tools(mcp: FastMCP, test_dir: Path) -> FastMCP: @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 587dcebb..5a55592a 100644 --- a/tests/server/fastmcp/test_elicitation.py +++ b/tests/server/mcpserver/test_elicitation.py @@ -7,7 +7,7 @@ from pydantic import BaseModel, Field 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 @@ def create_ask_user_tool(mcp: FastMCP): 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 test_stdio_elicitation(): @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 test_stdio_elicitation_decline(): @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 test_elicitation_schema_validation(): @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 test_elicitation_with_optional_fields(): @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 test_elicitation_with_default_values(): @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 ba1d30d2..c57d1ee9 100644 --- a/tests/server/fastmcp/test_func_metadata.py +++ b/tests/server/mcpserver/test_func_metadata.py @@ -12,8 +12,8 @@ import pytest 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 a7f945f7..4d624c68 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 @@ from examples.snippets.servers import ( 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 340ca716..ec9f22c2 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 5b6030be..9dfaefeb 100644 --- a/tests/server/fastmcp/test_server.py +++ b/tests/server/mcpserver/test_server.py @@ -9,11 +9,11 @@ from starlette.applications import Starlette 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 @@ from mcp.types import ( 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 @@ class TestServer: @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 @@ class TestServer: @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 @@ class TestServer: @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 @@ class TestServer: @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 @@ class TestServer: @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 @@ class TestServer: @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 @@ class TestDnsRebindingProtection: 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 @@ class TestDnsRebindingProtection: 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 @@ class TestDnsRebindingProtection: 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 @@ class TestServerTools: @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 @@ class TestServerTools: @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 @@ class TestServerTools: @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 @@ class TestServerTools: @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 @@ class TestServerTools: @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 @@ class TestServerTools: 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 @@ class TestServerTools: 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 @@ class TestServerTools: @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 @@ class TestServerTools: @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 @@ class TestServerTools: 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 @@ class TestServerTools: """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 @@ class TestServerTools: """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 @@ class TestServerTools: """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 @@ class TestServerTools: 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 @@ class TestServerTools: "config": {"nested": {"value": 123}}, } - mcp = FastMCP() + mcp = MCPServer() mcp.add_tool(get_metadata) async with Client(mcp) as client: @@ -599,7 +599,7 @@ class TestServerTools: """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 @@ class TestServerTools: @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 @@ class TestServerTools: @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 @@ class TestServerTools: @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 @@ class TestServerTools: @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 @@ class TestServerTools: 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 @@ class TestServerResources: @pytest.mark.anyio async def test_binary_resource(self): - mcp = FastMCP() + mcp = MCPServer() def get_binary(): return b"Binary data" @@ -734,7 +734,7 @@ class TestServerResources: @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 @@ class TestServerResources: @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 @@ class TestServerResources: @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 @@ class TestServerResourceTemplates: @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 @@ class TestServerResourceTemplates: @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 @@ class TestServerResourceTemplates: @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 @@ class TestServerResourceTemplates: @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 @@ class TestServerResourceTemplates: @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 @@ class TestServerResourceTemplates: 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 @@ class TestServerResourceTemplates: @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 @@ class TestServerResourceTemplates: @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 @@ class TestServerResourceTemplates: 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 @@ class TestServerResourceMetadata: 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 @@ class TestServerResourceMetadata: 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 @@ class TestContextInjection: @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 @@ class TestContextInjection: @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 @@ class TestContextInjection: @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 @@ class TestContextInjection: @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 @@ class TestContextInjection: @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 @@ class TestContextInjection: @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 @@ class TestContextInjection: @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 @@ class TestContextInjection: @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 @@ class TestContextInjection: @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 @@ class TestContextInjection: @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 @@ class TestContextInjection: 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 @@ class TestServerPrompts: @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 @@ class TestServerPrompts: @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 @@ class TestServerPrompts: 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 @@ class TestServerPrompts: @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 @@ class TestServerPrompts: @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 @@ class TestServerPrompts: @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 @@ class TestServerPrompts: @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 @@ class TestServerPrompts: @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 @@ class TestServerPrompts: @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 @@ class TestServerPrompts: @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 @@ class TestServerPrompts: @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 @@ class TestServerPrompts: 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 2cb1173b..66246475 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 @@ from mcp.types import Prompt, Resource, ResourceTemplate, Tool, ToolAnnotations @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 @@ async def test_tool_title_precedence(): @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 @@ async def test_prompt_title(): @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 5ac1bf28..42cac073 100644 --- a/tests/server/fastmcp/test_tool_manager.py +++ b/tests/server/mcpserver/test_tool_manager.py @@ -6,10 +6,10 @@ from typing import Any, TypedDict 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 @@ class TestContextHandling: 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 @@ class TestContextHandling: 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 @@ class TestContextHandling: 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 @@ class TestToolAnnotations: 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 @@ class TestToolMetadata: 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 @@ class TestToolMetadata: 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 @@ class TestToolMetadata: 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 @@ class TestToolMetadata: 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 cade2aa5..45ec40a3 100644 --- a/tests/server/fastmcp/test_url_elicitation.py +++ b/tests/server/mcpserver/test_url_elicitation.py @@ -7,7 +7,7 @@ from pydantic import BaseModel, Field 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 @@ from mcp.types import ElicitRequestParams, ElicitResult, TextContent @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 test_url_elicitation_accept(): @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 test_url_elicitation_decline(): @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 test_url_elicitation_cancel(): @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 test_url_elicitation_helper_function(): @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 test_url_no_content_in_response(): @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 test_form_mode_still_works(): @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 test_elicit_url_typed_results(): @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 test_deprecated_elicit_method(): @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 cacc0b74..36caa115 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 @@ from mcp.shared.exceptions import McpError, UrlElicitationRequiredError @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 test_url_elicitation_error_thrown_from_tool(): @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 test_url_elicitation_error_from_error(): @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 caeb0530..a303664a 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 anyio 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 test_lowlevel_server_lifespan(): @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_fastmcp_server_lifespan(): 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 @@ async def test_fastmcp_server_lifespan(): 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 6f390e33..86057af1 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -9,57 +9,59 @@ import sys 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")