From 9dd4c0693d6a924d008f449ad7d3a04e1e23394d Mon Sep 17 00:00:00 2001 From: Algis Dumbris Date: Fri, 19 Jun 2026 10:00:22 +0300 Subject: [PATCH] fix(frontend): surface pending tools in ServerDetail approval banner selectQuarantinedTools dropped freshly-pending tools whenever no tool was 'changed', so an all-pending, non-quarantined server hid the Tool-Quarantine banner even though those tools are genuinely blocked by the backend (checkToolApprovals -> BlockedTools) and the Servers page counts them (pending_count + changed_count). Banner and count now agree. - selectQuarantinedTools: surface both 'pending' and 'changed' tools on a non-quarantined server; keep suppression while the server is quarantined. - ServerDetail: subtle dismissible hint explaining pending tools come from tool-level quarantine and can be auto-approved via skip_quarantine / quarantine_enabled. - Tests: reconcile MCP-2101 trust-model unit tests to the new behavior, add all-pending regression test, add view-level banner/buttons/badge/hint test. - Docs: update tool-quarantine.md Web UI section. Related: MCP-2917 --- docs/features/tool-quarantine.md | 16 +-- frontend/src/utils/toolQuarantine.ts | 27 +++-- frontend/src/views/ServerDetail.vue | 32 +++++- .../tests/unit/quarantine-block-view.spec.ts | 4 +- .../quarantine-pending-banner-view.spec.ts | 106 ++++++++++++++++++ .../tests/unit/tool-quarantine-banner.spec.ts | 46 ++++---- 6 files changed, 187 insertions(+), 44 deletions(-) create mode 100644 frontend/tests/unit/quarantine-pending-banner-view.spec.ts diff --git a/docs/features/tool-quarantine.md b/docs/features/tool-quarantine.md index a8b8078e..f6b5329e 100644 --- a/docs/features/tool-quarantine.md +++ b/docs/features/tool-quarantine.md @@ -205,7 +205,7 @@ curl -H "X-API-Key: your-key" \ 1. Open the MCPProxy dashboard 2. Click on a server in the server list 3. Navigate to the **Tools** tab in the server detail view -4. Review changed (and any residual pending) tools and click **Approve** or **Approve All** +4. Review pending and changed tools and click **Approve** or **Approve All** Each quarantined tool also offers a **Block** button (and the banner a **Block All**) next to Approve. Blocking rejects the tool: it leaves the quarantine list @@ -213,12 +213,14 @@ and is disabled in the tools list, so AI agents can neither see nor call it. Blocking is reversible — re-enable the tool later with its toggle in the tools list (or `mcpproxy tools enable `). -The server detail view's **Tool Quarantine** banner is shown only when a tool's -status is `changed` (a rug-pull). Once a change has surfaced, any residual -`pending` tools are listed alongside it so they can be cleared in the same pass. -Freshly-`pending` baseline tools do **not** raise the banner on their own: -approving the **server** (lifting the server-level Security Quarantine) promotes -its baseline `pending` tools to `approved`. While the server-level **Security +The server detail view's **Tool Quarantine** banner surfaces every `pending` +(new, never-approved) or `changed` (rug-pull) tool while the server itself is +**not** quarantined. Both are genuinely blocked by the backend until the +operator acts, and the server list page counts them (`pending_count + +changed_count`), so the banner and the count agree. The banner carries a short, +dismissible hint noting that pending tools come from tool-level quarantine and +can be auto-approved by setting `skip_quarantine: true` (per-server) or +`quarantine_enabled: false` (global). While the server-level **Security Quarantine** banner is showing, the Tool-Quarantine banner is suppressed entirely — the operator approves the server first, and the two banners never appear at once. diff --git a/frontend/src/utils/toolQuarantine.ts b/frontend/src/utils/toolQuarantine.ts index 42e0a39b..b371e6f1 100644 --- a/frontend/src/utils/toolQuarantine.ts +++ b/frontend/src/utils/toolQuarantine.ts @@ -2,28 +2,33 @@ import type { ToolApproval } from '@/types' /** * Selects the tools that warrant the per-server Tool-Quarantine banner / list - * (Spec 032, parent MCP-2081, MCP-2101). + * (Spec 032, parent MCP-2916, MCP-2917). * - * Trust model (confirmed on MCP-2081): when an operator approves a *server* - * (lifting the server-level Security Quarantine), the backend promotes that - * server's baseline `pending` tools to `approved`. A freshly-`pending` baseline - * tool is therefore NOT a reason to nag the operator with a tool-level banner — - * only a `changed` tool (a rug-pull) is. + * On a live, NON-quarantined server a `pending` (new, never-approved) tool is + * genuinely blocked by the backend (`checkToolApprovals` → `BlockedTools`) and + * the Servers page already counts it (`pending_count + changed_count`). The + * banner must therefore surface both `pending` and `changed` tools so the + * operator can approve them; banner and count must agree. Pending tools come + * from tool-level quarantine and can be auto-approved by setting + * `skip_quarantine: true` (per-server) or `quarantine_enabled: false` (global). * * Rules: * - While the server is quarantined, suppress the tool banner entirely. The * server-level Security Quarantine banner already covers it and the operator * must approve the server first — never show two banners at once. - * - Otherwise the banner keys off `status === 'changed'`. Only once a change - * has surfaced do we also include any residual `pending` tools so the - * operator can clear them in the same approval pass. + * - Otherwise surface every tool that is `pending` (awaiting first approval) or + * `changed` (a rug-pull), since both are blocked until the operator acts. + * + * Note: this intentionally reverses the MCP-2101 "don't nag on a pending + * baseline" behavior for non-quarantined servers. That trust model assumed + * approving the server would promote pending→approved, but a server can be + * non-quarantined (e.g. `skip_quarantine`) while its tools stay pending and + * blocked, leaving the operator no way to approve them. */ export function selectQuarantinedTools( toolApprovals: ToolApproval[], serverQuarantined: boolean, ): ToolApproval[] { if (serverQuarantined) return [] - const hasChanged = toolApprovals.some((t) => t.status === 'changed') - if (!hasChanged) return [] return toolApprovals.filter((t) => t.status === 'changed' || t.status === 'pending') } diff --git a/frontend/src/views/ServerDetail.vue b/frontend/src/views/ServerDetail.vue index e037a0bd..d04e7f33 100644 --- a/frontend/src/views/ServerDetail.vue +++ b/frontend/src/views/ServerDetail.vue @@ -340,6 +340,26 @@
{{ quarantinedTools.length }} tool(s) require approval before they can be used by AI agents.
+ +
+ + Pending tools come from tool-level quarantine. To approve them automatically, set + skip_quarantine: true for this server or + quarantine_enabled: false globally. + + +