You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(discovery): surface quarantined tools in retrieve_tools (locked, name-only) (#778)
Quarantined tools — both server-level quarantine and tool-level
pending/changed approvals — are intentionally absent from the BM25 search
index so their untrusted descriptions cannot expose a Tool Poisoning Attack
(TPA) payload to the agent. As a side effect, retrieve_tools could only answer
"no such tool" for a capability a quarantined server provides, even though the
correct remediation is "ask the user to approve it".
Add an opt-in second pass to retrieve_tools(include_disabled=true) that
enumerates quarantined tools from authoritative state and returns NAME-ONLY
locked entries (description and schema withheld) with a status + remediation:
- server-level quarantine -> new status `server_quarantined`
- tool-level pending/changed -> existing `pending_approval`
The pass is fully self-contained — it does NOT alter the shared callability or
classification helpers, so visible-tool counts and other consumers are
unchanged. Specifically it:
- matches the query against the tool NAME only (quarantined tools aren't in
the index, so they can't be BM25-ranked); short keywords (>=2 chars, e.g.
"ui"/"qa") are retained;
- applies the same agent-scope + profile filtering as the callable path, via
a shared `serverDiscoverable` helper (de-duplicates what were three inline
copies down to one for the discovery surface);
- dedups against tools the index loop already handled (a `seen` set), so the
brief post-quarantine reindex window can't list a tool as both callable and
locked, nor double-count it in the zero-result nudge;
- skips tools also denied by operator config (enabled_tools/disabled_tools),
which approval cannot unlock — so the agent isn't sent down a dead-end
remediation;
- prepends its matches so the shared min(limit,10) cap can't truncate them in
favor of index hits.
Quarantined tools remain non-callable: the call path already blocks
server-quarantine and pending/changed before execution (handleQuarantinedToolCall),
so no change to isToolCallable is needed — discovery only makes them VISIBLE,
never callable.
Tests: pending tool surfaced by name with withheld description + remediation;
server-level branch (tool-names source injected); query-scoped exclusion;
config-denied skipped; dedup against seen; short-keyword tokens; zero-result
nudge; quarantined tool still blocked at the call path; remediation present.
Co-authored-by: Roman Chernyak <rchernyak@halocollar.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Dumbris <a.dumbris@gmail.com>
0 commit comments