Rename FastMCP to MCPServer (#1951)
This commit is contained in:
committed by
GitHub
parent
4a2d83a0cb
commit
65c614e48e
@@ -39,7 +39,7 @@ body:
|
||||
demonstrating the bug.
|
||||
|
||||
placeholder: |
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from mcp.server.mcpserver import MCPServer
|
||||
|
||||
...
|
||||
render: Python
|
||||
|
||||
@@ -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:
|
||||
|
||||
<!-- snippet-source examples/snippets/servers/fastmcp_quickstart.py -->
|
||||
<!-- snippet-source examples/snippets/servers/mcpserver_quickstart.py -->
|
||||
```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)_
|
||||
<!-- /snippet-source -->
|
||||
|
||||
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:
|
||||
|
||||
<!-- snippet-source examples/snippets/servers/lifespan_example.py -->
|
||||
```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
|
||||
|
||||
<!-- snippet-source examples/snippets/servers/basic_resource.py -->
|
||||
```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
|
||||
|
||||
<!-- snippet-source examples/snippets/servers/basic_tool.py -->
|
||||
```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)_
|
||||
<!-- /snippet-source -->
|
||||
|
||||
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:
|
||||
|
||||
<!-- snippet-source examples/snippets/servers/tool_progress.py -->
|
||||
```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
|
||||
|
||||
<!-- snippet-source examples/snippets/servers/basic_prompt.py -->
|
||||
```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:
|
||||
|
||||
<!-- snippet-source examples/snippets/servers/images.py -->
|
||||
```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:
|
||||
|
||||
<!-- snippet-source examples/snippets/servers/tool_progress.py -->
|
||||
```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):
|
||||
|
||||
<!-- snippet-source examples/snippets/servers/sampling.py -->
|
||||
```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:
|
||||
|
||||
<!-- snippet-source examples/snippets/servers/notifications.py -->
|
||||
```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)_
|
||||
<!-- /snippet-source -->
|
||||
|
||||
You can mount multiple FastMCP servers in a Starlette application:
|
||||
You can mount multiple MCPServer servers in a Starlette application:
|
||||
|
||||
<!-- snippet-source examples/snippets/servers/streamable_starlette_mount.py -->
|
||||
```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
|
||||
|
||||
<!-- snippet-source examples/snippets/servers/streamable_http_path_config.py -->
|
||||
```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", "")},
|
||||
)
|
||||
|
||||
|
||||
+2
-2
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
+34
-10
@@ -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
|
||||
|
||||
+2
-2
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
@@ -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")
|
||||
+3
-3
@@ -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):
|
||||
@@ -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()
|
||||
@@ -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])
|
||||
+3
-3
@@ -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()
|
||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -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)
|
||||
|
||||
|
||||
+3
-3
@@ -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()
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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: á é í ó ú ñ 漢字 🎉")
|
||||
@@ -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
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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", "")},
|
||||
)
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -22,7 +22,7 @@ def run_server():
|
||||
print("Usage: server <server-name> [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)
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
+4
-4
@@ -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
|
||||
@@ -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()
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
+5
-1
@@ -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}"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""FastMCP CLI package."""
|
||||
"""MCP CLI package."""
|
||||
|
||||
from .cli import app
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
+8
-10
@@ -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)},
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"]
|
||||
@@ -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."""
|
||||
@@ -1 +0,0 @@
|
||||
"""FastMCP utility modules."""
|
||||
@@ -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"]
|
||||
@@ -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."""
|
||||
@@ -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
|
||||
|
||||
+4
-4
@@ -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] = {}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
"""Base classes and interfaces for FastMCP resources."""
|
||||
"""Base classes and interfaces for MCPServer resources."""
|
||||
|
||||
import abc
|
||||
from typing import Any
|
||||
+5
-5
@@ -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] = {}
|
||||
+4
-4
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
@@ -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
|
||||
|
||||
+5
-5
@@ -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,
|
||||
@@ -0,0 +1 @@
|
||||
"""MCPServer utility modules."""
|
||||
+2
-2
@@ -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:
|
||||
+7
-7
@@ -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.
|
||||
+3
-3
@@ -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
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
"""Common types used across FastMCP."""
|
||||
"""Common types used across MCPServer."""
|
||||
|
||||
import base64
|
||||
from pathlib import Path
|
||||
@@ -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
|
||||
|
||||
+15
-15
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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=[
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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] = []
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user