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
|
|
@ -1,7 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from contextlib import suppress
|
||||
from collections.abc import AsyncIterator, Callable
|
||||
from contextlib import AsyncExitStack, asynccontextmanager, suppress
|
||||
import logging
|
||||
from uuid import uuid4
|
||||
|
||||
|
|
@ -16,6 +16,7 @@ from qonto_assistant.auth import actor_claims_from_request
|
|||
from qonto_assistant.config import Settings
|
||||
from qonto_assistant.credentials import build_credential_provider
|
||||
from qonto_assistant.errors import QontoAssistantError, UpstreamError
|
||||
from qonto_assistant.mcp_server import create_mcp_server
|
||||
from qonto_assistant.policy import PolicyEngine
|
||||
from qonto_assistant.qonto_client import FixtureQontoClient, QontoClient
|
||||
from qonto_assistant.rate_limits import ConcurrencyLimiter, RateLimiter
|
||||
|
|
@ -39,9 +40,21 @@ def create_app(
|
|||
concurrency_limiter=concurrency_limiter,
|
||||
)
|
||||
|
||||
app = FastAPI(title="qonto-assistant", version=__version__)
|
||||
mcp_server = create_mcp_server(settings=settings)
|
||||
mcp_app = mcp_server.streamable_http_app()
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
|
||||
async with AsyncExitStack() as stack:
|
||||
await stack.enter_async_context(mcp_app.router.lifespan_context(mcp_app))
|
||||
yield
|
||||
with suppress(Exception):
|
||||
service.client.close()
|
||||
|
||||
app = FastAPI(title="qonto-assistant", version=__version__, lifespan=lifespan)
|
||||
app.state.settings = settings
|
||||
app.state.service = service
|
||||
app.mount("/mcp", mcp_app)
|
||||
|
||||
@app.exception_handler(QontoAssistantError)
|
||||
def handle_qonto_error(_: Request, exc: QontoAssistantError) -> JSONResponse:
|
||||
|
|
@ -108,11 +121,6 @@ def create_app(
|
|||
)
|
||||
return JSONResponse(content=payload, headers={"X-Request-ID": request_id})
|
||||
|
||||
@app.on_event("shutdown")
|
||||
def shutdown_event() -> None:
|
||||
with suppress(Exception):
|
||||
service.client.close()
|
||||
|
||||
return app
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue