Spaces:
Running
Running
Jeremiah Lowin commited on
Commit ·
2cbf3b1
1
Parent(s): 7e21d4d
Treat all openapi routes as tools
Browse files- docs/servers/openapi.mdx +18 -37
- src/fastmcp/server/openapi.py +3 -9
- src/fastmcp/server/server.py +1 -34
docs/servers/openapi.mdx
CHANGED
|
@@ -41,17 +41,9 @@ That's it! Your entire API is now available as an MCP server. Clients can discov
|
|
| 41 |
|
| 42 |
## Route Mapping
|
| 43 |
|
|
|
|
| 44 |
|
| 45 |
-
|
| 46 |
-
FastMCP analyzes your API specification and automatically creates MCP components based on HTTP semantics and REST conventions. By default, the following rules are used to determine what MCP component to create for each route:
|
| 47 |
-
|
| 48 |
-
| OpenAPI Route | Example | MCP Component |
|
| 49 |
-
|---------------|---------|---------------|
|
| 50 |
-
| `GET` with path params | `GET /users/{id}` | **Resource Template** |
|
| 51 |
-
| `GET` without path params | `GET /stats` | **Resource** |
|
| 52 |
-
| `POST`, `PUT`, `PATCH`, `DELETE`, etc. | `POST /users` | **Tool** |
|
| 53 |
-
|
| 54 |
-
Interally, FastMCP uses an ordered list of `RouteMap` objects to determine how to map OpenAPI routes to various MCP component types.
|
| 55 |
|
| 56 |
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.
|
| 57 |
|
|
@@ -60,33 +52,14 @@ Each `RouteMap` specifies a combination of methods, patterns, and tags, as well
|
|
| 60 |
- **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.
|
| 61 |
- **MCP type**: What MCP component type to create (`TOOL`, `RESOURCE`, `RESOURCE_TEMPLATE`, or `EXCLUDE`)
|
| 62 |
|
| 63 |
-
|
| 64 |
|
| 65 |
```python
|
| 66 |
from fastmcp.server.openapi import RouteMap, MCPType
|
| 67 |
|
| 68 |
DEFAULT_ROUTE_MAPPINGS = [
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
RouteMap(
|
| 72 |
-
methods=["GET"],
|
| 73 |
-
pattern=r".*\{.*\}.*",
|
| 74 |
-
mcp_type=MCPType.RESOURCE_TEMPLATE
|
| 75 |
-
),
|
| 76 |
-
|
| 77 |
-
# GET without path parameters → Resource
|
| 78 |
-
RouteMap(
|
| 79 |
-
methods=["GET"],
|
| 80 |
-
pattern=r".*",
|
| 81 |
-
mcp_type=MCPType.RESOURCE
|
| 82 |
-
),
|
| 83 |
-
|
| 84 |
-
# All other methods → Tool
|
| 85 |
-
RouteMap(
|
| 86 |
-
methods=["*"],
|
| 87 |
-
pattern=r".*",
|
| 88 |
-
mcp_type=MCPType.TOOL
|
| 89 |
-
),
|
| 90 |
]
|
| 91 |
```
|
| 92 |
|
|
@@ -94,20 +67,28 @@ DEFAULT_ROUTE_MAPPINGS = [
|
|
| 94 |
|
| 95 |
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.
|
| 96 |
|
| 97 |
-
For example,
|
| 98 |
|
| 99 |
-
```python {
|
| 100 |
from fastmcp import FastMCP
|
| 101 |
from fastmcp.server.openapi import RouteMap, MCPType
|
| 102 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
mcp = FastMCP.from_openapi(
|
| 104 |
...,
|
| 105 |
-
route_maps=
|
| 106 |
-
RouteMap(mcp_type=MCPType.TOOL),
|
| 107 |
-
],
|
| 108 |
)
|
| 109 |
```
|
| 110 |
|
|
|
|
|
|
|
| 111 |
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:
|
| 112 |
|
| 113 |
```python
|
|
|
|
| 41 |
|
| 42 |
## Route Mapping
|
| 43 |
|
| 44 |
+
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.
|
| 45 |
|
| 46 |
+
While this is a pragmatic default for maximum compatibility, you can easily customize this behavior. Interally, FastMCP uses an ordered list of `RouteMap` objects to determine how to map OpenAPI routes to various MCP component types.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
|
| 48 |
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.
|
| 49 |
|
|
|
|
| 52 |
- **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.
|
| 53 |
- **MCP type**: What MCP component type to create (`TOOL`, `RESOURCE`, `RESOURCE_TEMPLATE`, or `EXCLUDE`)
|
| 54 |
|
| 55 |
+
Here is FastMCP's default rule:
|
| 56 |
|
| 57 |
```python
|
| 58 |
from fastmcp.server.openapi import RouteMap, MCPType
|
| 59 |
|
| 60 |
DEFAULT_ROUTE_MAPPINGS = [
|
| 61 |
+
# All routes become tools
|
| 62 |
+
RouteMap(mcp_type=MCPType.TOOL),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
]
|
| 64 |
```
|
| 65 |
|
|
|
|
| 67 |
|
| 68 |
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.
|
| 69 |
|
| 70 |
+
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:
|
| 71 |
|
| 72 |
+
```python {2, 5-10}
|
| 73 |
from fastmcp import FastMCP
|
| 74 |
from fastmcp.server.openapi import RouteMap, MCPType
|
| 75 |
|
| 76 |
+
# Restore pre-2.8.0 semantic mapping
|
| 77 |
+
semantic_maps = [
|
| 78 |
+
# GET requests with path parameters become ResourceTemplates
|
| 79 |
+
RouteMap(methods=["GET"], pattern=r".*\{.*\}.*", mcp_type=MCPType.RESOURCE_TEMPLATE),
|
| 80 |
+
# All other GET requests become Resources
|
| 81 |
+
RouteMap(methods=["GET"], pattern=r".*", mcp_type=MCPType.RESOURCE),
|
| 82 |
+
]
|
| 83 |
+
|
| 84 |
mcp = FastMCP.from_openapi(
|
| 85 |
...,
|
| 86 |
+
route_maps=semantic_maps,
|
|
|
|
|
|
|
| 87 |
)
|
| 88 |
```
|
| 89 |
|
| 90 |
+
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.
|
| 91 |
+
|
| 92 |
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:
|
| 93 |
|
| 94 |
```python
|
src/fastmcp/server/openapi.py
CHANGED
|
@@ -155,16 +155,10 @@ class RouteMap:
|
|
| 155 |
self.route_type = self.mcp_type
|
| 156 |
|
| 157 |
|
| 158 |
-
# Default route
|
|
|
|
| 159 |
DEFAULT_ROUTE_MAPPINGS = [
|
| 160 |
-
|
| 161 |
-
RouteMap(
|
| 162 |
-
methods=["GET"], pattern=r".*\{.*\}.*", mcp_type=MCPType.RESOURCE_TEMPLATE
|
| 163 |
-
),
|
| 164 |
-
# GET requests without path parameters go to Resource
|
| 165 |
-
RouteMap(methods=["GET"], pattern=r".*", mcp_type=MCPType.RESOURCE),
|
| 166 |
-
# All other HTTP methods go to Tool
|
| 167 |
-
RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL),
|
| 168 |
]
|
| 169 |
|
| 170 |
|
|
|
|
| 155 |
self.route_type = self.mcp_type
|
| 156 |
|
| 157 |
|
| 158 |
+
# Default route mapping: all routes become tools.
|
| 159 |
+
# Users can provide custom route_maps to override this behavior.
|
| 160 |
DEFAULT_ROUTE_MAPPINGS = [
|
| 161 |
+
RouteMap(mcp_type=MCPType.TOOL),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
]
|
| 163 |
|
| 164 |
|
src/fastmcp/server/server.py
CHANGED
|
@@ -1551,28 +1551,11 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 1551 |
route_map_fn: OpenAPIRouteMapFn | None = None,
|
| 1552 |
mcp_component_fn: OpenAPIComponentFn | None = None,
|
| 1553 |
mcp_names: dict[str, str] | None = None,
|
| 1554 |
-
all_routes_as_tools: bool = False,
|
| 1555 |
**settings: Any,
|
| 1556 |
) -> FastMCPOpenAPI:
|
| 1557 |
"""
|
| 1558 |
Create a FastMCP server from an OpenAPI specification.
|
| 1559 |
"""
|
| 1560 |
-
from .openapi import FastMCPOpenAPI, MCPType, RouteMap
|
| 1561 |
-
|
| 1562 |
-
# Deprecated since 2.5.0
|
| 1563 |
-
if all_routes_as_tools:
|
| 1564 |
-
warnings.warn(
|
| 1565 |
-
"The 'all_routes_as_tools' parameter is deprecated and will be removed in a future version. "
|
| 1566 |
-
'Use \'route_maps=[RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]\' instead.',
|
| 1567 |
-
DeprecationWarning,
|
| 1568 |
-
stacklevel=2,
|
| 1569 |
-
)
|
| 1570 |
-
|
| 1571 |
-
if all_routes_as_tools and route_maps:
|
| 1572 |
-
raise ValueError("Cannot specify both all_routes_as_tools and route_maps")
|
| 1573 |
-
|
| 1574 |
-
elif all_routes_as_tools:
|
| 1575 |
-
route_maps = [RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]
|
| 1576 |
|
| 1577 |
return FastMCPOpenAPI(
|
| 1578 |
openapi_spec=openapi_spec,
|
|
@@ -1593,7 +1576,6 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 1593 |
route_map_fn: OpenAPIRouteMapFn | None = None,
|
| 1594 |
mcp_component_fn: OpenAPIComponentFn | None = None,
|
| 1595 |
mcp_names: dict[str, str] | None = None,
|
| 1596 |
-
all_routes_as_tools: bool = False,
|
| 1597 |
httpx_client_kwargs: dict[str, Any] | None = None,
|
| 1598 |
**settings: Any,
|
| 1599 |
) -> FastMCPOpenAPI:
|
|
@@ -1601,22 +1583,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 1601 |
Create a FastMCP server from a FastAPI application.
|
| 1602 |
"""
|
| 1603 |
|
| 1604 |
-
from .openapi import FastMCPOpenAPI
|
| 1605 |
-
|
| 1606 |
-
# Deprecated since 2.5.0
|
| 1607 |
-
if all_routes_as_tools:
|
| 1608 |
-
warnings.warn(
|
| 1609 |
-
"The 'all_routes_as_tools' parameter is deprecated and will be removed in a future version. "
|
| 1610 |
-
'Use \'route_maps=[RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]\' instead.',
|
| 1611 |
-
DeprecationWarning,
|
| 1612 |
-
stacklevel=2,
|
| 1613 |
-
)
|
| 1614 |
-
|
| 1615 |
-
if all_routes_as_tools and route_maps:
|
| 1616 |
-
raise ValueError("Cannot specify both all_routes_as_tools and route_maps")
|
| 1617 |
-
|
| 1618 |
-
elif all_routes_as_tools:
|
| 1619 |
-
route_maps = [RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]
|
| 1620 |
|
| 1621 |
if httpx_client_kwargs is None:
|
| 1622 |
httpx_client_kwargs = {}
|
|
|
|
| 1551 |
route_map_fn: OpenAPIRouteMapFn | None = None,
|
| 1552 |
mcp_component_fn: OpenAPIComponentFn | None = None,
|
| 1553 |
mcp_names: dict[str, str] | None = None,
|
|
|
|
| 1554 |
**settings: Any,
|
| 1555 |
) -> FastMCPOpenAPI:
|
| 1556 |
"""
|
| 1557 |
Create a FastMCP server from an OpenAPI specification.
|
| 1558 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1559 |
|
| 1560 |
return FastMCPOpenAPI(
|
| 1561 |
openapi_spec=openapi_spec,
|
|
|
|
| 1576 |
route_map_fn: OpenAPIRouteMapFn | None = None,
|
| 1577 |
mcp_component_fn: OpenAPIComponentFn | None = None,
|
| 1578 |
mcp_names: dict[str, str] | None = None,
|
|
|
|
| 1579 |
httpx_client_kwargs: dict[str, Any] | None = None,
|
| 1580 |
**settings: Any,
|
| 1581 |
) -> FastMCPOpenAPI:
|
|
|
|
| 1583 |
Create a FastMCP server from a FastAPI application.
|
| 1584 |
"""
|
| 1585 |
|
| 1586 |
+
from .openapi import FastMCPOpenAPI
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1587 |
|
| 1588 |
if httpx_client_kwargs is None:
|
| 1589 |
httpx_client_kwargs = {}
|