UnattendedClaude Code Guides

Claude Code Subagent allowed-tools: Syntax, Examples, Fixes

By Aoba Works · Published · Sources checked · 17 min read

How this guide was made: drafted with AI assistance from the official sources linked below, then passed an automated editorial check before publishing. Our process

Claude Code subagents don't have an allowed-tools field. The key you want in a subagent's .md frontmatter is tools. It's an allowlist, written as a comma-separated string like tools: Read, Grep, Glob or as a YAML list. disallowedTools is the matching denylist. allowed-tools belongs to Skills, and it does something else: it pre-approves tools for one turn and restricts nothing. If you leave tools out, the subagent "Inherits every tool available to subagents if omitted."

Which option is right for you?

The best place to start depends on whether you want a safe default fast, a dependable daily setup, or the lowest token cost.

Which option is right for you?
Your situationWhat to pick firstFirst page to open
Beginner, first custom subagenttools: Read, Grep, Glob (read-only). Nothing can breakCreate custom subagents
Daily work, subagent edits code and runs teststools: Read, Edit, Bash, Grep, Glob plus a permissions.deny rule for risky Bash commandsConfigure permissions
Subagent needs one MCP server (GitHub, Slack)tools: Read, mcp__github__*, or scope the server with mcpServersSub-agents: MCP section
You want everything except writesdisallowedTools: Write, Edit and keep inheritingSub-agents: restrict tools
Budget: keep context and tokens smallInline MCP servers in the subagent so the parent never loads their tool descriptionsSub-agents: scope MCP servers
Unattended or cron runsExplicit tools list plus a PreToolUse hook as a second checkHooks reference

The reason for each row: an explicit read-only list can't fail in a dangerous way. A Bash deny rule blocks particular commands without taking Bash away. A server wildcard keeps the list short. A denylist is the least work when you only fear one or two tools. Inline MCP keeps tool definitions out of the main context. Hooks still run when a frontmatter list turns out wrong.

What is the allowed-tools field in Claude Code subagents?

In Claude Code subagents, the "allowed-tools" job is done by the tools frontmatter key, an allowlist that limits which tools that subagent can call. Many people search for allowed-tools because they've seen it in Skills or the Agent SDK, so the three names are worth separating before you write any YAML.

What is the allowed-tools field in Claude Code subagents?
Where you write itKey nameWhat it does
Subagent file (.claude/agents/*.md)toolsAllowlist: the subagent can use only these tools
Subagent filedisallowedToolsDenylist: removes tools from the inherited or listed pool
Skill file (SKILL.md)allowed-toolsPre-approves listed tools for the turn that invokes the skill. Other tools stay callable
Agent SDK optionsallowedTools / allowed_toolsAuto-approves tools before your permission callback runs

The Skills documentation says the Skills allowed-tools grant clears when you send your next message, and that it does not restrict which tools are available. The Agent SDK permissions page treats allowedTools as approval too. Put allowed-tools: in a subagent file and you get neither behavior: it isn't a supported subagent field, so the subagent inherits everything.

Here is a minimal, valid subagent. Save it as .claude/agents/code-reviewer.md (example):

---
name: code-reviewer
description: Reviews diffs for bugs and style issues. Use after code changes.
tools: Read, Grep, Glob
model: sonnet
---
You are a code reviewer. Read the changed files, report problems with file and line, and never modify files.

Some official limits are worth knowing (as of October 2026, sub-agents docs). name can be up to 256 characters and can't contain :. Claude Code warns at startup when the combined descriptions of your custom subagents go over 15,000 tokens. By default, 20 subagents running at once is the ceiling, and the next spawn fails with Concurrent subagent limit reached.

How do you write the allowed-tools syntax (wildcards, lists, MCP tool names)?

Claude Code's subagent tools field takes exact tool names, either as a comma-separated string or a YAML list. The documented wildcards are for MCP servers, such as mcp__github__*.

Steps to add or change a tool list

  1. Open (or create) the file: .claude/agents/<name>.md for one project, or ~/.claude/agents/<name>.md for all your projects. A subagent with the same name in the project folder wins over the one in your home folder. The priority table puts managed settings first, then the --agents CLI flag, then project, then user, then plugins.
  2. Add a tools: line between the --- markers. Spell tool names with their exact capitalization (Read, not read).
  3. Save the file, then start a new session or ask Claude to use the subagent by name.
  4. On current versions, typing /agents only reminds you to ask Claude or edit .claude/agents/ and ~/.claude/agents/ directly. The interactive wizard with Running and Library tabs exists only on v2.1.197 and earlier (sub-agents docs).

Syntax patterns from the official docs

String form:

tools: Read, Grep, Glob, Bash

YAML list form (same meaning):

tools:
  - Read
  - Grep
  - Glob
  - Bash

Every tool from one MCP server, plus Read:

tools: Read, mcp__github__*

Everything inherited except one MCP server:

disallowedTools: mcp__github

No MCP tools at all, built-ins kept:

disallowedTools: mcp__*

Those three MCP patterns come straight from the sub-agents table. One MCP tool is named mcp__<server>__<tool>, the same format hook matchers use, for example mcp__memory__create_entities.

What the docs don't promise

Defining the list from the CLI

For one-off or scripted runs, the --agents flag takes JSON, with tools as an array (sub-agents docs):

claude --agents '{
  "code-reviewer": {
    "description": "Expert code reviewer. Use proactively after code changes.",
    "prompt": "You are a senior code reviewer.",
    "tools": ["Read", "Grep", "Glob", "Bash"],
    "model": "sonnet"
  }
}'

In non-interactive mode you can pass a file path instead, such as claude -p --agents ./agents.json "Review my changes". Interactive sessions refuse a file path (sub-agents docs).

Inherit vs restrict: should a subagent inherit all tools or list them explicitly?

For a Claude Code subagent that runs without you watching, list its tools explicitly. Inheriting is fine for interactive helpers you supervise. If you leave tools out, the subagent picks up every built-in and MCP tool the main conversation has, after a couple of filters. Listing tools gives you predictable behavior, and in exchange you have to update the list when the job changes.

Inherit vs restrict: should a subagent inherit all tools or list them explicitly?
Omit tools (inherit)tools allowlistdisallowedTools denylist
New MCP servers you add laterPicked up automaticallyNot available until you add themPicked up automatically
Risk of surprise side effectsHighestLowestMedium
MaintenanceNoneUpdate when the job changesUpdate when a new risky tool appears
Good forAd-hoc helpers in an interactive sessionCron jobs, reviewers, anything unattended"Everything but writes" style agents
Failure modeDoes too muchMissing tool, or launch refused if nothing resolvesForgetting a new dangerous tool

When both fields are set: "If both are set, disallowedTools is applied first, then tools is resolved against the remaining pool." A tool that appears in both lists ends up removed.

Two inheritance details tend to catch people:

A practical rule: if the subagent can run when you're not at the keyboard, such as from cron, CI, or a background session, write out its tools. If you start it yourself and watch the output, inheriting is acceptable.

Which tools can be assigned to a Claude Code subagent?

A Claude Code subagent can use any built-in or connected MCP tool, except a few that are always withheld. Built-in names come from the tools reference, which says they "are the exact strings you use in permission rules, subagent tool lists, and hook matchers."

Tools a background subagent keeps

Subagents run in the background by default. A background subagent keeps every MCP tool but only these built-in tools (as of October 2026, sub-agents docs):

Tools a background subagent keeps
GroupTool names
Read and searchRead, Grep, Glob, LSP
Change filesEdit, Write, NotebookEdit
Run commandsBash, PowerShell, Monitor, TaskStop
WebWebFetch, WebSearch
WorkflowTodoWrite, Skill, ToolSearch, SendMessage, Artifact
WorktreesEnterWorktree, ExitWorktree

The list also includes SubagentHandback for a subagent that reports through it, and Agent, which follows the depth limit. Before v2.1.280, background subagents couldn't use LSP (sub-agents docs).

Tools no subagent gets, even if listed

AskUserQuestion, EndConversation, EnterPlanMode, ScheduleWakeup, WaitForMcpServers, Workflow, ExitPlanMode (unless permissionMode is plan), and Agent once the subagent hits the depth limit. Subagents can spawn their own subagents "up to three layers below the main conversation" by default. You change that limit with CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH.

That affects how you design the job. A subagent can't stop and ask you a question, so its prompt has to contain everything it needs. And because the background filter drops some tools silently, "the same definition can resolve to different tools in the foreground and the background."

Starter lists by job (examples)

What happens when a subagent's tools don't match its job (failure modes)?

When a Claude Code subagent lacks a tool it needs, it either can't launch or does a weaker version of the task. When it has more tools than it needs, it can change things you never asked it to touch. Here are the failures you're most likely to hit, with fixes.

1. Every entry is wrong, so the subagent won't start

If nothing in tools resolves to a real tool, because of typos, lowercase names, or tools subagents can't have, Claude Code "usually refuses to launch the subagent," and the Agent tool returns an error naming the entries it couldn't resolve (sub-agents docs; errors page). The error text contains "spawned with zero tools." Fix: check capitalization against the tools reference, and remove anything from the withheld list above.

2. Some entries are wrong, and nothing tells you

The error only appears when the list resolves to nothing. Suppose you write tools: Read, edit, Bash (example). edit doesn't resolve, so the subagent starts with Read and Bash. It may then try to make changes through shell commands, or report that it couldn't edit. Fix: after any edit to the list, ask the subagent to name the tools it has before the first real run.

3. Background runs lose a tool you listed

A tool that isn't on the background keep-list gets removed with no error. A definition that works when you watch it in the foreground can then break overnight. Fix: build your lists only from the background table above, or set background: false for interactive use. When a cron run stops halfway, also read /why-claude-code-stops-mid-automation-and-how-to-fix-it/.

4. allowed-tools in a subagent file

That key isn't a subagent field, so the subagent inherits every tool, and the safety limit you thought you'd set isn't there. Fix: rename the key to tools.

5. A Bash specifier that removes all of Bash

disallowedTools: Bash(rm *) takes away the whole Bash tool, so test runs fail too. Fix: move the pattern to permissions.deny in .claude/settings.json.

6. Too many tools on a read-only job

For example, a "reviewer" with inherited Bash and Write might run a formatter or rewrite files while it reviews. In acceptEdits or auto mode, nothing asks you first. Fix: use an explicit read-only list, and add a hook (next sections) that denies writes from that agent type. For broader rule sets you can paste in, see /ai-agent-guardrails-example-copy-paste-claude-code-rules/.

7. A new MCP server reaches every inheriting agent

Connect a Slack server, and every subagent without a tools list can now post messages. Fix: add disallowedTools: mcp__* to agents that should stay local, or define the server inline with mcpServers so only one agent connects to it.

What are Claude Code hooks and how do they relate to subagents?

Claude Code hooks are commands (or HTTP calls, MCP tools, prompts, or agents) that run automatically at fixed lifecycle points. They give you a second layer of enforcement that keeps working even when a subagent's tool list is wrong. The tools field decides what a subagent can try. A hook can inspect each attempt as it happens and block it.

Hooks reach subagents in two ways (sub-agents docs):

Each hook gets JSON input that includes agent_id and agent_type (the subagent's name), so one settings-level script can apply different rules to different agents. SubagentStart and SubagentStop also accept a matcher on the agent's name. Plugin-scoped names like my-plugin:db-agent contain a colon, which makes them regex, so anchor them as ^my-plugin:db-agent$.

Example frontmatter hook, adapted from the docs:

---
name: db-migrator
description: Runs database migrations
tools: Read, Bash
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/security-check.sh"
---

For a full map of every event and when it fires, see /claude-code-hooks-lifecycle-every-event-explained/.

What do Claude Code hooks PreToolUse, PostToolUse, UserPromptSubmit, and Notification do?

In Claude Code, PreToolUse runs before a tool call and can block it, and PostToolUse runs after the tool succeeds. UserPromptSubmit runs when a prompt is submitted. Notification runs when Claude Code sends a notification and is for side effects only.

What do Claude Code hooks PreToolUse, PostToolUse, UserPromptSubmit, and Notification do?
EventWhen it firesMatcher filters onCan it block?Typical subagent use
PreToolUseBefore a tool executesTool name (Bash, `Edit\Write, mcp__memory__.*`)Yes: exit code 2, or permissionDecision: "deny"Deny writes from a reviewer agent
PostToolUseAfter a tool succeedsTool nameNot the tool, which already ran. decision: "block" stops Claude from continuingRun a linter after a subagent edits
UserPromptSubmitWhen a prompt is submitted, before Claude processes itNo matcher; fires every timeYes: exit code 2 or decision: "block"Add project context to every turn
NotificationWhen Claude Code sends a notificationpermission_prompt, idle_prompt, auth_success, agent_needs_input, agent_completed, and othersNo, exit code is ignoredAlert you when an unattended run needs input

All four come from the hooks reference (as of October 2026). A few more details:

Exit codes work like this. 0 means success, and any JSON output is applied. 2 is a blocking error on events that can block. Any other code is a non-blocking error, and the action goes ahead unless you set onFailure: "block". For ready-made scripts on these events, see /claude-code-hooks-examples-7-copy-paste-scripts/.

What is a Claude Code hook and how do you configure one?

A Claude Code hook is a JSON settings entry that runs a handler when an event matches. The handler is usually a shell command, and the entry links it to an event and a matcher. Here is how to set up a hook that stops a read-only subagent from writing files, even if its tool list was set up wrong.

Steps

  1. Choose the scope. .claude/settings.json is for one project and is shared through git. .claude/settings.local.json is for one project and is gitignored. ~/.claude/settings.json covers all your projects (hooks reference).
  2. Create the script at .claude/hooks/deny-reviewer-writes.sh (example):
#!/bin/bash
INPUT=$(cat)
AGENT=$(echo "$INPUT" | jq -r '.agent_type // empty')

if [ "$AGENT" = "code-reviewer" ]; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "code-reviewer is read-only"
    }
  }'
else
  exit 0
fi
  1. Make it executable: chmod +x .claude/hooks/deny-reviewer-writes.sh. You also need jq installed.
  2. Register it in .claude/settings.json:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write|NotebookEdit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/deny-reviewer-writes.sh"
          }
        ]
      }
    ]
  }
}
  1. Open /hooks in a session to confirm the hook is listed and to see which file it came from.
  2. Run the reviewer on a small diff and ask it to fix something. The write attempt should come back denied, with your reason.

Matcher rules to get right

A matcher that is "", "", or left out matches everything. A value made only of letters, digits, _, -, spaces, , and | is treated as an exact name or a list. Anything else is treated as an unanchored JavaScript regex (hooks reference). So mcp__github__. is a regex that covers every GitHub MCP tool. You can narrow further with the if field, which takes permission-rule syntax such as "Bash(rm *)".

Common mistakes and fixes

Next steps

Do today (DIY):

  1. Search your agents for the wrong key: grep -rn "allowed-tools" .claude/agents ~/.claude/agents. Rename every match to tools.
  2. For each subagent that runs unattended, write out a tools list built only from the background keep-list, then ask the subagent to list its tools before its next real run.
  3. Add one PreToolUse hook like the one above for your most sensitive agent, and put Bash(git push *) in permissions.deny if no agent should push.

If you'd rather not set this up by hand: the main official option is a Claude subscription that includes Claude Code, so you can ask Claude to write and check agent files for you. Claude Pro is $20/month ($17/month billed annually), and Max starts at $100/month with 5x or 20x Pro's usage (as of October 2026; check the official pricing page). Pro suits solo builders running a few scheduled subagents. Max suits people who run many parallel or long jobs and keep hitting limits. Neither plan reviews your configuration for you, and the Free plan doesn't include Claude Code. If your runs are small and occasional, pay-as-you-go API use may cost less. Token tracking is covered in /claude-code-how-to-see-token-usage-and-reduce-it/.

Related guides on this site:

FAQ

What tools can a Claude Code subagent use?

If you don't set a tools field, it inherits the main conversation's built-in and MCP tools, minus a few that are always withheld, such as AskUserQuestion and EnterPlanMode. Background subagents, the default, keep only a fixed set of built-ins like Read, Edit, Write, Bash, Grep, Glob, WebFetch, and WebSearch, plus all MCP tools. Use tools or disallowedTools to narrow that set.

What are Claude Code hooks?

Hooks are handlers you configure in settings files or agent frontmatter, usually shell commands, that run automatically at lifecycle events such as PreToolUse, PostToolUse, SessionStart, and SubagentStop. They get JSON describing the event and can allow, deny, or change some actions. Hooks set in settings also run inside subagents.

Where are the docs for PreToolUse, PostToolUse, UserPromptSubmit, and Notification?

All four are on the official hooks reference, with input fields, matcher values, and decision options for each. The sub-agents page covers how those events behave inside subagents and the SubagentStart/SubagentStop matchers.

How do I make a Claude Code hook block a command?

Register a PreToolUse hook with a matcher such as Bash, and either exit with code 2 or print JSON with permissionDecision: "deny" and a reason. Add "onFailure": "block" if a crashing script should also block. For simple command patterns, a permissions.deny rule is often enough and needs no script.

Sources

Spotted something out of date or wrong? Tell us at shigoto.ai.note@gmail.com. Guides are re-checked against official sources on a rolling schedule.