Spaces:
Running
Running
File size: 14,684 Bytes
d625dbe 54dc4d4 d625dbe 54dc4d4 d625dbe 54dc4d4 d625dbe 54dc4d4 ca8cdf5 54dc4d4 ca8cdf5 54dc4d4 d625dbe ca8cdf5 d625dbe ca8cdf5 d625dbe ca8cdf5 d625dbe 54dc4d4 ca8cdf5 54dc4d4 ca8cdf5 d625dbe 2cbf3b1 d625dbe d14a99a d625dbe ca8cdf5 d625dbe ffbebf8 ca8cdf5 54dc4d4 d625dbe 2cbf3b1 d625dbe 2cbf3b1 d625dbe ca8cdf5 2cbf3b1 d625dbe 54dc4d4 ca8cdf5 d625dbe 2cbf3b1 d625dbe 54dc4d4 2cbf3b1 ca8cdf5 2cbf3b1 ca8cdf5 54dc4d4 d625dbe ca8cdf5 d625dbe ca8cdf5 d625dbe ca8cdf5 d625dbe ca8cdf5 d625dbe ca8cdf5 d625dbe ca8cdf5 d625dbe ca8cdf5 d625dbe 54dc4d4 ca8cdf5 d625dbe ca8cdf5 54dc4d4 ca8cdf5 54dc4d4 ca8cdf5 54dc4d4 ca8cdf5 54dc4d4 ca8cdf5 d625dbe 3993fde ca8cdf5 3993fde ca8cdf5 3993fde ca8cdf5 3993fde ca8cdf5 3993fde ca8cdf5 3993fde ca8cdf5 3993fde 54dc4d4 3993fde 54dc4d4 3993fde 1bfd496 0d426dc 1bfd496 0d426dc 1bfd496 54dc4d4 1bfd496 54dc4d4 1bfd496 54dc4d4 1bfd496 54dc4d4 1bfd496 0d426dc 54dc4d4 ca8cdf5 3993fde ca8cdf5 54dc4d4 ca8cdf5 54dc4d4 ca8cdf5 54dc4d4 d625dbe ca8cdf5 d625dbe ca8cdf5 d625dbe ca8cdf5 d625dbe ca8cdf5 d625dbe ca8cdf5 d625dbe ca8cdf5 d625dbe ca8cdf5 d625dbe ca8cdf5 d625dbe ca8cdf5 d625dbe ca8cdf5 54dc4d4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 | ---
title: OpenAPI π€ FastMCP
sidebarTitle: OpenAPI
description: Generate MCP servers from any OpenAPI specification
icon: list-tree
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="2.0.0" />
FastMCP can automatically generate an MCP server from any OpenAPI specification, allowing AI models to interact with existing APIs through the MCP protocol. Instead of manually creating tools and resources, you provide an OpenAPI spec and FastMCP intelligently converts API endpoints into the appropriate MCP components.
<Tip>
Generating MCP servers from OpenAPI is a great way to get started with FastMCP, but in practice LLMs achieve **significantly better performance** with well-designed and curated MCP servers than with auto-converted OpenAPI servers. This is especially true for complex APIs with many endpoints and parameters.
</Tip>
## Create a Server
To convert an OpenAPI specification to an MCP server, use the `FastMCP.from_openapi()` class method:
```python server.py
import httpx
from fastmcp import FastMCP
# Create an HTTP client for your API
client = httpx.AsyncClient(base_url="https://api.example.com")
# Load your OpenAPI spec
openapi_spec = httpx.get("https://api.example.com/openapi.json").json()
# Create the MCP server
mcp = FastMCP.from_openapi(
openapi_spec=openapi_spec,
client=client,
name="My API Server"
)
if __name__ == "__main__":
mcp.run()
```
### Authentication
If your API requires authentication, configure it on the HTTP client:
```python
import httpx
from fastmcp import FastMCP
# Bearer token authentication
api_client = httpx.AsyncClient(
base_url="https://api.example.com",
headers={"Authorization": "Bearer YOUR_TOKEN"}
)
# Create MCP server with authenticated client
mcp = FastMCP.from_openapi(
openapi_spec=spec,
client=api_client,
timeout=30.0 # 30 second timeout for all requests
)
```
## Route Mapping
By default, FastMCP converts **every endpoint** in your OpenAPI specification into an MCP **Tool**. This provides a simple, predictable starting point that ensures all your API's functionality is immediately available to the vast majority of LLM clients which only support MCP tools.
While this is a pragmatic default for maximum compatibility, you can easily customize this behavior. Internally, FastMCP uses an ordered list of `RouteMap` objects to determine how to map OpenAPI routes to various MCP component types.
Each `RouteMap` specifies a combination of methods, patterns, and tags, as well as a corresponding MCP component type. Each OpenAPI route is checked against each `RouteMap` in order, and the first one that matches every criteria is used to determine its converted MCP type. A special type, `EXCLUDE`, can be used to exclude routes from the MCP server entirely.
- **Methods**: HTTP methods to match (e.g. `["GET", "POST"]` or `"*"` for all)
- **Pattern**: Regex pattern to match the route path (e.g. `r"^/users/.*"` or `r".*"` for all)
- **Tags**: A set of OpenAPI tags that must all be present. An empty set (`{}`) means no tag filtering, so the route matches regardless of its tags.
- **MCP type**: What MCP component type to create (`TOOL`, `RESOURCE`, `RESOURCE_TEMPLATE`, or `EXCLUDE`)
- **MCP tags**: A set of custom tags to add to components created from matching routes
Here is FastMCP's default rule:
```python
from fastmcp.server.openapi import RouteMap, MCPType
DEFAULT_ROUTE_MAPPINGS = [
# All routes become tools
RouteMap(mcp_type=MCPType.TOOL),
]
```
### Custom Route Maps
When creating your FastMCP server, you can customize routing behavior by providing your own list of `RouteMap` objects. Your custom maps are processed before the default route maps, and routes will be assigned to the first matching custom map.
For example, prior to FastMCP 2.8.0, GET requests were automatically mapped to `Resource` and `ResourceTemplate` components based on whether they had path parameters. (This was changed solely for client compatibility reasons.) You can restore this behavior by providing custom route maps:
```python
from fastmcp import FastMCP
from fastmcp.server.openapi import RouteMap, MCPType
# Restore pre-2.8.0 semantic mapping
semantic_maps = [
# GET requests with path parameters become ResourceTemplates
RouteMap(methods=["GET"], pattern=r".*\{.*\}.*", mcp_type=MCPType.RESOURCE_TEMPLATE),
# All other GET requests become Resources
RouteMap(methods=["GET"], pattern=r".*", mcp_type=MCPType.RESOURCE),
]
mcp = FastMCP.from_openapi(
openapi_spec=spec,
client=client,
route_maps=semantic_maps,
)
```
With these maps, `GET` requests are handled semantically, and all other methods (`POST`, `PUT`, etc.) will fall through to the default rule and become `Tool`s.
Here is a more complete example that uses custom route maps to convert all `GET` endpoints under `/analytics/` to tools while excluding all admin endpoints and all routes tagged "internal". All other routes will be handled by the default rules:
```python
from fastmcp import FastMCP
from fastmcp.server.openapi import RouteMap, MCPType
mcp = FastMCP.from_openapi(
openapi_spec=spec,
client=client,
route_maps=[
# Analytics `GET` endpoints are tools
RouteMap(
methods=["GET"],
pattern=r"^/analytics/.*",
mcp_type=MCPType.TOOL,
),
# Exclude all admin endpoints
RouteMap(
pattern=r"^/admin/.*",
mcp_type=MCPType.EXCLUDE,
),
# Exclude all routes tagged "internal"
RouteMap(
tags={"internal"},
mcp_type=MCPType.EXCLUDE,
),
],
)
```
<Tip>
The default route maps are always applied after your custom maps, so you do not have to create route maps for every possible route.
</Tip>
### Excluding Routes
To exclude routes from the MCP server, use a route map to assign them to `MCPType.EXCLUDE`.
You can use this to remove sensitive or internal routes by targeting them specifically:
```python
from fastmcp import FastMCP
from fastmcp.server.openapi import RouteMap, MCPType
mcp = FastMCP.from_openapi(
openapi_spec=spec,
client=client,
route_maps=[
RouteMap(pattern=r"^/admin/.*", mcp_type=MCPType.EXCLUDE),
RouteMap(tags={"internal"}, mcp_type=MCPType.EXCLUDE),
],
)
```
Or you can use a catch-all rule to exclude everything that your maps don't handle explicitly:
```python
from fastmcp import FastMCP
from fastmcp.server.openapi import RouteMap, MCPType
mcp = FastMCP.from_openapi(
openapi_spec=spec,
client=client,
route_maps=[
# custom mapping logic goes here
# ... your specific route maps ...
# exclude all remaining routes
RouteMap(mcp_type=MCPType.EXCLUDE),
],
)
```
<Tip>
Using a catch-all exclusion rule will prevent the default route mappings from being applied, since it will match every remaining route. This is useful if you want to explicitly allow-list certain routes.
</Tip>
### Advanced Route Mapping
<VersionBadge version="2.5.0" />
For advanced use cases that require more complex logic, you can provide a `route_map_fn` callable. After the route map logic is applied, this function is called on each matched route and its assigned MCP component type. It can optionally return a different component type to override the mapped assignment. If it returns `None`, the assigned type is used.
In addition to more precise targeting of methods, patterns, and tags, this function can access any additional OpenAPI metadata about the route.
<Tip>
The `route_map_fn` **is** called on routes that matched `MCPType.EXCLUDE` in your custom maps, giving you an opportunity to override the exclusion.
</Tip>
```python
from fastmcp import FastMCP
from fastmcp.server.openapi import RouteMap, MCPType, HTTPRoute
def custom_route_mapper(route: HTTPRoute, mcp_type: MCPType) -> MCPType | None:
"""Advanced route type mapping."""
# Convert all admin routes to tools regardless of HTTP method
if "/admin/" in route.path:
return MCPType.TOOL
elif "internal" in route.tags:
return MCPType.EXCLUDE
# Convert user detail routes to templates even if they're POST
elif route.path.startswith("/users/") and route.method == "POST":
return MCPType.RESOURCE_TEMPLATE
# Use defaults for all other routes
return None
mcp = FastMCP.from_openapi(
openapi_spec=spec,
client=client,
route_map_fn=custom_route_mapper,
)
```
## Customization
### Component Names
<VersionBadge version="2.5.0" />
FastMCP automatically generates names for MCP components based on the OpenAPI specification. By default, it uses the `operationId` from your OpenAPI spec, up to the first double underscore (`__`).
All component names are automatically:
- **Slugified**: Spaces and special characters are converted to underscores or removed
- **Truncated**: Limited to 56 characters maximum to ensure compatibility
- **Unique**: If multiple components have the same name, a number is automatically appended to make them unique
For more control over component names, you can provide an `mcp_names` dictionary that maps `operationId` values to your desired names. The `operationId` must be exactly as it appears in the OpenAPI spec. The provided name will always be slugified and truncated.
```python
mcp = FastMCP.from_openapi(
openapi_spec=spec,
client=client,
mcp_names={
"list_users__with_pagination": "user_list",
"create_user__admin_required": "create_user",
"get_user_details__admin_required": "user_detail",
}
)
```
Any `operationId` not found in `mcp_names` will use the default strategy (operationId up to the first `__`).
### Tags
<VersionBadge version="2.8.0" />
FastMCP provides several ways to add tags to your MCP components, allowing you to categorize and organize them for better discoverability and filtering. Tags are combined from multiple sources to create the final set of tags on each component.
#### RouteMap Tags
You can add custom tags to components created from specific routes using the `mcp_tags` parameter in `RouteMap`. These tags will be applied to all components created from routes that match that particular route map.
```python
from fastmcp.server.openapi import RouteMap, MCPType
mcp = FastMCP.from_openapi(
openapi_spec=spec,
client=client,
route_maps=[
# Add custom tags to all POST endpoints
RouteMap(
methods=["POST"],
pattern=r".*",
mcp_type=MCPType.TOOL,
mcp_tags={"write-operation", "api-mutation"}
),
# Add different tags to detail view endpoints
RouteMap(
methods=["GET"],
pattern=r".*\{.*\}.*",
mcp_type=MCPType.RESOURCE_TEMPLATE,
mcp_tags={"detail-view", "parameterized"}
),
# Add tags to list endpoints
RouteMap(
methods=["GET"],
pattern=r".*",
mcp_type=MCPType.RESOURCE,
mcp_tags={"list-data", "collection"}
),
],
)
```
#### Global Tags
You can add tags to **all** components by providing a `tags` parameter when creating your MCP server. These global tags will be applied to every component created from your OpenAPI specification.
```python
mcp = FastMCP.from_openapi(
openapi_spec=spec,
client=client,
tags={"api-v2", "production", "external"}
)
```
### Advanced Customization
<VersionBadge version="2.5.0" />
By default, FastMCP creates MCP components using a variety of metadata from the OpenAPI spec, such as incorporating the OpenAPI description into the MCP component description.
At times you may want to modify those MCP components in a variety of ways, such as adding LLM-specific instructions or tags. For fine-grained customization, you can provide a `mcp_component_fn` when creating the MCP server. After each MCP component has been created, this function is called on it and has the opportunity to modify it in-place.
<Tip>
Your `mcp_component_fn` is expected to modify the component in-place, not to return a new component. The result of the function is ignored.
</Tip>
```python
from fastmcp.server.openapi import (
HTTPRoute,
OpenAPITool,
OpenAPIResource,
OpenAPIResourceTemplate,
)
def customize_components(
route: HTTPRoute,
component: OpenAPITool | OpenAPIResource | OpenAPIResourceTemplate,
) -> None:
# Add custom tags to all components
component.tags.add("openapi")
# Customize based on component type
if isinstance(component, OpenAPITool):
component.description = f"π§ {component.description} (via API)"
if isinstance(component, OpenAPIResource):
component.description = f"π {component.description}"
component.tags.add("data")
mcp = FastMCP.from_openapi(
openapi_spec=spec,
client=client,
mcp_component_fn=customize_components,
)
```
## Request Parameter Handling
FastMCP intelligently handles different types of parameters in OpenAPI requests:
### Query Parameters
By default, FastMCP only includes query parameters that have non-empty values. Parameters with `None` values or empty strings are automatically filtered out.
```python
# When calling this tool...
await client.call_tool("search_products", {
"category": "electronics", # β
Included
"min_price": 100, # β
Included
"max_price": None, # β Excluded
"brand": "", # β Excluded
})
# The HTTP request will be: GET /products?category=electronics&min_price=100
```
### Path Parameters
Path parameters are typically required by REST APIs. FastMCP:
- Filters out `None` values
- Validates that all required path parameters are provided
- Raises clear errors for missing required parameters
```python
# β
This works
await client.call_tool("get_user", {"user_id": 123})
# β This raises: "Missing required path parameters: {'user_id'}"
await client.call_tool("get_user", {"user_id": None})
```
### Array Parameters
FastMCP handles array parameters according to OpenAPI specifications:
- **Query arrays**: Serialized based on the `explode` parameter (default: `True`)
- **Path arrays**: Serialized as comma-separated values (OpenAPI 'simple' style)
```python
# Query array with explode=true (default)
# ?tags=red&tags=blue&tags=green
# Query array with explode=false
# ?tags=red,blue,green
# Path array (always comma-separated)
# /items/red,blue,green
```
### Headers
Header parameters are automatically converted to strings and included in the HTTP request. |