Automating MCP Server Creation from OpenAPI and FastAPI

blog preview

In our recent blog post about creating an MCP server with FastMCP we've described what MCP servers are and why they are important for the current AI ecosystem. We've also discussed various resources an MCP server can provide and shown how to create such a server from scratch.

While this was a great introduction, and it is remarkably easy to create an MCP server using the tooling around them, it's still a bit of a conundrum: MCP servers - at it's core - only provide a standardized API which is optimized for LLMs. (I don't mean to undersell the importance of this. It's a very much needed standardization and provides a whole lot of benefits. But in the end of the day, it's just and API). And APIs are a thing you most probably already have in your applications. So what you end up with MCP servers is wrapping another API around your existing APIs.

That's where this blog post comes in. While we still wrap around the MCP server API around your existing one, we show you a way of how to automatically generate an MCP server from your existing OpenAPI specifications or FastAPI application. This way you can easily use your already existing APIs and make them available in the MCP ecosystem and therefore for virtually any LLM client out there.

Automatically creating an MCP server from an OpenAPI specification

If you already have an existing API with an OpenAPI specification, you're in luck. FastMCP provides a powerful feature that allows you to generate an MCP server directly from your OpenAPI spec with minimal effort. This functionality, introduced in FastMCP version 2.0.0, creates a bridge between your existing API infrastructure and the LLM-friendly MCP ecosystem.

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 will analyze your OpenAPI specification, create the appropriate MCP components, and handle all the routing between MCP requests and your underlying API.

Configuration Options

You can configure MCP server timeouts and route mappings as follows:

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.

Route Mapping

We somehow need to map the API routes to MCP components. If you refer to our previous post, there are two main components delivering data: resources and tools. Resources are read-only endpoints, while tools are action-oriented endpoints.

FastMCP makes an educated guess about how to interpret and map your API routes to these components.

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

While this default mapping might work well for most APIs, we also need a way to provide a custom mapping. For example a GET endpoint might very well also be a tool. Or a POST endpoint in some cases might be a resource.

To customize the mapping, you can define your own route maps. Simply define a RouteMap object with the desired HTTP methods, URL pattern, and specify the route type (either RouteType.TOOL or RouteType.RESOURCE).

You then pass this mapping to the FastMCP.from_openapi method.

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()

Your custom route maps are applied first, before the default mapping rules.

How It 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, only parameters with non-empty values are included in requests. Parameters with None values or empty strings ("") are automatically filtered out, ensuring your API doesn't receive unnecessary 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 will only include category=electronics&min_price=100.

Path Parameters

For path parameters, FastMCP ensures all required parameters are provided and have non-null values (because you can't omit path parameters in 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

After following the above steps, and running the MCP server with mcp.run(), you can view the created components as follows. Create the following function and call it with asyncio.run(check_mcp(mcp)). It will show all Tools and Resources created by 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 you're already using FastAPI for your backend services, FastMCP also provides a seamless way to expose these as MCP servers. This integration, introduced in FastMCP version 2.0.0, allows you to convert your existing FastAPI applications into MCP servers with minimal code changes.

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

Converting a FastAPI application to an MCP server works as follows. 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}

All we need to do is to call FastMCP.from_fastapi(app) and run the mcp server:

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

Configuration Options

Similarly to 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)

Route Mapping

You can also provide custom route mappings for your FastAPI application by defining a list of RouteMap objects, just like with the OpenAPI integration. This allows you to customize how FastMCP interprets your FastAPI routes and maps them 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 actually all there is to it. Please refer to the Parameter Handling section above for details on how FastMCP handles query and path parameters. And to the Validating the created MCP server components for how to check the created components.

This way of creating an MCP server has two additional benefits: It supports all FastAPI features, including authentication and the communication between your MCP server and the FastAPI application runs directly on the ASGI transport - no additional overhead is introduced.


Interested in how to train your very own Large Language Model?

We prepared a well-researched guide for how to use the latest advancements in Open Source technology to fine-tune your own LLM. This has many advantages like:

  • Cost control
  • Data privacy
  • Excellent performance - adjusted specifically for your intended use

Further Reading

More information on our managed RAG solution?
To Pondhouse AI
More tips and tricks on how to work with AI?
To our Blog