[FEATURE] Expose the resolved MCP tool catalogue to plugins: add `inputSchema`/`outputSchema` to `$.tool.list()`
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
Use case
We build a security product that inventories and governs MCP servers on managed developer
endpoints. For each configured MCP server we need its declared tool surface — names,
descriptions, inputSchema — to inventory what an endpoint can reach, and to validatetools/call arguments against the schema the server declared.
Scope: we are not asking for anything not already resident in the Claude Code process, and not
asking to read user conversation content.
Why hooks are not sufficient
Hooks fire on tools/call, so we see only the tools someone happened to invoke, by name, with no
schema. A server can expose forty tools and we see the three that were used.
Proposed Solution
Asks, in priority order
- Add
inputSchemaandoutputSchemato$.tool.list(). The data is already resident; this
is a field addition, and the one change that unblocks schema validation.
- Ship
tengu_plugin_hooks_modules— or provide any supported way for a plugin to read the
resolved MCP tool catalogue. The flag is not in the served payload at all, so the seam is
unreachable, not merely disabled.
- Lift or make configurable the 300-char description truncation, or return the untruncated
description on a catalogue-oriented call. Truncation that is fine for prompt-building is lossy
for inventory.
- Provide a non-interactive path. Inventory collection is not an interactive activity;
requiring a bound interactive session rules out the scanning context entirely.
- Lower priority: a stable, documented way to read the catalogue without parsing the private
transcript format.
Alternative Solutions
What we do today, and why we would rather not
Our fallback parses the session transcript (~/.claude/projects/<slug>/<session>.jsonl), whosedeferred_tools_delta attachment lists every MCP tool name on connect — complete, including
never-invoked tools, but names only, with . irreversibly sanitized to _. That is
unsatisfactory for both sides: it is a private format with no stability contract (its shape
changed between 2.1.246 and 2.1.247), and it means a security agent reads files containing user
conversation content just to obtain a tool list. A supported catalogue API would let us stop
touching those files — which we think is the strongest reason to do this.
Priority
High - Significant impact on productivity
Feature Category
MCP server integration
Use Case Example
_No response_
Additional Context
What we measured (Claude Code 2.1.247)
- A plugin's
hooks/hooks.jsonmay declaremodules: ["./register.js"]; Claude Code loads it
into a node:vm and passes a capability object of ~32 ops, including $.tool.list().
$.tool.list()returns exactly{name, description, mcp}— **noinputSchema, no
outputSchema** — and description is hard-truncated at 300 characters (a 616-char
description came back cut mid-word, no ellipsis).
- The seam is gated on the GrowthBook flag
tengu_plugin_hooks_modules, which is **absent from
the served feature payload entirely** (556 features fetched) — not off for this user, but not
shipped to this client population.
$.tool.describeis referenced in the bundle but isundefinedat runtime.$.tool.list()refuses to run headlessly: *"not available in this mode: no interactive session
is bound in this process (a -p run or the SDK)"*.
- The schema is in the process: Claude Code's own
/mcppanel renders bothDescription:
and Parameters: per tool. So omitting inputSchema from $.tool.list() is an API-shape
choice, not a data-availability limit.
Happy to test a build behind a flag.
Appendix — verbatim measurements (Claude Code 2.1.247)
| Observation | Detail |
|---|---|
| Plugin module seam | hooks/hooks.json → modules: ["./register.js"], loaded into a node:vm, called with a capability object of ~32 ops including $.tool.list() |
| $.tool.list() shape | {name, description, mcp} — no inputSchema, no outputSchema |
| Description truncation | Hard cut at 300 chars; a 616-char description returned cut mid-word, no ellipsis |
| Gating flag | tengu_plugin_hooks_modules — absent from the served feature payload (556 features fetched); not present-and-false |
| $.tool.describe | Referenced in the bundle, undefined at runtime |
| Headless behaviour | $.tool.list(): "not available in this mode: no interactive session is bound in this process (a -p run or the SDK)" |
| Data is resident | /mcp panel renders Description: and Parameters: per tool |
| Transcript fallback | ~/.claude/projects/<slug>/<session>.jsonl, deferred_tools_delta attachment, written on connect; complete tool-name list incl. never-invoked tools; names only; . → _, irreversible; shape changed between 2.1.246 and 2.1.247 |