FIELD NOTES / 4 MIN READ

Claude Agent SDK Subagents: Tools Field Restricts, Permission Mode Doesn't Always

If you give a Claude Agent SDK subagent a narrow tools list, that list is a real, enforced ceiling on what it can call. If you also set a tighter permissionMode on that subagent, it may be silently ignored whenever the parent session runs bypassPermissions, acceptEdits, or auto. Builders who only check the tools field and assume permissionMode works the same way end up with subagents that have full autonomous access they never intended to grant.

AI-assisted editorial: researched and drafted with AI using the primary sources linked below. Examples are illustrative; they are not measured product results. Editorial policy.

Key takeaways

  • The tools field on an AgentDefinition is a hard allowlist: list fewer tools and the subagent literally cannot call the rest, regardless of permission mode.
  • A subagent's own permissionMode setting is overridden whenever the parent session runs bypassPermissions, acceptEdits, or auto; it only takes effect when the parent is in default, dontAsk, or plan mode.
  • Use tools and disallowedTools to set the real capability ceiling for a subagent; don't rely on permissionMode to tighten a subagent under a loosely-permissioned parent.

Two Settings, Two Different Guarantees

The Claude Agent SDK gives you two separate levers for controlling a subagent: a tools field on its AgentDefinition, and a permissionMode field on the same object. They look like they do the same job — restrict what the subagent can do — but they behave differently once a parent session is running in a loose mode. One of them is a hard boundary. The other is a request that the runtime may simply ignore.

This matters because a common pattern is to let the main agent run fast and mostly unattended, usually under acceptEdits or bypassPermissions, while spawning a narrower subagent for a sensitive subtask and trying to lock that subagent down tighter than the parent. Whether that actually works depends on which of the two fields you used.

What the tools Field Actually Locks

The AgentDefinition's tools field is documented as an allowlist of what the subagent can use at all. The SDK's own description is explicit about the default and the override: if you omit it, the subagent inherits every tool available to it; if you list specific tools, it can only use those.

Sources: Agent SDK reference - Python - Claude Code Docs, Subagents in the SDK - Claude Code Docs.

What permissionMode Doesn't Guarantee

permissionMode is a different kind of setting. It controls whether calls to tools the subagent already has access to get auto-approved, denied, or sent to a callback — not which tools exist for it. And its inheritance rules are asymmetric. The API documentation states the rule for the common case plainly: when the parent session uses bypassPermissions, every subagent inherits that mode and it cannot be overridden by anything set on the AgentDefinition.

The Claude Code-oriented documentation describes the same mechanism with slightly more nuance: a subagent runs in the parent session's permission mode unless you set permissionMode on its AgentDefinition and the parent session is in default, dontAsk, or plan mode. Even in that narrower window where an override is possible, the subagent is never pushed into bypassPermissions unless the parent itself is already there, and an auto override only applies if auto mode is actually available to that subagent.

Put together, these two documents describe one rule with two levels of detail: a tighter permissionMode on a subagent is advisory, and the parent's mode wins whenever the parent is in bypassPermissions, acceptEdits, or auto. The subagent's own setting only has a chance to apply when the parent is already running a conservative mode — exactly the situation where you'd need it least.

Sources: Configure permissions - Claude API Docs, Configure permissions - Claude Code Docs.

Worked Example: A Code-Reviewer Subagent Under a Loose Parent

Illustrative scenario. A parent agent runs under permissionMode="bypassPermissions" so it can refactor a codebase without stopping for approval on every edit. It spawns a code-reviewer subagent with tools=["Read", "Grep", "Glob"] to check the work. Because tools is a hard allowlist, that subagent genuinely cannot call Edit, Write, or Bash — the fence holds no matter what mode is active, since those tool names simply aren't in its set.

Now suppose the same team adds a test-runner subagent that needs Bash to execute tests, and tries to make it safer by also setting permissionMode="default" on that AgentDefinition, expecting Bash calls to prompt for approval. Under the documented inheritance rule, that setting doesn't take effect: the parent is in bypassPermissions, so the subagent runs in bypassPermissions too, and its Bash calls execute without any prompt. The permissionMode field on the AgentDefinition did nothing in this configuration — the only setting with teeth was the tools list.

Sources: Configure permissions - Claude API Docs, Configure permissions - Claude Code Docs.

The Boundary That Holds Even Under bypassPermissions

There is one mechanism documented to survive bypassPermissions: deny rules. The permission evaluation order checks deny rules — built from disallowedTools and settings.json — before it applies the permission mode, and a matching deny rule blocks the tool even in bypassPermissions.

That gives a concrete, checkable rule for builders: if a capability must never be available to a subagent regardless of what mode the parent is running in, put it in that subagent's disallowedTools (or a project-level deny rule), not in a hopeful permissionMode override. disallowedTools also accepts MCP server-level patterns, so an entire connected tool server can be removed from a subagent's set in one entry rather than listing every tool name.

Sources: Configure permissions - Claude API Docs.

The Decision for Your Subagent Config

Treat tools and disallowedTools as the actual capability boundary for a subagent — these determine what exists for it to call, independent of permission mode. Treat permissionMode on an AgentDefinition as an optimization that only applies when the parent session is already conservative (default, dontAsk, or plan); don't design a safety story around it when the parent runs bypassPermissions, acceptEdits, or auto.

If a subagent needs to be meaningfully more locked down than its parent, the reliable path is narrowing its tools list and adding disallowedTools entries for anything destructive, not setting a tighter permissionMode and hoping it sticks. If the parent genuinely needs bypassPermissions for its own work, consider whether the sensitive subtask should run as a separate top-level session in a stricter mode instead of as a subagent that will inherit the parent's bypass.

  • Check which parent mode you're running before trusting a subagent's permissionMode: it only has a chance to apply under default, dontAsk, or plan.
  • Use tools (allowlist) or disallowedTools (denylist) to set what a subagent can call at all — these hold regardless of mode.
  • For anything that must never run, put it in disallowedTools; it's checked before the permission mode is applied, so it survives bypassPermissions.
  • If a subtask needs real isolation from a bypassPermissions parent, run it as a separate session rather than a subagent.

Sources: Configure permissions - Claude API Docs, Configure permissions - Claude Code Docs.

Primary sources

Sources checked 2026-10-15. Standards and product documentation can change; follow the linked version when implementing.

  1. Subagents in the SDK - Claude Code Docs — Anthropic
  2. Agent SDK reference - Python - Claude Code Docs — Anthropic
  3. Configure permissions - Claude API Docs — Anthropic
  4. Configure permissions - Claude Code Docs — Anthropic

BotBento is in development. Suggest a correction.