Create custom subagents
Define specialist subagents with their own prompt, tools, model and permissions, and control how Claude Code delegates, runs, forks and resumes them.
A subagent is a worker Claude hands a task to. It runs in a separate context window with its own system prompt, its own tool list and its own permissions, does the job, and sends back a summary. The search results, log dumps and file contents it chewed through stay out of your main conversation.
That is the main reason to use one: keeping a side task from flooding your context. The second reason is repetition. If you keep asking for the same kind of helper with the same instructions, write it down once as a custom subagent.
Subagents also let you:
- restrict what a helper can touch (a reviewer that cannot edit files, for example)
- route cheap, high-volume work to a faster model such as Haiku
- share a specialist across every project, or check one into a repository for the team
Subagents make their own API requests, and those count against the same usage limits as your main session.
Note: Subagents live inside one session. To run whole sessions in parallel see agent view; for sessions that message each other see cross-session messaging; for a supervised group see agent teams.
Built-in subagents
Claude Code ships with several subagents and uses them without being asked. All of them inherit the main conversation's permission rules.
| Name | Model | Tools | What Claude uses it for |
|---|---|---|---|
Explore | The main conversation's model (see note below) | Read-only; Write and Edit denied | Searching and understanding a codebase. Claude picks a thoroughness level of quick, medium or very thorough |
Plan | Inherits the main model | Read-only; Write and Edit denied | Gathering context while you are in plan mode |
general-purpose | CLAUDE_CODE_SUBAGENT_MODEL if set and nothing else chooses, otherwise the main model | Everything available to subagents | Multi-step jobs that need to both explore and change things |
claude | Follows the normal model order | Everything available to subagents | Catch-all when nothing more specific fits; also the default agent for background sessions in agent view |
statusline-setup | Sonnet | Limited | Runs when you use /statusline |
claude-code-guide | Haiku | Limited | Answers questions about Claude Code itself |
When the main conversation runs Fable, Explore runs on whatever the opus alias resolves to if you connect with a Claude subscription, a Console account or a gateway via ANTHROPIC_BASE_URL. On Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, Claude Platform on AWS or a Claude apps gateway it stays on the main model.
Explore and Plan deliberately skip your CLAUDE.md files and the git status snapshot so they stay fast. Every other subagent loads both unless its definition sets omitClaudeMd.
To narrow the built-ins:
- Block one type with a deny rule such as
Agent(Explore)(see Disable specific subagents). - Stop all delegation by denying the
Agenttool itself inpermissions.deny. - Remove only Explore and Plan with
CLAUDE_CODE_DISABLE_EXPLORE_PLAN_AGENTS=1(v2.1.198+). Claude then reads files directly. - In headless runs and the Agent SDK, set
CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS=1to drop every built-in and supply your own.
If a session has no general-purpose agent, an Agent call that leaves out subagent_type fails with subagent_type is required.
Your first custom subagent
Subagents are Markdown files with YAML frontmatter. The easiest route is to ask Claude to write one.
-
Describe what you want and where it should live:
Make me a user-level subagent in ~/.claude/agents/ called migration-checker. It reads Prisma migration files and flags anything that would lock a large table or drop data. Read-only tools only, and run it on Haiku. -
Open the file Claude created and check it. It should look roughly like this:
--- name: migration-checker description: Reviews database migrations for locking and data-loss risk. Use before merging any migration. tools: Read, Grep, Glob model: haiku --- You review SQL and Prisma migrations. For each file, list operations that take an exclusive lock, rewrite a table, or drop a column or table. Rate each finding low, medium or high and suggest a safer sequence. -
Try it:
Use the migration-checker agent on the migrations in this branch. The transcript shows a row likemigration-checker(Check new migrations).
If Claude cannot see it, restart. That only happens when ~/.claude/agents/ did not exist when the session began, because the file watcher only covers directories that were already there.
Note:
/agentsnow just prints a reminder to ask Claude or edit.claude/agents/and~/.claude/agents/. On v2.1.197 and earlier it opened an interactive manager with Running and Library tabs.
Where subagent files live
When two definitions share a name, the higher-priority location wins.
| Priority | Location | Available to |
|---|---|---|
| 1 (highest) | Managed settings directory, under .claude/agents/ | Everyone in the organisation |
| 2 | --agents JSON on the command line | That session only |
| 3 | .claude/agents/ in the project | That project |
| 4 | ~/.claude/agents/ | All your projects |
| 5 (lowest) | A plugin's agents/ folder | Wherever the plugin is enabled |
Some details worth knowing:
- Project scope walks upwards. Every
.claude/agents/between your working directory and the repository root is scanned; the closest definition wins on a name clash. --add-dirdirectories contribute their.claude/agents/too, but they are not watched for changes, so restart after editing them.- Subfolders are fine. Both project and user folders are scanned recursively, and the folder path does not affect the agent's identity: only
namedoes. Keep names unique, because two files in the same tree with the same name load in filesystem order./doctorflags duplicates. - Plugins are different. In a plugin, subfolders become part of the scoped name:
agents/review/security.mdin pluginacmeregisters asacme:review:security. - Plugin subagents ignore
hooks,mcpServersandpermissionModefor security. Copy the file into your own agents folder if you need them, or, as the plugin author, ship hooks inhooks/hooks.jsonand servers in.mcp.json. See Plugin components.
Defining subagents on the command line
--agents takes a JSON object keyed by agent name. Nothing is saved to disk, which makes it handy for scripts and experiments:
claude --agents '{
"changelog-writer": {
"description": "Drafts CHANGELOG entries from the current diff.",
"prompt": "Summarise user-facing changes in Keep a Changelog format.",
"tools": ["Read", "Bash"],
"model": "haiku"
}
}'
Each definition accepts prompt (the system prompt, may be empty from v2.1.281) plus the frontmatter fields description, tools, disallowedTools, model, permissionMode, mcpServers, hooks, maxTurns, skills, initialPrompt, memory, effort, background, omitClaudeMd and isolation. color and experimental are silently ignored. Names must not start with -.
In headless mode (-p) you can pass a file path instead of inline JSON, for example claude -p --agents ./agents.json "..." (v2.1.281+). Interactive sessions refuse a path.
Writing the file
The frontmatter configures the agent and the Markdown body becomes its system prompt. A subagent gets only that prompt plus basic environment details, not the full Claude Code system prompt.
Claude Code watches ~/.claude/agents/ and .claude/agents/ and picks up edits within seconds. You need a restart only when you create the first file in a brand new agents folder, when the file sits under an --add-dir directory, or when the session was started with --disable-slash-commands.
In headless mode, --append-subagent-system-prompt (v2.1.205+) or --append-subagent-system-prompt-file (v2.1.261+) adds text to the end of every subagent prompt except forks.
A subagent starts in the main session's working directory. cd does not persist between its Bash calls and never moves the main session. For a separate copy of the repository, use isolation: worktree; Claude Code then refuses commands whose working directory resolves back into your main checkout and blocks Bash that redirects git there. See Worktrees.
Frontmatter fields
Only name and description are required. Field names are camelCase and must match exactly; an unknown field is ignored without an error.
| Field | What it does |
|---|---|
name | Unique ID, up to 256 characters, no : and not starting with -. Hooks see it as agent_type. Need not match the filename |
description | Tells Claude when to delegate. Keep it short |
tools | Allowlist, comma-separated or a YAML list. Omit to inherit everything available to subagents |
disallowedTools | Denylist applied before tools. An entry like Bash(git push *) still removes the whole tool |
model | sonnet, opus, haiku, fable, a full ID such as claude-opus-5-5, or inherit |
permissionMode | default, acceptEdits, auto, dontAsk, bypassPermissions, plan, or manual (alias of default) |
maxTurns | Stop after this many agentic turns; output is marked partial and can be resumed |
skills | Skills whose full content is injected at startup |
mcpServers | MCP servers for this agent only, by name or inline definition |
hooks | Lifecycle hooks that run only while this agent is active |
memory | Persistent memory scope: user, project or local |
background | true keeps the agent in the background even if Claude asks for the foreground |
omitClaudeMd | true skips user, project and local CLAUDE.md (v2.1.271+). Managed policy files still load, except for managed subagents |
effort | low, medium, high, xhigh or max. Overrides the session effort but not CLAUDE_CODE_EFFORT_LEVEL |
isolation | worktree runs the agent in a temporary git worktree branched from your default branch, removed if nothing changed |
color | red, blue, green, yellow, purple, orange, pink or cyan in the task list and transcript |
initialPrompt | First user turn, auto-submitted when this agent runs as the main session via --agent or the agent setting |
experimental | Map of experimental options. cacheTtl: 5m or 1h sets the prompt cache lifetime for this agent (v2.1.248+) |
cacheTtl must sit inside experimental, not at the top level:
---
name: monorepo-indexer
description: Builds a map of packages and their owners
experimental:
cacheTtl: 1h
---
Files that silently fail to load
Claude Code skips a project, user or managed agent file without telling you in the session when:
- there is no
name(it is treated as documentation) - the opening
---is not on the first line - the
namestarts with-, contains:or is over 256 characters - there is a
namebut nodescription - the YAML does not parse
All but the first two leave a reason in the debug log, so run claude --debug when an agent goes missing. Before a session, claude plugin validate .claude/agents (v2.1.233+) reports frontmatter that does not parse. Plugin agents with bad or missing names still load under their filename.
Choosing a model
Claude Code resolves a subagent's model in this order:
- The
modelClaude passes on that particular invocation - The definition's
modelfield (inheritmeans the main model) CLAUDE_CODE_SUBAGENT_MODEL, if set to an alias or ID- The main conversation's model
A mod that sets a model in its agent.spawn hook replaces step 1.
Two quirks: if the main model already belongs to the family you name (say opus while you run Opus), the subagent gets your exact model, including any [1m] suffix. And an alias in CLAUDE_CODE_SUBAGENT_MODEL always resolves to the alias's current target. Setting the variable to inherit is the same as leaving it unset.
Your organisation's availableModels allowlist is checked too. A blocked family alias falls back to the newest permitted version of that family; anything else falls back to the inherited model, with a warning in interactive sessions.
Run /tasks while a subagent is running to see its model (and effort, if set) on its row. Subagents also inherit the main session's extended thinking on/off state; there is no per-agent switch.
Forcing one model everywhere
CLAUDE_CODE_SUBAGENT_MODEL is only a default. To override definitions and per-call choices for every subagent, teammate and workflow agent, also set CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1 (v2.1.257+):
{
"env": {
"CLAUDE_CODE_SUBAGENT_MODEL": "sonnet",
"CLAUDE_CODE_SUBAGENT_MODEL_FORCE": "1"
}
}
With only the force flag set, subagents run on the main model (Explore keeps its listed model). Forks and skills with model: inherit always stay on the main model regardless.
Controlling what a subagent can do
Tool access
Subagents inherit built-in and MCP tools from the main conversation, then two filters apply. The first always removes AskUserQuestion, EndConversation, EnterPlanMode, ScheduleWakeup, WaitForMcpServers and Workflow; removes ExitPlanMode unless the agent's mode is plan; and removes Agent once the depth limit is reached.
The second filter applies to background subagents, which is the default. They keep every MCP tool but only these built-ins: Read, Grep, Glob, LSP, Bash, PowerShell, Edit, Write, NotebookEdit, WebFetch, WebSearch, TodoWrite, Skill, ToolSearch, EnterWorktree, ExitWorktree, Monitor, TaskStop, SendMessage and Artifact (plus SubagentHandback where used). Forks skip both filters. Agent team teammates also keep TaskCreate, TaskGet, TaskList, TaskUpdate, CronCreate, CronDelete and CronList.
Allowlist example, a read-only researcher:
---
name: incident-reader
description: Reads logs and code to explain a production incident
tools: Read, Grep, Glob, Bash
---
Denylist example, everything except one MCP server:
---
name: offline-helper
description: General helper that must not touch Jira
disallowedTools: mcp__jira
---
mcp__<server> or mcp__<server>__* matches every tool on a server, and mcp__* in disallowedTools removes all MCP tools. If nothing in tools resolves to a real tool, Claude Code refuses to launch the agent and names the bad entries.
To keep Bash but block particular commands, use a Bash deny rule in permissions.deny (see Permissions) rather than disallowedTools.
Limiting which subagents an agent can spawn
When an agent runs as the main thread with claude --agent, Agent(type1, type2) in its tools is an allowlist of spawnable types:
tools: Agent(test-runner, migration-checker), Read, Bash
Plain Agent allows any type, and leaving Agent out stops spawning. Inside an ordinary subagent definition the parenthesised list is ignored. The tool was called Task before v2.1.63 and Task(...) still works as an alias.
MCP servers for one subagent
mcpServers lets a subagent use a server the main conversation never loads, which keeps those tool descriptions out of your main context:
---
name: ui-checker
description: Opens the app in a browser and checks layouts
mcpServers:
- playwright:
type: stdio
command: npx
args: ["-y", "@playwright/mcp@latest"]
- linear
---
Inline entries use the .mcp.json schema (stdio, http, sse, ws) and connect when the agent starts; name references reuse the parent's connection. Managed MCP policy, --strict-mcp-config and --bare still apply. Inline servers from a project's .claude/agents/ load only after you have trusted that exact folder (a parent's trust does not count); user-level, --agents and managed definitions load without that check.
Permission modes
If permissionMode is unset, the subagent inherits the main mode. If the main session is in bypassPermissions, acceptEdits or auto, the subagent uses that mode regardless of what you set. If the main session is in default, dontAsk or plan, your value applies, except that bypassPermissions is never honoured from a subagent definition.
| Mode | Behaviour |
|---|---|
default | Manual: prompts for permission |
acceptEdits | Auto-accepts edits and common file commands inside the working or added directories |
auto | A background classifier reviews actions |
dontAsk | Denies anything not pre-approved |
bypassPermissions | Skips prompts, only when the main session does too |
plan | Read-only exploration |
More detail on each is in Permission modes.
Preloading skills
skills injects the full content of named skills at startup, so the agent does not have to discover them:
---
name: endpoint-builder
description: Adds REST endpoints following house conventions
skills:
- api-style
- error-envelopes
---
This does not restrict which skills the agent can call later; remove Skill from its tools for that. Skills with disable-model-invocation: true (including the bundled /verify) cannot be preloaded. A skill with context: fork is the mirror image: there the skill picks the agent.
Persistent memory
memory gives the agent a folder that survives between conversations:
| Scope | Folder |
|---|---|
user | ~/.claude/agent-memory/<agent-name>/ |
project | .claude/agent-memory/<agent-name>/ |
local | .claude/agent-memory-local/<agent-name>/ |
The agent's prompt gains instructions for using the folder and the first 200 lines or 25KB of its MEMORY.md, and Read, Write and Edit are enabled. It is part of auto memory, so it does nothing if auto memory is turned off. I default to project so the team benefits, and I end long reviews with "save what you learned to your memory".
Conditional rules with hooks
For "allow some uses of a tool but not others", attach a PreToolUse hook. This agent may run psql, but only read-only queries:
---
name: report-query
description: Answers questions from the analytics database
tools: Bash
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "./scripts/guard-sql.sh"
---
#!/usr/bin/env bash
cmd=$(jq -r '.tool_input.command // ""')
if grep -qiE '\b(insert|update|delete|drop|alter|truncate|grant)\b' <<<"$cmd"; then
echo "Read-only agent: write statements are blocked" >&2
exit 2
fi
Exit code 2 blocks the call and sends stderr back to the agent. Remember chmod +x. On Windows, write the script in PowerShell and add shell: powershell to the hook entry. See Hooks.
Disable specific subagents
{
"permissions": {
"deny": ["Agent(Explore)", "Agent(legacy-reviewer)"]
}
}
The same works from the command line: claude --disallowedTools "Agent(Explore)".
Hooks and subagents
There are two places to hook subagents:
- Frontmatter hooks run only while that agent is active, whether it was spawned as a subagent or launched as the main session with
--agent. Any event is supported. AStophook here is converted toSubagentStopat runtime. Project-level frontmatter hooks need the folder to be trusted; until then the agent runs without them and the debug log explains why. - Settings hooks fire inside subagents as well, so a
PreToolUseinsettings.jsonsees subagent tool calls.SubagentStartandSubagentStopfire when any subagent begins and ends, with the agent's name as the matcher. Plugin agents use their scoped name; because it contains a colon it is treated as a regex, so anchor it, for example^acme:db-agent$.
{
"hooks": {
"SubagentStart": [
{ "matcher": "report-query", "hooks": [{ "type": "command", "command": "./scripts/open-tunnel.sh" }] }
],
"SubagentStop": [
{ "hooks": [{ "type": "command", "command": "./scripts/close-tunnel.sh" }] }
]
}
}
Working with subagents
Automatic delegation
Claude decides from your request, each agent's description and the current context. Adding "use proactively" to a description encourages Claude to reach for it. Descriptions cost context, so when the combined descriptions of your non-built-in agents exceed 15,000 tokens, Claude Code warns at startup (and still loads them all). Put detail in the body, which only loads when the agent runs. For plugin agents, claude plugin eval can measure delegation reliability across many prompts.
Asking for a subagent explicitly
Three levels, from gentle to absolute:
- Name it in prose: "Have the migration-checker look at this branch." Claude usually obliges.
- @-mention it: type
@and pick it, giving something like@"migration-checker (agent)" check 0042_add_index. The named agent will run; Claude still writes its task prompt. You can type@agent-<name>by hand, or@agent-plugin:namefor plugin agents. - Run the whole session as it:
claude --agent migration-checker. The agent's prompt replaces the default system prompt (CLAUDE.md still loads), its tools and model apply, and@migration-checkerappears in the startup header. It survives resume. To make it a project default, set"agent": "migration-checker"in.claude/settings.json; the flag overrides the setting. Useplugin:nameorplugin:folder:namewhen plugin names clash.
Foreground and background
A foreground subagent blocks the conversation until it finishes. A background one runs alongside you and surfaces any permission prompt in your main session, naming itself; Esc denies that one call without stopping it. A choice that lasts beyond one call, such as "allow for this session", applies to the whole session.
Claude Code chooses using the first rule that applies:
- Subagents spawned by an in-process agent team teammate run in the foreground.
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1forces the foreground everywhere.- With fork mode on (the interactive default), everything Claude spawns runs in the background and Claude cannot ask otherwise.
- With fork mode off (default for
-pand the SDK), Claude backgrounds by default and uses the foreground when it needs the answer first.background: truein frontmatter keeps an agent in the background regardless.
You can also press Ctrl+B to push a running task into the background. When a background subagent succeeds, its row vanishes and the footer shows /tasks to see subagents for 30 seconds; failed or stopped rows linger for 30 seconds (press x to clear one). Claude waits for the completion notice before reporting results.
Names
Claude may pass a name when spawning, which lets it message or resume the agent by name later. With agent teams enabled, a named subagent spawned from the main conversation becomes a teammate instead, unless it is a fork or the call itself passes isolation.
When the API fails mid-run
If a response is cut off with text but no tool calls, Claude Code nudges the subagent to continue. If the run still ends on an error, a foreground agent returns its partial text with a note, or fails with Agent terminated early due to an API error if it produced nothing; a background agent is marked failed and its last output is passed back. A configured fallback model chain lets the agent switch models instead. Once the error clears, ask Claude to retry or resume.
Output scanning
Before Claude reads a subagent's final report, Claude Code scans it (v2.1.210+). Text imitating Claude Code's own markup, such as a <system-reminder> tag or a line starting Human:, gets a backslash inserted so it reads as plain text. Reports that imitate those tags or mention permission settings like bypassPermissions get a marker line starting [harness: subagent output matched instruction-shaped pattern(s):. Nothing is removed, and the scan is not a security boundary: tool calls still go through normal permissions and sandboxing.
Patterns that work well
- Soak up noisy output: "Use a subagent to run the e2e suite and give me only the failing specs with their first error line."
- Parallel research: "Investigate the billing, auth and notifications modules in parallel with separate subagents." Remember each one's summary lands in your context.
- Chains: "Have the migration-checker review the branch, then have the endpoint-builder fix whatever it flags."
Stay in the main conversation for back-and-forth work, tightly connected phases, quick edits, or when latency matters. For a question about something already in the chat, /btw is lighter: it sees your context, has no tools, and does not add to history. For reusable prompts that should run in your main context, write a skill instead.
Nested subagents
Subagents can spawn their own, up to three layers below the main conversation by default. At the limit the Agent tool is withheld (a fork keeps it listed but it errors). Change the depth with CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH; 1 turns nesting off. In interactive sessions a parent waits for its background children before finishing; in -p and the SDK it does not, so late children report to the main conversation. The panel shows nesting as a tree with a (+N) count of descendants.
Concurrency limit
With 20 subagents already running, a new Agent call fails with Concurrent subagent limit reached. Change it with CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS (v2.1.217+). Ultracode sessions are exempt. /subtask forks and resumes take slots without being blocked, and workflow agents and teammates have their own limits. There is no cap on the total over a session.
Context and resuming
What a subagent starts with
A non-fork subagent sees none of your history. It starts with:
- its own system prompt plus environment details
- the delegation message Claude writes for it
- the full CLAUDE.md hierarchy, including
AGENTS.mdfiles loaded as instructions (not for Explore, Plan, or agents withomitClaudeMd) - a git status snapshot (not for Explore or Plan; controlled by
includeGitInstructions) - any preloaded skills
- a roster of other named agents it can
SendMessage, if it has that tool and one exists (v2.1.206+)
It does not get your output style, your auto memory, or your context window size (its own model sets that). If a rule must reach the agent, say it in the delegation prompt.
Resuming
Every spawn is a fresh instance. To continue earlier work, ask Claude to resume: it uses SendMessage with the agent's ID or name, and the agent carries on with its full history in the background. Explore and Plan are one-shot and cannot be resumed. Agents stopped at maxTurns can be resumed to finish. An agent you stopped yourself (with x in /tasks) will not auto-resume from Claude, though you can type into its transcript while its row is still visible.
SendMessage refuses to deliver if a name now points at a different agent, and messages from other agents never count as your approval for a permission prompt.
Transcripts live at ~/.claude/projects/{project}/{sessionId}/subagents/agent-{agentId}.jsonl, survive main-conversation compaction, and are deleted after cleanupPeriodDays (30 by default). Subagents auto-compact on the same rules as the main session, and CLAUDE_AUTOCOMPACT_PCT_OVERRIDE applies to them.
Fork the current conversation
A fork is a subagent that inherits everything: the same system prompt, tools, model and full message history. You lose input isolation but keep output isolation, so you can hand off a side task without re-explaining anything and still get back only the result. Because the prompt and tools match, a fork reuses your prompt cache and is cheaper than a fresh agent with the same context.
Start one yourself with /subtask (v2.1.212+; it was /fork on v2.1.161 to v2.1.211):
/subtask write a migration rollback script for the change we just agreed
| Fork | Ordinary subagent | |
|---|---|---|
| Context | Whole conversation | Fresh, plus your delegation prompt |
| Prompt and tools | Same as main | From its definition, filtered for background |
| Model | Same as main | From its model field |
| Prompt cache | Shared | Separate |
Running forks appear in a panel under the prompt. Use ↑/↓ to move, Enter to open a fork's transcript and message it, x to stop or dismiss, and Esc to return to the prompt. While viewing a transcript, messages and skills go to that agent but built-in commands go to the main session; /compact, /clear and /rewind ask first, and /model and /fast refuse to run. Ctrl+Enter (or Ctrl+X Ctrl+S) makes the agent read your message straight away (v2.1.286+).
Claude can pass isolation: "worktree" when it forks. A fork cannot fork again.
Fork mode
Fork mode is on by default in interactive sessions (v2.1.232+) and off in -p and the SDK. With it on, Claude can request the fork type, and everything Claude spawns runs in the background. Override with CLAUDE_CODE_FORK_SUBAGENT=1 (on everywhere) or 0 (off everywhere). To keep fork mode but stop Claude forking, deny Agent(fork).