Jeremiah Lowin commited on
Commit
0332908
·
1 Parent(s): 9b4ba17

Add pattern docs

Browse files
docs/docs.json CHANGED
@@ -10,7 +10,7 @@
10
  "colors": {
11
  "dark": "#f72585",
12
  "light": "#4cc9f0",
13
- "primary": "#3f37c9"
14
  },
15
  "description": "The fast, Pythonic way to build MCP servers.",
16
  "footer": {
@@ -54,6 +54,15 @@
54
  "clients/transports"
55
  ]
56
  },
 
 
 
 
 
 
 
 
 
57
  {
58
  "group": "Deployment",
59
  "pages": []
 
10
  "colors": {
11
  "dark": "#f72585",
12
  "light": "#4cc9f0",
13
+ "primary": "#2d00f7"
14
  },
15
  "description": "The fast, Pythonic way to build MCP servers.",
16
  "footer": {
 
54
  "clients/transports"
55
  ]
56
  },
57
+ {
58
+ "group": "Advanced Patterns",
59
+ "pages": [
60
+ "patterns/proxying",
61
+ "patterns/composition",
62
+ "patterns/openapi",
63
+ "patterns/fastapi"
64
+ ]
65
+ },
66
  {
67
  "group": "Deployment",
68
  "pages": []
docs/patterns/composition.mdx ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Server Composition
3
+ sidebarTitle: Composition
4
+ description: Combine multiple FastMCP servers into a single, larger application using mounting.
5
+ icon: puzzle-piece
6
+ ---
7
+
8
+ As your MCP applications grow, you might want to organize your tools, resources, and prompts into logical modules or reuse existing server components. FastMCP supports composition through the `server.mount()` method, allowing you to combine multiple `FastMCP` instances into a single, unified server.
9
+
10
+ ## Why Compose Servers?
11
+
12
+ - **Modularity**: Break down large applications into smaller, focused servers (e.g., a `WeatherServer`, a `DatabaseServer`, a `CalendarServer`).
13
+ - **Reusability**: Create common utility servers (e.g., a `TextProcessingServer`) and mount them wherever needed.
14
+ - **Teamwork**: Different teams can work on separate FastMCP servers that are later combined.
15
+ - **Organization**: Keep related functionality grouped together logically.
16
+
17
+ ## Mounting Subservers
18
+
19
+ The `mount()` method attaches all components (tools, resources, templates, prompts) from one `FastMCP` instance (the *subserver*) onto another (the *main server*). A `prefix` is added to avoid naming conflicts.
20
+
21
+ ```python
22
+ from fastmcp import FastMCP
23
+ from typing import dict, list
24
+
25
+ # --- Define Subservers ---
26
+
27
+ # Weather Service
28
+ weather_mcp = FastMCP(name="WeatherService")
29
+
30
+ @weather_mcp.tool()
31
+ def get_forecast(city: str) -> dict:
32
+ """Get weather forecast."""
33
+ return {"city": city, "forecast": "Sunny"}
34
+
35
+ @weather_mcp.resource("data://cities/supported")
36
+ def list_supported_cities() -> list[str]:
37
+ """List cities with weather support."""
38
+ return ["London", "Paris", "Tokyo"]
39
+
40
+ # Calculator Service
41
+ calc_mcp = FastMCP(name="CalculatorService")
42
+
43
+ @calc_mcp.tool()
44
+ def add(a: int, b: int) -> int:
45
+ """Add two numbers."""
46
+ return a + b
47
+
48
+ @calc_mcp.prompt()
49
+ def explain_addition() -> str:
50
+ """Explain the concept of addition."""
51
+ return "Addition is the process of combining two or more numbers."
52
+
53
+ # --- Define Main Server ---
54
+ main_mcp = FastMCP(name="MainApp")
55
+
56
+ # --- Mount Subservers ---
57
+ # Mount weather service with prefix "weather"
58
+ main_mcp.mount("weather", weather_mcp)
59
+
60
+ # Mount calculator service with prefix "calc"
61
+ main_mcp.mount("calc", calc_mcp)
62
+
63
+ # --- Now, main_mcp contains combined components ---
64
+ # Tools:
65
+ # - "weather_get_forecast"
66
+ # - "calc_add"
67
+ # Resources:
68
+ # - "weather+data://cities/supported" (prefixed URI)
69
+ # Prompts:
70
+ # - "calc_explain_addition"
71
+
72
+ if __name__ == "__main__":
73
+ # Run the main server, which now includes components from both subservers
74
+ main_mcp.run()
75
+ ```
76
+
77
+ ### How Mounting Works
78
+
79
+ When you call `main_mcp.mount(prefix, subserver)`:
80
+
81
+ 1. **Tools**: All tools from `subserver` are added to `main_mcp`. Their names are automatically prefixed using the `prefix` and a default separator (`_`).
82
+ - `subserver.tool(name="my_tool")` becomes `main_mcp.tool(name="{prefix}_my_tool")`.
83
+ 2. **Resources**: All resources from `subserver` are added. Their URIs are prefixed using the `prefix` and a default separator (`+`).
84
+ - `subserver.resource(uri="data://info")` becomes `main_mcp.resource(uri="{prefix}+data://info")`.
85
+ 3. **Resource Templates**: All templates from `subserver` are added. Their URI *templates* are prefixed similarly to resources.
86
+ - `subserver.resource(uri="data://{id}")` becomes `main_mcp.resource(uri="{prefix}+data://{id}")`.
87
+ 4. **Prompts**: All prompts from `subserver` are added, with names prefixed like tools.
88
+ - `subserver.prompt(name="my_prompt")` becomes `main_mcp.prompt(name="{prefix}_my_prompt")`.
89
+ 5. **Lifespan Management**: If the `subserver` has a `lifespan` function defined, it will be automatically executed within the `main_mcp`'s lifespan context. This ensures that setup and teardown logic for the subserver runs correctly.
90
+
91
+ ### Customizing Separators
92
+
93
+ You might prefer different separators for the prefixed names and URIs. You can customize these when calling `mount()`:
94
+
95
+ ```python
96
+ main_mcp.mount(
97
+ prefix="api",
98
+ app=some_subserver,
99
+ tool_separator="/", # Tool name becomes: "api/sub_tool_name"
100
+ resource_separator=":", # Resource URI becomes: "api:data://sub_resource"
101
+ prompt_separator="." # Prompt name becomes: "api.sub_prompt_name"
102
+ )
103
+ ```
104
+
105
+ <Warning>
106
+ Be cautious when choosing separators. Some MCP clients (like Claude Desktop) might have restrictions on characters allowed in tool names (e.g., `/` might not be supported). The defaults (`_` for names, `+` for URIs) are generally safe.
107
+ </Warning>
108
+
109
+ ## Example: Modular Application
110
+
111
+ ```python
112
+ # modules/text_utils.py
113
+ from fastmcp import FastMCP
114
+ from typing import list
115
+
116
+ text_mcp = FastMCP(name="TextUtilities")
117
+
118
+ @text_mcp.tool()
119
+ def count_words(text: str) -> int:
120
+ """Counts words in a text."""
121
+ return len(text.split())
122
+
123
+ @text_mcp.resource("resource://stopwords")
124
+ def get_stopwords() -> list[str]:
125
+ """Return a list of common stopwords."""
126
+ return ["the", "a", "is", "in"]
127
+
128
+ # ------------------------------
129
+ # modules/data_api.py
130
+ from fastmcp import FastMCP
131
+ import random
132
+ from typing import dict
133
+
134
+ data_mcp = FastMCP(name="DataAPI")
135
+
136
+ @data_mcp.tool()
137
+ def fetch_record(record_id: int) -> dict:
138
+ """Fetches a dummy data record."""
139
+ return {"id": record_id, "value": random.random()}
140
+
141
+ @data_mcp.resource("data://schema/{table}")
142
+ def get_table_schema(table: str) -> dict:
143
+ """Provides a dummy schema for a table."""
144
+ return {"table": table, "columns": ["id", "value"]}
145
+
146
+ # ------------------------------
147
+ # main_app.py
148
+ from fastmcp import FastMCP
149
+ from modules.text_utils import text_mcp # Import server instances
150
+ from modules.data_api import data_mcp
151
+
152
+ app = FastMCP(name="MainApplication")
153
+
154
+ # Mount the utility servers
155
+ app.mount("text", text_mcp)
156
+ app.mount("data", data_mcp)
157
+
158
+ @app.tool()
159
+ def process_and_analyze(record_id: int) -> str:
160
+ """Fetches a record and analyzes its string representation."""
161
+ # In a real application, you'd use proper methods to interact between
162
+ # mounted tools rather than accessing internal managers
163
+
164
+ # Get record data
165
+ record = {"id": record_id, "value": random.random()}
166
+
167
+ # Count words in the record string representation
168
+ word_count = len(str(record).split())
169
+
170
+ return (
171
+ f"Record {record_id} has {word_count} words in its string "
172
+ f"representation."
173
+ )
174
+
175
+ if __name__ == "__main__":
176
+ app.run()
177
+ ```
178
+
179
+ Now, running `main_app.py` starts a server that exposes:
180
+ - `text_count_words`
181
+ - `data_fetch_record`
182
+ - `process_and_analyze`
183
+ - `text+resource://stopwords`
184
+ - `data+data://schema/{table}` (template)
185
+
186
+ This pattern promotes code organization and reuse within your FastMCP projects.
docs/patterns/fastapi.mdx ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: FastAPI Integration
3
+ sidebarTitle: FastAPI
4
+ description: Automatically create FastMCP servers directly from FastAPI applications.
5
+ icon: square-bolt
6
+ ---
7
+
8
+ If you build your APIs using the popular [FastAPI](https://fastapi.tiangolo.com/) framework, FastMCP offers a seamless way to expose your FastAPI application as an MCP server. This leverages the OpenAPI integration internally but simplifies the setup significantly.
9
+
10
+ ## The Goal: FastAPI App -> MCP Server
11
+
12
+ FastAPI automatically generates an OpenAPI specification for your application. FastMCP uses this built-in capability to create an MCP server that mirrors your API routes.
13
+
14
+ - FastAPI path operations (`@app.get`, `@app.post`, etc.) become MCP tools, resources, or templates.
15
+ - Pydantic models used in FastAPI for request/response validation are used to generate MCP schemas.
16
+ - Communication happens directly in memory, making it very efficient.
17
+
18
+ ## Creating from FastAPI App
19
+
20
+ Use the `FastMCP.from_fastapi()` class method. You only need your FastAPI `app` instance.
21
+
22
+ ```python
23
+ import asyncio
24
+ from fastapi import FastAPI
25
+ from pydantic import BaseModel
26
+ from fastmcp import FastMCP, Client # Import FastMCP and Client
27
+
28
+ # 1. Define your FastAPI application
29
+ api_app = FastAPI(title="MyFastAPIApp")
30
+
31
+ class Item(BaseModel):
32
+ name: str
33
+ price: float
34
+ is_offer: bool | None = None
35
+
36
+ @api_app.get("/")
37
+ def read_root():
38
+ return {"Hello": "World"}
39
+
40
+ @api_app.get("/items/{item_id}") # -> Resource Template
41
+ def read_item(item_id: int, q: str | None = None):
42
+ # This will become resource://openapi/read_item_items__item_id__get/{item_id}
43
+ return {"item_id": item_id, "q": q, "description": f"Details for item {item_id}"}
44
+
45
+ @api_app.post("/items/") # -> Tool
46
+ def create_item(item: Item):
47
+ # This will become the 'create_item_items__post' tool
48
+ print(f"Creating item: {item.name}")
49
+ return {"item_name": item.name, "status": "created"}
50
+
51
+ # 2. Create the FastMCP server directly from the FastAPI app
52
+ # This is an async class method
53
+ async def create_mcp_server_from_fastapi():
54
+ mcp_server = await FastMCP.from_fastapi(
55
+ app=api_app,
56
+ name="FastAPI_MCP_Bridge" # Optional name for the MCP server
57
+ )
58
+ return mcp_server
59
+
60
+ # 3. (Example) Run the MCP server and test with an in-memory client
61
+ async def run_and_test():
62
+ server = await create_mcp_server_from_fastapi()
63
+ print(f"Created MCP server '{server.name}' from FastAPI app '{api_app.title}'")
64
+
65
+ # List discovered components
66
+ tools = await server.list_tools()
67
+ templates = await server.list_resource_templates()
68
+ print("Discovered Tools:", [t.name for t in tools])
69
+ print("Discovered Templates:", [t.uriTemplate for t in templates])
70
+
71
+ # Test using an in-memory client
72
+ client = Client(server) # Uses FastMCPTransport
73
+ async with client:
74
+ # Call the tool derived from POST /items/
75
+ create_result = await client.call_tool(
76
+ "create_item_items__post",
77
+ {"name": "MCP Special", "price": 99.99} # Pydantic model fields become args
78
+ )
79
+ print("Create Item Tool Result:", create_result[0].text) # JSON string
80
+
81
+ # Read the resource derived from GET /items/{item_id}
82
+ read_result = await client.read_resource(
83
+ "resource://openapi/read_item_items__item_id__get/42" # Match template URI
84
+ )
85
+ print("Read Item Resource Result:", read_result[0].text) # JSON string
86
+
87
+ # In a real scenario, you might run the MCP server via stdio or sse
88
+ # print("Running MCP server via stdio...")
89
+ # server.run()
90
+
91
+ if __name__ == "__main__":
92
+ # Requires fastapi, uvicorn, httpx:
93
+ # uv pip install "fastapi[all]" httpx
94
+ try:
95
+ asyncio.run(run_and_test())
96
+ except ImportError as e:
97
+ print(f"Error: {e}. Please install required packages: uv pip install \"fastapi[all]\" httpx")
98
+
99
+ # Example Output might include:
100
+ # Created MCP server 'FastAPI_MCP_Bridge' from FastAPI app 'MyFastAPIApp'
101
+ # Discovered Tools: ['read_root___get', 'create_item_items__post']
102
+ # Discovered Templates: ['resource://openapi/read_item_items__item_id__get/{item_id}']
103
+ # Create Item Tool Result: {"item_name": "MCP Special", "status": "created"}
104
+ # Read Item Resource Result: {"item_id": 42, "q": null, "description": "Details for item 42"}
105
+ ```
106
+
107
+ ### How it Works Internally
108
+
109
+ 1. **OpenAPI Generation**: `from_fastapi` asks the FastAPI `app` for its OpenAPI schema dictionary (`app.openapi()`).
110
+ 2. **In-Memory Client**: It creates an `httpx.AsyncClient` configured with an `ASGITransport`. This special transport allows `httpx` to call the FastAPI application directly in memory without needing a running web server process.
111
+ 3. **OpenAPI Integration**: It calls `FastMCP.from_openapi()`, passing the generated schema and the in-memory `httpx` client.
112
+ 4. **MCP Server Creation**: The standard OpenAPI integration logic then proceeds to parse the schema and create the `Tool`, `Resource`, and `ResourceTemplate` components that wrap calls to the in-memory FastAPI app.
113
+
114
+ This provides a highly efficient way to expose your FastAPI logic through the MCP protocol, leveraging FastAPI's routing, dependency injection, and validation features.
docs/patterns/openapi.mdx ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: OpenAPI Integration
3
+ sidebarTitle: OpenAPI
4
+ description: Automatically create FastMCP servers from existing OpenAPI specifications.
5
+ icon: code-branch
6
+ ---
7
+
8
+ If you have existing REST APIs documented with the OpenAPI Specification (OAS), FastMCP can automatically generate MCP tools, resources, and resource templates directly from that specification. This provides a quick way to make your existing HTTP APIs accessible to MCP clients and LLMs.
9
+
10
+ ## The Goal: API -> MCP Server
11
+
12
+ The core idea is to map OpenAPI paths and operations (like `GET /users/{id}` or `POST /orders`) to their corresponding MCP components:
13
+
14
+ - `GET` requests often map to MCP **Resources** (for fetching single items) or **Resource Templates** (if the path has parameters).
15
+ - `POST`, `PUT`, `PATCH`, `DELETE` requests typically map to MCP **Tools** (for actions that create or modify data).
16
+
17
+ FastMCP automates this mapping process.
18
+
19
+ ## Creating from OpenAPI Spec
20
+
21
+ Use the `FastMCP.from_openapi()` class method. You need:
22
+
23
+ 1. The OpenAPI specification as a Python dictionary.
24
+ 2. An `httpx.AsyncClient` configured to make requests to the actual API backend.
25
+
26
+ <CodeGroup>
27
+
28
+ ```python server.py
29
+ import asyncio
30
+ import httpx
31
+ from fastmcp import FastMCP
32
+
33
+ # load the OpenAPI specification from the openapi_spec.py file
34
+ petstore_spec = PETSTORE_SPEC
35
+
36
+ # Client to communicate with the actual Pet Store API backend
37
+ # The base_url should match the server URL in the OpenAPI spec
38
+ http_client = httpx.AsyncClient(base_url="http://petstore.example.com/api")
39
+
40
+ # Create the FastMCP server from the spec
41
+ # This is an async class method
42
+ async def create_openapi_server():
43
+ mcp_server = await FastMCP.from_openapi(
44
+ openapi_spec=petstore_spec,
45
+ client=http_client,
46
+ name="PetStoreMCP" # Optional name for the MCP server
47
+ )
48
+ return mcp_server
49
+
50
+ async def run_server():
51
+ server = await create_openapi_server()
52
+ print(f"Starting OpenAPI-based server '{server.name}'...")
53
+
54
+ # List discovered components
55
+ tools = await server.list_tools()
56
+ resources = await server.list_resources()
57
+ templates = await server.list_resource_templates()
58
+ print("Discovered Tools:", [t.name for t in tools])
59
+ print("Discovered Resources:", [r.uri for r in resources]) # Should be empty if no parameterless GETs
60
+ print("Discovered Templates:", [t.uriTemplate for t in templates])
61
+
62
+ # Run the server (e.g., via stdio)
63
+ # server.run()
64
+
65
+ if __name__ == "__main__":
66
+ # Example: Create the server and print discovered components
67
+ # Requires httpx: uv pip install httpx
68
+ asyncio.run(run_server())
69
+
70
+ # Expected Output might include:
71
+ # Discovered Tools: ['listPets', 'createPet']
72
+ # Discovered Resources: []
73
+ # Discovered Templates: ['resource://openapi/showPetById/{petId}']
74
+ ```
75
+
76
+ ```python openapi_spec.py
77
+ # Example OpenAPI Specification (simplified Pet Store)
78
+ PETSTORE_SPEC = {
79
+ "openapi": "3.1.0",
80
+ "info": {"title": "Simple Pet Store", "version": "1.0.0"},
81
+ "servers": [{"url": "http://petstore.example.com/api"}], # Base URL for API calls
82
+ "paths": {
83
+ "/pets": {
84
+ "get": {
85
+ "summary": "List all pets",
86
+ "operationId": "listPets",
87
+ "tags": ["pets"],
88
+ "parameters": [{ # Query parameter -> Tool argument
89
+ "name": "limit", "in": "query", "schema": {"type": "integer"}
90
+ }],
91
+ "responses": {"200": {"description": "A list of pets."}},
92
+ },
93
+ "post": { # POST -> Tool
94
+ "summary": "Create a pet",
95
+ "operationId": "createPet",
96
+ "tags": ["pets"],
97
+ "requestBody": { # Request body -> Tool arguments
98
+ "required": True,
99
+ "content": {"application/json": {"schema": {"$ref": "#/components/schemas/PetInput"}}}
100
+ },
101
+ "responses": {"201": {"description": "Pet created."}},
102
+ },
103
+ },
104
+ "/pets/{petId}": { # Path parameter -> Resource Template
105
+ "get": { # GET with path param -> Resource Template / FunctionResource
106
+ "summary": "Info for a specific pet",
107
+ "operationId": "showPetById",
108
+ "tags": ["pets"],
109
+ "parameters": [{ # Path parameter -> Template function argument
110
+ "name": "petId", "in": "path", "required": True, "schema": {"type": "string"}
111
+ }],
112
+ "responses": {"200": {"description": "Information about the pet."}},
113
+ },
114
+ },
115
+ },
116
+ "components": {
117
+ "schemas": {
118
+ "PetInput": {"type": "object", "properties": {"name": {"type": "string"}, "tag": {"type": "string"}}},
119
+ }
120
+ }
121
+ }
122
+ ```
123
+
124
+ </CodeGroup>
125
+
126
+ ### How it Works Internally
127
+
128
+ 1. **Parsing**: `from_openapi` parses the spec using utilities that leverage `openapi-pydantic`. It extracts paths, operations, parameters, request bodies, and responses.
129
+ 2. **Mapping**: It applies mapping rules (see below) to decide whether each OpenAPI operation (`GET /pets`, `POST /pets`, `GET /pets/{petId}`) becomes an MCP `Tool`, `Resource`, or `ResourceTemplate`.
130
+ 3. **Component Creation**: It creates specialized internal components (`OpenAPITool`, `OpenAPIResource`, `OpenAPIResourceTemplate`).
131
+ 4. **HTTP Execution**: When an MCP client calls a tool or reads a resource from this server:
132
+ * The corresponding OpenAPI component constructs an HTTP request based on the OpenAPI definition and the arguments provided by the MCP client.
133
+ * It uses the provided `httpx.AsyncClient` to send the request to the backend API.
134
+ * It processes the HTTP response and returns it to the MCP client in the appropriate MCP format.
135
+ 5. **Schema Generation**: The schemas for MCP tools are derived by combining OpenAPI parameters (path, query, header) and request body schemas. Resource template function arguments are derived from path parameters.
136
+ 6. **Descriptions**: Tool/Resource descriptions are enhanced with information from OpenAPI responses to give the LLM more context about potential outcomes.
137
+
138
+ ### Default Mapping Rules
139
+
140
+ FastMCP uses the following default rules to map OpenAPI operations:
141
+
142
+ - `GET` operation with path parameters (e.g., `/users/{id}`) -> **`ResourceTemplate`**
143
+ - `GET` operation without path parameters (e.g., `/users`) -> **`Resource`**
144
+ - `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS`, `HEAD` -> **`Tool`**
145
+
146
+ ### Customize Route Mapping
147
+
148
+ You can customize the mapping rules by providing a list of `RouteMap` objects directly to `FastMCP.from_openapi()` using the `route_maps` parameter:
149
+
150
+ ```python
151
+ from fastmcp.server.openapi import RouteMap, RouteType
152
+ from fastmcp import FastMCP
153
+
154
+ # Custom mapping: Treat GET /admin/stats as a Tool, not a Resource
155
+ custom_maps = [
156
+ RouteMap(methods=["GET"], pattern=r"^/admin/stats$", route_type=RouteType.TOOL)
157
+ ]
158
+
159
+ async def create_server_with_custom_mapping():
160
+ mcp_server = await FastMCP.from_openapi(
161
+ openapi_spec=petstore_spec,
162
+ client=http_client,
163
+ name="PetStoreMCP",
164
+ route_maps=custom_maps # Pass custom mapping rules
165
+ )
166
+ return mcp_server
167
+ ```
168
+
169
+ Each `RouteMap` maps one or more HTTP methods and a regular expression pattern for the route path to an MCP `RouteType`. Route maps are processed in order, and the first match wins.
170
+
171
+ All parameters passed to `FastMCP.from_openapi()` will be forwarded to the underlying `FastMCPOpenAPI` constructor, so you can customize any aspect of the OpenAPI integration directly through this method call.
172
+
docs/patterns/proxying.mdx ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Proxying Servers
3
+ sidebarTitle: Proxying
4
+ description: Use FastMCP to act as an intermediary or change transport for other MCP servers.
5
+ icon: arrows-retweet
6
+ ---
7
+
8
+ FastMCP provides a powerful proxying capability that allows one FastMCP server instance to act as a frontend for another MCP server (which could be remote, running on a different transport, or even another FastMCP instance). This is achieved using the `FastMCP.as_proxy()` class method.
9
+
10
+ ## What is Proxying?
11
+
12
+ Proxying means setting up a FastMCP server that doesn't implement its own tools or resources directly. Instead, when it receives a request (like `tools/call` or `resources/read`), it forwards that request to a *backend* MCP server, receives the response, and then relays that response back to the original client.
13
+
14
+ ```mermaid
15
+ sequenceDiagram
16
+ participant Client
17
+ participant ProxyServer as FastMCP Proxy Server
18
+ participant BackendServer as Backend MCP Server
19
+
20
+ Client->>ProxyServer: Request (e.g., stdio)
21
+ ProxyServer->>BackendServer: Request (e.g., sse)
22
+ BackendServer-->>ProxyServer: Response (e.g., sse)
23
+ ProxyServer-->>Client: Response (e.g., stdio)
24
+ ```
25
+
26
+ ### Use Cases
27
+
28
+ - **Transport Bridging**: Expose a server running on one transport (e.g., a remote SSE server) via a different transport (e.g., local Stdio for Claude Desktop).
29
+ - **Adding Functionality**: Insert a layer in front of an existing server to add caching, logging, authentication, or modify requests/responses (though direct modification requires subclassing `FastMCPProxy`).
30
+ - **Security Boundary**: Use the proxy as a controlled gateway to an internal server.
31
+ - **Simplifying Client Configuration**: Provide a single, stable endpoint (the proxy) even if the backend server's location or transport changes.
32
+
33
+ ## Creating a Proxy
34
+
35
+ The easiest way to create a proxy is using the `FastMCP.as_proxy()` class method. This creates a standard FastMCP server that forwards requests to another MCP server.
36
+
37
+ ```python
38
+ from fastmcp import FastMCP, Client
39
+
40
+ # Create a client configured to talk to the backend server
41
+ # This could be any MCP server - remote, local, or using any transport
42
+ backend_client = Client("backend_server.py") # Could be "http://remote.server/sse", etc.
43
+
44
+ # Create the proxy server with as_proxy()
45
+ proxy_server = await FastMCP.as_proxy(
46
+ backend_client,
47
+ name="MyProxyServer" # Optional settings for the proxy
48
+ )
49
+
50
+ # That's it! You now have a proxy FastMCP server that can be used
51
+ # with any transport (SSE, stdio, etc.) just like any other FastMCP server
52
+ ```
53
+
54
+ **How `as_proxy` Works:**
55
+
56
+ 1. It connects to the backend server using the provided client.
57
+ 2. It discovers all the tools, resources, resource templates, and prompts available on the backend server.
58
+ 3. It creates corresponding "proxy" components that forward requests to the backend.
59
+ 4. It returns a standard `FastMCP` server instance that can be used like any other.
60
+
61
+ ### Bridging Transports
62
+
63
+ A common use case is to bridge transports. For example, making a remote SSE server available locally via Stdio:
64
+
65
+ ```python
66
+ from fastmcp import FastMCP, Client
67
+
68
+ # Client targeting a remote SSE server
69
+ client = Client("http://example.com/mcp/sse")
70
+
71
+ # Create a proxy server - it's just a regular FastMCP server
72
+ proxy = await FastMCP.as_proxy(client, name="SSE to Stdio Proxy")
73
+
74
+ # The proxy can now be used with any transport
75
+ # No special handling needed - it works like any FastMCP server
76
+ ```
77
+
78
+ ### In-Memory Proxies
79
+
80
+ You can also proxy an in-memory `FastMCP` instance, which is useful for adjusting the configuration or behavior of a server you don't completely control.
81
+
82
+ ```python
83
+ from fastmcp import FastMCP
84
+
85
+ # Original server
86
+ original_server = FastMCP(name="Original")
87
+
88
+ @original_server.tool()
89
+ def tool_a() -> str:
90
+ return "A"
91
+
92
+ # Create a proxy of the original server
93
+ proxy = await FastMCP.as_proxy(
94
+ original_server,
95
+ name="Proxy Server"
96
+ )
97
+
98
+ # proxy is now a regular FastMCP server that forwards
99
+ # requests to original_server
100
+ ```
101
+
102
+ ## `FastMCPProxy` Class
103
+
104
+ Internally, `FastMCP.as_proxy()` uses the `FastMCPProxy` class. You generally don't need to interact with this class directly, but it's available if needed. It has two primary async constructors:
105
+
106
+ * `FastMCPProxy.from_client(client: Client, **settings)`: Creates a proxy from a client instance.
107
+ * `FastMCPProxy.from_server(server: FastMCP, **settings)`: Creates a proxy from another FastMCP server instance.
108
+
109
+ Using the class directly might be necessary for advanced scenarios, like subclassing `FastMCPProxy` to add custom logic before or after forwarding requests.