FastAPI MCP: build an MCP server from your FastAPI app

FastAPI MCP is the integration most Python teams reach for when they want to expose an existing FastAPI app to Claude, Cursor, or any other MCP-compatible client without rewriting their API surface. The fastapi-mcp package handles the conversion automatically: it reads your FastAPI route definitions, generates MCP tool descriptions from them, and exposes the whole thing over the MCP protocol. Three lines of code to add to a working FastAPI app, no separate server to maintain.
I shipped my first FastAPI MCP server in an afternoon last month, wired into a small internal documentation API. The integration is genuinely as simple as the docs claim, but a few production-relevant details (auth, transport choice, deployment) deserve more attention than the quickstart gives them. What follows is the working setup, the gotchas I hit, and the questions teams ask after the first successful run.
Quick answer: what is fastapi-mcp?
fastapi-mcp is a Python package that exposes a FastAPI application as a Model Context Protocol (MCP) server. It introspects your existing routes and turns them into MCP tools, complete with type-aware schemas pulled from your Pydantic models. Add FastApiMCP(app).mount() to a working FastAPI app and any MCP client (Claude Desktop, Cursor, custom agents on the Claude Agent SDK) can call your API endpoints as tools. The package supports both stdio and HTTP transports, and works with FastAPI’s existing auth, middleware, and dependency-injection patterns.
What fastapi-mcp actually does
The conceptual move is small and the impact is large. An MCP server normally requires you to write tool definitions explicitly, mapping each callable function to an MCP schema, handling the JSON-RPC protocol, and managing the transport layer. FastAPI already has all of that information sitting in the route definitions, the Pydantic models, and the OpenAPI schema FastAPI generates automatically. fastapi-mcp reads that existing structure and produces an MCP server from it without you writing a separate definition.
What you get out of that conversion is everything your FastAPI app already does, exposed in a form Claude or any other MCP client can use. Type safety carries over because the Pydantic models become the input schemas the model sees. Authentication carries over because FastAPI’s dependency-injection system handles it before the route runs. Middleware, error handling, and validation all work the same way they did when the only consumer of the API was a browser or another service. The MCP layer becomes one more way to invoke the same routes.
That’s the simple version. The honest version is that not every FastAPI app makes a great MCP server. Routes that take dozens of optional parameters, return large unstructured payloads, or depend on session state translate awkwardly into tools. The package gives you control over which routes to expose and how to describe them to the model, but the curation work is real.
Building a FastAPI MCP server
A working setup starts with a working FastAPI app and one new import. Here’s the minimal example:
from fastapi import FastAPI
from pydantic import BaseModel
from fastapi_mcp import FastApiMCP
app = FastAPI(title="My API")
class Item(BaseModel):
name: str
price: float
@app.get("/items/{item_id}", operation_id="get_item")
async def get_item(item_id: int) -> Item:
"""Return a single item by ID."""
return Item(name=f"Item {item_id}", price=9.99)
@app.post("/items", operation_id="create_item")
async def create_item(item: Item) -> Item:
"""Create a new item."""
return item
# Add MCP integration
mcp = FastApiMCP(app)
mcp.mount()Run it the way you’d run any FastAPI app:
uvicorn main:app --port 8000The MCP server is now available at http://localhost:8000/mcp. Two endpoints have become two MCP tools (get_item and create_item), each with input schemas derived from the function signatures and Pydantic models. The docstrings became the tool descriptions the model sees, which matters more than first-time users expect: a vague docstring produces a tool the model doesn’t know when to invoke.
The operation_id field is the one piece worth setting explicitly. FastAPI auto-generates these names from the route paths, which produces tool names that aren’t always intuitive to a model. Setting them explicitly gives you control over the names the LLM will see and reason about. For production servers, naming the operations carefully pays back in better tool selection.
Connecting a FastAPI MCP server to Claude Desktop
Once the server is running, Claude Desktop needs to be told about it. Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS path; Windows uses %APPDATA%\Claude\claude_desktop_config.json):
{
"mcpServers": {
"my-api": {
"url": "http://localhost:8000/mcp"
}
}
}Restart Claude Desktop. The configured server should appear in the available tools, and the routes you exposed should show up as callable functions.
For remote deployments, swap the localhost URL for the production URL, and configure authentication so the MCP server doesn’t sit unprotected on the public internet. The fastapi-mcp package respects whatever auth your FastAPI app already uses, so an OAuth2-protected route stays protected when accessed via MCP. The MCP client passes credentials through, and the FastAPI dependency runs before the route handler does.
Choosing routes carefully
The pattern that produces a useful MCP server is selective exposure. Default behavior in fastapi-mcp is to expose every route, which works fine for small focused APIs and produces a confusing tool surface for larger ones.
You control which routes become tools through the include_operations and exclude_operations parameters:
mcp = FastApiMCP(
app,
include_operations=["get_item", "create_item", "list_items"],
)
mcp.mount()The criteria that actually matter when picking which routes to expose: does an LLM benefit from being able to call this, does the route have a clean input schema the model can fill in correctly, and would calling this route from an agent ever be the right thing to do. Routes that fail any of those tests are better left out of the MCP surface even when they make sense as part of the underlying API.
Common FastAPI MCP gotchas
A few things tripped me up on the first deployment and show up in others’ migration notes too.
The first is transport choice. The default is HTTP, which is what you want for a deployed server. If you’re running the server locally and Claude Desktop is the only client, stdio is simpler to wire up but requires running the FastAPI app as a subprocess of Claude Desktop rather than as a standalone process. Most teams pick HTTP and accept the extra setup of the URL config.
The second is docstring quality. The model reads your docstrings to decide when to invoke each tool. A docstring like """Get item.""" produces a tool the model uses unpredictably. A docstring like """Return a single item by ID. Use this when the user asks about a specific item they've referenced by name or number.""" produces a tool the model invokes when it should. Treat docstrings as the model’s documentation, not just code documentation.
The third is state and side effects. MCP tools that have side effects (POST/PUT/DELETE routes that change data) work fine technically, but the model will sometimes call them speculatively while exploring. For destructive operations, consider gating them behind explicit user confirmation in the application layer rather than relying on the model to decide when to invoke them. The package doesn’t prevent the model from calling a delete endpoint; your application logic has to.
The fourth is the auth question for remote MCP servers. When the MCP server is local, auth doesn’t matter much. When it’s deployed and reachable from the public internet, every route exposed to MCP is now accessible to anyone who can connect to your URL. Make sure the auth on your FastAPI app is real before the MCP layer goes live, because the MCP layer doesn’t add its own.
When fastapi-mcp is the right call
The decision rule on adopting fastapi-mcp for a real workload is straightforward. If you already have a FastAPI app and want to expose it to LLM clients, the package is the path of least resistance and the right default. If you’re building an MCP server from scratch and don’t already have a FastAPI app, the standalone MCP SDK in Python gives you more flexibility and a smaller dependency surface.
For most teams I’ve seen adopt MCP integration, the FastAPI app already exists, the API is well-shaped, and the addition takes an afternoon. That’s the situation fastapi-mcp was built for, and it’s where the package shines.
FAQ
If you’ve shipped a FastAPI MCP server in production and have notes on what surprised you (auth gotchas, tool-selection issues, performance under real LLM agent traffic), that writeup is the gap worth filling. The package is new enough that production reports are scarce, and the patterns the next wave of adopters will need to learn aren’t fully documented yet.