New in version: 2.0.0

FastMCP can automatically generate an MCP server from an OpenAPI specification. Users only need to provide an OpenAPI specification (3.0 or 3.1) and an API client.

import httpx
from fastmcp import FastMCP

# Create a client for your API
api_client = httpx.AsyncClient(base_url="https://api.example.com")

# Load your OpenAPI spec
spec = {...} 

# Create an MCP server from your OpenAPI spec
mcp = FastMCP.from_openapi(openapi_spec=spec, client=api_client)

if __name__ == "__main__":
    mcp.run()

Configuration Options

Timeout

You can set a timeout for all API requests:

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

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

Route Mapping

By default, OpenAPI routes are mapped to MCP components based on these rules:

OpenAPI RouteExampleMCP ComponentNotes
GET without path paramsGET /statsResourceSimple resources for fetching data
GET with path paramsGET /users/{id}Resource TemplatePath parameters become template parameters
POST, PUT, PATCH, DELETE, etc.POST /usersToolOperations that modify data

Internally, FastMCP uses a priority-ordered set of RouteMap objects to determine the component type. Route maps indicate that a specific HTTP method (or methods) and path pattern should be treated as a specific component type. This is the default set of route maps:

# Simplified version of the actual mapping rules
DEFAULT_ROUTE_MAPPINGS = [
    # GET with path parameters -> ResourceTemplate
    RouteMap(methods=["GET"], pattern=r".*\{.*\}.*", 
             route_type=RouteType.RESOURCE_TEMPLATE),
    
    # GET without path parameters -> Resource
    RouteMap(methods=["GET"], pattern=r".*", 
             route_type=RouteType.RESOURCE),
    
    # All other methods -> Tool
    RouteMap(methods=["POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"],
             pattern=r".*", route_type=RouteType.TOOL),
]

Custom Route Maps

Users can add custom route maps to override the default mapping behavior. User-supplied route maps are always applied first, before the default route maps.

from fastmcp.server.openapi import RouteMap, RouteType

# Custom mapping rules
custom_maps = [
    # Force all analytics endpoints to be Tools
    RouteMap(methods=["GET"], 
             pattern=r"^/analytics/.*", 
             route_type=RouteType.TOOL)
]

# Apply custom mappings
mcp = await FastMCP.from_openapi(
    openapi_spec=spec,
    client=api_client,
    route_maps=custom_maps
)

How It Works

  1. FastMCP parses your OpenAPI spec to extract routes and schemas
  2. It applies mapping rules to categorize each route
  3. When an MCP client calls a tool or accesses a resource:
    • FastMCP constructs an HTTP request based on the OpenAPI definition
    • It sends the request through the provided httpx client
    • It translates the HTTP response to the appropriate MCP format

Request Parameter Handling

FastMCP carefully handles different types of parameters in OpenAPI requests:

Query Parameters

By default, FastMCP will only include query parameters that have non-empty values. Parameters with None values or empty strings ("") are automatically filtered out of requests. This ensures that API servers don’t receive unnecessary empty parameters that might cause issues.

For example, if you call a tool with these parameters:

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

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

Path Parameters

For path parameters, which are typically required by REST APIs, FastMCP filters out None values and checks that all required path parameters are provided. If a required path parameter is missing or None, an error will be raised.

# This will work
await client.call_tool("get_product", {"product_id": 123})

# This will raise ValueError: "Missing required path parameters: {'product_id'}"
await client.call_tool("get_product", {"product_id": None})

Complete Example

import asyncio

import httpx

from fastmcp import FastMCP

# Sample OpenAPI spec for a Pet Store API
petstore_spec = {
    "openapi": "3.0.0",
    "info": {
        "title": "Pet Store API",
        "version": "1.0.0",
        "description": "A sample API for managing pets",
    },
    "paths": {
        "/pets": {
            "get": {
                "operationId": "listPets",
                "summary": "List all pets",
                "responses": {"200": {"description": "A list of pets"}},
            },
            "post": {
                "operationId": "createPet",
                "summary": "Create a new pet",
                "responses": {"201": {"description": "Pet created successfully"}},
            },
        },
        "/pets/{petId}": {
            "get": {
                "operationId": "getPet",
                "summary": "Get a pet by ID",
                "parameters": [
                    {
                        "name": "petId",
                        "in": "path",
                        "required": True,
                        "schema": {"type": "string"},
                    }
                ],
                "responses": {
                    "200": {"description": "Pet details"},
                    "404": {"description": "Pet not found"},
                },
            }
        },
    },
}


async def check_mcp(mcp: FastMCP):
    # List what components were created
    tools = await mcp.get_tools()
    resources = await mcp.get_resources()
    templates = await mcp.get_resource_templates()

    print(
        f"{len(tools)} Tool(s): {', '.join([t.name for t in tools.values()])}"
    )  # Should include createPet
    print(
        f"{len(resources)} Resource(s): {', '.join([r.name for r in resources.values()])}"
    )  # Should include listPets
    print(
        f"{len(templates)} Resource Template(s): {', '.join([t.name for t in templates.values()])}"
    )  # Should include getPet

    return mcp


if __name__ == "__main__":
    # Client for the Pet Store API
    client = httpx.AsyncClient(base_url="https://petstore.example.com/api")

    # Create the MCP server
    mcp = FastMCP.from_openapi(
        openapi_spec=petstore_spec, client=client, name="PetStore"
    )

    asyncio.run(check_mcp(mcp))

    # Start the MCP server
    mcp.run()