Skip to content

Commit b3a965b

Browse files
DumbrisAlgis Dumbris
andauthored
docs(spec): amend Spec 049 — legalize server_quarantined status (six-value taxonomy) (#779)
Aligns Spec 049 with PR #778, which adds a sixth disabled-tool status, server_quarantined, surfaced by a dedicated quarantined-tool discovery pass (quarantined tools are deliberately excluded from the search index as a TPA defense). Spec 049 pinned the taxonomy to exactly five values and assumed all locked tools live in the index, so #778's behavior was correct but undocumented. - FR-004: five -> six values; server_quarantined assigned by the discovery pass (not the classifier), name-only, description/schema withheld; config-denied tools skipped by the pass. - FR-003: note the name-only exception for quarantined entries. - Assumptions: quarantined tools are excluded from the index and enumerated from authoritative quarantine state. - contracts/mcp-deltas.md: add server_quarantined to the status enum + example response shape and remediation. - design doc taxonomy: five -> six, with the server_quarantined explanation. Related #778 Co-authored-by: Algis Dumbris <gordon.greatests@gmail.com>
1 parent 54d10a7 commit b3a965b

3 files changed

Lines changed: 53 additions & 9 deletions

File tree

docs/superpowers/specs/2026-05-18-agent-discoverable-disabled-tools-design.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ not exercised.
9191

9292
### 4.3 Status taxonomy & classification
9393

94-
`status` is one of five values. Classification order is **first match wins**:
94+
`status` is one of six values. The first five are classifier-assigned to
95+
index-discoverable tools; classification order is **first match wins**:
9596

9697
| Order | Condition | `status` | `remediation` text (emitted once if present) |
9798
|------|-----------|----------|----------------------------------------------|
@@ -105,6 +106,17 @@ not exercised.
105106
*wrong* remediation (e.g. telling the user to toggle a UI switch for a
106107
config-locked tool). The four happy-path states stay clean.
107108

109+
The sixth value, `server_quarantined`, is **not** classifier-assigned: quarantined
110+
tools are deliberately absent from the search index (their untrusted descriptions
111+
are withheld as a Tool Poisoning Attack defense), so the index loop cannot reach
112+
them. A dedicated discovery pass enumerates them from authoritative quarantine
113+
state and emits **name-only** locked entries — a tool on a quarantined server gets
114+
`server_quarantined`; a tool-level pending/changed approval on a trusted server
115+
re-uses `pending_approval`. A tool also denied by operator config is skipped
116+
(approval could not make it callable). Remediation for `server_quarantined`: "Its
117+
server is quarantined for security review. Its tools cannot be called until the
118+
user reviews and approves the server in the mcpproxy UI or system tray."
119+
108120
### 4.4 Reactive triggers (discoverability)
109121

110122
Option C's only weakness is the agent not knowing the flag exists. Closed two ways:

specs/049-agent-discoverable-disabled-tools/contracts/mcp-deltas.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,27 @@ Tool description gains exactly one sentence (FR-014):
2424
"tools": [ /* callable results — UNCHANGED order/shape (FR-002, FR-006) */ ],
2525
"disabled": [ // NEW, after callable, ≤ min(limit,10)
2626
{ "name": "delete_repo", "server": "github",
27-
"description": "Delete a repository", "status": "disabled_by_config" }
27+
"description": "Delete a repository", "status": "disabled_by_config" },
28+
// Quarantined-tool discovery pass: name-only, description/schema withheld
29+
// (TPA defense); `name` is the "<server>:<tool>" key. Prepended ahead of
30+
// index-derived locked entries so the min(limit,10) cap can't drop them.
31+
{ "name": "github:rotate_keys", "server": "github",
32+
"status": "server_quarantined" }
2833
],
2934
"remediation": { // NEW, once, only present statuses
30-
"disabled_by_config": "Locked by operator policy in mcp_config.json (enabled_tools/disabled_tools). The user cannot enable this from the UI; ask the operator to change the server config."
35+
"disabled_by_config": "Locked by operator policy in mcp_config.json (enabled_tools/disabled_tools). The user cannot enable this from the UI; ask the operator to change the server config.",
36+
"server_quarantined": "Its server is quarantined for security review. Its tools cannot be called until the user reviews and approves the server in the mcpproxy UI or system tray."
3137
}
3238
}
3339
```
3440

41+
`status` enum (FR-004): `disabled_by_config`, `disabled_by_user`,
42+
`pending_approval`, `server_disabled`, `disabled_unknown`, `server_quarantined`.
43+
The first five are classifier-assigned to index-discoverable tools; the last is
44+
assigned by the quarantined-tool discovery pass (also re-using `pending_approval`
45+
for tool-level pending/changed approvals), which surfaces name-only entries from
46+
authoritative quarantine state because quarantined tools are not in the index.
47+
3548
### Response delta (0 callable results, ≥1 locked match, flag OFF) — FR-009
3649

3750
A one-line note added to the existing result text:

specs/049-agent-discoverable-disabled-tools/spec.md

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,23 @@ callable one carries none.
120120
fields, no reordering).
121121
- **FR-003**: Each returned locked tool MUST carry a lean shape: name, owning
122122
server, the existing one-line description, and a single `status` value.
123-
- **FR-004**: `status` MUST be one of exactly five values —
123+
Exception: a `server_quarantined` entry (and any tool surfaced by the
124+
quarantined-tool discovery pass) withholds the description and schema, so it
125+
carries name + owning server + `status` only.
126+
- **FR-004**: `status` MUST be one of exactly six values —
124127
`disabled_by_config`, `disabled_by_user`, `pending_approval`,
125-
`server_disabled`, `disabled_unknown` — assigned by fixed first-match
126-
precedence in that order (server-off, then config, then user-disabled, then
127-
pending approval, else unknown).
128+
`server_disabled`, `disabled_unknown`, `server_quarantined`. The first five
129+
are assigned to index-discoverable tools by fixed first-match precedence in
130+
that order (server-off, then config, then user-disabled, then pending
131+
approval, else unknown). `server_quarantined` is assigned separately by the
132+
quarantined-tool discovery pass (not the classifier), because quarantined
133+
tools are deliberately absent from the search index (see Assumptions): it
134+
covers a tool on a quarantined server (status `server_quarantined`) and a
135+
tool-level pending/changed approval on a trusted server (status
136+
`pending_approval`), with description and schema withheld to avoid exposing a
137+
Tool Poisoning Attack payload. A tool also denied by operator config
138+
(`enabled_tools`/`disabled_tools`) is skipped by this pass rather than
139+
surfaced, since approval could not make it callable.
128140
- **FR-005**: The response MUST include a single remediation map emitted once,
129141
containing only the keys for statuses actually present in the response; no
130142
per-tool remediation text.
@@ -154,7 +166,8 @@ callable one carries none.
154166
### Key Entities *(include if feature involves data)*
155167

156168
- **Locked tool entry**: A discovered tool that is not callable — name, server,
157-
one-line description, and one `status` of the five-value set.
169+
one-line description (withheld for quarantined entries), and one `status` of
170+
the six-value set.
158171
- **Status**: The single machine-branchable reason a tool is not callable,
159172
mapped 1:1 to a remediation class.
160173
- **Remediation map**: Response-level mapping from each present status to one
@@ -185,7 +198,13 @@ callable one carries none.
185198

186199
- Locked tools are present in the existing search index (verified during
187200
brainstorming: indexing does not filter by callability; filtering is
188-
request-time only).
201+
request-time only). Exception: quarantined tools — both on a quarantined
202+
server and tool-level pending/changed approvals — are deliberately excluded
203+
from the search index so their untrusted descriptions cannot be ranked or
204+
exposed (Tool Poisoning Attack defense). They are therefore not reachable via
205+
the index loop and are instead enumerated from authoritative quarantine state
206+
by a dedicated discovery pass that emits name-only `server_quarantined` /
207+
`pending_approval` entries.
189208
- The config-vs-user discriminator introduced by PR #468 is available and is
190209
reused unchanged as the authoritative config-denial signal.
191210
- "Limit" refers to the discovery request's existing result-limit parameter.

0 commit comments

Comments
 (0)