Update fastapi docs (#1198)

This commit is contained in:
Jeremiah Lowin 2025-07-20 19:56:24 -04:00 committed by GitHub
commit cbd751f821
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 338 additions and 127 deletions

View file

@ -7,222 +7,431 @@ icon: bolt
import { VersionBadge } from '/snippets/version-badge.mdx'
FastMCP provides two powerful ways to integrate with FastAPI applications, both of which are documented below.
FastMCP provides two powerful ways to integrate with FastAPI applications:
1. You can [generate an MCP server FROM your FastAPI app](#generating-an-mcp-server) by converting existing API endpoints into MCP tools. This is useful for bootstrapping and quickly attaching LLMs to your API.
2. You can [mount an MCP server INTO your FastAPI app](#mounting-an-mcp-server) by adding MCP functionality to your web application. This is useful for exposing your MCP tools alongside regular API endpoints.
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
You can even combine both approaches to create a single FastAPI app that serves both regular API endpoints and MCP tools!
<Tip>
Generating MCP servers from FastAPI apps 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 FastAPI servers. This is especially true for complex APIs with many endpoints and parameters.
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" />
FastMCP can directly convert your existing FastAPI applications into MCP servers, allowing AI models to interact with your API endpoints through the MCP protocol.
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.
<Tip>
Under the hood, the FastAPI integration is built on top of FastMCP's OpenAPI integration. See the [OpenAPI docs](/integrations/openapi) for more details.
</Tip>
### Create a Server
### Basic Conversion
The simplest way to convert a FastAPI app is using the `FastMCP.from_fastapi()` method:
Convert the FastAPI app to an MCP server with a single line:
```python server.py
from fastapi import FastAPI
```python {5}
# Assumes the FastAPI app from above is already defined
from fastmcp import FastMCP
# Your existing FastAPI app
app = FastAPI(title="My API", version="1.0.0")
@app.get("/items", tags=["items"], operation_id="list_items")
def list_items():
return [{"id": 1, "name": "Item 1"}, {"id": 2, "name": "Item 2"}]
@app.get("/items/{item_id}", tags=["items", "detail"], operation_id="get_item")
def get_item(item_id: int):
return {"id": item_id, "name": f"Item {item_id}"}
@app.post("/items", tags=["items", "create"], operation_id="create_item")
def create_item(name: str):
return {"id": 3, "name": name}
# Convert FastAPI app to MCP server
# Convert to MCP server
mcp = FastMCP.from_fastapi(app=app)
if __name__ == "__main__":
mcp.run() # Run as MCP server
mcp.run()
```
### Component Mapping
### Adding Components
By default, FastMCP converts **every endpoint** in your FastAPI app into an MCP **Tool**. This provides maximum compatibility with LLM clients that primarily support MCP tools.
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.
You can customize this behavior using route maps to control which endpoints become tools, resources, or resource templates:
```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 route mapping
# Custom mapping rules
mcp = FastMCP.from_fastapi(
app=app,
route_maps=[
# GET requests with path parameters become ResourceTemplates
RouteMap(methods=["GET"], pattern=r".*\{.*\}.*", mcp_type=MCPType.RESOURCE_TEMPLATE),
# All other GET requests become Resources
RouteMap(methods=["GET"], pattern=r".*", mcp_type=MCPType.RESOURCE),
# POST/PUT/DELETE become Tools (handled by default rule)
# 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
```
The `FastMCP.from_fastapi()` method accepts all the same configuration options as `FastMCP.from_openapi()`, including route maps, custom tags, component naming, timeouts, and component customization functions. For comprehensive configuration details, see the [OpenAPI Integration guide](/integrations/openapi).
<Tip>
To learn more about customizing the conversion process, see the [OpenAPI Integration guide](/integrations/openapi).
</Tip>
### Key Considerations
### Authentication and Headers
#### Operation IDs
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:
FastMCP uses your FastAPI operation IDs to name MCP components. Ensure your endpoints have meaningful operation IDs:
```python
@app.get("/users/{user_id}", operation_id="get_user_detail") # ✅ Good
@app.get("/users/{user_id}") # ❌ Auto-generated name might be unclear
```
#### Pydantic Models
Your Pydantic models are automatically converted to JSON schema for MCP tool parameters:
```python
from pydantic import BaseModel
class CreateItemRequest(BaseModel):
name: str
description: str | None = None
price: float
@app.post("/items")
def create_item(item: CreateItemRequest):
return {"id": 123, **item.dict()}
```
The MCP tool will have properly typed parameters matching your Pydantic model.
#### Error Handling
FastAPI error handling carries over to the MCP server. HTTPExceptions are automatically converted to appropriate MCP errors.
Since FastAPI integration is built on OpenAPI, all the same configuration options are available including authentication setup, timeout configuration, and request parameter handling. For detailed information on these features, see the [OpenAPI Integration guide](/integrations/openapi).
## Mounting an MCP Server
<VersionBadge version="2.3.1" />
You can also mount an existing FastMCP server into your FastAPI application, adding MCP functionality to your web application. This is useful for exposing your MCP tools alongside regular API endpoints.
### Basic Integration
```python
```python {27-31}
# Assumes the FastAPI app from above is already defined
from fastmcp import FastMCP
from fastapi import FastAPI
# Create your FastMCP server
mcp = FastMCP("MyServer")
@mcp.tool
def analyze_data(query: str) -> dict:
"""Analyze data based on the query."""
return {"result": f"Analysis for: {query}"}
# Create the ASGI app from your MCP server
mcp_app = mcp.http_app(path='/mcp')
# Create a FastAPI app and mount the MCP server
app = FastAPI(lifespan=mcp_app.lifespan)
app.mount("/mcp-server", mcp_app)
# Add regular FastAPI routes
@app.get("/health")
def health_check():
return {"status": "healthy"}
```
The MCP endpoint will be available at `/mcp-server/mcp/` of your FastAPI application.
<Warning>
For Streamable HTTP transport, you **must** pass the lifespan context from the FastMCP app to the FastAPI app. Otherwise, the FastMCP server's session manager will not be properly initialized.
</Warning>
### Advanced Integration
You can combine both approaches - generate an MCP server from your FastAPI app AND mount additional MCP servers:
```python
from fastmcp import FastMCP
from fastapi import FastAPI
# Your existing FastAPI app
app = FastAPI()
@app.get("/items")
def list_items():
return [{"id": 1, "name": "Item 1"}]
# Generate MCP server from FastAPI app
api_mcp = FastMCP.from_fastapi(app=app, name="API Server")
# Create additional purpose-built MCP server
tools_mcp = FastMCP("Tools Server")
@tools_mcp.tool
def advanced_analysis(data: dict) -> dict:
"""Perform advanced analysis not available via API."""
return {"analysis": "complex results"}
# Mount the tools server into the same FastAPI app
tools_app = tools_mcp.http_app(path='/mcp')
app.mount("/tools", tools_app, lifespan=tools_app.lifespan)
```
Now you have:
- API endpoints converted to MCP tools (via `api_mcp`)
- Additional MCP tools available at `/tools/mcp/`
- Regular FastAPI endpoints at their original paths
### Authentication and Middleware
When mounting MCP servers into FastAPI, you can leverage FastAPI's authentication and middleware:
```python
from fastapi import FastAPI, Depends, HTTPException
# 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 token")
return credentials
raise HTTPException(status_code=401, detail="Invalid authentication")
return credentials.credentials
app = FastAPI()
# 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()))
}
# Mount MCP server with authentication
@app.get("/secure")
def secure_endpoint(auth=Depends(verify_token)):
return {"message": "Authenticated"}
# The mounted MCP server inherits the app's security
mcp_app = mcp.http_app()
app.mount("/mcp", mcp_app, lifespan=mcp_app.lifespan)
# Create MCP server with authentication headers
mcp = FastMCP.from_fastapi(
app=app,
httpx_client_kwargs={
"headers": {
"Authorization": "Bearer secret-token",
}
}
)
```
For more advanced ASGI integration patterns, see the [ASGI Integration guide](/integrations/asgi).
## 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 mount it back into 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. Mount it back into your FastAPI app
app = FastAPI(title="E-commerce API", lifespan=mcp_app.lifespan)
app.mount("/llm", mcp_app)
# Now you have:
# - Regular API: http://localhost:8000/products
# - LLM-friendly MCP: http://localhost:8000/llm/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
```
### 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

@ -13,6 +13,8 @@ FastMCP can automatically generate an MCP server from any OpenAPI specification,
<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>
## Create a Server