From 1e476309a300cb571f5f27621d1058d7cab9f592 Mon Sep 17 00:00:00 2001 From: tegwick Date: Thu, 23 Jul 2026 09:48:36 +0200 Subject: [PATCH] QONTO-WP-0003-T04: document finance-qonto-read tool profile contract qonto-assistant's side only, per task scope: the contract a profile named finance-qonto-read must satisfy in agent-harness (~/agent-harness, ADR-001, ToolProfile) to reach this MCP surface -- lane (green/blue, never red), the exact mcp__qonto-assistant__* allowed-tools list, and the optional finance.qonto.read scope. Checked agent-harness/agent_harness/profiles.py directly: only CLI-only profiles exist today (green-commit-only, blue-mail-triage), none grant an MCP server yet. Included a ready-to-paste ToolProfile entry matching its actual schema, marked as a proposal for agent-harness's own PR -- not applied here, since registering it is a separate repo/workplan. Added the end-to-end example: instance manifest -> harness-injected MCP server config -> the four allowed tool calls. Co-Authored-By: Claude Sonnet 5 --- docs/mcp-integration.md | 79 ++++++++++++++++++++++++++ workplans/QONTO-WP-0003-mcp-surface.md | 25 +++++++- 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/docs/mcp-integration.md b/docs/mcp-integration.md index 15d905d..71399a4 100644 --- a/docs/mcp-integration.md +++ b/docs/mcp-integration.md @@ -74,6 +74,85 @@ Replace the `url` host/port with wherever the service is actually deployed. whatever secret-interpolation syntax your harness config supports — never paste the literal token into a shared config file. +## Agent-harness tool profile: `finance-qonto-read` + +`agent-harness` (`~/agent-harness`, ADR-001) is the session runtime that +coding-agent sessions actually run inside. It enforces a hard tool allow-list +per session via a named `ToolProfile` (`agent_harness/profiles.py`) — +instances declare a profile by name in their manifest; the harness resolves +and enforces the allow-list; instances never enumerate tools themselves. +Today `agent_harness/profiles.py` only registers CLI/session profiles +(`green-commit-only`, `blue-mail-triage`) built from Claude Code +`--allowedTools` strings — no profile currently grants any MCP server. + +This is **qonto-assistant's side only**: the contract a profile named +`finance-qonto-read` must satisfy so a session granted it can reach this +MCP surface and nothing else. Registering the profile itself is +agent-harness's own workplan/repo — this is not applied here. + +**Contract:** + +| Field | Value | +| --- | --- | +| Profile name | `finance-qonto-read` | +| Lane | `green` or `blue` only — never `red` (matches `specs/ArchitectureBlueprint.md` §4.6: reads are Green/Blue; plan/key/transfer changes are Red, and this surface never exposes those anyway) | +| MCP server | the `qonto-assistant` entry from the client config snippet below, with the bearer token and `X-Actor-*` headers injected by the harness — never left for the instance to fill in | +| Allowed tools | `mcp__qonto-assistant__qonto_ping`, `mcp__qonto-assistant__qonto_org_summary`, `mcp__qonto-assistant__qonto_list_transactions`, `mcp__qonto-assistant__qonto_cost_run_rate_hints` — the full catalog above, nothing more (no future write tool is ever silently included; adding one here always requires an explicit profile edit) | +| Required scope (optional today) | `finance.qonto.read` — matches `QONTO_ASSISTANT_REQUIRED_SCOPE`; only enforced when the service sets `QONTO_ASSISTANT_ENFORCE_SCOPE=true` | + +**Proposed `agent_harness/profiles.py` entry** (for agent-harness's own PR, +not applied by this task): + +```python +"finance-qonto-read": ToolProfile( + name="finance-qonto-read", + description=( + "Read-only Qonto finance awareness via qonto-assistant MCP. " + "No spend, transfer, card, or volume-cost tools." + ), + allowed_tools=( + "mcp__qonto-assistant__qonto_ping," + "mcp__qonto-assistant__qonto_org_summary," + "mcp__qonto-assistant__qonto_list_transactions," + "mcp__qonto-assistant__qonto_cost_run_rate_hints" + ), + lane="green", +), +``` + +**End-to-end example** — an agent-harness instance manifest declaring the +profile, paired with the MCP server config the harness would inject for that +session (the same shape as the shared snippet above, scoped to one profile): + +```yaml +# instance manifest (agent-harness side) +tool_profile: finance-qonto-read +``` + +```json +// MCP server config the harness injects for a finance-qonto-read session +{ + "mcpServers": { + "qonto-assistant": { + "url": "http://127.0.0.1:8080/mcp", + "headers": { + "Authorization": "Bearer ${QONTO_ASSISTANT_MCP_TOKEN}", + "X-Actor-ID": "agent-harness:${INSTANCE_ID}", + "X-Tenant-ID": "binky", + "X-Actor-Lane": "green" + } + } + } +} +``` + +The instance never sees the bearer token or picks its own headers — the +harness resolves the profile, injects this config, and the session's +`--allowedTools` allow-list (from the profile) is the only thing standing +between the model and which of the four tools it can call. Policy +enforcement itself still happens inside qonto-assistant regardless of what +the harness allows, per the shared `PolicyEngine.decide()` path. + ## Local smoke: start with auth enabled, connect with only this snippet ```bash diff --git a/workplans/QONTO-WP-0003-mcp-surface.md b/workplans/QONTO-WP-0003-mcp-surface.md index 6a58bd9..7a70e5f 100644 --- a/workplans/QONTO-WP-0003-mcp-surface.md +++ b/workplans/QONTO-WP-0003-mcp-surface.md @@ -175,7 +175,7 @@ headers) listed tools and called `qonto_org_summary` successfully, with ```task id: QONTO-WP-0003-T04 -status: todo +status: done priority: medium state_hub_task_id: "c6805812-8419-4200-b4ad-a048a541b3be" ``` @@ -191,6 +191,29 @@ 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. +**Done 2026-07-23:** Checked `~/agent-harness` (ADR-001, +`agent_harness/profiles.py`) directly rather than guessing the shape — +`ToolProfile` is real: instances declare a profile by name, the harness +resolves and enforces a hard `--allowedTools` allow-list, instances never +enumerate tools. Today only `green-commit-only` and `blue-mail-triage` are +registered, both CLI-only; no profile currently grants any MCP server. + +Added a **`finance-qonto-read`** section to `docs/mcp-integration.md` with +the qonto-assistant-side contract: lane (`green`/`blue` only, never `red`), +the exact `mcp__qonto-assistant__*` allowed-tools list (all four tools, +nothing more), and the optional `finance.qonto.read` required scope. Included +a ready-to-paste proposed `ToolProfile` entry matching agent-harness's actual +`profiles.py` schema, explicitly marked as a proposal for agent-harness's own +PR — **not applied to that repo by this task**, since registering it there is +outside qonto-assistant's scope and a separate repo/workplan. Added the +end-to-end example the done-criteria asks for: an instance manifest +(`tool_profile: finance-qonto-read`) paired with the MCP server config the +harness would inject for that session, showing the full path from profile +name to the four allowed tool calls. + +No code changes — this task is qonto-assistant-side documentation only, per +its own scope note. `pytest` → `28 passed` (unchanged). + ## Task: Unified audit schema across REST and MCP ```task