Feature request: exclusive-allowlist mode for --allowedTools (currently additive on top of built-in default safe-list)
Problem
--allowedTools is documented and intuitive as a whitelist, but at runtime it
behaves additively — it appends to the Claude CLI's default safe-list of
built-in tools rather than replacing it. The only way to truly restrict a
subprocess to a specific tool set today is to enumerate everything else in--disallowedTools, which means tracking every new built-in tool Claude Code
ships.
Reproduction
Spawn a Claude Code subprocess with:
claude --print --output-format stream-json \
--allowedTools Write \
--disallowedTools Bash,PowerShell,Edit,MultiEdit,NotebookEdit,Glob,Grep,Task,ToolSearch,WebFetch,WebSearch \
--strict-mcp-config --mcp-config <per-run-config>.json \
--model claude-opus-4-7 \
...
The intent is "this subprocess should only call Write." In theagent_session_init event, the subprocess actually has 20 built-in tools
available:
AskUserQuestion, CronCreate, CronDelete, CronList,
EnterPlanMode, EnterWorktree, ExitPlanMode, ExitWorktree,
ListMcpResourcesTool, Monitor, PushNotification, Read,
ReadMcpResourceTool, RemoteTrigger, ScheduleWakeup,
Skill, TaskOutput, TaskStop, TodoWrite, Write
In our case the agent auto-invoked Skill('init') (the built-in /init
skill that generates CLAUDE.md from codebase analysis), then re-read its
own output file twice, ballooning a 130 s / \$0.85 step into 255 s / \$1.94
with 9 turns instead of the expected 3.
Use case context
We run Claude Code as a non-interactive subprocess from a Python orchestrator
("agent step in a multi-step pipeline"). Each step has a narrow contract —
e.g., "read inline-injected inputs, emit two files, exit." We've already done:
- Fake
HOMEwith only.credentials.jsoncopied (no~/.claude.json, no
user skills, no CLAUDE.md memory)
- Scrubbed
CLAUDE_*andCLAUDECODEenv vars --strict-mcp-configwith a per-run config listing only the MCP servers
the step needs
--allowedToolslisting only what the step's prompt requires
User-installed state (skills, MCP servers, settings) is correctly isolated.
But built-in tools ship inside the claude binary itself — filesystem
isolation can't strip them, so they leak into every subprocess.
Proposed solution
A flag like:
--strict-tools # symmetric to --strict-mcp-config
Semantics: --allowedTools becomes an exclusive whitelist. Built-in tools
not in --allowedTools are unavailable (same effect as listing them in--disallowedTools), without callers having to enumerate them.
Alternatively, a complementary flag:
--default-tools none # disable all built-in tools by default
So callers can opt out of the default safe-list once instead of subtracting
each tool by name.
Why this matters
Without it, every Claude Code release that adds a built-in tool potentially
breaks downstream agentic harnesses. We've already hit this twice in the past
month — Skill and ScheduleWakeup showed up in subprocesses that hadn't
asked for them. The maintenance burden falls on each caller to keep their--disallowedTools list in sync with the CLI's tool catalogue across versions.
A strict-mode flag would let callers express the constraint they actually
mean ("only these tools, please") and stay forward-compatible.
Workaround (today)
Enumerate every current built-in in --disallowedTools:
Bash,PowerShell,Edit,MultiEdit,NotebookEdit,Glob,Grep,Task,ToolSearch,
WebFetch,WebSearch,Read,Skill,TodoWrite,AskUserQuestion,EnterPlanMode,
ExitPlanMode,EnterWorktree,ExitWorktree,ScheduleWakeup,CronCreate,
CronDelete,CronList,Monitor,PushNotification,RemoteTrigger,TaskOutput,
TaskStop,ListMcpResourcesTool,ReadMcpResourceTool
This works but is fragile — every new built-in is silently opt-in until it
breaks something.
Related
--strict-mcp-configalready exists and solves the exact same problem for
MCP servers; the same pattern for built-in tools would be a natural
extension.
Thanks for considering!
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗