From 094ead2a77fa311fe697c2270ab942651c80ea46 Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Tue, 10 Jun 2025 14:42:10 -0400 Subject: [PATCH] Treat all openapi routes as tools --- docs/servers/openapi.mdx | 55 ++++++++++++----------------------- src/fastmcp/server/openapi.py | 12 ++------ src/fastmcp/server/server.py | 35 +--------------------- 3 files changed, 22 insertions(+), 80 deletions(-) diff --git a/docs/servers/openapi.mdx b/docs/servers/openapi.mdx index 580502a49..786aefdfd 100644 --- a/docs/servers/openapi.mdx +++ b/docs/servers/openapi.mdx @@ -41,17 +41,9 @@ That's it! Your entire API is now available as an MCP server. Clients can discov ## 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. - -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: - -| OpenAPI Route | Example | MCP Component | -|---------------|---------|---------------| -| `GET` with path params | `GET /users/{id}` | **Resource Template** | -| `GET` without path params | `GET /stats` | **Resource** | -| `POST`, `PUT`, `PATCH`, `DELETE`, etc. | `POST /users` | **Tool** | - -Interally, FastMCP uses an ordered list of `RouteMap` objects to determine how to map OpenAPI routes to various MCP component types. +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. 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. @@ -60,33 +52,14 @@ Each `RouteMap` specifies a combination of methods, patterns, and tags, as well - **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`) -To illustrate this in practice, here are FastMCP's default rules as a list of `RouteMap` objects: +Here is FastMCP's default rule: ```python from fastmcp.server.openapi import RouteMap, MCPType DEFAULT_ROUTE_MAPPINGS = [ - - # GET with path parameters → ResourceTemplate - RouteMap( - methods=["GET"], - pattern=r".*\{.*\}.*", - mcp_type=MCPType.RESOURCE_TEMPLATE - ), - - # GET without path parameters → Resource - RouteMap( - methods=["GET"], - pattern=r".*", - mcp_type=MCPType.RESOURCE - ), - - # All other methods → Tool - RouteMap( - methods=["*"], - pattern=r".*", - mcp_type=MCPType.TOOL - ), + # All routes become tools + RouteMap(mcp_type=MCPType.TOOL), ] ``` @@ -94,20 +67,28 @@ DEFAULT_ROUTE_MAPPINGS = [ 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, the following simple rule will treat every OpenAPI route as a tool: +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 {7} +```python {2, 5-10} 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( ..., - route_maps=[ - RouteMap(mcp_type=MCPType.TOOL), - ], + 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 diff --git a/src/fastmcp/server/openapi.py b/src/fastmcp/server/openapi.py index 58a0d5037..8f6682799 100644 --- a/src/fastmcp/server/openapi.py +++ b/src/fastmcp/server/openapi.py @@ -155,16 +155,10 @@ class RouteMap: self.route_type = self.mcp_type -# Default route mappings as a list, where order determines priority +# Default route mapping: all routes become tools. +# Users can provide custom route_maps to override this behavior. DEFAULT_ROUTE_MAPPINGS = [ - # GET requests with path parameters go to ResourceTemplate - RouteMap( - methods=["GET"], pattern=r".*\{.*\}.*", mcp_type=MCPType.RESOURCE_TEMPLATE - ), - # GET requests without path parameters go to Resource - RouteMap(methods=["GET"], pattern=r".*", mcp_type=MCPType.RESOURCE), - # All other HTTP methods go to Tool - RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL), + RouteMap(mcp_type=MCPType.TOOL), ] diff --git a/src/fastmcp/server/server.py b/src/fastmcp/server/server.py index 04997426c..b0804fe92 100644 --- a/src/fastmcp/server/server.py +++ b/src/fastmcp/server/server.py @@ -1551,28 +1551,11 @@ class FastMCP(Generic[LifespanResultT]): route_map_fn: OpenAPIRouteMapFn | None = None, mcp_component_fn: OpenAPIComponentFn | None = None, mcp_names: dict[str, str] | None = None, - all_routes_as_tools: bool = False, **settings: Any, ) -> FastMCPOpenAPI: """ Create a FastMCP server from an OpenAPI specification. """ - from .openapi import FastMCPOpenAPI, MCPType, RouteMap - - # Deprecated since 2.5.0 - if all_routes_as_tools: - warnings.warn( - "The 'all_routes_as_tools' parameter is deprecated and will be removed in a future version. " - 'Use \'route_maps=[RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]\' instead.', - DeprecationWarning, - stacklevel=2, - ) - - if all_routes_as_tools and route_maps: - raise ValueError("Cannot specify both all_routes_as_tools and route_maps") - - elif all_routes_as_tools: - route_maps = [RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)] return FastMCPOpenAPI( openapi_spec=openapi_spec, @@ -1593,7 +1576,6 @@ class FastMCP(Generic[LifespanResultT]): route_map_fn: OpenAPIRouteMapFn | None = None, mcp_component_fn: OpenAPIComponentFn | None = None, mcp_names: dict[str, str] | None = None, - all_routes_as_tools: bool = False, httpx_client_kwargs: dict[str, Any] | None = None, **settings: Any, ) -> FastMCPOpenAPI: @@ -1601,22 +1583,7 @@ class FastMCP(Generic[LifespanResultT]): Create a FastMCP server from a FastAPI application. """ - from .openapi import FastMCPOpenAPI, MCPType, RouteMap - - # Deprecated since 2.5.0 - if all_routes_as_tools: - warnings.warn( - "The 'all_routes_as_tools' parameter is deprecated and will be removed in a future version. " - 'Use \'route_maps=[RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]\' instead.', - DeprecationWarning, - stacklevel=2, - ) - - if all_routes_as_tools and route_maps: - raise ValueError("Cannot specify both all_routes_as_tools and route_maps") - - elif all_routes_as_tools: - route_maps = [RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)] + from .openapi import FastMCPOpenAPI if httpx_client_kwargs is None: httpx_client_kwargs = {}