docs/v2/servers/server.mdx
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.
Instantiating a server is straightforward. You typically provide a name for your server, which helps identify it in client applications or logs.
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
mcp_with_instructions = FastMCP(
name="HelpfulAssistant",
instructions="""
This server provides data analysis tools.
Call get_average() to analyze numerical data.
""",
)
The FastMCP constructor accepts several arguments:
URL to a website with more information about your server. Displayed in client applications </ParamField>
<ParamField body="icons" type="list[Icon] | None"> <VersionBadge version="2.13.0" />List of icon representations for your server. Icons help users visually identify your server in client applications. See Icons for detailed examples </ParamField>
<ParamField body="auth" type="OAuthProvider | TokenVerifier | None"> Authentication provider for securing HTTP-based transports. See [Authentication](/v2/servers/auth/authentication) for configuration options </ParamField> <ParamField body="lifespan" type="AsyncContextManager | None"> An async context manager function for server startup and shutdown logic </ParamField> <ParamField body="tools" type="list[Tool | Callable] | None"> A list of tools (or functions to convert to tools) to add to the server. In some cases, providing tools programmatically may be more convenient than using the `@mcp.tool` decorator </ParamField> <ParamField body="include_tags" type="set[str] | None"> Only expose components with at least one matching tag </ParamField> <ParamField body="exclude_tags" type="set[str] | None"> Hide components with any matching tag </ParamField> <ParamField body="on_duplicate_tools" type='Literal["error", "warn", "replace"]' default="error"> How to handle duplicate tool registrations </ParamField> <ParamField body="on_duplicate_resources" type='Literal["error", "warn", "replace"]' default="warn"> How to handle duplicate resource registrations </ParamField> <ParamField body="on_duplicate_prompts" type='Literal["error", "warn", "replace"]' default="replace"> How to handle duplicate prompt registrations </ParamField> <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](/v2/servers/tools#input-validation-modes) for details </ParamField> <ParamField body="include_fastmcp_meta" type="bool" default="True"> <VersionBadge version="2.11.0" />Whether to include FastMCP metadata in component responses. When True, component tags and other FastMCP-specific metadata are included in the _fastmcp namespace within each component's meta field. When False, this metadata is omitted, resulting in cleaner integration with external systems. Can be overridden globally via FASTMCP_INCLUDE_FASTMCP_META environment variable
</ParamField>
</Card>
FastMCP servers expose several types of components to the client:
Tools are functions that the client can call to perform actions or access external systems.
@mcp.tool
def multiply(a: float, b: float) -> float:
"""Multiplies two numbers together."""
return a * b
See Tools for detailed documentation.
Resources expose data sources that the client can read.
@mcp.resource("data://config")
def get_config() -> dict:
"""Provides the application configuration."""
return {"theme": "dark", "version": "1.0"}
See Resources & Templates for detailed documentation.
Resource templates are parameterized resources that allow the client to request specific data.
@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 for detailed documentation.
Prompts are reusable message templates for guiding the LLM.
@mcp.prompt
def analyze_data(data_points: list[float]) -> str:
"""Creates a prompt asking for analysis of numerical data."""
formatted_data = ", ".join(str(point) for point in data_points)
return f"Please analyze these data points: {formatted_data}"
See Prompts for detailed documentation.
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.
Components can be tagged when defined using the tags parameter:
@mcp.tool(tags={"public", "utility"})
def public_tool() -> str:
return "This tool is public"
@mcp.tool(tags={"internal", "admin"})
def admin_tool() -> str:
return "This tool is for admins only"
The filtering logic works as follows:
You configure tag-based filtering when creating your server:
# Only expose components tagged with "public"
mcp = FastMCP(include_tags={"public"})
# Hide components tagged as "internal" or "deprecated"
mcp = FastMCP(exclude_tags={"internal", "deprecated"})
# Combine both: show admin tools but hide deprecated ones
mcp = FastMCP(include_tags={"admin"}, exclude_tags={"deprecated"})
This filtering applies to all component types (tools, resources, resource templates, and prompts) and affects both listing and access.
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.
# my_server.py
from fastmcp import FastMCP
mcp = FastMCP(name="MyServer")
@mcp.tool
def greet(name: str) -> str:
"""Greet a user by name."""
return f"Hello, {name}!"
if __name__ == "__main__":
# This runs the server, defaulting to STDIO transport
mcp.run()
# To use a different transport, e.g., HTTP:
# mcp.run(transport="http", host="127.0.0.1", port=9000)
FastMCP supports several transport options:
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 guide.
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:
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")
if __name__ == "__main__":
mcp.run(transport="http") # Health check at http://localhost:8000/health
Custom routes are served alongside your MCP endpoint and are useful for:
For more complex web applications, consider mounting your MCP server into a FastAPI or Starlette app.
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 Server Composition guide for full details, best practices, and examples.
# 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, prefix="sub")
FastMCP can act as a proxy for any MCP server (local or remote) using FastMCP.as_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 Proxying Servers guide for details and advanced usage.
from fastmcp import FastMCP, Client
backend = Client("http://example.com/mcp/sse")
proxy = FastMCP.as_proxy(backend, name="ProxyServer")
# Now use the proxy like any FastMCP server
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 and OpenAPI Integration guides for detailed examples and configuration options.
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)
Servers can be configured using a combination of initialization arguments, global settings, and transport-specific settings.
Server-specific settings are passed when creating the FastMCP instance and control server behavior:
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",
include_fastmcp_meta=False, # Disable FastMCP metadata for cleaner integration
)
Global settings affect all FastMCP servers and can be configured via environment variables (prefixed with FASTMCP_) or in a .env file:
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
print(fastmcp.settings.include_fastmcp_meta) # Default: True
Common global settings include:
log_level: Logging level ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"), set with FASTMCP_LOG_LEVELmask_error_details: Whether to hide detailed error information from clients, set with FASTMCP_MASK_ERROR_DETAILSstrict_input_validation: Controls tool input validation mode (default: False for flexible coercion), set with FASTMCP_STRICT_INPUT_VALIDATION. See Input Validation Modesinclude_fastmcp_meta: Whether to include FastMCP metadata in component responses (default: True), set with FASTMCP_INCLUDE_FASTMCP_METAenv_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-dotenvTransport settings are provided when running the server and control network behavior:
# 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,
)
Global FastMCP settings can be configured via environment variables (prefixed with FASTMCP_):
# Configure global FastMCP behavior
export FASTMCP_LOG_LEVEL=DEBUG
export FASTMCP_MASK_ERROR_DETAILS=True
export FASTMCP_STRICT_INPUT_VALIDATION=False
export FASTMCP_INCLUDE_FASTMCP_META=False
By default, FastMCP serializes tool return values to JSON when they need to be converted to text. You can customize this behavior by providing a tool_serializer function when creating your server:
import yaml
from fastmcp import FastMCP
# Define a custom serializer that formats dictionaries as YAML
def yaml_serializer(data):
return yaml.dump(data, sort_keys=False)
# Create a server with the custom serializer
mcp = FastMCP(name="MyServer", tool_serializer=yaml_serializer)
@mcp.tool
def get_config():
"""Returns configuration in YAML format."""
return {"api_key": "abc123", "debug": True, "rate_limit": 100}
The serializer function takes any data object and returns a string representation. This is applied to all non-string return values from your tools. Tools that already return strings bypass the serializer.
This customization is useful when you want to: