Automating MCP Server Creation from OpenAPI and FastAPI

Andreas Nigg
Andreas Nigg · Co-Founder, Pondhouse Data
Published Updated 6 min read
Automating MCP Server Creation from OpenAPI and FastAPI
On this page

You don't have to write an MCP server by hand if you already have an API. FastMCP can generate one for you: FastMCP.from_openapi() builds an MCP server from an OpenAPI specification, and FastMCP.from_fastapi() does the same for a FastAPI application. This post shows both, plus how to control which endpoints become MCP tools and which become resources.

In our recent blog post about creating an MCP server with FastMCP we described what MCP servers are, why they matter for the current AI ecosystem, which resources they can provide, and how to build one from scratch.

Building one is easy with the current tooling. Still, there's a catch: at its core, an MCP server only provides a standardized API optimized for LLMs. (I don't mean to undersell that. It's a much-needed standard with a lot of benefits. But at the end of the day, it's just an API.) You most likely already have APIs in your applications, so with MCP you end up wrapping another API around your existing ones.

We still wrap an MCP server around your existing API here, but we generate it automatically from your OpenAPI specification or FastAPI application. That way your existing endpoints become available in the MCP ecosystem, and therefore to almost any LLM client. (If you work with Python SDKs rather than HTTP APIs, langchain-autotools takes a similar approach and turns SDK methods into LLM tools.)

Update (November 2026): This post was written for an early FastMCP 2.x release. FastMCP 2.5 replaced RouteType and the route_type argument with MCPType and mcp_type (values TOOL, RESOURCE, RESOURCE_TEMPLATE and EXCLUDE), and RouteType has since been removed. Since FastMCP 2.8.0, every route becomes a tool by default, so the default mapping table below no longer applies; pin fastmcp<2.8 to follow the post as written. The FastMCP OpenAPI docs now import these classes with from fastmcp.server.providers.openapi import RouteMap, MCPType and recommend generated servers for bootstrapping and prototyping only, because LLMs do much better with curated MCP servers.

Automatically creating an MCP server from an OpenAPI specification

If your API has an OpenAPI specification, FastMCP can generate an MCP server directly from that spec. The feature was introduced in FastMCP version 2.0.0 and connects your existing API to MCP clients without writing a tool definition per endpoint.

Note: This example heavily relies on the great FastMCP documentation.

The simplest way to create an MCP server from your OpenAPI specification requires just a few lines of code:

1import httpx
2from fastmcp import FastMCP
3
4api_client = httpx.AsyncClient(base_url="https://api.my-api-url.com")
5
6# Load your OpenAPI spec
7spec = {...} # Your OpenAPI specification as a Python dict
8
9# Create an MCP server from your OpenAPI spec
10mcp = FastMCP.from_openapi(openapi_spec=spec, client=api_client)
11
12if __name__ == "__main__":
13 mcp.run()

That's it. FastMCP analyzes your OpenAPI specification, creates the matching MCP components and routes MCP requests to your underlying API.

OpenAPI configuration options

FastMCP.from_openapi lets you configure request timeouts and route mappings.

Timeout configuration

1# Set a 5 second timeout for all requests
2mcp = FastMCP.from_openapi(
3 openapi_spec=spec,
4 client=api_client,
5 timeout=5.0
6)

This timeout applies to all requests made by tools, resources and resource templates.

Default route mapping

FastMCP has to map each API route to an MCP component. As described in our previous post, MCP servers deliver data through two main components: resources and tools. Resources are read-only endpoints, while tools are action-oriented endpoints.

FastMCP makes an educated guess about which component each route should become:

OpenAPI RouteExampleMCP Component
GET without path paramsGET /statsResource
GET with path paramsGET /users/{id}Resource Template
POST, PUT, PATCH, DELETE, etc.POST /usersTool
  • Read-only endpoints become resources
  • Parameterized read endpoints become resource templates
  • Action-oriented endpoints become tools

Custom route maps

The default mapping works for most APIs, but not all. A GET endpoint might be a tool (for example, one that runs a database query), and a POST endpoint might in some cases be a resource.

To change the mapping, define RouteMap objects with the HTTP methods, a URL pattern and the route type (RouteType.TOOL or RouteType.RESOURCE), then pass them to FastMCP.from_openapi:

1from fastmcp.server.openapi import RouteMap, RouteType
2
3custom_maps = [
4 RouteMap(methods=["GET"],
5 pattern=r"^/database/.*",
6 route_type=RouteType.TOOL),
7 RouteMap(methods=["POST"],
8 pattern=r"^/user/profile/status",
9 route_type=RouteType.RESOURCE),
10]
11
12mcp = FastMCP.from_openapi(
13 openapi_spec=spec,
14 client=api_client,
15 route_maps=custom_maps
16)
17
18mcp.run()

FastMCP applies your custom route maps first, before the default mapping rules.

How the FastMCP OpenAPI integration works

Under the hood, FastMCP's OpenAPI integration:

  1. Parses your OpenAPI specification to extract routes and schemas
  2. Applies mapping rules to categorize each route as a resource, resource template, or tool
  3. When an MCP client interacts with the server:
    • FastMCP constructs an HTTP request based on the OpenAPI definition
    • It sends the request through your provided httpx client
    • It translates the HTTP response to the appropriate MCP format

Parameter handling

FastMCP also takes care of query and path parameters.

Query parameters

By default, FastMCP only includes query parameters with non-empty values. It drops parameters set to None or to an empty string (""), so your API doesn't receive empty parameters:

1await client.call_tool("search_products", {
2 "category": "electronics", # Will be included
3 "min_price": 100, # Will be included
4 "max_price": None, # Will be excluded
5 "brand": "", # Will be excluded
6})

The resulting HTTP request only includes category=electronics&min_price=100.

Path parameters

For path parameters, FastMCP checks that every required parameter is present and not null, because you can't omit a path parameter from a URL.

1# This will work
2await client.call_tool("get_product", {"product_id": 123})
3
4# This will raise ValueError: "Missing required path parameters: {'product_id'}"
5await client.call_tool("get_product", {"product_id": None})

Validating the created MCP server components

To see which components FastMCP generated from your spec, create the following function and call it with asyncio.run(check_mcp(mcp)). It prints all tools, resources and resource templates of the MCP server:

1async def check_mcp(mcp: FastMCP):
2 tools = await mcp.get_tools()
3 resources = await mcp.get_resources()
4 templates = await mcp.get_resource_templates()
5
6 print(f"{len(tools)} Tool(s): {', '.join([t.name for t in tools.values()])}")
7 print(f"{len(resources)} Resource(s): {', '.join([r.name for r in resources.values()])}")
8 print(f"{len(templates)} Resource Template(s): {', '.join([t.name for t in templates.values()])}")
9
10 return mcp

Automatically creating an MCP server from a FastAPI application

If your backend services run on FastAPI, FastMCP can expose them as MCP servers directly. This integration was also introduced in FastMCP version 2.0.0 and needs only minimal code changes.

Important: FastAPI needs to be installed in your environment for this to work. You can install it with pip install fastapi.

Let's assume we have the following FastAPI application:

1from fastapi import FastAPI
2
3# An existing FastAPI app
4app = FastAPI()
5
6@app.get("/items")
7def list_items():
8 return [{"id": 1, "name": "Item 1"}, {"id": 2, "name": "Item 2"}]
9
10@app.get("/items/{item_id}")
11def get_item(item_id: int):
12 return {"id": item_id, "name": f"Item {item_id}"}
13
14@app.post("/items")
15def create_item(name: str):
16 return {"id": 3, "name": name}

To turn it into an MCP server, call FastMCP.from_fastapi(app) and run the server:

1mcp = FastMCP.from_fastapi(app=app)
2mcp.run()

FastAPI configuration options

As with the OpenAPI integration, you can configure timeouts and route mappings for your FastAPI application.

Timeout configuration

1mcp = FastMCP.from_fastapi(
2 app=app,
3 timeout=5.0
4)

Custom route maps for FastAPI

Custom route mappings for FastAPI work the same way as for OpenAPI: pass a list of RouteMap objects to control how FastMCP maps your FastAPI routes to MCP components.

1custom_maps = [
2 RouteMap(methods=["GET"],
3 pattern=r"^/items/.*",
4 route_type=RouteType.TOOL),
5 RouteMap(methods=["POST"],
6 pattern=r"^/items",
7 route_type=RouteType.RESOURCE),
8]
9mcp = FastMCP.from_fastapi(
10 app=app,
11 route_maps=custom_maps
12)

That's all there is to it. See Parameter handling for how FastMCP treats query and path parameters, and Validating the created MCP server components for how to list the generated components.

The FastAPI route has two extra benefits: it supports all FastAPI features, including authentication, and the MCP server talks to the FastAPI application directly over the ASGI transport, so there's no additional network overhead.

Once your API is available as an MCP server, the same FastMCP features apply as for hand-written servers. For example, you can use LLM sampling with FastMCP to let the server call the client's LLM, or see how we run an MCP server for BigQuery to give AI agents access to a data warehouse.


Interested in building high-quality AI agent systems?

We prepared a comprehensive guide based on cutting-edge research for how to build robust, reliable AI agent systems that actually work in production. This guide covers:

  • Understanding the 14 systematic failure modes in multi-agent systems
  • Evidence-based best practices for agent design
  • Structured communication protocols and verification mechanisms

Further reading

Andreas Nigg

About the author

Andreas Nigg

Co-Founder, Pondhouse Data

Andreas has spent more than eight years building data applications. At Pondhouse Data he designs and ships AI systems and integrates them into complex, real-world environments.