The Model Context Protocol (MCP) is an open standard that allows LLMs to interact with external tools and data sources. With FastMCP, you can turn any existing FastAPI application into an MCP server with minimal code changes, exposing your API endpoints as tools that LLM clients (like Claude Code) can discover and call directly.
This post walks through building a simple MCP server with FastAPI + FastMCP and connecting it to Claude Code.
uv add fastapi fastmcp uvicorn
FastMCP.from_fastapi() wraps an existing FastAPI app so that every endpoint is automatically registered as an MCP tool. The endpoint’s docstring becomes the tool description, and query/path parameters become tool parameters.
# uv run python -m main.py
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager
import uvicorn
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
from fastmcp import FastMCP
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None]:
_ = app
async with mcp_app.router.lifespan_context(mcp_app):
yield
app = FastAPI(
title="Dummy Project",
version="v0.0.0",
swagger_ui_parameters={"displayRequestDuration": True},
lifespan=lifespan,
)
@app.get("/hidden_value")
async def select_hidden_value_endpoint(base_value: float) -> dict[str, int]:
"""
Returns the hidden oracle value for testing purposes.
Keep it simple and deterministic for local checks.
output:
{"result": int}
"""
return {"result": int(base_value * 2)}
mcp = FastMCP.from_fastapi(app, name="Dummy Project")
mcp_app = mcp.http_app(transport="http", path="/")
app.mount("/mcp", mcp_app)
@app.get("/", response_class=RedirectResponse)
async def redirect_to_docs() -> str:
"""
Redirect from ip:8000/ to ip:8000/docs
"""
return "/docs"
@app.get("/health")
async def health_check() -> dict[str, str]:
return {"status": "healthy"}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
Key points:
FastMCP.from_fastapi(app) converts all FastAPI routes into MCP tools.mcp.http_app(transport="http", path="/") creates the MCP transport app.app.mount("/mcp", mcp_app) mounts the MCP server at /mcp, so the MCP endpoint lives alongside your regular API.lifespan context manager ensures the MCP server starts and stops with the FastAPI app.Register the MCP server in .claude/settings.json so Claude Code can discover and call your tools:
// .claude/settings.json
{
"mcpServers": {
"my-server-local": {
"type": "http",
"url": "http://localhost:8000/mcp/"
}
}
}
Once the server is running and Claude Code is configured, use the /mcp command inside Claude Code to verify the tools are registered:
❯ /mcp
───────────────────────────────────────────────────────────────────────────────────────────────────────────
select_hidden_value_endpoint_hidden_value_get
my-server-local
Tool name: select_hidden_value_endpoint_hidden_value_get
Full name: mcp__my-server-local__select_hidden_value_endpoint_hidden_value_get
Description:
Returns the hidden oracle value for testing purposes.
Keep it simple and deterministic for local checks.
output:
{"result": int}
Parameters:
• base_value (required): number
Esc to go back
The tool name, description, and parameters are all derived automatically from the FastAPI endpoint definition.