Draft QONTO-WP-0003: Phase 2 MCP surface workplan
Streamable-HTTP MCP adapter on the same capability core and policy kernel as Phase 1's REST surface, per specs/ArchitectureBlueprint.md Phase 2. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
ca12843013
commit
d643e552f4
1 changed files with 159 additions and 0 deletions
159
workplans/QONTO-WP-0003-mcp-surface.md
Normal file
159
workplans/QONTO-WP-0003-mcp-surface.md
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
---
|
||||
id: QONTO-WP-0003
|
||||
type: workplan
|
||||
title: "Phase 2 — MCP surface for all harnesses"
|
||||
domain: infotech
|
||||
repo: qonto-assistant
|
||||
status: ready
|
||||
owner: codex
|
||||
topic_slug: the-custodian
|
||||
created: "2026-07-22"
|
||||
updated: "2026-07-22"
|
||||
---
|
||||
|
||||
# Phase 2 — MCP surface for all harnesses
|
||||
|
||||
Execute **Phase 2** of `specs/ArchitectureBlueprint.md` §6: add a
|
||||
streamable-HTTP MCP adapter on top of the *same* protocol-neutral capability
|
||||
core and `decide(request, claims) -> Allow|Deny` policy kernel that Phase 1's
|
||||
REST surface already uses. No new capabilities and no policy fork — MCP is a
|
||||
second transport onto the existing allow-listed reads
|
||||
(`org_summary`, `list_transactions`, `cost_run_rate_hints`, optionally
|
||||
`snapshot_bundle`). Spend/volume-cost/credential-exfil stay hard-denied.
|
||||
|
||||
**Depends on:** QONTO-WP-0002 (done) for the capability core, policy kernel,
|
||||
Qonto client, and audit layer this phase reuses. **Non-goals:** flex-auth
|
||||
resource scopes and multi-tenant path layout (Phase 3/4); vendor
|
||||
`qonto-mcp-server` as an internal backend (blueprint §4.3/§5.1 — explicitly
|
||||
rejected).
|
||||
|
||||
## Task: MCP adapter skeleton on the shared capability core
|
||||
|
||||
```task
|
||||
id: QONTO-WP-0003-T01
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
Pick an MCP server library/transport for the Python 3.12 stack (streamable-HTTP,
|
||||
per blueprint §6 Phase 2.1 — not stdio-only). Scaffold the adapter under
|
||||
`src/qonto_assistant/` alongside the existing REST adapter, routing every tool
|
||||
call through the same `CapabilityRequest` → `decide()` path used by REST
|
||||
(`protocol: mcp` on the request). Do not duplicate policy logic in the MCP
|
||||
layer.
|
||||
|
||||
Done when: adapter starts, exposes an empty/smoke tool list, and a manual
|
||||
`decide()` call for a known-deny case is proven to hit the identical policy
|
||||
path as the REST equivalent (e.g. shared unit test parametrized over both
|
||||
protocols).
|
||||
|
||||
## Task: MCP tool catalog mapped to allowed capabilities
|
||||
|
||||
```task
|
||||
id: QONTO-WP-0003-T02
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
Implement MCP tools per the capability map in `specs/ArchitectureBlueprint.md`
|
||||
§5:
|
||||
|
||||
| Internal capability id | MCP tool name |
|
||||
| --- | --- |
|
||||
| `org_summary` | `qonto_org_summary` |
|
||||
| `list_transactions` | `qonto_list_transactions` |
|
||||
| `cost_run_rate_hints` | `qonto_cost_run_rate_hints` |
|
||||
| `snapshot_bundle` | optional — only if it stays a pure orchestrator over already-allowed reads, not a bigger export |
|
||||
|
||||
Tool names may differ from REST paths but must resolve to the same internal
|
||||
capability id and go through the same deny-reason vocabulary (authz, tenant
|
||||
scope, argument constraint, credential exfil). No `find_counterparties` or any
|
||||
write/spend-shaped tool — those stay hard-denied per Phase 1 policy.
|
||||
|
||||
Done when: unit tests cover allow paths for each tool and deny paths for
|
||||
out-of-catalog / spend-shaped tool names, mirroring the REST policy tests from
|
||||
QONTO-WP-0002-T02.
|
||||
|
||||
## Task: Client auth and one shared config snippet
|
||||
|
||||
```task
|
||||
id: QONTO-WP-0003-T03
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
Wire OIDC/workload auth for the MCP endpoint — **no bank secrets ever reach
|
||||
the client**, matching the REST surface's custody model (blueprint §4.1,
|
||||
§7). Produce a single documented client config snippet usable across
|
||||
Claude Code, Codex, Cursor, and Grok-style harnesses: URL + auth only, no
|
||||
per-harness variants.
|
||||
|
||||
Done when: the snippet is in `docs/operator-runbook.md` (or a new
|
||||
`docs/mcp-integration.md`), and a local smoke connects a real MCP client
|
||||
against the running adapter using only that snippet.
|
||||
|
||||
## Task: agent-harness tool profile `finance-qonto-read`
|
||||
|
||||
```task
|
||||
id: QONTO-WP-0003-T04
|
||||
status: todo
|
||||
priority: medium
|
||||
```
|
||||
|
||||
Register the `finance-qonto-read` tool profile (blueprint §6 Phase 2.3) so
|
||||
harness-side configuration grants this MCP surface only to the intended
|
||||
assistant context, not blanket per-harness key sprawl. Coordinate with
|
||||
whichever agent-harness config surface is authoritative for profile
|
||||
definitions (outside this repo) — this task covers qonto-assistant's side:
|
||||
documenting the profile contract (tool names, required scopes) it expects
|
||||
callers to present.
|
||||
|
||||
Done when: profile contract is documented and referenced from the MCP
|
||||
integration doc; at least one harness config example uses it end-to-end.
|
||||
|
||||
## Task: Unified audit schema across REST and MCP
|
||||
|
||||
```task
|
||||
id: QONTO-WP-0003-T05
|
||||
status: todo
|
||||
priority: medium
|
||||
```
|
||||
|
||||
Confirm MCP tool calls emit the exact same structured audit event shape as
|
||||
REST calls (actor, capability, decision, deny_reason, latency, upstream HTTP
|
||||
status, policy version — no secret fields), per blueprint §6 Phase 2.4. State
|
||||
Hub continues to receive operator-level progress notes, not per-call
|
||||
hot-path events — do not wire per-invocation State Hub writes.
|
||||
|
||||
Done when: a test asserts REST and MCP audit records for the same capability
|
||||
call are schema-identical modulo protocol field.
|
||||
|
||||
## Task: MCP smoke path + operator runbook update
|
||||
|
||||
```task
|
||||
id: QONTO-WP-0003-T06
|
||||
status: todo
|
||||
priority: medium
|
||||
```
|
||||
|
||||
Add an MCP-equivalent of `scripts/smoke_rest_api.py`: start the service,
|
||||
connect over streamable-HTTP, exercise each allowed tool against fixture data
|
||||
(`QONTO_FIXTURE_DIR`), and confirm deny behavior for an out-of-catalog tool.
|
||||
Extend `docs/operator-runbook.md` with MCP start/verify instructions
|
||||
alongside the existing REST section.
|
||||
|
||||
Done when: smoke script passes locally against fixtures without real Qonto
|
||||
credentials, matching the Phase 1 REST smoke pattern.
|
||||
|
||||
## Task: Closure review
|
||||
|
||||
```task
|
||||
id: QONTO-WP-0003-T07
|
||||
status: todo
|
||||
priority: low
|
||||
```
|
||||
|
||||
Mark workplan finished when T01–T06 are done; confirm REST and MCP surfaces
|
||||
enforce identical policy outcomes end to end. Note Phase 3 seed (flex-auth
|
||||
resource `finance.qonto.read`, graduated quotas, redaction profiles) in
|
||||
closure. Run `statehub fix-consistency`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue