Skip to content

Commit e11ab83

Browse files
instrument mcp server
1 parent a196315 commit e11ab83

File tree

5 files changed

+58
-1
lines changed

5 files changed

+58
-1
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ $ touch .env && echo MONGODB_CONNECTION_STRING="$ConnectionString" > .env
3030
- Dev: `python3 -m uvicorn src:app --reload --port 3000`
3131
- Prod: `gunicorn -k uvicorn.workers.UvicornWorker src:app -b 0.0.0.0:3000`
3232

33+
## MCP Server
34+
- Infinity API automatically serves an MCP bridge at `/mcp` alongside the REST endpoints.
35+
3336
## Project structure
3437
```
3538
├── README.md # this file

src/api.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,23 @@
99
from src import logger, parse_error
1010
from src.routes import flight, environment, motor, rocket
1111
from src.utils import RocketPyGZipMiddleware
12+
from src.mcp.server import build_mcp
1213

1314
app = FastAPI(
15+
title="Infinity API",
1416
swagger_ui_parameters={
1517
"defaultModelsExpandDepth": 0,
1618
"syntaxHighlight.theme": "obsidian",
17-
}
19+
},
1820
)
1921
app.include_router(flight.router)
2022
app.include_router(environment.router)
2123
app.include_router(motor.router)
2224
app.include_router(rocket.router)
2325

26+
_mcp_server = build_mcp(app)
27+
app.mount('/mcp', _mcp_server.http_app())
28+
2429
FastAPIInstrumentor.instrument_app(app)
2530
RequestsInstrumentor().instrument()
2631

src/mcp/__init__.py

Whitespace-only changes.

src/mcp/server.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""FastMCP integration helpers for Infinity API."""
2+
3+
from __future__ import annotations
4+
5+
from fastapi import FastAPI
6+
from fastmcp import FastMCP
7+
8+
9+
def build_mcp(app: FastAPI) -> FastMCP:
10+
"""
11+
Create (or return cached) FastMCP server
12+
that mirrors the FastAPI app.
13+
"""
14+
15+
if hasattr(app.state, 'mcp'):
16+
return app.state.mcp # type: ignore[attr-defined]
17+
18+
mcp = FastMCP.from_fastapi(app, name=app.title)
19+
app.state.mcp = mcp # type: ignore[attr-defined]
20+
return mcp
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from unittest.mock import MagicMock, patch
2+
3+
import pytest
4+
5+
from src.api import app
6+
from src.mcp.server import build_mcp
7+
8+
9+
@pytest.fixture(autouse=True)
10+
def reset_mcp_state():
11+
if hasattr(app.state, 'mcp'):
12+
delattr(app.state, 'mcp')
13+
yield
14+
if hasattr(app.state, 'mcp'):
15+
delattr(app.state, 'mcp')
16+
17+
18+
def test_build_mcp_uses_fastapi_adapter():
19+
mock_mcp = MagicMock()
20+
with patch(
21+
'src.mcp.server.FastMCP.from_fastapi', return_value=mock_mcp
22+
) as mock_factory:
23+
result = build_mcp(app)
24+
assert result is mock_mcp
25+
mock_factory.assert_called_once_with(app, name=app.title)
26+
# Subsequent calls reuse cached server
27+
again = build_mcp(app)
28+
assert again is mock_mcp
29+
mock_factory.assert_called_once()

0 commit comments

Comments
 (0)