Spaces:
Running
Running
| --- | |
| title: OpenAPI Integration | |
| sidebarTitle: OpenAPI | |
| description: Generate MCP servers from OpenAPI specs | |
| icon: code-branch | |
| --- | |
| import { VersionBadge } from '/snippets/version-badge.mdx' | |
| <VersionBadge 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. | |
| ```python | |
| 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: | |
| ```python | |
| # 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 Route | Example |MCP Component | Notes | | |
| |- | - | - | - | | |
| | `GET` without path params | `GET /stats` | Resource | Simple resources for fetching data | | |
| | `GET` with path params | `GET /users/{id}` | Resource Template | Path parameters become template parameters | | |
| | `POST`, `PUT`, `PATCH`, `DELETE`, etc. | `POST /users` | Tool | Operations 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: | |
| ```python | |
| # 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="*", | |
| 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. | |
| ```python | |
| 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 | |
| ) | |
| ``` | |
| ### All Routes as Tools | |
| When building AI agent backends, it's often useful to treat all routes as callable tools regardless of their HTTP method. You can use the `all_routes_as_tools` parameter to automatically map every route to a Tool: | |
| ```python | |
| # Make all endpoints tools, regardless of HTTP method | |
| mcp = FastMCP.from_openapi( | |
| openapi_spec=spec, | |
| client=api_client, | |
| all_routes_as_tools=True | |
| ) | |
| ``` | |
| This is equivalent to defining a single route map that matches all routes: | |
| ```python | |
| # Same effect as all_routes_as_tools=True | |
| mcp = FastMCP.from_openapi( | |
| openapi_spec=spec, | |
| client=api_client, | |
| route_maps=[ | |
| RouteMap(methods="*", pattern=r".*", route_type=RouteType.TOOL) | |
| ] | |
| ) | |
| ``` | |
| Note that `all_routes_as_tools` and `route_maps` cannot be used together - if you need more complex mapping rules, use `route_maps` instead. | |
| ## 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: | |
| ```python | |
| 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. | |
| ```python | |
| # 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 | |
| ```python [expandable] | |
| 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() | |
| ``` | |