Skip to content

Risk overrides Phase 2: REST + GUI surface for per-tool MCP risk - #461

Open
kayeungadrian-tam wants to merge 1 commit into
andrewyng:mainfrom
kayeungadrian-tam:feat/risk-override-surface
Open

Risk overrides Phase 2: REST + GUI surface for per-tool MCP risk#461
kayeungadrian-tam wants to merge 1 commit into
andrewyng:mainfrom
kayeungadrian-tam:feat/risk-override-surface

Conversation

@kayeungadrian-tam

Copy link
Copy Markdown

What was broken

RiskOverrideStore (coworker/overrides.py) is annotated as the Phase 2 relaxation path for MCP's conservative default, and build_engine already wires it into every PermissionEngine — but nothing could ever write it. There was no REST endpoint and no UI, so every MCP tool prompted as external forever: a read-only get_status call gated exactly like a destructive write, on every single call. Hand-editing risk_overrides.json didn't help live sessions either, because rules loaded once at engine build.

Found while running a real deployment: a heatstroke-monitoring automation calling a read-only MCP status tool every 10 minutes — each poll parked an approval.

What this adds

  • overrides.py: mtime-watched lazy reload — a REST write through the manager's store instance is seen immediately by the per-engine instances captured in live PermissionEngines (no rebuild, no reload endpoint); plus remove_rule() / rules().
  • Manager: a shared store, list/set/delete methods, and /v1/mcp/{name}/tools rows enriched with full_name, effective risk, default_risk, and overridden.
  • REST: GET/POST/DELETE /v1/risk-overrides with risk-class validation. User-local trust decisions only — the persona/no-self-grant boundary is untouched (personas still cannot reach this path).
  • GUI: tool chips on the MCP server row show asks / read / auto; clicking a tool trusts it as read-only for this machine, clicking again restores approval.

How it looks

tool risk chips: get_status relaxed to auto, set_value still asks

get_status has an active override (runs without asking); set_value keeps the conservative default.

Verification

  • pytest tests -q: 1108 passed, no new failures (the 7 test_bedrock_provider failures reproduce identically on clean main in a venv without the bedrock extra)
  • npm run build: clean · npm test: no new failures
  • Playwright: new e2e/mcp-tool-risk.spec.ts (chip toggle round-trip) plus the four adjacent MCP specs, all green
  • New unit tests: cross-instance reload propagation, REST round-trip incl. validation errors, remove/list

🤖 Generated with Claude Code

The RiskOverrideStore existed and was wired into every engine, but nothing
could write it — no endpoint, no UI — so MCP's conservative default (every
tool prompts as external) could only be relaxed by hand-editing
risk_overrides.json, and live engines would not notice anyway (rules loaded
once at engine build).

- overrides.py: mtime-watched lazy reload, so REST writes through the
  manager's store instance are seen immediately by the per-engine instances
  captured in live PermissionEngines — no rebuild; adds remove_rule/rules
- manager: shared store, list/set/delete methods, and /v1/mcp/{name}/tools
  rows enriched with full_name + effective/default risk + overridden flag
- app.py: GET/POST/DELETE /v1/risk-overrides (user-local trust decisions;
  personas still cannot reach this path — no-self-grant preserved)
- GUI: tool chips on the MCP server row show asks/read/auto and toggle a
  read override per tool (click to trust, click to restore approval)
- tests: reload propagation across instances, REST round-trip incl.
  validation, remove/list; e2e for the chip toggle flow

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant