[BUG] disallowedTools in settings.json does not trim tool schemas, but --disallowedTools does
Preflight Checklist
- [x] I have searched existing issues
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
disallowedTools in settings.json blocks the call but still sends the tool's schema, so it does not reduce context. The identically-named CLI flag --disallowedTools does remove the schema, and so does permissions.deny.
For Workflow the gap is 7,900 input tokens on every request.
--model opus, first API request of a headless run, summing input_tokens + cache_creation_input_tokens + cache_read_input_tokens. All rows taken back to back in one unchanged environment:
| config | input tokens | reclaimed |
|---|---|---|
| baseline | 23,052 | - |
| permissions.deny: ["Workflow"] | 15,152 | 7,900 |
| --disallowedTools Workflow | 15,152 | 7,900 |
| disallowedTools: ["Workflow"] (settings.json) | 23,052 | 0 |
The reclaimed column is the reproducible figure; absolute totals also carry the local CLAUDE.md, memory files and MCP definitions.
What Should Happen?
disallowedTools in settings.json should keep the denied tool's schema out of context, matching --disallowedTools and permissions.deny.
If the divergence is deliberate, documenting it would help. The shared name reads as a promise that the flag and the settings key do the same thing, and the settings key is the one an organization deploys fleet-wide.
Steps to Reproduce
m() { claude -p "ok" --model opus --output-format stream-json --verbose "$@" \
| python3 -c 'import sys,json
for l in sys.stdin:
try: d=json.loads(l)
except: continue
if d.get("type")=="assistant":
u=d["message"]["usage"]
print(u.get("input_tokens",0)+u.get("cache_creation_input_tokens",0)+u.get("cache_read_input_tokens",0)); break'; }
m # baseline
m --settings '{"disallowedTools":["Workflow"]}' # unchanged
m --disallowedTools Workflow # -7,900
m --settings '{"permissions":{"deny":["Workflow"]}}' # -7,900
Rows 2 and 3 deny the same tool by the same name and produce different prompt sizes.
Claude Model
Opus
Is this a regression?
I don't know
Claude Code Version
2.1.247
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Non-interactive/CI environment
Additional Information
Workflow is opt-in by design: its own tool description requires explicit user opt-in before it may be called. Its schema is the largest single item in the built-in tool block, so users who never invoke it still pay 7,900 tokens per request unless they find permissions.deny or the CLI flag.
Tool-search deferral already solves this class of problem and already applies to several built-ins (Monitor, WebFetch, Cron*, EnterPlanMode cost 0). Its scope looks MCP-only by design: CHANGELOG.md:4553 describes it as deferring "MCP tool descriptions" past a 10%-of-context threshold, and the toolSearchEnabled setting description likewise says "MCP tool schemas". Extending deferral to eagerly-loaded opt-in built-ins would make the default lean without per-org settings work.