Skip to content

D123 — Expose read-only governance tools to descriptor-bound agents

Status: implemented (2026-08-04).

Context. The default agent tools profile (D65) hid validate, lint, gate_check, and kb_status from tools/list. They stayed registered and directly callable by name (ToolAdvanced filters tools/list, not dispatch), which is enough for a stdio/TUI agent that can invoke any known tool name. It is not enough for a descriptor-bound MCP host such as Codex, which can only invoke tools tools/list advertises: the documented agent loop (docs/loop.md §Validate and lint, docs/use-cases.md) requires all four, but a descriptor-bound agent had no way to call them.

Decision. Remove validate, lint, gate_check, and kb_status from advancedToolNames (internal/mcpserver/visibility.go): they join the agent profile's core set, unchanged in every other respect — same ReadOnly: true classification (readonly.go), same resourceWhole resource class and whole-KB RBAC (policy.go), same handlers. Everything else stays advanced: commit_gate, contradiction/conflict mutation, index maintenance, provisioning, secrets, artifact enumeration/removal, and PR finalization remain operator-only, because they are either mutating or genuinely niche — unlike these four, no documented agent-loop step depends on a descriptor-bound host being able to invoke them without an operator switching the server to profile: full.

Measured cost. Serialized tools/list under the agent profile against the demo KB: 28 tools / 21814 bytes (~5453 tokens, bytes/4 estimate per D65) before, 32 tools / 23324 bytes (~5831 tokens) after — a ~378-token, ~7% increase for the four added descriptors, well short of erasing D65's ~2.9k-token reduction from the pre-D65 baseline.

Rationale. No third profile and no umbrella governance tool: a third profile duplicates the two-value contract admin/operators already reason about (agent/full) without resolving the descriptor-bound gap for the existing default, and an umbrella tool (kb_admin(action=...), already discarded once by D65) trades four typed, independently discoverable schemas for one opaque one. Reclassification also does not touch RBAC: ToolAdvanced gates tools/list visibility only, authorizeTool and the resource-class tables are untouched, so a caller who could not call these four before still cannot call them after — only what a compliant descriptor-bound client is told exists has changed.