Restructure documentation for FastMCP 3.0 (#2951)

This commit is contained in:
Jeremiah Lowin 2026-01-19 21:33:35 -05:00 committed by GitHub
commit 3af9de197a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
145 changed files with 27876 additions and 5545 deletions

View file

@ -3,7 +3,6 @@ title: OAuth Authentication
sidebarTitle: OAuth
description: Authenticate your FastMCP client via OAuth 2.1.
icon: window
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"

View file

@ -9,24 +9,17 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="2.0.0" />
The central piece of MCP client applications is the `fastmcp.Client` class. This class provides a **programmatic interface** for interacting with any Model Context Protocol (MCP) server, handling protocol details and connection management automatically.
The FastMCP Client is designed for deterministic, controlled interactions rather than autonomous behavior, making it ideal for:
- **Testing MCP servers** during development
- **Building deterministic applications** that need reliable MCP interactions
- **Creating the foundation for agentic or LLM-based clients** with structured, type-safe operations
All client operations require using the `async with` context manager for proper connection lifecycle management.
The `fastmcp.Client` class provides a programmatic interface for interacting with any MCP server. It handles protocol details and connection management automatically, letting you focus on the operations you want to perform.
The FastMCP Client is designed for deterministic, controlled interactions rather than autonomous behavior, making it ideal for testing MCP servers during development, building deterministic applications that need reliable MCP interactions, and creating the foundation for agentic or LLM-based clients with structured, type-safe operations.
<Note>
This is not an agentic client - it requires explicit function calls and provides direct control over all MCP operations. Use it as a building block for higher-level systems.
This is a programmatic client that requires explicit function calls and provides direct control over all MCP operations. Use it as a building block for higher-level systems.
</Note>
## Creating a Client
Creating a client is straightforward. You provide a server source and the client automatically infers the appropriate transport mechanism.
You provide a server source and the client automatically infers the appropriate transport mechanism.
```python
import asyncio
@ -46,12 +39,12 @@ async def main():
async with client:
# Basic server interaction
await client.ping()
# List available operations
tools = await client.list_tools()
resources = await client.list_resources()
prompts = await client.list_prompts()
# Execute operations
result = await client.call_tool("example_tool", {"param": "value"})
print(result)
@ -59,35 +52,42 @@ async def main():
asyncio.run(main())
```
## Client-Transport Architecture
All client operations require using the `async with` context manager for proper connection lifecycle management.
The FastMCP Client separates concerns between protocol and connection:
## Choosing a Transport
- **`Client`**: Handles MCP protocol operations (tools, resources, prompts) and manages callbacks
- **`Transport`**: Establishes and maintains the connection (WebSockets, HTTP, Stdio, in-memory)
The client automatically selects a transport based on what you pass to it, but different transports have different characteristics that matter for your use case.
### Transport Inference
The client automatically infers the appropriate transport based on the input:
1. **`FastMCP` instance** → In-memory transport (perfect for testing)
2. **File path ending in `.py`** → Python Stdio transport
3. **File path ending in `.js`** → Node.js Stdio transport
4. **URL starting with `http://` or `https://`** → HTTP transport
5. **`MCPConfig` dictionary** → Multi-server client
**In-memory transport** connects directly to a FastMCP server instance within the same Python process. Use this for testing and development where you want to eliminate subprocess and network complexity. The server shares your process's environment and memory space.
```python
from fastmcp import Client, FastMCP
# Examples of transport inference
client_memory = Client(FastMCP("TestServer"))
client_script = Client("./server.py")
client_http = Client("https://api.example.com/mcp")
server = FastMCP("TestServer")
client = Client(server) # In-memory, no network or subprocess
```
<Tip>
For testing and development, always prefer the in-memory transport by passing a `FastMCP` server directly to the client. This eliminates network complexity and separate processes.
</Tip>
**STDIO transport** launches a server as a subprocess and communicates through stdin/stdout pipes. This is the standard mechanism used by desktop clients like Claude Desktop. The subprocess runs in an isolated environment, so you must explicitly pass any environment variables the server needs.
```python
from fastmcp import Client
# Simple inference from file path
client = Client("my_server.py")
# With explicit environment configuration
client = Client("my_server.py", env={"API_KEY": "secret"})
```
**HTTP transport** connects to servers running as web services. Use this for production deployments where the server runs independently and manages its own lifecycle.
```python
from fastmcp import Client
client = Client("https://api.example.com/mcp")
```
See [Transports](/clients/transports) for detailed configuration options including authentication headers, session persistence, and multi-server configurations.
## Configuration-Based Clients
@ -95,39 +95,18 @@ For testing and development, always prefer the in-memory transport by passing a
Create clients from MCP configuration dictionaries, which can include multiple servers. While there is no official standard for MCP configuration format, FastMCP follows established conventions used by tools like Claude Desktop.
### Configuration Format
```python
config = {
"mcpServers": {
"server_name": {
# Remote HTTP/SSE server
"transport": "http", # or "sse"
"url": "https://api.example.com/mcp",
"headers": {"Authorization": "Bearer token"},
"auth": "oauth" # or bearer token string
"weather": {
"url": "https://weather-api.example.com/mcp"
},
"local_server": {
# Local stdio server
"transport": "stdio",
"assistant": {
"command": "python",
"args": ["./server.py", "--verbose"],
"env": {"DEBUG": "true"},
"cwd": "/path/to/server",
"args": ["./assistant_server.py"]
}
}
}
```
### Multi-Server Example
```python
config = {
"mcpServers": {
"weather": {"url": "https://weather-api.example.com/mcp"},
"assistant": {"command": "python", "args": ["./assistant_server.py"]}
}
}
client = Client(config)
@ -135,97 +114,14 @@ async with client:
# Tools are prefixed with server names
weather_data = await client.call_tool("weather_get_forecast", {"city": "London"})
response = await client.call_tool("assistant_answer_question", {"question": "What's the capital of France?"})
# Resources use prefixed URIs
icons = await client.read_resource("weather://weather/icons/sunny")
templates = await client.read_resource("resource://assistant/templates/list")
```
## Connection Lifecycle
The client operates asynchronously and uses context managers for connection management:
```python
async def example():
client = Client("my_mcp_server.py")
# Connection established here
async with client:
print(f"Connected: {client.is_connected()}")
# Make multiple calls within the same session
tools = await client.list_tools()
result = await client.call_tool("greet", {"name": "World"})
# Connection closed automatically here
print(f"Connected: {client.is_connected()}")
```
## Operations
FastMCP clients can interact with several types of server components:
### Tools
Tools are server-side functions that the client can execute with arguments.
```python
async with client:
# List available tools
tools = await client.list_tools()
# Execute a tool
result = await client.call_tool("multiply", {"a": 5, "b": 3})
print(result.data) # 15
```
See [Tools](/clients/tools) for detailed documentation.
### Resources
Resources are data sources that the client can read, either static or templated.
```python
async with client:
# List available resources
resources = await client.list_resources()
# Read a resource
content = await client.read_resource("file:///config/settings.json")
print(content[0].text)
```
See [Resources](/clients/resources) for detailed documentation.
### Prompts
Prompts are reusable message templates that can accept arguments.
```python
async with client:
# List available prompts
prompts = await client.list_prompts()
# Get a rendered prompt
messages = await client.get_prompt("analyze_data", {"data": [1, 2, 3]})
print(messages.messages)
```
See [Prompts](/clients/prompts) for detailed documentation.
### Server Connectivity
Use `ping()` to verify the server is reachable:
```python
async with client:
await client.ping()
print("Server is reachable")
```
### Initialization and Server Information
When you enter the client context manager, the client automatically performs an MCP initialization handshake with the server. This handshake exchanges capabilities, server metadata, and instructions. The result is available through the `initialize_result` property.
The client uses context managers for connection management. When you enter the context, the client establishes a connection and performs an MCP initialization handshake with the server. This handshake exchanges capabilities, server metadata, and instructions.
```python
from fastmcp import Client, FastMCP
@ -240,21 +136,15 @@ def greet(name: str) -> str:
async with Client(mcp) as client:
# Initialization already happened automatically
print(f"Server: {client.initialize_result.serverInfo.name}")
print(f"Version: {client.initialize_result.serverInfo.version}")
print(f"Instructions: {client.initialize_result.instructions}")
print(f"Capabilities: {client.initialize_result.capabilities.tools}")
```
#### Manual Initialization Control
In advanced scenarios, you might want precise control over when initialization happens. For example, you may need custom error handling, want to defer initialization until after other setup, or need to measure initialization timing separately.
Disable automatic initialization and call `initialize()` manually:
For advanced scenarios where you need precise control over when initialization happens, disable automatic initialization and call `initialize()` manually:
```python
from fastmcp import Client
# Disable automatic initialization
client = Client("my_mcp_server.py", auto_initialize=False)
async with client:
@ -270,15 +160,46 @@ async with client:
tools = await client.list_tools()
```
The `initialize()` method is idempotent - calling it multiple times returns the cached result from the first successful call.
## Operations
## Client Configuration
FastMCP clients interact with three types of server components.
Clients can be configured with additional handlers and settings for specialized use cases.
**Tools** are server-side functions that the client can execute with arguments. Call them with `call_tool()` and receive structured results.
### Callback Handlers
```python
async with client:
tools = await client.list_tools()
result = await client.call_tool("multiply", {"a": 5, "b": 3})
print(result.data) # 15
```
The client supports several callback handlers for advanced server interactions:
See [Tools](/clients/tools) for detailed documentation including version selection, error handling, and structured output.
**Resources** are data sources that the client can read, either static or templated. Access them with `read_resource()` using URIs.
```python
async with client:
resources = await client.list_resources()
content = await client.read_resource("file:///config/settings.json")
print(content[0].text)
```
See [Resources](/clients/resources) for detailed documentation including templates and binary content.
**Prompts** are reusable message templates that can accept arguments. Retrieve rendered prompts with `get_prompt()`.
```python
async with client:
prompts = await client.list_prompts()
messages = await client.get_prompt("analyze_data", {"data": [1, 2, 3]})
print(messages.messages)
```
See [Prompts](/clients/prompts) for detailed documentation including argument serialization.
## Callback Handlers
The client supports callback handlers for advanced server interactions. These let you respond to server-initiated requests and receive notifications.
```python
from fastmcp import Client
@ -303,38 +224,14 @@ client = Client(
)
```
The `Client` constructor accepts several configuration options:
Each handler type has its own documentation:
- `transport`: Transport instance or source for automatic inference
- `log_handler`: Handle server log messages
- `progress_handler`: Monitor long-running operations
- `sampling_handler`: Respond to server LLM requests
- `roots`: Provide local context to servers
- `timeout`: Default timeout for requests (in seconds)
### Transport Configuration
For detailed transport configuration (headers, authentication, environment variables), see the [Transports](/clients/transports) documentation.
## Next Steps
Explore the detailed documentation for each operation type:
### Core Operations
- **[Tools](/clients/tools)** - Execute server-side functions and handle results
- **[Resources](/clients/resources)** - Access static and templated resources
- **[Prompts](/clients/prompts)** - Work with message templates and argument serialization
### Advanced Features
- **[Logging](/clients/logging)** - Handle server log messages
- **[Progress](/clients/progress)** - Monitor long-running operations
- **[Sampling](/clients/sampling)** - Respond to server LLM requests
- **[Elicitation](/clients/elicitation)** - Handle server requests for user input
- **[Progress](/clients/progress)** - Monitor long-running operations
- **[Logging](/clients/logging)** - Handle server log messages
- **[Roots](/clients/roots)** - Provide local context to servers
### Connection Details
- **[Transports](/clients/transports)** - Configure connection methods and parameters
- **[Authentication](/clients/auth/oauth)** - Set up OAuth and bearer token authentication
<Tip>
The FastMCP Client is designed as a foundational tool. Use it directly for deterministic operations, or build higher-level agentic systems on top of its reliable, type-safe interface.
</Tip>
</Tip>

View file

@ -1,7 +1,7 @@
---
title: User Elicitation
sidebarTitle: Elicitation
description: Handle server-initiated user input requests with structured schemas.
description: Handle server requests for structured user input.
icon: message-question
---
@ -9,44 +9,43 @@ import { VersionBadge } from "/snippets/version-badge.mdx";
<VersionBadge version="2.10.0" />
## What is Elicitation?
Use this when you need to respond to server requests for user input during tool execution.
Elicitation allows MCP servers to request structured input from users during tool execution. Instead of requiring all inputs upfront, servers can interactively ask users for information as needed - like prompting for missing parameters, requesting clarification, or gathering additional context.
Elicitation allows MCP servers to request structured input from users during operations. Instead of requiring all inputs upfront, servers can interactively ask for missing parameters, request clarification, or gather additional context.
For example, a file management tool might ask "Which directory should I create?" or a data analysis tool might request "What date range should I analyze?"
## How FastMCP Makes Elicitation Easy
FastMCP's client provides a helpful abstraction layer that:
- **Converts JSON schemas to Python types**: The raw MCP protocol uses JSON schemas, but FastMCP automatically converts these to Python dataclasses
- **Provides structured constructors**: Instead of manually building dictionaries that match the schema, you get dataclass constructors that ensure correct structure
- **Handles type conversion**: FastMCP takes care of converting between JSON representations and Python objects
- **Runtime introspection**: You can inspect the generated dataclass fields to understand the expected structure
When you implement an elicitation handler, FastMCP gives you a dataclass type that matches the server's schema, making it easy to create properly structured responses without having to manually parse JSON schemas.
## Elicitation Handler
Provide an `elicitation_handler` function when creating the client. FastMCP automatically converts the server's JSON schema into a Python dataclass type, making it easy to construct the response:
## Handler Template
```python
from fastmcp import Client
from fastmcp.client.elicitation import ElicitResult
from fastmcp.client.elicitation import ElicitResult, ElicitRequestParams, RequestContext
async def elicitation_handler(message: str, response_type: type, params, context):
# Present the message to the user and collect input
async def elicitation_handler(
message: str,
response_type: type | None,
params: ElicitRequestParams,
context: RequestContext
) -> ElicitResult | object:
"""
Handle server requests for user input.
Args:
message: The prompt to display to the user
response_type: Python dataclass type for the response (None if no data expected)
params: Original MCP elicitation parameters including raw JSON schema
context: Request context with metadata
Returns:
- Data directly (implicitly accepts the elicitation)
- ElicitResult for explicit control over the action
"""
# Present the message and collect input
user_input = input(f"{message}: ")
if not user_input:
return ElicitResult(action="decline")
# Create response using the provided dataclass type
# FastMCP converted the JSON schema to this Python type for you
response_data = response_type(value=user_input)
# You can return data directly - FastMCP will implicitly accept the elicitation
return response_data
# Or explicitly return an ElicitResult for more control
# return ElicitResult(action="accept", content=response_data)
return response_type(value=user_input)
client = Client(
"my_mcp_server.py",
@ -54,21 +53,23 @@ client = Client(
)
```
### Handler Parameters
## How It Works
The elicitation handler receives four parameters:
When a server needs user input, it sends an elicitation request with a message prompt and a JSON schema describing the expected response structure. FastMCP automatically converts this schema into a Python dataclass type, making it easy to construct properly typed responses without manually parsing JSON schemas.
<Card icon="code" title="Elicitation Handler Parameters">
The handler receives four parameters:
<Card icon="code" title="Handler Parameters">
<ResponseField name="message" type="str">
The prompt message to display to the user
</ResponseField>
<ResponseField name="response_type" type="type">
A Python dataclass type that FastMCP created from the server's JSON schema. Use this to construct your response with proper typing and IDE support. If the server requests an empty object (indicating no response), this will be `None`.
<ResponseField name="response_type" type="type | None">
A Python dataclass type that FastMCP created from the server's JSON schema. Use this to construct your response with proper typing. If the server requests an empty object, this will be `None`.
</ResponseField>
<ResponseField name="params" type="ElicitRequestParams">
The original MCP elicitation request parameters, including the raw JSON schema in `params.requestedSchema` if you need it
The original MCP elicitation parameters, including the raw JSON schema in `params.requestedSchema`
</ResponseField>
<ResponseField name="context" type="RequestContext">
@ -76,50 +77,62 @@ The elicitation handler receives four parameters:
</ResponseField>
</Card>
### Response Actions
## Response Actions
The handler can return data directly (which implicitly accepts the elicitation) or an `ElicitResult` object for more control over the response action:
You can return data directly, which implicitly accepts the elicitation:
<Card icon="code" title="ElicitResult Structure">
<ResponseField name="action" type="Literal['accept', 'decline', 'cancel']">
How the user responded to the elicitation request
</ResponseField>
```python
async def elicitation_handler(message, response_type, params, context):
user_input = input(f"{message}: ")
return response_type(value=user_input) # Implicit accept
```
<ResponseField name="content" type="dataclass instance | dict | None">
The user's input data (required for "accept", omitted for "decline"/"cancel")
</ResponseField>
</Card>
Or return an `ElicitResult` for explicit control over the action:
**Action Types:**
- **`accept`**: User provided valid input - include their data in the `content` field
- **`decline`**: User chose not to provide the requested information - omit `content`
- **`cancel`**: User cancelled the entire operation - omit `content`
```python
from fastmcp.client.elicitation import ElicitResult
## Basic Example
async def elicitation_handler(message, response_type, params, context):
user_input = input(f"{message}: ")
if not user_input:
return ElicitResult(action="decline") # User declined
if user_input == "cancel":
return ElicitResult(action="cancel") # Cancel entire operation
return ElicitResult(
action="accept",
content=response_type(value=user_input)
)
```
**Action types:**
- **`accept`**: User provided valid input. Include the data in the `content` field.
- **`decline`**: User chose not to provide the requested information. Omit `content`.
- **`cancel`**: User cancelled the entire operation. Omit `content`.
## Example
A file management tool might ask which directory to create:
```python
from fastmcp import Client
from fastmcp.client.elicitation import ElicitResult
async def basic_elicitation_handler(message: str, response_type: type, params, context):
async def elicitation_handler(message, response_type, params, context):
print(f"Server asks: {message}")
# Simple text input for demonstration
user_response = input("Your response: ")
if not user_response:
# For non-acceptance, use ElicitResult explicitly
return ElicitResult(action="decline")
# Use the response_type dataclass to create a properly structured response
# FastMCP handles the conversion from JSON schema to Python type
# Return data directly - FastMCP will implicitly accept the elicitation
return response_type(value=user_response)
client = Client(
"my_mcp_server.py",
elicitation_handler=basic_elicitation_handler
"my_mcp_server.py",
elicitation_handler=elicitation_handler
)
```

View file

@ -9,82 +9,61 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="2.0.0" />
MCP servers can emit log messages to clients. The client can handle these logs through a log handler callback.
Use this when you need to capture or process log messages sent by the server.
MCP servers can emit log messages to clients. The client handles these through a log handler callback.
## Log Handler
Provide a `log_handler` function when creating the client. For robust logging, the log messages can be integrated with Python's standard `logging` module.
Provide a `log_handler` function when creating the client:
```python
import logging
from fastmcp import Client
from fastmcp.client.logging import LogMessage
# In a real app, you might configure this in your main entry point
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Get a logger for the module where the client is used
logger = logging.getLogger(__name__)
# This mapping is useful for converting MCP level strings to Python's levels
LOGGING_LEVEL_MAP = logging.getLevelNamesMapping()
async def log_handler(message: LogMessage):
"""
Handles incoming logs from the MCP server and forwards them
to the standard Python logging system.
"""
"""Forward MCP server logs to Python's logging system."""
msg = message.data.get('msg')
extra = message.data.get('extra')
# Convert the MCP log level to a Python log level
level = LOGGING_LEVEL_MAP.get(message.level.upper(), logging.INFO)
# Log the message using the standard logging library
logger.log(level, msg, extra=extra)
client = Client(
"my_mcp_server.py",
log_handler=log_handler,
)
```
## Handling Structured Logs
The handler receives a `LogMessage` object:
The `message.data` attribute is a dictionary that contains the log payload from the server. This enables structured logging, allowing you to receive rich, contextual information.
<Card icon="code" title="LogMessage">
<ResponseField name="level" type='Literal["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"]'>
The log level
</ResponseField>
The dictionary contains two keys:
- `msg`: The string log message.
- `extra`: A dictionary containing any extra data sent from the server.
<ResponseField name="logger" type="str | None">
The logger name (may be None)
</ResponseField>
This structure is preserved even when logs are forwarded through a FastMCP proxy, making it a powerful tool for debugging complex, multi-server applications.
### Handler Parameters
The `log_handler` is called every time a log message is received. It receives a `LogMessage` object:
<Card icon="code" title="Log Handler Parameters">
<ResponseField name="LogMessage" type="Log Message Object">
<Expandable title="attributes">
<ResponseField name="level" type='Literal["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"]'>
The log level
</ResponseField>
<ResponseField name="logger" type="str | None">
The logger name (optional, may be None)
</ResponseField>
<ResponseField name="data" type="dict">
The log payload, containing `msg` and `extra` keys.
</ResponseField>
</Expandable>
<ResponseField name="data" type="dict">
The log payload, containing `msg` and `extra` keys
</ResponseField>
</Card>
## Structured Logs
The `message.data` attribute is a dictionary containing the log payload. This enables structured logging with rich contextual information.
```python
async def detailed_log_handler(message: LogMessage):
msg = message.data.get('msg')
@ -98,14 +77,16 @@ async def detailed_log_handler(message: LogMessage):
print(f"{message.level.upper()}: {msg}")
```
## Default Log Handling
This structure is preserved even when logs are forwarded through a FastMCP proxy, making it useful for debugging multi-server applications.
If you don't provide a custom `log_handler`, FastMCP's default handler routes server logs to the appropriate Python logging levels. The MCP levels are mapped as follows: `notice` → INFO; `alert` and `emergency` → CRITICAL. If the server includes a logger name, it is prefixed in the message, and any `extra` data is forwarded via the logging `extra` parameter.
## Default Behavior
If you do not provide a custom `log_handler`, FastMCP's default handler routes server logs to Python's logging system at the appropriate severity level. The MCP levels map as follows: `notice` becomes INFO; `alert` and `emergency` become CRITICAL.
```python
client = Client("my_mcp_server.py")
async with client:
# Server logs are forwarded at their proper severity (DEBUG/INFO/WARNING/ERROR/CRITICAL)
# Server logs are forwarded at proper severity automatically
await client.call_tool("some_tool")
```

View file

@ -0,0 +1,155 @@
---
title: Notifications
sidebarTitle: Notifications
description: Handle server-sent notifications for list changes and other events.
icon: envelope
---
import { VersionBadge } from "/snippets/version-badge.mdx";
<VersionBadge version="2.9.1" />
Use this when you need to react to server-side changes like tool list updates or resource modifications.
MCP servers can send notifications to inform clients about state changes. The message handler provides a unified way to process these notifications.
## Handling Notifications
The simplest approach is a function that receives all messages and filters for the notifications you care about:
```python
from fastmcp import Client
async def message_handler(message):
"""Handle MCP notifications from the server."""
if hasattr(message, 'root'):
method = message.root.method
if method == "notifications/tools/list_changed":
print("Tools have changed - refresh tool cache")
elif method == "notifications/resources/list_changed":
print("Resources have changed")
elif method == "notifications/prompts/list_changed":
print("Prompts have changed")
client = Client(
"my_mcp_server.py",
message_handler=message_handler,
)
```
## MessageHandler Class
For fine-grained targeting, subclass `MessageHandler` to use specific hooks:
```python
from fastmcp import Client
from fastmcp.client.messages import MessageHandler
import mcp.types
class MyMessageHandler(MessageHandler):
async def on_tool_list_changed(
self, notification: mcp.types.ToolListChangedNotification
) -> None:
"""Handle tool list changes."""
print("Tool list changed - refreshing available tools")
async def on_resource_list_changed(
self, notification: mcp.types.ResourceListChangedNotification
) -> None:
"""Handle resource list changes."""
print("Resource list changed")
async def on_prompt_list_changed(
self, notification: mcp.types.PromptListChangedNotification
) -> None:
"""Handle prompt list changes."""
print("Prompt list changed")
client = Client(
"my_mcp_server.py",
message_handler=MyMessageHandler(),
)
```
### Handler Template
```python
from fastmcp.client.messages import MessageHandler
import mcp.types
class MyMessageHandler(MessageHandler):
async def on_message(self, message) -> None:
"""Called for ALL messages (requests and notifications)."""
pass
async def on_notification(
self, notification: mcp.types.ServerNotification
) -> None:
"""Called for notifications (fire-and-forget)."""
pass
async def on_tool_list_changed(
self, notification: mcp.types.ToolListChangedNotification
) -> None:
"""Called when the server's tool list changes."""
pass
async def on_resource_list_changed(
self, notification: mcp.types.ResourceListChangedNotification
) -> None:
"""Called when the server's resource list changes."""
pass
async def on_prompt_list_changed(
self, notification: mcp.types.PromptListChangedNotification
) -> None:
"""Called when the server's prompt list changes."""
pass
async def on_progress(
self, notification: mcp.types.ProgressNotification
) -> None:
"""Called for progress updates during long-running operations."""
pass
async def on_logging_message(
self, notification: mcp.types.LoggingMessageNotification
) -> None:
"""Called for log messages from the server."""
pass
```
## List Change Notifications
A practical example of maintaining a tool cache that refreshes when tools change:
```python
from fastmcp import Client
from fastmcp.client.messages import MessageHandler
import mcp.types
class ToolCacheHandler(MessageHandler):
def __init__(self):
self.cached_tools = []
async def on_tool_list_changed(
self, notification: mcp.types.ToolListChangedNotification
) -> None:
"""Clear tool cache when tools change."""
print("Tools changed - clearing cache")
self.cached_tools = [] # Force refresh on next access
client = Client("server.py", message_handler=ToolCacheHandler())
```
## Server Requests
While the message handler receives server-initiated requests, you should use dedicated callback parameters for most interactive scenarios:
- **Sampling requests**: Use [`sampling_handler`](/clients/sampling)
- **Elicitation requests**: Use [`elicitation_handler`](/clients/elicitation)
- **Progress updates**: Use [`progress_handler`](/clients/progress)
- **Log messages**: Use [`log_handler`](/clients/logging)
The message handler is primarily for monitoring and handling notifications rather than responding to requests.

View file

@ -9,18 +9,20 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="2.3.5" />
MCP servers can report progress during long-running operations. The client can receive these updates through a progress handler.
Use this when you need to track progress of long-running operations.
MCP servers can report progress during operations. The client receives these updates through a progress handler.
## Progress Handler
Set a progress handler when creating the client:
Set a handler when creating the client:
```python
from fastmcp import Client
async def my_progress_handler(
progress: float,
total: float | None,
async def progress_handler(
progress: float,
total: float | None,
message: str | None
) -> None:
if total is not None:
@ -31,40 +33,35 @@ async def my_progress_handler(
client = Client(
"my_mcp_server.py",
progress_handler=my_progress_handler
progress_handler=progress_handler
)
```
### Handler Parameters
The handler receives three parameters:
The progress handler receives three parameters:
<Card icon="code" title="Progress Handler Parameters">
<Card icon="code" title="Handler Parameters">
<ResponseField name="progress" type="float">
Current progress value
</ResponseField>
<ResponseField name="total" type="float | None">
Expected total value (may be None)
Expected total value (may be None if unknown)
</ResponseField>
<ResponseField name="message" type="str | None">
Optional status message (may be None)
Optional status message
</ResponseField>
</Card>
## Per-Call Handler
## Per-Call Progress Handler
Override the progress handler for specific tool calls:
Override the client-level handler for specific tool calls:
```python
async with client:
# Override with specific progress handler for this call
result = await client.call_tool(
"long_running_task",
{"param": "value"},
"long_running_task",
{"param": "value"},
progress_handler=my_progress_handler
)
```

View file

@ -1,7 +1,7 @@
---
title: Prompts
title: Getting Prompts
sidebarTitle: Prompts
description: Use server-side prompt templates with automatic argument serialization.
description: Retrieve rendered message templates with automatic argument serialization.
icon: message-lines
---
@ -9,112 +9,44 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="2.0.0" />
Use this when you need to retrieve server-defined message templates for LLM interactions.
Prompts are reusable message templates exposed by MCP servers. They can accept arguments to generate personalized message sequences for LLM interactions.
## Listing Prompts
## Basic Usage
Use `list_prompts()` to retrieve all available prompt templates. When the server paginates results, the client automatically fetches all pages and returns the complete list.
```python
async with client:
prompts = await client.list_prompts()
# prompts -> list[mcp.types.Prompt]
for prompt in prompts:
print(f"Prompt: {prompt.name}")
print(f"Description: {prompt.description}")
if prompt.arguments:
print(f"Arguments: {[arg.name for arg in prompt.arguments]}")
# Access tags and other metadata
if prompt.meta:
fastmcp_meta = prompt.meta.get('fastmcp', {})
print(f"Tags: {fastmcp_meta.get('tags', [])}")
```
For manual pagination control, use `list_prompts_mcp()` with the `cursor` parameter. See [Pagination](/servers/pagination#manual-pagination) for details.
### Filtering by Tags
<VersionBadge version="2.11.0" />
You can use the `meta` field to filter prompts based on their tags:
```python
async with client:
prompts = await client.list_prompts()
# Filter prompts by tag
analysis_prompts = [
prompt for prompt in prompts
if prompt.meta and
prompt.meta.get('fastmcp', {}) and
'analysis' in prompt.meta.get('fastmcp', {}).get('tags', [])
]
print(f"Found {len(analysis_prompts)} analysis prompts")
```
<Note>
The `meta` field is part of the standard MCP specification. FastMCP servers always include tags and other metadata within a `fastmcp` namespace (e.g., `meta.fastmcp.tags`) to avoid conflicts with user-defined metadata. Component versions are also included in the metadata when available (e.g., `meta.fastmcp.version`). Other MCP server implementations may not provide this metadata structure.
</Note>
## Using Prompts
### Basic Usage
Request a rendered prompt using `get_prompt()` with the prompt name and arguments:
Request a rendered prompt with `get_prompt()`:
```python
async with client:
# Simple prompt without arguments
result = await client.get_prompt("welcome_message")
# result -> mcp.types.GetPromptResult
# Access the generated messages
for message in result.messages:
print(f"Role: {message.role}")
print(f"Content: {message.content}")
```
### Prompts with Arguments
Pass arguments as a dictionary to customize the prompt:
Pass arguments to customize the prompt:
```python
async with client:
# Prompt with simple arguments
result = await client.get_prompt("user_greeting", {
"name": "Alice",
"role": "administrator"
})
# Access the personalized messages
for message in result.messages:
print(f"Generated message: {message.content}")
```
### Requesting Specific Versions
<VersionBadge version="3.0.0" />
When a server has multiple versions of a prompt, you can request a specific version instead of the default (highest) version.
```python
async with client:
# Get the highest version (default)
result = await client.get_prompt("summarize", {"text": "..."})
# Get a specific version
result_v1 = await client.get_prompt("summarize", {"text": "..."}, version="1.0")
```
To discover available versions, check the `meta.fastmcp.versions` field when listing prompts. See [Version Selection](#version-selection) below for more details.
## Automatic Argument Serialization
## Argument Serialization
<VersionBadge version="2.9.0" />
FastMCP automatically serializes complex arguments to JSON strings as required by the MCP specification. This allows you to pass typed objects directly:
FastMCP automatically serializes complex arguments to JSON strings as required by the MCP specification. You can pass typed objects directly:
```python
from dataclasses import dataclass
@ -125,82 +57,31 @@ class UserData:
age: int
async with client:
# Complex arguments are automatically serialized
result = await client.get_prompt("analyze_user", {
"user": UserData(name="Alice", age=30), # Automatically serialized to JSON
"preferences": {"theme": "dark"}, # Dict serialized to JSON string
"scores": [85, 92, 78], # List serialized to JSON string
"simple_name": "Bob" # Strings passed through unchanged
"user": UserData(name="Alice", age=30), # Automatically serialized
"preferences": {"theme": "dark"}, # Dict serialized
"scores": [85, 92, 78], # List serialized
"simple_name": "Bob" # Strings unchanged
})
```
The client handles serialization using `pydantic_core.to_json()` for consistent formatting. FastMCP servers can automatically deserialize these JSON strings back to the expected types.
The client handles serialization using `pydantic_core.to_json()` for consistent formatting. FastMCP servers automatically deserialize these JSON strings back to the expected types.
### Serialization Examples
## Working with Results
```python
async with client:
result = await client.get_prompt("data_analysis", {
# These will be automatically serialized to JSON strings:
"config": {
"format": "csv",
"include_headers": True,
"delimiter": ","
},
"filters": [
{"field": "age", "operator": ">", "value": 18},
{"field": "status", "operator": "==", "value": "active"}
],
# This remains a string:
"report_title": "Monthly Analytics Report"
})
```
## Working with Prompt Results
The `get_prompt()` method returns a `GetPromptResult` object containing a list of messages:
The `get_prompt()` method returns a `GetPromptResult` containing a list of messages:
```python
async with client:
result = await client.get_prompt("conversation_starter", {"topic": "climate"})
# Access individual messages
for i, message in enumerate(result.messages):
print(f"Message {i + 1}:")
print(f" Role: {message.role}")
print(f" Content: {message.content.text if hasattr(message.content, 'text') else message.content}")
```
## Raw MCP Protocol Access
For access to the complete MCP protocol objects, use the `*_mcp` methods:
```python
async with client:
# Raw MCP method returns full protocol object
prompts_result = await client.list_prompts_mcp()
# prompts_result -> mcp.types.ListPromptsResult
prompt_result = await client.get_prompt_mcp("example_prompt", {"arg": "value"})
# prompt_result -> mcp.types.GetPromptResult
```
## Multi-Server Clients
When using multi-server clients, prompts are accessible without prefixing (unlike tools):
```python
async with client: # Multi-server client
# Prompts from any server are directly accessible
result1 = await client.get_prompt("weather_prompt", {"city": "London"})
result2 = await client.get_prompt("assistant_prompt", {"query": "help"})
```
## Common Prompt Patterns
### System Messages
Many prompts generate system messages for LLM configuration:
Prompts can generate different message types. System messages configure LLM behavior:
```python
async with client:
@ -208,15 +89,13 @@ async with client:
"role": "helpful assistant",
"expertise": "python programming"
})
# Typically returns messages with role="system"
system_message = result.messages[0]
print(f"System prompt: {system_message.content}")
```
### Conversation Templates
Prompts can generate multi-turn conversation templates:
Conversation templates generate multi-turn flows:
```python
async with client:
@ -224,56 +103,45 @@ async with client:
"candidate_name": "Alice",
"position": "Senior Developer"
})
# Multiple messages for a conversation flow
for message in result.messages:
print(f"{message.role}: {message.content}")
```
<Tip>
Prompt arguments and their expected types depend on the specific prompt implementation. Check the server's documentation or use `list_prompts()` to see available arguments for each prompt.
</Tip>
## Version Selection
<VersionBadge version="3.0.0" />
FastMCP servers can expose multiple versions of the same prompt. By default, clients receive and request the highest version, but you can request a specific version when needed.
### Discovering Versions
When a server registers multiple versions of a prompt, the `list_prompts()` response includes version information in the metadata. The `meta.fastmcp.version` field shows which version is being returned, while `meta.fastmcp.versions` lists all available versions sorted from highest to lowest.
```python
async with client:
prompts = await client.list_prompts()
for prompt in prompts:
if prompt.meta:
fastmcp_meta = prompt.meta.get("fastmcp", {})
version = fastmcp_meta.get("version")
all_versions = fastmcp_meta.get("versions")
if all_versions:
print(f"{prompt.name}: v{version} (available: {all_versions})")
```
Unversioned prompts omit these metadata fields entirely.
### Getting Specific Versions
Pass the `version` parameter to `get_prompt()` to render a specific version instead of the highest.
When a server exposes multiple versions of a prompt, you can request a specific version:
```python
async with client:
# Get the highest version (default)
result = await client.get_prompt("summarize", {"text": "..."})
# Get version 1.0 specifically
# Get a specific version
result_v1 = await client.get_prompt("summarize", {"text": "..."}, version="1.0")
```
If the requested version doesn't exist, the server raises a `NotFoundError`. This ensures you get exactly what you asked for rather than silently falling back to a different version.
See [Metadata](/servers/versioning#version-discovery) for how to discover available versions.
<Note>
Version selection is a FastMCP extension to the MCP protocol. See [Versioning](/servers/versioning#requesting-specific-versions) for details on how this works at the protocol level for non-FastMCP clients.
</Note>
## Multi-Server Clients
When using multi-server clients, prompts are accessible directly without prefixing:
```python
async with client: # Multi-server client
result1 = await client.get_prompt("weather_prompt", {"city": "London"})
result2 = await client.get_prompt("assistant_prompt", {"query": "help"})
```
## Raw Protocol Access
For complete control, use `get_prompt_mcp()` which returns the full MCP protocol object:
```python
async with client:
result = await client.get_prompt_mcp("example_prompt", {"arg": "value"})
# result -> mcp.types.GetPromptResult
```

View file

@ -1,7 +1,7 @@
---
title: Resource Operations
title: Reading Resources
sidebarTitle: Resources
description: Access static and templated resources from MCP servers.
description: Access static and templated data sources from MCP servers.
icon: folder-open
---
@ -9,226 +9,102 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="2.0.0" />
Resources are data sources exposed by MCP servers. They can be static files or dynamic templates that generate content based on parameters.
Use this when you need to read data from server-exposed resources like configuration files, generated content, or external data sources.
## Types of Resources
MCP servers expose two types of resources:
- **Static Resources**: Fixed content accessible via URI (e.g., configuration files, documentation)
- **Resource Templates**: Dynamic resources that accept parameters to generate content (e.g., API endpoints, database queries)
## Listing Resources
### Static Resources
Use `list_resources()` to retrieve all static resources available on the server. When the server paginates results, the client automatically fetches all pages and returns the complete list.
```python
async with client:
resources = await client.list_resources()
# resources -> list[mcp.types.Resource]
for resource in resources:
print(f"Resource URI: {resource.uri}")
print(f"Name: {resource.name}")
print(f"Description: {resource.description}")
print(f"MIME Type: {resource.mimeType}")
# Access tags and other metadata
if resource.meta:
fastmcp_meta = resource.meta.get('fastmcp', {})
print(f"Tags: {fastmcp_meta.get('tags', [])}")
```
For manual pagination control, use `list_resources_mcp()` with the `cursor` parameter. See [Pagination](/servers/pagination#manual-pagination) for details.
### Resource Templates
Use `list_resource_templates()` to retrieve available resource templates. When the server paginates results, the client automatically fetches all pages and returns the complete list.
```python
async with client:
templates = await client.list_resource_templates()
# templates -> list[mcp.types.ResourceTemplate]
for template in templates:
print(f"Template URI: {template.uriTemplate}")
print(f"Name: {template.name}")
print(f"Description: {template.description}")
# Access tags and other metadata
if template.meta:
fastmcp_meta = template.meta.get('fastmcp', {})
print(f"Tags: {fastmcp_meta.get('tags', [])}")
```
For manual pagination control, use `list_resource_templates_mcp()` with the `cursor` parameter. See [Pagination](/servers/pagination#manual-pagination) for details.
### Filtering by Tags
<VersionBadge version="2.11.0" />
You can use the `meta` field to filter resources based on their tags:
```python
async with client:
resources = await client.list_resources()
# Filter resources by tag
config_resources = [
resource for resource in resources
if resource.meta and
resource.meta.get('fastmcp', {}) and
'config' in resource.meta.get('fastmcp', {}).get('tags', [])
]
print(f"Found {len(config_resources)} config resources")
```
<Note>
The `meta` field is part of the standard MCP specification. FastMCP servers always include tags and other metadata within a `fastmcp` namespace (e.g., `meta.fastmcp.tags`) to avoid conflicts with user-defined metadata. For versioned resources, `meta.fastmcp.version` shows the current version and `meta.fastmcp.versions` lists all available versions. Other MCP server implementations may not provide this metadata structure.
</Note>
### Version Information
<VersionBadge version="3.0.0" />
When a server registers multiple versions of a resource, the metadata includes version information.
```python
async with client:
resources = await client.list_resources()
for resource in resources:
if resource.meta:
fastmcp_meta = resource.meta.get("fastmcp", {})
version = fastmcp_meta.get("version")
all_versions = fastmcp_meta.get("versions")
if all_versions:
print(f"{resource.uri}: v{version} (available: {all_versions})")
```
To read a specific version, use the `version` parameter:
```python
# Read a specific version
content = await client.read_resource("data://config", version="1.0")
```
Resources are data sources exposed by MCP servers. They can be static files with fixed content, or dynamic templates that generate content based on parameters in the URI.
## Reading Resources
### Static Resources
Read a static resource using its URI:
Read a resource using its URI:
```python
async with client:
# Read a static resource
content = await client.read_resource("file:///path/to/README.md")
# content -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents]
# content -> list[TextResourceContents | BlobResourceContents]
# Access text content
if hasattr(content[0], 'text'):
print(content[0].text)
# Access binary content
if hasattr(content[0], 'blob'):
print(f"Binary data: {len(content[0].blob)} bytes")
```
### Resource Templates
Read from a resource template by providing the URI with parameters:
Resource templates generate content based on URI parameters. The template defines a pattern like `weather://{{city}}/current`, and you fill in the parameters when reading:
```python
async with client:
# Read a resource generated from a template
# For example, a template like "weather://{{city}}/current"
# Read from a resource template
weather_content = await client.read_resource("weather://london/current")
# Access the generated content
print(weather_content[0].text) # Assuming text JSON response
print(weather_content[0].text)
```
## Content Types
Resources can return different content types:
Resources return different content types depending on what they expose.
### Text Resources
Text resources include configuration files, JSON data, and other human-readable content:
```python
async with client:
content = await client.read_resource("resource://config/settings.json")
for item in content:
if hasattr(item, 'text'):
print(f"Text content: {item.text}")
print(f"MIME type: {item.mimeType}")
```
### Binary Resources
Binary resources include images, PDFs, and other non-text data:
```python
async with client:
content = await client.read_resource("resource://images/logo.png")
for item in content:
if hasattr(item, 'blob'):
print(f"Binary content: {len(item.blob)} bytes")
print(f"MIME type: {item.mimeType}")
# Save to file
with open("downloaded_logo.png", "wb") as f:
f.write(item.blob)
```
## Working with Multi-Server Clients
## Multi-Server Clients
When using multi-server clients, resource URIs are automatically prefixed with the server name:
When using multi-server clients, resource URIs are prefixed with the server name:
```python
async with client: # Multi-server client
# Access resources from different servers
weather_icons = await client.read_resource("weather://weather/icons/sunny")
templates = await client.read_resource("resource://assistant/templates/list")
print(f"Weather icon: {weather_icons[0].blob}")
print(f"Templates: {templates[0].text}")
```
## Raw MCP Protocol Access
## Version Selection
For access to the complete MCP protocol objects, use the `*_mcp` methods:
<VersionBadge version="3.0.0" />
When a server exposes multiple versions of a resource, you can request a specific version:
```python
async with client:
# Raw MCP methods return full protocol objects
resources_result = await client.list_resources_mcp()
# resources_result -> mcp.types.ListResourcesResult
templates_result = await client.list_resource_templates_mcp()
# templates_result -> mcp.types.ListResourceTemplatesResult
content_result = await client.read_resource_mcp("resource://example")
# content_result -> mcp.types.ReadResourceResult
# Read the highest version (default)
content = await client.read_resource("data://config")
# Read a specific version
content_v1 = await client.read_resource("data://config", version="1.0")
```
## Common Resource URI Patterns
See [Metadata](/servers/versioning#version-discovery) for how to discover available versions.
Different MCP servers may use various URI schemes:
## Raw Protocol Access
For complete control, use `read_resource_mcp()` which returns the full MCP protocol object:
```python
# File system resources
"file:///path/to/file.txt"
# Custom protocol resources
"weather://london/current"
"database://users/123"
# Generic resource protocol
"resource://config/settings"
"resource://templates/email"
async with client:
result = await client.read_resource_mcp("resource://example")
# result -> mcp.types.ReadResourceResult
```
<Tip>
Resource URIs and their formats depend on the specific MCP server implementation. Check the server's documentation for available resources and their URI patterns.
</Tip>

View file

@ -9,23 +9,28 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="2.0.0" />
Roots are a way for clients to inform servers about the resources they have access to. Servers can use this information to adjust behavior or provide more relevant responses.
Use this when you need to tell servers what local resources the client has access to.
## Setting Static Roots
Roots inform servers about resources the client can provide. Servers can use this information to adjust behavior or provide more relevant responses.
## Static Roots
Provide a list of roots when creating the client:
<CodeGroup>
```python Static Roots
```python
from fastmcp import Client
client = Client(
"my_mcp_server.py",
"my_mcp_server.py",
roots=["/path/to/root1", "/path/to/root2"]
)
```
```python Dynamic Roots Callback
## Dynamic Roots
Use a callback to compute roots dynamically when the server requests them:
```python
from fastmcp import Client
from fastmcp.client.roots import RequestContext
@ -34,9 +39,7 @@ async def roots_callback(context: RequestContext) -> list[str]:
return ["/path/to/root1", "/path/to/root2"]
client = Client(
"my_mcp_server.py",
"my_mcp_server.py",
roots=roots_callback
)
```
</CodeGroup>

View file

@ -1,7 +1,7 @@
---
title: LLM Sampling
sidebarTitle: Sampling
description: Handle server-initiated LLM sampling requests.
description: Handle server-initiated LLM completion requests.
icon: robot
---
@ -9,137 +9,32 @@ import { VersionBadge } from "/snippets/version-badge.mdx";
<VersionBadge version="2.0.0" />
MCP servers can request LLM completions from clients. The client handles these requests through a sampling handler callback.
Use this when you need to respond to server requests for LLM completions.
## Sampling Handler
MCP servers can request LLM completions from clients during tool execution. This enables servers to delegate AI reasoning to the client, which controls which LLM is used and how requests are made.
Provide a `sampling_handler` function when creating the client:
## Handler Template
```python
from fastmcp import Client
from fastmcp.client.sampling import (
SamplingMessage,
SamplingParams,
RequestContext,
)
from fastmcp.client.sampling import SamplingMessage, SamplingParams, RequestContext
async def sampling_handler(
messages: list[SamplingMessage],
params: SamplingParams,
context: RequestContext
) -> str:
# Your LLM integration logic here
# Extract text from messages and generate a response
return "Generated response based on the messages"
"""
Handle server requests for LLM completions.
client = Client(
"my_mcp_server.py",
sampling_handler=sampling_handler,
)
```
Args:
messages: Conversation messages to send to the LLM
params: Sampling parameters (temperature, max_tokens, etc.)
context: Request context with metadata
### Handler Parameters
The sampling handler receives three parameters:
<Card icon="code" title="Sampling Handler Parameters">
<ResponseField name="SamplingMessage" type="Sampling Message Object">
<Expandable title="attributes">
<ResponseField name="role" type='Literal["user", "assistant"]'>
The role of the message.
</ResponseField>
<ResponseField name="content" type="TextContent | ImageContent | AudioContent">
The content of the message.
TextContent is most common, and has a `.text` attribute.
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="SamplingParams" type="Sampling Parameters Object">
<Expandable title="attributes">
<ResponseField name="messages" type="list[SamplingMessage]">
The messages to sample from
</ResponseField>
<ResponseField name="modelPreferences" type="ModelPreferences | None">
The server's preferences for which model to select. The client MAY ignore
these preferences.
<Expandable title="attributes">
<ResponseField name="hints" type="list[ModelHint] | None">
The hints to use for model selection.
</ResponseField>
<ResponseField name="costPriority" type="float | None">
The cost priority for model selection.
</ResponseField>
<ResponseField name="speedPriority" type="float | None">
The speed priority for model selection.
</ResponseField>
<ResponseField name="intelligencePriority" type="float | None">
The intelligence priority for model selection.
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="systemPrompt" type="str | None">
An optional system prompt the server wants to use for sampling.
</ResponseField>
<ResponseField name="includeContext" type="IncludeContext | None">
A request to include context from one or more MCP servers (including the caller), to
be attached to the prompt.
</ResponseField>
<ResponseField name="temperature" type="float | None">
The sampling temperature.
</ResponseField>
<ResponseField name="maxTokens" type="int">
The maximum number of tokens to sample.
</ResponseField>
<ResponseField name="stopSequences" type="list[str] | None">
The stop sequences to use for sampling.
</ResponseField>
<ResponseField name="metadata" type="dict[str, Any] | None">
Optional metadata to pass through to the LLM provider.
</ResponseField>
<ResponseField name="tools" type="list[Tool] | None">
Optional list of tools the LLM can use during sampling. See [Using the OpenAI Handler](#using-the-openai-handler).
</ResponseField>
<ResponseField name="toolChoice" type="ToolChoice | None">
Optional control over tool usage behavior (`auto`, `required`, or `none`).
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="RequestContext" type="Request Context Object">
<Expandable title="attributes">
<ResponseField name="request_id" type="RequestId">
Unique identifier for the MCP request
</ResponseField>
</Expandable>
</ResponseField>
</Card>
## Basic Example
```python
from fastmcp import Client
from fastmcp.client.sampling import SamplingMessage, SamplingParams, RequestContext
async def basic_sampling_handler(
messages: list[SamplingMessage],
params: SamplingParams,
context: RequestContext
) -> str:
Returns:
Generated text response from your LLM
"""
# Extract message content
conversation = []
for message in messages:
@ -149,44 +44,65 @@ async def basic_sampling_handler(
# Use the system prompt if provided
system_prompt = params.systemPrompt or "You are a helpful assistant."
# Here you would integrate with your preferred LLM service
# This is just a placeholder response
return f"Response based on conversation: {' | '.join(conversation)}"
# Integrate with your LLM service here
return "Generated response based on the messages"
client = Client(
"my_mcp_server.py",
sampling_handler=basic_sampling_handler
sampling_handler=sampling_handler,
)
```
<Note>
If the client doesn't provide a sampling handler, servers can optionally configure a fallback handler. See [Server Sampling](/servers/sampling#sampling-fallback-handler) for details.
</Note>
## Handler Parameters
## Sampling Capabilities
<Card icon="code" title="SamplingMessage">
<ResponseField name="role" type='Literal["user", "assistant"]'>
The role of the message
</ResponseField>
When you provide a `sampling_handler`, FastMCP automatically advertises full sampling capabilities to the server, including tool support. To disable tool support (for simpler handlers that don't support tools), pass `sampling_capabilities` explicitly:
<ResponseField name="content" type="TextContent | ImageContent | AudioContent">
The content of the message. TextContent has a `.text` attribute.
</ResponseField>
</Card>
```python
from mcp.types import SamplingCapability
<Card icon="code" title="SamplingParams">
<ResponseField name="systemPrompt" type="str | None">
Optional system prompt the server wants to use
</ResponseField>
client = Client(
"my_mcp_server.py",
sampling_handler=basic_handler,
sampling_capabilities=SamplingCapability(), # No tool support
)
```
<ResponseField name="modelPreferences" type="ModelPreferences | None">
Server preferences for model selection (hints, cost/speed/intelligence priorities)
</ResponseField>
<ResponseField name="temperature" type="float | None">
Sampling temperature
</ResponseField>
<ResponseField name="maxTokens" type="int">
Maximum tokens to generate
</ResponseField>
<ResponseField name="stopSequences" type="list[str] | None">
Stop sequences for sampling
</ResponseField>
<ResponseField name="tools" type="list[Tool] | None">
Tools the LLM can use during sampling
</ResponseField>
<ResponseField name="toolChoice" type="ToolChoice | None">
Tool usage behavior (`auto`, `required`, or `none`)
</ResponseField>
</Card>
## Built-in Handlers
FastMCP provides built-in sampling handlers for OpenAI and Anthropic APIs. These handlers support the full sampling API including tool use, handling message conversion and response formatting automatically.
FastMCP provides built-in handlers for OpenAI and Anthropic APIs that support the full sampling API including tool use.
### OpenAI Handler
<VersionBadge version="2.11.0" />
The OpenAI handler works with OpenAI's API and any OpenAI-compatible provider:
```python
from fastmcp import Client
from fastmcp.client.sampling.handlers.openai import OpenAISamplingHandler
@ -197,7 +113,7 @@ client = Client(
)
```
For OpenAI-compatible APIs (like local models), pass a custom client:
For OpenAI-compatible APIs (like local models):
```python
from openai import AsyncOpenAI
@ -219,8 +135,6 @@ Install the OpenAI handler with `pip install fastmcp[openai]`.
<VersionBadge version="2.14.1" />
The Anthropic handler uses Claude models via the Anthropic API:
```python
from fastmcp import Client
from fastmcp.client.sampling.handlers.anthropic import AnthropicSamplingHandler
@ -231,28 +145,28 @@ client = Client(
)
```
You can pass a custom client for advanced configuration:
```python
from anthropic import AsyncAnthropic
client = Client(
"my_mcp_server.py",
sampling_handler=AnthropicSamplingHandler(
default_model="claude-sonnet-4-5",
client=AsyncAnthropic(), # Uses ANTHROPIC_API_KEY env var
),
)
```
<Note>
Install the Anthropic handler with `pip install fastmcp[anthropic]`.
</Note>
### Tool Execution
## Sampling Capabilities
When you provide a `sampling_handler`, FastMCP automatically advertises full sampling capabilities to the server, including tool support. To disable tool support for simpler handlers:
```python
from mcp.types import SamplingCapability
client = Client(
"my_mcp_server.py",
sampling_handler=basic_handler,
sampling_capabilities=SamplingCapability(), # No tool support
)
```
## Tool Execution
Tool execution happens on the server side. The client's role is to pass tools to the LLM and return the LLM's response (which may include tool use requests). The server then executes the tools and may send follow-up sampling requests with tool results.
<Tip>
To implement a custom sampling handler, see the [handler source code](https://github.com/jlowin/fastmcp/tree/main/src/fastmcp/client/sampling/handlers) as a reference.
</Tip>
</Tip>

View file

@ -1,7 +1,7 @@
---
title: Background Tasks
sidebarTitle: Background Tasks
description: Execute operations asynchronously and track their progress
sidebarTitle: Tasks
description: Execute operations asynchronously and track their progress.
icon: clock
tag: "NEW"
---
@ -10,13 +10,13 @@ import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.14.0" />
The [MCP task protocol](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks) lets you request operations to run asynchronously. This returns a Task object immediately, letting you track progress, cancel operations, or await results.
Use this when you need to run long operations asynchronously while doing other work.
See [Server Background Tasks](/servers/tasks) for how to enable this on the server side.
The MCP task protocol lets you request operations to run in the background. The call returns a Task object immediately, letting you track progress, cancel operations, or await results.
## Requesting Background Execution
Pass `task=True` to run an operation as a background task. The call returns immediately with a Task object while the work executes on the server.
Pass `task=True` to run an operation as a background task:
```python
from fastmcp import Client
@ -41,24 +41,35 @@ resource_task = await client.read_resource("file://large.txt", task=True)
prompt_task = await client.get_prompt("my_prompt", args, task=True)
```
## Working with Task Objects
## Task API
All task types share a common interface for retrieving results, checking status, and receiving updates.
All task types share a common interface.
To get the result, call `await task.result()` or simply `await task`. This blocks until the task completes and returns the result. You can also check status without blocking using `await task.status()`, which returns the current state (`"working"`, `"completed"`, `"failed"`, or `"cancelled"`) along with any progress message from the server.
### Getting Results
Call `await task.result()` or simply `await task` to block until the task completes:
```python
task = await client.call_tool("analyze", {"text": "hello"}, task=True)
# Check current status (non-blocking)
status = await task.status()
print(f"{status.status}: {status.statusMessage}")
# Wait for result (blocking)
result = await task.result()
# or: result = await task
```
For more control over waiting, use `task.wait()` with an optional timeout or target state:
### Checking Status
Check the current status without blocking:
```python
status = await task.status()
print(f"{status.status}: {status.statusMessage}")
# status.status is "working", "completed", "failed", or "cancelled"
```
### Waiting with Control
Use `task.wait()` for more control over waiting:
```python
# Wait up to 30 seconds for completion
@ -68,11 +79,17 @@ status = await task.wait(timeout=30.0)
status = await task.wait(state="completed", timeout=30.0)
```
To cancel a running task, call `await task.cancel()`.
### Cancellation
### Real-Time Status Updates
Cancel a running task:
Register callbacks to receive status updates as the server reports progress. Both sync and async callbacks are supported.
```python
await task.cancel()
```
## Status Updates
Register callbacks to receive real-time status updates as the server reports progress:
```python
def on_status_change(status):
@ -87,9 +104,34 @@ async def on_status_async(status):
task.on_status_change(on_status_async)
```
### Handler Template
```python
from fastmcp import Client
def status_handler(status):
"""
Handle task status updates.
Args:
status: Task status object with:
- taskId: Unique task identifier
- status: "working", "completed", "failed", or "cancelled"
- statusMessage: Optional progress message from server
"""
if status.status == "working":
print(f"Progress: {status.statusMessage}")
elif status.status == "completed":
print("Task completed")
elif status.status == "failed":
print(f"Task failed: {status.statusMessage}")
task.on_status_change(status_handler)
```
## Graceful Degradation
You can always pass `task=True` regardless of whether the server supports background tasks. Per the MCP specification, servers without task support execute the operation immediately and return the result inline. The Task API provides a consistent interface either way.
You can always pass `task=True` regardless of whether the server supports background tasks. Per the MCP specification, servers without task support execute the operation immediately and return the result inline.
```python
task = await client.call_tool("my_tool", args, task=True)
@ -103,9 +145,9 @@ else:
result = await task.result()
```
This means you can write task-aware client code without worrying about server capabilities.
This lets you write task-aware client code without worrying about server capabilities.
## Complete Example
## Example
```python
import asyncio
@ -136,3 +178,5 @@ async def main():
asyncio.run(main())
```
See [Server Background Tasks](/servers/tasks) for how to enable background task support on the server side.

View file

@ -1,7 +1,7 @@
---
title: Tool Operations
title: Calling Tools
sidebarTitle: Tools
description: Discover and execute server-side tools with the FastMCP client.
description: Execute server-side tools and handle structured results.
icon: wrench
---
@ -9,88 +9,40 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="2.0.0" />
Tools are executable functions exposed by MCP servers. The FastMCP client provides methods to discover available tools and execute them with arguments.
Use this when you need to execute server-side functions and process their results.
## Discovering Tools
Tools are executable functions exposed by MCP servers. The client's `call_tool()` method executes a tool by name with arguments and returns structured results.
Use `list_tools()` to retrieve all tools available on the server. When the server paginates results, the client automatically fetches all pages and returns the complete list.
## Basic Execution
```python
async with client:
tools = await client.list_tools()
# tools -> list[mcp.types.Tool]
for tool in tools:
print(f"Tool: {tool.name}")
print(f"Description: {tool.description}")
if tool.inputSchema:
print(f"Parameters: {tool.inputSchema}")
# Access tags and other metadata
if tool.meta:
fastmcp_meta = tool.meta.get('fastmcp', {})
print(f"Tags: {fastmcp_meta.get('tags', [])}")
```
For manual pagination control, use `list_tools_mcp()` with the `cursor` parameter. See [Pagination](/servers/pagination#manual-pagination) for details.
### Filtering by Tags
<VersionBadge version="2.11.0" />
You can use the `meta` field to filter tools based on their tags:
```python
async with client:
tools = await client.list_tools()
# Filter tools by tag
analysis_tools = [
tool for tool in tools
if tool.meta and
tool.meta.get('fastmcp', {}) and
'analysis' in tool.meta.get('fastmcp', {}).get('tags', [])
]
print(f"Found {len(analysis_tools)} analysis tools")
```
<Note>
The `meta` field is part of the standard MCP specification. FastMCP servers always include tags and other metadata within a `fastmcp` namespace (e.g., `meta.fastmcp.tags`) to avoid conflicts with user-defined metadata. Component versions are also included in the metadata when available (e.g., `meta.fastmcp.version`). Other MCP server implementations may not provide this metadata structure.
</Note>
## Executing Tools
### Basic Execution
Execute a tool using `call_tool()` with the tool name and arguments:
```python
async with client:
# Simple tool call
result = await client.call_tool("add", {"a": 5, "b": 3})
# result -> CallToolResult with structured and unstructured data
# Access structured data (automatically deserialized)
print(result.data) # 8 (int) or {"result": 8} for primitive types
# Access traditional content blocks
print(result.content[0].text) # "8" (TextContent)
print(result.data) # 8
# Access traditional content blocks
print(result.content[0].text) # "8"
```
### Advanced Execution Options
Arguments are passed as a dictionary. For multi-server clients, tool names are automatically prefixed with the server name (e.g., `weather_get_forecast` for a tool named `get_forecast` on the `weather` server).
The `call_tool()` method supports additional parameters for timeout control and progress monitoring:
## Execution Options
The `call_tool()` method supports timeout control and progress monitoring:
```python
async with client:
# With timeout (aborts if execution takes longer than 2 seconds)
result = await client.call_tool(
"long_running_task",
{"param": "value"},
"long_running_task",
{"param": "value"},
timeout=2.0
)
# With progress handler (to track execution progress)
# With progress handler
result = await client.call_tool(
"long_running_task",
{"param": "value"},
@ -98,19 +50,103 @@ async with client:
)
```
**Parameters:**
- `name`: The tool name (string)
- `arguments`: Dictionary of arguments to pass to the tool (optional)
- `version`: Specific tool version to call (optional, see [Version Selection](#version-selection) below)
- `timeout`: Maximum execution time in seconds (optional, overrides client-level timeout)
- `progress_handler`: Progress callback function (optional, overrides client-level handler)
- `meta`: Dictionary of metadata to send with the request (optional, see below)
## Structured Results
<VersionBadge version="2.10.0" />
Tool execution returns a `CallToolResult` object. The `.data` property provides fully hydrated Python objects including complex types like datetimes and UUIDs, reconstructed from the server's output schema.
```python
from datetime import datetime
from uuid import UUID
async with client:
result = await client.call_tool("get_weather", {"city": "London"})
# FastMCP reconstructs complete Python objects
weather = result.data
print(f"Temperature: {weather.temperature}C at {weather.timestamp}")
# Complex types are properly deserialized
assert isinstance(weather.timestamp, datetime)
assert isinstance(weather.station_id, UUID)
# Raw structured JSON is also available
print(f"Raw JSON: {result.structured_content}")
```
<Card icon="code" title="CallToolResult Properties">
<ResponseField name=".data" type="Any">
Fully hydrated Python objects with complex type support (datetimes, UUIDs, custom classes). FastMCP exclusive.
</ResponseField>
<ResponseField name=".content" type="list[mcp.types.ContentBlock]">
Standard MCP content blocks (`TextContent`, `ImageContent`, `AudioContent`, etc.).
</ResponseField>
<ResponseField name=".structured_content" type="dict[str, Any] | None">
Standard MCP structured JSON data as sent by the server.
</ResponseField>
<ResponseField name=".is_error" type="bool">
Boolean indicating if the tool execution failed.
</ResponseField>
</Card>
For tools without output schemas or when deserialization fails, `.data` will be `None`. Fall back to content blocks in that case:
```python
async with client:
result = await client.call_tool("legacy_tool", {"param": "value"})
if result.data is not None:
print(f"Structured: {result.data}")
else:
for content in result.content:
if hasattr(content, 'text'):
print(f"Text result: {content.text}")
```
<Tip>
FastMCP servers automatically wrap primitive results (like `int`, `str`, `bool`) in a `{"result": value}` structure. FastMCP clients automatically unwrap this, so you get the original value in `.data`.
</Tip>
## Error Handling
By default, `call_tool()` raises a `ToolError` if the tool execution fails:
```python
from fastmcp.exceptions import ToolError
async with client:
try:
result = await client.call_tool("potentially_failing_tool", {"param": "value"})
print("Tool succeeded:", result.data)
except ToolError as e:
print(f"Tool failed: {e}")
```
To handle errors manually instead of catching exceptions, disable automatic error raising:
```python
async with client:
result = await client.call_tool(
"potentially_failing_tool",
{"param": "value"},
raise_on_error=False
)
if result.is_error:
print(f"Tool failed: {result.content[0].text}")
else:
print(f"Tool succeeded: {result.data}")
```
## Sending Metadata
<VersionBadge version="2.13.1" />
The `meta` parameter sends ancillary information alongside tool calls. This can be used for various purposes like observability, debugging, client identification, or any context the server may need beyond the tool's primary arguments.
The `meta` parameter sends ancillary information alongside tool calls for observability, debugging, or client identification:
```python
async with client:
@ -128,215 +164,20 @@ async with client:
)
```
The structure and usage of `meta` is determined by your application. See [Client Metadata](/servers/context#client-metadata) in the server documentation to learn how to access this data in your tool implementations.
See [Client Metadata](/servers/context#client-metadata) to learn how servers access this data.
## Handling Results
<VersionBadge version="2.10.0" />
Tool execution returns a `CallToolResult` object with both structured and traditional content. FastMCP's standout feature is the `.data` property, which doesn't just provide raw JSON but actually hydrates complete Python objects including complex types like datetimes, UUIDs, and custom classes.
### CallToolResult Properties
<Card icon="code" title="CallToolResult Properties">
<ResponseField name=".data" type="Any">
**FastMCP exclusive**: Fully hydrated Python objects with complex type support (datetimes, UUIDs, custom classes). Goes beyond JSON to provide complete object reconstruction from output schemas.
</ResponseField>
<ResponseField name=".content" type="list[mcp.types.ContentBlock]">
Standard MCP content blocks (`TextContent`, `ImageContent`, `AudioContent`, etc.) available from all MCP servers.
</ResponseField>
<ResponseField name=".structured_content" type="dict[str, Any] | None">
Standard MCP structured JSON data as sent by the server, available from all MCP servers that support structured outputs.
</ResponseField>
<ResponseField name=".is_error" type="bool">
Boolean indicating if the tool execution failed.
</ResponseField>
</Card>
### Structured Data Access
FastMCP's `.data` property provides fully hydrated Python objects, not just JSON dictionaries. This includes complex type reconstruction:
```python
from datetime import datetime
from uuid import UUID
async with client:
result = await client.call_tool("get_weather", {"city": "London"})
# FastMCP reconstructs complete Python objects from the server's output schema
weather = result.data # Server-defined WeatherReport object
print(f"Temperature: {weather.temperature}°C at {weather.timestamp}")
print(f"Station: {weather.station_id}")
print(f"Humidity: {weather.humidity}%")
# The timestamp is a real datetime object, not a string!
assert isinstance(weather.timestamp, datetime)
assert isinstance(weather.station_id, UUID)
# Compare with raw structured JSON (standard MCP)
print(f"Raw JSON: {result.structured_content}")
# {"temperature": 20, "timestamp": "2024-01-15T14:30:00Z", "station_id": "123e4567-..."}
# Traditional content blocks (standard MCP)
print(f"Text content: {result.content[0].text}")
```
### Fallback Behavior
For tools without output schemas or when deserialization fails, `.data` will be `None`:
```python
async with client:
result = await client.call_tool("legacy_tool", {"param": "value"})
if result.data is not None:
# Structured output available and successfully deserialized
print(f"Structured: {result.data}")
else:
# No structured output or deserialization failed - use content blocks
for content in result.content:
if hasattr(content, 'text'):
print(f"Text result: {content.text}")
elif hasattr(content, 'data'):
print(f"Binary data: {len(content.data)} bytes")
```
### Primitive Type Unwrapping
<Tip>
FastMCP servers automatically wrap non-object results (like `int`, `str`, `bool`) in a `{"result": value}` structure to create valid structured outputs. FastMCP clients understand this convention and automatically unwrap the value in `.data` for convenience, so you get the original primitive value instead of a wrapper object.
</Tip>
```python
async with client:
result = await client.call_tool("calculate_sum", {"a": 5, "b": 3})
# FastMCP client automatically unwraps for convenience
print(result.data) # 8 (int) - the original value
# Raw structured content shows the server-side wrapping
print(result.structured_content) # {"result": 8}
# Other MCP clients would need to manually access ["result"]
# value = result.structured_content["result"] # Not needed with FastMCP!
```
## Error Handling
### Exception-Based Error Handling
By default, `call_tool()` raises a `ToolError` if the tool execution fails:
```python
from fastmcp.exceptions import ToolError
async with client:
try:
result = await client.call_tool("potentially_failing_tool", {"param": "value"})
print("Tool succeeded:", result.data)
except ToolError as e:
print(f"Tool failed: {e}")
```
### Manual Error Checking
You can disable automatic error raising and manually check the result:
```python
async with client:
result = await client.call_tool(
"potentially_failing_tool",
{"param": "value"},
raise_on_error=False
)
if result.is_error:
print(f"Tool failed: {result.content[0].text}")
else:
print(f"Tool succeeded: {result.data}")
```
### Raw MCP Protocol Access
## Raw Protocol Access
For complete control, use `call_tool_mcp()` which returns the raw MCP protocol object:
```python
async with client:
result = await client.call_tool_mcp("potentially_failing_tool", {"param": "value"})
result = await client.call_tool_mcp("my_tool", {"param": "value"})
# result -> mcp.types.CallToolResult
if result.isError:
print(f"Tool failed: {result.content}")
else:
print(f"Tool succeeded: {result.content}")
# Note: No automatic deserialization with call_tool_mcp()
```
## Argument Handling
Arguments are passed as a dictionary to the tool:
```python
async with client:
# Simple arguments
result = await client.call_tool("greet", {"name": "World"})
# Complex arguments
result = await client.call_tool("process_data", {
"config": {"format": "json", "validate": True},
"items": [1, 2, 3, 4, 5],
"metadata": {"source": "api", "version": "1.0"}
})
```
<Tip>
For multi-server clients, tool names are automatically prefixed with the server name (e.g., `weather_get_forecast` for a tool named `get_forecast` on the `weather` server).
</Tip>
## Version Selection
<VersionBadge version="3.0.0" />
FastMCP servers can expose multiple versions of the same tool. By default, clients receive and call the highest version, but you can request a specific version when needed.
### Discovering Versions
When a server registers multiple versions of a tool, the `list_tools()` response includes version information in the metadata. The `meta.fastmcp.version` field shows which version is being returned, while `meta.fastmcp.versions` lists all available versions sorted from highest to lowest.
```python
async with client:
tools = await client.list_tools()
for tool in tools:
if tool.meta:
fastmcp_meta = tool.meta.get("fastmcp", {})
version = fastmcp_meta.get("version")
all_versions = fastmcp_meta.get("versions")
if all_versions:
print(f"{tool.name}: v{version} (available: {all_versions})")
```
Unversioned tools omit these metadata fields entirely.
### Calling Specific Versions
Pass the `version` parameter to `call_tool()` to execute a specific version instead of the highest.
```python
async with client:
# Call the highest version (default)
result = await client.call_tool("calculate", {"x": 1, "y": 2})
# Call version 1.0 specifically
result_v1 = await client.call_tool("calculate", {"x": 1, "y": 2}, version="1.0")
```
If the requested version doesn't exist, the server raises a `NotFoundError`. This ensures you get exactly what you asked for rather than silently falling back to a different version.
<Note>
Version selection is a FastMCP extension to the MCP protocol. See [Versioning](/servers/versioning#requesting-specific-versions) for details on how this works at the protocol level for non-FastMCP clients.
</Note>

View file

@ -1,7 +1,7 @@
---
title: Client Transports
sidebarTitle: Transports
description: Configure how FastMCP Clients connect to and communicate with servers.
description: Configure how clients connect to and communicate with MCP servers.
icon: link
---
@ -9,88 +9,30 @@ import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.0.0" />
The FastMCP `Client` communicates with MCP servers through transport objects that handle the underlying connection mechanics. While the client can automatically select a transport based on what you pass to it, instantiating transports explicitly gives you full control over configuration—environment variables, authentication, session management, and more.
Think of transports as configurable adapters between your client code and MCP servers. Each transport type handles a different communication pattern: subprocesses with pipes, HTTP connections, or direct in-memory calls.
## Choosing the Right Transport
- **Use [STDIO Transport](#stdio-transport)** when you need to run local MCP servers with full control over their environment and lifecycle
- **Use [Remote Transports](#remote-transports)** when connecting to production services or shared MCP servers running independently
- **Use [In-Memory Transport](#in-memory-transport)** for testing FastMCP servers without subprocess or network overhead
- **Use [MCP JSON Configuration](#mcp-json-configuration-transport)** when you need to connect to multiple servers defined in configuration files
Transports handle the underlying connection between your client and MCP servers. While the client can automatically select a transport based on what you pass to it, instantiating transports explicitly gives you full control over configuration.
## STDIO Transport
STDIO (Standard Input/Output) transport communicates with MCP servers through subprocess pipes. This is the standard mechanism used by desktop clients like Claude Desktop and is the primary way to run local MCP servers.
### The Client Runs the Server
STDIO transport communicates with MCP servers through subprocess pipes. When using STDIO, your client launches and manages the server process, controlling its lifecycle and environment.
<Warning>
**Critical Concept**: When using STDIO transport, your client actually launches and manages the server process. This is fundamentally different from network transports where you connect to an already-running server. Understanding this relationship is key to using STDIO effectively.
STDIO servers run in isolated environments by default. They do not inherit your shell's environment variables. You must explicitly pass any configuration the server needs.
</Warning>
With STDIO transport, your client:
- Starts the server as a subprocess when you connect
- Manages the server's lifecycle (start, stop, restart)
- Controls the server's environment and configuration
- Communicates through stdin/stdout pipes
This architecture enables powerful local integrations but requires understanding environment isolation and process management.
### Environment Isolation
STDIO servers run in isolated environments by default. This is a security feature enforced by the MCP protocol to prevent accidental exposure of sensitive data.
When your client launches an MCP server:
- The server does NOT inherit your shell's environment variables
- API keys, paths, and other configuration must be explicitly passed
- The working directory and system paths may differ from your shell
To pass environment variables to your server, use the `env` parameter:
```python
from fastmcp import Client
# If your server needs environment variables (like API keys),
# you must explicitly pass them:
client = Client(
"my_server.py",
env={"API_KEY": "secret", "DEBUG": "true"}
)
# This won't work - the server runs in isolation:
# export API_KEY="secret" # in your shell
# client = Client("my_server.py") # server can't see API_KEY
```
### Basic Usage
To use STDIO transport, you create a transport instance with the command and arguments needed to run your server:
```python
from fastmcp.client.transports import StdioTransport
transport = StdioTransport(
command="python",
args=["my_server.py"]
)
client = Client(transport)
```
You can configure additional settings like environment variables, working directory, or command arguments:
```python
transport = StdioTransport(
command="python",
args=["my_server.py", "--verbose"],
env={"LOG_LEVEL": "DEBUG"},
env={"API_KEY": "secret", "LOG_LEVEL": "DEBUG"},
cwd="/path/to/server"
)
client = Client(transport)
```
For convenience, the client can also infer STDIO transport from file paths, but this doesn't allow configuration:
For convenience, the client can infer STDIO transport from file paths, though this limits configuration options:
```python
from fastmcp import Client
@ -100,26 +42,18 @@ client = Client("my_server.py") # Limited - no configuration options
### Environment Variables
Since STDIO servers don't inherit your environment, you need strategies for passing configuration. Here are two common approaches:
Since STDIO servers do not inherit your environment, you need strategies for passing configuration.
**Selective forwarding** passes only the variables your server actually needs:
**Selective forwarding** passes only the variables your server needs:
```python
import os
from fastmcp.client.transports import StdioTransport
required_vars = ["API_KEY", "DATABASE_URL", "REDIS_HOST"]
env = {
var: os.environ[var]
for var in required_vars
if var in os.environ
}
env = {var: os.environ[var] for var in required_vars if var in os.environ}
transport = StdioTransport(
command="python",
args=["server.py"],
env=env
)
transport = StdioTransport(command="python", args=["server.py"], env=env)
client = Client(transport)
```
@ -130,33 +64,24 @@ from dotenv import dotenv_values
from fastmcp.client.transports import StdioTransport
env = dotenv_values(".env")
transport = StdioTransport(
command="python",
args=["server.py"],
env=env
)
transport = StdioTransport(command="python", args=["server.py"], env=env)
client = Client(transport)
```
### Session Persistence
STDIO transports maintain sessions across multiple client contexts by default (`keep_alive=True`). This improves performance by reusing the same subprocess for multiple connections, but can be controlled when you need isolation.
By default, the subprocess persists between connections:
STDIO transports maintain sessions across multiple client contexts by default (`keep_alive=True`). This reuses the same subprocess for multiple connections, improving performance.
```python
from fastmcp.client.transports import StdioTransport
transport = StdioTransport(
command="python",
args=["server.py"]
)
transport = StdioTransport(command="python", args=["server.py"])
client = Client(transport)
async def efficient_multiple_operations():
async with client:
await client.ping()
async with client: # Reuses the same subprocess
await client.call_tool("process_data", {"file": "data.csv"})
```
@ -164,51 +89,19 @@ async def efficient_multiple_operations():
For complete isolation between connections, disable session persistence:
```python
transport = StdioTransport(
command="python",
args=["server.py"],
keep_alive=False
)
client = Client(transport)
transport = StdioTransport(command="python", args=["server.py"], keep_alive=False)
```
Use `keep_alive=False` when you need complete isolation (e.g., in test suites) or when server state could cause issues between connections.
### Specialized STDIO Transports
FastMCP provides convenience transports that are thin wrappers around `StdioTransport` with pre-configured commands:
- **`PythonStdioTransport`** - Uses `python` command for `.py` files
- **`NodeStdioTransport`** - Uses `node` command for `.js` files
- **`UvStdioTransport`** - Uses `uv` for Python packages (uses `env_vars` parameter)
- **`UvxStdioTransport`** - Uses `uvx` for Python packages (uses `env_vars` parameter)
- **`NpxStdioTransport`** - Uses `npx` for Node packages (uses `env_vars` parameter)
For most use cases, instantiate `StdioTransport` directly with your desired command. These specialized transports are primarily useful for client inference shortcuts.
## Remote Transports
Remote transports connect to MCP servers running as web services. This is a fundamentally different model from STDIO transports—instead of your client launching and managing a server process, you connect to an already-running service that manages its own environment and lifecycle.
### Streamable HTTP Transport
## HTTP Transport
<VersionBadge version="2.3.0" />
Streamable HTTP is the recommended transport for production deployments, providing efficient bidirectional streaming over HTTP connections.
- **Class:** `StreamableHttpTransport`
- **Server compatibility:** FastMCP servers running with `mcp run --transport http`
The transport requires a URL and optionally supports custom headers for authentication and configuration:
HTTP transport connects to MCP servers running as web services. This is the recommended transport for production deployments.
```python
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport
# Basic connection
transport = StreamableHttpTransport(url="https://api.example.com/mcp")
client = Client(transport)
# With custom headers for authentication
transport = StreamableHttpTransport(
url="https://api.example.com/mcp",
headers={
@ -219,9 +112,10 @@ transport = StreamableHttpTransport(
client = Client(transport)
```
For convenience, FastMCP also provides authentication helpers:
FastMCP also provides authentication helpers:
```python
from fastmcp import Client
from fastmcp.client.auth import BearerAuth
client = Client(
@ -230,14 +124,9 @@ client = Client(
)
```
### SSE Transport (Legacy)
### SSE Transport
Server-Sent Events transport is maintained for backward compatibility but is superseded by Streamable HTTP for new deployments.
- **Class:** `SSETransport`
- **Server compatibility:** FastMCP servers running with `mcp run --transport sse`
SSE transport supports the same configuration options as Streamable HTTP:
Server-Sent Events transport is maintained for backward compatibility. Use Streamable HTTP for new deployments unless you have specific infrastructure requirements.
```python
from fastmcp.client.transports import SSETransport
@ -249,17 +138,9 @@ transport = SSETransport(
client = Client(transport)
```
Use Streamable HTTP for new deployments unless you have specific infrastructure requirements for SSE.
## In-Memory Transport
In-memory transport connects directly to a FastMCP server instance within the same Python process. This eliminates both subprocess management and network overhead, making it ideal for testing and development.
- **Class:** `FastMCPTransport`
<Note>
Unlike STDIO transports, in-memory servers have full access to your Python process's environment. They share the same memory space and environment variables as your client code—no isolation or explicit environment passing required.
</Note>
In-memory transport connects directly to a FastMCP server instance within the same Python process. This eliminates both subprocess management and network overhead, making it ideal for testing.
```python
from fastmcp import FastMCP, Client
@ -278,15 +159,19 @@ async with client:
result = await client.call_tool("greet", {"name": "World"})
```
## MCP JSON Configuration Transport
<Note>
Unlike STDIO transports, in-memory servers share the same memory space and environment variables as your client code.
</Note>
## Multi-Server Configuration
<VersionBadge version="2.4.0" />
This transport supports the emerging MCP JSON configuration standard for defining multiple servers:
- **Class:** `MCPConfigTransport`
Connect to multiple servers defined in a configuration dictionary:
```python
from fastmcp import Client
config = {
"mcpServers": {
"weather": {
@ -309,9 +194,9 @@ async with client:
answer = await client.call_tool("assistant_ask", {"question": "What?"})
```
### Tool Transformation with FastMCP and MCPConfig
### Tool Transformations
FastMCP supports basic tool transformations to be defined alongside the MCP Servers in the MCPConfig file.
FastMCP supports tool transformations within the configuration. You can change names, descriptions, tags, and arguments for tools from a server.
```python
config = {
@ -319,65 +204,32 @@ config = {
"weather": {
"url": "https://weather.example.com/mcp",
"transport": "http",
"tools": { } # <--- This is the tool transformation section
}
}
}
```
With these transformations, you can transform (change) the name, title, description, tags, enablement, and arguments of a tool.
For each argument the tool takes, you can transform (change) the name, description, default, visibility, whether it's required, and you can provide example values.
In the following example, we're transforming the `weather_get_forecast` tool to only retrieve the weather for `Miami` and hiding the `city` argument from the client.
```python
tool_transformations = {
"weather_get_forecast": {
"name": "miami_weather",
"description": "Get the weather for Miami",
"arguments": {
"city": {
"name": "city",
"default": "Miami",
"hide": True,
"tools": {
"weather_get_forecast": {
"name": "miami_weather",
"description": "Get the weather for Miami",
"arguments": {
"city": {
"default": "Miami",
"hide": True,
}
}
}
}
}
}
}
```
To filter tools by tag, use `include_tags` or `exclude_tags` at the server level:
```python
config = {
"mcpServers": {
"weather": {
"url": "https://weather.example.com/mcp",
"transport": "http",
"tools": tool_transformations
"include_tags": ["forecast"] # Only tools with this tag
}
}
}
```
#### Allowlisting and Blocklisting Tools
Tools can be allowlisted or blocklisted from the client by applying `tags` to the tools on the server. In the following example, we're allowlisting only tools marked with the `forecast` tag, all other tools will be unavailable to the client.
```python
tool_transformations = {
"weather_get_forecast": {
"enabled": True,
"tags": ["forecast"]
}
}
config = {
"mcpServers": {
"weather": {
"url": "https://weather.example.com/mcp",
"transport": "http",
"tools": tool_transformations,
"include_tags": ["forecast"]
}
}
}
```

View file

@ -17,4 +17,43 @@ h6 code:not(pre code) {
background-color: rgba(247, 37, 133, 0.09);
}
/* V2 banner - inside content-container, breaks out of padding with negative margins */
#v2-banner {
display: block;
background: linear-gradient(135deg, #4cc9f0 0%, #2d00f7 100%);
color: white;
text-align: center;
padding: 10px 16px;
font-size: 0.875rem;
font-weight: 600;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin: -2rem -2rem 1.5rem -2rem;
width: calc(100% + 4rem);
border-radius: 8px 8px 0 0;
}
#v2-banner a {
color: white;
text-decoration: underline;
font-weight: 700;
}
#v2-banner a:hover {
opacity: 0.9;
}
@media (min-width: 1024px) {
#v2-banner {
margin: -3rem -4rem 1.5rem -4rem;
width: calc(100% + 8rem);
}
}
.dark #v2-banner {
background: linear-gradient(135deg, #2d00f7 0%, #4cc9f0 100%);
}

View file

@ -3,7 +3,6 @@ title: HTTP Deployment
sidebarTitle: HTTP Deployment
description: Deploy your FastMCP server over HTTP for remote access
icon: server
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx";

File diff suppressed because it is too large Load diff

View file

@ -47,7 +47,7 @@ You should see output like the following:
```bash
$ fastmcp version
FastMCP version: 2.11.3
FastMCP version: 3.0.0
MCP version: 1.12.4
Python version: 3.12.2
Platform: macOS-15.3.1-arm64-arm-64bit
@ -69,7 +69,7 @@ Alternatively, wait for the stable v5 release. See [this issue](https://github.c
</Info>
## Upgrading from the Official MCP SDK
Upgrading from the official MCP SDK's FastMCP 1.0 to FastMCP 2.0 is generally straightforward. The core server API is highly compatible, and in many cases, changing your import statement from `from mcp.server.fastmcp import FastMCP` to `from fastmcp import FastMCP` will be sufficient.
Upgrading from the official MCP SDK's FastMCP 1.0 to FastMCP 3.0 is generally straightforward. The core server API is highly compatible, and in many cases, changing your import statement from `from mcp.server.fastmcp import FastMCP` to `from fastmcp import FastMCP` will be sufficient.
```python {5}
@ -83,7 +83,7 @@ mcp = FastMCP("My MCP Server")
```
<Warning>
Prior to `fastmcp==2.3.0` and `mcp==1.8.0`, the 2.x API always mirrored the official 1.0 API. However, as the projects diverge, this can not be guaranteed. You may see deprecation warnings if you attempt to use 1.0 APIs in FastMCP 2.x. Please refer to this documentation for details on new capabilities.
Prior to `fastmcp==2.3.0` and `mcp==1.8.0`, the 2.x API always mirrored the official 1.0 API. However, as the projects diverge, this can not be guaranteed. You may see deprecation warnings if you attempt to use 1.0 APIs in FastMCP 3.x. Please refer to this documentation for details on new capabilities.
</Warning>
## Versioning Policy
@ -92,8 +92,8 @@ FastMCP follows semantic versioning with pragmatic adaptations for the rapidly e
For production use, always pin to exact versions:
```
fastmcp==2.11.0 # Good
fastmcp>=2.11.0 # Bad - will install breaking changes
fastmcp==3.0.0 # Good
fastmcp>=3.0.0 # Bad - may install breaking changes
```
See the full [versioning and release policy](/development/releases#versioning-policy) for details on our public API, deprecation practices, and breaking change philosophy.

View file

@ -1,18 +1,18 @@
---
title: "Welcome to FastMCP 2.0!"
title: "Welcome to FastMCP 3.0!"
sidebarTitle: "Welcome!"
description: The fast, Pythonic way to build MCP servers and clients.
icon: hand-wave
---
<img
src="/assets/brand/f-watercolor-waves.png"
<img
src="/assets/brand/f-watercolor-waves.png"
alt="'F' logo on a watercolor background"
noZoom
className="rounded-2xl block dark:hidden"
/>
<img
src="/assets/brand/f-watercolor-waves-dark.png"
<img
src="/assets/brand/f-watercolor-waves-dark.png"
alt="'F' logo on a watercolor background"
noZoom
className="rounded-2xl hidden dark:block"
@ -35,12 +35,22 @@ if __name__ == "__main__":
mcp.run()
```
<Tip>
**This documentation is for FastMCP 3.0**, which is currently in beta. For the 2.x release, see the [FastMCP 2.0 documentation](/v2/getting-started/welcome).
</Tip>
## Beyond Basic MCP
## Production-Ready MCP
FastMCP pioneered Python MCP development, and FastMCP 1.0 was incorporated into the [official MCP SDK](https://github.com/modelcontextprotocol/python-sdk) in 2024.
FastMCP pioneered Python MCP developmentFastMCP 1.0 was incorporated into the [official MCP SDK](https://github.com/modelcontextprotocol/python-sdk) in 2024. Today, roughly 70% of MCP servers run on some version of FastMCP.
**This is FastMCP 2.0,** the actively maintained version that extends far beyond basic protocol implementation. While the SDK provides core functionality, FastMCP 2.0 delivers everything needed for production: advanced MCP patterns (server composition, proxying, OpenAPI/FastAPI generation, tool transformation), enterprise auth (Google, GitHub, Azure, Auth0, WorkOS, and more), deployment tools, testing frameworks, and comprehensive client libraries.
**FastMCP 3.0 is built for enterprise MCP applications.** When you need to compose multiple servers, proxy remote APIs, add enterprise auth, or deploy multi-tenant systems, FastMCP provides the right abstractions:
- **[Providers](/servers/providers/overview)** let you source components from anywhere—decorated functions, filesystem discovery, remote servers, OpenAPI specs, or your own custom sources.
- **[Transforms](/servers/providers/transforms)** control what clients see—namespace for composition, filter by version or user permissions, convert resources to tools for compatibility.
- **[Servers](/servers/server)** expose everything over stdio, HTTP, or WebSocket with session management and protocol handling built in.
- **[Middleware](/servers/middleware)** handles cross-cutting concerns—authentication, authorization, logging, rate limiting, custom business logic.
Mount a weather API and a calendar server together with automatic namespacing. Proxy your company's internal MCP servers through a single authenticated gateway. Build a SaaS product where each tenant gets their own isolated component set. These aren't edge cases—they're what production MCP looks like.
Ready to build? Start with our [installation guide](/getting-started/installation) or jump straight to the [quickstart](/getting-started/quickstart).
@ -59,7 +69,7 @@ FastMCP provides a high-level, Pythonic interface for building, managing, and in
## Why FastMCP?
FastMCP handles all the complex protocol details so you can focus on building. In most cases, decorating a Python function is all you needFastMCP handles the rest.
FastMCP handles all the complex protocol details so you can focus on building. In most cases, decorating a Python function is all you needFastMCP handles the rest.
🚀 **Fast**: High-level interface means less code and faster development
@ -67,12 +77,14 @@ FastMCP handles all the complex protocol details so you can focus on building. I
🐍 **Pythonic**: Feels natural to Python developers
🔍 **Complete**: Everything for production — enterprise auth (Google, GitHub, Azure, Auth0, WorkOS), deployment tools, testing frameworks, client libraries, and more
🔍 **Complete**: Everything for production—enterprise auth (Google, GitHub, Azure, Auth0, WorkOS), deployment tools, testing frameworks, client libraries, and more
🔧 **Composable**: The v3 architecture scales from a single tool to complex multi-server deployments
FastMCP provides the shortest path from idea to production. Deploy locally, to the cloud with [FastMCP Cloud](https://fastmcp.cloud) (free for personal servers), or to your own infrastructure.
<Tip>
**This documentation reflects FastMCP's `main` branch**, meaning it always reflects the latest development version. Features are generally marked with version badges (e.g. `New in version: 2.13.1`) to indicate when they were introduced. Note that this may include features that are not yet released.
**This documentation reflects FastMCP's `main` branch**, meaning it always reflects the latest development version. Features are generally marked with version badges (e.g. `New in version: 3.0.0`) to indicate when they were introduced. Note that this may include features that are not yet released.
</Tip>
## LLM-Friendly Docs

View file

@ -3,7 +3,6 @@ title: Auth0 OAuth 🤝 FastMCP
sidebarTitle: Auth0
description: Secure your FastMCP server with Auth0 OAuth
icon: shield-check
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"

View file

@ -3,7 +3,6 @@ title: AuthKit 🤝 FastMCP
sidebarTitle: AuthKit
description: Secure your FastMCP server with AuthKit by WorkOS
icon: shield-check
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"

View file

@ -3,7 +3,6 @@ title: AWS Cognito OAuth 🤝 FastMCP
sidebarTitle: AWS Cognito
description: Secure your FastMCP server with AWS Cognito user pools
icon: aws
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"

View file

@ -3,7 +3,6 @@ title: Azure (Microsoft Entra ID) OAuth 🤝 FastMCP
sidebarTitle: Azure (Entra ID)
description: Secure your FastMCP server with Azure/Microsoft Entra OAuth
icon: microsoft
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"

View file

@ -3,7 +3,6 @@ title: ChatGPT 🤝 FastMCP
sidebarTitle: ChatGPT
description: Connect FastMCP servers to ChatGPT in Chat and Deep Research modes
icon: message-smile
tag: NEW
---
ChatGPT supports MCP servers through remote HTTP connections in two modes: **Chat mode** for interactive conversations and **Deep Research mode** for comprehensive information retrieval.

View file

@ -3,7 +3,6 @@ title: Descope 🤝 FastMCP
sidebarTitle: Descope
description: Secure your FastMCP server with Descope
icon: shield-check
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx";

View file

@ -3,7 +3,6 @@ title: Discord OAuth 🤝 FastMCP
sidebarTitle: Discord
description: Secure your FastMCP server with Discord OAuth
icon: discord
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"

View file

@ -3,7 +3,6 @@ title: Gemini CLI 🤝 FastMCP
sidebarTitle: Gemini CLI
description: Install and use FastMCP servers in Gemini CLI
icon: message-smile
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"

View file

@ -3,7 +3,6 @@ title: GitHub OAuth 🤝 FastMCP
sidebarTitle: GitHub
description: Secure your FastMCP server with GitHub OAuth
icon: github
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"

View file

@ -3,7 +3,6 @@ title: Google OAuth 🤝 FastMCP
sidebarTitle: Google
description: Secure your FastMCP server with Google OAuth
icon: google
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"

View file

@ -3,7 +3,6 @@ title: OCI IAM OAuth 🤝 FastMCP
sidebarTitle: Oracle
description: Secure your FastMCP server with OCI IAM OAuth
icon: shield-check
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"

View file

@ -3,7 +3,6 @@ title: Scalekit 🤝 FastMCP
sidebarTitle: Scalekit
description: Secure your FastMCP server with Scalekit
icon: shield-check
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"

View file

@ -3,7 +3,6 @@ title: Supabase 🤝 FastMCP
sidebarTitle: Supabase
description: Secure your FastMCP server with Supabase Auth
icon: shield-check
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"

View file

@ -3,7 +3,6 @@ title: WorkOS 🤝 FastMCP
sidebarTitle: WorkOS
description: Authenticate FastMCP servers with WorkOS Connect
icon: shield-check
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"

View file

@ -3,7 +3,6 @@ title: OIDC Proxy
sidebarTitle: OIDC Proxy
description: Bridge OIDC providers to work seamlessly with MCP's authentication flow.
icon: share
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx";

View file

@ -3,7 +3,6 @@ title: Remote OAuth
sidebarTitle: Remote OAuth
description: Integrate your FastMCP server with external identity providers like Descope, WorkOS, Auth0, and corporate SSO systems.
icon: camera-cctv
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"

View file

@ -3,6 +3,7 @@ title: MCP Context
sidebarTitle: Context
description: Access MCP capabilities like logging, progress, and resources within your MCP objects.
icon: rectangle-code
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'

View file

@ -3,7 +3,6 @@ title: User Elicitation
sidebarTitle: Elicitation
description: Request structured input from users during tool execution through the MCP context.
icon: message-question
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
@ -12,26 +11,20 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
User elicitation allows MCP servers to request structured input from users during tool execution. Instead of requiring all inputs upfront, tools can interactively ask for missing parameters, clarification, or additional context as needed.
<Tip>
Most of the examples in this document assume you have a FastMCP server instance named `mcp` and show how to use the `ctx.elicit` method to request user input from an `@mcp.tool`-decorated function.
</Tip>
## What is Elicitation?
Elicitation enables tools to pause execution and request specific information from users. This is particularly useful for:
Elicitation enables tools to pause execution and request specific information from users:
- **Missing parameters**: Ask for required information not provided initially
- **Clarification requests**: Get user confirmation or choices for ambiguous scenarios
- **Clarification requests**: Get user confirmation or choices for ambiguous scenarios
- **Progressive disclosure**: Collect complex information step-by-step
- **Dynamic workflows**: Adapt tool behavior based on user responses
For example, a file management tool might ask "Which directory should I create?" or a data analysis tool might request "What date range should I analyze?"
### Basic Usage
## Overview
Use the `ctx.elicit()` method within any tool function to request user input:
Use the `ctx.elicit()` method within any tool function to request user input. Specify the message to display and the type of response you expect.
```python {14-17}
```python
from fastmcp import FastMCP, Context
from dataclasses import dataclass
@ -49,7 +42,7 @@ async def collect_user_info(ctx: Context) -> str:
message="Please provide your information",
response_type=UserInfo
)
if result.action == "accept":
user = result.data
return f"Hello {user.name}, you are {user.age} years old"
@ -59,72 +52,27 @@ async def collect_user_info(ctx: Context) -> str:
return "Operation cancelled"
```
## Method Signature
<Card icon="code" title="Context Elicitation Method">
<ResponseField name="ctx.elicit" type="async method">
<Expandable title="Parameters">
<ResponseField name="message" type="str">
The prompt message to display to the user
</ResponseField>
<ResponseField name="response_type" type="type" default="None">
The Python type defining the expected response structure (dataclass, primitive type, etc.) Note that elicitation responses are subject to a restricted subset of JSON Schema types. See [Supported Response Types](#supported-response-types) for more details.
</ResponseField>
</Expandable>
<Expandable title="Response">
<ResponseField name="ElicitationResult" type="object">
Result object containing the user's response
<Expandable title="properties">
<ResponseField name="action" type="Literal['accept', 'decline', 'cancel']">
How the user responded to the request
</ResponseField>
<ResponseField name="data" type="response_type | None">
The user's input data (only present when action is "accept")
</ResponseField>
</Expandable>
</ResponseField>
</Expandable>
</ResponseField>
</Card>
## Elicitation Actions
The elicitation result contains an `action` field indicating how the user responded:
- **`accept`**: User provided valid input - data is available in the `data` field
- **`decline`**: User chose not to provide the requested information and the data field is `None`
- **`cancel`**: User cancelled the entire operation and the data field is `None`
| Action | Description |
|--------|-------------|
| `accept` | User provided valid input—data is available in the `data` field |
| `decline` | User chose not to provide the requested information |
| `cancel` | User cancelled the entire operation |
```python {5, 7}
@mcp.tool
async def my_tool(ctx: Context) -> str:
result = await ctx.elicit("Choose an action")
FastMCP also provides typed result classes for pattern matching:
if result.action == "accept":
return "Accepted!"
elif result.action == "decline":
return "Declined!"
else:
return "Cancelled!"
```
FastMCP also provides typed result classes for pattern matching on the `action` field:
```python {1-5, 12, 14, 16}
```python
from fastmcp.server.elicitation import (
AcceptedElicitation,
DeclinedElicitation,
AcceptedElicitation,
DeclinedElicitation,
CancelledElicitation,
)
@mcp.tool
async def pattern_example(ctx: Context) -> str:
result = await ctx.elicit("Enter your name:", response_type=str)
match result:
case AcceptedElicitation(data=name):
return f"Hello {name}!"
@ -134,50 +82,74 @@ async def pattern_example(ctx: Context) -> str:
return "Operation cancelled"
```
## Response Types
### Multi-Turn Elicitation
The server must send a schema to the client indicating the type of data it expects in response to the elicitation request. If the request is `accept`-ed, the client must send a response that matches the schema.
Tools can make multiple elicitation calls to gather information progressively:
The MCP spec only supports a limited subset of JSON Schema types for elicitation responses. Specifically, it only supports JSON **objects** with **primitive** properties including `string`, `number` (or `integer`), `boolean` and `enum` fields.
```python
@mcp.tool
async def plan_meeting(ctx: Context) -> str:
"""Plan a meeting by gathering details step by step."""
title_result = await ctx.elicit("What's the meeting title?", response_type=str)
if title_result.action != "accept":
return "Meeting planning cancelled"
duration_result = await ctx.elicit("Duration in minutes?", response_type=int)
if duration_result.action != "accept":
return "Meeting planning cancelled"
priority_result = await ctx.elicit(
"Is this urgent?",
response_type=["yes", "no"]
)
if priority_result.action != "accept":
return "Meeting planning cancelled"
urgent = priority_result.data == "yes"
return f"Meeting '{title_result.data}' for {duration_result.data} minutes (Urgent: {urgent})"
```
### Client Requirements
Elicitation requires the client to implement an elicitation handler. If a client doesn't support elicitation, calls to `ctx.elicit()` will raise an error indicating that elicitation is not supported.
See [Client Elicitation](/clients/elicitation) for details on how clients handle these requests.
## Schema and Response Types
The server must send a schema to the client indicating the type of data it expects in response to the elicitation request. The MCP spec only supports a limited subset of JSON Schema types for elicitation responses—specifically JSON **objects** with **primitive** properties including `string`, `number` (or `integer`), `boolean`, and `enum` fields.
FastMCP makes it easy to request a broader range of types, including scalars (e.g. `str`) or no response at all, by automatically wrapping them in MCP-compatible object schemas.
### Scalar Types
You can request simple scalar data types for basic input, such as a string, integer, or boolean.
When you request a scalar type, FastMCP automatically wraps it in an object schema for MCP spec compatibility. Clients will see a corresponding schema requesting a single "value" field of the requested type. Once clients respond, the provided object is "unwrapped" and the scalar value is returned to your tool function as the `data` field of the `ElicitationResult` object.
As a developer, this means you do not have to worry about creating or accessing a structured object when you only need a scalar value.
You can request simple scalar data types for basic input, such as a string, integer, or boolean. When you request a scalar type, FastMCP automatically wraps it in an object schema for MCP spec compatibility. Clients will see a schema requesting a single "value" field of the requested type. Once clients respond, the provided object is "unwrapped" and the scalar value is returned directly in the `data` field.
<CodeGroup>
```python {4} title="Request a string"
```python title="String"
@mcp.tool
async def get_user_name(ctx: Context) -> str:
"""Get the user's name."""
result = await ctx.elicit("What's your name?", response_type=str)
if result.action == "accept":
return f"Hello, {result.data}!"
return "No name provided"
```
```python {4} title="Request an integer"
```python title="Integer"
@mcp.tool
async def pick_a_number(ctx: Context) -> str:
"""Pick a number."""
result = await ctx.elicit("Pick a number!", response_type=int)
if result.action == "accept":
return f"You picked {result.data}"
return "No number provided"
```
```python {4} title="Request a boolean"
```python title="Boolean"
@mcp.tool
async def pick_a_boolean(ctx: Context) -> str:
"""Pick a boolean."""
result = await ctx.elicit("True or false?", response_type=bool)
if result.action == "accept":
return f"You picked {result.data}"
return "No boolean provided"
@ -186,12 +158,11 @@ async def pick_a_boolean(ctx: Context) -> str:
### No Response
Sometimes, the goal of an elicitation is to simply get a user to approve or reject an action. In this case, you can pass `None` as the response type to indicate that no response is expected. In order to comply with the MCP spec, the client will see a schema requesting an empty object in response. In this case, the `data` field of the `ElicitationResult` object will be `None` when the user accepts the elicitation.
Sometimes, the goal of an elicitation is to simply get a user to approve or reject an action. Pass `None` as the response type to indicate that no data is expected. The `data` field will be `None` when the user accepts.
```python {4} title="No response"
```python
@mcp.tool
async def approve_action(ctx: Context) -> str:
"""Approve an action."""
result = await ctx.elicit("Approve this action?", response_type=None)
if result.action == "accept":
@ -202,13 +173,12 @@ async def approve_action(ctx: Context) -> str:
### Constrained Options
Often you'll want to constrain the user's response to a specific set of values. You can do this by using a `Literal` type or a Python enum as the response type, or by passing a list of strings to the `response_type` parameter as a convenient shortcut.
Constrain the user's response to a specific set of values using a `Literal` type, Python enum, or a list of strings as a convenient shortcut.
<CodeGroup>
```python {6} title="Using a list of strings"
```python title="List of strings"
@mcp.tool
async def set_priority(ctx: Context) -> str:
"""Set task priority level."""
result = await ctx.elicit(
"What priority level?",
response_type=["low", "medium", "high"],
@ -217,12 +187,11 @@ async def set_priority(ctx: Context) -> str:
if result.action == "accept":
return f"Priority set to: {result.data}"
```
```python {1, 8} title="Using a Literal type"
```python title="Literal type"
from typing import Literal
@mcp.tool
async def set_priority(ctx: Context) -> str:
"""Set task priority level."""
result = await ctx.elicit(
"What priority level?",
response_type=Literal["low", "medium", "high"]
@ -232,7 +201,7 @@ async def set_priority(ctx: Context) -> str:
return f"Priority set to: {result.data}"
return "No priority set"
```
```python {1, 11} title="Using a Python enum"
```python title="Python enum"
from enum import Enum
class Priority(Enum):
@ -242,7 +211,6 @@ class Priority(Enum):
@mcp.tool
async def set_priority(ctx: Context) -> str:
"""Set task priority level."""
result = await ctx.elicit("What priority level?", response_type=Priority)
if result.action == "accept":
@ -251,28 +219,26 @@ async def set_priority(ctx: Context) -> str:
```
</CodeGroup>
#### Multi-Select
### Multi-Select
<VersionBadge version="2.14.0" />
Enable multi-select by wrapping your choices in an additional list level. This allows users to select multiple values from the available options.
<CodeGroup>
```python {6-8} title="List of a list of strings"
```python title="List of strings"
@mcp.tool
async def select_tags(ctx: Context) -> str:
"""Select multiple tags."""
result = await ctx.elicit(
"Choose tags",
response_type=[["bug", "feature", "documentation"]] # Note: list of a list
)
if result.action == "accept":
tags = result.data # List of selected strings
tags = result.data
return f"Selected tags: {', '.join(tags)}"
```
```python {1, 3-6, 11-14} title="list[Enum] type annotation"
```python title="list[Enum] type"
from enum import Enum
class Tag(Enum):
@ -284,7 +250,7 @@ class Tag(Enum):
async def select_tags(ctx: Context) -> str:
result = await ctx.elicit(
"Choose tags",
response_type=list[Tag] # Type annotation for multi-select
response_type=list[Tag]
)
if result.action == "accept":
tags = [tag.value for tag in result.data]
@ -292,38 +258,15 @@ async def select_tags(ctx: Context) -> str:
```
</CodeGroup>
For titled multi-select, wrap a dict in a list (see [Titled Options](#titled-options) for dict syntax):
```python {6-12}
@mcp.tool
async def select_priorities(ctx: Context) -> str:
"""Select multiple priorities."""
result = await ctx.elicit(
"Choose priorities",
response_type=[{ # Note: list containing a dict
"low": {"title": "Low Priority"},
"medium": {"title": "Medium Priority"},
"high": {"title": "High Priority"}
}]
)
if result.action == "accept":
priorities = result.data # List of selected strings
return f"Selected: {', '.join(priorities)}"
```
#### Titled Options
### Titled Options
<VersionBadge version="2.14.0" />
For better UI display, you can provide human-readable titles for enum options. FastMCP generates SEP-1330 compliant schemas using the `oneOf` pattern with `const` and `title` fields.
For better UI display, provide human-readable titles for enum options. FastMCP generates SEP-1330 compliant schemas using the `oneOf` pattern with `const` and `title` fields.
Use a dict to specify titles for enum values:
```python {6-10}
```python
@mcp.tool
async def set_priority(ctx: Context) -> str:
"""Set task priority level."""
result = await ctx.elicit(
"What priority level?",
response_type={
@ -339,13 +282,12 @@ async def set_priority(ctx: Context) -> str:
For multi-select with titles, wrap the dict in a list:
```python {6-12}
```python
@mcp.tool
async def select_priorities(ctx: Context) -> str:
"""Select multiple priorities."""
result = await ctx.elicit(
"Choose priorities",
response_type=[{ # List containing a dict for multi-select
response_type=[{
"low": {"title": "Low Priority"},
"medium": {"title": "Medium Priority"},
"high": {"title": "High Priority"}
@ -353,15 +295,14 @@ async def select_priorities(ctx: Context) -> str:
)
if result.action == "accept":
priorities = result.data # List of selected strings
return f"Selected: {', '.join(priorities)}"
return f"Selected: {', '.join(result.data)}"
```
### Structured Responses
You can request structured data with multiple fields by using a dataclass, typed dict, or Pydantic model as the response type. Note that the MCP spec only supports shallow objects with scalar (string, number, boolean) or enum properties.
Request structured data with multiple fields by using a dataclass, typed dict, or Pydantic model as the response type. Note that the MCP spec only supports shallow objects with scalar (string, number, boolean) or enum properties.
```python {1, 16, 20}
```python
from dataclasses import dataclass
from typing import Literal
@ -374,12 +315,11 @@ class TaskDetails:
@mcp.tool
async def create_task(ctx: Context) -> str:
"""Create a new task with user-provided details."""
result = await ctx.elicit(
"Please provide task details",
response_type=TaskDetails
)
if result.action == "accept":
task = result.data
return f"Created task: {task.title} (Priority: {task.priority})"
@ -390,16 +330,7 @@ async def create_task(ctx: Context) -> str:
<VersionBadge version="2.14.0" />
You can provide default values for elicitation fields using Pydantic's `Field(default=...)`. Clients will pre-populate form fields with these defaults, making it easier for users to provide input.
Default values are supported for all primitive types:
- Strings: `Field(default="[email protected]")`
- Integers: `Field(default=50)`
- Numbers: `Field(default=3.14)`
- Booleans: `Field(default=False)`
- Enums: `Field(default=EnumValue.A)`
Fields with default values are automatically marked as optional (not included in the `required` list), so users can accept the default or provide their own value.
Provide default values for elicitation fields using Pydantic's `Field(default=...)`. Clients will pre-populate form fields with these defaults. Fields with default values are automatically marked as optional.
```python
from pydantic import BaseModel, Field
@ -423,40 +354,4 @@ async def create_task(ctx: Context) -> str:
return "Task creation cancelled"
```
## Multi-Turn Elicitation
Tools can make multiple elicitation calls to gather information progressively:
```python {6, 11, 16-19}
@mcp.tool
async def plan_meeting(ctx: Context) -> str:
"""Plan a meeting by gathering details step by step."""
# Get meeting title
title_result = await ctx.elicit("What's the meeting title?", response_type=str)
if title_result.action != "accept":
return "Meeting planning cancelled"
# Get duration
duration_result = await ctx.elicit("Duration in minutes?", response_type=int)
if duration_result.action != "accept":
return "Meeting planning cancelled"
# Get priority
priority_result = await ctx.elicit(
"Is this urgent?",
response_type=Literal["yes", "no"]
)
if priority_result.action != "accept":
return "Meeting planning cancelled"
urgent = priority_result.data == "yes"
return f"Meeting '{title_result.data}' planned for {duration_result.data} minutes (Urgent: {urgent})"
```
## Client Requirements
Elicitation requires the client to implement an elicitation handler. See [Client Elicitation](/clients/elicitation) for details on how clients can handle these requests.
If a client doesn't support elicitation, calls to `ctx.elicit()` will raise an error indicating that elicitation is not supported.
Default values are supported for strings, integers, numbers, booleans, and enums.

View file

@ -2,7 +2,6 @@
title: Icons
description: Add visual icons to your servers, tools, resources, and prompts
icon: image
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
@ -13,11 +12,7 @@ Icons provide visual representations for your MCP servers and components, helpin
## Icon Format
Icons use the standard MCP Icon type from the MCP protocol specification. Each icon specifies:
- **src**: URL or data URI pointing to the icon image
- **mimeType** (optional): MIME type of the image (e.g., "image/png", "image/svg+xml")
- **sizes** (optional): Array of size descriptors (e.g., ["48x48"], ["any"])
Icons use the standard MCP Icon type from the MCP protocol specification. Each icon specifies a source URL or data URI, and optionally includes MIME type and size information.
```python
from mcp.types import Icon
@ -29,9 +24,15 @@ icon = Icon(
)
```
The fields serve different purposes:
- **src**: URL or data URI pointing to the icon image
- **mimeType** (optional): MIME type of the image (e.g., "image/png", "image/svg+xml")
- **sizes** (optional): Array of size descriptors (e.g., ["48x48"], ["any"])
## Server Icons
Add icons and a website URL to your server for display in client applications:
Add icons and a website URL to your server for display in client applications. Multiple icons at different sizes help clients choose the best resolution for their display context.
```python
from fastmcp import FastMCP
@ -59,7 +60,7 @@ Server icons appear in MCP client interfaces to help users identify your server
## Component Icons
Icons can be added to individual tools, resources, resource templates, and prompts:
Icons can be added to individual tools, resources, resource templates, and prompts. This helps users visually distinguish between different component types and purposes.
### Tool Icons
@ -111,7 +112,7 @@ def analyze_code(code: str):
## Using Data URIs
For small icons or when you want to embed the icon directly, use data URIs:
For small icons or when you want to embed the icon directly without external dependencies, use data URIs. This approach eliminates the need for hosting and ensures the icon is always available.
```python
from mcp.types import Icon
@ -127,8 +128,17 @@ svg_icon = Icon(
def my_tool() -> str:
"""A tool with an embedded SVG icon."""
return "result"
```
# Generating a data URI from a local image file.
### Generating Data URIs from Files
FastMCP provides the `Image` utility class to convert local image files into data URIs.
```python
from mcp.types import Icon
from fastmcp.utilities.types import Image
# Generate a data URI from a local image file
img = Image(path="./assets/brand/favicon.png")
icon = Icon(src=img.to_data_uri())
@ -137,3 +147,5 @@ def file_icon_tool() -> str:
"""A tool with an icon generated from a local file."""
return "result"
```
This approach is useful when you have local image assets and want to embed them directly in your server definition.

View file

@ -1,5 +1,6 @@
---
title: Lifespans
sidebarTitle: Lifespan
description: Server-level setup and teardown with composable lifespans
icon: heart-pulse
tag: NEW

View file

@ -8,27 +8,16 @@ icon: receipt
import { VersionBadge } from '/snippets/version-badge.mdx'
<Tip>
This documentation covers **MCP client logging** - sending messages from your server to MCP clients. For standard server-side logging (e.g., writing to files, console), use `fastmcp.utilities.logging.get_logger()` or Python's built-in `logging` module.
This documentation covers **MCP client logging**sending messages from your server to MCP clients. For standard server-side logging (e.g., writing to files, console), use `fastmcp.utilities.logging.get_logger()` or Python's built-in `logging` module.
</Tip>
Server logging allows MCP tools to send debug, info, warning, and error messages back to the client. This provides visibility into function execution and helps with debugging during development and operation.
Server logging allows MCP tools to send debug, info, warning, and error messages back to the client. Unlike standard Python logging, MCP server logging sends messages directly to the client, making them visible in the client's interface or logs.
## Why Use Server Logging?
Server logging is essential for:
- **Debugging**: Send detailed execution information to help diagnose issues
- **Progress visibility**: Keep users informed about what the tool is doing
- **Error reporting**: Communicate problems and their context to clients
- **Audit trails**: Create records of tool execution for compliance or analysis
Unlike standard Python logging, MCP server logging sends messages directly to the client, making them visible in the client's interface or logs.
### Basic Usage
## Basic Usage
Use the context logging methods within any tool function:
```python {8-9, 13, 17, 21}
```python
from fastmcp import FastMCP, Context
mcp = FastMCP("LoggingDemo")
@ -38,24 +27,33 @@ async def analyze_data(data: list[float], ctx: Context) -> dict:
"""Analyze numerical data with comprehensive logging."""
await ctx.debug("Starting analysis of numerical data")
await ctx.info(f"Analyzing {len(data)} data points")
try:
if not data:
await ctx.warning("Empty data list provided")
return {"error": "Empty data list"}
result = sum(data) / len(data)
await ctx.info(f"Analysis complete, average: {result}")
return {"average": result, "count": len(data)}
except Exception as e:
await ctx.error(f"Analysis failed: {str(e)}")
raise
```
## Structured Logging with `extra`
## Log Levels
All logging methods (`debug`, `info`, `warning`, `error`, `log`) now accept an `extra` parameter, which is a dictionary of arbitrary data. This allows you to send structured data to the client, which is useful for creating rich, queryable logs.
| Level | Use Case |
|-------|----------|
| `ctx.debug()` | Detailed execution information for diagnosing problems |
| `ctx.info()` | General information about normal program execution |
| `ctx.warning()` | Potentially harmful situations that don't prevent execution |
| `ctx.error()` | Error events that might still allow the application to continue |
## Structured Logging
All logging methods accept an `extra` parameter for sending structured data to the client. This is useful for creating rich, queryable logs.
```python
@mcp.tool
@ -68,182 +66,22 @@ async def process_transaction(transaction_id: str, amount: float, ctx: Context):
"currency": "USD"
}
)
# ... processing logic ...
```
## Server Logs
## Server-Side Logs
Client Logging in the form of `ctx.log()` and its convenience methods (`debug`, `info`, `warning`, `error`) are meant for sending messages to the MCP clients. Messages sent to clients are also logged to the server's log at `DEBUG` level. Enable debug logging on the server or enable debug logging on the `fastmcp.server.context.to_client` logger to see these messages in the server's log.
Messages sent to clients via `ctx.log()` and its convenience methods are also logged to the server's log at `DEBUG` level. Enable debug logging on the `fastmcp.server.context.to_client` logger to see these messages:
```python
import logging
from fastmcp.utilities.logging import get_logger
to_client_logger = get_logger(name="fastmcp.server.context.to_client")
to_client_logger.setLevel(level=logging.DEBUG)
```
## Logging Methods
<Card icon="code" title="Context Logging Methods">
<ResponseField name="ctx.debug" type="async method">
Send debug-level messages for detailed execution information
<Expandable title="parameters">
<ResponseField name="message" type="str">
The debug message to send to the client
</ResponseField>
<ResponseField name="extra" type="dict | None" default="None">
Optional dictionary for structured logging data
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="ctx.info" type="async method">
Send informational messages about normal execution
<Expandable title="parameters">
<ResponseField name="message" type="str">
The information message to send to the client
</ResponseField>
<ResponseField name="extra" type="dict | None" default="None">
Optional dictionary for structured logging data
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="ctx.warning" type="async method">
Send warning messages for potential issues that didn't prevent execution
<Expandable title="parameters">
<ResponseField name="message" type="str">
The warning message to send to the client
</ResponseField>
<ResponseField name="extra" type="dict | None" default="None">
Optional dictionary for structured logging data
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="ctx.error" type="async method">
Send error messages for problems that occurred during execution
<Expandable title="parameters">
<ResponseField name="message" type="str">
The error message to send to the client
</ResponseField>
<ResponseField name="extra" type="dict | None" default="None">
Optional dictionary for structured logging data
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="ctx.log" type="async method">
Generic logging method with custom level and logger name
<Expandable title="parameters">
<ResponseField name="level" type="Literal['debug', 'info', 'warning', 'error']">
The log level for the message
</ResponseField>
<ResponseField name="message" type="str">
The message to send to the client
</ResponseField>
<ResponseField name="logger_name" type="str | None" default="None">
Optional custom logger name for categorizing messages
</ResponseField>
<ResponseField name="extra" type="dict | None" default="None">
Optional dictionary for structured logging data
</ResponseField>
</Expandable>
</ResponseField>
</Card>
## Log Levels
### Debug
Use for detailed information that's typically only useful when diagnosing problems:
```python
@mcp.tool
async def process_file(file_path: str, ctx: Context) -> str:
"""Process a file with detailed debug logging."""
await ctx.debug(f"Starting to process file: {file_path}")
await ctx.debug("Checking file permissions")
# File processing logic
await ctx.debug("File processing completed successfully")
return "File processed"
```
### Info
Use for general information about normal program execution:
```python
@mcp.tool
async def backup_database(ctx: Context) -> str:
"""Backup database with progress information."""
await ctx.info("Starting database backup")
await ctx.info("Connecting to database")
await ctx.info("Backup completed successfully")
return "Database backed up"
```
### Warning
Use for potentially harmful situations that don't prevent execution:
```python
@mcp.tool
async def validate_config(config: dict, ctx: Context) -> dict:
"""Validate configuration with warnings for deprecated options."""
if "old_api_key" in config:
await ctx.warning(
"Using deprecated 'old_api_key' field. Please use 'api_key' instead",
extra={"deprecated_field": "old_api_key"}
)
if config.get("timeout", 30) > 300:
await ctx.warning(
"Timeout value is very high (>5 minutes), this may cause issues",
extra={"timeout_value": config.get("timeout")}
)
return {"status": "valid", "warnings": "see logs"}
```
### Error
Use for error events that might still allow the application to continue:
```python
@mcp.tool
async def batch_process(items: list[str], ctx: Context) -> dict:
"""Process multiple items, logging errors for failed items."""
successful = 0
failed = 0
for item in items:
try:
# Process item
successful += 1
except Exception as e:
await ctx.error(
f"Failed to process item '{item}': {str(e)}",
extra={"failed_item": item}
)
failed += 1
return {"successful": successful, "failed": failed}
```
## Client Handling
Log messages are sent to the client through the MCP protocol. How clients handle these messages depends on their implementation:
Log messages are sent to the client through the MCP protocol. How clients handle these messages depends on their implementation—development clients may display logs in real-time, production clients may store them for analysis, and integration clients may forward them to external logging systems.
- **Development clients**: May display logs in real-time for debugging
- **Production clients**: May store logs for later analysis or display to users
- **Integration clients**: May forward logs to external logging systems
See [Client Logging](/clients/logging) for details on how clients can handle server log messages.
See [Client Logging](/clients/logging) for details on how clients handle server log messages.

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,7 @@ title: Pagination
sidebarTitle: Pagination
description: Control how servers return large lists of components to clients.
icon: page
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'

View file

@ -7,22 +7,13 @@ icon: chart-line
import { VersionBadge } from '/snippets/version-badge.mdx'
Progress reporting allows MCP tools to notify clients about the progress of long-running operations. This enables clients to display progress indicators and provide better user experience during time-consuming tasks.
Progress reporting allows MCP tools to notify clients about the progress of long-running operations. Clients can display progress indicators and provide better user experience during time-consuming tasks.
## Why Use Progress Reporting?
## Basic Usage
Progress reporting is valuable for:
Use `ctx.report_progress()` to send progress updates to the client. The method accepts a `progress` value representing how much work is complete, and an optional `total` representing the full scope of work.
- **User experience**: Keep users informed about long-running operations
- **Progress indicators**: Enable clients to show progress bars or percentages
- **Timeout prevention**: Demonstrate that operations are actively progressing
- **Debugging**: Track execution progress for performance analysis
### Basic Usage
Use `ctx.report_progress()` to send progress updates to the client:
```python {14, 21}
```python
from fastmcp import FastMCP, Context
import asyncio
@ -33,157 +24,28 @@ async def process_items(items: list[str], ctx: Context) -> dict:
"""Process a list of items with progress updates."""
total = len(items)
results = []
for i, item in enumerate(items):
# Report progress as we process each item
await ctx.report_progress(progress=i, total=total)
# Simulate processing time
await asyncio.sleep(0.1)
results.append(item.upper())
# Report 100% completion
await ctx.report_progress(progress=total, total=total)
return {"processed": len(results), "results": results}
```
## Method Signature
<Card icon="code" title="Context Progress Method">
<ResponseField name="ctx.report_progress" type="async method">
Report progress to the client for long-running operations
<Expandable title="Parameters">
<ResponseField name="progress" type="float">
Current progress value (e.g., 24, 0.75, 1500)
</ResponseField>
<ResponseField name="total" type="float | None" default="None">
Optional total value (e.g., 100, 1.0, 2000). When provided, clients may interpret this as enabling percentage calculation.
</ResponseField>
</Expandable>
</ResponseField>
</Card>
## Progress Patterns
### Percentage-Based Progress
Report progress as a percentage (0-100):
```python {13-14}
@mcp.tool
async def download_file(url: str, ctx: Context) -> str:
"""Download a file with percentage progress."""
total_size = 1000 # KB
downloaded = 0
while downloaded < total_size:
# Download chunk
chunk_size = min(50, total_size - downloaded)
downloaded += chunk_size
# Report percentage progress
percentage = (downloaded / total_size) * 100
await ctx.report_progress(progress=percentage, total=100)
await asyncio.sleep(0.1) # Simulate download time
return f"Downloaded file from {url}"
```
### Absolute Progress
Report progress with absolute values:
```python {10}
@mcp.tool
async def backup_database(ctx: Context) -> str:
"""Backup database tables with absolute progress."""
tables = ["users", "orders", "products", "inventory", "logs"]
for i, table in enumerate(tables):
await ctx.info(f"Backing up table: {table}")
# Report absolute progress
await ctx.report_progress(progress=i + 1, total=len(tables))
# Simulate backup time
await asyncio.sleep(0.5)
return "Database backup completed"
```
### Indeterminate Progress
Report progress without a known total for operations where the endpoint is unknown:
```python {11}
@mcp.tool
async def scan_directory(directory: str, ctx: Context) -> dict:
"""Scan directory with indeterminate progress."""
files_found = 0
# Simulate directory scanning
for i in range(10): # Unknown number of files
files_found += 1
# Report progress without total for indeterminate operations
await ctx.report_progress(progress=files_found)
await asyncio.sleep(0.2)
return {"files_found": files_found, "directory": directory}
```
### Multi-Stage Operations
Break complex operations into stages with progress for each:
```python
@mcp.tool
async def data_migration(source: str, destination: str, ctx: Context) -> str:
"""Migrate data with multi-stage progress reporting."""
# Stage 1: Validation (0-25%)
await ctx.info("Validating source data")
for i in range(5):
await ctx.report_progress(progress=i * 5, total=100)
await asyncio.sleep(0.1)
# Stage 2: Export (25-60%)
await ctx.info("Exporting data from source")
for i in range(7):
progress = 25 + (i * 5)
await ctx.report_progress(progress=progress, total=100)
await asyncio.sleep(0.1)
# Stage 3: Transform (60-80%)
await ctx.info("Transforming data format")
for i in range(4):
progress = 60 + (i * 5)
await ctx.report_progress(progress=progress, total=100)
await asyncio.sleep(0.1)
# Stage 4: Import (80-100%)
await ctx.info("Importing to destination")
for i in range(4):
progress = 80 + (i * 5)
await ctx.report_progress(progress=progress, total=100)
await asyncio.sleep(0.1)
# Final completion
await ctx.report_progress(progress=100, total=100)
return f"Migration from {source} to {destination} completed"
```
| Pattern | Description | Example |
|---------|-------------|---------|
| Percentage | Progress as 0-100 percentage | `progress=75, total=100` |
| Absolute | Completed items of a known count | `progress=3, total=10` |
| Indeterminate | Progress without known endpoint | `progress=files_found` (no total) |
For multi-stage operations, map each stage to a portion of the total progress range. A four-stage operation might allocate 0-25% to validation, 25-60% to export, 60-80% to transform, and 80-100% to import.
## Client Requirements
Progress reporting requires clients to support progress handling:
Progress reporting requires clients to support progress handling. Clients must send a `progressToken` in the initial request to receive progress updates. If no progress token is provided, progress calls have no effect (they don't error).
- Clients must send a `progressToken` in the initial request to receive progress updates
- If no progress token is provided, progress calls will have no effect (they won't error)
- See [Client Progress](/clients/progress) for details on implementing client-side progress handling
See [Client Progress](/clients/progress) for details on implementing client-side progress handling.

View file

@ -3,6 +3,7 @@ title: Custom Providers
sidebarTitle: Custom
description: Build providers that source components from any data source
icon: code
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'

View file

@ -3,6 +3,7 @@ title: Filesystem Provider
sidebarTitle: Filesystem
description: Automatic component discovery from Python files
icon: folder-tree
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'

View file

@ -3,6 +3,7 @@ title: Local Provider
sidebarTitle: Local
description: The default provider for decorator-registered components
icon: house
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'

View file

@ -0,0 +1,64 @@
---
title: Namespace Transform
sidebarTitle: Namespace
description: Prefix component names to prevent conflicts
icon: tag
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.0.0" />
The `Namespace` transform prefixes all component names, preventing conflicts when composing multiple servers.
Tools and prompts receive an underscore-separated prefix. Resources and templates receive a path-segment prefix in their URIs.
| Component | Original | With `Namespace("api")` |
|-----------|----------|-------------------------|
| Tool | `my_tool` | `api_my_tool` |
| Prompt | `my_prompt` | `api_my_prompt` |
| Resource | `data://info` | `data://api/info` |
| Template | `data://{id}` | `data://api/{id}` |
The most common use is through the `mount()` method's `namespace` parameter.
```python
from fastmcp import FastMCP
weather = FastMCP("Weather")
calendar = FastMCP("Calendar")
@weather.tool
def get_data() -> str:
return "Weather data"
@calendar.tool
def get_data() -> str:
return "Calendar data"
# Without namespacing, these would conflict
main = FastMCP("Main")
main.mount(weather, namespace="weather")
main.mount(calendar, namespace="calendar")
# Clients see: weather_get_data, calendar_get_data
```
You can also apply namespacing directly using the `Namespace` transform.
```python
from fastmcp import FastMCP
from fastmcp.server.transforms import Namespace
mcp = FastMCP("Server")
@mcp.tool
def greet(name: str) -> str:
return f"Hello, {name}!"
# Namespace all components
mcp.add_transform(Namespace("api"))
# Tool is now: api_greet
```

View file

@ -3,6 +3,7 @@ title: Providers
sidebarTitle: Overview
description: How FastMCP sources tools, resources, and prompts
icon: layer-group
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'

View file

@ -3,6 +3,7 @@ title: Prompts as Tools
sidebarTitle: Prompts as Tools
description: Expose prompts to tool-only clients
icon: message-lines
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'

View file

@ -1,7 +1,7 @@
---
title: Remote Proxies
sidebarTitle: Proxying
description: Expose remote MCP servers through your local server
title: MCP Proxy Provider
sidebarTitle: MCP Proxy
description: Source components from other MCP servers
icon: arrows-retweet
---
@ -9,25 +9,25 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="2.0.0" />
Proxying lets you expose a remote MCP server's tools, resources, and prompts through your local server. Under the hood, FastMCP uses `ProxyProvider` (v3.0.0+) to source components from a client connection.
The Proxy Provider sources components from another MCP server through a client connection. This lets you expose any MCP server's tools, resources, and prompts through your own server, whether the source is local or accessed over the network.
## Why Proxy Servers
## Why Use Proxy Provider
Proxying lets you:
The Proxy Provider enables:
- **Bridge transports**: Expose a remote SSE server via local stdio
- **Aggregate servers**: Combine multiple remote servers into one
- **Add security**: Act as a controlled gateway to backend servers
- **Simplify access**: Single endpoint even if backends change
- **Bridge transports**: Make an HTTP server available via stdio, or vice versa
- **Aggregate servers**: Combine multiple source servers into one unified server
- **Add security**: Act as a controlled gateway with authentication and authorization
- **Simplify access**: Provide a stable endpoint even if backend servers change
```mermaid
sequenceDiagram
participant Client as Your Client
participant Proxy as FastMCP Proxy
participant Backend as Remote Server
participant Backend as Source Server
Client->>Proxy: MCP Request (stdio)
Proxy->>Backend: MCP Request (HTTP)
Proxy->>Backend: MCP Request (HTTP/stdio/SSE)
Backend-->>Proxy: MCP Response
Proxy-->>Client: MCP Response
```
@ -59,17 +59,17 @@ To mount a proxy inside another FastMCP server, see [Mounting External Servers](
## Transport Bridging
A common use case is bridging transports - making a remote server available locally:
A common use case is bridging transports between servers:
```python
from fastmcp.server import create_proxy
# Bridge remote HTTP to local stdio
remote_proxy = create_proxy("http://example.com/mcp/sse", name="Remote-to-Local")
# Bridge HTTP server to local stdio
http_proxy = create_proxy("http://example.com/mcp/sse", name="HTTP-to-stdio")
# Run locally via stdio for Claude Desktop
if __name__ == "__main__":
remote_proxy.run() # Defaults to stdio
http_proxy.run() # Defaults to stdio
```
Or expose a local server via HTTP:
@ -78,7 +78,7 @@ Or expose a local server via HTTP:
from fastmcp.server import create_proxy
# Bridge local server to HTTP
local_proxy = create_proxy("local_server.py", name="Local-to-HTTP")
local_proxy = create_proxy("local_server.py", name="stdio-to-HTTP")
if __name__ == "__main__":
local_proxy.run(transport="http", host="0.0.0.0", port=8080)
@ -280,7 +280,7 @@ This gives you full control over session creation and reuse strategies.
### Adding Proxied Components to Existing Server
Mount a proxy to add remote components to an existing server:
Mount a proxy to add components from another server:
```python
from fastmcp import FastMCP
@ -293,9 +293,9 @@ server = FastMCP("My Server")
def local_tool() -> str:
return "Local result"
# Mount proxied tools from remote server
remote = create_proxy("http://remote-server/mcp")
server.mount(remote)
# Mount proxied tools from another server
external = create_proxy("http://external-server/mcp")
server.mount(external)
# Now server has both local and proxied tools
```

View file

@ -3,6 +3,7 @@ title: Resources as Tools
sidebarTitle: Resources as Tools
description: Expose resources to tool-only clients
icon: toolbox
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'

View file

@ -3,6 +3,7 @@ title: Skills Provider
sidebarTitle: Skills
description: Expose agent skills as MCP resources
icon: wand-magic-sparkles
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
@ -247,45 +248,6 @@ With `reload=True`, the provider re-discovers skills on each `list_resources()`
Reload mode adds overhead to every request. Use it during development when you're actively editing skills, but disable it in production.
</Warning>
## Complete Example
This example creates a server that exposes Claude Code skills, then uses a client to discover and read them.
```python
import asyncio
import json
from fastmcp import Client, FastMCP
from fastmcp.server.providers.skills import ClaudeSkillsProvider
async def main():
# Create server with Claude skills
mcp = FastMCP("Skills Demo")
mcp.add_provider(ClaudeSkillsProvider(reload=True))
async with Client(mcp) as client:
# List available skill resources
resources = await client.list_resources()
print("Available skills:")
for r in resources:
if r.uri.path and r.uri.path.endswith("SKILL.md"):
print(f" - {r.name}: {r.description}")
# Read a specific skill's manifest
manifest_result = await client.read_resource("skill://pdf-processing/_manifest")
manifest = json.loads(manifest_result[0].text)
print(f"\nFiles in pdf-processing skill:")
for f in manifest["files"]:
print(f" - {f['path']} ({f['size']} bytes)")
# Read the main skill file
skill_result = await client.read_resource("skill://pdf-processing/SKILL.md")
print(f"\nSkill content:\n{skill_result[0].text[:500]}...")
if __name__ == "__main__":
asyncio.run(main())
```
## Client Utilities
FastMCP provides utilities for downloading skills from any MCP server that exposes them. These are standalone functions in `fastmcp.utilities.skills`.

View file

@ -0,0 +1,192 @@
---
title: Tool Transformation
sidebarTitle: Tool Transformation
description: Modify tool schemas - rename, reshape arguments, and customize behavior
icon: wrench
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.0.0" />
Tool transformation lets you modify tool schemas - renaming tools, changing descriptions, adjusting tags, and reshaping argument schemas. FastMCP provides two mechanisms that share the same configuration options but differ in timing.
**Deferred transformation** with `ToolTransform` applies modifications when tools flow through a transform chain. Use this for tools from mounted servers, proxies, or other providers where you don't control the source directly.
**Immediate transformation** with `Tool.from_tool()` creates a modified tool object right away. Use this when you have direct access to a tool and want to transform it before registration.
## ToolTransform
The `ToolTransform` class is a transform that modifies tools as they flow through a provider. Provide a dictionary mapping original tool names to their transformation configuration.
```python
from fastmcp import FastMCP
from fastmcp.server.transforms import ToolTransform
from fastmcp.tools.tool_transform import ToolTransformConfig
mcp = FastMCP("Server")
@mcp.tool
def verbose_internal_data_fetcher(query: str) -> str:
"""Fetches data from the internal database."""
return f"Results for: {query}"
# Rename the tool to something simpler
mcp.add_transform(ToolTransform({
"verbose_internal_data_fetcher": ToolTransformConfig(
name="search",
description="Search the database.",
)
}))
# Clients see "search" with the cleaner description
```
`ToolTransform` is useful when you want to modify tools from mounted or proxied servers without changing the original source.
## Tool.from_tool()
Use `Tool.from_tool()` when you have the tool object and want to create a transformed version for registration.
```python
from fastmcp import FastMCP
from fastmcp.tools import Tool, tool
from fastmcp.tools.tool_transform import ArgTransform
# Create a tool without registering it
@tool
def search(q: str, limit: int = 10) -> list[str]:
"""Search for items."""
return [f"Result {i} for {q}" for i in range(limit)]
# Transform it before registration
better_search = Tool.from_tool(
search,
name="find_items",
description="Find items matching your search query.",
transform_args={
"q": ArgTransform(
name="query",
description="The search terms to look for.",
),
},
)
mcp = FastMCP("Server")
mcp.add_tool(better_search)
```
The standalone `@tool` decorator (from `fastmcp.tools`) creates a Tool object without registering it to any server. This separates creation from registration, letting you transform tools before deciding where they go.
## Modification Options
Both mechanisms support the same modifications.
**Tool-level options:**
| Option | Description |
|--------|-------------|
| `name` | New name for the tool |
| `description` | New description |
| `title` | Human-readable title |
| `tags` | Set of tags for categorization |
| `annotations` | MCP ToolAnnotations |
| `meta` | Custom metadata dictionary |
**Argument-level options** (via `ArgTransform` or `ArgTransformConfig`):
| Option | Description |
|--------|-------------|
| `name` | Rename the argument |
| `description` | New description for the argument |
| `default` | New default value |
| `default_factory` | Callable that generates a default (requires `hide=True`) |
| `hide` | Remove from client-visible schema |
| `required` | Make an optional argument required |
| `type` | Change the argument's type |
| `examples` | Example values for the argument |
## Hiding Arguments
Hide arguments to simplify the interface or inject values the client shouldn't control.
```python
from fastmcp.tools.tool_transform import ArgTransform
# Hide with a constant value
transform_args = {
"api_key": ArgTransform(hide=True, default="secret-key"),
}
# Hide with a dynamic value
import uuid
transform_args = {
"request_id": ArgTransform(hide=True, default_factory=lambda: str(uuid.uuid4())),
}
```
Hidden arguments disappear from the tool's schema. The client never sees them, but the underlying function receives the configured value.
<Warning>
`default_factory` requires `hide=True`. Visible arguments need static defaults that can be represented in JSON Schema.
</Warning>
## Renaming Arguments
Rename arguments to make them more intuitive for LLMs or match your API conventions.
```python
from fastmcp.tools import Tool, tool
from fastmcp.tools.tool_transform import ArgTransform
@tool
def search(q: str, n: int = 10) -> list[str]:
"""Search for items."""
return []
better_search = Tool.from_tool(
search,
transform_args={
"q": ArgTransform(name="query", description="Search terms"),
"n": ArgTransform(name="max_results", description="Maximum results to return"),
},
)
```
## Custom Transform Functions
For advanced scenarios, provide a `transform_fn` that intercepts tool execution. The function can validate inputs, modify outputs, or add custom logic while still calling the original tool via `forward()`.
```python
from fastmcp import FastMCP
from fastmcp.tools import Tool, tool
from fastmcp.tools.tool_transform import forward, ArgTransform
@tool
def divide(a: float, b: float) -> float:
"""Divide a by b."""
return a / b
async def safe_divide(numerator: float, denominator: float) -> float:
if denominator == 0:
raise ValueError("Cannot divide by zero")
return await forward(numerator=numerator, denominator=denominator)
safe_division = Tool.from_tool(
divide,
name="safe_divide",
transform_fn=safe_divide,
transform_args={
"a": ArgTransform(name="numerator"),
"b": ArgTransform(name="denominator"),
},
)
mcp = FastMCP("Server")
mcp.add_tool(safe_division)
```
The `forward()` function handles argument mapping automatically. Call it with the transformed argument names, and it maps them back to the original function's parameters.
For direct access to the original function without mapping, use `forward_raw()` with the original parameter names.

View file

@ -1,8 +1,9 @@
---
title: Transforms
sidebarTitle: Transforms
title: Transforms Overview
sidebarTitle: Overview
description: Modify components as they flow through your server
icon: wand-magic-sparkles
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
@ -21,243 +22,15 @@ Provider → [Transform A] → [Transform B] → Client
When listing components, transforms receive sequences and return transformed sequences—a pure function pattern. When getting a specific component by name, transforms use a middleware pattern with `call_next`, working in reverse: mapping the client's requested name back to the original, then transforming the result.
## Namespace
## Built-in Transforms
The `Namespace` transform prefixes all component names, preventing conflicts when composing multiple servers.
FastMCP provides several transforms for common use cases:
Tools and prompts receive an underscore-separated prefix. Resources and templates receive a path-segment prefix in their URIs.
| Component | Original | With `Namespace("api")` |
|-----------|----------|-------------------------|
| Tool | `my_tool` | `api_my_tool` |
| Prompt | `my_prompt` | `api_my_prompt` |
| Resource | `data://info` | `data://api/info` |
| Template | `data://{id}` | `data://api/{id}` |
The most common use is through the `mount()` method's `namespace` parameter.
```python
from fastmcp import FastMCP
weather = FastMCP("Weather")
calendar = FastMCP("Calendar")
@weather.tool
def get_data() -> str:
return "Weather data"
@calendar.tool
def get_data() -> str:
return "Calendar data"
# Without namespacing, these would conflict
main = FastMCP("Main")
main.mount(weather, namespace="weather")
main.mount(calendar, namespace="calendar")
# Clients see: weather_get_data, calendar_get_data
```
You can also apply namespacing directly using the `Namespace` transform.
```python
from fastmcp import FastMCP
from fastmcp.server.transforms import Namespace
mcp = FastMCP("Server")
@mcp.tool
def greet(name: str) -> str:
return f"Hello, {name}!"
# Namespace all components
mcp.add_transform(Namespace("api"))
# Tool is now: api_greet
```
## Tool Transformation
Tool transformation lets you modify tool schemas - renaming tools, changing descriptions, adjusting tags, and reshaping argument schemas. FastMCP provides two mechanisms that share the same configuration options but differ in timing.
**Deferred transformation** with `ToolTransform` applies modifications when tools flow through a transform chain. Use this for tools from mounted servers, proxies, or other providers where you don't control the source directly.
**Immediate transformation** with `Tool.from_tool()` creates a modified tool object right away. Use this when you have direct access to a tool and want to transform it before registration.
### ToolTransform
The `ToolTransform` class is a transform that modifies tools as they flow through a provider. Provide a dictionary mapping original tool names to their transformation configuration.
```python
from fastmcp import FastMCP
from fastmcp.server.transforms import ToolTransform
from fastmcp.tools.tool_transform import ToolTransformConfig
mcp = FastMCP("Server")
@mcp.tool
def verbose_internal_data_fetcher(query: str) -> str:
"""Fetches data from the internal database."""
return f"Results for: {query}"
# Rename the tool to something simpler
mcp.add_transform(ToolTransform({
"verbose_internal_data_fetcher": ToolTransformConfig(
name="search",
description="Search the database.",
)
}))
# Clients see "search" with the cleaner description
```
`ToolTransform` is useful when you want to modify tools from mounted or proxied servers without changing the original source.
### Tool.from_tool()
Use `Tool.from_tool()` when you have the tool object and want to create a transformed version for registration.
```python
from fastmcp import FastMCP
from fastmcp.tools import Tool, tool
from fastmcp.tools.tool_transform import ArgTransform
# Create a tool without registering it
@tool
def search(q: str, limit: int = 10) -> list[str]:
"""Search for items."""
return [f"Result {i} for {q}" for i in range(limit)]
# Transform it before registration
better_search = Tool.from_tool(
search,
name="find_items",
description="Find items matching your search query.",
transform_args={
"q": ArgTransform(
name="query",
description="The search terms to look for.",
),
},
)
mcp = FastMCP("Server")
mcp.add_tool(better_search)
```
The standalone `@tool` decorator (from `fastmcp.tools`) creates a Tool object without registering it to any server. This separates creation from registration, letting you transform tools before deciding where they go.
### Modification Options
Both mechanisms support the same modifications.
**Tool-level options:**
| Option | Description |
|--------|-------------|
| `name` | New name for the tool |
| `description` | New description |
| `title` | Human-readable title |
| `tags` | Set of tags for categorization |
| `annotations` | MCP ToolAnnotations |
| `meta` | Custom metadata dictionary |
**Argument-level options** (via `ArgTransform` or `ArgTransformConfig`):
| Option | Description |
|--------|-------------|
| `name` | Rename the argument |
| `description` | New description for the argument |
| `default` | New default value |
| `default_factory` | Callable that generates a default (requires `hide=True`) |
| `hide` | Remove from client-visible schema |
| `required` | Make an optional argument required |
| `type` | Change the argument's type |
| `examples` | Example values for the argument |
### Hiding Arguments
Hide arguments to simplify the interface or inject values the client shouldn't control.
```python
from fastmcp.tools.tool_transform import ArgTransform
# Hide with a constant value
transform_args = {
"api_key": ArgTransform(hide=True, default="secret-key"),
}
# Hide with a dynamic value
import uuid
transform_args = {
"request_id": ArgTransform(hide=True, default_factory=lambda: str(uuid.uuid4())),
}
```
Hidden arguments disappear from the tool's schema. The client never sees them, but the underlying function receives the configured value.
<Warning>
`default_factory` requires `hide=True`. Visible arguments need static defaults that can be represented in JSON Schema.
</Warning>
### Renaming Arguments
Rename arguments to make them more intuitive for LLMs or match your API conventions.
```python
from fastmcp.tools import Tool, tool
from fastmcp.tools.tool_transform import ArgTransform
@tool
def search(q: str, n: int = 10) -> list[str]:
"""Search for items."""
return []
better_search = Tool.from_tool(
search,
transform_args={
"q": ArgTransform(name="query", description="Search terms"),
"n": ArgTransform(name="max_results", description="Maximum results to return"),
},
)
```
### Custom Transform Functions
For advanced scenarios, provide a `transform_fn` that intercepts tool execution. The function can validate inputs, modify outputs, or add custom logic while still calling the original tool via `forward()`.
```python
from fastmcp import FastMCP
from fastmcp.tools import Tool, tool
from fastmcp.tools.tool_transform import forward, ArgTransform
@tool
def divide(a: float, b: float) -> float:
"""Divide a by b."""
return a / b
async def safe_divide(numerator: float, denominator: float) -> float:
if denominator == 0:
raise ValueError("Cannot divide by zero")
return await forward(numerator=numerator, denominator=denominator)
safe_division = Tool.from_tool(
divide,
name="safe_divide",
transform_fn=safe_divide,
transform_args={
"a": ArgTransform(name="numerator"),
"b": ArgTransform(name="denominator"),
},
)
mcp = FastMCP("Server")
mcp.add_tool(safe_division)
```
The `forward()` function handles argument mapping automatically. Call it with the transformed argument names, and it maps them back to the original function's parameters.
For direct access to the original function without mapping, use `forward_raw()` with the original parameter names.
- **[Namespace](/servers/providers/namespace)** - Prefix component names to prevent conflicts when composing servers
- **[Tool Transformation](/servers/providers/tool-transformation)** - Rename tools, modify descriptions, reshape arguments
- **[Enabled](/servers/enabled)** - Control which components are visible at runtime
- **[Resources as Tools](/servers/providers/resources-as-tools)** - Expose resources to tool-only clients
- **[Prompts as Tools](/servers/providers/prompts-as-tools)** - Expose prompts to tool-only clients
## Server vs Provider Transforms

View file

@ -1,12 +1,11 @@
---
title: LLM Sampling
title: Sampling
sidebarTitle: Sampling
description: Request LLM text generation from the client or a configured provider through the MCP context.
icon: robot
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx";
import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.0.0" />
@ -14,6 +13,417 @@ LLM sampling allows your MCP tools to request text generation from an LLM during
By default, sampling requests are routed to the client's LLM. You can also configure a fallback handler to use a specific provider (like OpenAI) when the client doesn't support sampling, or to always use your own LLM regardless of client capabilities.
## Overview
The simplest use of sampling is passing a prompt string to `ctx.sample()`. The method sends the prompt to the LLM, waits for the complete response, and returns a `SamplingResult`. You can access the generated text through the `.text` attribute.
```python
from fastmcp import FastMCP, Context
mcp = FastMCP()
@mcp.tool
async def summarize(content: str, ctx: Context) -> str:
"""Generate a summary of the provided content."""
result = await ctx.sample(f"Please summarize this:\n\n{content}")
return result.text or ""
```
The `SamplingResult` also provides `.result` (identical to `.text` for plain text responses) and `.history` containing the full message exchange—useful if you need to continue the conversation or debug the interaction.
### System Prompts
System prompts let you establish the LLM's role and behavioral guidelines before it processes your request. This is useful for controlling tone, enforcing constraints, or providing context that shouldn't clutter the user-facing prompt.
````python
from fastmcp import FastMCP, Context
mcp = FastMCP()
@mcp.tool
async def generate_code(concept: str, ctx: Context) -> str:
"""Generate a Python code example for a concept."""
result = await ctx.sample(
messages=f"Write a Python example demonstrating '{concept}'.",
system_prompt=(
"You are an expert Python programmer. "
"Provide concise, working code without explanations."
),
temperature=0.7,
max_tokens=300
)
return f"```python\n{result.text}\n```"
````
The `temperature` parameter controls randomness—higher values (up to 1.0) produce more varied outputs, while lower values make responses more deterministic. The `max_tokens` parameter limits response length.
### Model Preferences
Model preferences let you hint at which LLM the client should use for a request. You can pass a single model name or a list of preferences in priority order. These are hints rather than requirements—the actual model used depends on what the client has available.
```python
from fastmcp import FastMCP, Context
mcp = FastMCP()
@mcp.tool
async def technical_analysis(data: str, ctx: Context) -> str:
"""Analyze data using a reasoning-focused model."""
result = await ctx.sample(
messages=f"Analyze this data:\n\n{data}",
model_preferences=["claude-opus-4-5", "gpt-5-2"],
temperature=0.2,
)
return result.text or ""
```
Use model preferences when different tasks benefit from different model characteristics. Creative writing might prefer faster models with higher temperature, while complex analysis might benefit from larger reasoning-focused models.
### Multi-Turn Conversations
For requests that need conversational context, construct a list of `SamplingMessage` objects representing the conversation history. Each message has a `role` ("user" or "assistant") and `content` (a `TextContent` object).
```python
from mcp.types import SamplingMessage, TextContent
from fastmcp import FastMCP, Context
mcp = FastMCP()
@mcp.tool
async def contextual_analysis(query: str, data: str, ctx: Context) -> str:
"""Analyze data with conversational context."""
messages = [
SamplingMessage(
role="user",
content=TextContent(type="text", text=f"Here's my data: {data}"),
),
SamplingMessage(
role="assistant",
content=TextContent(type="text", text="I see the data. What would you like to know?"),
),
SamplingMessage(
role="user",
content=TextContent(type="text", text=query),
),
]
result = await ctx.sample(messages=messages)
return result.text or ""
```
The LLM receives the full conversation thread and responds with awareness of the preceding context.
### Fallback Handlers
Client support for sampling is optional—some clients may not implement it. To ensure your tools work regardless of client capabilities, configure a `sampling_handler` that sends requests directly to an LLM provider.
FastMCP provides built-in handlers for [OpenAI and Anthropic APIs](/clients/sampling#built-in-handlers). These handlers support the full sampling API including tools, automatically converting your Python functions to each provider's format.
<Note>
Install handlers with `pip install fastmcp[openai]` or `pip install fastmcp[anthropic]`.
</Note>
```python
from fastmcp import FastMCP
from fastmcp.client.sampling.handlers.openai import OpenAISamplingHandler
server = FastMCP(
name="My Server",
sampling_handler=OpenAISamplingHandler(default_model="gpt-4o-mini"),
sampling_handler_behavior="fallback",
)
```
The `sampling_handler_behavior` parameter controls when the handler is used:
- **`"fallback"`** (default): Use the handler only when the client doesn't support sampling. This lets capable clients use their own LLM while ensuring your tools still work with clients that lack sampling support.
- **`"always"`**: Always use the handler, bypassing the client entirely. Use this when you need guaranteed control over which LLM processes requests—for cost control, compliance requirements, or when specific model characteristics are essential.
## Structured Output
<VersionBadge version="2.14.1" />
When you need validated, typed data instead of free-form text, use the `result_type` parameter. FastMCP ensures the LLM returns data matching your type, handling validation and retries automatically.
The `result_type` parameter accepts Pydantic models, dataclasses, and basic types like `int`, `list[str]`, or `dict[str, int]`. When you specify a result type, FastMCP automatically creates a `final_response` tool that the LLM calls to provide its response. If validation fails, the error is sent back to the LLM for retry.
```python
from pydantic import BaseModel
from fastmcp import FastMCP, Context
mcp = FastMCP()
class SentimentResult(BaseModel):
sentiment: str
confidence: float
reasoning: str
@mcp.tool
async def analyze_sentiment(text: str, ctx: Context) -> SentimentResult:
"""Analyze text sentiment with structured output."""
result = await ctx.sample(
messages=f"Analyze the sentiment of: {text}",
result_type=SentimentResult,
)
return result.result # A validated SentimentResult object
```
When you call this tool, the LLM returns a structured response that FastMCP validates against your Pydantic model. You access the validated object through `result.result`, while `result.text` contains the JSON representation.
### Structured Output with Tools
Combine structured output with tools for agentic workflows that return validated data. The LLM uses your tools to gather information, then returns a response matching your type.
```python
from pydantic import BaseModel
from fastmcp import FastMCP, Context
mcp = FastMCP()
def search(query: str) -> str:
"""Search the web for information."""
return f"Results for: {query}"
def fetch_url(url: str) -> str:
"""Fetch content from a URL."""
return f"Content from: {url}"
class ResearchResult(BaseModel):
summary: str
sources: list[str]
confidence: float
@mcp.tool
async def research(topic: str, ctx: Context) -> ResearchResult:
"""Research a topic and return structured findings."""
result = await ctx.sample(
messages=f"Research: {topic}",
tools=[search, fetch_url],
result_type=ResearchResult,
)
return result.result
```
<Note>
Structured output with automatic validation only applies to `sample()`. With `sample_step()`, you must manage structured output yourself.
</Note>
## Tool Use
<VersionBadge version="2.14.1" />
Sampling with tools enables agentic workflows where the LLM can call functions to gather information before responding. This implements [SEP-1577](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1577), allowing the LLM to autonomously orchestrate multi-step operations.
Pass Python functions to the `tools` parameter, and FastMCP handles the execution loop automatically—calling tools, returning results to the LLM, and continuing until the LLM provides a final response.
### Defining Tools
Define regular Python functions with type hints and docstrings. FastMCP extracts the function's name, docstring, and parameter types to create tool schemas that the LLM can understand.
```python
from fastmcp import FastMCP, Context
def search(query: str) -> str:
"""Search the web for information."""
return f"Results for: {query}"
def get_time() -> str:
"""Get the current time."""
from datetime import datetime
return datetime.now().strftime("%H:%M:%S")
mcp = FastMCP()
@mcp.tool
async def research(question: str, ctx: Context) -> str:
"""Answer questions using available tools."""
result = await ctx.sample(
messages=question,
tools=[search, get_time],
)
return result.text or ""
```
The LLM sees each function's signature and docstring, using this information to decide when and how to call them. Tool errors are caught and sent back to the LLM, allowing it to recover gracefully. An internal safety limit prevents infinite loops.
### Custom Tool Definitions
For custom names or descriptions, use `SamplingTool.from_function()`:
```python
from fastmcp.server.sampling import SamplingTool
tool = SamplingTool.from_function(
my_func,
name="custom_name",
description="Custom description"
)
result = await ctx.sample(messages="...", tools=[tool])
```
### Error Handling
By default, when a sampling tool raises an exception, the error message (including details) is sent back to the LLM so it can attempt recovery. To prevent sensitive information from leaking to the LLM, use the `mask_error_details` parameter:
```python
result = await ctx.sample(
messages=question,
tools=[search],
mask_error_details=True, # Generic error messages only
)
```
When `mask_error_details=True`, tool errors become generic messages like `"Error executing tool 'search'"` instead of exposing stack traces or internal details.
To intentionally provide specific error messages to the LLM regardless of masking, raise `ToolError`:
```python
from fastmcp.exceptions import ToolError
def search(query: str) -> str:
"""Search for information."""
if not query.strip():
raise ToolError("Search query cannot be empty")
return f"Results for: {query}"
```
`ToolError` messages always pass through to the LLM, making it the escape hatch for errors you want the LLM to see and handle.
### Client Requirements
<Note>
Sampling with tools requires the client to advertise the `sampling.tools` capability. FastMCP clients do this automatically. For external clients that don't support tool-enabled sampling, configure a fallback handler with `sampling_handler_behavior="always"`.
</Note>
## Advanced Control
<VersionBadge version="2.14.1" />
While `sample()` handles the tool execution loop automatically, some scenarios require fine-grained control over each step. The `sample_step()` method makes a single LLM call and returns a `SampleStep` containing the response and updated history.
Unlike `sample()`, `sample_step()` is stateless—it doesn't remember previous calls. You control the conversation by passing the full message history each time. The returned `step.history` includes all messages up through the current response, making it easy to continue the loop.
Use `sample_step()` when you need to:
- Inspect tool calls before they execute
- Implement custom termination conditions
- Add logging, metrics, or checkpointing between steps
- Build custom agentic loops with domain-specific logic
### Basic Loop
By default, `sample_step()` executes any tool calls and includes the results in the history. Call it in a loop, passing the updated history each time, until a stop condition is met.
```python
from mcp.types import SamplingMessage
from fastmcp import FastMCP, Context
mcp = FastMCP()
def search(query: str) -> str:
return f"Results for: {query}"
def get_time() -> str:
return "12:00 PM"
@mcp.tool
async def controlled_agent(question: str, ctx: Context) -> str:
"""Agent with manual loop control."""
messages: list[str | SamplingMessage] = [question]
while True:
step = await ctx.sample_step(
messages=messages,
tools=[search, get_time],
)
if step.is_tool_use:
# Tools already executed (execute_tools=True by default)
for call in step.tool_calls:
print(f"Called tool: {call.name}")
if not step.is_tool_use:
return step.text or ""
messages = step.history
```
### SampleStep Properties
Each `SampleStep` provides information about what the LLM returned:
| Property | Description |
|----------|-------------|
| `step.is_tool_use` | True if the LLM requested tool calls |
| `step.tool_calls` | List of tool calls requested (if any) |
| `step.text` | The text content (if any) |
| `step.history` | All messages exchanged so far |
The contents of `step.history` depend on `execute_tools`:
- **`execute_tools=True`** (default): Includes tool results, ready for the next iteration
- **`execute_tools=False`**: Includes the assistant's tool request, but you add results yourself
### Manual Tool Execution
Set `execute_tools=False` to handle tool execution yourself. When disabled, `step.history` contains the user message and the assistant's response with tool calls—but no tool results. You execute the tools and append the results as a user message.
```python
from mcp.types import SamplingMessage, ToolResultContent, TextContent
from fastmcp import FastMCP, Context
mcp = FastMCP()
@mcp.tool
async def research(question: str, ctx: Context) -> str:
"""Research with manual tool handling."""
def search(query: str) -> str:
return f"Results for: {query}"
def get_time() -> str:
return "12:00 PM"
tools = {"search": search, "get_time": get_time}
messages: list[SamplingMessage] = [question]
while True:
step = await ctx.sample_step(
messages=messages,
tools=list(tools.values()),
execute_tools=False,
)
if not step.is_tool_use:
return step.text or ""
# Execute tools and collect results
tool_results = []
for call in step.tool_calls:
fn = tools[call.name]
result = fn(**call.input)
tool_results.append(
ToolResultContent(
type="tool_result",
toolUseId=call.id,
content=[TextContent(type="text", text=result)],
)
)
messages = list(step.history)
messages.append(SamplingMessage(role="user", content=tool_results))
```
To report an error to the LLM, set `isError=True` on the tool result:
```python
tool_result = ToolResultContent(
type="tool_result",
toolUseId=call.id,
content=[TextContent(type="text", text="Permission denied")],
isError=True,
)
```
## Method Reference
<Card icon="code" title="ctx.sample()">
@ -70,7 +480,25 @@ By default, sampling requests are routed to the client's LLM. You can also confi
Make a single LLM sampling call. Use this for fine-grained control over the sampling loop.
<Expandable title="Parameters">
Same as `sample()`, plus:
<ResponseField name="messages" type="str | list[str | SamplingMessage]">
The prompt or conversation history.
</ResponseField>
<ResponseField name="system_prompt" type="str | None" default="None">
Instructions that establish the LLM's role and behavior.
</ResponseField>
<ResponseField name="temperature" type="float | None" default="None">
Controls randomness (0.0 = deterministic, 1.0 = creative).
</ResponseField>
<ResponseField name="max_tokens" type="int | None" default="512">
Maximum tokens to generate.
</ResponseField>
<ResponseField name="tools" type="list[Callable] | None" default="None">
Functions the LLM can call during sampling.
</ResponseField>
<ResponseField name="tool_choice" type="str | None" default="None">
Controls tool usage: `"auto"`, `"required"`, or `"none"`.
@ -81,9 +509,8 @@ By default, sampling requests are routed to the client's LLM. You can also confi
</ResponseField>
<ResponseField name="mask_error_details" type="bool | None" default="None">
If True, mask detailed error messages from tool execution. When None (default), uses the global `settings.mask_error_details` value. Tools can raise `ToolError` to bypass masking.
If True, mask detailed error messages from tool execution.
</ResponseField>
</Expandable>
<Expandable title="Response">
@ -97,392 +524,3 @@ By default, sampling requests are routed to the client's LLM. You can also confi
</Expandable>
</ResponseField>
</Card>
## Basic Sampling
The simplest use of sampling is passing a prompt string to `ctx.sample()`. The method sends the prompt to the LLM, waits for the complete response, and returns a `SamplingResult`. You can access the generated text through the `.text` attribute.
```python
from fastmcp import FastMCP, Context
mcp = FastMCP()
@mcp.tool
async def summarize(content: str, ctx: Context) -> str:
"""Generate a summary of the provided content."""
result = await ctx.sample(f"Please summarize this:\n\n{content}")
return result.text or ""
```
The `SamplingResult` also provides `.result` (identical to `.text` for plain text responses) and `.history` containing the full message exchange—useful if you need to continue the conversation or debug the interaction.
### System Prompts
System prompts let you establish the LLM's role and behavioral guidelines before it processes your request. This is useful for controlling tone, enforcing constraints, or providing context that shouldn't clutter the user-facing prompt.
````python
@mcp.tool
async def generate_code(concept: str, ctx: Context) -> str:
"""Generate a Python code example for a concept."""
result = await ctx.sample(
messages=f"Write a Python example demonstrating '{concept}'.",
system_prompt=(
"You are an expert Python programmer. "
"Provide concise, working code without explanations."
),
temperature=0.7,
max_tokens=300
)
return f"```python\n{result.text}\n```"
````
The `temperature` parameter controls randomness—higher values (up to 1.0) produce more varied outputs, while lower values make responses more deterministic. The `max_tokens` parameter limits response length.
### Model Preferences
Model preferences let you hint at which LLM the client should use for a request. You can pass a single model name or a list of preferences in priority order. These are hints rather than requirements—the actual model used depends on what the client has available.
```python
@mcp.tool
async def technical_analysis(data: str, ctx: Context) -> str:
"""Analyze data using a reasoning-focused model."""
result = await ctx.sample(
messages=f"Analyze this data:\n\n{data}",
model_preferences=["claude-opus-4-5", "gpt-5-2"],
temperature=0.2,
)
return result.text or ""
```
Use model preferences when different tasks benefit from different model characteristics. Creative writing might prefer faster models with higher temperature, while complex analysis might benefit from larger reasoning-focused models.
### Multi-Turn Conversations
For requests that need conversational context, construct a list of `SamplingMessage` objects representing the conversation history. Each message has a `role` ("user" or "assistant") and `content` (a `TextContent` object).
```python
from mcp.types import SamplingMessage, TextContent
@mcp.tool
async def contextual_analysis(query: str, data: str, ctx: Context) -> str:
"""Analyze data with conversational context."""
messages = [
SamplingMessage(
role="user",
content=TextContent(type="text", text=f"Here's my data: {data}"),
),
SamplingMessage(
role="assistant",
content=TextContent(type="text", text="I see the data. What would you like to know?"),
),
SamplingMessage(
role="user",
content=TextContent(type="text", text=query),
),
]
result = await ctx.sample(messages=messages)
return result.text or ""
```
The LLM receives the full conversation thread and responds with awareness of the preceding context.
## Structured Output
<VersionBadge version="2.14.1" />
When you need validated, typed data instead of free-form text, use the `result_type` parameter. FastMCP ensures the LLM returns data matching your type, handling validation and retries automatically. The `result_type` parameter accepts Pydantic models, dataclasses, and basic types like `int`, `list[str]`, or `dict[str, int]`.
```python
from pydantic import BaseModel
from fastmcp import FastMCP, Context
mcp = FastMCP()
class SentimentResult(BaseModel):
sentiment: str
confidence: float
reasoning: str
@mcp.tool
async def analyze_sentiment(text: str, ctx: Context) -> SentimentResult:
"""Analyze text sentiment with structured output."""
result = await ctx.sample(
messages=f"Analyze the sentiment of: {text}",
result_type=SentimentResult,
)
return result.result # A validated SentimentResult object
```
When you call this tool, the LLM returns a structured response that FastMCP validates against your Pydantic model. You access the validated object through `result.result`, while `result.text` contains the JSON representation.
<Note>
When you pass `result_type`, `sample()` automatically creates a
`final_response` tool that the LLM calls to provide its response. If
validation fails, the error is sent back to the LLM for retry. This automatic
handling only applies to `sample()`—with `sample_step()`, you must manage
structured output yourself.
</Note>
## Sampling with Tools
<VersionBadge version="2.14.1" />
Sampling with tools enables agentic workflows where the LLM can call functions to gather information before responding. This implements [SEP-1577](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1577), allowing the LLM to autonomously orchestrate multi-step operations.
Pass Python functions to the `tools` parameter, and FastMCP handles the execution loop automatically—calling tools, returning results to the LLM, and continuing until the LLM provides a final response.
### Defining Tools
Define regular Python functions with type hints and docstrings. FastMCP extracts the function's name, docstring, and parameter types to create tool schemas that the LLM can understand.
```python
from fastmcp import FastMCP, Context
def search(query: str) -> str:
"""Search the web for information."""
return f"Results for: {query}"
def get_time() -> str:
"""Get the current time."""
from datetime import datetime
return datetime.now().strftime("%H:%M:%S")
mcp = FastMCP()
@mcp.tool
async def research(question: str, ctx: Context) -> str:
"""Answer questions using available tools."""
result = await ctx.sample(
messages=question,
tools=[search, get_time],
)
return result.text or ""
```
The LLM sees each function's signature and docstring, using this information to decide when and how to call them. Tool errors are caught and sent back to the LLM, allowing it to recover gracefully. An internal safety limit prevents infinite loops.
### Tool Error Handling
By default, when a sampling tool raises an exception, the error message (including details) is sent back to the LLM so it can attempt recovery. To prevent sensitive information from leaking to the LLM, use the `mask_error_details` parameter:
```python
result = await ctx.sample(
messages=question,
tools=[search],
mask_error_details=True, # Generic error messages only
)
```
When `mask_error_details=True`, tool errors become generic messages like `"Error executing tool 'search'"` instead of exposing stack traces or internal details.
To intentionally provide specific error messages to the LLM regardless of masking, raise `ToolError`:
```python
from fastmcp.exceptions import ToolError
def search(query: str) -> str:
"""Search for information."""
if not query.strip():
raise ToolError("Search query cannot be empty")
return f"Results for: {query}"
```
`ToolError` messages always pass through to the LLM, making it the escape hatch for errors you want the LLM to see and handle.
For custom names or descriptions, use `SamplingTool.from_function()`:
```python
from fastmcp.server.sampling import SamplingTool
tool = SamplingTool.from_function(
my_func,
name="custom_name",
description="Custom description"
)
result = await ctx.sample(messages="...", tools=[tool])
```
### Combining with Structured Output
Combine tools with `result_type` for agentic workflows that return validated, structured data. The LLM uses your tools to gather information, then returns a response matching your type.
```python
result = await ctx.sample(
messages="Research Python async patterns",
tools=[search, fetch_url],
result_type=ResearchResult,
)
```
## Loop Control
<VersionBadge version="2.14.1" />
While `sample()` handles the tool execution loop automatically, some scenarios require fine-grained control over each step. The `sample_step()` method makes a single LLM call and returns a `SampleStep` containing the response and updated history.
Unlike `sample()`, `sample_step()` is stateless—it doesn't remember previous calls. You control the conversation by passing the full message history each time. The returned `step.history` includes all messages up through the current response, making it easy to continue the loop.
Use `sample_step()` when you need to:
- Inspect tool calls before they execute
- Implement custom termination conditions
- Add logging, metrics, or checkpointing between steps
- Build custom agentic loops with domain-specific logic
### Using sample_step()
By default, `sample_step()` executes any tool calls and includes the results in the history. Call it in a loop, passing the updated history each time, until a stop condition is met.
```python
from mcp.types import SamplingMessage
@mcp.tool
async def controlled_agent(question: str, ctx: Context) -> str:
"""Agent with manual loop control."""
messages: list[str | SamplingMessage] = [question] # strings auto-convert
while True:
step = await ctx.sample_step(
messages=messages,
tools=[search, get_time],
)
if step.is_tool_use:
# Tools already executed (execute_tools=True by default)
# Log what was called before continuing
for call in step.tool_calls:
print(f"Called tool: {call.name}")
if not step.is_tool_use:
return step.text or ""
# Continue with updated history
messages = step.history
```
### SampleStep Properties
Each `SampleStep` provides information about what the LLM returned:
- `step.is_tool_use` — True if the LLM requested tool calls
- `step.tool_calls` — List of tool calls requested (if any)
- `step.text` — The text content (if any)
- `step.history` — All messages exchanged so far
The contents of `step.history` depend on `execute_tools`:
- **`execute_tools=True`** (default): Includes tool results, ready for the next iteration
- **`execute_tools=False`**: Includes the assistant's tool request, but you add results yourself
### Manual Tool Execution
Set `execute_tools=False` to handle tool execution yourself. When disabled, `step.history` contains the user message and the assistant's response with tool calls—but no tool results. You execute the tools and append the results as a user message.
```python
from mcp.types import SamplingMessage, ToolResultContent, TextContent
from fastmcp import FastMCP, Context
mcp = FastMCP()
@mcp.tool
async def research(question: str, ctx: Context) -> str:
"""Research with manual tool handling."""
def search(query: str) -> str:
return f"Results for: {query}"
def get_time() -> str:
return "12:00 PM"
# Map tool names to functions
tools = {"search": search, "get_time": get_time}
messages: list[SamplingMessage] = [question] # strings are converted automatically
while True:
step = await ctx.sample_step(
messages=messages,
tools=list(tools.values()),
execute_tools=False,
)
if not step.is_tool_use:
return step.text or ""
# Execute tools and collect results
tool_results = []
for call in step.tool_calls:
fn = tools[call.name]
result = fn(**call.input)
tool_results.append(
ToolResultContent(
type="tool_result",
toolUseId=call.id,
content=[TextContent(type="text", text=result)],
)
)
messages = list(step.history)
messages.append(SamplingMessage(role="user", content=tool_results))
```
#### Error Handling
To report an error, set `isError=True`. The LLM will see the error and can decide how to proceed:
```python
tool_result = ToolResultContent(
type="tool_result",
toolUseId=call.id,
content=[TextContent(type="text", text="Permission denied")],
isError=True,
)
```
## Fallback Handlers
Client support for sampling is optional—some clients may not implement it. To ensure your tools work regardless of client capabilities, configure a `sampling_handler` that sends requests directly to an LLM provider.
FastMCP provides built-in handlers for [OpenAI and Anthropic APIs](/clients/sampling#built-in-handlers). These handlers support the full sampling API including tools, automatically converting your Python functions to each provider's format.
<Note>
Install handlers with `pip install fastmcp[openai]` or `pip install fastmcp[anthropic]`.
</Note>
```python
from fastmcp import FastMCP
from fastmcp.client.sampling.handlers.openai import OpenAISamplingHandler
server = FastMCP(
name="My Server",
sampling_handler=OpenAISamplingHandler(default_model="gpt-4o-mini"),
sampling_handler_behavior="fallback",
)
```
Or with Anthropic:
```python
from fastmcp import FastMCP
from fastmcp.client.sampling.handlers.anthropic import AnthropicSamplingHandler
server = FastMCP(
name="My Server",
sampling_handler=AnthropicSamplingHandler(default_model="claude-sonnet-4-5"),
sampling_handler_behavior="fallback",
)
```
### Behavior Modes
The `sampling_handler_behavior` parameter controls when the handler is used:
- **`"fallback"`** (default): Use the handler only when the client doesn't support sampling. This lets capable clients use their own LLM while ensuring your tools still work with clients that lack sampling support.
- **`"always"`**: Always use the handler, bypassing the client entirely. Use this when you need guaranteed control over which LLM processes requests—for cost control, compliance requirements, or when specific model characteristics are essential.
<Note>
Sampling with tools requires the client to advertise the `sampling.tools`
capability. FastMCP clients do this automatically. For external clients that
don't support tool-enabled sampling, configure a fallback handler with
`sampling_handler_behavior="always"`.
</Note>

View file

@ -1,25 +1,24 @@
---
title: The FastMCP Server
sidebarTitle: Overview
description: The core FastMCP server class for building MCP applications with tools, resources, and prompts.
description: The core FastMCP server class for building MCP applications
icon: server
---
import { VersionBadge } from "/snippets/version-badge.mdx"
The central piece of a FastMCP application is the `FastMCP` server class. This class acts as the main container for your application's tools, resources, and prompts, and manages communication with MCP clients.
The `FastMCP` class is the central piece of every FastMCP application. It acts as the container for your tools, resources, and prompts, managing communication with MCP clients and orchestrating the entire server lifecycle.
## Creating a Server
Instantiating a server is straightforward. You typically provide a name for your server, which helps identify it in client applications or logs.
Instantiate a server by providing a name that identifies it in client applications and logs. You can also provide instructions that help clients understand the server's purpose.
```python
from fastmcp import FastMCP
# Create a basic server instance
mcp = FastMCP(name="MyAssistantServer")
# You can also add instructions for how to interact with the server
# Instructions help clients understand how to interact with the server
mcp_with_instructions = FastMCP(
name="HelpfulAssistant",
instructions="""
@ -29,7 +28,7 @@ mcp_with_instructions = FastMCP(
)
```
The `FastMCP` constructor accepts several arguments:
The `FastMCP` constructor accepts several configuration options. The most commonly used parameters control server identity, authentication, and component behavior.
<Card icon="code" title="FastMCP Constructor Parameters">
<ParamField body="name" type="str" default="FastMCP">
@ -92,7 +91,7 @@ The `FastMCP` constructor accepts several arguments:
<ParamField body="strict_input_validation" type="bool" default="False">
<VersionBadge version="2.13.0" />
Controls how tool input parameters are validated. When `False` (default), FastMCP uses Pydantic's flexible validation that coerces compatible inputs (e.g., `"10"` `10` for int parameters). When `True`, uses the MCP SDK's JSON Schema validation to validate inputs against the exact schema before passing them to your function, rejecting any type mismatches. The default mode improves compatibility with LLM clients while maintaining type safety. See [Input Validation Modes](/servers/tools#input-validation-modes) for details
Controls how tool input parameters are validated. When `False` (default), FastMCP uses Pydantic's flexible validation that coerces compatible inputs (e.g., `"10"` to `10` for int parameters). When `True`, uses the MCP SDK's JSON Schema validation to validate inputs against the exact schema before passing them to your function, rejecting any type mismatches. The default mode improves compatibility with LLM clients while maintaining type safety. See [Input Validation Modes](/servers/tools#input-validation-modes) for details
</ParamField>
<ParamField body="list_page_size" type="int | None" default="None">
@ -101,13 +100,14 @@ The `FastMCP` constructor accepts several arguments:
</ParamField>
</Card>
## Components
FastMCP servers expose several types of components to the client:
FastMCP servers expose three types of components to clients. Each type serves a distinct purpose in the MCP protocol.
### Tools
Tools are functions that the client can call to perform actions or access external systems.
Tools are functions that clients can invoke to perform actions or access external systems. They're the primary way clients interact with your server's capabilities.
```python
@mcp.tool
@ -120,7 +120,7 @@ See [Tools](/servers/tools) for detailed documentation.
### Resources
Resources expose data sources that the client can read.
Resources expose data that clients can read. Unlike tools, resources are passive data sources that clients pull from rather than invoke.
```python
@mcp.resource("data://config")
@ -129,25 +129,24 @@ def get_config() -> dict:
return {"theme": "dark", "version": "1.0"}
```
See [Resources & Templates](/servers/resources) for detailed documentation.
See [Resources](/servers/resources) for detailed documentation.
### Resource Templates
Resource templates are parameterized resources that allow the client to request specific data.
Resource templates are parameterized resources. The client provides values for template parameters in the URI, and the server returns data specific to those parameters.
```python
@mcp.resource("users://{user_id}/profile")
def get_user_profile(user_id: int) -> dict:
"""Retrieves a user's profile by ID."""
# The {user_id} in the URI is extracted and passed to this function
return {"id": user_id, "name": f"User {user_id}", "status": "active"}
```
See [Resources & Templates](/servers/resources) for detailed documentation.
See [Resource Templates](/servers/resources#resource-templates) for detailed documentation.
### Prompts
Prompts are reusable message templates for guiding the LLM.
Prompts are reusable message templates that guide LLM interactions. They help establish consistent patterns for how clients should frame requests.
```python
@mcp.prompt
@ -163,9 +162,9 @@ See [Prompts](/servers/prompts) for detailed documentation.
<VersionBadge version="2.8.0" />
FastMCP supports tag-based filtering to selectively expose components based on configurable include/exclude tag sets. This is useful for creating different views of your server for different environments or users.
Tags let you categorize components and selectively expose them based on configurable include/exclude sets. This is useful for creating different views of your server for different environments or user types.
Components can be tagged when defined using the `tags` parameter:
Components can be tagged when defined using the `tags` parameter. A component can have multiple tags, and filtering operates on tag membership.
```python
@mcp.tool(tags={"public", "utility"})
@ -177,23 +176,22 @@ def admin_tool() -> str:
return "This tool is for admins only"
```
The filtering logic works as follows:
- **Include tags**: If specified, only components with at least one matching tag are exposed
- **Exclude tags**: Components with any matching tag are filtered out
- **Precedence**: Exclude tags always take priority over include tags
<Tip>
To ensure a component is never exposed, you can set `enabled=False` on the component itself. To learn more, see the component-specific documentation.
To ensure a component is never exposed, you can set `enabled=False` on the component itself. See the component-specific documentation for details.
</Tip>
You configure tag-based filtering when creating your server:
Configure tag-based filtering when creating your server.
```python
# Only expose components tagged with "public"
mcp = FastMCP(include_tags={"public"})
# Hide components tagged as "internal" or "deprecated"
# Hide components tagged as "internal" or "deprecated"
mcp = FastMCP(exclude_tags={"internal", "deprecated"})
# Combine both: show admin tools but hide deprecated ones
@ -204,10 +202,9 @@ This filtering applies to all component types (tools, resources, resource templa
## Running the Server
FastMCP servers need a transport mechanism to communicate with clients. You typically start your server by calling the `mcp.run()` method on your `FastMCP` instance, often within an `if __name__ == "__main__":` block in your main server script. This pattern ensures compatibility with various MCP clients.
FastMCP servers communicate with clients through transport mechanisms. Start your server by calling `mcp.run()`, typically within an `if __name__ == "__main__":` block. This pattern ensures compatibility with various MCP clients.
```python
# my_server.py
from fastmcp import FastMCP
mcp = FastMCP(name="MyServer")
@ -218,25 +215,23 @@ def greet(name: str) -> str:
return f"Hello, {name}!"
if __name__ == "__main__":
# This runs the server, defaulting to STDIO transport
# Defaults to STDIO transport
mcp.run()
# To use a different transport, e.g., HTTP:
# Or use HTTP transport
# mcp.run(transport="http", host="127.0.0.1", port=9000)
```
FastMCP supports several transport options:
- STDIO (default, for local tools)
- HTTP (recommended for web services, uses Streamable HTTP protocol)
- SSE (legacy web transport, deprecated)
FastMCP supports several transports:
- **STDIO** (default): For local integrations and CLI tools
- **HTTP**: For web services using the Streamable HTTP protocol
- **SSE**: Legacy web transport (deprecated)
The server can also be run using the FastMCP CLI.
For detailed information on each transport, how to configure them (host, port, paths), and when to use which, please refer to the [**Running Your FastMCP Server**](/deployment/running-server) guide.
The server can also be run using the FastMCP CLI. For detailed information on transports and configuration, see the [Running Your Server](/deployment/running-server) guide.
## Custom Routes
When running your server with HTTP transport, you can add custom web routes alongside your MCP endpoint using the `@custom_route` decorator. This is useful for simple endpoints like health checks that need to be served alongside your MCP server:
When running with HTTP transport, you can add custom web routes alongside your MCP endpoint using the `@custom_route` decorator. This is useful for auxiliary endpoints like health checks.
```python
from fastmcp import FastMCP
@ -259,142 +254,3 @@ Custom routes are served alongside your MCP endpoint and are useful for:
- Basic webhooks or callbacks
For more complex web applications, consider [mounting your MCP server into a FastAPI or Starlette app](/deployment/http#integration-with-web-frameworks).
## Composing Servers
<VersionBadge version="2.2.0" />
FastMCP supports composing multiple servers together using `import_server` (static copy) and `mount` (live link). This allows you to organize large applications into modular components or reuse existing servers.
See the [Mounting Servers](/servers/providers/mounting) guide for full details, best practices, and examples.
```python
# Example: Importing a subserver
from fastmcp import FastMCP
import asyncio
main = FastMCP(name="Main")
sub = FastMCP(name="Sub")
@sub.tool
def hello():
return "hi"
# Mount directly
main.mount(sub, namespace="sub")
```
## Proxying Servers
<VersionBadge version="2.0.0" />
FastMCP can act as a proxy for any MCP server (local or remote) using `create_proxy`, letting you bridge transports or add a frontend to existing servers. For example, you can expose a remote SSE server locally via stdio, or vice versa.
Proxies automatically handle concurrent operations safely by creating fresh sessions for each request when using disconnected clients.
See the [Remote Proxies](/servers/providers/proxy) guide for details and advanced usage.
```python
from fastmcp import Client
from fastmcp.server import create_proxy
backend = Client("http://example.com/mcp/sse")
proxy = create_proxy(backend, name="ProxyServer")
# Now use the proxy like any FastMCP server
```
## OpenAPI Integration
<VersionBadge version="2.0.0" />
FastMCP can automatically generate servers from OpenAPI specifications or existing FastAPI applications using `FastMCP.from_openapi()` and `FastMCP.from_fastapi()`. This allows you to instantly convert existing APIs into MCP servers without manual tool creation.
See the [FastAPI Integration](/integrations/fastapi) and [OpenAPI Integration](/integrations/openapi) guides for detailed examples and configuration options.
```python
import httpx
from fastmcp import FastMCP
# From OpenAPI spec
spec = httpx.get("https://api.example.com/openapi.json").json()
mcp = FastMCP.from_openapi(openapi_spec=spec, client=httpx.AsyncClient())
# From FastAPI app
from fastapi import FastAPI
app = FastAPI()
mcp = FastMCP.from_fastapi(app=app)
```
## Server Configuration
Servers can be configured using a combination of initialization arguments, global settings, and transport-specific settings.
### Server-Specific Configuration
Server-specific settings are passed when creating the `FastMCP` instance and control server behavior:
```python
from fastmcp import FastMCP
# Configure server-specific settings
mcp = FastMCP(
name="ConfiguredServer",
include_tags={"public", "api"}, # Only expose these tagged components
exclude_tags={"internal", "deprecated"}, # Hide these tagged components
on_duplicate_tools="error", # Handle duplicate registrations
on_duplicate_resources="warn",
on_duplicate_prompts="replace",
)
```
### Global Settings
Global settings affect all FastMCP servers and can be configured via environment variables (prefixed with `FASTMCP_`) or in a `.env` file:
```python
import fastmcp
# Access global settings
print(fastmcp.settings.log_level) # Default: "INFO"
print(fastmcp.settings.mask_error_details) # Default: False
print(fastmcp.settings.strict_input_validation) # Default: False
```
Common global settings include:
- **`log_level`**: Logging level ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"), set with `FASTMCP_LOG_LEVEL`
- **`mask_error_details`**: Whether to hide detailed error information from clients, set with `FASTMCP_MASK_ERROR_DETAILS`
- **`strict_input_validation`**: Controls tool input validation mode (default: False for flexible coercion), set with `FASTMCP_STRICT_INPUT_VALIDATION`. See [Input Validation Modes](/servers/tools#input-validation-modes)
- **`env_file`**: Path to the environment file to load settings from (default: ".env"), set with `FASTMCP_ENV_FILE`. Useful when your project uses a `.env` file with syntax incompatible with python-dotenv
### Transport-Specific Configuration
Transport settings are provided when running the server and control network behavior:
```python
# Configure transport when running
mcp.run(
transport="http",
host="0.0.0.0", # Bind to all interfaces
port=9000, # Custom port
log_level="DEBUG", # Override global log level
)
# Or for async usage
await mcp.run_async(
transport="http",
host="127.0.0.1",
port=8080,
)
```
### Setting Global Configuration
Global FastMCP settings can be configured via environment variables (prefixed with `FASTMCP_`):
```bash
# Configure global FastMCP behavior
export FASTMCP_LOG_LEVEL=DEBUG
export FASTMCP_MASK_ERROR_DETAILS=True
export FASTMCP_STRICT_INPUT_VALIDATION=False
```

View file

@ -3,6 +3,7 @@ title: OpenTelemetry
sidebarTitle: Telemetry
description: Native OpenTelemetry instrumentation for distributed tracing.
icon: chart-line
tag: NEW
---
FastMCP includes native OpenTelemetry instrumentation for observability. Traces are automatically generated for tool, prompt, resource, and resource template operations, providing visibility into server behavior, request handling, and provider delegation chains.

View file

@ -3,6 +3,7 @@ title: Versioning
sidebarTitle: Versioning
description: Serve multiple API versions from a single codebase
icon: code-branch
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
@ -15,7 +16,7 @@ The primary use case is serving different API versions from one codebase. Instea
## Versioned API Surfaces
Consider a server that needs to support both v1 and v2 clients. The v2 API adds new parameters to existing tools, and you want both versions to coexist cleanly. You define your components on a shared provider, then create separate servers with different version filters.
Consider a server that needs to support both v1 and v2 clients. The v2 API adds new parameters to existing tools, and you want both versions to coexist cleanly. Define your components on a shared provider, then create separate servers with different version filters.
```python
from fastmcp import FastMCP
@ -59,7 +60,7 @@ VersionFilter(version_gte="2.0", version_lt="3.0")
```
<Note>
**Unversioned components are exempt from version filtering.** A `VersionFilter` only affects versioned componentsunversioned components always pass through regardless of the filter's constraints. This ensures that adding version filtering to a server with mixed versioned and unversioned tools doesn't accidentally hide the unversioned ones. To prevent confusion, FastMCP forbids mixing versioned and unversioned components with the same name.
**Unversioned components are exempt from version filtering.** A `VersionFilter` only affects versioned components - unversioned components always pass through regardless of the filter's constraints. This ensures that adding version filtering to a server with mixed versioned and unversioned tools doesn't accidentally hide the unversioned ones. To prevent confusion, FastMCP forbids mixing versioned and unversioned components with the same name.
</Note>
### Filtering Mounted Servers
@ -139,6 +140,8 @@ The error message explains the conflict: "Cannot add versioned tool 'calculate'
This restriction exists because unversioned components always pass through version filters. If you could mix versioned and unversioned components, you'd have no way to filter out the unversioned one using `VersionFilter`. By enforcing consistency at registration, FastMCP ensures version filtering behaves predictably.
Resources and prompts follow the same pattern.
```python
@mcp.resource("config://app", version="1.0")
def config_v1() -> str:
@ -243,8 +246,8 @@ For [PEP 440](https://peps.python.org/pep-0440/) versions (like `"1.0"`, `"2.1.3
```python
# PEP 440 versions compare semantically
"1" < "2" < "10" # Not string order ("1" < "10" < "2")
"1.9" < "1.10" # Not string order ("1.10" < "1.9")
"1" < "2" < "10" # Numeric order (not "1" < "10" < "2")
"1.9" < "1.10" # Numeric order (not "1.10" < "1.9")
"1.0a1" < "1.0b1" < "1.0" # Pre-releases sort before releases
```

View file

@ -1,6 +1,6 @@
---
title: Component Visibility
sidebarTitle: Component Visibility
sidebarTitle: Visibility
description: Control which components are available to clients
icon: toggle-on
tag: NEW
@ -259,33 +259,6 @@ mcp.disable(tags={"beta"})
# new_feature is disabled (server's later disable overrides provider's enable)
```
## Dynamic Changes
Visibility state changes take effect immediately. You can adjust during request handling based on context.
```python
from fastmcp import FastMCP
from fastmcp.server import Context
mcp = FastMCP("Server")
@mcp.tool(tags={"admin"})
def admin_action() -> str:
return "Admin action performed"
@mcp.tool
def check_permissions(ctx: Context) -> str:
"""Check if admin tools should be available."""
user = ctx.request_context.get_user()
if user and user.is_admin:
mcp.enable(tags={"admin"})
return "Admin tools enabled"
else:
mcp.disable(tags={"admin"})
return "Admin tools disabled"
```
## Per-Session Visibility
Server-level visibility changes affect all connected clients simultaneously. When you need different clients to see different components, use per-session visibility instead.

39
docs/v2-banner.js Normal file
View file

@ -0,0 +1,39 @@
// Add v2 banner inside content-container with negative margins
(function() {
if (typeof window === 'undefined') return;
function addBanner() {
const isV2 = window.location.pathname.includes('/v2/');
const container = document.getElementById('content-container');
let banner = document.getElementById('v2-banner');
if (isV2 && container) {
if (!banner) {
banner = document.createElement('div');
banner.id = 'v2-banner';
banner.innerHTML = 'These are the docs for FastMCP 2.0. The beta of <a href="/getting-started/welcome" style="color: white; text-decoration: underline; font-weight: 700;">FastMCP 3.0</a> is now available.';
container.insertBefore(banner, container.firstChild);
}
} else if (!isV2 && banner) {
banner.remove();
}
}
function run() {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', addBanner);
} else {
addBanner();
}
}
run();
let lastUrl = location.href;
new MutationObserver(() => {
if (location.href !== lastUrl) {
lastUrl = location.href;
setTimeout(addBanner, 100);
}
}).observe(document.body, {subtree: true, childList: true});
})();

2280
docs/v2/changelog.mdx Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,88 @@
---
title: Bearer Token Authentication
sidebarTitle: Bearer Auth
description: Authenticate your FastMCP client with a Bearer token.
icon: key
---
import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.6.0" />
<Tip>
Bearer Token authentication is only relevant for HTTP-based transports.
</Tip>
You can configure your FastMCP client to use **bearer authentication** by supplying a valid access token. This is most appropriate for service accounts, long-lived API keys, CI/CD, applications where authentication is managed separately, or other non-interactive authentication methods.
A Bearer token is a JSON Web Token (JWT) that is used to authenticate a request. It is most commonly used in the `Authorization` header of an HTTP request, using the `Bearer` scheme:
```http
Authorization: Bearer <token>
```
## Client Usage
The most straightforward way to use a pre-existing Bearer token is to provide it as a string to the `auth` parameter of the `fastmcp.Client` or transport instance. FastMCP will automatically format it correctly for the `Authorization` header and bearer scheme.
<Tip>
If you're using a string token, do not include the `Bearer` prefix. FastMCP will add it for you.
</Tip>
```python {5}
from fastmcp import Client
async with Client(
"https://fastmcp.cloud/mcp",
auth="<your-token>",
) as client:
await client.ping()
```
You can also supply a Bearer token to a transport instance, such as `StreamableHttpTransport` or `SSETransport`:
```python {6}
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport
transport = StreamableHttpTransport(
"http://fastmcp.cloud/mcp",
auth="<your-token>",
)
async with Client(transport) as client:
await client.ping()
```
## `BearerAuth` Helper
If you prefer to be more explicit and not rely on FastMCP to transform your string token, you can use the `BearerAuth` class yourself, which implements the `httpx.Auth` interface.
```python {6}
from fastmcp import Client
from fastmcp.client.auth import BearerAuth
async with Client(
"https://fastmcp.cloud/mcp",
auth=BearerAuth(token="<your-token>"),
) as client:
await client.ping()
```
## Custom Headers
If the MCP server expects a custom header or token scheme, you can manually set the client's `headers` instead of using the `auth` parameter by setting them on your transport:
```python {5}
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport
async with Client(
transport=StreamableHttpTransport(
"https://fastmcp.cloud/mcp",
headers={"X-API-Key": "<your-token>"},
),
) as client:
await client.ping()
```

View file

@ -0,0 +1,132 @@
---
title: OAuth Authentication
sidebarTitle: OAuth
description: Authenticate your FastMCP client via OAuth 2.1.
icon: window
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.6.0" />
<Tip>
OAuth authentication is only relevant for HTTP-based transports and requires user interaction via a web browser.
</Tip>
When your FastMCP client needs to access an MCP server protected by OAuth 2.1, and the process requires user interaction (like logging in and granting consent), you should use the Authorization Code Flow. FastMCP provides the `fastmcp.client.auth.OAuth` helper to simplify this entire process.
This flow is common for user-facing applications where the application acts on behalf of the user.
## Client Usage
### Default Configuration
The simplest way to use OAuth is to pass the string `"oauth"` to the `auth` parameter of the `Client` or transport instance. FastMCP will automatically configure the client to use OAuth with default settings:
```python {4}
from fastmcp import Client
# Uses default OAuth settings
async with Client("https://fastmcp.cloud/mcp", auth="oauth") as client:
await client.ping()
```
### `OAuth` Helper
To fully configure the OAuth flow, use the `OAuth` helper and pass it to the `auth` parameter of the `Client` or transport instance. `OAuth` manages the complexities of the OAuth 2.1 Authorization Code Grant with PKCE (Proof Key for Code Exchange) for enhanced security, and implements the full `httpx.Auth` interface.
```python {2, 4, 6}
from fastmcp import Client
from fastmcp.client.auth import OAuth
oauth = OAuth(mcp_url="https://fastmcp.cloud/mcp")
async with Client("https://fastmcp.cloud/mcp", auth=oauth) as client:
await client.ping()
```
#### `OAuth` Parameters
- **`mcp_url`** (`str`): The full URL of the target MCP server endpoint. Used to discover OAuth server metadata
- **`scopes`** (`str | list[str]`, optional): OAuth scopes to request. Can be space-separated string or list of strings
- **`client_name`** (`str`, optional): Client name for dynamic registration. Defaults to `"FastMCP Client"`
- **`token_storage`** (`AsyncKeyValue`, optional): Storage backend for persisting OAuth tokens. Defaults to in-memory storage (tokens lost on restart). See [Token Storage](#token-storage) for encrypted storage options
- **`additional_client_metadata`** (`dict[str, Any]`, optional): Extra metadata for client registration
- **`callback_port`** (`int`, optional): Fixed port for OAuth callback server. If not specified, uses a random available port
## OAuth Flow
The OAuth flow is triggered when you use a FastMCP `Client` configured to use OAuth.
<Steps>
<Step title="Token Check">
The client first checks the configured `token_storage` backend for existing, valid tokens for the target server. If one is found, it will be used to authenticate the client.
</Step>
<Step title="OAuth Server Discovery">
If no valid tokens exist, the client attempts to discover the OAuth server's endpoints using a well-known URI (e.g., `/.well-known/oauth-authorization-server`) based on the `mcp_url`.
</Step>
<Step title="Dynamic Client Registration">
If the OAuth server supports it and the client isn't already registered (or credentials aren't cached), the client performs dynamic client registration according to RFC 7591.
</Step>
<Step title="Local Callback Server">
A temporary local HTTP server is started on an available port (or the port specified via `callback_port`). This server's address (e.g., `http://127.0.0.1:<port>/callback`) acts as the `redirect_uri` for the OAuth flow.
</Step>
<Step title="Browser Interaction">
The user's default web browser is automatically opened, directing them to the OAuth server's authorization endpoint. The user logs in and grants (or denies) the requested `scopes`.
</Step>
<Step title="Authorization Code & Token Exchange">
Upon approval, the OAuth server redirects the user's browser to the local callback server with an `authorization_code`. The client captures this code and exchanges it with the OAuth server's token endpoint for an `access_token` (and often a `refresh_token`) using PKCE for security.
</Step>
<Step title="Token Caching">
The obtained tokens are saved to the configured `token_storage` backend for future use, eliminating the need for repeated browser interactions.
</Step>
<Step title="Authenticated Requests">
The access token is automatically included in the `Authorization` header for requests to the MCP server.
</Step>
<Step title="Refresh Token">
If the access token expires, the client will automatically use the refresh token to get a new access token.
</Step>
</Steps>
## Token Storage
<VersionBadge version="2.13.0" />
By default, tokens are stored in memory and lost when your application restarts. For persistent storage, pass an `AsyncKeyValue`-compatible storage backend to the `token_storage` parameter.
<Warning>
**Security Consideration**: Use encrypted storage for production. MCP clients can accumulate OAuth credentials for many servers over time, and a compromised token store could expose access to multiple services.
</Warning>
```python
from fastmcp import Client
from fastmcp.client.auth import OAuth
from key_value.aio.stores.disk import DiskStore
from key_value.aio.wrappers.encryption import FernetEncryptionWrapper
from cryptography.fernet import Fernet
import os
# Create encrypted disk storage
encrypted_storage = FernetEncryptionWrapper(
key_value=DiskStore(directory="~/.fastmcp/oauth-tokens"),
fernet=Fernet(os.environ["OAUTH_STORAGE_ENCRYPTION_KEY"])
)
oauth = OAuth(
mcp_url="https://fastmcp.cloud/mcp",
token_storage=encrypted_storage
)
async with Client("https://fastmcp.cloud/mcp", auth=oauth) as client:
await client.ping()
```
You can use any `AsyncKeyValue`-compatible backend from the [key-value library](https://github.com/strawgate/py-key-value) including Redis, DynamoDB, and more. Wrap your storage in `FernetEncryptionWrapper` for encryption.
<Note>
When selecting a storage backend, review the [py-key-value documentation](https://github.com/strawgate/py-key-value) to understand the maturity level and limitations of your chosen backend. Some backends may be in preview or have constraints that affect production suitability.
</Note>

340
docs/v2/clients/client.mdx Normal file
View file

@ -0,0 +1,340 @@
---
title: The FastMCP Client
sidebarTitle: Overview
description: Programmatic client for interacting with MCP servers through a well-typed, Pythonic interface.
icon: user-robot
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="2.0.0" />
The central piece of MCP client applications is the `fastmcp.Client` class. This class provides a **programmatic interface** for interacting with any Model Context Protocol (MCP) server, handling protocol details and connection management automatically.
The FastMCP Client is designed for deterministic, controlled interactions rather than autonomous behavior, making it ideal for:
- **Testing MCP servers** during development
- **Building deterministic applications** that need reliable MCP interactions
- **Creating the foundation for agentic or LLM-based clients** with structured, type-safe operations
All client operations require using the `async with` context manager for proper connection lifecycle management.
<Note>
This is not an agentic client - it requires explicit function calls and provides direct control over all MCP operations. Use it as a building block for higher-level systems.
</Note>
## Creating a Client
Creating a client is straightforward. You provide a server source and the client automatically infers the appropriate transport mechanism.
```python
import asyncio
from fastmcp import Client, FastMCP
# In-memory server (ideal for testing)
server = FastMCP("TestServer")
client = Client(server)
# HTTP server
client = Client("https://example.com/mcp")
# Local Python script
client = Client("my_mcp_server.py")
async def main():
async with client:
# Basic server interaction
await client.ping()
# List available operations
tools = await client.list_tools()
resources = await client.list_resources()
prompts = await client.list_prompts()
# Execute operations
result = await client.call_tool("example_tool", {"param": "value"})
print(result)
asyncio.run(main())
```
## Client-Transport Architecture
The FastMCP Client separates concerns between protocol and connection:
- **`Client`**: Handles MCP protocol operations (tools, resources, prompts) and manages callbacks
- **`Transport`**: Establishes and maintains the connection (WebSockets, HTTP, Stdio, in-memory)
### Transport Inference
The client automatically infers the appropriate transport based on the input:
1. **`FastMCP` instance** → In-memory transport (perfect for testing)
2. **File path ending in `.py`** → Python Stdio transport
3. **File path ending in `.js`** → Node.js Stdio transport
4. **URL starting with `http://` or `https://`** → HTTP transport
5. **`MCPConfig` dictionary** → Multi-server client
```python
from fastmcp import Client, FastMCP
# Examples of transport inference
client_memory = Client(FastMCP("TestServer"))
client_script = Client("./server.py")
client_http = Client("https://api.example.com/mcp")
```
<Tip>
For testing and development, always prefer the in-memory transport by passing a `FastMCP` server directly to the client. This eliminates network complexity and separate processes.
</Tip>
## Configuration-Based Clients
<VersionBadge version="2.4.0" />
Create clients from MCP configuration dictionaries, which can include multiple servers. While there is no official standard for MCP configuration format, FastMCP follows established conventions used by tools like Claude Desktop.
### Configuration Format
```python
config = {
"mcpServers": {
"server_name": {
# Remote HTTP/SSE server
"transport": "http", # or "sse"
"url": "https://api.example.com/mcp",
"headers": {"Authorization": "Bearer token"},
"auth": "oauth" # or bearer token string
},
"local_server": {
# Local stdio server
"transport": "stdio",
"command": "python",
"args": ["./server.py", "--verbose"],
"env": {"DEBUG": "true"},
"cwd": "/path/to/server",
}
}
}
```
### Multi-Server Example
```python
config = {
"mcpServers": {
"weather": {"url": "https://weather-api.example.com/mcp"},
"assistant": {"command": "python", "args": ["./assistant_server.py"]}
}
}
client = Client(config)
async with client:
# Tools are prefixed with server names
weather_data = await client.call_tool("weather_get_forecast", {"city": "London"})
response = await client.call_tool("assistant_answer_question", {"question": "What's the capital of France?"})
# Resources use prefixed URIs
icons = await client.read_resource("weather://weather/icons/sunny")
templates = await client.read_resource("resource://assistant/templates/list")
```
## Connection Lifecycle
The client operates asynchronously and uses context managers for connection management:
```python
async def example():
client = Client("my_mcp_server.py")
# Connection established here
async with client:
print(f"Connected: {client.is_connected()}")
# Make multiple calls within the same session
tools = await client.list_tools()
result = await client.call_tool("greet", {"name": "World"})
# Connection closed automatically here
print(f"Connected: {client.is_connected()}")
```
## Operations
FastMCP clients can interact with several types of server components:
### Tools
Tools are server-side functions that the client can execute with arguments.
```python
async with client:
# List available tools
tools = await client.list_tools()
# Execute a tool
result = await client.call_tool("multiply", {"a": 5, "b": 3})
print(result.data) # 15
```
See [Tools](/clients/tools) for detailed documentation.
### Resources
Resources are data sources that the client can read, either static or templated.
```python
async with client:
# List available resources
resources = await client.list_resources()
# Read a resource
content = await client.read_resource("file:///config/settings.json")
print(content[0].text)
```
See [Resources](/clients/resources) for detailed documentation.
### Prompts
Prompts are reusable message templates that can accept arguments.
```python
async with client:
# List available prompts
prompts = await client.list_prompts()
# Get a rendered prompt
messages = await client.get_prompt("analyze_data", {"data": [1, 2, 3]})
print(messages.messages)
```
See [Prompts](/clients/prompts) for detailed documentation.
### Server Connectivity
Use `ping()` to verify the server is reachable:
```python
async with client:
await client.ping()
print("Server is reachable")
```
### Initialization and Server Information
When you enter the client context manager, the client automatically performs an MCP initialization handshake with the server. This handshake exchanges capabilities, server metadata, and instructions. The result is available through the `initialize_result` property.
```python
from fastmcp import Client, FastMCP
mcp = FastMCP(name="MyServer", instructions="Use the greet tool to say hello!")
@mcp.tool
def greet(name: str) -> str:
"""Greet a user by name."""
return f"Hello, {name}!"
async with Client(mcp) as client:
# Initialization already happened automatically
print(f"Server: {client.initialize_result.serverInfo.name}")
print(f"Version: {client.initialize_result.serverInfo.version}")
print(f"Instructions: {client.initialize_result.instructions}")
print(f"Capabilities: {client.initialize_result.capabilities.tools}")
```
#### Manual Initialization Control
In advanced scenarios, you might want precise control over when initialization happens. For example, you may need custom error handling, want to defer initialization until after other setup, or need to measure initialization timing separately.
Disable automatic initialization and call `initialize()` manually:
```python
from fastmcp import Client
# Disable automatic initialization
client = Client("my_mcp_server.py", auto_initialize=False)
async with client:
# Connection established, but not initialized yet
print(f"Connected: {client.is_connected()}")
print(f"Initialized: {client.initialize_result is not None}") # False
# Initialize manually with custom timeout
result = await client.initialize(timeout=10.0)
print(f"Server: {result.serverInfo.name}")
# Now ready for operations
tools = await client.list_tools()
```
The `initialize()` method is idempotent - calling it multiple times returns the cached result from the first successful call.
## Client Configuration
Clients can be configured with additional handlers and settings for specialized use cases.
### Callback Handlers
The client supports several callback handlers for advanced server interactions:
```python
from fastmcp import Client
from fastmcp.client.logging import LogMessage
async def log_handler(message: LogMessage):
print(f"Server log: {message.data}")
async def progress_handler(progress: float, total: float | None, message: str | None):
print(f"Progress: {progress}/{total} - {message}")
async def sampling_handler(messages, params, context):
# Integrate with your LLM service here
return "Generated response"
client = Client(
"my_mcp_server.py",
log_handler=log_handler,
progress_handler=progress_handler,
sampling_handler=sampling_handler,
timeout=30.0
)
```
The `Client` constructor accepts several configuration options:
- `transport`: Transport instance or source for automatic inference
- `log_handler`: Handle server log messages
- `progress_handler`: Monitor long-running operations
- `sampling_handler`: Respond to server LLM requests
- `roots`: Provide local context to servers
- `timeout`: Default timeout for requests (in seconds)
### Transport Configuration
For detailed transport configuration (headers, authentication, environment variables), see the [Transports](/clients/transports) documentation.
## Next Steps
Explore the detailed documentation for each operation type:
### Core Operations
- **[Tools](/clients/tools)** - Execute server-side functions and handle results
- **[Resources](/clients/resources)** - Access static and templated resources
- **[Prompts](/clients/prompts)** - Work with message templates and argument serialization
### Advanced Features
- **[Logging](/clients/logging)** - Handle server log messages
- **[Progress](/clients/progress)** - Monitor long-running operations
- **[Sampling](/clients/sampling)** - Respond to server LLM requests
- **[Roots](/clients/roots)** - Provide local context to servers
### Connection Details
- **[Transports](/clients/transports)** - Configure connection methods and parameters
- **[Authentication](/clients/auth/oauth)** - Set up OAuth and bearer token authentication
<Tip>
The FastMCP Client is designed as a foundational tool. Use it directly for deterministic operations, or build higher-level agentic systems on top of its reliable, type-safe interface.
</Tip>

View file

@ -0,0 +1,125 @@
---
title: User Elicitation
sidebarTitle: Elicitation
description: Handle server-initiated user input requests with structured schemas.
icon: message-question
---
import { VersionBadge } from "/snippets/version-badge.mdx";
<VersionBadge version="2.10.0" />
## What is Elicitation?
Elicitation allows MCP servers to request structured input from users during tool execution. Instead of requiring all inputs upfront, servers can interactively ask users for information as needed - like prompting for missing parameters, requesting clarification, or gathering additional context.
For example, a file management tool might ask "Which directory should I create?" or a data analysis tool might request "What date range should I analyze?"
## How FastMCP Makes Elicitation Easy
FastMCP's client provides a helpful abstraction layer that:
- **Converts JSON schemas to Python types**: The raw MCP protocol uses JSON schemas, but FastMCP automatically converts these to Python dataclasses
- **Provides structured constructors**: Instead of manually building dictionaries that match the schema, you get dataclass constructors that ensure correct structure
- **Handles type conversion**: FastMCP takes care of converting between JSON representations and Python objects
- **Runtime introspection**: You can inspect the generated dataclass fields to understand the expected structure
When you implement an elicitation handler, FastMCP gives you a dataclass type that matches the server's schema, making it easy to create properly structured responses without having to manually parse JSON schemas.
## Elicitation Handler
Provide an `elicitation_handler` function when creating the client. FastMCP automatically converts the server's JSON schema into a Python dataclass type, making it easy to construct the response:
```python
from fastmcp import Client
from fastmcp.client.elicitation import ElicitResult
async def elicitation_handler(message: str, response_type: type, params, context):
# Present the message to the user and collect input
user_input = input(f"{message}: ")
# Create response using the provided dataclass type
# FastMCP converted the JSON schema to this Python type for you
response_data = response_type(value=user_input)
# You can return data directly - FastMCP will implicitly accept the elicitation
return response_data
# Or explicitly return an ElicitResult for more control
# return ElicitResult(action="accept", content=response_data)
client = Client(
"my_mcp_server.py",
elicitation_handler=elicitation_handler,
)
```
### Handler Parameters
The elicitation handler receives four parameters:
<Card icon="code" title="Elicitation Handler Parameters">
<ResponseField name="message" type="str">
The prompt message to display to the user
</ResponseField>
<ResponseField name="response_type" type="type">
A Python dataclass type that FastMCP created from the server's JSON schema. Use this to construct your response with proper typing and IDE support. If the server requests an empty object (indicating no response), this will be `None`.
</ResponseField>
<ResponseField name="params" type="ElicitRequestParams">
The original MCP elicitation request parameters, including the raw JSON schema in `params.requestedSchema` if you need it
</ResponseField>
<ResponseField name="context" type="RequestContext">
Request context containing metadata about the elicitation request
</ResponseField>
</Card>
### Response Actions
The handler can return data directly (which implicitly accepts the elicitation) or an `ElicitResult` object for more control over the response action:
<Card icon="code" title="ElicitResult Structure">
<ResponseField name="action" type="Literal['accept', 'decline', 'cancel']">
How the user responded to the elicitation request
</ResponseField>
<ResponseField name="content" type="dataclass instance | dict | None">
The user's input data (required for "accept", omitted for "decline"/"cancel")
</ResponseField>
</Card>
**Action Types:**
- **`accept`**: User provided valid input - include their data in the `content` field
- **`decline`**: User chose not to provide the requested information - omit `content`
- **`cancel`**: User cancelled the entire operation - omit `content`
## Basic Example
```python
from fastmcp import Client
from fastmcp.client.elicitation import ElicitResult
async def basic_elicitation_handler(message: str, response_type: type, params, context):
print(f"Server asks: {message}")
# Simple text input for demonstration
user_response = input("Your response: ")
if not user_response:
# For non-acceptance, use ElicitResult explicitly
return ElicitResult(action="decline")
# Use the response_type dataclass to create a properly structured response
# FastMCP handles the conversion from JSON schema to Python type
# Return data directly - FastMCP will implicitly accept the elicitation
return response_type(value=user_response)
client = Client(
"my_mcp_server.py",
elicitation_handler=basic_elicitation_handler
)
```

111
docs/v2/clients/logging.mdx Normal file
View file

@ -0,0 +1,111 @@
---
title: Server Logging
sidebarTitle: Logging
description: Receive and handle log messages from MCP servers.
icon: receipt
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="2.0.0" />
MCP servers can emit log messages to clients. The client can handle these logs through a log handler callback.
## Log Handler
Provide a `log_handler` function when creating the client. For robust logging, the log messages can be integrated with Python's standard `logging` module.
```python
import logging
from fastmcp import Client
from fastmcp.client.logging import LogMessage
# In a real app, you might configure this in your main entry point
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Get a logger for the module where the client is used
logger = logging.getLogger(__name__)
# This mapping is useful for converting MCP level strings to Python's levels
LOGGING_LEVEL_MAP = logging.getLevelNamesMapping()
async def log_handler(message: LogMessage):
"""
Handles incoming logs from the MCP server and forwards them
to the standard Python logging system.
"""
msg = message.data.get('msg')
extra = message.data.get('extra')
# Convert the MCP log level to a Python log level
level = LOGGING_LEVEL_MAP.get(message.level.upper(), logging.INFO)
# Log the message using the standard logging library
logger.log(level, msg, extra=extra)
client = Client(
"my_mcp_server.py",
log_handler=log_handler,
)
```
## Handling Structured Logs
The `message.data` attribute is a dictionary that contains the log payload from the server. This enables structured logging, allowing you to receive rich, contextual information.
The dictionary contains two keys:
- `msg`: The string log message.
- `extra`: A dictionary containing any extra data sent from the server.
This structure is preserved even when logs are forwarded through a FastMCP proxy, making it a powerful tool for debugging complex, multi-server applications.
### Handler Parameters
The `log_handler` is called every time a log message is received. It receives a `LogMessage` object:
<Card icon="code" title="Log Handler Parameters">
<ResponseField name="LogMessage" type="Log Message Object">
<Expandable title="attributes">
<ResponseField name="level" type='Literal["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"]'>
The log level
</ResponseField>
<ResponseField name="logger" type="str | None">
The logger name (optional, may be None)
</ResponseField>
<ResponseField name="data" type="dict">
The log payload, containing `msg` and `extra` keys.
</ResponseField>
</Expandable>
</ResponseField>
</Card>
```python
async def detailed_log_handler(message: LogMessage):
msg = message.data.get('msg')
extra = message.data.get('extra')
if message.level == "error":
print(f"ERROR: {msg} | Details: {extra}")
elif message.level == "warning":
print(f"WARNING: {msg} | Details: {extra}")
else:
print(f"{message.level.upper()}: {msg}")
```
## Default Log Handling
If you don't provide a custom `log_handler`, FastMCP's default handler routes server logs to the appropriate Python logging levels. The MCP levels are mapped as follows: `notice` → INFO; `alert` and `emergency` → CRITICAL. If the server includes a logger name, it is prefixed in the message, and any `extra` data is forwarded via the logging `extra` parameter.
```python
client = Client("my_mcp_server.py")
async with client:
# Server logs are forwarded at their proper severity (DEBUG/INFO/WARNING/ERROR/CRITICAL)
await client.call_tool("some_tool")
```

View file

@ -0,0 +1,70 @@
---
title: Progress Monitoring
sidebarTitle: Progress
description: Handle progress notifications from long-running server operations.
icon: bars-progress
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="2.3.5" />
MCP servers can report progress during long-running operations. The client can receive these updates through a progress handler.
## Progress Handler
Set a progress handler when creating the client:
```python
from fastmcp import Client
async def my_progress_handler(
progress: float,
total: float | None,
message: str | None
) -> None:
if total is not None:
percentage = (progress / total) * 100
print(f"Progress: {percentage:.1f}% - {message or ''}")
else:
print(f"Progress: {progress} - {message or ''}")
client = Client(
"my_mcp_server.py",
progress_handler=my_progress_handler
)
```
### Handler Parameters
The progress handler receives three parameters:
<Card icon="code" title="Progress Handler Parameters">
<ResponseField name="progress" type="float">
Current progress value
</ResponseField>
<ResponseField name="total" type="float | None">
Expected total value (may be None)
</ResponseField>
<ResponseField name="message" type="str | None">
Optional status message (may be None)
</ResponseField>
</Card>
## Per-Call Progress Handler
Override the progress handler for specific tool calls:
```python
async with client:
# Override with specific progress handler for this call
result = await client.call_tool(
"long_running_task",
{"param": "value"},
progress_handler=my_progress_handler
)
```

216
docs/v2/clients/prompts.mdx Normal file
View file

@ -0,0 +1,216 @@
---
title: Prompts
sidebarTitle: Prompts
description: Use server-side prompt templates with automatic argument serialization.
icon: message-lines
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="2.0.0" />
Prompts are reusable message templates exposed by MCP servers. They can accept arguments to generate personalized message sequences for LLM interactions.
## Listing Prompts
Use `list_prompts()` to retrieve all available prompt templates:
```python
async with client:
prompts = await client.list_prompts()
# prompts -> list[mcp.types.Prompt]
for prompt in prompts:
print(f"Prompt: {prompt.name}")
print(f"Description: {prompt.description}")
if prompt.arguments:
print(f"Arguments: {[arg.name for arg in prompt.arguments]}")
# Access tags and other metadata
if hasattr(prompt, '_meta') and prompt._meta:
fastmcp_meta = prompt._meta.get('_fastmcp', {})
print(f"Tags: {fastmcp_meta.get('tags', [])}")
```
### Filtering by Tags
<VersionBadge version="2.11.0" />
You can use the `meta` field to filter prompts based on their tags:
```python
async with client:
prompts = await client.list_prompts()
# Filter prompts by tag
analysis_prompts = [
prompt for prompt in prompts
if hasattr(prompt, '_meta') and prompt._meta and
prompt._meta.get('_fastmcp', {}) and
'analysis' in prompt._meta.get('_fastmcp', {}).get('tags', [])
]
print(f"Found {len(analysis_prompts)} analysis prompts")
```
<Note>
The `_meta` field is part of the standard MCP specification. FastMCP servers include tags and other metadata within a `_fastmcp` namespace (e.g., `_meta._fastmcp.tags`) to avoid conflicts with user-defined metadata. This behavior can be controlled with the server's `include_fastmcp_meta` setting - when disabled, the `_fastmcp` namespace won't be included. Other MCP server implementations may not provide this metadata structure.
</Note>
## Using Prompts
### Basic Usage
Request a rendered prompt using `get_prompt()` with the prompt name and arguments:
```python
async with client:
# Simple prompt without arguments
result = await client.get_prompt("welcome_message")
# result -> mcp.types.GetPromptResult
# Access the generated messages
for message in result.messages:
print(f"Role: {message.role}")
print(f"Content: {message.content}")
```
### Prompts with Arguments
Pass arguments as a dictionary to customize the prompt:
```python
async with client:
# Prompt with simple arguments
result = await client.get_prompt("user_greeting", {
"name": "Alice",
"role": "administrator"
})
# Access the personalized messages
for message in result.messages:
print(f"Generated message: {message.content}")
```
## Automatic Argument Serialization
<VersionBadge version="2.9.0" />
FastMCP automatically serializes complex arguments to JSON strings as required by the MCP specification. This allows you to pass typed objects directly:
```python
from dataclasses import dataclass
@dataclass
class UserData:
name: str
age: int
async with client:
# Complex arguments are automatically serialized
result = await client.get_prompt("analyze_user", {
"user": UserData(name="Alice", age=30), # Automatically serialized to JSON
"preferences": {"theme": "dark"}, # Dict serialized to JSON string
"scores": [85, 92, 78], # List serialized to JSON string
"simple_name": "Bob" # Strings passed through unchanged
})
```
The client handles serialization using `pydantic_core.to_json()` for consistent formatting. FastMCP servers can automatically deserialize these JSON strings back to the expected types.
### Serialization Examples
```python
async with client:
result = await client.get_prompt("data_analysis", {
# These will be automatically serialized to JSON strings:
"config": {
"format": "csv",
"include_headers": True,
"delimiter": ","
},
"filters": [
{"field": "age", "operator": ">", "value": 18},
{"field": "status", "operator": "==", "value": "active"}
],
# This remains a string:
"report_title": "Monthly Analytics Report"
})
```
## Working with Prompt Results
The `get_prompt()` method returns a `GetPromptResult` object containing a list of messages:
```python
async with client:
result = await client.get_prompt("conversation_starter", {"topic": "climate"})
# Access individual messages
for i, message in enumerate(result.messages):
print(f"Message {i + 1}:")
print(f" Role: {message.role}")
print(f" Content: {message.content.text if hasattr(message.content, 'text') else message.content}")
```
## Raw MCP Protocol Access
For access to the complete MCP protocol objects, use the `*_mcp` methods:
```python
async with client:
# Raw MCP method returns full protocol object
prompts_result = await client.list_prompts_mcp()
# prompts_result -> mcp.types.ListPromptsResult
prompt_result = await client.get_prompt_mcp("example_prompt", {"arg": "value"})
# prompt_result -> mcp.types.GetPromptResult
```
## Multi-Server Clients
When using multi-server clients, prompts are accessible without prefixing (unlike tools):
```python
async with client: # Multi-server client
# Prompts from any server are directly accessible
result1 = await client.get_prompt("weather_prompt", {"city": "London"})
result2 = await client.get_prompt("assistant_prompt", {"query": "help"})
```
## Common Prompt Patterns
### System Messages
Many prompts generate system messages for LLM configuration:
```python
async with client:
result = await client.get_prompt("system_configuration", {
"role": "helpful assistant",
"expertise": "python programming"
})
# Typically returns messages with role="system"
system_message = result.messages[0]
print(f"System prompt: {system_message.content}")
```
### Conversation Templates
Prompts can generate multi-turn conversation templates:
```python
async with client:
result = await client.get_prompt("interview_template", {
"candidate_name": "Alice",
"position": "Senior Developer"
})
# Multiple messages for a conversation flow
for message in result.messages:
print(f"{message.role}: {message.content}")
```
<Tip>
Prompt arguments and their expected types depend on the specific prompt implementation. Check the server's documentation or use `list_prompts()` to see available arguments for each prompt.
</Tip>

View file

@ -0,0 +1,204 @@
---
title: Resource Operations
sidebarTitle: Resources
description: Access static and templated resources from MCP servers.
icon: folder-open
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="2.0.0" />
Resources are data sources exposed by MCP servers. They can be static files or dynamic templates that generate content based on parameters.
## Types of Resources
MCP servers expose two types of resources:
- **Static Resources**: Fixed content accessible via URI (e.g., configuration files, documentation)
- **Resource Templates**: Dynamic resources that accept parameters to generate content (e.g., API endpoints, database queries)
## Listing Resources
### Static Resources
Use `list_resources()` to retrieve all static resources available on the server:
```python
async with client:
resources = await client.list_resources()
# resources -> list[mcp.types.Resource]
for resource in resources:
print(f"Resource URI: {resource.uri}")
print(f"Name: {resource.name}")
print(f"Description: {resource.description}")
print(f"MIME Type: {resource.mimeType}")
# Access tags and other metadata
if hasattr(resource, '_meta') and resource._meta:
fastmcp_meta = resource._meta.get('_fastmcp', {})
print(f"Tags: {fastmcp_meta.get('tags', [])}")
```
### Resource Templates
Use `list_resource_templates()` to retrieve available resource templates:
```python
async with client:
templates = await client.list_resource_templates()
# templates -> list[mcp.types.ResourceTemplate]
for template in templates:
print(f"Template URI: {template.uriTemplate}")
print(f"Name: {template.name}")
print(f"Description: {template.description}")
# Access tags and other metadata
if hasattr(template, '_meta') and template._meta:
fastmcp_meta = template._meta.get('_fastmcp', {})
print(f"Tags: {fastmcp_meta.get('tags', [])}")
```
### Filtering by Tags
<VersionBadge version="2.11.0" />
You can use the `meta` field to filter resources based on their tags:
```python
async with client:
resources = await client.list_resources()
# Filter resources by tag
config_resources = [
resource for resource in resources
if hasattr(resource, '_meta') and resource._meta and
resource._meta.get('_fastmcp', {}) and
'config' in resource._meta.get('_fastmcp', {}).get('tags', [])
]
print(f"Found {len(config_resources)} config resources")
```
<Note>
The `_meta` field is part of the standard MCP specification. FastMCP servers include tags and other metadata within a `_fastmcp` namespace (e.g., `_meta._fastmcp.tags`) to avoid conflicts with user-defined metadata. This behavior can be controlled with the server's `include_fastmcp_meta` setting - when disabled, the `_fastmcp` namespace won't be included. Other MCP server implementations may not provide this metadata structure.
</Note>
## Reading Resources
### Static Resources
Read a static resource using its URI:
```python
async with client:
# Read a static resource
content = await client.read_resource("file:///path/to/README.md")
# content -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents]
# Access text content
if hasattr(content[0], 'text'):
print(content[0].text)
# Access binary content
if hasattr(content[0], 'blob'):
print(f"Binary data: {len(content[0].blob)} bytes")
```
### Resource Templates
Read from a resource template by providing the URI with parameters:
```python
async with client:
# Read a resource generated from a template
# For example, a template like "weather://{{city}}/current"
weather_content = await client.read_resource("weather://london/current")
# Access the generated content
print(weather_content[0].text) # Assuming text JSON response
```
## Content Types
Resources can return different content types:
### Text Resources
```python
async with client:
content = await client.read_resource("resource://config/settings.json")
for item in content:
if hasattr(item, 'text'):
print(f"Text content: {item.text}")
print(f"MIME type: {item.mimeType}")
```
### Binary Resources
```python
async with client:
content = await client.read_resource("resource://images/logo.png")
for item in content:
if hasattr(item, 'blob'):
print(f"Binary content: {len(item.blob)} bytes")
print(f"MIME type: {item.mimeType}")
# Save to file
with open("downloaded_logo.png", "wb") as f:
f.write(item.blob)
```
## Working with Multi-Server Clients
When using multi-server clients, resource URIs are automatically prefixed with the server name:
```python
async with client: # Multi-server client
# Access resources from different servers
weather_icons = await client.read_resource("weather://weather/icons/sunny")
templates = await client.read_resource("resource://assistant/templates/list")
print(f"Weather icon: {weather_icons[0].blob}")
print(f"Templates: {templates[0].text}")
```
## Raw MCP Protocol Access
For access to the complete MCP protocol objects, use the `*_mcp` methods:
```python
async with client:
# Raw MCP methods return full protocol objects
resources_result = await client.list_resources_mcp()
# resources_result -> mcp.types.ListResourcesResult
templates_result = await client.list_resource_templates_mcp()
# templates_result -> mcp.types.ListResourceTemplatesResult
content_result = await client.read_resource_mcp("resource://example")
# content_result -> mcp.types.ReadResourceResult
```
## Common Resource URI Patterns
Different MCP servers may use various URI schemes:
```python
# File system resources
"file:///path/to/file.txt"
# Custom protocol resources
"weather://london/current"
"database://users/123"
# Generic resource protocol
"resource://config/settings"
"resource://templates/email"
```
<Tip>
Resource URIs and their formats depend on the specific MCP server implementation. Check the server's documentation for available resources and their URI patterns.
</Tip>

42
docs/v2/clients/roots.mdx Normal file
View file

@ -0,0 +1,42 @@
---
title: Client Roots
sidebarTitle: Roots
description: Provide local context and resource boundaries to MCP servers.
icon: folder-tree
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="2.0.0" />
Roots are a way for clients to inform servers about the resources they have access to. Servers can use this information to adjust behavior or provide more relevant responses.
## Setting Static Roots
Provide a list of roots when creating the client:
<CodeGroup>
```python Static Roots
from fastmcp import Client
client = Client(
"my_mcp_server.py",
roots=["/path/to/root1", "/path/to/root2"]
)
```
```python Dynamic Roots Callback
from fastmcp import Client
from fastmcp.client.roots import RequestContext
async def roots_callback(context: RequestContext) -> list[str]:
print(f"Server requested roots (Request ID: {context.request_id})")
return ["/path/to/root1", "/path/to/root2"]
client = Client(
"my_mcp_server.py",
roots=roots_callback
)
```
</CodeGroup>

View file

@ -0,0 +1,258 @@
---
title: LLM Sampling
sidebarTitle: Sampling
description: Handle server-initiated LLM sampling requests.
icon: robot
---
import { VersionBadge } from "/snippets/version-badge.mdx";
<VersionBadge version="2.0.0" />
MCP servers can request LLM completions from clients. The client handles these requests through a sampling handler callback.
## Sampling Handler
Provide a `sampling_handler` function when creating the client:
```python
from fastmcp import Client
from fastmcp.client.sampling import (
SamplingMessage,
SamplingParams,
RequestContext,
)
async def sampling_handler(
messages: list[SamplingMessage],
params: SamplingParams,
context: RequestContext
) -> str:
# Your LLM integration logic here
# Extract text from messages and generate a response
return "Generated response based on the messages"
client = Client(
"my_mcp_server.py",
sampling_handler=sampling_handler,
)
```
### Handler Parameters
The sampling handler receives three parameters:
<Card icon="code" title="Sampling Handler Parameters">
<ResponseField name="SamplingMessage" type="Sampling Message Object">
<Expandable title="attributes">
<ResponseField name="role" type='Literal["user", "assistant"]'>
The role of the message.
</ResponseField>
<ResponseField name="content" type="TextContent | ImageContent | AudioContent">
The content of the message.
TextContent is most common, and has a `.text` attribute.
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="SamplingParams" type="Sampling Parameters Object">
<Expandable title="attributes">
<ResponseField name="messages" type="list[SamplingMessage]">
The messages to sample from
</ResponseField>
<ResponseField name="modelPreferences" type="ModelPreferences | None">
The server's preferences for which model to select. The client MAY ignore
these preferences.
<Expandable title="attributes">
<ResponseField name="hints" type="list[ModelHint] | None">
The hints to use for model selection.
</ResponseField>
<ResponseField name="costPriority" type="float | None">
The cost priority for model selection.
</ResponseField>
<ResponseField name="speedPriority" type="float | None">
The speed priority for model selection.
</ResponseField>
<ResponseField name="intelligencePriority" type="float | None">
The intelligence priority for model selection.
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="systemPrompt" type="str | None">
An optional system prompt the server wants to use for sampling.
</ResponseField>
<ResponseField name="includeContext" type="IncludeContext | None">
A request to include context from one or more MCP servers (including the caller), to
be attached to the prompt.
</ResponseField>
<ResponseField name="temperature" type="float | None">
The sampling temperature.
</ResponseField>
<ResponseField name="maxTokens" type="int">
The maximum number of tokens to sample.
</ResponseField>
<ResponseField name="stopSequences" type="list[str] | None">
The stop sequences to use for sampling.
</ResponseField>
<ResponseField name="metadata" type="dict[str, Any] | None">
Optional metadata to pass through to the LLM provider.
</ResponseField>
<ResponseField name="tools" type="list[Tool] | None">
Optional list of tools the LLM can use during sampling. See [Using the OpenAI Handler](#using-the-openai-handler).
</ResponseField>
<ResponseField name="toolChoice" type="ToolChoice | None">
Optional control over tool usage behavior (`auto`, `required`, or `none`).
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="RequestContext" type="Request Context Object">
<Expandable title="attributes">
<ResponseField name="request_id" type="RequestId">
Unique identifier for the MCP request
</ResponseField>
</Expandable>
</ResponseField>
</Card>
## Basic Example
```python
from fastmcp import Client
from fastmcp.client.sampling import SamplingMessage, SamplingParams, RequestContext
async def basic_sampling_handler(
messages: list[SamplingMessage],
params: SamplingParams,
context: RequestContext
) -> str:
# Extract message content
conversation = []
for message in messages:
content = message.content.text if hasattr(message.content, 'text') else str(message.content)
conversation.append(f"{message.role}: {content}")
# Use the system prompt if provided
system_prompt = params.systemPrompt or "You are a helpful assistant."
# Here you would integrate with your preferred LLM service
# This is just a placeholder response
return f"Response based on conversation: {' | '.join(conversation)}"
client = Client(
"my_mcp_server.py",
sampling_handler=basic_sampling_handler
)
```
<Note>
If the client doesn't provide a sampling handler, servers can optionally configure a fallback handler. See [Server Sampling](/servers/sampling#sampling-fallback-handler) for details.
</Note>
## Sampling Capabilities
When you provide a `sampling_handler`, FastMCP automatically advertises full sampling capabilities to the server, including tool support. To disable tool support (for simpler handlers that don't support tools), pass `sampling_capabilities` explicitly:
```python
from mcp.types import SamplingCapability
client = Client(
"my_mcp_server.py",
sampling_handler=basic_handler,
sampling_capabilities=SamplingCapability(), # No tool support
)
```
## Built-in Handlers
FastMCP provides built-in sampling handlers for OpenAI and Anthropic APIs. These handlers support the full sampling API including tool use, handling message conversion and response formatting automatically.
### OpenAI Handler
<VersionBadge version="2.11.0" />
The OpenAI handler works with OpenAI's API and any OpenAI-compatible provider:
```python
from fastmcp import Client
from fastmcp.client.sampling.handlers.openai import OpenAISamplingHandler
client = Client(
"my_mcp_server.py",
sampling_handler=OpenAISamplingHandler(default_model="gpt-4o"),
)
```
For OpenAI-compatible APIs (like local models), pass a custom client:
```python
from openai import AsyncOpenAI
client = Client(
"my_mcp_server.py",
sampling_handler=OpenAISamplingHandler(
default_model="llama-3.1-70b",
client=AsyncOpenAI(base_url="http://localhost:8000/v1"),
),
)
```
<Note>
Install the OpenAI handler with `pip install fastmcp[openai]`.
</Note>
### Anthropic Handler
<VersionBadge version="2.14.1" />
The Anthropic handler uses Claude models via the Anthropic API:
```python
from fastmcp import Client
from fastmcp.client.sampling.handlers.anthropic import AnthropicSamplingHandler
client = Client(
"my_mcp_server.py",
sampling_handler=AnthropicSamplingHandler(default_model="claude-sonnet-4-5"),
)
```
You can pass a custom client for advanced configuration:
```python
from anthropic import AsyncAnthropic
client = Client(
"my_mcp_server.py",
sampling_handler=AnthropicSamplingHandler(
default_model="claude-sonnet-4-5",
client=AsyncAnthropic(), # Uses ANTHROPIC_API_KEY env var
),
)
```
<Note>
Install the Anthropic handler with `pip install fastmcp[anthropic]`.
</Note>
### Tool Execution
Tool execution happens on the server side. The client's role is to pass tools to the LLM and return the LLM's response (which may include tool use requests). The server then executes the tools and may send follow-up sampling requests with tool results.
<Tip>
To implement a custom sampling handler, see the [handler source code](https://github.com/jlowin/fastmcp/tree/main/src/fastmcp/client/sampling/handlers) as a reference.
</Tip>

138
docs/v2/clients/tasks.mdx Normal file
View file

@ -0,0 +1,138 @@
---
title: Background Tasks
sidebarTitle: Background Tasks
description: Execute operations asynchronously and track their progress
icon: clock
tag: "NEW"
---
import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.14.0" />
The [MCP task protocol](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks) lets you request operations to run asynchronously. This returns a Task object immediately, letting you track progress, cancel operations, or await results.
See [Server Background Tasks](/servers/tasks) for how to enable this on the server side.
## Requesting Background Execution
Pass `task=True` to run an operation as a background task. The call returns immediately with a Task object while the work executes on the server.
```python
from fastmcp import Client
async with Client(server) as client:
# Start a background task
task = await client.call_tool("slow_computation", {"duration": 10}, task=True)
print(f"Task started: {task.task_id}")
# Do other work while it runs...
# Get the result when ready
result = await task.result()
```
This works with tools, resources, and prompts:
```python
tool_task = await client.call_tool("my_tool", args, task=True)
resource_task = await client.read_resource("file://large.txt", task=True)
prompt_task = await client.get_prompt("my_prompt", args, task=True)
```
## Working with Task Objects
All task types share a common interface for retrieving results, checking status, and receiving updates.
To get the result, call `await task.result()` or simply `await task`. This blocks until the task completes and returns the result. You can also check status without blocking using `await task.status()`, which returns the current state (`"working"`, `"completed"`, `"failed"`, or `"cancelled"`) along with any progress message from the server.
```python
task = await client.call_tool("analyze", {"text": "hello"}, task=True)
# Check current status (non-blocking)
status = await task.status()
print(f"{status.status}: {status.statusMessage}")
# Wait for result (blocking)
result = await task.result()
```
For more control over waiting, use `task.wait()` with an optional timeout or target state:
```python
# Wait up to 30 seconds for completion
status = await task.wait(timeout=30.0)
# Wait for a specific state
status = await task.wait(state="completed", timeout=30.0)
```
To cancel a running task, call `await task.cancel()`.
### Real-Time Status Updates
Register callbacks to receive status updates as the server reports progress. Both sync and async callbacks are supported.
```python
def on_status_change(status):
print(f"Task {status.taskId}: {status.status} - {status.statusMessage}")
task.on_status_change(on_status_change)
# Async callbacks work too
async def on_status_async(status):
await log_status(status)
task.on_status_change(on_status_async)
```
## Graceful Degradation
You can always pass `task=True` regardless of whether the server supports background tasks. Per the MCP specification, servers without task support execute the operation immediately and return the result inline. The Task API provides a consistent interface either way.
```python
task = await client.call_tool("my_tool", args, task=True)
if task.returned_immediately:
print("Server executed immediately (no background support)")
else:
print("Running in background")
# Either way, this works
result = await task.result()
```
This means you can write task-aware client code without worrying about server capabilities.
## Complete Example
```python
import asyncio
from fastmcp import Client
async def main():
async with Client(server) as client:
# Start background task
task = await client.call_tool(
"slow_computation",
{"duration": 10},
task=True,
)
# Subscribe to updates
def on_update(status):
print(f"Progress: {status.statusMessage}")
task.on_status_change(on_update)
# Do other work while task runs
print("Doing other work...")
await asyncio.sleep(2)
# Wait for completion and get result
result = await task.result()
print(f"Result: {result.content}")
asyncio.run(main())
```

295
docs/v2/clients/tools.mdx Normal file
View file

@ -0,0 +1,295 @@
---
title: Tool Operations
sidebarTitle: Tools
description: Discover and execute server-side tools with the FastMCP client.
icon: wrench
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="2.0.0" />
Tools are executable functions exposed by MCP servers. The FastMCP client provides methods to discover available tools and execute them with arguments.
## Discovering Tools
Use `list_tools()` to retrieve all tools available on the server:
```python
async with client:
tools = await client.list_tools()
# tools -> list[mcp.types.Tool]
for tool in tools:
print(f"Tool: {tool.name}")
print(f"Description: {tool.description}")
if tool.inputSchema:
print(f"Parameters: {tool.inputSchema}")
# Access tags and other metadata
if hasattr(tool, 'meta') and tool.meta:
fastmcp_meta = tool.meta.get('_fastmcp', {})
print(f"Tags: {fastmcp_meta.get('tags', [])}")
```
### Filtering by Tags
<VersionBadge version="2.11.0" />
You can use the `meta` field to filter tools based on their tags:
```python
async with client:
tools = await client.list_tools()
# Filter tools by tag
analysis_tools = [
tool for tool in tools
if hasattr(tool, 'meta') and tool.meta and
tool.meta.get('_fastmcp', {}) and
'analysis' in tool.meta.get('_fastmcp', {}).get('tags', [])
]
print(f"Found {len(analysis_tools)} analysis tools")
```
<Note>
The `meta` field is part of the standard MCP specification. FastMCP servers include tags and other metadata within a `_fastmcp` namespace (e.g., `meta._fastmcp.tags`) to avoid conflicts with user-defined metadata. This behavior can be controlled with the server's `include_fastmcp_meta` setting - when disabled, the `_fastmcp` namespace won't be included. Other MCP server implementations may not provide this metadata structure.
</Note>
## Executing Tools
### Basic Execution
Execute a tool using `call_tool()` with the tool name and arguments:
```python
async with client:
# Simple tool call
result = await client.call_tool("add", {"a": 5, "b": 3})
# result -> CallToolResult with structured and unstructured data
# Access structured data (automatically deserialized)
print(result.data) # 8 (int) or {"result": 8} for primitive types
# Access traditional content blocks
print(result.content[0].text) # "8" (TextContent)
```
### Advanced Execution Options
The `call_tool()` method supports additional parameters for timeout control and progress monitoring:
```python
async with client:
# With timeout (aborts if execution takes longer than 2 seconds)
result = await client.call_tool(
"long_running_task",
{"param": "value"},
timeout=2.0
)
# With progress handler (to track execution progress)
result = await client.call_tool(
"long_running_task",
{"param": "value"},
progress_handler=my_progress_handler
)
```
**Parameters:**
- `name`: The tool name (string)
- `arguments`: Dictionary of arguments to pass to the tool (optional)
- `timeout`: Maximum execution time in seconds (optional, overrides client-level timeout)
- `progress_handler`: Progress callback function (optional, overrides client-level handler)
- `meta`: Dictionary of metadata to send with the request (optional, see below)
## Sending Metadata
<VersionBadge version="2.13.1" />
The `meta` parameter sends ancillary information alongside tool calls. This can be used for various purposes like observability, debugging, client identification, or any context the server may need beyond the tool's primary arguments.
```python
async with client:
result = await client.call_tool(
name="send_email",
arguments={
"to": "user@example.com",
"subject": "Hello",
"body": "Welcome!"
},
meta={
"trace_id": "abc-123",
"request_source": "mobile_app"
}
)
```
The structure and usage of `meta` is determined by your application. See [Client Metadata](/servers/context#client-metadata) in the server documentation to learn how to access this data in your tool implementations.
## Handling Results
<VersionBadge version="2.10.0" />
Tool execution returns a `CallToolResult` object with both structured and traditional content. FastMCP's standout feature is the `.data` property, which doesn't just provide raw JSON but actually hydrates complete Python objects including complex types like datetimes, UUIDs, and custom classes.
### CallToolResult Properties
<Card icon="code" title="CallToolResult Properties">
<ResponseField name=".data" type="Any">
**FastMCP exclusive**: Fully hydrated Python objects with complex type support (datetimes, UUIDs, custom classes). Goes beyond JSON to provide complete object reconstruction from output schemas.
</ResponseField>
<ResponseField name=".content" type="list[mcp.types.ContentBlock]">
Standard MCP content blocks (`TextContent`, `ImageContent`, `AudioContent`, etc.) available from all MCP servers.
</ResponseField>
<ResponseField name=".structured_content" type="dict[str, Any] | None">
Standard MCP structured JSON data as sent by the server, available from all MCP servers that support structured outputs.
</ResponseField>
<ResponseField name=".is_error" type="bool">
Boolean indicating if the tool execution failed.
</ResponseField>
</Card>
### Structured Data Access
FastMCP's `.data` property provides fully hydrated Python objects, not just JSON dictionaries. This includes complex type reconstruction:
```python
from datetime import datetime
from uuid import UUID
async with client:
result = await client.call_tool("get_weather", {"city": "London"})
# FastMCP reconstructs complete Python objects from the server's output schema
weather = result.data # Server-defined WeatherReport object
print(f"Temperature: {weather.temperature}°C at {weather.timestamp}")
print(f"Station: {weather.station_id}")
print(f"Humidity: {weather.humidity}%")
# The timestamp is a real datetime object, not a string!
assert isinstance(weather.timestamp, datetime)
assert isinstance(weather.station_id, UUID)
# Compare with raw structured JSON (standard MCP)
print(f"Raw JSON: {result.structured_content}")
# {"temperature": 20, "timestamp": "2024-01-15T14:30:00Z", "station_id": "123e4567-..."}
# Traditional content blocks (standard MCP)
print(f"Text content: {result.content[0].text}")
```
### Fallback Behavior
For tools without output schemas or when deserialization fails, `.data` will be `None`:
```python
async with client:
result = await client.call_tool("legacy_tool", {"param": "value"})
if result.data is not None:
# Structured output available and successfully deserialized
print(f"Structured: {result.data}")
else:
# No structured output or deserialization failed - use content blocks
for content in result.content:
if hasattr(content, 'text'):
print(f"Text result: {content.text}")
elif hasattr(content, 'data'):
print(f"Binary data: {len(content.data)} bytes")
```
### Primitive Type Unwrapping
<Tip>
FastMCP servers automatically wrap non-object results (like `int`, `str`, `bool`) in a `{"result": value}` structure to create valid structured outputs. FastMCP clients understand this convention and automatically unwrap the value in `.data` for convenience, so you get the original primitive value instead of a wrapper object.
</Tip>
```python
async with client:
result = await client.call_tool("calculate_sum", {"a": 5, "b": 3})
# FastMCP client automatically unwraps for convenience
print(result.data) # 8 (int) - the original value
# Raw structured content shows the server-side wrapping
print(result.structured_content) # {"result": 8}
# Other MCP clients would need to manually access ["result"]
# value = result.structured_content["result"] # Not needed with FastMCP!
```
## Error Handling
### Exception-Based Error Handling
By default, `call_tool()` raises a `ToolError` if the tool execution fails:
```python
from fastmcp.exceptions import ToolError
async with client:
try:
result = await client.call_tool("potentially_failing_tool", {"param": "value"})
print("Tool succeeded:", result.data)
except ToolError as e:
print(f"Tool failed: {e}")
```
### Manual Error Checking
You can disable automatic error raising and manually check the result:
```python
async with client:
result = await client.call_tool(
"potentially_failing_tool",
{"param": "value"},
raise_on_error=False
)
if result.is_error:
print(f"Tool failed: {result.content[0].text}")
else:
print(f"Tool succeeded: {result.data}")
```
### Raw MCP Protocol Access
For complete control, use `call_tool_mcp()` which returns the raw MCP protocol object:
```python
async with client:
result = await client.call_tool_mcp("potentially_failing_tool", {"param": "value"})
# result -> mcp.types.CallToolResult
if result.isError:
print(f"Tool failed: {result.content}")
else:
print(f"Tool succeeded: {result.content}")
# Note: No automatic deserialization with call_tool_mcp()
```
## Argument Handling
Arguments are passed as a dictionary to the tool:
```python
async with client:
# Simple arguments
result = await client.call_tool("greet", {"name": "World"})
# Complex arguments
result = await client.call_tool("process_data", {
"config": {"format": "json", "validate": True},
"items": [1, 2, 3, 4, 5],
"metadata": {"source": "api", "version": "1.0"}
})
```
<Tip>
For multi-server clients, tool names are automatically prefixed with the server name (e.g., `weather_get_forecast` for a tool named `get_forecast` on the `weather` server).
</Tip>

View file

@ -0,0 +1,383 @@
---
title: Client Transports
sidebarTitle: Transports
description: Configure how FastMCP Clients connect to and communicate with servers.
icon: link
---
import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.0.0" />
The FastMCP `Client` communicates with MCP servers through transport objects that handle the underlying connection mechanics. While the client can automatically select a transport based on what you pass to it, instantiating transports explicitly gives you full control over configuration—environment variables, authentication, session management, and more.
Think of transports as configurable adapters between your client code and MCP servers. Each transport type handles a different communication pattern: subprocesses with pipes, HTTP connections, or direct in-memory calls.
## Choosing the Right Transport
- **Use [STDIO Transport](#stdio-transport)** when you need to run local MCP servers with full control over their environment and lifecycle
- **Use [Remote Transports](#remote-transports)** when connecting to production services or shared MCP servers running independently
- **Use [In-Memory Transport](#in-memory-transport)** for testing FastMCP servers without subprocess or network overhead
- **Use [MCP JSON Configuration](#mcp-json-configuration-transport)** when you need to connect to multiple servers defined in configuration files
## STDIO Transport
STDIO (Standard Input/Output) transport communicates with MCP servers through subprocess pipes. This is the standard mechanism used by desktop clients like Claude Desktop and is the primary way to run local MCP servers.
### The Client Runs the Server
<Warning>
**Critical Concept**: When using STDIO transport, your client actually launches and manages the server process. This is fundamentally different from network transports where you connect to an already-running server. Understanding this relationship is key to using STDIO effectively.
</Warning>
With STDIO transport, your client:
- Starts the server as a subprocess when you connect
- Manages the server's lifecycle (start, stop, restart)
- Controls the server's environment and configuration
- Communicates through stdin/stdout pipes
This architecture enables powerful local integrations but requires understanding environment isolation and process management.
### Environment Isolation
STDIO servers run in isolated environments by default. This is a security feature enforced by the MCP protocol to prevent accidental exposure of sensitive data.
When your client launches an MCP server:
- The server does NOT inherit your shell's environment variables
- API keys, paths, and other configuration must be explicitly passed
- The working directory and system paths may differ from your shell
To pass environment variables to your server, use the `env` parameter:
```python
from fastmcp import Client
# If your server needs environment variables (like API keys),
# you must explicitly pass them:
client = Client(
"my_server.py",
env={"API_KEY": "secret", "DEBUG": "true"}
)
# This won't work - the server runs in isolation:
# export API_KEY="secret" # in your shell
# client = Client("my_server.py") # server can't see API_KEY
```
### Basic Usage
To use STDIO transport, you create a transport instance with the command and arguments needed to run your server:
```python
from fastmcp.client.transports import StdioTransport
transport = StdioTransport(
command="python",
args=["my_server.py"]
)
client = Client(transport)
```
You can configure additional settings like environment variables, working directory, or command arguments:
```python
transport = StdioTransport(
command="python",
args=["my_server.py", "--verbose"],
env={"LOG_LEVEL": "DEBUG"},
cwd="/path/to/server"
)
client = Client(transport)
```
For convenience, the client can also infer STDIO transport from file paths, but this doesn't allow configuration:
```python
from fastmcp import Client
client = Client("my_server.py") # Limited - no configuration options
```
### Environment Variables
Since STDIO servers don't inherit your environment, you need strategies for passing configuration. Here are two common approaches:
**Selective forwarding** passes only the variables your server actually needs:
```python
import os
from fastmcp.client.transports import StdioTransport
required_vars = ["API_KEY", "DATABASE_URL", "REDIS_HOST"]
env = {
var: os.environ[var]
for var in required_vars
if var in os.environ
}
transport = StdioTransport(
command="python",
args=["server.py"],
env=env
)
client = Client(transport)
```
**Loading from .env files** keeps configuration separate from code:
```python
from dotenv import dotenv_values
from fastmcp.client.transports import StdioTransport
env = dotenv_values(".env")
transport = StdioTransport(
command="python",
args=["server.py"],
env=env
)
client = Client(transport)
```
### Session Persistence
STDIO transports maintain sessions across multiple client contexts by default (`keep_alive=True`). This improves performance by reusing the same subprocess for multiple connections, but can be controlled when you need isolation.
By default, the subprocess persists between connections:
```python
from fastmcp.client.transports import StdioTransport
transport = StdioTransport(
command="python",
args=["server.py"]
)
client = Client(transport)
async def efficient_multiple_operations():
async with client:
await client.ping()
async with client: # Reuses the same subprocess
await client.call_tool("process_data", {"file": "data.csv"})
```
For complete isolation between connections, disable session persistence:
```python
transport = StdioTransport(
command="python",
args=["server.py"],
keep_alive=False
)
client = Client(transport)
```
Use `keep_alive=False` when you need complete isolation (e.g., in test suites) or when server state could cause issues between connections.
### Specialized STDIO Transports
FastMCP provides convenience transports that are thin wrappers around `StdioTransport` with pre-configured commands:
- **`PythonStdioTransport`** - Uses `python` command for `.py` files
- **`NodeStdioTransport`** - Uses `node` command for `.js` files
- **`UvStdioTransport`** - Uses `uv` for Python packages (uses `env_vars` parameter)
- **`UvxStdioTransport`** - Uses `uvx` for Python packages (uses `env_vars` parameter)
- **`NpxStdioTransport`** - Uses `npx` for Node packages (uses `env_vars` parameter)
For most use cases, instantiate `StdioTransport` directly with your desired command. These specialized transports are primarily useful for client inference shortcuts.
## Remote Transports
Remote transports connect to MCP servers running as web services. This is a fundamentally different model from STDIO transports—instead of your client launching and managing a server process, you connect to an already-running service that manages its own environment and lifecycle.
### Streamable HTTP Transport
<VersionBadge version="2.3.0" />
Streamable HTTP is the recommended transport for production deployments, providing efficient bidirectional streaming over HTTP connections.
- **Class:** `StreamableHttpTransport`
- **Server compatibility:** FastMCP servers running with `mcp run --transport http`
The transport requires a URL and optionally supports custom headers for authentication and configuration:
```python
from fastmcp.client.transports import StreamableHttpTransport
# Basic connection
transport = StreamableHttpTransport(url="https://api.example.com/mcp")
client = Client(transport)
# With custom headers for authentication
transport = StreamableHttpTransport(
url="https://api.example.com/mcp",
headers={
"Authorization": "Bearer your-token-here",
"X-Custom-Header": "value"
}
)
client = Client(transport)
```
For convenience, FastMCP also provides authentication helpers:
```python
from fastmcp.client.auth import BearerAuth
client = Client(
"https://api.example.com/mcp",
auth=BearerAuth("your-token-here")
)
```
### SSE Transport (Legacy)
Server-Sent Events transport is maintained for backward compatibility but is superseded by Streamable HTTP for new deployments.
- **Class:** `SSETransport`
- **Server compatibility:** FastMCP servers running with `mcp run --transport sse`
SSE transport supports the same configuration options as Streamable HTTP:
```python
from fastmcp.client.transports import SSETransport
transport = SSETransport(
url="https://api.example.com/sse",
headers={"Authorization": "Bearer token"}
)
client = Client(transport)
```
Use Streamable HTTP for new deployments unless you have specific infrastructure requirements for SSE.
## In-Memory Transport
In-memory transport connects directly to a FastMCP server instance within the same Python process. This eliminates both subprocess management and network overhead, making it ideal for testing and development.
- **Class:** `FastMCPTransport`
<Note>
Unlike STDIO transports, in-memory servers have full access to your Python process's environment. They share the same memory space and environment variables as your client code—no isolation or explicit environment passing required.
</Note>
```python
from fastmcp import FastMCP, Client
import os
mcp = FastMCP("TestServer")
@mcp.tool
def greet(name: str) -> str:
prefix = os.environ.get("GREETING_PREFIX", "Hello")
return f"{prefix}, {name}!"
client = Client(mcp)
async with client:
result = await client.call_tool("greet", {"name": "World"})
```
## MCP JSON Configuration Transport
<VersionBadge version="2.4.0" />
This transport supports the emerging MCP JSON configuration standard for defining multiple servers:
- **Class:** `MCPConfigTransport`
```python
config = {
"mcpServers": {
"weather": {
"url": "https://weather.example.com/mcp",
"transport": "http"
},
"assistant": {
"command": "python",
"args": ["./assistant.py"],
"env": {"LOG_LEVEL": "INFO"}
}
}
}
client = Client(config)
async with client:
# Tools are namespaced by server
weather = await client.call_tool("weather_get_forecast", {"city": "NYC"})
answer = await client.call_tool("assistant_ask", {"question": "What?"})
```
### Tool Transformation with FastMCP and MCPConfig
FastMCP supports basic tool transformations to be defined alongside the MCP Servers in the MCPConfig file.
```python
config = {
"mcpServers": {
"weather": {
"url": "https://weather.example.com/mcp",
"transport": "http",
"tools": { } # <--- This is the tool transformation section
}
}
}
```
With these transformations, you can transform (change) the name, title, description, tags, enablement, and arguments of a tool.
For each argument the tool takes, you can transform (change) the name, description, default, visibility, whether it's required, and you can provide example values.
In the following example, we're transforming the `weather_get_forecast` tool to only retrieve the weather for `Miami` and hiding the `city` argument from the client.
```python
tool_transformations = {
"weather_get_forecast": {
"name": "miami_weather",
"description": "Get the weather for Miami",
"arguments": {
"city": {
"name": "city",
"default": "Miami",
"hide": True,
}
}
}
}
config = {
"mcpServers": {
"weather": {
"url": "https://weather.example.com/mcp",
"transport": "http",
"tools": tool_transformations
}
}
}
```
#### Allowlisting and Blocklisting Tools
Tools can be allowlisted or blocklisted from the client by applying `tags` to the tools on the server. In the following example, we're allowlisting only tools marked with the `forecast` tag, all other tools will be unavailable to the client.
```python
tool_transformations = {
"weather_get_forecast": {
"enabled": True,
"tags": ["forecast"]
}
}
config = {
"mcpServers": {
"weather": {
"url": "https://weather.example.com/mcp",
"transport": "http",
"tools": tool_transformations,
"include_tags": ["forecast"]
}
}
}
```

View file

@ -0,0 +1,65 @@
---
title: 'Community Showcase'
description: 'High-quality projects and examples from the FastMCP community'
icon: 'users'
---
import { YouTubeEmbed } from '/snippets/youtube-embed.mdx'
## Join the Community
<Card title="FastMCP Discord" icon="discord" href="https://discord.gg/uu8dJCgttd">
Connect with other FastMCP developers, share your projects, and discuss ideas.
</Card>
## Featured Projects
Discover exemplary MCP servers and implementations created by our community. These projects demonstrate best practices and innovative uses of FastMCP.
### Learning Resources
<Card title="MCP Dummy Server" icon="graduation-cap" href="https://github.com/WaiYanNyeinNaing/mcp-dummy-server">
A comprehensive educational example demonstrating FastMCP best practices with professional dual-transport server implementation, interactive test client, and detailed documentation.
</Card>
#### Video Tutorials
**Build Remote MCP Servers w/ Python & FastMCP** - Claude Integrations Tutorial by Greg + Code
<YouTubeEmbed
videoId="bOYkbXP-GGo"
title="Build Remote MCP Servers w/ Python & FastMCP"
/>
**FastMCP — the best way to build an MCP server with Python** - Tutorial by ZazenCodes
<YouTubeEmbed
videoId="rnljvmHorQw"
title="FastMCP — the best way to build an MCP server with Python"
/>
**Speedrun a MCP server for Claude Desktop (fastmcp)** - Tutorial by Nate from Prefect
<YouTubeEmbed
videoId="67ZwpkUEtSI"
title="Speedrun a MCP server for Claude Desktop (fastmcp)"
/>
### Community Examples
Have you built something interesting with FastMCP? We'd love to feature high-quality examples here! Start a [discussion on GitHub](https://github.com/jlowin/fastmcp/discussions) to share your project.
## Contributing
To get your project featured:
1. Ensure your project demonstrates best practices
2. Include comprehensive documentation
3. Add clear usage examples
4. Open a discussion in our [GitHub Discussions](https://github.com/jlowin/fastmcp/discussions)
We review submissions regularly and feature projects that provide value to the FastMCP community.
## Further Reading
- [Contrib Modules](/patterns/contrib) - Community-contributed modules that are distributed with FastMCP itself

View file

@ -0,0 +1,89 @@
---
title: FastMCP Cloud
sidebarTitle: FastMCP Cloud
description: The fastest way to deploy your MCP server
icon: cloud
tag: NEW
---
[FastMCP Cloud](https://fastmcp.cloud) is a managed platform for hosting MCP servers, built by the FastMCP team. While the FastMCP framework will always be fully open-source, we created FastMCP Cloud to solve the deployment challenges we've seen developers face. Our goal is to provide the absolute fastest way to make your MCP server available to LLM clients like Claude and Cursor.
FastMCP Cloud is a young product and we welcome your feedback. Please join our [Discord](https://discord.com/invite/aGsSC3yDF4) to share your thoughts and ideas, and you can expect to see new features and improvements every week.
<Note>
FastMCP Cloud supports both **FastMCP 2.0** servers and also **FastMCP 1.0** servers that were created with the official MCP Python SDK.
</Note>
<Tip>
FastMCP Cloud is completely free while in beta!
</Tip>
## Prerequisites
To use FastMCP Cloud, you'll need a [GitHub](https://github.com) account. In addition, you'll need a GitHub repo that contains a FastMCP server instance. If you don't want to create one yet, you can proceed to [step 1](#step-1-create-a-project) and use the FastMCP Cloud quickstart repo.
Your repo can be public or private, but must include at least a Python file that contains a FastMCP server instance.
<Tip>
To ensure your file is compatible with FastMCP Cloud, you can run `fastmcp inspect <file.py:server_object>` to see what FastMCP Cloud will see when it runs your server.
</Tip>
If you have a `requirements.txt` or `pyproject.toml` in the repo, FastMCP Cloud will automatically detect your server's dependencies and install them for you. Note that your file *can* have an `if __name__ == "__main__"` block, but it will be ignored by FastMCP Cloud.
For example, a minimal server file might look like:
```python
from fastmcp import FastMCP
mcp = FastMCP("MyServer")
@mcp.tool
def hello(name: str) -> str:
return f"Hello, {name}!"
```
## Getting Started
There are just three steps to deploying a server to FastMCP Cloud:
### Step 1: Create a Project
Visit [fastmcp.cloud](https://fastmcp.cloud) and sign in with your GitHub account. Then, create a project. Each project corresponds to a GitHub repo, and you can create one from either your own repo or using the FastMCP Cloud quickstart repo.
<img src="/assets/images/fastmcp_cloud/quickstart.png" alt="FastMCP Cloud Quickstart Screen" />
Next, you'll be prompted to configure your project.
<img src="/assets/images/fastmcp_cloud/create_project.png" alt="FastMCP Cloud Configuration Screen" />
The configuration screen lets you specify:
- **Name**: The name of your project. This will be used to generate a unique URL for your server.
- **Entrypoint**: The Python file containing your FastMCP server (e.g., `echo.py`). This field has the same syntax as the `fastmcp run` command, for example `echo.py:my_server` to specify a specific object in the file.
- **Authentication**: If disabled, your server is open to the public. If enabled, only other members of your FastMCP Cloud organization will be able to connect.
Note that FastMCP Cloud will automatically detect yours server's Python dependencies from either a `requirements.txt` or `pyproject.toml` file.
### Step 2: Deploy Your Server
Once you configure your project, FastMCP Cloud will:
1. Clone the repository
2. Build your FastMCP server
3. Deploy it to a unique URL
4. Make it immediately available for connections
<img src="/assets/images/fastmcp_cloud/deployment.png" alt="FastMCP Cloud Deployment Screen" />
FastMCP Cloud will monitor your repo and redeploy your server whenever you push a change to the `main` branch. In addition, FastMCP Cloud will build and deploy servers for every PR your open, hosting them on unique URLs, so you can test changes before updating your production server.
### Step 3: Connect to Your Server
Once your server is deployed, it will be accessible at a URL like:
```
https://your-project-name.fastmcp.app/mcp
```
You should be able to connect to it as soon as you see the deployment succeed! FastMCP Cloud provides instant connection options for popular LLM clients:
<img src="/assets/images/fastmcp_cloud/connect.png" alt="FastMCP Cloud Connection Screen" />

736
docs/v2/deployment/http.mdx Normal file
View file

@ -0,0 +1,736 @@
---
title: HTTP Deployment
sidebarTitle: HTTP Deployment
description: Deploy your FastMCP server over HTTP for remote access
icon: server
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx";
<Tip>
STDIO transport is perfect for local development and desktop applications. But to unlock the full potential of MCP—centralized services, multi-client access, and network availability—you need remote HTTP deployment.
</Tip>
This guide walks you through deploying your FastMCP server as a remote MCP service that's accessible via a URL. Once deployed, your MCP server will be available over the network, allowing multiple clients to connect simultaneously and enabling integration with cloud-based LLM applications. This guide focuses specifically on remote MCP deployment, not local STDIO servers.
## Choosing Your Approach
FastMCP provides two ways to deploy your server as an HTTP service. Understanding the trade-offs helps you choose the right approach for your needs.
The **direct HTTP server** approach is simpler and perfect for getting started quickly. You modify your server's `run()` method to use HTTP transport, and FastMCP handles all the web server configuration. This approach works well for standalone deployments where you want your MCP server to be the only service running on a port.
The **ASGI application** approach gives you more control and flexibility. Instead of running the server directly, you create an ASGI application that can be served by Uvicorn. This approach is better when you need advanced server features like multiple workers, custom middleware, or when you're integrating with existing web applications.
### Direct HTTP Server
The simplest way to get your MCP server online is to use the built-in `run()` method with HTTP transport. This approach handles all the server configuration for you and is ideal when you want a standalone MCP server without additional complexity.
```python server.py
from fastmcp import FastMCP
mcp = FastMCP("My Server")
@mcp.tool
def process_data(input: str) -> str:
"""Process data on the server"""
return f"Processed: {input}"
if __name__ == "__main__":
mcp.run(transport="http", host="0.0.0.0", port=8000)
```
Run your server with a simple Python command:
```bash
python server.py
```
Your server is now accessible at `http://localhost:8000/mcp` (or use your server's actual IP address for remote access).
This approach is ideal when you want to get online quickly with minimal configuration. It's perfect for internal tools, development environments, or simple deployments where you don't need advanced server features. The built-in server handles all the HTTP details, letting you focus on your MCP implementation.
### ASGI Application
For production deployments, you'll often want more control over how your server runs. FastMCP can create a standard ASGI application that works with any ASGI server like Uvicorn, Gunicorn, or Hypercorn. This approach is particularly useful when you need to configure advanced server options, run multiple workers, or integrate with existing infrastructure.
```python app.py
from fastmcp import FastMCP
mcp = FastMCP("My Server")
@mcp.tool
def process_data(input: str) -> str:
"""Process data on the server"""
return f"Processed: {input}"
# Create ASGI application
app = mcp.http_app()
```
Run with any ASGI server - here's an example with Uvicorn:
```bash
uvicorn app:app --host 0.0.0.0 --port 8000
```
Your server is accessible at the same URL: `http://localhost:8000/mcp` (or use your server's actual IP address for remote access).
The ASGI approach shines in production environments where you need reliability and performance. You can run multiple worker processes to handle concurrent requests, add custom middleware for logging or monitoring, integrate with existing deployment pipelines, or mount your MCP server as part of a larger application.
## Configuring Your Server
### Custom Path
By default, your MCP server is accessible at `/mcp/` on your domain. You can customize this path to fit your URL structure or avoid conflicts with existing endpoints. This is particularly useful when integrating MCP into an existing application or following specific API conventions.
```python
# Option 1: With mcp.run()
mcp.run(transport="http", host="0.0.0.0", port=8000, path="/api/mcp/")
# Option 2: With ASGI app
app = mcp.http_app(path="/api/mcp/")
```
Now your server is accessible at `http://localhost:8000/api/mcp/`.
### Authentication
<Warning>
Authentication is **highly recommended** for remote MCP servers. Some LLM clients require authentication for remote servers and will refuse to connect without it.
</Warning>
FastMCP supports multiple authentication methods to secure your remote server. See the [Authentication Overview](/servers/auth/authentication) for complete configuration options including Bearer tokens, JWT, and OAuth.
If you're mounting an authenticated server under a path prefix, see [Mounting Authenticated Servers](#mounting-authenticated-servers) below for important routing considerations.
### Health Checks
Health check endpoints are essential for monitoring your deployed server and ensuring it's responding correctly. FastMCP allows you to add custom routes alongside your MCP endpoints, making it easy to implement health checks that work with both deployment approaches.
```python
from starlette.responses import JSONResponse
@mcp.custom_route("/health", methods=["GET"])
async def health_check(request):
return JSONResponse({"status": "healthy", "service": "mcp-server"})
```
This health endpoint will be available at `http://localhost:8000/health` and can be used by load balancers, monitoring systems, or deployment platforms to verify your server is running.
### Custom Middleware
<VersionBadge version="2.3.2" />
Add custom Starlette middleware to your FastMCP ASGI apps:
```python
from fastmcp import FastMCP
from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware
# Create your FastMCP server
mcp = FastMCP("MyServer")
# Define middleware
middleware = [
Middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
]
# Create ASGI app with middleware
http_app = mcp.http_app(middleware=middleware)
```
### CORS for Browser-Based Clients
<Tip>
Most MCP clients, including those that you access through a browser like ChatGPT or Claude, don't need CORS configuration. Only enable CORS if you're working with an MCP client that connects directly from a browser, such as debugging tools or inspectors.
</Tip>
CORS (Cross-Origin Resource Sharing) is needed when JavaScript running in a web browser connects directly to your MCP server. This is different from using an LLM through a browser—in that case, the browser connects to the LLM service, and the LLM service connects to your MCP server (no CORS needed).
Browser-based MCP clients that need CORS include:
- **MCP Inspector** - Browser-based debugging tool for testing MCP servers
- **Custom browser-based MCP clients** - If you're building a web app that directly connects to MCP servers
For these scenarios, add CORS middleware with the specific headers required for MCP protocol:
```python
from fastmcp import FastMCP
from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware
mcp = FastMCP("MyServer")
# Configure CORS for browser-based clients
middleware = [
Middleware(
CORSMiddleware,
allow_origins=["*"], # Allow all origins; use specific origins for security
allow_methods=["GET", "POST", "DELETE", "OPTIONS"],
allow_headers=[
"mcp-protocol-version",
"mcp-session-id",
"Authorization",
"Content-Type",
],
expose_headers=["mcp-session-id"],
)
]
app = mcp.http_app(middleware=middleware)
```
**Key configuration details:**
- **`allow_origins`**: Specify exact origins (e.g., `["http://localhost:3000"]`) rather than `["*"]` for production deployments
- **`allow_headers`**: Must include `mcp-protocol-version`, `mcp-session-id`, and `Authorization` (for authenticated servers)
- **`expose_headers`**: Must include `mcp-session-id` so JavaScript can read the session ID from responses and send it in subsequent requests
Without `expose_headers=["mcp-session-id"]`, browsers will receive the session ID but JavaScript won't be able to access it, causing session management to fail.
<Warning>
**Production Security**: Never use `allow_origins=["*"]` in production. Specify the exact origins of your browser-based clients. Using wildcards exposes your server to unauthorized access from any website.
</Warning>
### SSE Polling for Long-Running Operations
<VersionBadge version="2.14.0" />
<Note>
This feature only applies to the **StreamableHTTP transport** (the default for `http_app()`). It does not apply to the legacy SSE transport (`transport="sse"`).
</Note>
When running tools that take a long time to complete, you may encounter issues with load balancers or proxies terminating connections that stay idle too long. [SEP-1699](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699) introduces SSE polling to solve this by allowing the server to gracefully close connections and have clients automatically reconnect.
To enable SSE polling, configure an `EventStore` when creating your HTTP application:
```python
from fastmcp import FastMCP, Context
from fastmcp.server.event_store import EventStore
mcp = FastMCP("My Server")
@mcp.tool
async def long_running_task(ctx: Context) -> str:
"""A task that takes several minutes to complete."""
for i in range(100):
await ctx.report_progress(i, 100)
# Periodically close the connection to avoid load balancer timeouts
# Client will automatically reconnect and resume receiving progress
if i % 30 == 0 and i > 0:
await ctx.close_sse_stream()
await do_expensive_work()
return "Done!"
# Configure with EventStore for resumability
event_store = EventStore()
app = mcp.http_app(
event_store=event_store,
retry_interval=2000, # Client reconnects after 2 seconds
)
```
**How it works:**
1. When `event_store` is configured, the server stores all events (progress updates, results) with unique IDs
2. Calling `ctx.close_sse_stream()` gracefully closes the HTTP connection
3. The client automatically reconnects with a `Last-Event-ID` header
4. The server replays any events the client missed during the disconnection
The `retry_interval` parameter (in milliseconds) controls how long clients wait before reconnecting. Choose a value that balances responsiveness with server load.
<Note>
`close_sse_stream()` is a no-op if called without an `EventStore` configured, so you can safely include it in tools that may run in different deployment configurations.
</Note>
#### Custom Storage Backends
By default, `EventStore` uses in-memory storage. For production deployments with multiple server instances, you can provide a custom storage backend using the `key_value` package:
```python
from fastmcp.server.event_store import EventStore
from key_value.aio.stores.redis import RedisStore
# Use Redis for distributed deployments
redis_store = RedisStore(url="redis://localhost:6379")
event_store = EventStore(
storage=redis_store,
max_events_per_stream=100, # Keep last 100 events per stream
ttl=3600, # Events expire after 1 hour
)
app = mcp.http_app(event_store=event_store)
```
## Integration with Web Frameworks
If you already have a web application running, you can add MCP capabilities by mounting a FastMCP server as a sub-application. This allows you to expose MCP tools alongside your existing API endpoints, sharing the same domain and infrastructure. The MCP server becomes just another route in your application, making it easy to manage and deploy.
### Mounting in Starlette
Mount your FastMCP server in a Starlette application:
```python
from fastmcp import FastMCP
from starlette.applications import Starlette
from starlette.routing import Mount
# Create your FastMCP server
mcp = FastMCP("MyServer")
@mcp.tool
def analyze(data: str) -> dict:
return {"result": f"Analyzed: {data}"}
# Create the ASGI app
mcp_app = mcp.http_app(path='/mcp')
# Create a Starlette app and mount the MCP server
app = Starlette(
routes=[
Mount("/mcp-server", app=mcp_app),
# Add other routes as needed
],
lifespan=mcp_app.lifespan,
)
```
The MCP endpoint will be available at `/mcp-server/mcp/` of the resulting Starlette app.
<Warning>
For Streamable HTTP transport, you **must** pass the lifespan context from the FastMCP app to the resulting Starlette app, as nested lifespans are not recognized. Otherwise, the FastMCP server's session manager will not be properly initialized.
</Warning>
#### Nested Mounts
You can create complex routing structures by nesting mounts:
```python
from fastmcp import FastMCP
from starlette.applications import Starlette
from starlette.routing import Mount
# Create your FastMCP server
mcp = FastMCP("MyServer")
# Create the ASGI app
mcp_app = mcp.http_app(path='/mcp')
# Create nested application structure
inner_app = Starlette(routes=[Mount("/inner", app=mcp_app)])
app = Starlette(
routes=[Mount("/outer", app=inner_app)],
lifespan=mcp_app.lifespan,
)
```
In this setup, the MCP server is accessible at the `/outer/inner/mcp/` path.
### FastAPI Integration
For FastAPI-specific integration patterns including both mounting MCP servers into FastAPI apps and generating MCP servers from FastAPI apps, see the [FastAPI Integration guide](/integrations/fastapi).
Here's a quick example showing how to add MCP to an existing FastAPI application:
```python
from fastapi import FastAPI
from fastmcp import FastMCP
# Your existing API
api = FastAPI()
@api.get("/api/status")
def status():
return {"status": "ok"}
# Create your MCP server
mcp = FastMCP("API Tools")
@mcp.tool
def query_database(query: str) -> dict:
"""Run a database query"""
return {"result": "data"}
# Mount MCP at /mcp
api.mount("/mcp", mcp.http_app())
# Run with: uvicorn app:api --host 0.0.0.0 --port 8000
```
Your existing API remains at `http://localhost:8000/api` while MCP is available at `http://localhost:8000/mcp`.
## Mounting Authenticated Servers
<VersionBadge version="2.13.0" />
<Tip>
This section only applies if you're **mounting an OAuth-protected FastMCP server under a path prefix** (like `/api`) inside another application using `Mount()`.
If you're deploying your FastMCP server at root level without any `Mount()` prefix, the well-known routes are automatically included in `mcp.http_app()` and you don't need to do anything special.
</Tip>
OAuth specifications (RFC 8414 and RFC 9728) require discovery metadata to be accessible at well-known paths under the root level of your domain. When you mount an OAuth-protected FastMCP server under a path prefix like `/api`, this creates a routing challenge: your operational OAuth endpoints move under the prefix, but discovery endpoints must remain at the root.
<Warning>
**Common Mistakes to Avoid:**
1. **Forgetting to mount `.well-known` routes at root** - FastMCP cannot do this automatically when your server is mounted under a path prefix. You must explicitly mount well-known routes at the root level.
2. **Including mount prefix in both base_url AND mcp_path** - The mount prefix (like `/api`) should only be in `base_url`, not in `mcp_path`. Otherwise you'll get double paths.
✅ **Correct:**
```python
base_url = "http://localhost:8000/api"
mcp_path = "/mcp"
# Result: /api/mcp
```
❌ **Wrong:**
```python
base_url = "http://localhost:8000/api"
mcp_path = "/api/mcp"
# Result: /api/api/mcp (double prefix!)
```
Follow the configuration instructions below to set up mounting correctly.
</Warning>
<Warning>
**CORS Middleware Conflicts:**
If you're integrating FastMCP into an existing application with its own CORS middleware, be aware that layering CORS middleware can cause conflicts (such as 404 errors on `.well-known` routes or OPTIONS requests).
FastMCP and the MCP SDK already handle CORS for OAuth routes. If you need CORS on your own application routes, consider using the sub-app pattern: mount FastMCP and your routes as separate apps, each with their own middleware, rather than adding application-wide CORS middleware.
</Warning>
### Route Types
OAuth-protected MCP servers expose two categories of routes:
**Operational routes** handle the OAuth flow and MCP protocol:
- `/authorize` - OAuth authorization endpoint
- `/token` - Token exchange endpoint
- `/auth/callback` - OAuth callback handler
- `/mcp` - MCP protocol endpoint
**Discovery routes** provide metadata for OAuth clients:
- `/.well-known/oauth-authorization-server` - Authorization server metadata
- `/.well-known/oauth-protected-resource/*` - Protected resource metadata
When you mount your MCP app under a prefix, operational routes move with it, but discovery routes must stay at root level for RFC compliance.
### Configuration Parameters
Three parameters control where routes are located and how they combine:
**`base_url`** tells clients where to find operational endpoints. This includes any Starlette `Mount()` path prefix (e.g., `/api`):
```python
base_url="http://localhost:8000/api" # Includes mount prefix
```
**`mcp_path`** is the internal FastMCP endpoint path, which gets appended to `base_url`:
```python
mcp_path="/mcp" # Internal MCP path, NOT the mount prefix
```
**`issuer_url`** (optional) controls the authorization server identity for OAuth discovery. Defaults to `base_url`.
```python
# Usually not needed - just set base_url and it works
issuer_url="http://localhost:8000" # Only if you want root-level discovery
```
When `issuer_url` has a path (either explicitly or by defaulting from `base_url`), FastMCP creates path-aware discovery routes per RFC 8414. For example, if `base_url` is `http://localhost:8000/api`, the authorization server metadata will be at `/.well-known/oauth-authorization-server/api`.
**Key Invariant:** `base_url + mcp_path = actual externally-accessible MCP URL`
Example:
- `base_url`: `http://localhost:8000/api` (mount prefix `/api`)
- `mcp_path`: `/mcp` (internal path)
- Result: `http://localhost:8000/api/mcp` (final MCP endpoint)
Note that the mount prefix (`/api` from `Mount("/api", ...)`) goes in `base_url`, while `mcp_path` is just the internal MCP route. Don't include the mount prefix in both places or you'll get `/api/api/mcp`.
### Mounting Strategy
When mounting an OAuth-protected server under a path prefix, declare your URLs upfront to make the relationships clear:
```python
from fastmcp import FastMCP
from fastmcp.server.auth.providers.github import GitHubProvider
from starlette.applications import Starlette
from starlette.routing import Mount
# Define the routing structure
ROOT_URL = "http://localhost:8000"
MOUNT_PREFIX = "/api"
MCP_PATH = "/mcp"
```
Create the auth provider with `base_url`:
```python
auth = GitHubProvider(
client_id="your-client-id",
client_secret="your-client-secret",
base_url=f"{ROOT_URL}{MOUNT_PREFIX}", # Operational endpoints under prefix
# issuer_url defaults to base_url - path-aware discovery works automatically
)
```
Create the MCP app, which generates operational routes at the specified path:
```python
mcp = FastMCP("Protected Server", auth=auth)
mcp_app = mcp.http_app(path=MCP_PATH)
```
Retrieve the discovery routes from the auth provider. The `mcp_path` argument should match the path used when creating the MCP app:
```python
well_known_routes = auth.get_well_known_routes(mcp_path=MCP_PATH)
```
Finally, mount everything in the Starlette app with discovery routes at root and the MCP app under the prefix:
```python
app = Starlette(
routes=[
*well_known_routes, # Discovery routes at root level
Mount(MOUNT_PREFIX, app=mcp_app), # Operational routes under prefix
],
lifespan=mcp_app.lifespan,
)
```
This configuration produces the following URL structure:
- MCP endpoint: `http://localhost:8000/api/mcp`
- OAuth authorization: `http://localhost:8000/api/authorize`
- OAuth callback: `http://localhost:8000/api/auth/callback`
- Authorization server metadata: `http://localhost:8000/.well-known/oauth-authorization-server/api`
- Protected resource metadata: `http://localhost:8000/.well-known/oauth-protected-resource/api/mcp`
Both discovery endpoints use path-aware URLs per RFC 8414 and RFC 9728, matching the `base_url` path.
### Complete Example
Here's a complete working example showing all the pieces together:
```python
from fastmcp import FastMCP
from fastmcp.server.auth.providers.github import GitHubProvider
from starlette.applications import Starlette
from starlette.routing import Mount
import uvicorn
# Define routing structure
ROOT_URL = "http://localhost:8000"
MOUNT_PREFIX = "/api"
MCP_PATH = "/mcp"
# Create OAuth provider
auth = GitHubProvider(
client_id="your-client-id",
client_secret="your-client-secret",
base_url=f"{ROOT_URL}{MOUNT_PREFIX}",
# issuer_url defaults to base_url - path-aware discovery works automatically
)
# Create MCP server
mcp = FastMCP("Protected Server", auth=auth)
@mcp.tool
def analyze(data: str) -> dict:
return {"result": f"Analyzed: {data}"}
# Create MCP app
mcp_app = mcp.http_app(path=MCP_PATH)
# Get discovery routes for root level
well_known_routes = auth.get_well_known_routes(mcp_path=MCP_PATH)
# Assemble the application
app = Starlette(
routes=[
*well_known_routes,
Mount(MOUNT_PREFIX, app=mcp_app),
],
lifespan=mcp_app.lifespan,
)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
```
For more details on OAuth authentication, see the [Authentication guide](/servers/auth).
## Production Deployment
### Running with Uvicorn
When deploying to production, you'll want to optimize your server for performance and reliability. Uvicorn provides several options to improve your server's capabilities:
```bash
# Run with basic configuration
uvicorn app:app --host 0.0.0.0 --port 8000
# Run with multiple workers for production (requires stateless mode - see below)
uvicorn app:app --host 0.0.0.0 --port 8000 --workers 4
```
### Horizontal Scaling
<VersionBadge version="2.10.2" />
When deploying FastMCP behind a load balancer or running multiple server instances, you need to understand how the HTTP transport handles sessions and configure your server appropriately.
#### Understanding Sessions
By default, FastMCP's Streamable HTTP transport maintains server-side sessions. Sessions enable stateful MCP features like [elicitation](/servers/elicitation) and [sampling](/servers/sampling), where the server needs to maintain context across multiple requests from the same client.
This works perfectly for single-instance deployments. However, sessions are stored in memory on each server instance, which creates challenges when scaling horizontally.
#### Without Stateless Mode
When running multiple server instances behind a load balancer (Traefik, nginx, HAProxy, Kubernetes, etc.), requests from the same client may be routed to different instances:
1. Client connects to Instance A → session created on Instance A
2. Next request routes to Instance B → session doesn't exist → **request fails**
You might expect sticky sessions (session affinity) to solve this, but they don't work reliably with MCP clients.
<Warning>
**Why sticky sessions don't work:** Most MCP clients—including Cursor and Claude Code—use `fetch()` internally and don't properly forward `Set-Cookie` headers. Without cookies, load balancers can't identify which instance should handle subsequent requests. This is a limitation in how these clients implement HTTP, not something you can fix with load balancer configuration.
</Warning>
#### Enabling Stateless Mode
For horizontally scaled deployments, enable stateless HTTP mode. In stateless mode, each request creates a fresh transport context, eliminating the need for session affinity entirely.
**Option 1: Via constructor**
```python
from fastmcp import FastMCP
mcp = FastMCP("My Server", stateless_http=True)
@mcp.tool
def process(data: str) -> str:
return f"Processed: {data}"
app = mcp.http_app()
```
**Option 2: Via `run()`**
```python
if __name__ == "__main__":
mcp.run(transport="http", stateless_http=True)
```
**Option 3: Via environment variable**
```bash
FASTMCP_STATELESS_HTTP=true uvicorn app:app --host 0.0.0.0 --port 8000 --workers 4
```
### Environment Variables
Production deployments should never hardcode sensitive information like API keys or authentication tokens. Instead, use environment variables to configure your server at runtime. This keeps your code secure and makes it easy to deploy the same code to different environments with different configurations.
Here's an example using bearer token authentication (though OAuth is recommended for production):
```python
import os
from fastmcp import FastMCP
from fastmcp.server.auth import BearerTokenAuth
# Read configuration from environment
auth_token = os.environ.get("MCP_AUTH_TOKEN")
if auth_token:
auth = BearerTokenAuth(token=auth_token)
mcp = FastMCP("Production Server", auth=auth)
else:
mcp = FastMCP("Production Server")
app = mcp.http_app()
```
Deploy with your secrets safely stored in environment variables:
```bash
MCP_AUTH_TOKEN=secret uvicorn app:app --host 0.0.0.0 --port 8000
```
### OAuth Token Security
<VersionBadge version="2.13.0" />
If you're using the [OAuth Proxy](/servers/auth/oauth-proxy), FastMCP issues its own JWT tokens to clients instead of forwarding upstream provider tokens. This maintains proper OAuth 2.0 token boundaries.
**Default Behavior (Development Only):**
By default, FastMCP automatically manages cryptographic keys:
- **Mac/Windows**: Keys are generated and stored in your system keyring, surviving server restarts. Suitable **only** for development and local testing.
- **Linux**: Keys are ephemeral (random salt at startup), so tokens are invalidated on restart.
This automatic approach is convenient for development but not suitable for production deployments.
**For Production:**
Production requires explicit key management to ensure tokens survive restarts and can be shared across multiple server instances. This requires the following two things working together:
1. **Explicit JWT signing key** for signing tokens issued to clients
3. **Persistent network-accessible storage** for upstream tokens (wrapped in `FernetEncryptionWrapper` to encrypt sensitive data at rest)
**Configuration:**
Add two parameters to your auth provider:
```python {8-12}
from key_value.aio.stores.redis import RedisStore
from key_value.aio.wrappers.encryption import FernetEncryptionWrapper
from cryptography.fernet import Fernet
auth = GitHubProvider(
client_id=os.environ["GITHUB_CLIENT_ID"],
client_secret=os.environ["GITHUB_CLIENT_SECRET"],
jwt_signing_key=os.environ["JWT_SIGNING_KEY"],
client_storage=FernetEncryptionWrapper(
key_value=RedisStore(host="redis.example.com", port=6379),
fernet=Fernet(os.environ["STORAGE_ENCRYPTION_KEY"])
),
base_url="https://your-server.com" # use HTTPS
)
```
Both parameters are required for production. Without an explicit signing key, keys are signed using a key derived from the client_secret, which will cause invalidation upon rotation of the client secret. Without persistent storage, tokens are local to the server and won't be trusted across hosts. **Wrap your storage backend in `FernetEncryptionWrapper` to encrypt sensitive OAuth tokens at rest** - without encryption, tokens are stored in plaintext.
For more details on the token architecture and key management, see [OAuth Proxy Key and Storage Management](/servers/auth/oauth-proxy#key-and-storage-management).
## Testing Your Deployment
Once your server is deployed, you'll need to verify it's accessible and functioning correctly. For comprehensive testing strategies including connectivity tests, client testing, and authentication testing, see the [Testing Your Server](/development/tests) guide.
## Hosting Your Server
This guide has shown you how to create an HTTP-accessible MCP server, but you'll still need a hosting provider to make it available on the internet. Your FastMCP server can run anywhere that supports Python web applications:
- **Cloud VMs** (AWS EC2, Google Compute Engine, Azure VMs)
- **Container platforms** (Cloud Run, Container Instances, ECS)
- **Platform-as-a-Service** (Railway, Render, Vercel)
- **Edge platforms** (Cloudflare Workers)
- **Kubernetes clusters** (self-managed or managed)
The key requirements are Python 3.10+ support and the ability to expose an HTTP port. Most providers will require you to package your server (requirements.txt, Dockerfile, etc.) according to their deployment format. For managed, zero-configuration deployment, see [FastMCP Cloud](/deployment/fastmcp-cloud).

View file

@ -0,0 +1,258 @@
---
title: Running Your Server
sidebarTitle: Running Your Server
description: Learn how to run your FastMCP server locally for development and testing
icon: circle-play
---
FastMCP servers can be run in different ways depending on your needs. This guide focuses on running servers locally for development and testing. For production deployment to a URL, see the [HTTP Deployment](/deployment/http) guide.
## The `run()` Method
Every FastMCP server needs to be started to accept connections. The simplest way to run a server is by calling the `run()` method on your FastMCP instance. This method starts the server and blocks until it's stopped, handling all the connection management for you.
<Tip>
For maximum compatibility, it's best practice to place the `run()` call within an `if __name__ == "__main__":` block. This ensures the server starts only when the script is executed directly, not when imported as a module.
</Tip>
```python {9-10} my_server.py
from fastmcp import FastMCP
mcp = FastMCP(name="MyServer")
@mcp.tool
def hello(name: str) -> str:
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.run()
```
You can now run this MCP server by executing `python my_server.py`.
## Transport Protocols
MCP servers communicate with clients through different transport protocols. Think of transports as the "language" your server speaks to communicate with clients. FastMCP supports three main transport protocols, each designed for specific use cases and deployment scenarios.
The choice of transport determines how clients connect to your server, what network capabilities are available, and how many clients can connect simultaneously. Understanding these transports helps you choose the right approach for your application.
### STDIO Transport (Default)
STDIO (Standard Input/Output) is the default transport for FastMCP servers. When you call `run()` without arguments, your server uses STDIO transport. This transport communicates through standard input and output streams, making it perfect for command-line tools and desktop applications like Claude Desktop.
With STDIO transport, the client spawns a new server process for each session and manages its lifecycle. The server reads MCP messages from stdin and writes responses to stdout. This is why STDIO servers don't stay running - they're started on-demand by the client.
```python
from fastmcp import FastMCP
mcp = FastMCP("MyServer")
@mcp.tool
def hello(name: str) -> str:
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.run() # Uses STDIO transport by default
```
STDIO is ideal for:
- Local development and testing
- Claude Desktop integration
- Command-line tools
- Single-user applications
### HTTP Transport (Streamable)
HTTP transport turns your MCP server into a web service accessible via a URL. This transport uses the Streamable HTTP protocol, which allows clients to connect over the network. Unlike STDIO where each client gets its own process, an HTTP server can handle multiple clients simultaneously.
The Streamable HTTP protocol provides full bidirectional communication between client and server, supporting all MCP operations including streaming responses. This makes it the recommended choice for network-based deployments.
To use HTTP transport, specify it in the `run()` method along with networking options:
```python
from fastmcp import FastMCP
mcp = FastMCP("MyServer")
@mcp.tool
def hello(name: str) -> str:
return f"Hello, {name}!"
if __name__ == "__main__":
# Start an HTTP server on port 8000
mcp.run(transport="http", host="127.0.0.1", port=8000)
```
Your server is now accessible at `http://localhost:8000/mcp`. This URL is the MCP endpoint that clients will connect to. HTTP transport enables:
- Network accessibility
- Multiple concurrent clients
- Integration with web infrastructure
- Remote deployment capabilities
For production HTTP deployment with authentication and advanced configuration, see the [HTTP Deployment](/deployment/http) guide.
### SSE Transport (Legacy)
Server-Sent Events (SSE) transport was the original HTTP-based transport for MCP. While still supported for backward compatibility, it has limitations compared to the newer Streamable HTTP transport. SSE only supports server-to-client streaming, making it less efficient for bidirectional communication.
```python
if __name__ == "__main__":
# SSE transport - use HTTP instead for new projects
mcp.run(transport="sse", host="127.0.0.1", port=8000)
```
We recommend using HTTP transport instead of SSE for all new projects. SSE remains available only for compatibility with older clients that haven't upgraded to Streamable HTTP.
### Choosing the Right Transport
Each transport serves different needs. STDIO is perfect when you need simple, local execution - it's what Claude Desktop and most command-line tools expect. HTTP transport is essential when you need network access, want to serve multiple clients, or plan to deploy your server remotely. SSE exists only for backward compatibility and shouldn't be used in new projects.
Consider your deployment scenario: Are you building a tool for local use? STDIO is your best choice. Need a centralized service that multiple clients can access? HTTP transport is the way to go.
## The FastMCP CLI
FastMCP provides a powerful command-line interface for running servers without modifying the source code. The CLI can automatically find and run your server with different transports, manage dependencies, and handle development workflows:
```bash
fastmcp run server.py
```
The CLI automatically finds a FastMCP instance in your file (named `mcp`, `server`, or `app`) and runs it with the specified options. This is particularly useful for testing different transports or configurations without changing your code.
### Dependency Management
The CLI integrates with `uv` to manage Python environments and dependencies:
```bash
# Run with a specific Python version
fastmcp run server.py --python 3.11
# Run with additional packages
fastmcp run server.py --with pandas --with numpy
# Run with dependencies from a requirements file
fastmcp run server.py --with-requirements requirements.txt
# Combine multiple options
fastmcp run server.py --python 3.10 --with httpx --transport http
# Run within a specific project directory
fastmcp run server.py --project /path/to/project
```
<Note>
When using `--python`, `--with`, `--project`, or `--with-requirements`, the server runs via `uv run` subprocess instead of using your local environment.
</Note>
### Passing Arguments to Servers
When servers accept command line arguments (using argparse, click, or other libraries), you can pass them after `--`:
```bash
fastmcp run config_server.py -- --config config.json
fastmcp run database_server.py -- --database-path /tmp/db.sqlite --debug
```
This is useful for servers that need configuration files, database paths, API keys, or other runtime options.
For more CLI features including development mode with the MCP Inspector, see the [CLI documentation](/patterns/cli).
### Async Usage
FastMCP servers are built on async Python, but the framework provides both synchronous and asynchronous APIs to fit your application's needs. The `run()` method we've been using is actually a synchronous wrapper around the async server implementation.
For applications that are already running in an async context, FastMCP provides the `run_async()` method:
```python {10-12}
from fastmcp import FastMCP
import asyncio
mcp = FastMCP(name="MyServer")
@mcp.tool
def hello(name: str) -> str:
return f"Hello, {name}!"
async def main():
# Use run_async() in async contexts
await mcp.run_async(transport="http", port=8000)
if __name__ == "__main__":
asyncio.run(main())
```
<Warning>
The `run()` method cannot be called from inside an async function because it creates its own async event loop internally. If you attempt to call `run()` from inside an async function, you'll get an error about the event loop already running.
Always use `run_async()` inside async functions and `run()` in synchronous contexts.
</Warning>
Both `run()` and `run_async()` accept the same transport arguments, so all the examples above apply to both methods.
## Custom Routes
When using HTTP transport, you might want to add custom web endpoints alongside your MCP server. This is useful for health checks, status pages, or simple APIs. FastMCP lets you add custom routes using the `@custom_route` decorator:
```python
from fastmcp import FastMCP
from starlette.requests import Request
from starlette.responses import PlainTextResponse
mcp = FastMCP("MyServer")
@mcp.custom_route("/health", methods=["GET"])
async def health_check(request: Request) -> PlainTextResponse:
return PlainTextResponse("OK")
@mcp.tool
def process(data: str) -> str:
return f"Processed: {data}"
if __name__ == "__main__":
mcp.run(transport="http") # Health check at http://localhost:8000/health
```
Custom routes are served by the same web server as your MCP endpoint. They're available at the root of your domain while the MCP endpoint is at `/mcp/`. For more complex web applications, consider [mounting your MCP server into a FastAPI or Starlette app](/deployment/http#integration-with-web-frameworks).
## Alternative Initialization Patterns
The `if __name__ == "__main__"` pattern works well for standalone scripts, but some deployment scenarios require different approaches. FastMCP handles these cases automatically.
### CLI-Only Servers
When using the FastMCP CLI, you don't need the `if __name__` block at all. The CLI will find your FastMCP instance and run it:
```python
# server.py
from fastmcp import FastMCP
mcp = FastMCP("MyServer") # CLI looks for 'mcp', 'server', or 'app'
@mcp.tool
def process(data: str) -> str:
return f"Processed: {data}"
# No if __name__ block needed - CLI will find and run 'mcp'
```
### ASGI Applications
For ASGI deployment (running with Uvicorn or similar), you'll want to create an ASGI application object. This approach is common in production deployments where you need more control over the server configuration:
```python
# app.py
from fastmcp import FastMCP
def create_app():
mcp = FastMCP("MyServer")
@mcp.tool
def process(data: str) -> str:
return f"Processed: {data}"
return mcp.http_app()
app = create_app() # Uvicorn will use this
```
See the [HTTP Deployment](/deployment/http) guide for more ASGI deployment patterns.

View file

@ -0,0 +1,640 @@
---
title: "Project Configuration"
sidebarTitle: "Project Configuration"
description: Use fastmcp.json for portable, declarative project configuration
icon: file-code
---
import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.12.0" />
FastMCP supports declarative configuration through `fastmcp.json` files. This is the canonical and preferred way to configure FastMCP projects, providing a single source of truth for server settings, dependencies, and deployment options that replaces complex command-line arguments.
The `fastmcp.json` file is designed to be a portable description of your server configuration that can be shared across environments and teams. When running from a `fastmcp.json` file, you can override any configuration values using CLI arguments.
## Overview
The `fastmcp.json` configuration file allows you to define all aspects of your FastMCP server in a structured, shareable format. Instead of remembering command-line arguments or writing shell scripts, you declare your server's configuration once and use it everywhere.
When you have a `fastmcp.json` file, running your server becomes as simple as:
```bash
# Run the server using the configuration
fastmcp run fastmcp.json
# Or if fastmcp.json exists in the current directory
fastmcp run
```
This configuration approach ensures reproducible deployments across different environments, from local development to production servers. It works seamlessly with Claude Desktop, VS Code extensions, and any MCP-compatible client.
## File Structure
The `fastmcp.json` configuration answers three fundamental questions about your server:
- **Source** = WHERE does your server code live?
- **Environment** = WHAT environment setup does it require?
- **Deployment** = HOW should the server run?
This conceptual model helps you understand the purpose of each configuration section and organize your settings effectively. The configuration file maps directly to these three concerns:
```json
{
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
"source": {
// WHERE: Location of your server code
"type": "filesystem", // Optional, defaults to "filesystem"
"path": "server.py",
"entrypoint": "mcp"
},
"environment": {
// WHAT: Environment setup and dependencies
"type": "uv", // Optional, defaults to "uv"
"python": ">=3.10",
"dependencies": ["pandas", "numpy"]
},
"deployment": {
// HOW: Runtime configuration
"transport": "stdio",
"log_level": "INFO"
}
}
```
Only the `source` field is required. The `environment` and `deployment` sections are optional and provide additional configuration when needed.
### JSON Schema Support
FastMCP provides JSON schemas for IDE autocomplete and validation. Add the schema reference to your `fastmcp.json` for enhanced developer experience:
```json
{
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
"source": {
"path": "server.py",
"entrypoint": "mcp"
}
}
```
Two schema URLs are available:
- **Version-specific**: `https://gofastmcp.com/public/schemas/fastmcp.json/v1.json`
- **Latest version**: `https://gofastmcp.com/public/schemas/fastmcp.json/latest.json`
Modern IDEs like VS Code will automatically provide autocomplete suggestions, validation, and inline documentation when the schema is specified.
### Source Configuration
The source configuration determines **WHERE** your server code lives. It tells FastMCP how to find and load your server, whether it's a local Python file, a remote repository, or hosted in the cloud. This section is required and forms the foundation of your configuration.
<Card icon="code" title="Source">
<ParamField body="source" type="object" required>
The server source configuration that determines where your server code lives.
<ParamField body="type" type="string" default="filesystem">
The source type identifier that determines which implementation to use. Currently supports `"filesystem"` for local files. Future releases will add support for `"git"` and `"cloud"` source types.
</ParamField>
<Expandable title="FileSystemSource">
When `type` is `"filesystem"` (or omitted), the source points to a local Python file containing your FastMCP server:
<ParamField body="path" type="string" required>
Path to the Python file containing your FastMCP server.
</ParamField>
<ParamField body="entrypoint" type="string">
Name of the server instance or factory function within the module:
- Can be a FastMCP server instance (e.g., `mcp = FastMCP("MyServer")`)
- Can be a function with no arguments that returns a FastMCP server
- If not specified, FastMCP searches for common names: `mcp`, `server`, or `app`
</ParamField>
**Example:**
```json
"source": {
"type": "filesystem",
"path": "src/server.py",
"entrypoint": "mcp"
}
```
Note: File paths are resolved relative to the configuration file's location.
</Expandable>
</ParamField>
</Card>
<Note>
**Future Source Types**
Future releases will support additional source types:
- **Git repositories** (`type: "git"`) for loading server code directly from version control
- **FastMCP Cloud** (`type: "cloud"`) for hosted servers with automatic scaling and management
</Note>
### Environment Configuration
The environment configuration determines **WHAT** environment setup your server requires. It controls the build-time setup of your Python environment, ensuring your server runs with the exact Python version and dependencies it requires. This section creates isolated, reproducible environments across different systems.
FastMCP uses an extensible environment system with a base `Environment` class that can be implemented by different environment providers. Currently, FastMCP supports the `UVEnvironment` for Python environment management using `uv`'s powerful dependency resolver.
<Card icon="code" title="Environment">
<ParamField body="environment" type="object">
Optional environment configuration. When specified, FastMCP uses the appropriate environment implementation to set up your server's runtime.
<ParamField body="type" type="string" default="uv">
The environment type identifier that determines which implementation to use. Currently supports `"uv"` for Python environments managed by uv. If omitted, defaults to `"uv"`.
</ParamField>
<Expandable title="UVEnvironment">
When `type` is `"uv"` (or omitted), the environment uses uv to manage Python dependencies:
<ParamField body="python" type="string">
Python version constraint. Examples:
- Exact version: `"3.12"`
- Minimum version: `">=3.10"`
- Version range: `">=3.10,<3.13"`
</ParamField>
<ParamField body="dependencies" type="list[str]">
List of pip packages with optional version specifiers (PEP 508 format).
```json
"dependencies": ["pandas>=2.0", "requests", "httpx"]
```
</ParamField>
<ParamField body="requirements" type="string">
Path to a requirements.txt file, resolved relative to the config file location.
```json
"requirements": "requirements.txt"
```
</ParamField>
<ParamField body="project" type="string">
Path to a project directory containing pyproject.toml for uv project management.
```json
"project": "."
```
</ParamField>
<ParamField body="editable" type="list[string]">
List of paths to packages to install in editable/development mode. Useful for local development when you want changes to be reflected immediately. Supports multiple packages for monorepo setups or shared libraries.
```json
"editable": ["."]
```
Or with multiple packages:
```json
"editable": [".", "../shared-lib", "/path/to/another-package"]
```
</ParamField>
**Example:**
```json
"environment": {
"type": "uv",
"python": ">=3.10",
"dependencies": ["pandas", "numpy"],
"editable": ["."]
}
```
Note: When any UVEnvironment field is specified, FastMCP automatically creates an isolated environment using `uv` before running your server.
</Expandable>
</ParamField>
</Card>
When environment configuration is provided, FastMCP:
1. Detects the environment type (defaults to `"uv"` if not specified)
2. Creates an isolated environment using the appropriate provider
3. Installs the specified dependencies
4. Runs your server in this clean environment
This build-time setup ensures your server always has the dependencies it needs, without polluting your system Python or conflicting with other projects.
<Note>
**Future Environment Types**
Similar to source types, future releases may support additional environment types for different runtime requirements, such as Docker containers or language-specific environments beyond Python.
</Note>
### Deployment Configuration
The deployment configuration controls **HOW** your server runs. It defines the runtime behavior including network settings, environment variables, and execution context. These settings determine how your server operates when it executes, from transport protocols to logging levels.
Environment variables are included in this section because they're runtime configuration that affects how your server behaves when it executes, not how its environment is built. The deployment configuration is applied every time your server starts, controlling its operational characteristics.
<Card icon="code" title="Deployment Fields">
<ParamField body="deployment" type="object">
Optional runtime configuration for the server.
<Expandable title="Deployment Fields">
<ParamField body="transport" type="string" default="stdio">
Protocol for client communication:
- `"stdio"`: Standard input/output for desktop clients
- `"http"`: Network-accessible HTTP server
- `"sse"`: Server-sent events
</ParamField>
<ParamField body="host" type="string" default="127.0.0.1">
Network interface to bind (HTTP transport only):
- `"127.0.0.1"`: Local connections only
- `"0.0.0.0"`: All network interfaces
</ParamField>
<ParamField body="port" type="integer" default="3000">
Port number for HTTP transport.
</ParamField>
<ParamField body="path" type="string" default="/mcp/">
URL path for the MCP endpoint when using HTTP transport.
</ParamField>
<ParamField body="log_level" type="string" default="INFO">
Server logging verbosity. Options:
- `"DEBUG"`: Detailed debugging information
- `"INFO"`: General informational messages
- `"WARNING"`: Warning messages
- `"ERROR"`: Error messages only
- `"CRITICAL"`: Critical errors only
</ParamField>
<ParamField body="env" type="object">
Environment variables to set when running the server. Supports `${VAR_NAME}` syntax for runtime interpolation.
```json
"env": {
"API_KEY": "secret-key",
"DATABASE_URL": "postgres://${DB_USER}@${DB_HOST}/mydb"
}
```
</ParamField>
<ParamField body="cwd" type="string">
Working directory for the server process. Relative paths are resolved from the config file location.
</ParamField>
<ParamField body="args" type="list[str]">
Command-line arguments to pass to the server, passed after `--` to the server's argument parser.
```json
"args": ["--config", "server-config.json"]
```
</ParamField>
</Expandable>
</ParamField>
</Card>
#### Environment Variable Interpolation
The `env` field in deployment configuration supports runtime interpolation of environment variables using `${VAR_NAME}` syntax. This enables dynamic configuration based on your deployment environment:
```json
{
"deployment": {
"env": {
"API_URL": "https://api.${ENVIRONMENT}.example.com",
"DATABASE_URL": "postgres://${DB_USER}:${DB_PASS}@${DB_HOST}/myapp",
"CACHE_KEY": "myapp_${ENVIRONMENT}_${VERSION}"
}
}
}
```
When the server starts, FastMCP replaces `${ENVIRONMENT}`, `${DB_USER}`, etc. with values from your system's environment variables. If a variable doesn't exist, the placeholder is preserved as-is.
**Example**: If your system has `ENVIRONMENT=production` and `DB_HOST=db.example.com`:
```json
// Configuration
{
"deployment": {
"env": {
"API_URL": "https://api.${ENVIRONMENT}.example.com",
"DB_HOST": "${DB_HOST}"
}
}
}
// Result at runtime
{
"API_URL": "https://api.production.example.com",
"DB_HOST": "db.example.com"
}
```
This feature is particularly useful for:
- Deploying the same configuration across development, staging, and production
- Keeping sensitive values out of configuration files
- Building dynamic URLs and connection strings
- Creating environment-specific prefixes or suffixes
## Usage with CLI Commands
FastMCP automatically detects and uses a file specifically named `fastmcp.json` in the current directory, making server execution simple and consistent. Files with FastMCP configuration format but different names are not auto-detected and must be specified explicitly:
```bash
# Auto-detect fastmcp.json in current directory
cd my-project
fastmcp run # No arguments needed!
# Or specify a configuration file explicitly
fastmcp run prod.fastmcp.json
# Skip environment setup when already in a uv environment
fastmcp run fastmcp.json --skip-env
# Skip source preparation when source is already prepared
fastmcp run fastmcp.json --skip-source
# Skip both environment and source preparation
fastmcp run fastmcp.json --skip-env --skip-source
```
### Pre-building Environments
You can use `fastmcp project prepare` to create a persistent uv project with all dependencies pre-installed:
```bash
# Create a persistent environment
fastmcp project prepare fastmcp.json --output-dir ./env
# Use the pre-built environment to run the server
fastmcp run fastmcp.json --project ./env
```
This pattern separates environment setup (slow) from server execution (fast), useful for deployment scenarios.
### Using an Existing Environment
By default, FastMCP creates an isolated environment with `uv` based on your configuration. When you already have a suitable Python environment, use the `--skip-env` flag to skip environment creation:
```bash
fastmcp run fastmcp.json --skip-env
```
**When you already have an environment:**
- You're in an activated virtual environment with all dependencies installed
- You're inside a Docker container with pre-installed dependencies
- You're in a CI/CD pipeline that pre-builds the environment
- You're using a system-wide installation with all required packages
- You're in a uv-managed environment (prevents infinite recursion)
This flag tells FastMCP: "I already have everything installed, just run the server."
### Using an Existing Source
When working with source types that require preparation (future support for git repositories or cloud sources), use the `--skip-source` flag when you already have the source code available:
```bash
fastmcp run fastmcp.json --skip-source
```
**When you already have the source:**
- You've previously cloned a git repository and don't need to re-fetch
- You have a cached copy of a cloud-hosted server
- You're in a CI/CD pipeline where source checkout is a separate step
- You're iterating locally on already-downloaded code
This flag tells FastMCP: "I already have the source code, skip any download/clone steps."
Note: For filesystem sources (local Python files), this flag has no effect since they don't require preparation.
The configuration file works with all FastMCP commands:
- **`run`** - Start the server in production mode
- **`dev`** - Launch with the Inspector UI for development
- **`inspect`** - View server capabilities and configuration
- **`install`** - Install to Claude Desktop, Cursor, or other MCP clients
When no file argument is provided, FastMCP searches the current directory for `fastmcp.json`. This means you can simply navigate to your project directory and run `fastmcp run` to start your server with all its configured settings.
### CLI Override Behavior
Command-line arguments take precedence over configuration file values, allowing ad-hoc adjustments without modifying the file:
```bash
# Config specifies port 3000, CLI overrides to 8080
fastmcp run fastmcp.json --port 8080
# Config specifies stdio, CLI overrides to HTTP
fastmcp run fastmcp.json --transport http
# Add extra dependencies not in config
fastmcp run fastmcp.json --with requests --with httpx
```
This precedence order enables:
- Quick testing of different settings
- Environment-specific overrides in deployment scripts
- Debugging with increased log levels
- Temporary configuration changes
### Custom Naming Patterns
You can use different configuration files for different environments:
- `fastmcp.json` - Default configuration
- `dev.fastmcp.json` - Development settings
- `prod.fastmcp.json` - Production settings
- `test_fastmcp.json` - Test configuration
Any file with "fastmcp.json" in the name is recognized as a configuration file.
## Examples
<Tabs>
<Tab title="Basic Configuration">
A minimal configuration for a simple server:
```json
{
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
"source": {
"path": "server.py",
"entrypoint": "mcp"
}
}
```
This configuration explicitly specifies the server entrypoint (`mcp`), making it clear which server instance or factory function to use. Uses all defaults: STDIO transport, no special dependencies, standard logging.
</Tab>
<Tab title="Development Configuration">
A configuration optimized for local development:
```json
{
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
// WHERE does the server live?
"source": {
"path": "src/server.py",
"entrypoint": "app"
},
// WHAT dependencies does it need?
"environment": {
"type": "uv",
"python": "3.12",
"dependencies": ["fastmcp[dev]"],
"editable": "."
},
// HOW should it run?
"deployment": {
"transport": "http",
"host": "127.0.0.1",
"port": 8000,
"log_level": "DEBUG",
"env": {
"DEBUG": "true",
"ENV": "development"
}
}
}
```
</Tab>
<Tab title="Production Configuration">
A production-ready configuration with full dependency management:
```json
{
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
// WHERE does the server live?
"source": {
"path": "app/main.py",
"entrypoint": "mcp_server"
},
// WHAT dependencies does it need?
"environment": {
"python": "3.11",
"requirements": "requirements/production.txt",
"project": "."
},
// HOW should it run?
"deployment": {
"transport": "http",
"host": "0.0.0.0",
"port": 3000,
"path": "/api/mcp/",
"log_level": "INFO",
"env": {
"ENV": "production",
"API_BASE_URL": "https://api.example.com",
"DATABASE_URL": "postgresql://user:pass@db.example.com/prod"
},
"cwd": "/app",
"args": ["--workers", "4"]
}
}
```
</Tab>
<Tab title="Data Science Server">
Configuration for a data analysis server with scientific packages:
```json
{
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
"source": {
"path": "analysis_server.py",
"entrypoint": "mcp"
},
"environment": {
"python": "3.11",
"dependencies": [
"pandas>=2.0",
"numpy",
"scikit-learn",
"matplotlib",
"jupyterlab"
]
},
"deployment": {
"transport": "stdio",
"env": {
"MATPLOTLIB_BACKEND": "Agg",
"DATA_PATH": "./datasets"
}
}
}
```
</Tab>
<Tab title="Multi-Environment Setup">
You can maintain multiple configuration files for different environments:
**dev.fastmcp.json**:
```json
{
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
"source": {
"path": "server.py",
"entrypoint": "mcp"
},
"deployment": {
"transport": "http",
"log_level": "DEBUG"
}
}
```
**prod.fastmcp.json**:
```json
{
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
"source": {
"path": "server.py",
"entrypoint": "mcp"
},
"environment": {
"requirements": "requirements/production.txt"
},
"deployment": {
"transport": "http",
"host": "0.0.0.0",
"log_level": "WARNING"
}
}
```
Run different configurations:
```bash
fastmcp run dev.fastmcp.json # Development
fastmcp run prod.fastmcp.json # Production
```
</Tab>
</Tabs>
## Migrating from CLI Arguments
If you're currently using command-line arguments or shell scripts, migrating to `fastmcp.json` simplifies your workflow. Here's how common CLI patterns map to configuration:
**CLI Command**:
```bash
uv run --with pandas --with requests \
fastmcp run server.py \
--transport http \
--port 8000 \
--log-level INFO
```
**Equivalent fastmcp.json**:
```json
{
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
"source": {
"path": "server.py",
"entrypoint": "mcp"
},
"environment": {
"dependencies": ["pandas", "requests"]
},
"deployment": {
"transport": "http",
"port": 8000,
"log_level": "INFO"
}
}
```
Now simply run:
```bash
fastmcp run # Automatically finds and uses fastmcp.json
```
The configuration file approach provides better documentation, easier sharing, and consistent execution across different environments while maintaining the flexibility to override settings when needed.

View file

@ -0,0 +1,187 @@
---
title: "Contributing"
description: "Development workflow for FastMCP contributors"
icon: code-pull-request
---
Contributing to FastMCP means joining a community that values clean, maintainable code and thoughtful API design. All contributions are valued - from fixing typos in documentation to implementing major features.
## Issues
### Issue First, Code Second
**Every pull request requires a corresponding issue - no exceptions.** This requirement creates a collaborative space where approach, scope, and alignment are established before code is written. Issues serve as design documents where maintainers and contributors discuss implementation strategy, identify potential conflicts with existing patterns, and ensure proposed changes advance FastMCP's vision.
**FastMCP is an opinionated framework, not a kitchen sink.** The maintainers have strong beliefs about what FastMCP should and shouldn't do. Just because something takes N lines of code and you want it in fewer lines doesn't mean FastMCP should take on the maintenance burden or endorse that pattern. This is judged at the maintainers' discretion.
Use issues to understand scope BEFORE opening PRs. The issue discussion determines whether a feature belongs in core, contrib, or not at all.
### Writing Good Issues
FastMCP is an extremely highly-trafficked repository maintained by a very small team. Issues that appear to transfer burden to maintainers without any effort to validate the problem will be closed. Please help the maintainers help you by always providing a minimal reproducible example and clearly describing the problem.
**LLM-generated issues will be closed immediately.** Issues that contain paragraphs of unnecessary explanation, verbose problem descriptions, or obvious LLM authorship patterns obfuscate the actual problem and transfer burden to maintainers.
Write clear, concise issues that:
- State the problem directly
- Provide a minimal reproducible example
- Skip unnecessary background or context
- Take responsibility for clear communication
Issues may be labeled "Invalid" simply due to confusion caused by verbosity or not adhering to the guidelines outlined here.
## Pull Requests
PRs that deviate from FastMCP's core principles will be rejected regardless of implementation quality. **PRs are NOT for iterating on ideas** - they should only be opened for ideas that already have a bias toward acceptance based on issue discussion.
### Development Environment
#### Installation
To contribute to FastMCP, you'll need to set up a development environment with all necessary tools and dependencies.
```bash
# Clone the repository
git clone https://github.com/jlowin/fastmcp.git
cd fastmcp
# Install all dependencies including dev tools
uv sync
# Install prek hooks
uv run prek install
```
In addition, some development commands require [just](https://github.com/casey/just) to be installed.
Prek hooks will run automatically on every commit to catch issues before they reach CI. If you see failures, fix them before committing - never commit broken code expecting to fix it later.
### Development Standards
#### Scope
Large pull requests create review bottlenecks and quality risks. Unless you're fixing a discrete bug or making an incredibly well-scoped change, keep PRs small and focused.
A PR that changes 50 lines across 3 files can be thoroughly reviewed in minutes. A PR that changes 500 lines across 20 files requires hours of careful analysis and often hides subtle issues.
Breaking large features into smaller PRs:
- Creates better review experiences
- Makes git history clear
- Simplifies debugging with bisect
- Reduces merge conflicts
- Gets your code merged faster
#### Code Quality
FastMCP values clarity over cleverness. Every line you write will be maintained by someone else - possibly years from now, possibly without context about your decisions.
**PRs can be rejected for two opposing reasons:**
1. **Insufficient quality** - Code that doesn't meet our standards for clarity, maintainability, or idiomaticity
2. **Overengineering** - Code that is overbearing, unnecessarily complex, or tries to be too clever
The focus is on idiomatic, high-quality Python. FastMCP uses patterns like `NotSet` type as an alternative to `None` in certain situations - follow existing patterns.
#### Required Practices
**Full type annotations** on all functions and methods. They catch bugs before runtime and serve as inline documentation.
**Async/await patterns** for all I/O operations. Even if your specific use case doesn't need concurrency, consistency means users can compose features without worrying about blocking operations.
**Descriptive names** make code self-documenting. `auth_token` is clear; `tok` requires mental translation.
**Specific exception types** make error handling predictable. Catching `ValueError` tells readers exactly what error you expect. Never use bare `except` clauses.
#### Anti-Patterns to Avoid
**Complex one-liners** are hard to debug and modify. Break operations into clear steps.
**Mutable default arguments** cause subtle bugs. Use `None` as the default and create the mutable object inside the function.
**Breaking established patterns** confuses readers. If you must deviate, discuss in the issue first.
### Prek Checks
```bash
# Runs automatically on commit, or manually:
uv run prek run --all-files
```
This runs three critical tools:
- **Ruff**: Linting and formatting
- **Prettier**: Code formatting
- **ty**: Static type checking
Pytest runs separately as a distinct workflow step after prek checks pass. CI will reject PRs that fail these checks. Always run them locally first.
### Testing
Tests are documentation that shows how features work. Good tests give reviewers confidence and help future maintainers understand intent.
```bash
# Run specific test directory
uv run pytest tests/server/ -v
# Run all tests before submitting PR
uv run pytest
```
Every new feature needs tests. See the [Testing Guide](/development/tests) for patterns and requirements.
### Documentation
A feature doesn't exist unless it's documented. Note that FastMCP's hosted documentation always tracks the main branch - users who want historical documentation can clone the repo, checkout a specific tag, and host it themselves.
```bash
# Preview documentation locally
just docs
```
Documentation requirements:
- **Explain concepts in prose first** - Code without context is just syntax
- **Complete, runnable examples** - Every code block should be copy-pasteable
- **Register in docs.json** - Makes pages appear in navigation
- **Version badges** - Mark when features were added using `<VersionBadge />`
#### SDK Documentation
FastMCP's SDK documentation is auto-generated from the source code docstrings and type annotations. It is automatically updated on every merge to main by a GitHub Actions workflow, so users are *not* responsible for keeping the documentation up to date. However, to generate it proactively, you can use the following command:
```bash
just api-ref-all
```
### Submitting Your PR
#### Before Submitting
1. **Run all checks**: `uv run prek run --all-files && uv run pytest`
2. **Keep scope small**: One feature or fix per PR
3. **Write clear description**: Your PR description becomes permanent documentation
4. **Update docs**: Include documentation for API changes
#### PR Description
Write PR descriptions that explain:
- What problem you're solving
- Why you chose this approach
- Any trade-offs or alternatives considered
- Migration path for breaking changes
Focus on the "why" - the code shows the "what". Keep it concise but complete.
#### What We Look For
**Framework Philosophy**: FastMCP is NOT trying to do all things or provide all shortcuts. Features are rejected when they don't align with the framework's vision, even if perfectly implemented. The burden of proof is on the PR to demonstrate value.
**Code Quality**: We verify code follows existing patterns. Consistency reduces cognitive load. When every module works similarly, developers understand new code quickly.
**Test Coverage**: Not every line needs testing, but every behavior does. Tests document intent and protect against regressions.
**Breaking Changes**: May be acceptable in minor versions but must be clearly documented. See the [versioning policy](/development/releases#versioning-policy).
## Special Modules
**`contrib`**: Community-maintained patterns and utilities. Original authors maintain their contributions. Not representative of the core framework.
**`experimental`**: Maintainer-developed features that may preview future functionality. Can break or be deleted at any time without notice. Pin your FastMCP version when using these features.

View file

@ -0,0 +1,79 @@
---
title: "Releases"
description: "FastMCP versioning and release process"
icon: "truck-fast"
---
FastMCP releases frequently to deliver features quickly in the rapidly evolving MCP ecosystem. We use semantic versioning pragmatically - the Model Context Protocol is young, patterns are still emerging, and waiting for perfect stability would mean missing opportunities to empower developers with better tools.
## Versioning Policy
### Semantic Versioning
**Major (x.0.0)**: Complete API redesigns
Major versions represent fundamental shifts. FastMCP 2.x is entirely different from 1.x in both implementation and design philosophy.
**Minor (2.x.0)**: New features and evolution
<Warning>
Unlike traditional semantic versioning, minor versions **may** include [breaking changes](#breaking-changes) when necessary for the ecosystem's evolution. This flexibility is essential in a young ecosystem where perfect backwards compatibility would prevent important improvements.
</Warning>
FastMCP always targets the most current MCP Protocol version. Breaking changes in the MCP spec or MCP SDK automatically flow through to FastMCP - we prioritize staying current with the latest features and conventions over maintaining compatibility with older protocol versions.
**Patch (2.0.x)**: Bug fixes and refinements
Patch versions contain only bug fixes without breaking changes. These are safe updates you can apply with confidence.
### Breaking Changes
We permit breaking changes in minor versions because the MCP ecosystem is rapidly evolving. Refusing to break problematic APIs would accumulate design debt that eventually makes the framework unusable. Each breaking change represents a deliberate decision to keep FastMCP aligned with the ecosystem's evolution.
When breaking changes occur:
- They only happen in minor versions (e.g., 2.3.x to 2.4.0)
- Release notes explain what changed and how to migrate
- We provide deprecation warnings at least 1 minor version in advance when possible
- Changes must substantially benefit users to justify disruption
The public API is what's covered by our compatibility guarantees - these are the parts of FastMCP you can rely on to remain stable within a minor version. The public API consists of:
- `FastMCP` server class, `Client` class, and FastMCP `Context`
- Core MCP components: `Tool`, `Prompt`, `Resource`, `ResourceTemplate`, and transports
- Their public methods and documented behaviors
Everything else (utilities, private methods, internal modules) may change without notice. This boundary lets us refactor internals and improve implementation details without breaking your code. For production stability, pin to specific versions.
<Warning>
The `fastmcp.server.auth` module was introduced in 2.12.0 and is exempted from this policy temporarily, meaning it is *expected* to have breaking changes even on patch versions. This is because auth is a rapidly evolving part of the MCP spec and it would be dangerous to be beholden to old decisions. Please pin your FastMCP version if using authentication in production.
We expect this exemption to last through at least the 2.12.x and 2.13.x release series.
</Warning>
### Production Use
Pin to exact versions:
```
fastmcp==2.11.0 # Good
fastmcp>=2.11.0 # Bad - will install breaking changes
```
## Creating Releases
Our release process is intentionally simple:
1. Create GitHub release with tag `vMAJOR.MINOR.PATCH` (e.g., `v2.11.0`)
2. Generate release notes automatically, and curate or add additional editorial information as needed
3. GitHub releases automatically trigger PyPI deployments
This automation lets maintainers focus on code quality rather than release mechanics.
### Release Cadence
We follow a feature-driven release cadence rather than a fixed schedule. Minor versions ship approximately every 3-4 weeks when significant functionality is ready.
Patch releases ship promptly for:
- Critical bug fixes
- Security updates (immediate release)
- Regression fixes
This approach means you get improvements as soon as they're ready rather than waiting for arbitrary release dates.

View file

@ -0,0 +1,396 @@
---
title: "Tests"
description: "Testing patterns and requirements for FastMCP"
icon: vial
---
import { VersionBadge } from "/snippets/version-badge.mdx"
Good tests are the foundation of reliable software. In FastMCP, we treat tests as first-class documentation that demonstrates how features work while protecting against regressions. Every new capability needs comprehensive tests that demonstrate correctness.
## FastMCP Tests
### Running Tests
```bash
# Run all tests
uv run pytest
# Run specific test file
uv run pytest tests/server/test_auth.py
# Run with coverage
uv run pytest --cov=fastmcp
# Skip integration tests for faster runs
uv run pytest -m "not integration"
# Skip tests that spawn processes
uv run pytest -m "not integration and not client_process"
```
Tests should complete in under 1 second unless marked as integration tests. This speed encourages running them frequently, catching issues early.
### Test Organization
Our test organization mirrors the `src/` directory structure, creating a predictable mapping between code and tests. When you're working on `src/fastmcp/server/auth.py`, you'll find its tests in `tests/server/test_auth.py`. In rare cases tests are split further - for example, the OpenAPI tests are so comprehensive they're split across multiple files.
### Test Markers
We use pytest markers to categorize tests that require special resources or take longer to run:
```python
@pytest.mark.integration
async def test_github_api_integration():
"""Test GitHub API integration with real service."""
token = os.getenv("FASTMCP_GITHUB_TOKEN")
if not token:
pytest.skip("FASTMCP_GITHUB_TOKEN not available")
# Test against real GitHub API
client = GitHubClient(token)
repos = await client.list_repos("jlowin")
assert "fastmcp" in [repo.name for repo in repos]
@pytest.mark.client_process
async def test_stdio_transport():
"""Test STDIO transport with separate process."""
# This spawns a subprocess
async with Client("python examples/simple_echo.py") as client:
result = await client.call_tool("echo", {"message": "test"})
assert result.content[0].text == "test"
```
## Writing Tests
### Test Requirements
Following these practices creates maintainable, debuggable test suites that serve as both documentation and regression protection.
#### Single Behavior Per Test
Each test should verify exactly one behavior. When it fails, you need to know immediately what broke. A test that checks five things gives you five potential failure points to investigate. A test that checks one thing points directly to the problem.
<CodeGroup>
```python Good: Atomic Test
async def test_tool_registration():
"""Test that tools are properly registered with the server."""
mcp = FastMCP("test-server")
@mcp.tool
def add(a: int, b: int) -> int:
return a + b
tools = mcp.list_tools()
assert len(tools) == 1
assert tools[0].name == "add"
```
```python Bad: Multi-Behavior Test
async def test_server_functionality():
"""Test multiple server features at once."""
mcp = FastMCP("test-server")
# Tool registration
@mcp.tool
def add(a: int, b: int) -> int:
return a + b
# Resource creation
@mcp.resource("config://app")
def get_config():
return {"version": "1.0"}
# Authentication setup
mcp.auth = BearerTokenProvider({"token": "user"})
# What exactly are we testing? If this fails, what broke?
assert mcp.list_tools()
assert mcp.list_resources()
assert mcp.auth is not None
```
</CodeGroup>
#### Self-Contained Setup
Every test must create its own setup. Tests should be runnable in any order, in parallel, or in isolation. When a test fails, you should be able to run just that test to reproduce the issue.
<CodeGroup>
```python Good: Self-Contained
async def test_tool_execution_with_error():
"""Test that tool errors are properly handled."""
mcp = FastMCP("test-server")
@mcp.tool
def divide(a: int, b: int) -> float:
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
async with Client(mcp) as client:
with pytest.raises(Exception):
await client.call_tool("divide", {"a": 10, "b": 0})
```
```python Bad: Test Dependencies
# Global state that tests depend on
test_server = None
def test_setup_server():
"""Setup for other tests."""
global test_server
test_server = FastMCP("shared-server")
def test_server_works():
"""Test server functionality."""
# Depends on test_setup_server running first
assert test_server is not None
```
</CodeGroup>
#### Clear Intent
Test names and assertions should make the verified behavior obvious. A developer reading your test should understand what feature it validates and how that feature should behave.
```python
async def test_authenticated_tool_requires_valid_token():
"""Test that authenticated users can access protected tools."""
mcp = FastMCP("test-server")
mcp.auth = BearerTokenProvider({"secret-token": "test-user"})
@mcp.tool
def protected_action() -> str:
return "success"
async with Client(mcp, auth=BearerAuth("secret-token")) as client:
result = await client.call_tool("protected_action", {})
assert result.content[0].text == "success"
```
#### Using Fixtures
Use fixtures to create reusable data, server configurations, or other resources for your tests. Note that you should **not** open FastMCP clients in your fixtures as it can create hard-to-diagnose issues with event loops.
```python
import pytest
from fastmcp import FastMCP, Client
@pytest.fixture
def weather_server():
server = FastMCP("WeatherServer")
@server.tool
def get_temperature(city: str) -> dict:
temps = {"NYC": 72, "LA": 85, "Chicago": 68}
return {"city": city, "temp": temps.get(city, 70)}
return server
async def test_temperature_tool(weather_server):
async with Client(weather_server) as client:
result = await client.call_tool("get_temperature", {"city": "LA"})
assert result.data == {"city": "LA", "temp": 85}
```
#### Effective Assertions
Assertions should be specific and provide context on failure. When a test fails during CI, the assertion message should tell you exactly what went wrong.
```python
# Basic assertion - minimal context on failure
assert result.status == "success"
# Better - explains what was expected
assert result.status == "success", f"Expected successful operation, got {result.status}: {result.error}"
```
Try not to have too many assertions in a single test unless you truly need to check various aspects of the same behavior. In general, assertions of different behaviors should be in separate tests.
#### Inline Snapshots
FastMCP uses `inline-snapshot` for testing complex data structures. On first run of `pytest --inline-snapshot=create` with an empty `snapshot()`, pytest will auto-populate the expected value. To update snapshots after intentional changes, run `pytest --inline-snapshot=fix`. This is particularly useful for testing JSON schemas and API responses.
```python
from inline_snapshot import snapshot
async def test_tool_schema_generation():
"""Test that tool schemas are generated correctly."""
mcp = FastMCP("test-server")
@mcp.tool
def calculate_tax(amount: float, rate: float = 0.1) -> dict:
"""Calculate tax on an amount."""
return {"amount": amount, "tax": amount * rate, "total": amount * (1 + rate)}
tools = mcp.list_tools()
schema = tools[0].inputSchema
# First run: snapshot() is empty, gets auto-populated
# Subsequent runs: compares against stored snapshot
assert schema == snapshot({
"type": "object",
"properties": {
"amount": {"type": "number"},
"rate": {"type": "number", "default": 0.1}
},
"required": ["amount"]
})
```
### In-Memory Testing
FastMCP uses in-memory transport for testing, where servers and clients communicate directly. The majority of functionality can be tested in a deterministic fashion this way. We use more complex setups only when testing transports themselves.
The in-memory transport runs the real MCP protocol implementation without network overhead. Instead of deploying your server or managing network connections, you pass your server instance directly to the client. Everything runs in the same Python process - you can set breakpoints anywhere and step through with your debugger.
```python
from fastmcp import FastMCP, Client
# Create your server
server = FastMCP("WeatherServer")
@server.tool
def get_temperature(city: str) -> dict:
"""Get current temperature for a city"""
temps = {"NYC": 72, "LA": 85, "Chicago": 68}
return {"city": city, "temp": temps.get(city, 70)}
async def test_weather_operations():
# Pass server directly - no deployment needed
async with Client(server) as client:
result = await client.call_tool("get_temperature", {"city": "NYC"})
assert result.data == {"city": "NYC", "temp": 72}
```
This pattern makes tests deterministic and fast - typically completing in milliseconds rather than seconds.
### Mocking External Dependencies
FastMCP servers are standard Python objects, so you can mock external dependencies using your preferred approach:
```python
from unittest.mock import AsyncMock
async def test_database_tool():
server = FastMCP("DataServer")
# Mock the database
mock_db = AsyncMock()
mock_db.fetch_users.return_value = [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"}
]
@server.tool
async def list_users() -> list:
return await mock_db.fetch_users()
async with Client(server) as client:
result = await client.call_tool("list_users", {})
assert len(result.data) == 2
assert result.data[0]["name"] == "Alice"
mock_db.fetch_users.assert_called_once()
```
### Testing Network Transports
While in-memory testing covers most unit testing needs, you'll occasionally need to test actual network transports like HTTP or SSE. FastMCP provides two approaches: in-process async servers (preferred), and separate subprocess servers (for special cases).
#### In-Process Network Testing (Preferred)
<VersionBadge version="2.13.0" />
For most network transport tests, use `run_server_async` as an async context manager. This runs the server as a task in the same process, providing fast, deterministic tests with full debugger support:
```python
import pytest
from fastmcp import FastMCP, Client
from fastmcp.client.transports import StreamableHttpTransport
from fastmcp.utilities.tests import run_server_async
def create_test_server() -> FastMCP:
"""Create a test server instance."""
server = FastMCP("TestServer")
@server.tool
def greet(name: str) -> str:
return f"Hello, {name}!"
return server
@pytest.fixture
async def http_server() -> str:
"""Start server in-process for testing."""
server = create_test_server()
async with run_server_async(server) as url:
yield url
async def test_http_transport(http_server: str):
"""Test actual HTTP transport behavior."""
async with Client(
transport=StreamableHttpTransport(http_server)
) as client:
result = await client.ping()
assert result is True
greeting = await client.call_tool("greet", {"name": "World"})
assert greeting.data == "Hello, World!"
```
The `run_server_async` context manager automatically handles server lifecycle and cleanup. This approach is faster than subprocess-based testing and provides better error messages.
#### Subprocess Testing (Special Cases)
For tests that require complete process isolation (like STDIO transport or testing subprocess behavior), use `run_server_in_process`:
```python
import pytest
from fastmcp.utilities.tests import run_server_in_process
from fastmcp import FastMCP, Client
from fastmcp.client.transports import StreamableHttpTransport
def run_server(host: str, port: int) -> None:
"""Function to run in subprocess."""
server = FastMCP("TestServer")
@server.tool
def greet(name: str) -> str:
return f"Hello, {name}!"
server.run(host=host, port=port)
@pytest.fixture
async def http_server():
"""Fixture that runs server in subprocess."""
with run_server_in_process(run_server, transport="http") as url:
yield f"{url}/mcp"
async def test_http_transport(http_server: str):
"""Test actual HTTP transport behavior."""
async with Client(
transport=StreamableHttpTransport(http_server)
) as client:
result = await client.ping()
assert result is True
```
The `run_server_in_process` utility handles server lifecycle, port allocation, and cleanup automatically. Use this only when subprocess isolation is truly necessary, as it's slower and harder to debug than in-process testing. FastMCP uses the `client_process` marker to isolate these tests in CI.
### Documentation Testing
Documentation requires the same validation as code. The `just docs` command launches a local Mintlify server that renders your documentation exactly as users will see it:
```bash
# Start local documentation server with hot reload
just docs
# Or run Mintlify directly
mintlify dev
```
The local server watches for changes and automatically refreshes. This preview catches formatting issues and helps you see documentation as users will experience it.

View file

@ -0,0 +1,153 @@
---
title: Upgrade Guide
sidebarTitle: Upgrade Guide
description: Migration instructions for upgrading between FastMCP versions
icon: up
tag: NEW
---
This guide provides migration instructions for breaking changes and major updates when upgrading between FastMCP versions.
## v2.14.0
### OpenAPI Parser Promotion
The experimental OpenAPI parser is now the standard implementation. The legacy parser has been removed.
**If you were using the legacy parser:** No code changes required. The new parser is a drop-in replacement with improved architecture.
**If you were using the experimental parser:** Update your imports from the experimental module to the standard location:
<CodeGroup>
```python Before
from fastmcp.experimental.server.openapi import FastMCPOpenAPI, RouteMap, MCPType
```
```python After
from fastmcp.server.openapi import FastMCPOpenAPI, RouteMap, MCPType
```
</CodeGroup>
The experimental imports will continue working temporarily but will show deprecation warnings. The `FASTMCP_EXPERIMENTAL_ENABLE_NEW_OPENAPI_PARSER` environment variable is no longer needed and can be removed.
### Deprecated Features Removed
The following deprecated features have been removed in v2.14.0:
**BearerAuthProvider** (deprecated in v2.11):
<CodeGroup>
```python Before
from fastmcp.server.auth.providers.bearer import BearerAuthProvider
```
```python After
from fastmcp.server.auth.providers.jwt import JWTVerifier
```
</CodeGroup>
**Context.get_http_request()** (deprecated in v2.2.11):
<CodeGroup>
```python Before
request = context.get_http_request()
```
```python After
from fastmcp.server.dependencies import get_http_request
request = get_http_request()
```
</CodeGroup>
**Top-level Image import** (deprecated in v2.8.1):
<CodeGroup>
```python Before
from fastmcp import Image
```
```python After
from fastmcp.utilities.types import Image
```
</CodeGroup>
**FastMCP dependencies parameter** (deprecated in v2.11.4):
<CodeGroup>
```python Before
mcp = FastMCP("server", dependencies=["requests", "pandas"])
```
```json After
{
"environment": {
"dependencies": ["requests", "pandas"]
}
}
```
</CodeGroup>
**Legacy resource prefix format**: The `resource_prefix_format` parameter and "protocol" format have been removed. Only the "path" format is supported (this was already the default).
**FastMCPProxy client parameter**:
<CodeGroup>
```python Before
proxy = FastMCPProxy(client=my_client)
```
```python After
proxy = FastMCPProxy(client_factory=lambda: my_client)
```
</CodeGroup>
**output_schema=False**:
<CodeGroup>
```python Before
@mcp.tool(output_schema=False)
def my_tool() -> str:
return "result"
```
```python After
@mcp.tool(output_schema=None)
def my_tool() -> str:
return "result"
```
</CodeGroup>
## v2.13.0
### OAuth Token Key Management
The OAuth proxy now issues its own JWT tokens to clients instead of forwarding upstream provider tokens. This improves security by maintaining proper token audience boundaries.
**What changed:**
The OAuth proxy now implements a token factory pattern - it receives tokens from your OAuth provider (GitHub, Google, etc.), encrypts and stores them, then issues its own FastMCP JWT tokens to clients. This requires cryptographic keys for JWT signing and token encryption.
**Default behavior (development):**
By default, FastMCP automatically manages keys based on your platform:
- **Mac/Windows**: Keys are auto-managed via system keyring, surviving server restarts with zero configuration. Suitable **only** for development and local testing.
- **Linux**: Keys are ephemeral (random salt at startup, regenerated on each restart).
This works fine for development and testing where re-authentication after restart is acceptable.
**For production:**
Production deployments must provide explicit keys and use persistent storage. Add these three things:
```python
auth = GitHubProvider(
client_id=os.environ["GITHUB_CLIENT_ID"],
client_secret=os.environ["GITHUB_CLIENT_SECRET"],
base_url="https://your-server.com",
# Explicit keys (required for production)
jwt_signing_key=os.environ["JWT_SIGNING_KEY"],
# Persistent network storage (required for production)
client_storage=RedisStore(host="redis.example.com", port=6379)
)
```
**More information:**
- [OAuth Token Security](/deployment/http#oauth-token-security) - Complete production setup guide
- [Key and Storage Management](/servers/auth/oauth-proxy#key-and-storage-management) - Detailed explanation of defaults and production requirements
- [OAuth Proxy Parameters](/servers/auth/oauth-proxy#configuration-parameters) - Parameter documentation

View file

@ -0,0 +1,100 @@
---
title: Installation
icon: arrow-down-to-line
---
## Install FastMCP
We recommend using [uv](https://docs.astral.sh/uv/getting-started/installation/) to install and manage FastMCP.
If you plan to use FastMCP in your project, you can add it as a dependency with:
```bash
uv add fastmcp
```
Alternatively, you can install it directly with `pip` or `uv pip`:
<CodeGroup>
```bash uv
uv pip install fastmcp
```
```bash pip
pip install fastmcp
```
</CodeGroup>
<Warning>
**FastMCP 3.0** is in development and may include breaking changes. To avoid unexpected issues, pin your dependency to v2: `fastmcp<3`
</Warning>
### Verify Installation
To verify that FastMCP is installed correctly, you can run the following command:
```bash
fastmcp version
```
You should see output like the following:
```bash
$ fastmcp version
FastMCP version: 2.11.3
MCP version: 1.12.4
Python version: 3.12.2
Platform: macOS-15.3.1-arm64-arm-64bit
FastMCP root path: ~/Developer/fastmcp
```
### Dependency Licensing
<Info>
FastMCP depends on Cyclopts for CLI functionality. Cyclopts v4 includes docutils as a transitive dependency, which has complex licensing that may trigger compliance reviews in some organizations.
If this is a concern, you can install Cyclopts v5 alpha which removes this dependency:
```bash
pip install "cyclopts>=5.0.0a1"
```
Alternatively, wait for the stable v5 release. See [this issue](https://github.com/BrianPugh/cyclopts/issues/672) for details.
</Info>
## Upgrading from the Official MCP SDK
Upgrading from the official MCP SDK's FastMCP 1.0 to FastMCP 2.0 is generally straightforward. The core server API is highly compatible, and in many cases, changing your import statement from `from mcp.server.fastmcp import FastMCP` to `from fastmcp import FastMCP` will be sufficient.
```python {5}
# Before
# from mcp.server.fastmcp import FastMCP
# After
from fastmcp import FastMCP
mcp = FastMCP("My MCP Server")
```
<Warning>
Prior to `fastmcp==2.3.0` and `mcp==1.8.0`, the 2.x API always mirrored the official 1.0 API. However, as the projects diverge, this can not be guaranteed. You may see deprecation warnings if you attempt to use 1.0 APIs in FastMCP 2.x. Please refer to this documentation for details on new capabilities.
</Warning>
## Versioning Policy
FastMCP follows semantic versioning with pragmatic adaptations for the rapidly evolving MCP ecosystem. Breaking changes may occur in minor versions (e.g., 2.3.x to 2.4.0) when necessary to stay current with the MCP Protocol.
For production use, always pin to exact versions:
```
fastmcp==2.11.0 # Good
fastmcp>=2.11.0 # Bad - will install breaking changes
```
See the full [versioning and release policy](/development/releases#versioning-policy) for details on our public API, deprecation practices, and breaking change philosophy.
## Contributing to FastMCP
Interested in contributing to FastMCP? See the [Contributing Guide](/development/contributing) for details on:
- Setting up your development environment
- Running tests and pre-commit hooks
- Submitting issues and pull requests
- Code standards and review process

View file

@ -0,0 +1,136 @@
---
title: Quickstart
icon: rocket-launch
---
Welcome! This guide will help you quickly set up FastMCP, run your first MCP server, and deploy a server to FastMCP Cloud.
If you haven't already installed FastMCP, follow the [installation instructions](/getting-started/installation).
## Create a FastMCP Server
A FastMCP server is a collection of tools, resources, and other MCP components. To create a server, start by instantiating the `FastMCP` class.
Create a new file called `my_server.py` and add the following code:
```python my_server.py
from fastmcp import FastMCP
mcp = FastMCP("My MCP Server")
```
That's it! You've created a FastMCP server, albeit a very boring one. Let's add a tool to make it more interesting.
## Add a Tool
To add a tool that returns a simple greeting, write a function and decorate it with `@mcp.tool` to register it with the server:
```python my_server.py {5-7}
from fastmcp import FastMCP
mcp = FastMCP("My MCP Server")
@mcp.tool
def greet(name: str) -> str:
return f"Hello, {name}!"
```
## Run the Server
The simplest way to run your FastMCP server is to call its `run()` method. You can choose between different transports, like `stdio` for local servers, or `http` for remote access:
<CodeGroup>
```python my_server.py (stdio) {9, 10}
from fastmcp import FastMCP
mcp = FastMCP("My MCP Server")
@mcp.tool
def greet(name: str) -> str:
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.run()
```
```python my_server.py (HTTP) {9, 10}
from fastmcp import FastMCP
mcp = FastMCP("My MCP Server")
@mcp.tool
def greet(name: str) -> str:
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.run(transport="http", port=8000)
```
</CodeGroup>
This lets us run the server with `python my_server.py`. The stdio transport is the traditional way to connect MCP servers to clients, while the HTTP transport enables remote connections.
<Tip>
Why do we need the `if __name__ == "__main__":` block?
The `__main__` block is recommended for consistency and compatibility, ensuring your server works with all MCP clients that execute your server file as a script. Users who will exclusively run their server with the FastMCP CLI can omit it, as the CLI imports the server object directly.
</Tip>
### Using the FastMCP CLI
You can also use the `fastmcp run` command to start your server. Note that the FastMCP CLI **does not** execute the `__main__` block of your server file. Instead, it imports your server object and runs it with whatever transport and options you provide.
For example, to run this server with the default stdio transport (no matter how you called `mcp.run()`), you can use the following command:
```bash
fastmcp run my_server.py:mcp
```
To run this server with the HTTP transport, you can use the following command:
```bash
fastmcp run my_server.py:mcp --transport http --port 8000
```
## Call Your Server
Once your server is running with HTTP transport, you can connect to it with a FastMCP client or any LLM client that supports the MCP protocol:
```python my_client.py
import asyncio
from fastmcp import Client
client = Client("http://localhost:8000/mcp")
async def call_tool(name: str):
async with client:
result = await client.call_tool("greet", {"name": name})
print(result)
asyncio.run(call_tool("Ford"))
```
Note that:
- FastMCP clients are asynchronous, so we need to use `asyncio.run` to run the client
- We must enter a client context (`async with client:`) before using the client
- You can make multiple client calls within the same context
## Deploy to FastMCP Cloud
[FastMCP Cloud](https://fastmcp.cloud) is a hosting service run by the FastMCP team at [Prefect](https://www.prefect.io/fastmcp). It is optimized to deploy authenticated FastMCP servers as quickly as possible, giving you a secure URL that you can plug into any LLM client.
<Info>
FastMCP Cloud is **free for personal servers** and offers simple pay-as-you-go pricing for teams.
</Info>
To deploy your server, you'll need a [GitHub account](https://github.com). Once you have one, you can deploy your server in three steps:
1. Push your `my_server.py` file to a GitHub repository
2. Sign in to [FastMCP Cloud](https://fastmcp.cloud) with your GitHub account
3. Create a new project from your repository and enter `my_server.py:mcp` as the server entrypoint
That's it! FastMCP Cloud will build and deploy your server, making it available at a URL like `https://your-project.fastmcp.app/mcp`. You can chat with it to test its functionality, or connect to it from any LLM client that supports the MCP protocol.
For more details, see the [FastMCP Cloud guide](/deployment/fastmcp-cloud).

View file

@ -0,0 +1,115 @@
---
title: "Welcome to FastMCP 2.0!"
sidebarTitle: "Welcome!"
description: The fast, Pythonic way to build MCP servers and clients.
icon: hand-wave
---
<img
src="/assets/brand/f-watercolor-waves.png"
alt="'F' logo on a watercolor background"
noZoom
className="rounded-2xl block dark:hidden"
/>
<img
src="/assets/brand/f-watercolor-waves-dark.png"
alt="'F' logo on a watercolor background"
noZoom
className="rounded-2xl hidden dark:block"
/>
**FastMCP is the standard framework for building MCP applications.** The [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) provides a standardized way to connect LLMs to tools and data, and FastMCP makes it production-ready with clean, Pythonic code:
```python {1}
from fastmcp import FastMCP
mcp = FastMCP("Demo 🚀")
@mcp.tool
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
if __name__ == "__main__":
mcp.run()
```
## Beyond Basic MCP
FastMCP pioneered Python MCP development, and FastMCP 1.0 was incorporated into the [official MCP SDK](https://github.com/modelcontextprotocol/python-sdk) in 2024.
**This is FastMCP 2.0,** the actively maintained version that extends far beyond basic protocol implementation. While the SDK provides core functionality, FastMCP 2.0 delivers everything needed for production: advanced MCP patterns (server composition, proxying, OpenAPI/FastAPI generation, tool transformation), enterprise auth (Google, GitHub, Azure, Auth0, WorkOS, and more), deployment tools, testing frameworks, and comprehensive client libraries.
Ready to build? Start with our [installation guide](/getting-started/installation) or jump straight to the [quickstart](/getting-started/quickstart).
FastMCP is made with 💙 by [Prefect](https://www.prefect.io/).
<Warning>
**FastMCP 3.0** is in development and may include breaking changes. To avoid unexpected issues, pin your dependency to v2: `fastmcp<3`
</Warning>
## What is MCP?
The Model Context Protocol lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. It is often described as "the USB-C port for AI", providing a uniform way to connect LLMs to resources they can use. It may be easier to think of it as an API, but specifically designed for LLM interactions. MCP servers can:
- Expose data through `Resources` (think of these sort of like GET endpoints; they are used to load information into the LLM's context)
- Provide functionality through `Tools` (sort of like POST endpoints; they are used to execute code or otherwise produce a side effect)
- Define interaction patterns through `Prompts` (reusable templates for LLM interactions)
- And more!
FastMCP provides a high-level, Pythonic interface for building, managing, and interacting with these servers.
## Why FastMCP?
FastMCP handles all the complex protocol details so you can focus on building. In most cases, decorating a Python function is all you need — FastMCP handles the rest.
🚀 **Fast**: High-level interface means less code and faster development
🍀 **Simple**: Build MCP servers with minimal boilerplate
🐍 **Pythonic**: Feels natural to Python developers
🔍 **Complete**: Everything for production — enterprise auth (Google, GitHub, Azure, Auth0, WorkOS), deployment tools, testing frameworks, client libraries, and more
FastMCP provides the shortest path from idea to production. Deploy locally, to the cloud with [FastMCP Cloud](https://fastmcp.cloud) (free for personal servers), or to your own infrastructure.
<Tip>
**This documentation reflects FastMCP's `main` branch**, meaning it always reflects the latest development version. Features are generally marked with version badges (e.g. `New in version: 2.13.1`) to indicate when they were introduced. Note that this may include features that are not yet released.
</Tip>
## LLM-Friendly Docs
The FastMCP documentation is available in multiple LLM-friendly formats:
### MCP Server
The FastMCP docs are accessible via MCP! The server URL is `https://gofastmcp.com/mcp`.
In fact, you can use FastMCP to search the FastMCP docs:
```python
import asyncio
from fastmcp import Client
async def main():
async with Client("https://gofastmcp.com/mcp") as client:
result = await client.call_tool(
name="SearchFastMcp",
arguments={"query": "deploy a FastMCP server"}
)
print(result)
asyncio.run(main())
```
### Text Formats
The docs are also available in [llms.txt format](https://llmstxt.org/):
- [llms.txt](https://gofastmcp.com/llms.txt) - A sitemap listing all documentation pages
- [llms-full.txt](https://gofastmcp.com/llms-full.txt) - The entire documentation in one file (may exceed context windows)
Any page can be accessed as markdown by appending `.md` to the URL. For example, this page becomes `https://gofastmcp.com/getting-started/welcome.md`.
You can also copy any page as markdown by pressing "Cmd+C" (or "Ctrl+C" on Windows) on your keyboard.

View file

@ -0,0 +1,228 @@
---
title: Anthropic API 🤝 FastMCP
sidebarTitle: Anthropic API
description: Connect FastMCP servers to the Anthropic API
icon: message-code
---
import { VersionBadge } from "/snippets/version-badge.mdx"
Anthropic's [Messages API](https://docs.anthropic.com/en/api/messages) supports MCP servers as remote tool sources. This tutorial will show you how to create a FastMCP server and deploy it to a public URL, then how to call it from the Messages API.
<Tip>
Currently, the MCP connector only accesses **tools** from MCP servers—it queries the `list_tools` endpoint and exposes those functions to Claude. Other MCP features like resources and prompts are not currently supported. You can read more about the MCP connector in the [Anthropic documentation](https://docs.anthropic.com/en/docs/agents-and-tools/mcp-connector).
</Tip>
## Create a Server
First, create a FastMCP server with the tools you want to expose. For this example, we'll create a server with a single tool that rolls dice.
```python server.py
import random
from fastmcp import FastMCP
mcp = FastMCP(name="Dice Roller")
@mcp.tool
def roll_dice(n_dice: int) -> list[int]:
"""Roll `n_dice` 6-sided dice and return the results."""
return [random.randint(1, 6) for _ in range(n_dice)]
if __name__ == "__main__":
mcp.run(transport="http", port=8000)
```
## Deploy the Server
Your server must be deployed to a public URL in order for Anthropic to access it. The MCP connector supports both SSE and Streamable HTTP transports.
For development, you can use tools like `ngrok` to temporarily expose a locally-running server to the internet. We'll do that for this example (you may need to install `ngrok` and create a free account), but you can use any other method to deploy your server.
Assuming you saved the above code as `server.py`, you can run the following two commands in two separate terminals to deploy your server and expose it to the internet:
<CodeGroup>
```bash FastMCP server
python server.py
```
```bash ngrok
ngrok http 8000
```
</CodeGroup>
<Warning>
This exposes your unauthenticated server to the internet. Only run this command in a safe environment if you understand the risks.
</Warning>
## Call the Server
To use the Messages API with MCP servers, you'll need to install the Anthropic Python SDK (not included with FastMCP):
```bash
pip install anthropic
```
You'll also need to authenticate with Anthropic. You can do this by setting the `ANTHROPIC_API_KEY` environment variable. Consult the Anthropic SDK documentation for more information.
```bash
export ANTHROPIC_API_KEY="your-api-key"
```
Here is an example of how to call your server from Python. Note that you'll need to replace `https://your-server-url.com` with the actual URL of your server. In addition, we use `/mcp/` as the endpoint because we deployed a streamable-HTTP server with the default path; you may need to use a different endpoint if you customized your server's deployment. **At this time you must also include the `extra_headers` parameter with the `anthropic-beta` header.**
```python {5, 13-22}
import anthropic
from rich import print
# Your server URL (replace with your actual URL)
url = 'https://your-server-url.com'
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1000,
messages=[{"role": "user", "content": "Roll a few dice!"}],
mcp_servers=[
{
"type": "url",
"url": f"{url}/mcp/",
"name": "dice-server",
}
],
extra_headers={
"anthropic-beta": "mcp-client-2025-04-04"
}
)
print(response.content)
```
If you run this code, you'll see something like the following output:
```text
I'll roll some dice for you! Let me use the dice rolling tool.
I rolled 3 dice and got: 4, 2, 6
The results were 4, 2, and 6. Would you like me to roll again or roll a different number of dice?
```
## Authentication
<VersionBadge version="2.6.0" />
The MCP connector supports OAuth authentication through authorization tokens, which means you can secure your server while still allowing Anthropic to access it.
### Server Authentication
The simplest way to add authentication to the server is to use a bearer token scheme.
For this example, we'll quickly generate our own tokens with FastMCP's `RSAKeyPair` utility, but this may not be appropriate for production use. For more details, see the complete server-side [Token Verification](/servers/auth/token-verification) documentation.
We'll start by creating an RSA key pair to sign and verify tokens.
```python
from fastmcp.server.auth.providers.jwt import RSAKeyPair
key_pair = RSAKeyPair.generate()
access_token = key_pair.create_token(audience="dice-server")
```
<Warning>
FastMCP's `RSAKeyPair` utility is for development and testing only.
</Warning>
Next, we'll create a `JWTVerifier` to authenticate the server.
```python
from fastmcp import FastMCP
from fastmcp.server.auth import JWTVerifier
auth = JWTVerifier(
public_key=key_pair.public_key,
audience="dice-server",
)
mcp = FastMCP(name="Dice Roller", auth=auth)
```
Here is a complete example that you can copy/paste. For simplicity and the purposes of this example only, it will print the token to the console. **Do NOT do this in production!**
```python server.py [expandable]
from fastmcp import FastMCP
from fastmcp.server.auth import JWTVerifier
from fastmcp.server.auth.providers.jwt import RSAKeyPair
import random
key_pair = RSAKeyPair.generate()
access_token = key_pair.create_token(audience="dice-server")
auth = JWTVerifier(
public_key=key_pair.public_key,
audience="dice-server",
)
mcp = FastMCP(name="Dice Roller", auth=auth)
@mcp.tool
def roll_dice(n_dice: int) -> list[int]:
"""Roll `n_dice` 6-sided dice and return the results."""
return [random.randint(1, 6) for _ in range(n_dice)]
if __name__ == "__main__":
print(f"\n---\n\n🔑 Dice Roller access token:\n\n{access_token}\n\n---\n")
mcp.run(transport="http", port=8000)
```
### Client Authentication
If you try to call the authenticated server with the same Anthropic code we wrote earlier, you'll get an error indicating that the server rejected the request because it's not authenticated.
```python
Error code: 400 - {
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "MCP server 'dice-server' requires authentication. Please provide an authorization_token.",
},
}
```
To authenticate the client, you can pass the token using the `authorization_token` parameter in your MCP server configuration:
```python {8, 21}
import anthropic
from rich import print
# Your server URL (replace with your actual URL)
url = 'https://your-server-url.com'
# Your access token (replace with your actual token)
access_token = 'your-access-token'
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1000,
messages=[{"role": "user", "content": "Roll a few dice!"}],
mcp_servers=[
{
"type": "url",
"url": f"{url}/mcp/",
"name": "dice-server",
"authorization_token": access_token
}
],
extra_headers={
"anthropic-beta": "mcp-client-2025-04-04"
}
)
print(response.content)
```
You should now see the dice roll results in the output.

View file

@ -0,0 +1,277 @@
---
title: Auth0 OAuth 🤝 FastMCP
sidebarTitle: Auth0
description: Secure your FastMCP server with Auth0 OAuth
icon: shield-check
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.12.4" />
This guide shows you how to secure your FastMCP server using **Auth0 OAuth**. While Auth0 does have support for Dynamic Client Registration, it is not enabled by default so this integration uses the [**OIDC Proxy**](/servers/auth/oidc-proxy) pattern to bridge Auth0's dynamic OIDC configuration with MCP's authentication requirements.
## Configuration
### Prerequisites
Before you begin, you will need:
1. An **[Auth0 Account](https://auth0.com/)** with access to create Applications
2. Your FastMCP server's URL (can be localhost for development, e.g., `http://localhost:8000`)
### Step 1: Create an Auth0 Application
Create an Application in your Auth0 settings to get the credentials needed for authentication:
<Steps>
<Step title="Navigate to Applications">
Go to **Applications → Applications** in your Auth0 account.
Click **"+ Create Application"** to create a new application.
</Step>
<Step title="Create Your Application">
- **Name**: Choose a name users will recognize (e.g., "My FastMCP Server")
- **Choose an application type**: Choose "Single Page Web Applications"
- Click **Create** to create the application
</Step>
<Step title="Configure Your Application">
Select the "Settings" tab for your application, then find the "Application URIs" section.
- **Allowed Callback URLs**: Your server URL + `/auth/callback` (e.g., `http://localhost:8000/auth/callback`)
- Click **Save** to save your changes
<Warning>
The callback URL must match exactly. The default path is `/auth/callback`, but you can customize it using the `redirect_path` parameter.
</Warning>
<Tip>
If you want to use a custom callback path (e.g., `/auth/auth0/callback`), make sure to set the same path in both your Auth0 Application settings and the `redirect_path` parameter when configuring the Auth0Provider.
</Tip>
</Step>
<Step title="Save Your Credentials">
After creating the app, in the "Basic Information" section you'll see:
- **Client ID**: A public identifier like `tv2ObNgaZAWWhhycr7Bz1LU2mxlnsmsB`
- **Client Secret**: A private hidden value that should always be stored securely
<Tip>
Store these credentials securely. Never commit them to version control. Use environment variables or a secrets manager in production.
</Tip>
</Step>
<Step title="Select Your Audience">
Go to **Applications → APIs** in your Auth0 account.
- Find the API that you want to use for your application
- **API Audience**: A URL that uniquely identifies the API
<Tip>
Store this along with of the credentials above. Never commit this to version control. Use environment variables or a secrets manager in production.
</Tip>
</Step>
</Steps>
### Step 2: FastMCP Configuration
Create your FastMCP server using the `Auth0Provider`.
```python server.py
from fastmcp import FastMCP
from fastmcp.server.auth.providers.auth0 import Auth0Provider
# The Auth0Provider utilizes Auth0 OIDC configuration
auth_provider = Auth0Provider(
config_url="https://.../.well-known/openid-configuration", # Your Auth0 configuration URL
client_id="tv2ObNgaZAWWhhycr7Bz1LU2mxlnsmsB", # Your Auth0 application Client ID
client_secret="vPYqbjemq...", # Your Auth0 application Client Secret
audience="https://...", # Your Auth0 API audience
base_url="http://localhost:8000", # Must match your application configuration
# redirect_path="/auth/callback" # Default value, customize if needed
)
mcp = FastMCP(name="Auth0 Secured App", auth=auth_provider)
# Add a protected tool to test authentication
@mcp.tool
async def get_token_info() -> dict:
"""Returns information about the Auth0 token."""
from fastmcp.server.dependencies import get_access_token
token = get_access_token()
return {
"issuer": token.claims.get("iss"),
"audience": token.claims.get("aud"),
"scope": token.claims.get("scope")
}
```
## Testing
### Running the Server
Start your FastMCP server with HTTP transport to enable OAuth flows:
```bash
fastmcp run server.py --transport http --port 8000
```
Your server is now running and protected by Auth0 authentication.
### Testing with a Client
Create a test client that authenticates with your Auth0-protected server:
```python test_client.py
from fastmcp import Client
import asyncio
async def main():
# The client will automatically handle Auth0 OAuth flows
async with Client("http://localhost:8000/mcp", auth="oauth") as client:
# First-time connection will open Auth0 login in your browser
print("✓ Authenticated with Auth0!")
# Test the protected tool
result = await client.call_tool("get_token_info")
print(f"Auth0 audience: {result['audience']}")
if __name__ == "__main__":
asyncio.run(main())
```
When you run the client for the first time:
1. Your browser will open to Auth0's authorization page
2. After you authorize the app, you'll be redirected back
3. The client receives the token and can make authenticated requests
## Production Configuration
<VersionBadge version="2.13.0" />
For production deployments with persistent token management across server restarts, configure `jwt_signing_key`, and `client_storage`:
```python server.py
import os
from fastmcp import FastMCP
from fastmcp.server.auth.providers.auth0 import Auth0Provider
from key_value.aio.stores.redis import RedisStore
from key_value.aio.wrappers.encryption import FernetEncryptionWrapper
from cryptography.fernet import Fernet
# Production setup with encrypted persistent token storage
auth_provider = Auth0Provider(
config_url="https://.../.well-known/openid-configuration",
client_id="tv2ObNgaZAWWhhycr7Bz1LU2mxlnsmsB",
client_secret="vPYqbjemq...",
audience="https://...",
base_url="https://your-production-domain.com",
# Production token management
jwt_signing_key=os.environ["JWT_SIGNING_KEY"],
client_storage=FernetEncryptionWrapper(
key_value=RedisStore(
host=os.environ["REDIS_HOST"],
port=int(os.environ["REDIS_PORT"])
),
fernet=Fernet(os.environ["STORAGE_ENCRYPTION_KEY"])
)
)
mcp = FastMCP(name="Production Auth0 App", auth=auth_provider)
```
<Note>
Parameters (`jwt_signing_key` and `client_storage`) work together to ensure tokens and client registrations survive server restarts. **Wrap your storage in `FernetEncryptionWrapper` to encrypt sensitive OAuth tokens at rest** - without it, tokens are stored in plaintext. Store secrets in environment variables and use a persistent storage backend like Redis for distributed deployments.
For complete details on these parameters, see the [OAuth Proxy documentation](/servers/auth/oauth-proxy#configuration-parameters).
</Note>
<Info>
The client caches tokens locally, so you won't need to re-authenticate for subsequent runs unless the token expires or you explicitly clear the cache.
</Info>
## Environment Variables
For production deployments, use environment variables instead of hardcoding credentials.
### Provider Selection
Setting this environment variable allows the Auth0 provider to be used automatically without explicitly instantiating it in code.
<Card>
<ParamField path="FASTMCP_SERVER_AUTH" default="Not set">
Set to `fastmcp.server.auth.providers.auth0.Auth0Provider` to use Auth0 authentication.
</ParamField>
</Card>
### Auth0-Specific Configuration
These environment variables provide default values for the Auth0 provider, whether it's instantiated manually or configured via `FASTMCP_SERVER_AUTH`.
<Card>
<ParamField path="FASTMCP_SERVER_AUTH_AUTH0_CONFIG_URL" required>
Your Auth0 Application Configuration URL (e.g., `https://.../.well-known/openid-configuration`)
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AUTH0_CLIENT_ID" required>
Your Auth0 Application Client ID (e.g., `tv2ObNgaZAWWhhycr7Bz1LU2mxlnsmsB`)
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AUTH0_CLIENT_SECRET" required>
Your Auth0 Application Client Secret (e.g., `vPYqbjemq...`)
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AUTH0_AUDIENCE" required>
Your Auth0 API Audience
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AUTH0_BASE_URL" required>
Public URL where OAuth endpoints will be accessible (includes any mount path)
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AUTH0_ISSUER_URL" default="Uses BASE_URL">
Issuer URL for OAuth metadata (defaults to `BASE_URL`). Set to root-level URL when mounting under a path prefix to avoid 404 logs. See [HTTP Deployment guide](/deployment/http#mounting-authenticated-servers) for details.
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AUTH0_REDIRECT_PATH" default="/auth/callback">
Redirect path configured in your Auth0 Application
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AUTH0_REQUIRED_SCOPES" default='["openid"]'>
Comma-, space-, or JSON-separated list of required AUth0 scopes (e.g., `openid email` or `["openid","email"]`)
</ParamField>
</Card>
Example `.env` file:
```bash
# Use the Auth0 provider
FASTMCP_SERVER_AUTH=fastmcp.server.auth.providers.auth0.Auth0Provider
# Auth0 configuration and credentials
FASTMCP_SERVER_AUTH_AUTH0_CONFIG_URL=https://.../.well-known/openid-configuration
FASTMCP_SERVER_AUTH_AUTH0_CLIENT_ID=tv2ObNgaZAWWhhycr7Bz1LU2mxlnsmsB
FASTMCP_SERVER_AUTH_AUTH0_CLIENT_SECRET=vPYqbjemq...
FASTMCP_SERVER_AUTH_AUTH0_AUDIENCE=https://...
FASTMCP_SERVER_AUTH_AUTH0_BASE_URL=https://your-server.com
FASTMCP_SERVER_AUTH_AUTH0_REQUIRED_SCOPES=openid,email
```
With environment variables set, your server code simplifies to:
```python server.py
from fastmcp import FastMCP
# Authentication is automatically configured from environment
mcp = FastMCP(name="Auth0 Secured App")
@mcp.tool
async def search_logs() -> list[str]:
"""Search the service logs."""
# Your tool implementation here
pass
```

View file

@ -0,0 +1,133 @@
---
title: AuthKit 🤝 FastMCP
sidebarTitle: AuthKit
description: Secure your FastMCP server with AuthKit by WorkOS
icon: shield-check
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.11.0" />
This guide shows you how to secure your FastMCP server using WorkOS's **AuthKit**, a complete authentication and user management solution. This integration uses the [**Remote OAuth**](/servers/auth/remote-oauth) pattern, where AuthKit handles user login and your FastMCP server validates the tokens.
## Configuration
### Prerequisites
Before you begin, you will need:
1. A **[WorkOS Account](https://workos.com/)** and a new **Project**.
2. An **[AuthKit](https://www.authkit.com/)** instance configured within your WorkOS project.
3. Your FastMCP server's URL (can be localhost for development, e.g., `http://localhost:8000`).
### Step 1: AuthKit Configuration
In your WorkOS Dashboard, enable AuthKit and configure the following settings:
<Steps>
<Step title="Enable Dynamic Client Registration">
Go to **Applications → Configuration** and enable **Dynamic Client Registration**. This allows MCP clients register with your application automatically.
![Enable Dynamic Client Registration](./images/authkit/enable_dcr.png)
</Step>
<Step title="Note Your AuthKit Domain">
Find your **AuthKit Domain** on the configuration page. It will look like `https://your-project-12345.authkit.app`. You'll need this for your FastMCP server configuration.
</Step>
</Steps>
### Step 2: FastMCP Configuration
Create your FastMCP server file and use the `AuthKitProvider` to handle all the OAuth integration automatically:
```python server.py
from fastmcp import FastMCP
from fastmcp.server.auth.providers.workos import AuthKitProvider
# The AuthKitProvider automatically discovers WorkOS endpoints
# and configures JWT token validation
auth_provider = AuthKitProvider(
authkit_domain="https://your-project-12345.authkit.app",
base_url="http://localhost:8000" # Use your actual server URL
)
mcp = FastMCP(name="AuthKit Secured App", auth=auth_provider)
```
## Testing
To test your server, you can use the `fastmcp` CLI to run it locally. Assuming you've saved the above code to `server.py` (after replacing the `authkit_domain` and `base_url` with your actual values!), you can run the following command:
```bash
fastmcp run server.py --transport http --port 8000
```
Now, you can use a FastMCP client to test that you can reach your server after authenticating:
```python
from fastmcp import Client
import asyncio
async def main():
async with Client("http://localhost:8000/mcp", auth="oauth") as client:
assert await client.ping()
if __name__ == "__main__":
asyncio.run(main())
```
## Environment Variables
<VersionBadge version="2.12.1" />
For production deployments, use environment variables instead of hardcoding credentials.
### Provider Selection
Setting this environment variable allows the AuthKit provider to be used automatically without explicitly instantiating it in code.
<Card>
<ParamField path="FASTMCP_SERVER_AUTH" default="Not set">
Set to `fastmcp.server.auth.providers.workos.AuthKitProvider` to use AuthKit authentication.
</ParamField>
</Card>
### AuthKit-Specific Configuration
These environment variables provide default values for the AuthKit provider, whether it's instantiated manually or configured via `FASTMCP_SERVER_AUTH`.
<Card>
<ParamField path="FASTMCP_SERVER_AUTH_AUTHKITPROVIDER_AUTHKIT_DOMAIN" required>
Your AuthKit domain (e.g., `https://your-project-12345.authkit.app`)
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AUTHKITPROVIDER_BASE_URL" required>
Public URL of your FastMCP server (e.g., `https://your-server.com` or `http://localhost:8000` for development)
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AUTHKITPROVIDER_REQUIRED_SCOPES" default="[]">
Comma-, space-, or JSON-separated list of required OAuth scopes (e.g., `openid profile email` or `["openid", "profile", "email"]`)
</ParamField>
</Card>
Example `.env` file:
```bash
# Use the AuthKit provider
FASTMCP_SERVER_AUTH=fastmcp.server.auth.providers.workos.AuthKitProvider
# AuthKit configuration
FASTMCP_SERVER_AUTH_AUTHKITPROVIDER_AUTHKIT_DOMAIN=https://your-project-12345.authkit.app
FASTMCP_SERVER_AUTH_AUTHKITPROVIDER_BASE_URL=https://your-server.com
FASTMCP_SERVER_AUTH_AUTHKITPROVIDER_REQUIRED_SCOPES=openid,profile,email
```
With environment variables set, your server code simplifies to:
```python server.py
from fastmcp import FastMCP
# Authentication is automatically configured from environment
mcp = FastMCP(name="AuthKit Secured App")
```

View file

@ -0,0 +1,365 @@
---
title: AWS Cognito OAuth 🤝 FastMCP
sidebarTitle: AWS Cognito
description: Secure your FastMCP server with AWS Cognito user pools
icon: aws
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.12.4" />
This guide shows you how to secure your FastMCP server using **AWS Cognito user pools**. Since AWS Cognito doesn't support Dynamic Client Registration, this integration uses the [**OAuth Proxy**](/servers/auth/oauth-proxy) pattern to bridge AWS Cognito's traditional OAuth with MCP's authentication requirements. It also includes robust JWT token validation, ensuring enterprise-grade authentication.
## Configuration
### Prerequisites
Before you begin, you will need:
1. An **[AWS Account](https://aws.amazon.com/)** with access to create AWS Cognito user pools
2. Basic familiarity with AWS Cognito concepts (user pools, app clients)
3. Your FastMCP server's URL (can be localhost for development, e.g., `http://localhost:8000`)
### Step 1: Create an AWS Cognito User Pool and App Client
Set up AWS Cognito user pool with an app client to get the credentials needed for authentication:
<Steps>
<Step title="Navigate to AWS Cognito">
Go to the **[AWS Cognito Console](https://console.aws.amazon.com/cognito/)** and ensure you're in your desired AWS region.
Select **"User pools"** from the side navigation (click on the hamburger icon at the top left in case you don't see any), and click **"Create user pool"** to create a new user pool.
</Step>
<Step title="Define Your Application">
AWS Cognito now provides a streamlined setup experience:
1. **Application type**: Select **"Traditional web application"** (this is the correct choice for FastMCP server-side authentication)
2. **Name your application**: Enter a descriptive name (e.g., `FastMCP Server`)
The traditional web application type automatically configures:
- Server-side authentication with client secrets
- Authorization code grant flow
- Appropriate security settings for confidential clients
<Info>
Choose "Traditional web application" rather than SPA, Mobile app, or Machine-to-machine options. This ensures proper OAuth 2.0 configuration for FastMCP.
</Info>
</Step>
<Step title="Configure Options">
AWS will guide you through configuration options:
- **Sign-in identifiers**: Choose how users will sign in (email, username, or phone)
- **Required attributes**: Select any additional user information you need
- **Return URL**: Add your callback URL (e.g., `http://localhost:8000/auth/callback` for development)
<Tip>
The simplified interface handles most OAuth security settings automatically based on your application type selection.
</Tip>
</Step>
<Step title="Review and Create">
Review your configuration and click **"Create user pool"**.
After creation, you'll see your user pool details. Save these important values:
- **User pool ID** (format: `eu-central-1_XXXXXXXXX`)
- **Client ID** (found under → "Applications" → "App clients" in the side navigation → \<Your application name, e.g., `FastMCP Server`\> → "App client information")
- **Client Secret** (found under → "Applications" → "App clients" in the side navigation → \<Your application name, e.g., `FastMCP Server`\> → "App client information")
<Tip>
The user pool ID and app client credentials are all you need for FastMCP configuration.
</Tip>
</Step>
<Step title="Configure OAuth Settings">
Under "Login pages" in your app client's settings, you can double check and adjust the OAuth configuration:
- **Allowed callback URLs**: Add your server URL + `/auth/callback` (e.g., `http://localhost:8000/auth/callback`)
- **Allowed sign-out URLs**: Optional, for logout functionality
- **OAuth 2.0 grant types**: Ensure "Authorization code grant" is selected
- **OpenID Connect scopes**: Select scopes your application needs (e.g., `openid`, `email`, `profile`)
<Tip>
For local development, you can use `http://localhost` URLs. For production, you must use HTTPS.
</Tip>
</Step>
<Step title="Configure Resource Server">
AWS Cognito requires a resource server entry to support OAuth with protected resources. Without this, token exchange will fail with an `invalid_grant` error.
Navigate to **"Branding" → "Domain"** in the side navigation, then:
1. Click **"Create resource server"**
2. **Resource server name**: Enter a descriptive name (e.g., `My MCP Server`)
3. **Resource server identifier**: Enter your MCP endpoint URL exactly as it will be accessed (e.g., `http://localhost:8000/mcp` for development, or `https://your-server.com/mcp` for production)
4. Click **"Create resource server"**
<Warning>
The resource server identifier must exactly match your `base_url + mcp_path`. For the default configuration with `base_url="http://localhost:8000"` and `path="/mcp"`, use `http://localhost:8000/mcp`.
</Warning>
</Step>
<Step title="Save Your Credentials">
After setup, you'll have:
- **User Pool ID**: Format like `eu-central-1_XXXXXXXXX`
- **Client ID**: Your application's client identifier
- **Client Secret**: Generated client secret (keep secure)
- **AWS Region**: Where Your AWS Cognito user pool is located
<Tip>
Store these credentials securely. Never commit them to version control. Use environment variables or AWS Secrets Manager in production.
</Tip>
</Step>
</Steps>
### Step 2: FastMCP Configuration
Create your FastMCP server using the `AWSCognitoProvider`, which handles AWS Cognito's JWT tokens and user claims automatically:
```python server.py
from fastmcp import FastMCP
from fastmcp.server.auth.providers.aws import AWSCognitoProvider
from fastmcp.server.dependencies import get_access_token
# The AWSCognitoProvider handles JWT validation and user claims
auth_provider = AWSCognitoProvider(
user_pool_id="eu-central-1_XXXXXXXXX", # Your AWS Cognito user pool ID
aws_region="eu-central-1", # AWS region (defaults to eu-central-1)
client_id="your-app-client-id", # Your app client ID
client_secret="your-app-client-secret", # Your app client Secret
base_url="http://localhost:8000", # Must match your callback URL
# redirect_path="/auth/callback" # Default value, customize if needed
)
mcp = FastMCP(name="AWS Cognito Secured App", auth=auth_provider)
# Add a protected tool to test authentication
@mcp.tool
async def get_access_token_claims() -> dict:
"""Get the authenticated user's access token claims."""
token = get_access_token()
return {
"sub": token.claims.get("sub"),
"username": token.claims.get("username"),
"cognito:groups": token.claims.get("cognito:groups", []),
}
```
## Testing
### Running the Server
Start your FastMCP server with HTTP transport to enable OAuth flows:
```bash
fastmcp run server.py --transport http --port 8000
```
Your server is now running and protected by AWS Cognito OAuth authentication.
### Testing with a Client
Create a test client that authenticates with Your AWS Cognito-protected server:
```python test_client.py
from fastmcp import Client
import asyncio
async def main():
# The client will automatically handle AWS Cognito OAuth
async with Client("http://localhost:8000/mcp", auth="oauth") as client:
# First-time connection will open AWS Cognito login in your browser
print("✓ Authenticated with AWS Cognito!")
# Test the protected tool
print("Calling protected tool: get_access_token_claims")
result = await client.call_tool("get_access_token_claims")
user_data = result.data
print("Available access token claims:")
print(f"- sub: {user_data.get('sub', 'N/A')}")
print(f"- username: {user_data.get('username', 'N/A')}")
print(f"- cognito:groups: {user_data.get('cognito:groups', [])}")
if __name__ == "__main__":
asyncio.run(main())
```
When you run the client for the first time:
1. Your browser will open to AWS Cognito's hosted UI login page
2. After you sign in (or sign up), you'll be redirected back to your MCP server
3. The client receives the JWT token and can make authenticated requests
<Info>
The client caches tokens locally, so you won't need to re-authenticate for subsequent runs unless the token expires or you explicitly clear the cache.
</Info>
## Production Configuration
<VersionBadge version="2.13.0" />
For production deployments with persistent token management across server restarts, configure `jwt_signing_key`, and `client_storage`:
```python server.py
import os
from fastmcp import FastMCP
from fastmcp.server.auth.providers.aws import AWSCognitoProvider
from key_value.aio.stores.redis import RedisStore
from key_value.aio.wrappers.encryption import FernetEncryptionWrapper
from cryptography.fernet import Fernet
# Production setup with encrypted persistent token storage
auth_provider = AWSCognitoProvider(
user_pool_id="eu-central-1_XXXXXXXXX",
aws_region="eu-central-1",
client_id="your-app-client-id",
client_secret="your-app-client-secret",
base_url="https://your-production-domain.com",
# Production token management
jwt_signing_key=os.environ["JWT_SIGNING_KEY"],
client_storage=FernetEncryptionWrapper(
key_value=RedisStore(
host=os.environ["REDIS_HOST"],
port=int(os.environ["REDIS_PORT"])
),
fernet=Fernet(os.environ["STORAGE_ENCRYPTION_KEY"])
)
)
mcp = FastMCP(name="Production AWS Cognito App", auth=auth_provider)
```
<Note>
Parameters (`jwt_signing_key` and `client_storage`) work together to ensure tokens and client registrations survive server restarts. **Wrap your storage in `FernetEncryptionWrapper` to encrypt sensitive OAuth tokens at rest** - without it, tokens are stored in plaintext. Store secrets in environment variables and use a persistent storage backend like Redis for distributed deployments.
For complete details on these parameters, see the [OAuth Proxy documentation](/servers/auth/oauth-proxy#configuration-parameters).
</Note>
## Environment Variables
For production deployments, use environment variables instead of hardcoding credentials.
### Provider Selection
Setting this environment variable allows the AWS Cognito provider to be used automatically without explicitly instantiating it in code.
<Card>
<ParamField path="FASTMCP_SERVER_AUTH" default="Not set">
Set to `fastmcp.server.auth.providers.aws.AWSCognitoProvider` to use AWS Cognito authentication.
</ParamField>
</Card>
### AWS Cognito-Specific Configuration
These environment variables provide default values for the AWS Cognito provider, whether it's instantiated manually or configured via `FASTMCP_SERVER_AUTH`.
<Card>
<ParamField path="FASTMCP_SERVER_AUTH_AWS_COGNITO_USER_POOL_ID" required>
Your AWS Cognito user pool ID (e.g., `eu-central-1_XXXXXXXXX`)
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AWS_COGNITO_AWS_REGION" default="eu-central-1">
AWS region where your AWS Cognito user pool is located
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AWS_COGNITO_CLIENT_ID" required>
Your AWS Cognito app client ID
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AWS_COGNITO_CLIENT_SECRET" required>
Your AWS Cognito app client secret
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AWS_COGNITO_BASE_URL" default="http://localhost:8000">
Public URL where OAuth endpoints will be accessible (includes any mount path)
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AWS_COGNITO_ISSUER_URL" default="Uses BASE_URL">
Issuer URL for OAuth metadata (defaults to `BASE_URL`). Set to root-level URL when mounting under a path prefix to avoid 404 logs. See [HTTP Deployment guide](/deployment/http#mounting-authenticated-servers) for details.
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AWS_COGNITO_REDIRECT_PATH" default="/auth/callback">
One of the redirect paths configured in your AWS Cognito app client
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AWS_COGNITO_REQUIRED_SCOPES" default='["openid"]'>
Comma-, space-, or JSON-separated list of required OAuth scopes (e.g., `openid email` or `["openid","email","profile"]`)
</ParamField>
</Card>
Example `.env` file:
```bash
# Use the AWS Cognito provider
FASTMCP_SERVER_AUTH=fastmcp.server.auth.providers.aws.AWSCognitoProvider
# AWS Cognito credentials
FASTMCP_SERVER_AUTH_AWS_COGNITO_USER_POOL_ID=eu-central-1_XXXXXXXXX
FASTMCP_SERVER_AUTH_AWS_COGNITO_AWS_REGION=eu-central-1
FASTMCP_SERVER_AUTH_AWS_COGNITO_CLIENT_ID=your-app-client-id
FASTMCP_SERVER_AUTH_AWS_COGNITO_CLIENT_SECRET=your-app-client-secret
FASTMCP_SERVER_AUTH_AWS_COGNITO_BASE_URL=https://your-server.com
FASTMCP_SERVER_AUTH_AWS_COGNITO_REQUIRED_SCOPES=openid,email,profile
```
With environment variables set, your server code simplifies to:
```python server.py
from fastmcp import FastMCP
from fastmcp.server.dependencies import get_access_token
# Authentication is automatically configured from environment
mcp = FastMCP(name="AWS Cognito Secured App")
@mcp.tool
async def get_access_token_claims() -> dict:
"""Get the authenticated user's access token claims."""
token = get_access_token()
return {
"sub": token.claims.get("sub"),
"username": token.claims.get("username"),
"cognito:groups": token.claims.get("cognito:groups", []),
}
```
## Features
### JWT Token Validation
The AWS Cognito provider includes robust JWT token validation:
- **Signature Verification**: Validates tokens against AWS Cognito's public keys (JWKS)
- **Expiration Checking**: Automatically rejects expired tokens
- **Issuer Validation**: Ensures tokens come from your specific AWS Cognito user pool
- **Scope Enforcement**: Verifies required OAuth scopes are present
### User Claims and Groups
Access rich user information from AWS Cognito JWT tokens:
```python
from fastmcp.server.dependencies import get_access_token
@mcp.tool
async def admin_only_tool() -> str:
"""A tool only available to admin users."""
token = get_access_token()
user_groups = token.claims.get("cognito:groups", [])
if "admin" not in user_groups:
raise ValueError("This tool requires admin access")
return "Admin access granted!"
```
### Enterprise Integration
Perfect for enterprise environments with:
- **Single Sign-On (SSO)**: Integrate with corporate identity providers
- **Multi-Factor Authentication (MFA)**: Leverage AWS Cognito's built-in MFA
- **User Groups**: Role-based access control through AWS Cognito groups
- **Custom Attributes**: Access custom user attributes defined in your AWS Cognito user pool
- **Compliance**: Meet enterprise security and compliance requirements

View file

@ -0,0 +1,391 @@
---
title: Azure (Microsoft Entra ID) OAuth 🤝 FastMCP
sidebarTitle: Azure (Entra ID)
description: Secure your FastMCP server with Azure/Microsoft Entra OAuth
icon: microsoft
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.13.0" />
This guide shows you how to secure your FastMCP server using **Azure OAuth** (Microsoft Entra ID). Since Azure doesn't support Dynamic Client Registration, this integration uses the [**OAuth Proxy**](/servers/auth/oauth-proxy) pattern to bridge Azure's traditional OAuth with MCP's authentication requirements. FastMCP validates Azure JWTs against your application's client_id.
## Configuration
### Prerequisites
Before you begin, you will need:
1. An **[Azure Account](https://portal.azure.com/)** with access to create App registrations
2. Your FastMCP server's URL (can be localhost for development, e.g., `http://localhost:8000`)
3. Your Azure tenant ID (found in Azure Portal under Microsoft Entra ID)
### Step 1: Create an Azure App Registration
Create an App registration in Azure Portal to get the credentials needed for authentication:
<Steps>
<Step title="Navigate to App registrations">
Go to the [Azure Portal](https://portal.azure.com) and navigate to **Microsoft Entra ID → App registrations**.
Click **"New registration"** to create a new application.
</Step>
<Step title="Configure Your Application">
Fill in the application details:
- **Name**: Choose a name users will recognize (e.g., "My FastMCP Server")
- **Supported account types**: Choose based on your needs:
- **Single tenant**: Only users in your organization
- **Multitenant**: Users in any Microsoft Entra directory
- **Multitenant + personal accounts**: Any Microsoft account
- **Redirect URI**: Select "Web" and enter your server URL + `/auth/callback` (e.g., `http://localhost:8000/auth/callback`)
<Warning>
The redirect URI must match exactly. The default path is `/auth/callback`, but you can customize it using the `redirect_path` parameter. For local development, Azure allows `http://localhost` URLs. For production, you must use HTTPS.
</Warning>
<Tip>
If you want to use a custom callback path (e.g., `/auth/azure/callback`), make sure to set the same path in both your Azure App registration and the `redirect_path` parameter when configuring the AzureProvider.
</Tip>
- **Expose an API**: Configure your Application ID URI and define scopes
- Go to **Expose an API** in the App registration sidebar.
- Click **Set** next to "Application ID URI" and choose one of:
- Keep the default `api://{client_id}`
- Set a custom value, following the supported formats (see [Identifier URI restrictions](https://learn.microsoft.com/en-us/entra/identity-platform/identifier-uri-restrictions))
- Click **Add a scope** and create a scope your app will require, for example:
- Scope name: `read` (or `write`, etc.)
- Admin consent display name/description: as appropriate for your org
- Who can consent: as needed (Admins only or Admins and users)
- **Configure Access Token Version**: Ensure your app uses access token v2
- Go to **Manifest** in the App registration sidebar.
- Find the `requestedAccessTokenVersion` property and set it to `2`:
```json
"api": {
"requestedAccessTokenVersion": 2
}
```
- Click **Save** at the top of the manifest editor.
<Warning>
Access token v2 is required for FastMCP's Azure integration to work correctly. If this is not set, you may encounter authentication errors.
</Warning>
<Note>
In FastMCP's `AzureProvider`, set `identifier_uri` to your Application ID URI (optional; defaults to `api://{client_id}`) and set `required_scopes` to the unprefixed scope names (e.g., `read`, `write`). During authorization, FastMCP automatically prefixes scopes with your `identifier_uri`.
</Note>
</Step>
<Step title="Create Client Secret">
After registration, navigate to **Certificates & secrets** in your app's settings.
- Click **"New client secret"**
- Add a description (e.g., "FastMCP Server")
- Choose an expiration period
- Click **"Add"**
<Warning>
Copy the secret value immediately - it won't be shown again! You'll need to create a new secret if you lose it.
</Warning>
</Step>
<Step title="Note Your Credentials">
From the **Overview** page of your app registration, note:
- **Application (client) ID**: A UUID like `835f09b6-0f0f-40cc-85cb-f32c5829a149`
- **Directory (tenant) ID**: A UUID like `08541b6e-646d-43de-a0eb-834e6713d6d5`
- **Client Secret**: The value you copied in the previous step
<Tip>
Store these credentials securely. Never commit them to version control. Use environment variables or a secrets manager in production.
</Tip>
</Step>
</Steps>
### Step 2: FastMCP Configuration
Create your FastMCP server using the `AzureProvider`, which handles Azure's OAuth flow automatically:
```python server.py
from fastmcp import FastMCP
from fastmcp.server.auth.providers.azure import AzureProvider
# The AzureProvider handles Azure's token format and validation
auth_provider = AzureProvider(
client_id="835f09b6-0f0f-40cc-85cb-f32c5829a149", # Your Azure App Client ID
client_secret="your-client-secret", # Your Azure App Client Secret
tenant_id="08541b6e-646d-43de-a0eb-834e6713d6d5", # Your Azure Tenant ID (REQUIRED)
base_url="http://localhost:8000", # Must match your App registration
required_scopes=["your-scope"], # At least one scope REQUIRED - name of scope from your App
# identifier_uri defaults to api://{client_id}
# identifier_uri="api://your-api-id",
# Optional: request additional upstream scopes in the authorize request
# additional_authorize_scopes=["User.Read", "offline_access", "openid", "email"],
# redirect_path="/auth/callback" # Default value, customize if needed
# base_authority="login.microsoftonline.us" # For Azure Government (default: login.microsoftonline.com)
)
mcp = FastMCP(name="Azure Secured App", auth=auth_provider)
# Add a protected tool to test authentication
@mcp.tool
async def get_user_info() -> dict:
"""Returns information about the authenticated Azure user."""
from fastmcp.server.dependencies import get_access_token
token = get_access_token()
# The AzureProvider stores user data in token claims
return {
"azure_id": token.claims.get("sub"),
"email": token.claims.get("email"),
"name": token.claims.get("name"),
"job_title": token.claims.get("job_title"),
"office_location": token.claims.get("office_location")
}
```
<Note>
**Important**: The `tenant_id` parameter is **REQUIRED**. Azure no longer supports using "common" for new applications due to security requirements. You must use one of:
- **Your specific tenant ID**: Found in Azure Portal (e.g., `08541b6e-646d-43de-a0eb-834e6713d6d5`)
- **"organizations"**: For work and school accounts only
- **"consumers"**: For personal Microsoft accounts only
Using your specific tenant ID is recommended for better security and control.
</Note>
<Note>
**Important**: The `required_scopes` parameter is **REQUIRED** and must include at least one scope. Azure's OAuth API requires the `scope` parameter in all authorization requests - you cannot authenticate without specifying at least one scope. Use the unprefixed scope names from your Azure App registration (e.g., `["read", "write"]`). These scopes must be created under **Expose an API** in your App registration.
</Note>
### Scope Handling
FastMCP automatically prefixes `required_scopes` with your `identifier_uri` (e.g., `api://your-client-id`) since these are your custom API scopes. Scopes in `additional_authorize_scopes` are sent as-is since they target external resources like Microsoft Graph.
**`required_scopes`** — Your custom API scopes, defined in Azure "Expose an API":
| You write | Sent to Azure | Validated on tokens |
|-----------|---------------|---------------------|
| `mcp-read` | `api://xxx/mcp-read` | ✓ |
| `my.scope` | `api://xxx/my.scope` | ✓ |
| `openid` | `openid` | ✗ (OIDC scope) |
| `api://xxx/read` | `api://xxx/read` | ✓ |
**`additional_authorize_scopes`** — External scopes (e.g., Microsoft Graph) for server-side use:
| You write | Sent to Azure | Validated on tokens |
|-----------|---------------|---------------------|
| `User.Read` | `User.Read` | ✗ |
| `Mail.Send` | `Mail.Send` | ✗ |
<Info>
**Why aren't `additional_authorize_scopes` validated?** Azure issues separate tokens per resource. The access token FastMCP receives is for *your API*—Graph scopes aren't in its `scp` claim. To call Graph APIs, your server uses the upstream Azure token in an on-behalf-of (OBO) flow.
</Info>
<Note>
OIDC scopes (`openid`, `profile`, `email`, `offline_access`) are never prefixed and excluded from validation because Azure doesn't include them in access token `scp` claims.
</Note>
## Testing
### Running the Server
Start your FastMCP server with HTTP transport to enable OAuth flows:
```bash
fastmcp run server.py --transport http --port 8000
```
Your server is now running and protected by Azure OAuth authentication.
### Testing with a Client
Create a test client that authenticates with your Azure-protected server:
```python test_client.py
from fastmcp import Client
import asyncio
async def main():
# The client will automatically handle Azure OAuth
async with Client("http://localhost:8000/mcp", auth="oauth") as client:
# First-time connection will open Azure login in your browser
print("✓ Authenticated with Azure!")
# Test the protected tool
result = await client.call_tool("get_user_info")
print(f"Azure user: {result['email']}")
print(f"Name: {result['name']}")
if __name__ == "__main__":
asyncio.run(main())
```
When you run the client for the first time:
1. Your browser will open to Microsoft's authorization page
2. Sign in with your Microsoft account (work, school, or personal based on your tenant configuration)
3. Grant the requested permissions
4. After authorization, you'll be redirected back
5. The client receives the token and can make authenticated requests
<Info>
The client caches tokens locally, so you won't need to re-authenticate for subsequent runs unless the token expires or you explicitly clear the cache.
</Info>
## Production Configuration
<VersionBadge version="2.13.0" />
For production deployments with persistent token management across server restarts, configure `jwt_signing_key` and `client_storage`:
```python server.py
import os
from fastmcp import FastMCP
from fastmcp.server.auth.providers.azure import AzureProvider
from key_value.aio.stores.redis import RedisStore
from key_value.aio.wrappers.encryption import FernetEncryptionWrapper
from cryptography.fernet import Fernet
# Production setup with encrypted persistent token storage
auth_provider = AzureProvider(
client_id="835f09b6-0f0f-40cc-85cb-f32c5829a149",
client_secret="your-client-secret",
tenant_id="08541b6e-646d-43de-a0eb-834e6713d6d5",
base_url="https://your-production-domain.com",
required_scopes=["your-scope"],
# Production token management
jwt_signing_key=os.environ["JWT_SIGNING_KEY"],
client_storage=FernetEncryptionWrapper(
key_value=RedisStore(
host=os.environ["REDIS_HOST"],
port=int(os.environ["REDIS_PORT"])
),
fernet=Fernet(os.environ["STORAGE_ENCRYPTION_KEY"])
)
)
mcp = FastMCP(name="Production Azure App", auth=auth_provider)
```
<Note>
Parameters (`jwt_signing_key` and `client_storage`) work together to ensure tokens and client registrations survive server restarts. **Wrap your storage in `FernetEncryptionWrapper` to encrypt sensitive OAuth tokens at rest** - without it, tokens are stored in plaintext. Store secrets in environment variables and use a persistent storage backend like Redis for distributed deployments.
For complete details on these parameters, see the [OAuth Proxy documentation](/servers/auth/oauth-proxy#configuration-parameters).
</Note>
## Environment Variables
<VersionBadge version="2.12.1" />
For production deployments, use environment variables instead of hardcoding credentials.
### Provider Selection
Setting this environment variable allows the Azure provider to be used automatically without explicitly instantiating it in code.
<Card>
<ParamField path="FASTMCP_SERVER_AUTH" default="Not set">
Set to `fastmcp.server.auth.providers.azure.AzureProvider` to use Azure authentication.
</ParamField>
</Card>
### Azure-Specific Configuration
These environment variables provide default values for the Azure provider, whether it's instantiated manually or configured via `FASTMCP_SERVER_AUTH`.
<Card>
<ParamField path="FASTMCP_SERVER_AUTH_AZURE_CLIENT_ID" required>
Your Azure App registration Client ID (e.g., `835f09b6-0f0f-40cc-85cb-f32c5829a149`)
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AZURE_CLIENT_SECRET" required>
Your Azure App registration Client Secret
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AZURE_TENANT_ID" required>
Your Azure tenant ID (specific ID, "organizations", or "consumers")
<Note>
This is **REQUIRED**. Find your tenant ID in Azure Portal under Microsoft Entra ID → Overview.
</Note>
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AZURE_BASE_URL" default="http://localhost:8000">
Public URL where OAuth endpoints will be accessible (includes any mount path)
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AZURE_ISSUER_URL" default="Uses BASE_URL">
Issuer URL for OAuth metadata (defaults to `BASE_URL`). Set to root-level URL when mounting under a path prefix to avoid 404 logs. See [HTTP Deployment guide](/deployment/http#mounting-authenticated-servers) for details.
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AZURE_REDIRECT_PATH" default="/auth/callback">
Redirect path configured in your Azure App registration
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AZURE_REQUIRED_SCOPES" required>
Comma-, space-, or JSON-separated list of required scopes for your API (at least one scope required). These are validated on tokens and used as defaults if the client does not request specific scopes. Use unprefixed scope names from your Azure App registration (e.g., `read,write`).
You can include standard OIDC scopes (`openid`, `profile`, `email`, `offline_access`) in `required_scopes`. FastMCP automatically handles them correctly: they're sent to Azure unprefixed and excluded from token validation (since Azure doesn't include OIDC scopes in access token `scp` claims).
<Note>
Azure's OAuth API requires the `scope` parameter - you must provide at least one scope.
</Note>
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AZURE_ADDITIONAL_AUTHORIZE_SCOPES" default="">
Comma-, space-, or JSON-separated list of additional scopes to include in the authorization request without prefixing. Use this to request upstream scopes such as Microsoft Graph permissions. These are not used for token validation.
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AZURE_IDENTIFIER_URI" default="api://{client_id}">
Application ID URI used to prefix scopes during authorization.
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_AZURE_BASE_AUTHORITY" default="login.microsoftonline.com">
Azure authority base URL. Override this to use Azure Government:
- `login.microsoftonline.com` - Azure Public Cloud (default)
- `login.microsoftonline.us` - Azure Government
This setting affects all Azure OAuth endpoints (authorization, token, issuer, JWKS).
</ParamField>
</Card>
Example `.env` file:
```bash
# Use the Azure provider
FASTMCP_SERVER_AUTH=fastmcp.server.auth.providers.azure.AzureProvider
# Azure OAuth credentials
FASTMCP_SERVER_AUTH_AZURE_CLIENT_ID=835f09b6-0f0f-40cc-85cb-f32c5829a149
FASTMCP_SERVER_AUTH_AZURE_CLIENT_SECRET=your-client-secret-here
FASTMCP_SERVER_AUTH_AZURE_TENANT_ID=08541b6e-646d-43de-a0eb-834e6713d6d5
FASTMCP_SERVER_AUTH_AZURE_BASE_URL=https://your-server.com
FASTMCP_SERVER_AUTH_AZURE_REQUIRED_SCOPES=read,write
# Optional custom API configuration
# FASTMCP_SERVER_AUTH_AZURE_IDENTIFIER_URI=api://your-api-id
# Request additional upstream scopes (optional)
# FASTMCP_SERVER_AUTH_AZURE_ADDITIONAL_AUTHORIZE_SCOPES=User.Read,Mail.Read
```
With environment variables set, your server code simplifies to:
```python server.py
from fastmcp import FastMCP
# Authentication is automatically configured from environment
mcp = FastMCP(name="Azure Secured App")
@mcp.tool
async def protected_tool(query: str) -> str:
"""A tool that requires Azure authentication to access."""
# Your tool implementation here
return f"Processing authenticated request: {query}"
```

View file

@ -0,0 +1,157 @@
---
title: ChatGPT 🤝 FastMCP
sidebarTitle: ChatGPT
description: Connect FastMCP servers to ChatGPT in Chat and Deep Research modes
icon: message-smile
tag: NEW
---
ChatGPT supports MCP servers through remote HTTP connections in two modes: **Chat mode** for interactive conversations and **Deep Research mode** for comprehensive information retrieval.
<Tip>
**Developer Mode Required for Chat Mode**: To use MCP servers in regular ChatGPT conversations, you must first enable Developer Mode in your ChatGPT settings. This feature is available for ChatGPT Pro, Team, Enterprise, and Edu users.
</Tip>
<Note>
OpenAI's official MCP documentation and examples are built with **FastMCP v2**! Learn more from their [MCP documentation](https://platform.openai.com/docs/mcp) and [Developer Mode guide](https://platform.openai.com/docs/guides/developer-mode).
</Note>
## Build a Server
First, let's create a simple FastMCP server:
```python server.py
from fastmcp import FastMCP
import random
mcp = FastMCP("Demo Server")
@mcp.tool
def roll_dice(sides: int = 6) -> int:
"""Roll a dice with the specified number of sides."""
return random.randint(1, sides)
if __name__ == "__main__":
mcp.run(transport="http", port=8000)
```
### Deploy Your Server
Your server must be accessible from the internet. For development, use `ngrok`:
<CodeGroup>
```bash Terminal 1
python server.py
```
```bash Terminal 2
ngrok http 8000
```
</CodeGroup>
Note your public URL (e.g., `https://abc123.ngrok.io`) for the next steps.
## Chat Mode
Chat mode lets you use MCP tools directly in ChatGPT conversations. See [OpenAI's Developer Mode guide](https://platform.openai.com/docs/guides/developer-mode) for the latest requirements.
### Add to ChatGPT
#### 1. Enable Developer Mode
1. Open ChatGPT and go to **Settings** → **Connectors**
2. Under **Advanced**, toggle **Developer Mode** to enabled
#### 2. Create Connector
1. In **Settings** → **Connectors**, click **Create**
2. Enter:
- **Name**: Your server name
- **Server URL**: `https://your-server.ngrok.io/mcp/`
3. Check **I trust this provider**
4. Add authentication if needed
5. Click **Create**
<Note>
**Without Developer Mode**: If you don't have search/fetch tools, ChatGPT will reject the server. With Developer Mode enabled, you don't need search/fetch tools for Chat mode.
</Note>
#### 3. Use in Chat
1. Start a new chat
2. Click the **+** button → **More** → **Developer Mode**
3. **Enable your MCP server connector** (required - the connector must be explicitly added to each chat)
4. Now you can use your tools:
Example usage:
- "Roll a 20-sided dice"
- "Roll dice" (uses default 6 sides)
<Tip>
The connector must be explicitly enabled in each chat session through Developer Mode. Once added, it remains active for the entire conversation.
</Tip>
### Skip Confirmations
Use `annotations={"readOnlyHint": True}` to skip confirmation prompts for read-only tools:
```python
@mcp.tool(annotations={"readOnlyHint": True})
def get_status() -> str:
"""Check system status."""
return "All systems operational"
@mcp.tool() # No annotation - ChatGPT may ask for confirmation
def delete_item(id: str) -> str:
"""Delete an item."""
return f"Deleted {id}"
```
## Deep Research Mode
Deep Research mode provides systematic information retrieval with citations. See [OpenAI's MCP documentation](https://platform.openai.com/docs/mcp) for the latest Deep Research specifications.
<Warning>
**Search and Fetch Required**: Without Developer Mode, ChatGPT will reject any server that doesn't have both `search` and `fetch` tools. Even in Developer Mode, Deep Research only uses these two tools.
</Warning>
### Tool Implementation
Deep Research tools must follow this pattern:
```python
@mcp.tool()
def search(query: str) -> dict:
"""
Search for records matching the query.
Must return {"ids": [list of string IDs]}
"""
# Your search logic
matching_ids = ["id1", "id2", "id3"]
return {"ids": matching_ids}
@mcp.tool()
def fetch(id: str) -> dict:
"""
Fetch a complete record by ID.
Return the full record data for ChatGPT to analyze.
"""
# Your fetch logic
return {
"id": id,
"title": "Record Title",
"content": "Full record content...",
"metadata": {"author": "Jane Doe", "date": "2024"}
}
```
### Using Deep Research
1. Ensure your server is added to ChatGPT's connectors (same as Chat mode)
2. Start a new chat
3. Click **+** → **Deep Research**
4. Select your MCP server as a source
5. Ask research questions
ChatGPT will use your `search` and `fetch` tools to find and cite relevant information.

View file

@ -0,0 +1,177 @@
---
title: Claude Code 🤝 FastMCP
sidebarTitle: Claude Code
description: Install and use FastMCP servers in Claude Code
icon: message-smile
---
import { VersionBadge } from "/snippets/version-badge.mdx"
import { LocalFocusTip } from "/snippets/local-focus.mdx"
<LocalFocusTip />
Claude Code supports MCP servers through multiple transport methods including STDIO, SSE, and HTTP, allowing you to extend Claude's capabilities with custom tools, resources, and prompts from your FastMCP servers.
## Requirements
This integration uses STDIO transport to run your FastMCP server locally. For remote deployments, you can run your FastMCP server with HTTP or SSE transport and configure it directly using Claude Code's built-in MCP management commands.
## Create a Server
The examples in this guide will use the following simple dice-rolling server, saved as `server.py`.
```python server.py
import random
from fastmcp import FastMCP
mcp = FastMCP(name="Dice Roller")
@mcp.tool
def roll_dice(n_dice: int) -> list[int]:
"""Roll `n_dice` 6-sided dice and return the results."""
return [random.randint(1, 6) for _ in range(n_dice)]
if __name__ == "__main__":
mcp.run()
```
## Install the Server
### FastMCP CLI
<VersionBadge version="2.10.3" />
The easiest way to install a FastMCP server in Claude Code is using the `fastmcp install claude-code` command. This automatically handles the configuration, dependency management, and calls Claude Code's built-in MCP management system.
```bash
fastmcp install claude-code server.py
```
The install command supports the same `file.py:object` notation as the `run` command. If no object is specified, it will automatically look for a FastMCP server object named `mcp`, `server`, or `app` in your file:
```bash
# These are equivalent if your server object is named 'mcp'
fastmcp install claude-code server.py
fastmcp install claude-code server.py:mcp
# Use explicit object name if your server has a different name
fastmcp install claude-code server.py:my_custom_server
```
The command will automatically configure the server with Claude Code's `claude mcp add` command.
#### Dependencies
FastMCP provides flexible dependency management options for your Claude Code servers:
**Individual packages**: Use the `--with` flag to specify packages your server needs. You can use this flag multiple times:
```bash
fastmcp install claude-code server.py --with pandas --with requests
```
**Requirements file**: If you maintain a `requirements.txt` file with all your dependencies, use `--with-requirements` to install them:
```bash
fastmcp install claude-code server.py --with-requirements requirements.txt
```
**Editable packages**: For local packages under development, use `--with-editable` to install them in editable mode:
```bash
fastmcp install claude-code server.py --with-editable ./my-local-package
```
Alternatively, you can use a `fastmcp.json` configuration file (recommended):
```json fastmcp.json
{
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
"source": {
"path": "server.py",
"entrypoint": "mcp"
},
"environment": {
"dependencies": ["pandas", "requests"]
}
}
```
#### Python Version and Project Configuration
Control the Python environment for your server with these options:
**Python version**: Use `--python` to specify which Python version your server requires. This ensures compatibility when your server needs specific Python features:
```bash
fastmcp install claude-code server.py --python 3.11
```
**Project directory**: Use `--project` to run your server within a specific project context. This tells `uv` to use the project's configuration files and virtual environment:
```bash
fastmcp install claude-code server.py --project /path/to/my-project
```
#### Environment Variables
If your server needs environment variables (like API keys), you must include them:
```bash
fastmcp install claude-code server.py --server-name "Weather Server" \
--env API_KEY=your-api-key \
--env DEBUG=true
```
Or load them from a `.env` file:
```bash
fastmcp install claude-code server.py --server-name "Weather Server" --env-file .env
```
<Warning>
**Claude Code must be installed**. The integration looks for the Claude Code CLI at the default installation location (`~/.claude/local/claude`) and uses the `claude mcp add` command to register servers.
</Warning>
### Manual Configuration
For more control over the configuration, you can manually use Claude Code's built-in MCP management commands. This gives you direct control over how your server is launched:
```bash
# Add a server with custom configuration
claude mcp add dice-roller -- uv run --with fastmcp fastmcp run server.py
# Add with environment variables
claude mcp add weather-server -e API_KEY=secret -e DEBUG=true -- uv run --with fastmcp fastmcp run server.py
# Add with specific scope (local, user, or project)
claude mcp add my-server --scope user -- uv run --with fastmcp fastmcp run server.py
```
You can also manually specify Python versions and project directories in your Claude Code commands:
```bash
# With specific Python version
claude mcp add ml-server -- uv run --python 3.11 --with fastmcp fastmcp run server.py
# Within a project directory
claude mcp add project-server -- uv run --project /path/to/project --with fastmcp fastmcp run server.py
```
## Using the Server
Once your server is installed, you can start using your FastMCP server with Claude Code.
Try asking Claude something like:
> "Roll some dice for me"
Claude will automatically detect your `roll_dice` tool and use it to fulfill your request, returning something like:
> I'll roll some dice for you! Here are your results: [4, 2, 6]
>
> You rolled three dice and got a 4, a 2, and a 6!
Claude Code can now access all the tools, resources, and prompts you've defined in your FastMCP server.
If your server provides resources, you can reference them with `@` mentions using the format `@server:protocol://resource/path`. If your server provides prompts, you can use them as slash commands with `/mcp__servername__promptname`.

View file

@ -0,0 +1,298 @@
---
title: Claude Desktop 🤝 FastMCP
sidebarTitle: Claude Desktop
description: Connect FastMCP servers to Claude Desktop
icon: message-smile
---
import { VersionBadge } from "/snippets/version-badge.mdx"
import { LocalFocusTip } from "/snippets/local-focus.mdx"
<LocalFocusTip />
Claude Desktop supports MCP servers through local STDIO connections and remote servers (beta), allowing you to extend Claude's capabilities with custom tools, resources, and prompts from your FastMCP servers.
<Note>
Remote MCP server support is currently in beta and available for users on Claude Pro, Max, Team, and Enterprise plans (as of June 2025). Most users will still need to use local STDIO connections.
</Note>
<Note>
This guide focuses specifically on using FastMCP servers with Claude Desktop. For general Claude Desktop MCP setup and official examples, see the [official Claude Desktop quickstart guide](https://modelcontextprotocol.io/quickstart/user).
</Note>
## Requirements
Claude Desktop traditionally requires MCP servers to run locally using STDIO transport, where your server communicates with Claude through standard input/output rather than HTTP. However, users on certain plans now have access to remote server support as well.
<Tip>
If you don't have access to remote server support or need to connect to remote servers, you can create a **proxy server** that runs locally via STDIO and forwards requests to remote HTTP servers. See the [Proxy Servers](#proxy-servers) section below.
</Tip>
## Create a Server
The examples in this guide will use the following simple dice-rolling server, saved as `server.py`.
```python server.py
import random
from fastmcp import FastMCP
mcp = FastMCP(name="Dice Roller")
@mcp.tool
def roll_dice(n_dice: int) -> list[int]:
"""Roll `n_dice` 6-sided dice and return the results."""
return [random.randint(1, 6) for _ in range(n_dice)]
if __name__ == "__main__":
mcp.run()
```
## Install the Server
### FastMCP CLI
<VersionBadge version="2.10.3" />
The easiest way to install a FastMCP server in Claude Desktop is using the `fastmcp install claude-desktop` command. This automatically handles the configuration and dependency management.
<Tip>
Prior to version 2.10.3, Claude Desktop could be managed by running `fastmcp install <path>` without specifying the client.
</Tip>
```bash
fastmcp install claude-desktop server.py
```
The install command supports the same `file.py:object` notation as the `run` command. If no object is specified, it will automatically look for a FastMCP server object named `mcp`, `server`, or `app` in your file:
```bash
# These are equivalent if your server object is named 'mcp'
fastmcp install claude-desktop server.py
fastmcp install claude-desktop server.py:mcp
# Use explicit object name if your server has a different name
fastmcp install claude-desktop server.py:my_custom_server
```
After installation, restart Claude Desktop completely. You should see a hammer icon (🔨) in the bottom left of the input box, indicating that MCP tools are available.
#### Dependencies
FastMCP provides several ways to manage your server's dependencies when installing in Claude Desktop:
**Individual packages**: Use the `--with` flag to specify packages your server needs. You can use this flag multiple times:
```bash
fastmcp install claude-desktop server.py --with pandas --with requests
```
**Requirements file**: If you have a `requirements.txt` file listing all your dependencies, use `--with-requirements` to install them all at once:
```bash
fastmcp install claude-desktop server.py --with-requirements requirements.txt
```
**Editable packages**: For local packages in development, use `--with-editable` to install them in editable mode:
```bash
fastmcp install claude-desktop server.py --with-editable ./my-local-package
```
Alternatively, you can use a `fastmcp.json` configuration file (recommended):
```json fastmcp.json
{
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
"source": {
"path": "server.py",
"entrypoint": "mcp"
},
"environment": {
"dependencies": ["pandas", "requests"]
}
}
```
#### Python Version and Project Directory
FastMCP allows you to control the Python environment for your server:
**Python version**: Use `--python` to specify which Python version your server should run with. This is particularly useful when your server requires a specific Python version:
```bash
fastmcp install claude-desktop server.py --python 3.11
```
**Project directory**: Use `--project` to run your server within a specific project directory. This ensures that `uv` will discover all `pyproject.toml`, `uv.toml`, and `.python-version` files from that project:
```bash
fastmcp install claude-desktop server.py --project /path/to/my-project
```
When you specify a project directory, all relative paths in your server will be resolved from that directory, and the project's virtual environment will be used.
#### Environment Variables
<Warning>
Claude Desktop runs servers in a completely isolated environment with no access to your shell environment or locally installed applications. You must explicitly pass any environment variables your server needs.
</Warning>
If your server needs environment variables (like API keys), you must include them:
```bash
fastmcp install claude-desktop server.py --server-name "Weather Server" \
--env API_KEY=your-api-key \
--env DEBUG=true
```
Or load them from a `.env` file:
```bash
fastmcp install claude-desktop server.py --server-name "Weather Server" --env-file .env
```
<Warning>
- **`uv` must be installed and available in your system PATH**. Claude Desktop runs in its own isolated environment and needs `uv` to manage dependencies.
- **On macOS, it is recommended to install `uv` globally with Homebrew** so that Claude Desktop will detect it: `brew install uv`. Installing `uv` with other methods may not make it accessible to Claude Desktop.
</Warning>
### Manual Configuration
For more control over the configuration, you can manually edit Claude Desktop's configuration file. You can open the configuration file from Claude's developer settings, or find it in the following locations:
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
The configuration file is a JSON object with a `mcpServers` key, which contains the configuration for each MCP server.
```json
{
"mcpServers": {
"dice-roller": {
"command": "python",
"args": ["path/to/your/server.py"]
}
}
}
```
After updating the configuration file, restart Claude Desktop completely. Look for the hammer icon (🔨) to confirm your server is loaded.
#### Dependencies
If your server has dependencies, you can use `uv` or another package manager to set up the environment.
When manually configuring dependencies, the recommended approach is to use `uv` with FastMCP. The configuration uses `uv run` to create an isolated environment with your specified packages:
```json
{
"mcpServers": {
"dice-roller": {
"command": "uv",
"args": [
"run",
"--with", "fastmcp",
"--with", "pandas",
"--with", "requests",
"fastmcp",
"run",
"path/to/your/server.py"
]
}
}
}
```
You can also manually specify Python versions and project directories in your configuration. Add `--python` to use a specific Python version, or `--project` to run within a project directory:
```json
{
"mcpServers": {
"dice-roller": {
"command": "uv",
"args": [
"run",
"--python", "3.11",
"--project", "/path/to/project",
"--with", "fastmcp",
"fastmcp",
"run",
"path/to/your/server.py"
]
}
}
}
```
The order of arguments matters: Python version and project settings come before package specifications, which come before the actual command to run.
<Warning>
- **`uv` must be installed and available in your system PATH**. Claude Desktop runs in its own isolated environment and needs `uv` to manage dependencies.
- **On macOS, it is recommended to install `uv` globally with Homebrew** so that Claude Desktop will detect it: `brew install uv`. Installing `uv` with other methods may not make it accessible to Claude Desktop.
</Warning>
#### Environment Variables
You can also specify environment variables in the configuration:
```json
{
"mcpServers": {
"weather-server": {
"command": "python",
"args": ["path/to/weather_server.py"],
"env": {
"API_KEY": "your-api-key",
"DEBUG": "true"
}
}
}
}
```
<Warning>
Claude Desktop runs servers in a completely isolated environment with no access to your shell environment or locally installed applications. You must explicitly pass any environment variables your server needs.
</Warning>
## Remote Servers
Users on Claude Pro, Max, Team, and Enterprise plans have first-class remote server support via integrations. For other users, or as an alternative approach, FastMCP can create a proxy server that forwards requests to a remote HTTP server. You can install the proxy server in Claude Desktop.
Create a proxy server that connects to a remote HTTP server:
```python proxy_server.py
from fastmcp import FastMCP
# Create a proxy to a remote server
proxy = FastMCP.as_proxy(
"https://example.com/mcp/sse",
name="Remote Server Proxy"
)
if __name__ == "__main__":
proxy.run() # Runs via STDIO for Claude Desktop
```
### Authentication
For authenticated remote servers, create an authenticated client following the guidance in the [client auth documentation](/clients/auth/bearer) and pass it to the proxy:
```python auth_proxy_server.py {7}
from fastmcp import FastMCP, Client
from fastmcp.client.auth import BearerAuth
# Create authenticated client
client = Client(
"https://api.example.com/mcp/sse",
auth=BearerAuth(token="your-access-token")
)
# Create proxy using the authenticated client
proxy = FastMCP.as_proxy(client, name="Authenticated Proxy")
if __name__ == "__main__":
proxy.run()
```

View file

@ -0,0 +1,284 @@
---
title: Cursor 🤝 FastMCP
sidebarTitle: Cursor
description: Install and use FastMCP servers in Cursor
icon: message-smile
---
import { VersionBadge } from "/snippets/version-badge.mdx"
import { LocalFocusTip } from "/snippets/local-focus.mdx"
<LocalFocusTip />
Cursor supports MCP servers through multiple transport methods including STDIO, SSE, and Streamable HTTP, allowing you to extend Cursor's AI assistant with custom tools, resources, and prompts from your FastMCP servers.
## Requirements
This integration uses STDIO transport to run your FastMCP server locally. For remote deployments, you can run your FastMCP server with HTTP or SSE transport and configure it directly in Cursor's settings.
## Create a Server
The examples in this guide will use the following simple dice-rolling server, saved as `server.py`.
```python server.py
import random
from fastmcp import FastMCP
mcp = FastMCP(name="Dice Roller")
@mcp.tool
def roll_dice(n_dice: int) -> list[int]:
"""Roll `n_dice` 6-sided dice and return the results."""
return [random.randint(1, 6) for _ in range(n_dice)]
if __name__ == "__main__":
mcp.run()
```
## Install the Server
### FastMCP CLI
<VersionBadge version="2.10.3" />
The easiest way to install a FastMCP server in Cursor is using the `fastmcp install cursor` command. This automatically handles the configuration, dependency management, and opens Cursor with a deeplink to install the server.
```bash
fastmcp install cursor server.py
```
#### Workspace Installation
<VersionBadge version="2.12.0" />
By default, FastMCP installs servers globally for Cursor. You can also install servers to project-specific workspaces using the `--workspace` flag:
```bash
# Install to current directory's .cursor/ folder
fastmcp install cursor server.py --workspace .
# Install to specific workspace
fastmcp install cursor server.py --workspace /path/to/project
```
This creates a `.cursor/mcp.json` configuration file in the specified workspace directory, allowing different projects to have their own MCP server configurations.
The install command supports the same `file.py:object` notation as the `run` command. If no object is specified, it will automatically look for a FastMCP server object named `mcp`, `server`, or `app` in your file:
```bash
# These are equivalent if your server object is named 'mcp'
fastmcp install cursor server.py
fastmcp install cursor server.py:mcp
# Use explicit object name if your server has a different name
fastmcp install cursor server.py:my_custom_server
```
After running the command, Cursor will open automatically and prompt you to install the server. The command will be `uv`, which is expected as this is a Python STDIO server. Click "Install" to confirm:
![Cursor install prompt](./cursor-install-mcp.png)
#### Dependencies
FastMCP offers multiple ways to manage dependencies for your Cursor servers:
**Individual packages**: Use the `--with` flag to specify packages your server needs. You can use this flag multiple times:
```bash
fastmcp install cursor server.py --with pandas --with requests
```
**Requirements file**: For projects with a `requirements.txt` file, use `--with-requirements` to install all dependencies at once:
```bash
fastmcp install cursor server.py --with-requirements requirements.txt
```
**Editable packages**: When developing local packages, use `--with-editable` to install them in editable mode:
```bash
fastmcp install cursor server.py --with-editable ./my-local-package
```
Alternatively, you can use a `fastmcp.json` configuration file (recommended):
```json fastmcp.json
{
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
"source": {
"path": "server.py",
"entrypoint": "mcp"
},
"environment": {
"dependencies": ["pandas", "requests"]
}
}
```
#### Python Version and Project Configuration
Control your server's Python environment with these options:
**Python version**: Use `--python` to specify which Python version your server should use. This is essential when your server requires specific Python features:
```bash
fastmcp install cursor server.py --python 3.11
```
**Project directory**: Use `--project` to run your server within a specific project context. This ensures `uv` discovers all project configuration files and uses the correct virtual environment:
```bash
fastmcp install cursor server.py --project /path/to/my-project
```
#### Environment Variables
<Warning>
Cursor runs servers in a completely isolated environment with no access to your shell environment or locally installed applications. You must explicitly pass any environment variables your server needs.
</Warning>
If your server needs environment variables (like API keys), you must include them:
```bash
fastmcp install cursor server.py --server-name "Weather Server" \
--env API_KEY=your-api-key \
--env DEBUG=true
```
Or load them from a `.env` file:
```bash
fastmcp install cursor server.py --server-name "Weather Server" --env-file .env
```
<Warning>
**`uv` must be installed and available in your system PATH**. Cursor runs in its own isolated environment and needs `uv` to manage dependencies.
</Warning>
### Generate MCP JSON
<Note>
**Use the first-class integration above for the best experience.** The MCP JSON generation is useful for advanced use cases, manual configuration, or integration with other tools.
</Note>
You can generate MCP JSON configuration for manual use:
```bash
# Generate configuration and output to stdout
fastmcp install mcp-json server.py --server-name "Dice Roller" --with pandas
# Copy configuration to clipboard for easy pasting
fastmcp install mcp-json server.py --server-name "Dice Roller" --copy
```
This generates the standard `mcpServers` configuration format that can be used with any MCP-compatible client.
### Manual Configuration
For more control over the configuration, you can manually edit Cursor's configuration file. The configuration file is located at:
- **All platforms**: `~/.cursor/mcp.json`
The configuration file is a JSON object with a `mcpServers` key, which contains the configuration for each MCP server.
```json
{
"mcpServers": {
"dice-roller": {
"command": "python",
"args": ["path/to/your/server.py"]
}
}
}
```
After updating the configuration file, your server should be available in Cursor.
#### Dependencies
If your server has dependencies, you can use `uv` or another package manager to set up the environment.
When manually configuring dependencies, the recommended approach is to use `uv` with FastMCP. The configuration should use `uv run` to create an isolated environment with your specified packages:
```json
{
"mcpServers": {
"dice-roller": {
"command": "uv",
"args": [
"run",
"--with", "fastmcp",
"--with", "pandas",
"--with", "requests",
"fastmcp",
"run",
"path/to/your/server.py"
]
}
}
}
```
You can also manually specify Python versions and project directories in your configuration:
```json
{
"mcpServers": {
"dice-roller": {
"command": "uv",
"args": [
"run",
"--python", "3.11",
"--project", "/path/to/project",
"--with", "fastmcp",
"fastmcp",
"run",
"path/to/your/server.py"
]
}
}
}
```
Note that the order of arguments is important: Python version and project settings should come before package specifications.
<Warning>
**`uv` must be installed and available in your system PATH**. Cursor runs in its own isolated environment and needs `uv` to manage dependencies.
</Warning>
#### Environment Variables
You can also specify environment variables in the configuration:
```json
{
"mcpServers": {
"weather-server": {
"command": "python",
"args": ["path/to/weather_server.py"],
"env": {
"API_KEY": "your-api-key",
"DEBUG": "true"
}
}
}
}
```
<Warning>
Cursor runs servers in a completely isolated environment with no access to your shell environment or locally installed applications. You must explicitly pass any environment variables your server needs.
</Warning>
## Using the Server
Once your server is installed, you can start using your FastMCP server with Cursor's AI assistant.
Try asking Cursor something like:
> "Roll some dice for me"
Cursor will automatically detect your `roll_dice` tool and use it to fulfill your request, returning something like:
> 🎲 Here are your dice rolls: 4, 6, 4
>
> You rolled 3 dice with a total of 14! The 6 was a nice high roll there!
The AI assistant can now access all the tools, resources, and prompts you've defined in your FastMCP server.

View file

@ -0,0 +1,146 @@
---
title: Descope 🤝 FastMCP
sidebarTitle: Descope
description: Secure your FastMCP server with Descope
icon: shield-check
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx";
<VersionBadge version="2.12.4" />
This guide shows you how to secure your FastMCP server using [**Descope**](https://www.descope.com), a complete authentication and user management solution. This integration uses the [**Remote OAuth**](/servers/auth/remote-oauth) pattern, where Descope handles user login and your FastMCP server validates the tokens.
## Configuration
### Prerequisites
Before you begin, you will need:
1. To [sign up](https://www.descope.com/sign-up) for a Free Forever Descope account
2. Your FastMCP server's URL (can be localhost for development, e.g., `http://localhost:3000`)
### Step 1: Configure Descope
<Steps>
<Step title="Create an MCP Server">
1. Go to the [MCP Servers page](https://app.descope.com/mcp-servers) of the Descope Console, and create a new MCP Server.
2. Give the MCP server a name and description.
3. Ensure that **Dynamic Client Registration (DCR)** is enabled. Then click **Create**.
4. Once you've created the MCP Server, note your Well-Known URL.
<Warning>
DCR is required for FastMCP clients to automatically register with your authentication server.
</Warning>
</Step>
<Step title="Note Your Well-Known URL">
Save your Well-Known URL from [MCP Server Settings](https://app.descope.com/mcp-servers):
```
Well-Known URL: https://.../v1/apps/agentic/P.../M.../.well-known/openid-configuration
```
</Step>
</Steps>
### Step 2: Environment Setup
Create a `.env` file with your Descope configuration:
```bash
DESCOPE_CONFIG_URL=https://.../v1/apps/agentic/P.../M.../.well-known/openid-configuration # Your Descope Well-Known URL
SERVER_URL=http://localhost:3000 # Your server's base URL
```
### Step 3: FastMCP Configuration
Create your FastMCP server file and use the DescopeProvider to handle all the OAuth integration automatically:
```python server.py
from fastmcp import FastMCP
from fastmcp.server.auth.providers.descope import DescopeProvider
# The DescopeProvider automatically discovers Descope endpoints
# and configures JWT token validation
auth_provider = DescopeProvider(
config_url=https://.../.well-known/openid-configuration, # Your MCP Server .well-known URL
base_url=SERVER_URL, # Your server's public URL
)
# Create FastMCP server with auth
mcp = FastMCP(name="My Descope Protected Server", auth=auth_provider)
```
## Testing
To test your server, you can use the `fastmcp` CLI to run it locally. Assuming you've saved the above code to `server.py` (after replacing the environment variables with your actual values!), you can run the following command:
```bash
fastmcp run server.py --transport http --port 8000
```
Now, you can use a FastMCP client to test that you can reach your server after authenticating:
```python
from fastmcp import Client
import asyncio
async def main():
async with Client("http://localhost:8000/mcp", auth="oauth") as client:
assert await client.ping()
if __name__ == "__main__":
asyncio.run(main())
```
## Environment Variables
For production deployments, use environment variables instead of hardcoding credentials.
### Provider Selection
Setting this environment variable allows the Descope provider to be used automatically without explicitly instantiating it in code.
<Card>
<ParamField path="FASTMCP_SERVER_AUTH" default="Not set">
Set to `fastmcp.server.auth.providers.descope.DescopeProvider` to use
Descope authentication.
</ParamField>
</Card>
### Descope-Specific Configuration
These environment variables provide default values for the Descope provider, whether it's instantiated manually or configured via `FASTMCP_SERVER_AUTH`.
<Card>
<ParamField path="FASTMCP_SERVER_AUTH_DESCOPEPROVIDER_CONFIG_URL" required>
Your Well-Known URL from the [Descope Console](https://app.descope.com/mcp-servers)
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_DESCOPEPROVIDER_BASE_URL" required>
Public URL of your FastMCP server (e.g., `https://your-server.com` or
`http://localhost:8000` for development)
</ParamField>
</Card>
Example `.env` file:
```bash
# Use the Descope provider
FASTMCP_SERVER_AUTH=fastmcp.server.auth.providers.descope.DescopeProvider
# Descope configuration
FASTMCP_SERVER_AUTH_DESCOPEPROVIDER_CONFIG_URL=https://.../v1/apps/agentic/P.../M.../.well-known/openid-configuration
FASTMCP_SERVER_AUTH_DESCOPEPROVIDER_BASE_URL=https://your-server.com
```
With environment variables set, your server code simplifies to:
```python server.py
from fastmcp import FastMCP
# Authentication is automatically configured from environment
mcp = FastMCP(name="My Descope Protected Server")
```

View file

@ -0,0 +1,255 @@
---
title: Discord OAuth 🤝 FastMCP
sidebarTitle: Discord
description: Secure your FastMCP server with Discord OAuth
icon: discord
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.13.2" />
This guide shows you how to secure your FastMCP server using **Discord OAuth**. Since Discord doesn't support Dynamic Client Registration, this integration uses the [**OAuth Proxy**](/servers/auth/oauth-proxy) pattern to bridge Discord's traditional OAuth with MCP's authentication requirements.
## Configuration
### Prerequisites
Before you begin, you will need:
1. A **[Discord Account](https://discord.com/)** with access to create applications
2. Your FastMCP server's URL (can be localhost for development, e.g., `http://localhost:8000`)
### Step 1: Create a Discord Application
Create an application in the Discord Developer Portal to get the credentials needed for authentication:
<Steps>
<Step title="Navigate to Discord Developer Portal">
Go to the [Discord Developer Portal](https://discord.com/developers/applications).
Click **"New Application"** and give it a name users will recognize (e.g., "My FastMCP Server").
</Step>
<Step title="Configure OAuth2 Settings">
In the left sidebar, click **"OAuth2"**.
In the **Redirects** section, click **"Add Redirect"** and enter your callback URL:
- For development: `http://localhost:8000/auth/callback`
- For production: `https://your-domain.com/auth/callback`
<Warning>
The redirect URL must match exactly. The default path is `/auth/callback`, but you can customize it using the `redirect_path` parameter. Discord allows `http://localhost` URLs for development. For production, use HTTPS.
</Warning>
</Step>
<Step title="Save Your Credentials">
On the same OAuth2 page, you'll find:
- **Client ID**: A numeric string like `12345`
- **Client Secret**: Click "Reset Secret" to generate one
<Tip>
Store these credentials securely. Never commit them to version control. Use environment variables or a secrets manager in production.
</Tip>
</Step>
</Steps>
### Step 2: FastMCP Configuration
Create your FastMCP server using the `DiscordProvider`, which handles Discord's OAuth flow automatically:
```python server.py
from fastmcp import FastMCP
from fastmcp.server.auth.providers.discord import DiscordProvider
auth_provider = DiscordProvider(
client_id="12345", # Your Discord Application Client ID
client_secret="your-client-secret", # Your Discord OAuth Client Secret
base_url="http://localhost:8000", # Must match your OAuth configuration
)
mcp = FastMCP(name="Discord Secured App", auth=auth_provider)
@mcp.tool
async def get_user_info() -> dict:
"""Returns information about the authenticated Discord user."""
from fastmcp.server.dependencies import get_access_token
token = get_access_token()
return {
"discord_id": token.claims.get("sub"),
"username": token.claims.get("username"),
"avatar": token.claims.get("avatar"),
}
```
## Testing
### Running the Server
Start your FastMCP server with HTTP transport to enable OAuth flows:
```bash
fastmcp run server.py --transport http --port 8000
```
Your server is now running and protected by Discord OAuth authentication.
### Testing with a Client
Create a test client that authenticates with your Discord-protected server:
```python test_client.py
from fastmcp import Client
import asyncio
async def main():
async with Client("http://localhost:8000/mcp", auth="oauth") as client:
print("✓ Authenticated with Discord!")
result = await client.call_tool("get_user_info")
print(f"Discord user: {result['username']}")
if __name__ == "__main__":
asyncio.run(main())
```
When you run the client for the first time:
1. Your browser will open to Discord's authorization page
2. Sign in with your Discord account and authorize the app
3. After authorization, you'll be redirected back
4. The client receives the token and can make authenticated requests
<Info>
The client caches tokens locally, so you won't need to re-authenticate for subsequent runs unless the token expires or you explicitly clear the cache.
</Info>
## Discord Scopes
Discord OAuth supports several scopes for accessing different types of user data:
| Scope | Description |
|-------|-------------|
| `identify` | Access username, avatar, and discriminator (default) |
| `email` | Access the user's email address |
| `guilds` | Access the user's list of servers |
| `guilds.join` | Ability to add the user to a server |
To request additional scopes:
```python
auth_provider = DiscordProvider(
client_id="...",
client_secret="...",
base_url="http://localhost:8000",
required_scopes=["identify", "email"],
)
```
## Production Configuration
For production deployments with persistent token management across server restarts, configure `jwt_signing_key` and `client_storage`:
```python server.py
import os
from fastmcp import FastMCP
from fastmcp.server.auth.providers.discord import DiscordProvider
from key_value.aio.stores.redis import RedisStore
from key_value.aio.wrappers.encryption import FernetEncryptionWrapper
from cryptography.fernet import Fernet
auth_provider = DiscordProvider(
client_id="12345",
client_secret=os.environ["DISCORD_CLIENT_SECRET"],
base_url="https://your-production-domain.com",
jwt_signing_key=os.environ["JWT_SIGNING_KEY"],
client_storage=FernetEncryptionWrapper(
key_value=RedisStore(
host=os.environ["REDIS_HOST"],
port=int(os.environ["REDIS_PORT"])
),
fernet=Fernet(os.environ["STORAGE_ENCRYPTION_KEY"])
)
)
mcp = FastMCP(name="Production Discord App", auth=auth_provider)
```
<Note>
Parameters (`jwt_signing_key` and `client_storage`) work together to ensure tokens and client registrations survive server restarts. **Wrap your storage in `FernetEncryptionWrapper` to encrypt sensitive OAuth tokens at rest** - without it, tokens are stored in plaintext. Store secrets in environment variables and use a persistent storage backend like Redis for distributed deployments.
For complete details on these parameters, see the [OAuth Proxy documentation](/servers/auth/oauth-proxy#configuration-parameters).
</Note>
## Environment Variables
For production deployments, use environment variables instead of hardcoding credentials.
### Provider Selection
Setting this environment variable allows the Discord provider to be used automatically without explicitly instantiating it in code.
<Card>
<ParamField path="FASTMCP_SERVER_AUTH" default="Not set">
Set to `fastmcp.server.auth.providers.discord.DiscordProvider` to use Discord authentication.
</ParamField>
</Card>
### Discord-Specific Configuration
These environment variables provide default values for the Discord provider, whether it's instantiated manually or configured via `FASTMCP_SERVER_AUTH`.
<Card>
<ParamField path="FASTMCP_SERVER_AUTH_DISCORD_CLIENT_ID" required>
Your Discord Application Client ID (e.g., `12345`)
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_DISCORD_CLIENT_SECRET" required>
Your Discord OAuth Client Secret
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_DISCORD_BASE_URL" default="http://localhost:8000">
Public URL where OAuth endpoints will be accessible (includes any mount path)
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_DISCORD_ISSUER_URL" default="Uses BASE_URL">
Issuer URL for OAuth metadata (defaults to `BASE_URL`). Set to root-level URL when mounting under a path prefix to avoid 404 logs. See [HTTP Deployment guide](/deployment/http#mounting-authenticated-servers) for details.
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_DISCORD_REDIRECT_PATH" default="/auth/callback">
Redirect path configured in your Discord OAuth settings
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_DISCORD_REQUIRED_SCOPES" default='["identify"]'>
Comma-, space-, or JSON-separated list of required Discord scopes (e.g., `identify,email` or `["identify","email"]`)
</ParamField>
<ParamField path="FASTMCP_SERVER_AUTH_DISCORD_TIMEOUT_SECONDS" default="10">
HTTP request timeout for Discord API calls
</ParamField>
</Card>
Example `.env` file:
```bash
FASTMCP_SERVER_AUTH=fastmcp.server.auth.providers.discord.DiscordProvider
FASTMCP_SERVER_AUTH_DISCORD_CLIENT_ID=12345
FASTMCP_SERVER_AUTH_DISCORD_CLIENT_SECRET=your-client-secret
FASTMCP_SERVER_AUTH_DISCORD_BASE_URL=https://your-server.com
FASTMCP_SERVER_AUTH_DISCORD_REQUIRED_SCOPES=identify,email
```
With environment variables set, your server code simplifies to:
```python server.py
from fastmcp import FastMCP
mcp = FastMCP(name="Discord Secured App")
@mcp.tool
async def protected_tool(query: str) -> str:
"""A tool that requires Discord authentication to access."""
return f"Processing authenticated request: {query}"
```

View file

@ -0,0 +1,129 @@
---
title: Eunomia Authorization 🤝 FastMCP
sidebarTitle: Eunomia Auth
description: Add policy-based authorization to your FastMCP servers with Eunomia
icon: shield-check
---
Add **policy-based authorization** to your FastMCP servers with one-line code addition with the **[Eunomia][eunomia-github] authorization middleware**.
Control which tools, resources and prompts MCP clients can view and execute on your server. Define dynamic JSON-based policies and obtain a comprehensive audit log of all access attempts and violations.
## How it Works
Exploiting FastMCP's [Middleware][fastmcp-middleare], the Eunomia middleware intercepts all MCP requests to your server and automatically maps MCP methods to authorization checks.
### Listing Operations
The middleware behaves as a filter for listing operations (`tools/list`, `resources/list`, `prompts/list`), hiding to the client components that are not authorized by the defined policies.
```mermaid
sequenceDiagram
participant MCPClient as MCP Client
participant EunomiaMiddleware as Eunomia Middleware
participant MCPServer as FastMCP Server
participant EunomiaServer as Eunomia Server
MCPClient->>EunomiaMiddleware: MCP Listing Request (e.g., tools/list)
EunomiaMiddleware->>MCPServer: MCP Listing Request
MCPServer-->>EunomiaMiddleware: MCP Listing Response
EunomiaMiddleware->>EunomiaServer: Authorization Checks
EunomiaServer->>EunomiaMiddleware: Authorization Decisions
EunomiaMiddleware-->>MCPClient: Filtered MCP Listing Response
```
### Execution Operations
The middleware behaves as a firewall for execution operations (`tools/call`, `resources/read`, `prompts/get`), blocking operations that are not authorized by the defined policies.
```mermaid
sequenceDiagram
participant MCPClient as MCP Client
participant EunomiaMiddleware as Eunomia Middleware
participant MCPServer as FastMCP Server
participant EunomiaServer as Eunomia Server
MCPClient->>EunomiaMiddleware: MCP Execution Request (e.g., tools/call)
EunomiaMiddleware->>EunomiaServer: Authorization Check
EunomiaServer->>EunomiaMiddleware: Authorization Decision
EunomiaMiddleware-->>MCPClient: MCP Unauthorized Error (if denied)
EunomiaMiddleware->>MCPServer: MCP Execution Request (if allowed)
MCPServer-->>EunomiaMiddleware: MCP Execution Response (if allowed)
EunomiaMiddleware-->>MCPClient: MCP Execution Response (if allowed)
```
## Add Authorization to Your Server
<Note>
Eunomia is an AI-specific authorization server that handles policy decisions. The server runs embedded within your MCP server by default for a zero-effort configuration, but can alternatively be run remotely for centralized policy decisions.
</Note>
### Create a Server with Authorization
First, install the `eunomia-mcp` package:
```bash
pip install eunomia-mcp
```
Then create a FastMCP server and add the Eunomia middleware in one line:
```python server.py
from fastmcp import FastMCP
from eunomia_mcp import create_eunomia_middleware
# Create your FastMCP server
mcp = FastMCP("Secure MCP Server 🔒")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
# Add middleware to your server
middleware = create_eunomia_middleware(policy_file="mcp_policies.json")
mcp.add_middleware(middleware)
if __name__ == "__main__":
mcp.run()
```
### Configure Access Policies
Use the `eunomia-mcp` CLI in your terminal to manage your authorization policies:
```bash
# Create a default policy file
eunomia-mcp init
# Or create a policy file customized for your FastMCP server
eunomia-mcp init --custom-mcp "app.server:mcp"
```
This creates `mcp_policies.json` file that you can further edit to your access control needs.
```bash
# Once edited, validate your policy file
eunomia-mcp validate mcp_policies.json
```
### Run the Server
Start your FastMCP server normally:
```bash
python server.py
```
The middleware will now intercept all MCP requests and check them against your policies. Requests include agent identification through headers like `X-Agent-ID`, `X-User-ID`, `User-Agent`, or `Authorization` and an automatic mapping of MCP methods to authorization resources and actions.
<Tip>
For detailed policy configuration, custom authentication, and remote
deployments, visit the [Eunomia MCP Middleware
repository][eunomia-mcp-github].
</Tip>
[eunomia-github]: https://github.com/whataboutyou-ai/eunomia
[eunomia-mcp-github]: https://github.com/whataboutyou-ai/eunomia/tree/main/pkgs/extensions/mcp
[fastmcp-middleare]: /servers/middleware

View file

@ -0,0 +1,451 @@
---
title: FastAPI 🤝 FastMCP
sidebarTitle: FastAPI
description: Integrate FastMCP with FastAPI applications
icon: bolt
---
import { VersionBadge } from '/snippets/version-badge.mdx'
FastMCP provides two powerful ways to integrate with FastAPI applications:
1. **[Generate an MCP server FROM your FastAPI app](#generating-an-mcp-server)** - Convert existing API endpoints into MCP tools
2. **[Mount an MCP server INTO your FastAPI app](#mounting-an-mcp-server)** - Add MCP functionality to your web application
<Tip>
Generating MCP servers from OpenAPI is a great way to get started with FastMCP, but in practice LLMs achieve **significantly better performance** with well-designed and curated MCP servers than with auto-converted OpenAPI servers. This is especially true for complex APIs with many endpoints and parameters.
We recommend using the FastAPI integration for bootstrapping and prototyping, not for mirroring your API to LLM clients. See the post [Stop Converting Your REST APIs to MCP](https://www.jlowin.dev/blog/stop-converting-rest-apis-to-mcp) for more details.
</Tip>
<Note>
FastMCP does *not* include FastAPI as a dependency; you must install it separately to use this integration.
</Note>
## Example FastAPI Application
Throughout this guide, we'll use this e-commerce API as our example (click the `Copy` button to copy it for use with other code blocks):
```python [expandable]
# Copy this FastAPI server into other code blocks in this guide
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
# Models
class Product(BaseModel):
name: str
price: float
category: str
description: str | None = None
class ProductResponse(BaseModel):
id: int
name: str
price: float
category: str
description: str | None = None
# Create FastAPI app
app = FastAPI(title="E-commerce API", version="1.0.0")
# In-memory database
products_db = {
1: ProductResponse(
id=1, name="Laptop", price=999.99, category="Electronics"
),
2: ProductResponse(
id=2, name="Mouse", price=29.99, category="Electronics"
),
3: ProductResponse(
id=3, name="Desk Chair", price=299.99, category="Furniture"
),
}
next_id = 4
@app.get("/products", response_model=list[ProductResponse])
def list_products(
category: str | None = None,
max_price: float | None = None,
) -> list[ProductResponse]:
"""List all products with optional filtering."""
products = list(products_db.values())
if category:
products = [p for p in products if p.category == category]
if max_price:
products = [p for p in products if p.price <= max_price]
return products
@app.get("/products/{product_id}", response_model=ProductResponse)
def get_product(product_id: int):
"""Get a specific product by ID."""
if product_id not in products_db:
raise HTTPException(status_code=404, detail="Product not found")
return products_db[product_id]
@app.post("/products", response_model=ProductResponse)
def create_product(product: Product):
"""Create a new product."""
global next_id
product_response = ProductResponse(id=next_id, **product.model_dump())
products_db[next_id] = product_response
next_id += 1
return product_response
@app.put("/products/{product_id}", response_model=ProductResponse)
def update_product(product_id: int, product: Product):
"""Update an existing product."""
if product_id not in products_db:
raise HTTPException(status_code=404, detail="Product not found")
products_db[product_id] = ProductResponse(
id=product_id,
**product.model_dump(),
)
return products_db[product_id]
@app.delete("/products/{product_id}")
def delete_product(product_id: int):
"""Delete a product."""
if product_id not in products_db:
raise HTTPException(status_code=404, detail="Product not found")
del products_db[product_id]
return {"message": "Product deleted"}
```
<Tip>
All subsequent code examples in this guide assume you have the above FastAPI application code already defined. Each example builds upon this base application, `app`.
</Tip>
## Generating an MCP Server
<VersionBadge version="2.0.0" />
One of the most common ways to bootstrap an MCP server is to generate it from an existing FastAPI application. FastMCP will expose your FastAPI endpoints as MCP components (tools, by default) in order to expose your API to LLM clients.
### Basic Conversion
Convert the FastAPI app to an MCP server with a single line:
```python {5}
# Assumes the FastAPI app from above is already defined
from fastmcp import FastMCP
# Convert to MCP server
mcp = FastMCP.from_fastapi(app=app)
if __name__ == "__main__":
mcp.run()
```
### Adding Components
Your converted MCP server is a full FastMCP instance, meaning you can add new tools, resources, and other components to it just like you would with any other FastMCP instance.
```python {8-11}
# Assumes the FastAPI app from above is already defined
from fastmcp import FastMCP
# Convert to MCP server
mcp = FastMCP.from_fastapi(app=app)
# Add a new tool
@mcp.tool
def get_product(product_id: int) -> ProductResponse:
"""Get a product by ID."""
return products_db[product_id]
# Run the MCP server
if __name__ == "__main__":
mcp.run()
```
### Interacting with the MCP Server
Once you've converted your FastAPI app to an MCP server, you can interact with it using the FastMCP client to test functionality before deploying it to an LLM-based application.
```python {3, }
# Assumes the FastAPI app from above is already defined
from fastmcp import FastMCP
from fastmcp.client import Client
import asyncio
# Convert to MCP server
mcp = FastMCP.from_fastapi(app=app)
async def demo():
async with Client(mcp) as client:
# List available tools
tools = await client.list_tools()
print(f"Available tools: {[t.name for t in tools]}")
# Create a product
result = await client.call_tool(
"create_product_products_post",
{
"name": "Wireless Keyboard",
"price": 79.99,
"category": "Electronics",
"description": "Bluetooth mechanical keyboard"
}
)
print(f"Created product: {result.data}")
# List electronics under $100
result = await client.call_tool(
"list_products_products_get",
{"category": "Electronics", "max_price": 100}
)
print(f"Affordable electronics: {result.data}")
if __name__ == "__main__":
asyncio.run(demo())
```
### Custom Route Mapping
Because FastMCP's FastAPI integration is based on its [OpenAPI integration](/integrations/openapi), you can customize how endpoints are converted to MCP components in exactly the same way. For example, here we use a `RouteMap` to map all GET requests to MCP resources, and all POST/PUT/DELETE requests to MCP tools:
```python
# Assumes the FastAPI app from above is already defined
from fastmcp import FastMCP
from fastmcp.server.openapi import RouteMap, MCPType
# Custom mapping rules
mcp = FastMCP.from_fastapi(
app=app,
route_maps=[
# GET with path params → ResourceTemplates
RouteMap(
methods=["GET"],
pattern=r".*\{.*\}.*",
mcp_type=MCPType.RESOURCE_TEMPLATE
),
# Other GETs → Resources
RouteMap(
methods=["GET"],
pattern=r".*",
mcp_type=MCPType.RESOURCE
),
# POST/PUT/DELETE → Tools (default)
],
)
# Now:
# - GET /products → Resource
# - GET /products/{id} → ResourceTemplate
# - POST/PUT/DELETE → Tools
```
<Tip>
To learn more about customizing the conversion process, see the [OpenAPI Integration guide](/integrations/openapi).
</Tip>
### Authentication and Headers
You can configure headers and other client options via the `httpx_client_kwargs` parameter. For example, to add authentication to your FastAPI app, you can pass a `headers` dictionary to the `httpx_client_kwargs` parameter:
```python {27-31}
# Assumes the FastAPI app from above is already defined
from fastmcp import FastMCP
# Add authentication to your FastAPI app
from fastapi import Depends, Header
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
security = HTTPBearer()
def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
if credentials.credentials != "secret-token":
raise HTTPException(status_code=401, detail="Invalid authentication")
return credentials.credentials
# Add a protected endpoint
@app.get("/admin/stats", dependencies=[Depends(verify_token)])
def get_admin_stats():
return {
"total_products": len(products_db),
"categories": list(set(p.category for p in products_db.values()))
}
# Create MCP server with authentication headers
mcp = FastMCP.from_fastapi(
app=app,
httpx_client_kwargs={
"headers": {
"Authorization": "Bearer secret-token",
}
}
)
```
## Mounting an MCP Server
<VersionBadge version="2.3.1" />
In addition to generating servers, FastMCP can facilitate adding MCP servers to your existing FastAPI application. You can do this by mounting the MCP ASGI application.
### Basic Mounting
To mount an MCP server, you can use the `http_app` method on your FastMCP instance. This will return an ASGI application that can be mounted to your FastAPI application.
```python {23-30}
from fastmcp import FastMCP
from fastapi import FastAPI
# Create MCP server
mcp = FastMCP("Analytics Tools")
@mcp.tool
def analyze_pricing(category: str) -> dict:
"""Analyze pricing for a category."""
products = [p for p in products_db.values() if p.category == category]
if not products:
return {"error": f"No products in {category}"}
prices = [p.price for p in products]
return {
"category": category,
"avg_price": round(sum(prices) / len(prices), 2),
"min": min(prices),
"max": max(prices),
}
# Create ASGI app from MCP server
mcp_app = mcp.http_app(path='/mcp')
# Key: Pass lifespan to FastAPI
app = FastAPI(title="E-commerce API", lifespan=mcp_app.lifespan)
# Mount the MCP server
app.mount("/analytics", mcp_app)
# Now: API at /products/*, MCP at /analytics/mcp/
```
## Offering an LLM-Friendly API
A common pattern is to generate an MCP server from your FastAPI app and serve both interfaces from the same application. This provides an LLM-optimized interface alongside your regular API:
```python
# Assumes the FastAPI app from above is already defined
from fastmcp import FastMCP
from fastapi import FastAPI
# 1. Generate MCP server from your API
mcp = FastMCP.from_fastapi(app=app, name="E-commerce MCP")
# 2. Create the MCP's ASGI app
mcp_app = mcp.http_app(path='/mcp')
# 3. Create a new FastAPI app that combines both sets of routes
combined_app = FastAPI(
title="E-commerce API with MCP",
routes=[
*mcp_app.routes, # MCP routes
*app.routes, # Original API routes
],
lifespan=mcp_app.lifespan,
)
# Now you have:
# - Regular API: http://localhost:8000/products
# - LLM-friendly MCP: http://localhost:8000/mcp
# Both served from the same FastAPI application!
```
This approach lets you maintain a single codebase while offering both traditional REST endpoints and MCP-compatible endpoints for LLM clients.
## Key Considerations
### Operation IDs
FastAPI operation IDs become MCP component names. Always specify meaningful operation IDs:
```python
# Good - explicit operation_id
@app.get("/users/{user_id}", operation_id="get_user_by_id")
def get_user(user_id: int):
return {"id": user_id}
# Less ideal - auto-generated name
@app.get("/users/{user_id}")
def get_user(user_id: int):
return {"id": user_id}
```
### Lifespan Management
When mounting MCP servers, always pass the lifespan context:
```python
# Correct - lifespan passed
mcp_app = mcp.http_app(path='/mcp')
app = FastAPI(lifespan=mcp_app.lifespan)
app.mount("/mcp", mcp_app)
# Incorrect - missing lifespan
app = FastAPI()
app.mount("/mcp", mcp.http_app()) # Session manager won't initialize
```
If you're mounting an authenticated MCP server under a path prefix, see [Mounting Authenticated Servers](/deployment/http#mounting-authenticated-servers) for important OAuth routing considerations.
### CORS Middleware
If your FastAPI app uses `CORSMiddleware` and you're mounting an OAuth-protected FastMCP server, avoid adding application-wide CORS middleware. FastMCP and the MCP SDK already handle CORS for OAuth routes, and layering CORS middleware can cause conflicts (such as 404 errors on `.well-known` routes or OPTIONS requests).
If you need CORS on your own FastAPI routes, use the sub-app pattern: mount your API and FastMCP as separate apps, each with their own middleware, rather than adding top-level `CORSMiddleware` to the combined application.
### Combining Lifespans
If your FastAPI app already has a lifespan (for database connections, startup tasks, etc.), you can't simply replace it with the MCP lifespan. Instead, you need to create a new lifespan function that manages both contexts. This ensures that both your app's initialization logic and the MCP server's session manager run properly:
```python
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastmcp import FastMCP
# Your existing lifespan
@asynccontextmanager
async def app_lifespan(app: FastAPI):
# Startup
print("Starting up the app...")
# Initialize database, cache, etc.
yield
# Shutdown
print("Shutting down the app...")
# Create MCP server
mcp = FastMCP("Tools")
mcp_app = mcp.http_app(path='/mcp')
# Combine both lifespans
@asynccontextmanager
async def combined_lifespan(app: FastAPI):
# Run both lifespans
async with app_lifespan(app):
async with mcp_app.lifespan(app):
yield
# Use the combined lifespan
app = FastAPI(lifespan=combined_lifespan)
app.mount("/mcp", mcp_app)
```
This pattern ensures both your app's initialization logic and the MCP server's session manager are properly managed. The key is using nested `async with` statements - the inner context (MCP) will be initialized after the outer context (your app), and cleaned up before it. This maintains the correct initialization and cleanup order for all your resources.
### Performance Tips
1. **Use in-memory transport for testing** - Pass MCP servers directly to clients
2. **Design purpose-built MCP tools** - Better than auto-converting complex APIs
3. **Keep tool parameters simple** - LLMs perform better with focused interfaces
For more details on configuration options, see the [OpenAPI Integration guide](/integrations/openapi).

View file

@ -0,0 +1,174 @@
---
title: Gemini CLI 🤝 FastMCP
sidebarTitle: Gemini CLI
description: Install and use FastMCP servers in Gemini CLI
icon: message-smile
tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx"
import { LocalFocusTip } from "/snippets/local-focus.mdx"
<LocalFocusTip />
Gemini CLI supports MCP servers through multiple transport methods including STDIO, SSE, and HTTP, allowing you to extend Gemini's capabilities with custom tools, resources, and prompts from your FastMCP servers.
## Requirements
This integration uses STDIO transport to run your FastMCP server locally. For remote deployments, you can run your FastMCP server with HTTP or SSE transport and configure it directly using Gemini CLI's built-in MCP management commands.
## Create a Server
The examples in this guide will use the following simple dice-rolling server, saved as `server.py`.
```python server.py
import random
from fastmcp import FastMCP
mcp = FastMCP(name="Dice Roller")
@mcp.tool
def roll_dice(n_dice: int) -> list[int]:
"""Roll `n_dice` 6-sided dice and return the results."""
return [random.randint(1, 6) for _ in range(n_dice)]
if __name__ == "__main__":
mcp.run()
```
## Install the Server
### FastMCP CLI
<VersionBadge version="2.13.0" />
The easiest way to install a FastMCP server in Gemini CLI is using the `fastmcp install gemini-cli` command. This automatically handles the configuration, dependency management, and calls Gemini CLI's built-in MCP management system.
```bash
fastmcp install gemini-cli server.py
```
The install command supports the same `file.py:object` notation as the `run` command. If no object is specified, it will automatically look for a FastMCP server object named `mcp`, `server`, or `app` in your file:
```bash
# These are equivalent if your server object is named 'mcp'
fastmcp install gemini-cli server.py
fastmcp install gemini-cli server.py:mcp
# Use explicit object name if your server has a different name
fastmcp install gemini-cli server.py:my_custom_server
```
The command will automatically configure the server with Gemini CLI's `gemini mcp add` command.
#### Dependencies
FastMCP provides flexible dependency management options for your Gemini CLI servers:
**Individual packages**: Use the `--with` flag to specify packages your server needs. You can use this flag multiple times:
```bash
fastmcp install gemini-cli server.py --with pandas --with requests
```
**Requirements file**: If you maintain a `requirements.txt` file with all your dependencies, use `--with-requirements` to install them:
```bash
fastmcp install gemini-cli server.py --with-requirements requirements.txt
```
**Editable packages**: For local packages under development, use `--with-editable` to install them in editable mode:
```bash
fastmcp install gemini-cli server.py --with-editable ./my-local-package
```
Alternatively, you can use a `fastmcp.json` configuration file (recommended):
```json fastmcp.json
{
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
"source": {
"path": "server.py",
"entrypoint": "mcp"
},
"environment": {
"dependencies": ["pandas", "requests"]
}
}
```
#### Python Version and Project Configuration
Control the Python environment for your server with these options:
**Python version**: Use `--python` to specify which Python version your server requires. This ensures compatibility when your server needs specific Python features:
```bash
fastmcp install gemini-cli server.py --python 3.11
```
**Project directory**: Use `--project` to run your server within a specific project context. This tells `uv` to use the project's configuration files and virtual environment:
```bash
fastmcp install gemini-cli server.py --project /path/to/my-project
```
#### Environment Variables
If your server needs environment variables (like API keys), you must include them:
```bash
fastmcp install gemini-cli server.py --server-name "Weather Server" \
--env API_KEY=your-api-key \
--env DEBUG=true
```
Or load them from a `.env` file:
```bash
fastmcp install gemini-cli server.py --server-name "Weather Server" --env-file .env
```
<Warning>
**Gemini CLI must be installed**. The integration looks for the Gemini CLI and uses the `gemini mcp add` command to register servers.
</Warning>
### Manual Configuration
For more control over the configuration, you can manually use Gemini CLI's built-in MCP management commands. This gives you direct control over how your server is launched:
```bash
# Add a server with custom configuration
gemini mcp add dice-roller uv -- run --with fastmcp fastmcp run server.py
# Add with environment variables
gemini mcp add weather-server -e API_KEY=secret -e DEBUG=true uv -- run --with fastmcp fastmcp run server.py
# Add with specific scope (user, or project)
gemini mcp add my-server --scope user uv -- run --with fastmcp fastmcp run server.py
```
You can also manually specify Python versions and project directories in your Gemini CLI commands:
```bash
# With specific Python version
gemini mcp add ml-server uv -- run --python 3.11 --with fastmcp fastmcp run server.py
# Within a project directory
gemini mcp add project-server uv -- run --project /path/to/project --with fastmcp fastmcp run server.py
```
## Using the Server
Once your server is installed, you can start using your FastMCP server with Gemini CLI.
Try asking Gemini something like:
> "Roll some dice for me"
Gemini will automatically detect your `roll_dice` tool and use it to fulfill your request.
Gemini CLI can now access all the tools and prompts you've defined in your FastMCP server.
If your server provides prompts, you can use them as slash commands with `/prompt_name`.

Some files were not shown because too many files have changed in this diff Show more