--- title: OpenAPI Integration sidebarTitle: OpenAPI description: Automatically create FastMCP servers from existing OpenAPI specifications. icon: code-branch --- If you have existing REST APIs documented with the OpenAPI Specification (OAS), FastMCP can automatically generate MCP tools, resources, and resource templates directly from that specification. This provides a quick way to make your existing HTTP APIs accessible to MCP clients and LLMs. FastMCP supports both OpenAPI 3.0 and 3.1 specifications for maximum compatibility with existing API definitions. ## The Goal: API -> MCP Server The core idea is to map OpenAPI paths and operations (like `GET /users/{id}` or `POST /orders`) to their corresponding MCP components: - `GET` requests often map to MCP **Resources** (for fetching single items) or **Resource Templates** (if the path has parameters). - `POST`, `PUT`, `PATCH`, `DELETE` requests typically map to MCP **Tools** (for actions that create or modify data). FastMCP automates this mapping process. ## Creating from OpenAPI Spec Use the `FastMCP.from_openapi()` class method. You need: 1. The OpenAPI specification as a Python dictionary. 2. An `httpx.AsyncClient` configured to make requests to the actual API backend. ```python server.py import asyncio import httpx from fastmcp import FastMCP # load the OpenAPI specification from the openapi_spec.py file petstore_spec = PETSTORE_SPEC # Client to communicate with the actual Pet Store API backend # The base_url should match the server URL in the OpenAPI spec http_client = httpx.AsyncClient(base_url="http://petstore.example.com/api") # Create the FastMCP server from the spec # This is an async class method async def create_openapi_server(): mcp_server = await FastMCP.from_openapi( openapi_spec=petstore_spec, client=http_client, name="PetStoreMCP" # Optional name for the MCP server ) return mcp_server async def run_server(): server = await create_openapi_server() print(f"Starting OpenAPI-based server '{server.name}'...") # List discovered components tools = await server.list_tools() resources = await server.list_resources() templates = await server.list_resource_templates() print("Discovered Tools:", [t.name for t in tools]) print("Discovered Resources:", [r.uri for r in resources]) # Should be empty if no parameterless GETs print("Discovered Templates:", [t.uriTemplate for t in templates]) # Run the server (e.g., via stdio) # server.run() if __name__ == "__main__": # Example: Create the server and print discovered components # Requires httpx: uv pip install httpx asyncio.run(run_server()) # Expected Output might include: # Discovered Tools: ['listPets', 'createPet'] # Discovered Resources: [] # Discovered Templates: ['resource://openapi/showPetById/{petId}'] ``` ```python openapi_spec.py # Example OpenAPI Specification (simplified Pet Store) PETSTORE_SPEC = { "openapi": "3.1.0", "info": {"title": "Simple Pet Store", "version": "1.0.0"}, "servers": [{"url": "http://petstore.example.com/api"}], # Base URL for API calls "paths": { "/pets": { "get": { "summary": "List all pets", "operationId": "listPets", "tags": ["pets"], "parameters": [{ # Query parameter -> Tool argument "name": "limit", "in": "query", "schema": {"type": "integer"} }], "responses": {"200": {"description": "A list of pets."}}, }, "post": { # POST -> Tool "summary": "Create a pet", "operationId": "createPet", "tags": ["pets"], "requestBody": { # Request body -> Tool arguments "required": True, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/PetInput"}}} }, "responses": {"201": {"description": "Pet created."}}, }, }, "/pets/{petId}": { # Path parameter -> Resource Template "get": { # GET with path param -> Resource Template / FunctionResource "summary": "Info for a specific pet", "operationId": "showPetById", "tags": ["pets"], "parameters": [{ # Path parameter -> Template function argument "name": "petId", "in": "path", "required": True, "schema": {"type": "string"} }], "responses": {"200": {"description": "Information about the pet."}}, }, }, }, "components": { "schemas": { "PetInput": {"type": "object", "properties": {"name": {"type": "string"}, "tag": {"type": "string"}}}, } } } ``` ### How it Works Internally 1. **Parsing**: `from_openapi` parses the spec using utilities that leverage `openapi-pydantic`. It extracts paths, operations, parameters, request bodies, and responses. 2. **Mapping**: It applies mapping rules (see below) to decide whether each OpenAPI operation (`GET /pets`, `POST /pets`, `GET /pets/{petId}`) becomes an MCP `Tool`, `Resource`, or `ResourceTemplate`. 3. **Component Creation**: It creates specialized internal components (`OpenAPITool`, `OpenAPIResource`, `OpenAPIResourceTemplate`). 4. **HTTP Execution**: When an MCP client calls a tool or reads a resource from this server: * The corresponding OpenAPI component constructs an HTTP request based on the OpenAPI definition and the arguments provided by the MCP client. * It uses the provided `httpx.AsyncClient` to send the request to the backend API. * It processes the HTTP response and returns it to the MCP client in the appropriate MCP format. 5. **Schema Generation**: The schemas for MCP tools are derived by combining OpenAPI parameters (path, query, header) and request body schemas. Resource template function arguments are derived from path parameters. 6. **Descriptions**: Tool/Resource descriptions are enhanced with information from OpenAPI responses to give the LLM more context about potential outcomes. ### Default Mapping Rules FastMCP uses the following default rules to map OpenAPI operations: - `GET` operation with path parameters (e.g., `/users/{id}`) -> **`ResourceTemplate`** - `GET` operation without path parameters (e.g., `/users`) -> **`Resource`** - `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS`, `HEAD` -> **`Tool`** ### Customize Route Mapping You can customize the mapping rules by providing a list of `RouteMap` objects directly to `FastMCP.from_openapi()` using the `route_maps` parameter: ```python from fastmcp.server.openapi import RouteMap, RouteType from fastmcp import FastMCP # Custom mapping: Treat GET /admin/stats as a Tool, not a Resource custom_maps = [ RouteMap(methods=["GET"], pattern=r"^/admin/stats$", route_type=RouteType.TOOL) ] async def create_server_with_custom_mapping(): mcp_server = await FastMCP.from_openapi( openapi_spec=petstore_spec, client=http_client, name="PetStoreMCP", route_maps=custom_maps # Pass custom mapping rules ) return mcp_server ``` Each `RouteMap` maps one or more HTTP methods and a regular expression pattern for the route path to an MCP `RouteType`. Route maps are processed in order, and the first match wins. All parameters passed to `FastMCP.from_openapi()` will be forwarded to the underlying `FastMCPOpenAPI` constructor, so you can customize any aspect of the OpenAPI integration directly through this method call.