Skip to content

[BUG] disallowedTools in settings.json does not trim tool schemas, but --disallowedTools does #90059

Description

@N4darae

Preflight Checklist

  • I have searched existing issues
  • This is a single bug report
  • 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions