The fast, Pythonic way to build MCP servers and clients.
The Model Context Protocol (MCP) is a new, standardized way to provide context and tools to your LLMs, and FastMCP makes building MCP servers and clients simple and intuitive. Create tools, expose resources, define prompts, and connect components with clean, Pythonic code.
1
2
3
4
5
6
7
8
9
10
11
12
# server.pyfromfastmcpimportFastMCPmcp=FastMCP("Demo 🚀")@mcp.tool()defadd(a:int,b:int)->int:"""Add two numbers"""returna+bif__name__=="__main__":mcp.run()
Run the server locally:
1
fastmcp run server.py
FastMCP handles the complex protocol details and server management, letting you focus on building great tools and applications. It’s designed to feel natural to Python developers.
The Model Context Protocol (MCP) lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. Think of it like a web API, but specifically designed for LLM interactions. MCP servers can:
Expose data through Resources (think GET endpoints; load info into context)
Provide functionality through Tools (think POST/PUT endpoints; execute actions)
Define interaction patterns through Prompts (reusable templates)
And more!
FastMCP provides a high-level, Pythonic interface for building and interacting with these servers.
Why FastMCP?
The MCP protocol is powerful but implementing it involves a lot of boilerplate - server setup, protocol handlers, content types, error management. FastMCP handles all the complex protocol details and server management, so you can focus on building great tools. It’s designed to be high-level and Pythonic; in most cases, decorating a function is all you need.
FastMCP aims to be:
🚀 Fast: High-level interface means less code and faster development
🍀 Simple: Build MCP servers with minimal boilerplate
🐍 Pythonic: Feels natural to Python developers
🔍 Complete: FastMCP aims to provide a full implementation of the core MCP specification for both servers and clients
Key Features
Servers
Create servers with minimal boilerplate using intuitive decorators
Proxy existing servers to modify configuration or transport
Compose servers by into complex applications
Generate servers from OpenAPI specs or FastAPI objects
Clients
Interact with MCP servers programmatically
Connect to any MCP server using any transport
Test your servers without manual intervention
Innovate with core MCP capabilities like LLM sampling
What’s New in v2?
FastMCP 1.0 made it so easy to build MCP servers that it’s now part of the official Model Context Protocol Python SDK! For basic use cases, you can use the upstream version by importing mcp.server.fastmcp.FastMCP (or installing fastmcp=1.0).
Based on how the MCP ecosystem is evolving, FastMCP 2.0 builds on that foundation to introduce a variety of new features (and more experimental ideas). It adds advanced features like proxying and composing MCP servers, as well as automatically generating them from OpenAPI specs or FastAPI objects. FastMCP 2.0 also introduces new client-side functionality like LLM sampling.
Documentation
📚 FastMCP’s documentation is available at gofastmcp.com.
Installation
We strongly recommend installing FastMCP with uv, as it is required for deploying servers via the CLI:
1
uv pip install fastmcp
Note: on macOS, uv may need to be installed with Homebrew (brew install uv) in order to make it available to the Claude Desktop app.
For development, install with:
1
2
3
4
5
# Clone the repo firstgit clone https://github.com/jlowin/fastmcp.git
cd fastmcp
# Install with dev dependenciesuv sync
Quickstart
Let’s create a simple MCP server that exposes a calculator tool and some data:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# server.pyfromfastmcpimportFastMCP# Create an MCP servermcp=FastMCP("Demo")# Add an addition tool@mcp.tool()defadd(a:int,b:int)->int:"""Add two numbers"""returna+b# Add a dynamic greeting resource@mcp.resource("greeting://{name}")defget_greeting(name:str)->str:"""Get a personalized greeting"""returnf"Hello, {name}!"
You can install this server in Claude Desktop and interact with it right away by running:
1
fastmcp install server.py
Core Concepts
These are the building blocks for creating MCP servers, using the familiar decorator-based approach.
The FastMCP Server
The central object representing your MCP application. It handles connections, protocol details, and routing.
1
2
3
4
5
6
7
fromfastmcpimportFastMCP# Create a named servermcp=FastMCP("My App")# Specify dependencies needed when deployed via `fastmcp install`mcp=FastMCP("My App",dependencies=["pandas","numpy"])
Tools
Tools allow LLMs to perform actions by executing your Python functions. They are ideal for tasks that involve computation, external API calls, or side effects.
Decorate synchronous or asynchronous functions with @mcp.tool(). FastMCP automatically generates the necessary MCP schema based on type hints and docstrings. Pydantic models can be used for complex inputs.
importhttpxfrompydanticimportBaseModelclassUserInfo(BaseModel):user_id:intnotify:bool=False@mcp.tool()asyncdefsend_notification(user:UserInfo,message:str)->dict:"""Sends a notification to a user if requested."""ifuser.notify:# Simulate sending notificationprint(f"Notifying user {user.user_id}: {message}")return{"status":"sent","user_id":user.user_id}return{"status":"skipped","user_id":user.user_id}@mcp.tool()defget_stock_price(ticker:str)->float:"""Gets the current price for a stock ticker."""# Replace with actual API callprices={"AAPL":180.50,"GOOG":140.20}returnprices.get(ticker.upper(),0.0)
Resources
Resources expose data to LLMs. They should primarily provide information without significant computation or side effects (like GET requests).
Decorate functions with @mcp.resource("your://uri"). Use curly braces {} in the URI to define dynamic resources (templates) where parts of the URI become function parameters.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Static resource returning simple text@mcp.resource("config://app-version")defget_app_version()->str:"""Returns the application version."""return"v2.1.0"# Dynamic resource template expecting a 'user_id' from the URI@mcp.resource("db://users/{user_id}/email")asyncdefget_user_email(user_id:str)->str:"""Retrieves the email address for a given user ID."""# Replace with actual database lookupemails={"123":"alice@example.com","456":"bob@example.com"}returnemails.get(user_id,"not_found@example.com")# Resource returning JSON data@mcp.resource("data://product-categories")defget_categories()->list[str]:"""Returns a list of available product categories."""return["Electronics","Books","Home Goods"]
Prompts
Prompts define reusable templates or interaction patterns for the LLM. They help guide the LLM on how to use your server’s capabilities effectively.
Decorate functions with @mcp.prompt(). The function should return the desired prompt content, which can be a simple string, a Message object (like UserMessage or AssistantMessage), or a list of these.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
fromfastmcp.prompts.baseimportUserMessage,AssistantMessage@mcp.prompt()defask_review(code_snippet:str)->str:"""Generates a standard code review request."""returnf"Please review the following code snippet for potential bugs and style issues:\n```python\n{code_snippet}\n```"@mcp.prompt()defdebug_session_start(error_message:str)->list[Message]:"""Initiates a debugging help session."""return[UserMessage(f"I encountered an error:\n{error_message}"),AssistantMessage("Okay, I can help with that. Can you provide the full traceback and tell me what you were trying to do?")]
Context
Gain access to MCP server capabilities within your tool or resource functions by adding a parameter type-hinted with fastmcp.Context.
fromfastmcpimportContext,FastMCPmcp=FastMCP("Context Demo")@mcp.resource("system://status")asyncdefget_system_status(ctx:Context)->dict:"""Checks system status and logs information."""awaitctx.info("Checking system status...")# Perform checksawaitctx.report_progress(1,1)# Report completionreturn{"status":"OK","load":0.5,"client":ctx.client_id}@mcp.tool()asyncdefprocess_large_file(file_uri:str,ctx:Context)->str:"""Processes a large file, reporting progress and reading resources."""awaitctx.info(f"Starting processing for {file_uri}")# Read the resource using the contextfile_content_resource=awaitctx.read_resource(file_uri)file_content=file_content_resource[0].content# Assuming single text contentlines=file_content.splitlines()total_lines=len(lines)fori,lineinenumerate(lines):# Process line...if(i+1)%100==0:# Report progress every 100 linesawaitctx.report_progress(i+1,total_lines)awaitctx.info(f"Finished processing {file_uri}")returnf"Processed {total_lines} lines."
fromfastmcpimportFastMCP,ImagefromPILimportImageasPILImageimportiomcp=FastMCP("Image Demo")@mcp.tool()defcreate_thumbnail(image_data:Image)->Image:"""Creates a 100x100 thumbnail from the provided image."""img=PILImage.open(io.BytesIO(image_data.data))# Assumes image_data received as Image with bytesimg.thumbnail((100,100))buffer=io.BytesIO()img.save(buffer,format="PNG")# Return a new Image object with the thumbnail datareturnImage(data=buffer.getvalue(),format="png")@mcp.tool()defload_image_from_disk(path:str)->Image:"""Loads an image from the specified path."""# Handles reading file and detecting format based on extensionreturnImage(path=path)
FastMCP handles the conversion to/from the base64-encoded format required by the MCP protocol.
MCP Clients
The Client class lets you interact with any MCP server (not just FastMCP ones) from Python code:
1
2
3
4
5
6
7
8
9
10
fromfastmcpimportClientasyncwithClient("path/to/server")asclient:# Call a toolresult=awaitclient.call_tool("weather",{"location":"San Francisco"})print(result)# Read a resourceres=awaitclient.read_resource("db://users/123/profile")print(res)
You can connect to servers using any supported transport protocol (Stdio, SSE, FastMCP, etc.). If you don’t specify a transport, the Client class automatically attempts to detect an appropriate one from your connection string or server object.
Client Methods
The Client class exposes several methods for interacting with MCP servers.
asyncwithClient("path/to/server")asclient:# List available toolstools=awaitclient.list_tools()# List available resourcesresources=awaitclient.list_resources()# Call a tool with argumentsresult=awaitclient.call_tool("generate_report",{"user_id":123})# Read a resourceuser_data=awaitclient.read_resource("db://users/123/profile")# Get a promptgreeting=awaitclient.get_prompt("welcome",{"name":"Alice"})# Send progress updatesawaitclient.progress("task-123",50,100)# 50% complete# Basic connectivity testingawaitclient.ping()
These methods correspond directly to MCP protocol operations, making it easy to interact with any MCP-compatible server (not just FastMCP ones).
Transport Options
FastMCP supports various transport protocols for connecting to MCP servers:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fromfastmcpimportClientfromfastmcp.client.transportsimport(SSETransport,PythonStdioTransport,FastMCPTransport)# Connect to a server over SSE (common for web-based MCP servers)asyncwithClient(SSETransport("http://localhost:8000/mcp"))asclient:# Use client here...# Connect to a Python script using stdio (useful for local tools)asyncwithClient(PythonStdioTransport("path/to/script.py"))asclient:# Use client here...# Connect directly to a FastMCP server object in the same processfromyour_appimportmcp_serverasyncwithClient(FastMCPTransport(mcp_server))asclient:# Use client here...
Common transport options include:
SSETransport: Connect to a server via Server-Sent Events (HTTP)
PythonStdioTransport: Run a Python script and communicate via stdio
FastMCPTransport: Connect directly to a FastMCP server object
WSTransport: Connect via WebSockets
In addition, if you pass a connection string or FastMCP server object to the Client constructor, it will try to automatically detect the appropriate transport.
LLM Sampling
Sampling is an MCP feature that allows a server to request a completion from the client LLM, enabling sophisticated use cases while maintaining security and privacy on the server.
importmarvin# Or any other LLM clientfromfastmcpimportClient,Context,FastMCPfromfastmcp.client.samplingimportRequestContext,SamplingMessage,SamplingParams# -- SERVER SIDE --# Create a server that requests LLM completions from the clientmcp=FastMCP("Sampling Example")@mcp.tool()asyncdefgenerate_poem(topic:str,context:Context)->str:"""Generate a short poem about the given topic."""# The server requests a completion from the client LLMresponse=awaitcontext.sample(f"Write a short poem about {topic}",system_prompt="You are a talented poet who writes concise, evocative verses.")returnresponse.text@mcp.tool()asyncdefsummarize_document(document_uri:str,context:Context)->str:"""Summarize a document using client-side LLM capabilities."""# First read the document as a resourcedoc_resource=awaitcontext.read_resource(document_uri)doc_content=doc_resource[0].content# Assuming single text content# Then ask the client LLM to summarize itresponse=awaitcontext.sample(f"Summarize the following document:\n\n{doc_content}",system_prompt="You are an expert summarizer. Create a concise summary.")returnresponse.text# -- CLIENT SIDE --# Create a client that handles the sampling requestsasyncdefsampling_handler(messages:list[SamplingMessage],params:SamplingParams,ctx:RequestContext,)->str:"""Handle sampling requests from the server using your preferred LLM."""# Extract the messages and system promptprompt=[m.content.textforminmessagesifm.content.type=="text"]system_instruction=params.systemPromptor"You are a helpful assistant."# Use your preferred LLM client to generate completionsreturnawaitmarvin.say_async(message=prompt,instructions=system_instruction,)# Connect them togetherasyncwithClient(mcp,sampling_handler=sampling_handler)asclient:result=awaitclient.call_tool("generate_poem",{"topic":"autumn leaves"})print(result.content[0].text)
This pattern is powerful because:
The server can delegate text generation to the client LLM
The server remains focused on business logic and data handling
The client maintains control over which LLM is used and how requests are handled
No sensitive data needs to be sent to external APIs
Roots Access
FastMCP exposes the MCP roots functionality, allowing clients to specify which file system roots they can access. This creates a secure boundary for tools that need to work with files. Note that the server must account for client roots explicitly.
1
2
3
4
5
6
7
8
fromfastmcpimportClient,RootsList# Specify file roots that the client can accessroots=["file:///path/to/allowed/directory"]asyncwithClient(mcp_server,roots=roots)asclient:# Now tools in the MCP server can access files in the specified rootsawaitclient.call_tool("process_file",{"filename":"data.csv"})
Advanced Features
Building on the core concepts, FastMCP v2 introduces powerful features for more complex scenarios:
Proxy Servers
Create a FastMCP server that acts as an intermediary, proxying requests to another MCP endpoint (which could be a server or another client connection).
Use Cases:
Transport Conversion: Expose a server running on Stdio (like many local tools) over SSE or WebSockets, making it accessible to web clients or Claude Desktop.
Adding Functionality: Wrap an existing server to add authentication, request logging, or modified tool behavior.
Aggregating Servers: Combine multiple backend MCP servers behind a single proxy interface (though mount might be simpler for this).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
importasynciofromfastmcpimportFastMCP,Clientfromfastmcp.client.transportsimportPythonStdioTransport# Create a client that connects to the original serverproxy_client=Client(transport=PythonStdioTransport('path/to/original_stdio_server.py'),)# Create a proxy server that connects to the client and exposes its capabilitiesproxy=FastMCP.from_client(proxy_client,name="Stdio-to-SSE Proxy")if__name__=="__main__":proxy.run(transport='sse')
FastMCP.from_client is a class method that connects to the target, discovers its capabilities, and dynamically builds the proxy server instance.
Composing MCP Servers
Structure larger MCP applications by creating modular FastMCP servers and “mounting” them onto a parent server. This automatically handles prefixing for tool names and resource URIs, preventing conflicts.
fromfastmcpimportFastMCP# --- Weather MCP ---weather_mcp=FastMCP("Weather Service")@weather_mcp.tool()defget_forecast(city:str):returnf"Sunny in {city}"@weather_mcp.resource("data://temp/{city}")defget_temp(city:str):return25.0# --- News MCP ---news_mcp=FastMCP("News Service")@news_mcp.tool()deffetch_headlines():return["Big news!","Other news"]@news_mcp.resource("data://latest_story")defget_story():return"A story happened."# --- Composite MCP ---mcp=FastMCP("Composite")# Mount sub-apps with prefixesmcp.mount("weather",weather_mcp)# Tools prefixed "weather/", resources prefixed "weather+"mcp.mount("news",news_mcp)# Tools prefixed "news/", resources prefixed "news+"@mcp.tool()defping():return"Composite OK"if__name__=="__main__":mcp.run()
This promotes code organization and reusability for complex MCP systems.
OpenAPI & FastAPI Generation
Leverage your existing web APIs by automatically generating FastMCP servers from them.
By default, the following rules are applied:
GET requests -> MCP resources
GET requests with path parameters -> MCP resource templates
All other HTTP methods -> MCP tools
You can override these rules to customize or even ignore certain endpoints.
From FastAPI:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fromfastapiimportFastAPIfromfastmcpimportFastMCP# Your existing FastAPI applicationfastapi_app=FastAPI(title="My Existing API")@fastapi_app.get("/status")defget_status():return{"status":"running"}@fastapi_app.post("/items")defcreate_item(name:str,price:float):return{"id":1,"name":name,"price":price}# Generate an MCP server directly from the FastAPI appmcp_server=FastMCP.from_fastapi(fastapi_app)if__name__=="__main__":mcp_server.run()
From an OpenAPI Specification:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
importhttpximportjsonfromfastmcpimportFastMCP# Load the OpenAPI spec (dict)# with open("my_api_spec.json", "r") as f:# openapi_spec = json.load(f)openapi_spec={...}# Your spec dict# Create an HTTP client to make requests to the actual API endpointhttp_client=httpx.AsyncClient(base_url="https://api.yourservice.com")# Generate the MCP servermcp_server=FastMCP.from_openapi(openapi_spec,client=http_client)if__name__=="__main__":mcp_server.run()
Handling stderr
The MCP spec allows for the server to write anything it wants to stderr, and it
doesn’t specify the format in any way. FastMCP will forward the server’s stderr
to the client’s stderr.
Running Your Server
Choose the method that best suits your needs:
Development Mode (Recommended for Building & Testing)
Use fastmcp dev for an interactive testing environment with the MCP Inspector.
1
2
3
4
5
fastmcp dev your_server_file.py
# With temporary dependenciesfastmcp dev your_server_file.py --with pandas --with numpy
# With local package in editable modefastmcp dev your_server_file.py --with-editable .
Claude Desktop Integration (For Regular Use)
Use fastmcp install to set up your server for persistent use within the Claude Desktop app. It handles creating an isolated environment using uv.
1
2
3
4
5
fastmcp install your_server_file.py
# With a custom name in Claudefastmcp install your_server_file.py --name "My Analysis Tool"# With extra packages and environment variablesfastmcp install server.py --with requests -v API_KEY=123 -f .env
Direct Execution (For Advanced Use Cases)
Run your server script directly for custom deployments or integrations outside of Claude. You manage the environment and dependencies yourself.
Add to your your_server_file.py:
1
2
if__name__=="__main__":mcp.run()# Assuming 'mcp' is your FastMCP instance
Run with:
1
2
3
python your_server_file.py
# oruv run python your_server_file.py
Server Object Names
If your FastMCP instance is not named mcp, server, or app, specify it using file:object syntax for the dev and install commands:
1
2
fastmcp dev my_module.py:my_mcp_instance
fastmcp install api.py:api_app
Examples
Explore the examples/ directory for code samples demonstrating various features:
simple_echo.py: Basic tool, resource, and prompt.
complex_inputs.py: Using Pydantic models for tool inputs.