QONTO-WP-0003-T01: MCP adapter skeleton on shared capability core
Add a streamable-HTTP MCP adapter (mcp_server.py, official FastMCP SDK) mounted at /mcp in the existing FastAPI app, with a combined lifespan so the MCP session manager starts/stops with the service. Ships one smoke tool (qonto_ping, no bank call) — real capability tools land in T02. Prove REST and MCP hit the identical policy path: PolicyEngine.decide() never branches on request.protocol, verified by a parametrized test. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
dc3431cda0
commit
d4656be310
6 changed files with 133 additions and 10 deletions
37
src/qonto_assistant/mcp_server.py
Normal file
37
src/qonto_assistant/mcp_server.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from starlette.applications import Starlette
|
||||
|
||||
from qonto_assistant import __version__
|
||||
from qonto_assistant.config import Settings
|
||||
|
||||
|
||||
def create_mcp_server(*, settings: Settings) -> FastMCP:
|
||||
"""Build the MCP adapter on the same capability core as the REST surface.
|
||||
|
||||
Phase 2 (QONTO-WP-0003) scope note: this skeleton exposes only a smoke
|
||||
tool. Real read tools (`qonto_org_summary`, `qonto_list_transactions`,
|
||||
`qonto_cost_run_rate_hints`) land in QONTO-WP-0003-T02, routed through the
|
||||
same `CapabilityRequest` -> `PolicyEngine.decide()` path REST already
|
||||
uses -- no policy fork between transports.
|
||||
"""
|
||||
server = FastMCP(
|
||||
name=settings.service_name,
|
||||
instructions=(
|
||||
"Governed read-only Qonto finance awareness for Binky. "
|
||||
"No spend, transfer, card, or volume-cost tools are exposed here."
|
||||
),
|
||||
stateless_http=True,
|
||||
)
|
||||
|
||||
@server.tool()
|
||||
def qonto_ping() -> dict[str, str]:
|
||||
"""Smoke tool confirming the MCP adapter is reachable. No bank call."""
|
||||
return {"status": "ok", "service": settings.service_name, "version": __version__}
|
||||
|
||||
return server
|
||||
|
||||
|
||||
def mcp_asgi_app(*, settings: Settings) -> Starlette:
|
||||
return create_mcp_server(settings=settings).streamable_http_app()
|
||||
Loading…
Add table
Add a link
Reference in a new issue