anthropics / anthropics/claude-code

[FEATURE] Expose the resolved MCP tool catalogue to plugins: add `inputSchema`/`outputSchema` to `$.tool.list()`

Open
#90,131 0 comments 0 reactions 0 assignees View on GitHub
area:mcp area:plugins enhancement
Dominant language
Python
Stars
145k
Forks
23.1k
PR merge metrics
PR metrics pending

Description

### Preflight Checklist

- [x] I have searched [existing requests](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20label%3Aenhancement) 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 validate
`tools/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

1. **Add `inputSchema` and `outputSchema` to `$.tool.list()`.** The data is already resident; this
is a field addition, and the one change that unblocks schema validation.
2. **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.
3. **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.
4. **Provide a non-interactive path.** Inventory collection is not an interactive activity;
requiring a bound interactive session rules out the scanning context entirely.
5. 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//.jsonl`), whose
`deferred_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.json` may declare `modules: ["./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}` — **no `inputSchema`, 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.describe` is referenced in the bundle but is `undefined` at 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 `/mcp` panel renders both `Description:`
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//.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 |

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with hooks/hooks.json and its modules entry, then examine the plugin capability object around $.tool.list() and the reported interactive-session restriction. Done means plugins can access the resolved MCP catalogue with inputSchema and outputSchema through a supported path, with the requested truncation and non-interactive behavior addressed or explicitly scoped.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
api, devtools, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.