mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-09 23:29:10 +02:00
226 lines
6.8 KiB
Text
226 lines
6.8 KiB
Text
---
|
|
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=["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.
|
|
|
|
```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
|
|
)
|
|
```
|
|
|
|
## 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()
|
|
```
|
|
|