Skip to content

Commit 8b6e0b8

Browse files
committed
Expose fastapi-mcp
1 parent 00c3af2 commit 8b6e0b8

File tree

4 files changed

+76
-30
lines changed

4 files changed

+76
-30
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Python Workers: FastMCP Example
1+
# Python Workers: FastAPI-MCP Example
22

3-
This is an example of a Python Worker that uses the FastMCP package.
3+
This is an example of a Python Worker that uses the FastAPI-MCP package.
44

55
## Adding Packages
66

src/httpx_patch.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from httpx._transports.jsfetch import AsyncJavascriptFetchTransport
2+
3+
orig_handle_async_request = AsyncJavascriptFetchTransport.handle_async_request
4+
5+
async def handle_async_request(self, request):
6+
response = await orig_handle_async_request(self, request)
7+
# fix content-encoding headers because the javascript fetch handles that
8+
response.headers.update({"content-encoding": "identity"})
9+
return response
10+
11+
AsyncJavascriptFetchTransport.handle_async_request = handle_async_request

src/worker.py

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,81 @@
22
from workers import DurableObject
33
from logger import logger
44
import sys
5+
import urllib.parse
6+
import httpx_patch # noqa: F401
57
sys.path.insert(0, "/session/metadata/vendor")
68
sys.path.insert(0, "/session/metadata")
79

810

9-
def setup_server():
11+
def setup_server(base_url: str):
12+
from fastapi import FastAPI, Request
13+
from pydantic import BaseModel
14+
from fastapi_mcp import FastApiMCP
1015
from exceptions import HTTPException, http_exception
11-
from mcp.server.fastmcp import FastMCP
12-
mcp = FastMCP("Demo")
13-
14-
@mcp.tool()
15-
def add(a: int, b: int) -> int:
16-
"""Add two numbers"""
17-
return a + b
18-
19-
@mcp.resource("greeting://{name}")
20-
def get_greeting(name: str) -> str:
21-
"""Get a personalized greeting"""
22-
return f"Hello, {name}!"
23-
24-
@mcp.tool()
25-
def calculate_bmi(weight_kg: float, height_m: float) -> float:
26-
"""Calculate BMI given weight in kg and height in meters"""
27-
return weight_kg / (height_m**2)
28-
29-
@mcp.prompt()
30-
def echo_prompt(message: str) -> str:
31-
"""Create an echo prompt"""
32-
return f"Please process this message: {message}"
33-
34-
app = mcp.sse_app()
16+
17+
app = FastAPI()
3518
app.add_exception_handler(HTTPException, http_exception)
19+
20+
mcp = FastApiMCP(
21+
app,
22+
# Optional parameters
23+
name="My API MCP",
24+
description="My API description",
25+
base_url=base_url
26+
)
27+
28+
# Mount the MCP server directly to your FastAPI app
29+
mcp.mount()
30+
# Auto-generated operation_id (something like "read_user_users__user_id__get")
31+
@app.get("/")
32+
async def root():
33+
return {"message": "Hello, World!"}
34+
35+
@app.get("/env")
36+
async def root(req: Request):
37+
env = req.scope["env"]
38+
return {"message": "Here is an example of getting an environment variable: " + env.MESSAGE}
39+
40+
class Item(BaseModel):
41+
name: str
42+
description: str | None = None
43+
price: float
44+
tax: float | None = None
45+
46+
@app.post("/items/")
47+
async def create_item(item: Item):
48+
return item
49+
50+
@app.put("/items/{item_id}")
51+
async def create_item(item_id: int, item: Item, q: str | None = None):
52+
result = {"item_id": item_id, **item.dict()}
53+
if q:
54+
result.update({"q": q})
55+
return result
56+
57+
@app.get("/items/{item_id}")
58+
async def read_item(item_id: int):
59+
return {"item_id": item_id}
60+
61+
mcp.setup_server()
3662
return mcp, app
3763

3864

3965
class FastMCPServer(DurableObject):
4066
def __init__(self, ctx, env):
4167
self.ctx = ctx
4268
self.env = env
43-
self.mcp, self.app = setup_server()
69+
self.mcp = None
70+
self.app = None
71+
self.base_url = None
4472

4573
async def call(self, request):
4674
import asgi
75+
request_url = urllib.parse.urlparse(request.url)
76+
base_url = f"{request_url.scheme}://{request_url.netloc}/"
77+
if self.mcp is None or self.app is None or self.base_url is None or self.base_url != base_url:
78+
self.mcp, self.app = setup_server(base_url)
79+
self.base_url = base_url
4780
return await asgi.fetch(self.app, request, self.env, self.ctx)
4881

4982
async def on_fetch(request, env):

vendor.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
mcp
2-
structlog
1+
fastapi-mcp
2+
fastapi
3+
pydantic
4+
structlog

0 commit comments

Comments
 (0)