fastapi-fastmcp 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastapi_fastmcp-0.1.0/LICENSE +21 -0
- fastapi_fastmcp-0.1.0/PKG-INFO +412 -0
- fastapi_fastmcp-0.1.0/README.md +376 -0
- fastapi_fastmcp-0.1.0/fastapi_fastmcp.egg-info/PKG-INFO +412 -0
- fastapi_fastmcp-0.1.0/fastapi_fastmcp.egg-info/SOURCES.txt +23 -0
- fastapi_fastmcp-0.1.0/fastapi_fastmcp.egg-info/dependency_links.txt +1 -0
- fastapi_fastmcp-0.1.0/fastapi_fastmcp.egg-info/entry_points.txt +3 -0
- fastapi_fastmcp-0.1.0/fastapi_fastmcp.egg-info/requires.txt +12 -0
- fastapi_fastmcp-0.1.0/fastapi_fastmcp.egg-info/top_level.txt +1 -0
- fastapi_fastmcp-0.1.0/fastapi_mcp/__init__.py +13 -0
- fastapi_fastmcp-0.1.0/fastapi_mcp/__main__.py +6 -0
- fastapi_fastmcp-0.1.0/fastapi_mcp/cli.py +132 -0
- fastapi_fastmcp-0.1.0/fastapi_mcp/core.py +533 -0
- fastapi_fastmcp-0.1.0/fastapi_mcp/dispatcher.py +155 -0
- fastapi_fastmcp-0.1.0/fastapi_mcp/scanner.py +271 -0
- fastapi_fastmcp-0.1.0/fastapi_mcp/ui.py +400 -0
- fastapi_fastmcp-0.1.0/pyproject.toml +75 -0
- fastapi_fastmcp-0.1.0/setup.cfg +4 -0
- fastapi_fastmcp-0.1.0/tests/test_auto_scan.py +105 -0
- fastapi_fastmcp-0.1.0/tests/test_cli.py +46 -0
- fastapi_fastmcp-0.1.0/tests/test_coexistence.py +56 -0
- fastapi_fastmcp-0.1.0/tests/test_coverage_complete.py +541 -0
- fastapi_fastmcp-0.1.0/tests/test_enterprise.py +156 -0
- fastapi_fastmcp-0.1.0/tests/test_hybrid.py +73 -0
- fastapi_fastmcp-0.1.0/tests/test_ui.py +45 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 manavmehta2403
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastapi-fastmcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The unified bridge between FastAPI and FastMCP / Model Context Protocol.
|
|
5
|
+
Author: Manav Mehta
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/manavmehta2403/fastapi-mcp
|
|
8
|
+
Project-URL: Repository, https://github.com/manavmehta2403/fastapi-mcp
|
|
9
|
+
Project-URL: Issues, https://github.com/manavmehta2403/fastapi-mcp/issues
|
|
10
|
+
Keywords: fastapi,mcp,fastmcp,model-context-protocol,ai,agents,llm,claude,cursor
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: fastapi>=0.100.0
|
|
25
|
+
Requires-Dist: mcp>=1.0.0
|
|
26
|
+
Requires-Dist: httpx>=0.25.0
|
|
27
|
+
Requires-Dist: pydantic>=2.0.0
|
|
28
|
+
Requires-Dist: starlette>=0.27.0
|
|
29
|
+
Requires-Dist: uvicorn>=0.22.0
|
|
30
|
+
Provides-Extra: test
|
|
31
|
+
Requires-Dist: pytest>=8.0.0; extra == "test"
|
|
32
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == "test"
|
|
33
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
|
|
34
|
+
Requires-Dist: pylint>=3.0.0; extra == "test"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# FastAPI-MCP โก๐ค
|
|
38
|
+
|
|
39
|
+
<p align="center">
|
|
40
|
+
<b>The unified bridge between FastAPI and FastMCP / Model Context Protocol.</b><br>
|
|
41
|
+
<i>Serve REST APIs and MCP AI Tools simultaneously from a single server with zero resource overhead.</i>
|
|
42
|
+
</p>
|
|
43
|
+
|
|
44
|
+
<p align="center">
|
|
45
|
+
<a href="#-key-highlights"><img src="https://img.shields.io/badge/FastAPI-009688?style=flat&logo=fastapi&logoColor=white" alt="FastAPI"></a>
|
|
46
|
+
<a href="#-key-highlights"><img src="https://img.shields.io/badge/MCP-Protocol-blueviolet?style=flat" alt="MCP"></a>
|
|
47
|
+
<a href="#-key-highlights"><img src="https://img.shields.io/badge/Python-3.10+-3776AB?style=flat&logo=python&logoColor=white" alt="Python"></a>
|
|
48
|
+
<a href="#license"><img src="https://img.shields.io/badge/License-MIT-green.svg" alt="License"></a>
|
|
49
|
+
</p>
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## ๐ Key Highlights
|
|
54
|
+
|
|
55
|
+
* ๐ค **True Protocol Coexistence**: Serve your REST API (OpenAPI/Swagger docs), MCP Server-Sent Events (SSE), and Streamable HTTP endpoints from a **single Uvicorn process and single port**.
|
|
56
|
+
* ๐ ๏ธ **Dual `@app.api_tool` Decorator**: Define an endpoint once โ it instantly becomes both an HTTP REST endpoint for web clients and an MCP tool for AI agents (Claude, Cursor, Antigravity).
|
|
57
|
+
* ๐ **2-Line Existing App Conversion**: Point to any existing FastAPI application and automatically expose its endpoints as MCP tools with zero changes to your existing code.
|
|
58
|
+
* ๐ **Built-in MCP Swagger Explorer (`/mcp/docs`)**: An interactive visual playground for testing MCP tools right alongside FastAPI's `/docs`. Inspect parameters and execute tools live with "Try it out" in your browser.
|
|
59
|
+
* โก **Zero-Network In-Memory Dispatch**: In-memory ASGI transport executes tool calls directly without TCP socket loopbacks, preserving FastAPI `Depends()` injections, auth, and Pydantic validation.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## ๐ก Why a Single Server Architecture? (Resource & Performance Benefits)
|
|
64
|
+
|
|
65
|
+
Running separate microservices or independent processes for REST and MCP introduces unnecessary infrastructure complexity and resource waste:
|
|
66
|
+
|
|
67
|
+
### 1. Zero Process Multiplication & Low Memory Footprint
|
|
68
|
+
A typical Python web server process uses 60โ150 MB of RAM at boot. Running a separate server process for MCP doubles baseline memory consumption and CPU thread contention. With `fastapi-mcp`, everything runs in a **single shared event loop**, conserving memory on budget VMs and cloud containers.
|
|
69
|
+
|
|
70
|
+
### 2. Zero-Network In-Memory Execution
|
|
71
|
+
When an AI agent triggers an MCP tool that maps to a FastAPI route, `fastapi-mcp` dispatches the request through an **in-memory ASGI pipeline (`httpx.ASGITransport`)**:
|
|
72
|
+
- No TCP socket handshakes
|
|
73
|
+
- No loopback network latency
|
|
74
|
+
- Sub-millisecond execution speeds
|
|
75
|
+
|
|
76
|
+
### 3. Shared Database Pools & Dependencies
|
|
77
|
+
Instead of maintaining two independent database connection pools (one for your REST API and one for your MCP server), your MCP tools directly reuse the existing FastAPI `Depends(get_db)` connection pool and caching layers.
|
|
78
|
+
|
|
79
|
+
### 4. Single Source of Truth for Schemas
|
|
80
|
+
You define your Pydantic v2 data models once. Both your OpenAPI specification (`/docs`) and your MCP tool definitions (`/mcp/docs`) are generated from the exact same model definitions without drift.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## ๐๏ธ Architecture Overview
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
+-----------------------+
|
|
88
|
+
| AI Agent / Client |
|
|
89
|
+
| (Claude Desktop, Cursor|
|
|
90
|
+
+-----------+-----------+
|
|
91
|
+
|
|
|
92
|
+
+------------------------+-----------------------+
|
|
93
|
+
| (HTTP / SSE) | (stdio)
|
|
94
|
+
v v
|
|
95
|
+
+--------------------+---------------------+ +-----------------+---------------------+
|
|
96
|
+
| FastAPI ASGI Server (Uvicorn) | | fastapi-mcp CLI (stdio) |
|
|
97
|
+
| - Swagger UI: /docs | | (Direct pipe for desktop LLM agents) |
|
|
98
|
+
| - REST Endpoints: /items, /users | +-----------------+---------------------+
|
|
99
|
+
| - MCP Streamable: /mcp | |
|
|
100
|
+
| - MCP SSE Stream: /sse | |
|
|
101
|
+
| - MCP Swagger Explorer: /mcp/docs | |
|
|
102
|
+
+--------------------+---------------------+ |
|
|
103
|
+
| |
|
|
104
|
+
+------------------------+-----------------------+
|
|
105
|
+
|
|
|
106
|
+
v
|
|
107
|
+
+---------------+---------------+
|
|
108
|
+
| FastAPIMCP Unified Hub |
|
|
109
|
+
+---------------+---------------+
|
|
110
|
+
|
|
|
111
|
+
+-------------------------+-------------------------+
|
|
112
|
+
| |
|
|
113
|
+
v v
|
|
114
|
+
+-------------------+--------------------+ +-----------------+-------------------+
|
|
115
|
+
| Route Scanner & Dynamic Tools | | Native MCP Tools |
|
|
116
|
+
| - Inspects Path, Query, Body, Headers | | - @app.tool() |
|
|
117
|
+
| - Filters out Depends(db), Request | | - @app.resource() |
|
|
118
|
+
| - Preserves Pydantic Schema & Docstrings| | - @app.prompt() |
|
|
119
|
+
+-------------------+--------------------+ +-----------------+-------------------+
|
|
120
|
+
| |
|
|
121
|
+
v v
|
|
122
|
+
+-------------------+---------------------------------------------------+-------------------+
|
|
123
|
+
| In-Memory ASGITransport Dispatcher |
|
|
124
|
+
| Resolves Depends() -> Validates Schema -> Invokes Route -> Returns Structured Result |
|
|
125
|
+
+-------------------------------------------------------------------------------------------+
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## ๐ฆ Installation
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
pip install fastapi-fastmcp
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
*(Works with the latest releases of `fastapi`, `mcp`, `pydantic`, `httpx`, and `uvicorn`)*
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## ๐ Quickstart Guides
|
|
141
|
+
|
|
142
|
+
### Pattern 1: Convert an Existing FastAPI App (2 Lines)
|
|
143
|
+
|
|
144
|
+
Take any existing FastAPI application with routes, database sessions, and dependencies, and expose it to AI agents:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from fastapi import FastAPI, Depends, Query, Path
|
|
148
|
+
from pydantic import BaseModel, Field
|
|
149
|
+
from fastapi_mcp import FastAPIMCP
|
|
150
|
+
|
|
151
|
+
app = FastAPI(title="Store API")
|
|
152
|
+
|
|
153
|
+
class Item(BaseModel):
|
|
154
|
+
name: str = Field(..., description="Product title")
|
|
155
|
+
price: float = Field(..., description="Product price")
|
|
156
|
+
|
|
157
|
+
def get_db():
|
|
158
|
+
return {"status": "connected"}
|
|
159
|
+
|
|
160
|
+
@app.get("/items/{item_id}", summary="Get item details")
|
|
161
|
+
async def get_item(
|
|
162
|
+
item_id: int = Path(..., description="Unique product ID"),
|
|
163
|
+
verbose: bool = Query(False, description="Include detailed specs"),
|
|
164
|
+
db: dict = Depends(get_db) # Auto-resolved in-memory, hidden from LLM schema!
|
|
165
|
+
):
|
|
166
|
+
"""Retrieve item details by ID."""
|
|
167
|
+
return {"id": item_id, "verbose": verbose, "db": db["status"]}
|
|
168
|
+
|
|
169
|
+
@app.post("/items", summary="Create item")
|
|
170
|
+
async def create_item(item: Item, db: dict = Depends(get_db)):
|
|
171
|
+
"""Create a new item in the catalog."""
|
|
172
|
+
return {"status": "created", "item": item.model_dump()}
|
|
173
|
+
|
|
174
|
+
# -------------------------------------------------------------
|
|
175
|
+
# ๐ CONVERT & MOUNT IN 2 LINES:
|
|
176
|
+
# -------------------------------------------------------------
|
|
177
|
+
mcp = FastAPIMCP.bind(app, name="StoreMCP")
|
|
178
|
+
mcp.mount_to(app, path="/mcp", sse_path="/sse")
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
### Pattern 2: Hybrid Dual API (`@app.api_tool`)
|
|
184
|
+
|
|
185
|
+
Build new services where functions are simultaneously REST endpoints and MCP tools:
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
from fastapi_mcp import FastAPIMCP
|
|
189
|
+
from pydantic import BaseModel, Field
|
|
190
|
+
|
|
191
|
+
# FastAPIMCP is a drop-in replacement for FastAPI
|
|
192
|
+
app = FastAPIMCP(title="Hybrid Service", mcp_name="UnifiedServer")
|
|
193
|
+
|
|
194
|
+
class CalculateRequest(BaseModel):
|
|
195
|
+
x: float = Field(..., description="First operand")
|
|
196
|
+
y: float = Field(..., description="Second operand")
|
|
197
|
+
op: str = Field("add", description="Operation: 'add', 'sub', 'mul', 'div'")
|
|
198
|
+
|
|
199
|
+
# 1. ๐ ๏ธ DUAL ENDPOINT: Accessible via REST POST /math/calc AND as MCP Tool 'calculate'
|
|
200
|
+
@app.api_tool("/math/calc", methods=["POST"], tool_name="calculate")
|
|
201
|
+
async def calculate(data: CalculateRequest) -> dict:
|
|
202
|
+
"""Perform mathematical calculations."""
|
|
203
|
+
if data.op == "add":
|
|
204
|
+
res = data.x + data.y
|
|
205
|
+
elif data.op == "mul":
|
|
206
|
+
res = data.x * data.y
|
|
207
|
+
else:
|
|
208
|
+
res = data.x - data.y
|
|
209
|
+
return {"result": res, "op": data.op}
|
|
210
|
+
|
|
211
|
+
# 2. ๐งฉ PURE MCP TOOL: Agent-only tool (hidden from public REST routes)
|
|
212
|
+
@app.tool(name="internal_rag_search")
|
|
213
|
+
async def internal_search(query: str) -> list[str]:
|
|
214
|
+
"""Search internal documentation."""
|
|
215
|
+
return [f"Article match for '{query}'"]
|
|
216
|
+
|
|
217
|
+
# 3. ๐ PURE REST ROUTE: Standard FastAPI route
|
|
218
|
+
@app.get("/health")
|
|
219
|
+
async def health():
|
|
220
|
+
return {"status": "ok"}
|
|
221
|
+
|
|
222
|
+
# Mount MCP endpoints onto the server
|
|
223
|
+
app.mount_mcp(path="/mcp", sse_path="/sse")
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## ๐ Deployment Options
|
|
229
|
+
|
|
230
|
+
### Option A: Single Port Deployment (Default & Recommended)
|
|
231
|
+
Serve both your REST API and MCP transports from the same port:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
uvicorn main:app --host 0.0.0.0 --port 8000
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Available endpoints on port 8000:
|
|
238
|
+
* **FastAPI Swagger UI**: `http://localhost:8000/docs`
|
|
239
|
+
* **MCP Swagger Explorer**: `http://localhost:8000/mcp/docs`
|
|
240
|
+
* **MCP Streamable HTTP**: `http://localhost:8000/mcp`
|
|
241
|
+
* **MCP Server-Sent Events (SSE)**: `http://localhost:8000/sse`
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
### Option B: Split Port Deployment (Network Segmentation)
|
|
246
|
+
If your infrastructure requires keeping internal AI traffic on an isolated private port while serving public REST traffic externally:
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
import uvicorn
|
|
250
|
+
import threading
|
|
251
|
+
|
|
252
|
+
# Serve REST on public port 8000, and MCP on internal port 9000
|
|
253
|
+
def run_rest():
|
|
254
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
255
|
+
|
|
256
|
+
def run_mcp():
|
|
257
|
+
# Run standalone MCP SSE or streamable app on isolated port
|
|
258
|
+
mcp_app = mcp.mcp_server.streamable_http_app()
|
|
259
|
+
uvicorn.run(mcp_app, host="127.0.0.1", port=9000)
|
|
260
|
+
|
|
261
|
+
if __name__ == "__main__":
|
|
262
|
+
threading.Thread(target=run_mcp, daemon=True).start()
|
|
263
|
+
run_rest()
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## ๐ Interactive MCP Swagger Explorer (`/mcp/docs`)
|
|
269
|
+
|
|
270
|
+
FastAPI-MCP includes a visual explorer for your MCP tools, served right at `/mcp/docs`.
|
|
271
|
+
|
|
272
|
+
Features:
|
|
273
|
+
- **Interactive Tool Catalog**: Inspect all tools, schemas, and parameter requirements.
|
|
274
|
+
- **Live "Try It Out"**: Input fields for numbers, strings, booleans, and JSON objects.
|
|
275
|
+
- **Instant Live Execution**: Trigger tool calls directly in the browser and see structured responses in real time.
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## ๐ป Claude Desktop & Cursor Integration
|
|
280
|
+
|
|
281
|
+
Run your FastAPI application directly over `stdio` without needing an active HTTP server:
|
|
282
|
+
|
|
283
|
+
Add this to your `claude_desktop_config.json`:
|
|
284
|
+
|
|
285
|
+
```json
|
|
286
|
+
{
|
|
287
|
+
"mcpServers": {
|
|
288
|
+
"my-fastapi-app": {
|
|
289
|
+
"command": "python",
|
|
290
|
+
"args": ["-m", "fastapi_mcp.cli", "run", "main:app", "--transport", "stdio"]
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## ๐ก๏ธ Enterprise Hardening: Solving Production Bottlenecks
|
|
299
|
+
|
|
300
|
+
In large enterprise microservices with dozens or hundreds of routes, simply dumping every endpoint as an individual MCP tool creates serious production hurdles. `fastapi-mcp` ships with built-in primitives to solve them:
|
|
301
|
+
|
|
302
|
+
### 1. Librarian Discovery Pattern (Preventing Context Window Bloat)
|
|
303
|
+
Exposing 80+ tools dumps 15,000โ25,000 tokens into the LLM system prompt on every single turn, degrading reasoning and exploding API bills.
|
|
304
|
+
|
|
305
|
+
Activate **Librarian Mode** to condense all catalog tools into **3 lightweight meta-tools**:
|
|
306
|
+
```python
|
|
307
|
+
mcp = FastAPIMCP.bind(app, librarian=True)
|
|
308
|
+
```
|
|
309
|
+
The LLM now only sees:
|
|
310
|
+
* `list_api_categories()`: Browse high-level domains (e.g. `billing`, `inventory`, `users`) with tool summaries.
|
|
311
|
+
* `search_api_tools(query, category)`: Fetch detailed schemas for matching tools on demand.
|
|
312
|
+
* `execute_api_tool(tool_name, arguments)`: Dynamically invoke the requested underlying endpoint.
|
|
313
|
+
|
|
314
|
+
### 2. Dry-Run Simulation Mode (`enable_dry_run=True`)
|
|
315
|
+
Allow AI agents to validate parameters and simulate state mutations (`POST`, `PUT`, `DELETE`, `PATCH`) safely:
|
|
316
|
+
```python
|
|
317
|
+
mcp = FastAPIMCP.bind(app, enable_dry_run=True)
|
|
318
|
+
```
|
|
319
|
+
* Injects a `dry_run: bool = False` argument into mutation tools.
|
|
320
|
+
* Dispatches in-memory with `X-Dry-Run: true` header and `?dry_run=true` query parameter.
|
|
321
|
+
* Wraps responses with `{"dry_run": True, "simulated": True, "result": ...}` so agents can test changes before committing them.
|
|
322
|
+
|
|
323
|
+
### 3. Dynamic Session Context Provider (Multi-Tenant Stdio & SSE Auth)
|
|
324
|
+
Desktop agents connecting via `stdio` (like Claude Desktop or Cursor) do not send standard HTTP `Authorization: Bearer <token>` headers. Without caller context, endpoints with `Depends(get_current_user)` fail with 401 Unauthorized.
|
|
325
|
+
|
|
326
|
+
Inject authentication claims dynamically per session:
|
|
327
|
+
```python
|
|
328
|
+
def resolve_tenant_context(tool_args: dict) -> dict:
|
|
329
|
+
# Resolve active tenant / user from session or environment
|
|
330
|
+
return {"Authorization": "Bearer session-token-xyz", "X-Tenant-ID": "acme-corp"}
|
|
331
|
+
|
|
332
|
+
mcp = FastAPIMCP.bind(
|
|
333
|
+
app,
|
|
334
|
+
session_context_provider=resolve_tenant_context,
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
# Or update dynamically during runtime
|
|
338
|
+
mcp.set_session_context_provider(lambda kwargs: {"Authorization": "Bearer rotated-token"})
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## ๐ ๏ธ CLI Utilities
|
|
344
|
+
|
|
345
|
+
### 1. Inspect Tools and Schemas
|
|
346
|
+
Inspect which tools and schemas are generated from your FastAPI application:
|
|
347
|
+
```bash
|
|
348
|
+
fastapi-mcp inspect main:app
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Output:
|
|
352
|
+
```text
|
|
353
|
+
=== FastAPI-MCP Server: UnifiedServer ===
|
|
354
|
+
Found 2 tools:
|
|
355
|
+
|
|
356
|
+
* Tool: calculate
|
|
357
|
+
Description: [POST /math/calc] Perform mathematical calculations.
|
|
358
|
+
Arguments (1):
|
|
359
|
+
- data: CalculateRequest (required) - Calculation input data
|
|
360
|
+
|
|
361
|
+
* Tool: internal_rag_search
|
|
362
|
+
Description: Search internal documentation.
|
|
363
|
+
Arguments (1):
|
|
364
|
+
- query: string (required)
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Export schema as JSON:
|
|
368
|
+
```bash
|
|
369
|
+
fastapi-mcp inspect main:app --json
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### 2. Run Directly from CLI
|
|
373
|
+
```bash
|
|
374
|
+
fastapi-mcp run main:app --transport stdio
|
|
375
|
+
fastapi-mcp run main:app --transport sse
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## โ๏ธ Route Filtering & Custom Headers
|
|
381
|
+
|
|
382
|
+
```python
|
|
383
|
+
mcp = FastAPIMCP.bind(
|
|
384
|
+
app,
|
|
385
|
+
# Exclude internal routes
|
|
386
|
+
exclude_paths=["/admin/*", "/metrics"],
|
|
387
|
+
# Only expose routes with specific OpenAPI tags
|
|
388
|
+
include_tags={"mcp", "public"},
|
|
389
|
+
# Filter methods
|
|
390
|
+
include_methods={"GET", "POST"},
|
|
391
|
+
# Static auth headers for protected routes
|
|
392
|
+
default_headers={"Authorization": "Bearer internal-service-token"},
|
|
393
|
+
# Or dynamic headers callback
|
|
394
|
+
headers_factory=lambda: {"X-Request-Source": "mcp-agent"}
|
|
395
|
+
)
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## ๐งช Running Tests
|
|
401
|
+
|
|
402
|
+
The test suite covers route auto-scanning, in-memory ASGI dispatch, dual-mode decorators, single-port co-hosting, and the MCP Swagger UI:
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
pytest tests/ -v
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
## ๐ License
|
|
411
|
+
|
|
412
|
+
MIT License. See [LICENSE](LICENSE) for details.
|