Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ not exercised.

### 4.3 Status taxonomy & classification

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

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

The sixth value, `server_quarantined`, is **not** classifier-assigned: quarantined
tools are deliberately absent from the search index (their untrusted descriptions
are withheld as a Tool Poisoning Attack defense), so the index loop cannot reach
them. A dedicated discovery pass enumerates them from authoritative quarantine
state and emits **name-only** locked entries — a tool on a quarantined server gets
`server_quarantined`; a tool-level pending/changed approval on a trusted server
re-uses `pending_approval`. A tool also denied by operator config is skipped
(approval could not make it callable). Remediation for `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."

### 4.4 Reactive triggers (discoverability)

Option C's only weakness is the agent not knowing the flag exists. Closed two ways:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,27 @@ Tool description gains exactly one sentence (FR-014):
"tools": [ /* callable results — UNCHANGED order/shape (FR-002, FR-006) */ ],
"disabled": [ // NEW, after callable, ≤ min(limit,10)
{ "name": "delete_repo", "server": "github",
"description": "Delete a repository", "status": "disabled_by_config" }
"description": "Delete a repository", "status": "disabled_by_config" },
// Quarantined-tool discovery pass: name-only, description/schema withheld
// (TPA defense); `name` is the "<server>:<tool>" key. Prepended ahead of
// index-derived locked entries so the min(limit,10) cap can't drop them.
{ "name": "github:rotate_keys", "server": "github",
"status": "server_quarantined" }
],
"remediation": { // NEW, once, only present statuses
"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."
"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.",
"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."
}
}
```

`status` enum (FR-004): `disabled_by_config`, `disabled_by_user`,
`pending_approval`, `server_disabled`, `disabled_unknown`, `server_quarantined`.
The first five are classifier-assigned to index-discoverable tools; the last is
assigned by the quarantined-tool discovery pass (also re-using `pending_approval`
for tool-level pending/changed approvals), which surfaces name-only entries from
authoritative quarantine state because quarantined tools are not in the index.

### Response delta (0 callable results, ≥1 locked match, flag OFF) — FR-009

A one-line note added to the existing result text:
Expand Down
31 changes: 25 additions & 6 deletions specs/049-agent-discoverable-disabled-tools/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,23 @@ callable one carries none.
fields, no reordering).
- **FR-003**: Each returned locked tool MUST carry a lean shape: name, owning
server, the existing one-line description, and a single `status` value.
- **FR-004**: `status` MUST be one of exactly five values —
Exception: a `server_quarantined` entry (and any tool surfaced by the
quarantined-tool discovery pass) withholds the description and schema, so it
carries name + owning server + `status` only.
- **FR-004**: `status` MUST be one of exactly six values —
`disabled_by_config`, `disabled_by_user`, `pending_approval`,
`server_disabled`, `disabled_unknown` — assigned by fixed first-match
precedence in that order (server-off, then config, then user-disabled, then
pending approval, else unknown).
`server_disabled`, `disabled_unknown`, `server_quarantined`. The first five
are assigned to index-discoverable tools by fixed first-match precedence in
that order (server-off, then config, then user-disabled, then pending
approval, else unknown). `server_quarantined` is assigned separately by the
quarantined-tool discovery pass (not the classifier), because quarantined
tools are deliberately absent from the search index (see Assumptions): it
covers a tool on a quarantined server (status `server_quarantined`) and a
tool-level pending/changed approval on a trusted server (status
`pending_approval`), with description and schema withheld to avoid exposing a
Tool Poisoning Attack payload. A tool also denied by operator config
(`enabled_tools`/`disabled_tools`) is skipped by this pass rather than
surfaced, since approval could not make it callable.
- **FR-005**: The response MUST include a single remediation map emitted once,
containing only the keys for statuses actually present in the response; no
per-tool remediation text.
Expand Down Expand Up @@ -154,7 +166,8 @@ callable one carries none.
### Key Entities *(include if feature involves data)*

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

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