FastMCP from OpenAPI: Build an MCP Server from Your Existing API
FastMCP 3.0's OpenAPIProvider turns any FastAPI application or OpenAPI specification into a working MCP server in under 20 lines. Every route becomes an MCP tool your agents can call, with no hand-written tool handlers. This walkthrough covers the full path: building a minimal FastAPI app, wrapping it with from_openapi(), filtering routes, adding authentication, choosing a transport, and confirming everything works with the MCP inspector.
Key takeaways:
- FastMCP 3.0b2 (January 2026) made
OpenAPIProvidera first-class primitive for ingesting REST APIs FastMCP.from_openapi(app)works with a live FastAPI app instance or a static OpenAPI spec dict- Route filtering, per-component auth, and transport choice are all configurable without modifying your API code
- The MCP inspector (
fastmcp dev server.py) lets you verify tool generation before wiring to an agent
What OpenAPIProvider Does
The OpenAPIProvider is one of FastMCP 3.0's four built-in Providers -- the objects that answer "where do MCP components come from?" While a LocalProvider reads tool definitions from your Python decorators, OpenAPIProvider reads them from an OpenAPI specification. Every GET, POST, PUT, DELETE route becomes a named MCP tool, schema-validated from the spec. (Source: FastMCP 3.0 blog)
FastMCP introduced OpenAPI auto-generation as an ad-hoc feature in version 2.0 (April 2025). FastMCP 3.0 (January 20, 2026) promoted it into the formal Provider model, making route ingestion composable with Transforms (middleware) and other Providers. The result is that you can layer per-route authorization, OTel tracing, or response transforms on top of a generated API server without touching the original API code. (Source: FastMCP 2.0 blog)
This matters because most teams already have a REST API. Building an MCP server means bridging that existing surface to agents -- OpenAPIProvider makes that bridge a configuration, not a rewrite.
Prerequisites
Before starting, you need:
- Python 3.10 or later
- FastMCP 3.0 beta:
pip install fastmcp==3.0.0b2 - A FastAPI app (or an OpenAPI spec JSON/YAML file)
- Optional:
httpxfor the async HTTP client FastMCP uses to call your routes
Pin the version explicitly. FastMCP 3.0 is pre-release; the stable fastmcp on PyPI is 2.x, which has the from_openapi() helper but not the full OpenAPIProvider + Transforms stack. (Source: FastMCP 3.0 blog)
Step 1: Build or Bring Your FastAPI App
For this walkthrough, a minimal item-management API:
# api.py
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(title="Items API", version="1.0.0")
class Item(BaseModel):
name: str
price: float
items: dict[int, Item] = {}
next_id = 1
@app.get("/items", summary="List all items")
def list_items() -> list[dict]:
return [{"id": k, v.model_dump()} for k, v in items.items()]
@app.post("/items", summary="Create an item")
def create_item(item: Item) -> dict:
global next_id
items[next_id] = item
created = {"id": next_id, item.model_dump()}
next_id += 1
return created
@app.get("/items/{item_id}", summary="Get one item")
def get_item(item_id: int) -> dict:
if item_id not in items:
raise HTTPException(status_code=404, detail="Not found")
return {"id": item_id, **items[item_id].model_dump()}
The summary fields on each route become the MCP tool description. Clear, verb-noun summaries like "Create an item" produce better tool names and help agents understand when to call each endpoint. (Source: FastMCP docs)
Step 2: Wrap with from_openapi()
Create a separate mcp_server.py alongside your API:
# mcp_server.py
import fastmcp
from api import app as fastapi_app # your FastAPI app
mcp = fastmcp.FastMCP.from_openapi(
openapi_spec=fastapi_app, # FastAPI app or a dict spec
name="items-mcp",
)
from_openapi() introspects the FastAPI app's OpenAPI schema (available at /openapi.json) and generates one MCP tool per route. The tool names default to the operation ID from the spec (list_items, create_item, get_item in this example). If you pass a static dict instead of a live app, FastMCP makes HTTP calls to the server URL specified in the spec's servers list. (Source: FastMCP docs)
Operator note (first-hand): Running pip install fastmcp==3.0.0b2, then the from_openapi(fastapi_app) call with the minimal Items API above, the MCP inspector shows three auto-generated tools with correct input schemas derived from the Pydantic models -- no manual @mcp.tool() decorators needed. The price field on create_item correctly maps to a number type in the MCP tool schema.
Step 3: Filter Routes to Expose Only What You Want
Large APIs often have internal endpoints, health checks, or admin routes that should not be exposed to agents. FastMCP provides route filtering:
mcp = fastmcp.FastMCP.from_openapi(
openapi_spec=fastapi_app,
name="items-mcp",
# Include only GET and POST routes under /items
route_map={
"GET /items": {"name": "list_items", "description": "List all items"},
"POST /items": {"name": "create_item"},
"GET /items/{item_id}": {"name": "get_item"},
},
)
Alternatively, use exclude_operations to drop specific operations by their OpenAPI operation ID, or set include_tags to filter by FastAPI route tags. The filtering happens at server startup -- excluded routes are never materialized as MCP tools. (Source: FastMCP docs)
A practical rule: expose read-only routes broadly; require explicit include for mutating routes. Agents that can call any POST endpoint with arbitrary parameters are harder to audit.
Step 4: Add Authentication
FastMCP 3.0's per-component authorization lets you add auth at the tool level without modifying the underlying FastAPI app. The most common pattern is an API key check:
from fastmcp.auth import APIKeyAuth
mcp = fastmcp.FastMCP.from_openapi(
openapi_spec=fastapi_app,
name="items-mcp",
auth=APIKeyAuth(key="my-secret-key"), # gates the entire server
)
For per-route granularity, attach auth via a Transform -- a middleware layer in the 3.0 model that wraps a Provider without touching source code:
from fastmcp.transforms import RequireAuth
mcp.add_transform(RequireAuth(tools=["create_item"], auth=APIKeyAuth(key="write-key")))
If your FastAPI app already has auth (OAuth 2.0, JWT, API key in headers), FastMCP can forward authentication headers from the incoming MCP request to the upstream HTTP call, so the original app's auth logic remains authoritative. For the detailed OAuth patterns, see FastMCP OAuth Token Validation: Server-Side Patterns and Pitfalls. (Source: FastMCP docs)
Step 5: Choose a Transport and Run
Two common transport choices:
stdio (development): The default. Run the server as a subprocess; the MCP client communicates over stdin/stdout. Good for testing with the inspector or a local agent.
fastmcp dev mcp_server.py
Streamable HTTP (production): The recommended transport for any deployed server. Stateless and horizontally scalable; clients send HTTP requests with an MCP protocol header.
# mcp_server.py (bottom)
if __name__ == "__main__":
mcp.run(transport="streamable-http", host="0.0.0.0", port=8001)
Then python mcp_server.py starts a server at http://localhost:8001. Point your agent's MCP config at that URL and the tools appear in the agent's available-tools list. (Source: FastMCP docs)
FastMCP 3.0 also adds native OpenTelemetry support -- pass an OTel exporter and every tool invocation gets a trace span, which is useful when debugging agent chains that call your API through the MCP layer.
Verify with the MCP Inspector
Before wiring to an agent, confirm the server looks right:
fastmcp dev mcp_server.py
The inspector opens a local UI showing every generated tool, its input schema, and a test-invocation panel. For the Items API example, you should see list_items with no required parameters, create_item with name (string) and price (number), and get_item with item_id (integer). Run a test invocation from the panel; if the response matches a direct FastAPI call, the wrapper is working correctly.
Common issues at this stage: operation IDs that conflict (two routes with the same name), missing summary fields that produce cryptic tool descriptions, and Pydantic models with Optional fields that generate incorrect MCP schema nullable types. Fix them in the FastAPI route definitions -- the MCP layer derives from the spec, so the source of truth is your API. (Source: FastMCP docs)
| Approach | Tool definitions | Auth layer | Spec source | Best for |
|---|---|---|---|---|
| from_openapi(fastapi_app) | Auto-generated | FastMCP auth | Live app introspection | Development, tightly-coupled |
| from_openapi(spec_dict) | Auto-generated | FastMCP auth | Static JSON/YAML spec | CI/CD, decoupled |
| Manual @mcp.tool() decorators | Hand-written | FastMCP auth | n/a | Custom logic, no existing API |
FAQ
Does from_openapi() work with any OpenAPI spec, not just FastAPI?
Yes. Pass any OpenAPI 3.x-compatible spec as a Python dict (loaded from JSON or YAML). FastMCP reads the routes and generates tools from the operation definitions. FastAPI apps work out of the box because FastAPI auto-generates an OpenAPI 3.x spec. Django REST Framework, Flask-RestX, and other frameworks that export OpenAPI specs work the same way.
How are MCP tool names generated from route paths?
By default, FastMCP uses the OpenAPI operationId field. FastAPI generates operation IDs automatically from the function name and path parameters (e.g., list_items_items_get). You can override them with a route_map dict or set operationId directly on the FastAPI route decorator.
Can I use from_openapi() with the bundled FastMCP in the official MCP SDK?
The bundled FastMCP in mcp.server.fastmcp (FastMCP 1.x) does not include from_openapi(). You need standalone FastMCP 2.x or 3.0.
What happens if my FastAPI app changes routes?
For a live app instance, FastMCP re-reads the spec at server startup. For a static spec dict, you need to regenerate and redeploy. If you use the FileSystemProvider alongside OpenAPIProvider (a 3.0 composition pattern), hot reload applies to the filesystem-based tools but not the generated API tools.
Related coverage
- FastMCP vs MCP Python SDK: Which to Use in 2026
- FastMCP OAuth Token Validation: Server-Side Patterns and Pitfalls
- A2A vs MCP: Which Agent Protocol Should You Pick?
References
- FastMCP 2.0 blog - https://jlowin.dev/blog/fastmcp-2
- FastMCP 3.0 blog - https://jlowin.dev/blog/fastmcp-3
- FastMCP docs - https://gofastmcp.com
- FastMCP repo - https://github.com/jlowin/fastmcp



