[BUG] disallowedTools not inherited by subagents spawned via Agent tool
When a named agent has disallowedTools configured, the restriction is NOT applied to subagents spawned via the Agent tool. Subagents can freely call the disallowed tool — including the exact command pattern the restriction was supposed to block.
Reproduction
claude \
--agents '{"test":{"description":"Test","prompt":"Test agent.","disallowedTools":["Bash(curl *)"]}}' \
--agent test --print --dangerously-skip-permissions \
'Spawn a subagent with the Agent tool. Tell it to run: curl -s -o /dev/null -w "%{http_code}" https://httpbin.org/get'
Expected: Subagent inherits parent's disallowedTools. The curl call is blocked in the subagent too.
Actual: Subagent runs curl successfully and returns HTTP 200. The disallowedTools restriction only applies to the parent session.
You can verify the parent lacks Bash by asking it to list its tools — Bash is absent from the 66 tools listed (that's a separate issue, see below). But even if the scoped pattern were parsed correctly and only blocked curl calls, the subagent would still bypass it entirely.
Why this matters
The model quickly learns to work around tool restrictions by delegating to subagents. After a single failed Bash call in the parent, the agent exclusively uses subagents for ALL subsequent Bash work — making disallowedTools purely cosmetic.
Related issues
Whether scoped patterns like Bash(curl *) should strip the entire Bash tool or only block curl-pattern calls is a separate question (see #6527, #19501). This issue is about the restriction not propagating to subagents regardless of how it's parsed.
Environment
- Version: v2.1.209
- Terminal: Non-interactive (
--print) - OS: Linux (Fedora 44)
3 Comments
JSONL output from the reproduction command above:
<details>
<summary>output.jsonl</summary>
</details>
Thanks for the detailed repro. Confirmed the behavior on 2.1.233 (Linux): with
disallowedTools: ["Bash(curl *)"]on the agent, a subagent spawned via the Agent tool runs curl and gets 200 — same withdisallowedTools: ["Bash"].This is how it's currently designed, though we agree it's confusing:
disallowedToolsin an agent definition only shapes that agent's own tool list (which is why the parent loses Bash) — it isn't a permission rule and isn't inherited by subagents, which get their tool set independently. The docs describe the field as removing tools "from inherited or specified list" for that agent: https://code.claude.com/docs/en/sub-agentsFor an enforced restriction that does apply to subagents, use permission deny rules —
permissions.denyin settings or the session-level--disallowedToolsflag. I verified on 2.1.233 thatclaude --disallowedTools 'Bash(curl *)' ...blocks the subagent's curl (even with--dangerously-skip-permissions). You can also prevent spawning entirely by addingAgentto the agent'sdisallowedTools, or restrict which types it can spawn withAgent(agent_type).We're looking at clarifying the docs and considering whether the agent-level field should cascade to spawned subagents. Leaving this open to track that.
🤖 Generated with Claude Code
Still reproducible on v2.1.241, but only through one of the two ways to deny a
tool — which may be useful for whoever picks this up.
I ran both paths on the same machine, same version, minutes apart.
1.
--disallowed-toolson the command line — subagent DOES obeyThe
Agentcall happens, and the model reports back:2.
disallowedToolsinside--agents— subagent does NOT obeyThe transcript shows the denied tool executing inside the delegated agent:
Reading
On 2.1.241 the two paths diverge, which looks like the propagation landed on the
CLI flag and not on the per-agent
disallowedToolsin--agents. If that isintentional, it is worth documenting, since both read like "this tool is denied
for this run".
A small note on the general shape of the problem: the non-null
parent_tool_use_idis a reliable marker for "this call was born inside asubagent", so it works as an after-the-fact audit for anyone who needs a
genuinely confined run today.
Tested on: Claude Code v2.1.241, macOS, headless (
-p/--print).