name: fastmcp-mcp-builder description: Guide for creating high-quality MCP (Model Context Protocol) servers using FastMCP, the Pythonic MCP framework. Use when building MCP servers to integrate external APIs or services with tools, resources, and prompts using FastMCP decorators. Includes advanced features like structured outputs, tag-based filtering, dependency injection, custom routes, and CLI development workflows. license: Complete terms in LICENSE.txt
FastMCP Server Development Guide
Overview
Create MCP (Model Context Protocol) servers using FastMCP, the standard Pythonic framework for building MCP applications. FastMCP provides a clean, Pythonic API for registering tools, resources, and prompts, with automatic schema generation and transport management. FastMCP 1.0 was incorporated into the official MCP Python SDK in 2024, and today some version of FastMCP powers 70% of MCP servers across all languages.
Process
๐ High-Level Workflow
Creating a high-quality FastMCP server involves four main phases:
Phase 1: Deep Research and Planning
1.1 Understand FastMCP Design
Three Pillars of FastMCP:
- Servers: Wrap your Python functions into MCP-compliant tools, resources, and prompts
- Clients: Connect to any server with full protocol support
- Apps: Give your tools interactive UIs rendered directly in the conversation
Tools, Resources, and Prompts: FastMCP servers expose three types of capabilities:
- Tools: Server-side functions that clients can execute with arguments
- Resources: Data sources that clients can read (static or template-based)
- Prompts: Reusable message templates that guide LLM interactions
Balance comprehensive API coverage with specialized workflow tools. When uncertain, prioritize comprehensive API coverage.
Component Naming:
- Tools: snake_case with service prefix (e.g.,
github_create_issue,slack_send_message) - Resources: URI-based with template parameters (e.g.,
user://{user_id}/profile) - Prompts: snake_case function names (e.g.,
analyze_data)
Context Management:
FastMCP provides a Context object for logging, progress reporting, resource access, and LLM sampling. Use it to enhance tool capabilities.
Actionable Error Messages: Error messages should guide agents toward solutions with specific suggestions and next steps.
1.2 Study FastMCP Documentation
Key Resources: FastMCP documentation is available in multiple LLM-friendly formats:
- MCP Server: Accessible at https://gofastmcp.com/mcp
- Text Formats: llms.txt (sitemap), llms-full.txt (complete docs)
- Markdown: Any page can be accessed by appending .md (e.g., https://gofastmcp.com/getting-started/welcome.md)
Load these documentation pages as needed:
- Welcome & Quickstart: Use WebFetch to load
https://gofastmcp.com/getting-started/welcomeandhttps://gofastmcp.com/getting-started/quickstart - Server Guide:
https://gofastmcp.com/servers/server - Tools:
https://gofastmcp.com/servers/tools - Resources:
https://gofastmcp.com/servers/resources - Prompts:
https://gofastmcp.com/servers/prompts - Running Servers:
https://gofastmcp.com/deployment/running-server
1.3 Plan Your Implementation
Understand the API: Review the service's API documentation to identify key endpoints, authentication requirements, and data models. Use web search and WebFetch as needed.
Capability Selection: Prioritize comprehensive API coverage. List which endpoints to implement as tools, which data to expose as resources, and what prompt templates would be useful.
Phase 2: Implementation
2.1 Set Up Project Structure
Create the following structure for FastMCP servers:
{service}_mcp/
โโโ {service}_mcp.py # Main entry point with FastMCP initialization
โโโ requirements.txt # Dependencies (fastmcp)
โโโ README.md # Documentation
For larger projects, you can organize into modules:
{service}_mcp/
โโโ __init__.py
โโโ main.py # FastMCP server initialization
โโโ tools/ # Tool implementations
โ โโโ __init__.py
โ โโโ users.py
โ โโโ projects.py
โโโ resources/ # Resource definitions
โ โโโ __init__.py
โ โโโ data.py
โโโ prompts/ # Prompt templates
โ โโโ __init__.py
โ โโโ analysis.py
โโโ requirements.txt
โโโ README.md
2.2 Implement Core Infrastructure
Server Initialization
from fastmcp import FastMCP
# Basic initialization
mcp = FastMCP(name="service_name_mcp")
# With instructions for clients
mcp = FastMCP(
name="HelpfulAssistant",
instructions="""
This server provides data analysis tools.
Call get_average() to analyze numerical data.
""",
)
# With security and validation settings
mcp = FastMCP(
name="SecureServer",
strict_input_validation=False, # Default: flexible type coercion
mask_error_details=True, # Mask internal error details
on_duplicate_tools="error", # "warn", "error", "replace", "ignore"
on_duplicate_resources="error" # Same options as tools
)
# Add tools, resources, and prompts here...
if __name__ == "__main__":
mcp.run() # Uses stdio transport by default
Tag-Based Filtering
New in version 2.8.0, tags let you categorize components and selectively expose them:
from fastmcp import FastMCP
mcp = FastMCP()
@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"
# Only expose components tagged with "public"
mcp.enable(tags={"public"}, only=True)
# Hide components tagged as "internal" or "deprecated"
mcp.disable(tags={"internal", "deprecated"})
# Combine both: show admin tools but hide deprecated ones
mcp.enable(tags={"admin"}, only=True).disable(tags={"deprecated"})
Shared Utilities
Create shared utilities for common operations:
- API client with authentication
- Error handling helpers
- Response formatting
- Pagination support
2.3 Implement Tools
Tools are Python functions decorated with @mcp.tool. FastMCP automatically:
- Uses the function name as the tool name
- Uses the function's docstring as the tool description
- Generates an input schema from function parameters and type annotations
- Handles parameter validation and error reporting
Basic Tool Pattern:
@mcp.tool
def add(a: int, b: int) -> int:
"""Adds two integer numbers together."""
return a + b
Tool with Custom Name and Annotations:
@mcp.tool(
name="service_search_users",
description="Search for users in the service by name or email",
tags={"users", "search"},
meta={"version": "1.2", "author": "product-team"},
annotations={
"title": "Search Users",
"readOnlyHint": True,
"destructiveHint": False,
"idempotentHint": True,
"openWorldHint": True
},
timeout=30.0 # New in 3.0.0: Tool timeout in seconds
)
async def search_users(query: str, limit: int = 20) -> list[dict]:
"""Search for users with pagination support."""
# Implementation here
pass
Simple Parameter Descriptions (New in 2.11.0):
from typing import Annotated
@mcp.tool
def process_image(
image_url: Annotated[str, "URL of the image to process"],
resize: Annotated[bool, "Whether to resize the image"] = False,
width: Annotated[int, "Target width in pixels"] = 800,
format: Annotated[str, "Output image format"] = "jpeg"
) -> dict:
"""Process an image with optional resizing."""
# Implementation...
pass
Advanced Metadata with Field:
from typing import Annotated
from pydantic import Field
from typing import Literal
@mcp.tool
def process_image(
image_url: Annotated[str, Field(description="URL of the image to process")],
resize: Annotated[bool, Field(description="Whether to resize the image")] = False,
width: Annotated[int, Field(description="Target width in pixels", ge=1, le=2000)] = 800,
format: Annotated[
Literal["jpeg", "png", "webp"],
Field(description="Output image format")
] = "jpeg"
) -> dict:
"""Process an image with optional resizing."""
# Implementation...
pass
Dependency Injection (New in 2.14.0):
from fastmcp import FastMCP
from fastmcp.dependencies import Depends
mcp = FastMCP()
def get_user_id() -> str:
return "user_123" # Injected at runtime
@mcp.tool
def get_user_details(user_id: str = Depends(get_user_id)) -> str:
# user_id is injected by the server, not provided by the LLM
return f"Details for {user_id}"
Tool with Context:
from fastmcp import FastMCP, Context
@mcp.tool
async def process_data(data_uri: str, ctx: Context) -> dict:
"""Process data from a resource with progress reporting."""
await ctx.info(f"Processing data from {data_uri}")
# Read a resource
resource = await ctx.read_resource(data_uri)
data = resource[0].content if resource else ""
# Report progress
await ctx.report_progress(progress=50, total=100)
# Example request to the client's LLM for help
summary = await ctx.sample(f"Summarize this in 10 words: {data[:200]}")
await ctx.report_progress(progress=100, total=100)
return {
"length": len(data),
"summary": summary.text
}
Structured Output (New in 2.10.0):
FastMCP automatically creates structured outputs when you add return type annotations:
from dataclasses import dataclass
@dataclass
class UserData:
id: str
name: str
email: str
@mcp.tool
def get_user(user_id: str) -> UserData:
"""Get user data with structured output."""
return UserData(id=user_id, name="Alice", email="alice@example.com")
ToolResult for Full Control:
from fastmcp.tools.tool import ToolResult
from mcp.types import TextContent
@mcp.tool
def advanced_tool() -> ToolResult:
"""Tool with full control over output."""
return ToolResult(
content=[TextContent(type="text", text="Human-readable summary")],
structured_content={"data": "value", "count": 42},
meta={"execution_time_ms": 145}
)
Error Handling:
from fastmcp import FastMCP
from fastmcp.exceptions import ToolError
mcp = FastMCP(name="SecureServer", mask_error_details=True)
@mcp.tool
def divide(a: float, b: float) -> float:
"""Divide a by b."""
if b == 0:
# Error messages from ToolError are always sent to clients
raise ToolError("Division by zero is not allowed.")
return a / b
Using with Methods:
from fastmcp import FastMCP
from fastmcp.tools import tool
class Calculator:
def __init__(self, multiplier: int):
self.multiplier = multiplier
@tool()
def multiply(self, x: int) -> int:
"""Multiply x by the instance multiplier."""
return x * self.multiplier
calc = Calculator(multiplier=3)
mcp = FastMCP()
mcp.add_tool(calc.multiply) # Registers with correct schema
Tool Decorator Parameters:
name: Explicit tool name (overrides function name)description: Explicit description (overrides docstring)tags: Set of strings for categorizing toolsmeta: Custom metadata dictionaryannotations: Tool behavior hints (readOnlyHint, destructiveHint, idempotentHint, openWorldHint, title)timeout: Tool timeout in seconds (new in 3.0.0)output_schema: Custom JSON schema for outputversion: Component version for versioning
2.4 Implement Resources
Resources are data sources exposed via URIs. Use the @mcp.resource decorator.
Static Resource:
@mcp.resource("config://app_settings")
def get_app_settings() -> dict:
"""Returns application configuration."""
return {"version": "1.0.0", "theme": "dark"}
Resource with Custom Configuration:
@mcp.resource(
uri="data://app-status",
name="ApplicationStatus",
description="Provides the current status of the application.",
mime_type="application/json",
tags={"monitoring", "status"},
meta={"version": "2.1", "team": "infrastructure"},
annotations={
"readOnlyHint": True,
"idempotentHint": True
}
)
def get_application_status() -> str:
"""Provides application status."""
return json.dumps({"status": "ok", "uptime": 12345})
Resource Template (Dynamic):
@mcp.resource("user://{user_id}/profile")
def get_user_profile(user_id: str) -> dict:
"""Retrieves a user's profile by ID."""
return {
"id": user_id,
"name": f"User {user_id}",
"status": "active"
}
Multiple Parameters with Annotations:
@mcp.resource(
"repos://{owner}/{repo}/info",
annotations={
"readOnlyHint": True,
"idempotentHint": True
}
)
def get_repo_info(owner: str, repo: str) -> dict:
"""Retrieves information about a repository."""
return {
"owner": owner,
"name": repo,
"full_name": f"{owner}/{repo}"
}
Wildcard Parameters (New in 2.2.4):
@mcp.resource("path://{filepath*}")
def get_path_content(filepath: str) -> str:
"""Retrieves content at a specific path."""
# Can match path://docs/server/resources.mdx
return f"Content at path: {filepath}"
Query Parameters (New in 2.13.0):
@mcp.resource("data://{id}{?format}")
def get_data(id: str, format: str = "json") -> str:
"""Retrieve data in specified format."""
if format == "xml":
return f"<data id='{id}' />"
return f'{{"id": "{id}"}}'
@mcp.resource("api://{endpoint}{?version,limit,offset}")
def call_api(endpoint: str, version: int = 1, limit: int = 10, offset: int = 0) -> dict:
"""Call API endpoint with pagination."""
return {
"endpoint": endpoint,
"version": version,
"limit": limit,
"offset": offset
}
ResourceResult for Full Control (New in 3.0.0):
from fastmcp import FastMCP
from fastmcp.resources import ResourceResult, ResourceContent
@mcp.resource("data://users")
def get_users() -> ResourceResult:
return ResourceResult(
contents=[
ResourceContent(content='[{"id": 1}]', mime_type="application/json"),
ResourceContent(content="# Users\n...", mime_type="text/markdown"),
],
meta={"total": 1}
)
Resource Classes:
from pathlib import Path
from fastmcp import FastMCP
from fastmcp.resources import FileResource, TextResource, DirectoryResource
mcp = FastMCP(name="DataServer")
# Exposing a static file directly
readme_path = Path("./README.md").resolve()
if readme_path.exists():
readme_resource = FileResource(
uri=f"file://{readme_path.as_posix()}",
path=readme_path,
name="README File",
description="The project's README.",
mime_type="text/markdown",
tags={"documentation"}
)
mcp.add_resource(readme_resource)
# Exposing simple, predefined text
notice_resource = TextResource(
uri="resource://notice",
name="Important Notice",
text="System maintenance scheduled for Sunday.",
tags={"notification"}
)
mcp.add_resource(notice_resource)
Resource Decorator Parameters:
uri: Required URI (e.g., "resource://my-resource" or "resource://{param}")name: Optional namedescription: Optional descriptionmime_type: Optional MIME typetags: Optional tagsannotations: Optional behavior hintsmeta: Optional metadata
2.5 Implement Prompts
Prompts are reusable message templates. Use the @mcp.prompt decorator.
Basic Prompt Returning String:
@mcp.prompt
def ask_about_topic(topic: str) -> str:
"""Generates a user message asking for an explanation of a topic."""
return f"Can you please explain the concept of '{topic}'?"
Prompt with Custom Configuration:
@mcp.prompt(
name="data_analysis_request",
description="Generates a prompt for analyzing numerical data",
tags={"analysis", "data"}
)
def analyze_data_prompt(data_points: list[float], context: str = "") -> str:
"""Generates a data analysis request."""
formatted_data = ", ".join(str(point) for point in data_points)
prompt = f"Please analyze these data points: {formatted_data}"
if context:
prompt += f"\n\nContext: {context}"
return prompt
Prompt Decorator Parameters:
name: Explicit prompt namedescription: Explicit descriptiontags: Optional tags
2.6 Custom Routes (HTTP Transport Only)
When using HTTP transport, add custom web routes:
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
Phase 3: Review and Test
3.1 Code Quality
Review for:
- No duplicated code (DRY principle)
- Consistent error handling
- Clear type hints
- Comprehensive docstrings
- Proper use of async/await for I/O operations
3.2 Build and Test
Run the server (stdio):
python {service}_mcp.py
Run the server (HTTP):
if __name__ == "__main__":
mcp.run(transport="http", host="127.0.0.1", port=8000)
Using FastMCP CLI:
fastmcp run {service}_mcp.py:mcp
fastmcp run {service}_mcp.py:mcp --transport http --port 8000
fastmcp run {service}_mcp.py:mcp --reload # Auto-reload during development
fastmcp run {service}_mcp.py:mcp --python 3.11 --with httpx --with-requirements requirements.txt
Test with MCP clients:
- Claude Desktop (stdio)
- Cursor (stdio, HTTP)
- Gemini CLI (stdio, HTTP)
Phase 4: Create Evaluations
After implementing your FastMCP server, create comprehensive evaluations. Load ../mcp-builder/reference/evaluation.md for complete evaluation guidelines.
Reference Files
๐ Documentation Library
Load these resources as needed during development:
Core FastMCP Documentation (Load First)
- FastMCP Welcome: Use WebFetch to load
https://gofastmcp.com/getting-started/welcome - FastMCP Quickstart: Use WebFetch to load
https://gofastmcp.com/getting-started/quickstart - Server Guide: โก Server Implementation - Complete FastMCP server guide
Component Documentation (Load During Phase 2)
- Tools Guide: Use WebFetch to load
https://gofastmcp.com/servers/tools - Resources Guide: Use WebFetch to load
https://gofastmcp.com/servers/resources - Prompts Guide: Use WebFetch to load
https://gofastmcp.com/servers/prompts
Deployment Guide
- Running Servers: Use WebFetch to load
https://gofastmcp.com/deployment/running-server
Evaluation Guide
- Evaluation Guide: Load from
../mcp-builder/reference/evaluation.md- Complete evaluation creation guide