Inline --agents 'tools: []' no longer denies tools in headless -p mode (2.1.214); no flag combination behaviorally denies all

Status Closed — not planned
Reported on v2.1.214
Maintainer reply None cached
Activity 3 comments · opened Jul 19, 2026 · closed Aug 24, 2026

Environment

  • Claude Code 2.1.214, Linux, OAuth auth, headless -p mode with an inline agent as the top-level session (--agent <name>, not the Agent tool).

Command shape

claude -p "<msg>" --model claude-sonnet-5 \
  --agents '{"probe":{"description":"...","prompt":"<system>","tools":[]}}' \
  --agent probe --output-format json --strict-mcp-config --disable-slash-commands

Expected

An empty tools: [] allowlist denies every tool (verified behavior on ~2.1.206, 2026-07-10: a probe agent could not read files and replied NO_TOOL in one turn). The 2.1.208 changelog changed empty/unresolvable tools lists to a refuse-to-launch error, but scoped to "the Agent tool" — the --agent main-thread path appears uncovered.

Observed on 2.1.214 (all with the command shape above)

  1. A probe agent instructed to invoke its most harmless tool completed a real TodoWrite round-trip (num_turns: 2 in the result JSON) under tools: [].
  2. Behavior is nondeterministic: sibling runs of the same probe returned NO_TOOL (1 turn), a fabricated tool-call transcript (1 turn), or a real 2-turn attempt.
  3. Adding --disallowedTools "*" and --tools "" did not stop tool-call attempts (2-turn round-trips, sometimes empty final replies). An enumerated --disallowedTools list additionally left ToolSearch callable.
  4. Ground truth was ambiguous in the safe direction only: in our side-effect probes (agent asked to Write a token file / Read a secret token file, including with natural task framing) no file was ever read or written — but nothing we could pass makes the CLI refuse or cleanly strip tool use for a main-thread inline agent, and reply text/turn counts are unreliable signals either way.

Ask

  1. What are the intended semantics of an empty tools array for --agent main-thread sessions on current releases — deny-all, inherit-all, or refuse-to-launch?
  2. Parity request: apply the 2.1.208 refuse-to-launch behavior (or a documented deny-all) to the --agent main-thread path, so headless dispatchers can rely on a contract instead of behavioral probing.

Context: we dispatch single-turn no-tool agents programmatically and currently verify isolation per session with a token-leak probe because no flag combination is contractual.

🤖 Generated with Claude Code

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗