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:
tegwick 2026-07-22 21:41:24 +02:00
parent dc3431cda0
commit d4656be310
6 changed files with 133 additions and 10 deletions

View file

@ -4,7 +4,7 @@ type: workplan
title: "Phase 2 — MCP surface for all harnesses"
domain: infotech
repo: qonto-assistant
status: ready
status: active
owner: codex
topic_slug: the-custodian
created: "2026-07-22"
@ -32,7 +32,7 @@ rejected).
```task
id: QONTO-WP-0003-T01
status: todo
status: done
priority: high
state_hub_task_id: "b899ea70-13d4-4b70-8b45-b602359ab90b"
```
@ -49,6 +49,23 @@ Done when: adapter starts, exposes an empty/smoke tool list, and a manual
path as the REST equivalent (e.g. shared unit test parametrized over both
protocols).
**Done 2026-07-22:** Picked the official `mcp` Python SDK (`FastMCP`,
streamable-HTTP transport, `stateless_http=True`) — added as a dependency in
`pyproject.toml`. `src/qonto_assistant/mcp_server.py` builds the adapter with
one smoke tool (`qonto_ping`, no bank call). `app.py` mounts it at `/mcp` with
a combined FastAPI lifespan (`AsyncExitStack` entering the MCP session
manager's lifespan) so the adapter starts/stops with the service — verified
live (`StreamableHTTP session manager started/shutting down` in logs,
`/v1/health` still 200, `/mcp` reachable). `tests/test_policy.py` adds
`test_policy_decision_identical_across_protocols`, parametrized over
`protocol="rest"`/`"mcp"`, proving `PolicyEngine.decide()` is transport-
agnostic (the `protocol` field is carried on `CapabilityRequest` but never
branches policy logic). `tests/test_mcp_server.py` proves the adapter starts
and lists/calls the smoke tool. `pytest-asyncio` added as a dev dependency
(`asyncio_mode = "auto"`). Verified: `pytest``20 passed`;
`python3 -m compileall src tests scripts`. No capability tools yet — that's
QONTO-WP-0003-T02.
## Task: MCP tool catalog mapped to allowed capabilities
```task