diff --git a/docs/integrations/fastapi.mdx b/docs/integrations/fastapi.mdx
index d29b2589f..5f3ff8e98 100644
--- a/docs/integrations/fastapi.mdx
+++ b/docs/integrations/fastapi.mdx
@@ -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!
-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.
+
FastMCP does *not* include FastAPI as a dependency; you must install it separately to use this integration.
+## 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"}
+```
+
+
+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`.
+
+
## Generating an MCP Server
-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.
-
-Under the hood, the FastAPI integration is built on top of FastMCP's OpenAPI integration. See the [OpenAPI docs](/integrations/openapi) for more details.
-
-### 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).
+
+To learn more about customizing the conversion process, see the [OpenAPI Integration guide](/integrations/openapi).
+
-### 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
-
-
-
-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.
-
-
-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.
-
-
-### 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).
\ No newline at end of file
+## Mounting an MCP Server
+
+
+
+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).
\ No newline at end of file
diff --git a/docs/integrations/openapi.mdx b/docs/integrations/openapi.mdx
index 805911e5c..6f3124710 100644
--- a/docs/integrations/openapi.mdx
+++ b/docs/integrations/openapi.mdx
@@ -13,6 +13,8 @@ FastMCP can automatically generate an MCP server from any OpenAPI specification,
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.
## Create a Server