Skip to content

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.

NameModelToolsWhat Claude uses it for
ExploreThe main conversation's model (see note below)Read-only; Write and Edit deniedSearching and understanding a codebase. Claude picks a thoroughness level of quick, medium or very thorough
PlanInherits the main modelRead-only; Write and Edit deniedGathering context while you are in plan mode
general-purposeCLAUDE_CODE_SUBAGENT_MODEL if set and nothing else chooses, otherwise the main modelEverything available to subagentsMulti-step jobs that need to both explore and change things
claudeFollows the normal model orderEverything available to subagentsCatch-all when nothing more specific fits; also the default agent for background sessions in agent view
statusline-setupSonnetLimitedRuns when you use /statusline
claude-code-guideHaikuLimitedAnswers 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 Agent tool itself in permissions.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=1 to 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.

  1. 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.
    
  2. 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.
    
  3. Try it: Use the migration-checker agent on the migrations in this branch. The transcript shows a row like migration-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: /agents now 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.

PriorityLocationAvailable to
1 (highest)Managed settings directory, under .claude/agents/Everyone in the organisation
2--agents JSON on the command lineThat session only
3.claude/agents/ in the projectThat project
4~/.claude/agents/All your projects
5 (lowest)A plugin's agents/ folderWherever 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-dir directories 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 name does. Keep names unique, because two files in the same tree with the same name load in filesystem order. /doctor flags duplicates.
  • Plugins are different. In a plugin, subfolders become part of the scoped name: agents/review/security.md in plugin acme registers as acme:review:security.
  • Plugin subagents ignore hooks, mcpServers and permissionMode for security. Copy the file into your own agents folder if you need them, or, as the plugin author, ship hooks in hooks/hooks.json and 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.

FieldWhat it does
nameUnique ID, up to 256 characters, no : and not starting with -. Hooks see it as agent_type. Need not match the filename
descriptionTells Claude when to delegate. Keep it short
toolsAllowlist, comma-separated or a YAML list. Omit to inherit everything available to subagents
disallowedToolsDenylist applied before tools. An entry like Bash(git push *) still removes the whole tool
modelsonnet, opus, haiku, fable, a full ID such as claude-opus-5-5, or inherit
permissionModedefault, acceptEdits, auto, dontAsk, bypassPermissions, plan, or manual (alias of default)
maxTurnsStop after this many agentic turns; output is marked partial and can be resumed
skillsSkills whose full content is injected at startup
mcpServersMCP servers for this agent only, by name or inline definition
hooksLifecycle hooks that run only while this agent is active
memoryPersistent memory scope: user, project or local
backgroundtrue keeps the agent in the background even if Claude asks for the foreground
omitClaudeMdtrue skips user, project and local CLAUDE.md (v2.1.271+). Managed policy files still load, except for managed subagents
effortlow, medium, high, xhigh or max. Overrides the session effort but not CLAUDE_CODE_EFFORT_LEVEL
isolationworktree runs the agent in a temporary git worktree branched from your default branch, removed if nothing changed
colorred, blue, green, yellow, purple, orange, pink or cyan in the task list and transcript
initialPromptFirst user turn, auto-submitted when this agent runs as the main session via --agent or the agent setting
experimentalMap 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 name starts with -, contains : or is over 256 characters
  • there is a name but no description
  • 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:

  1. The model Claude passes on that particular invocation
  2. The definition's model field (inherit means the main model)
  3. CLAUDE_CODE_SUBAGENT_MODEL, if set to an alias or ID
  4. 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.

ModeBehaviour
defaultManual: prompts for permission
acceptEditsAuto-accepts edits and common file commands inside the working or added directories
autoA background classifier reviews actions
dontAskDenies anything not pre-approved
bypassPermissionsSkips prompts, only when the main session does too
planRead-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:

ScopeFolder
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. A Stop hook here is converted to SubagentStop at 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 PreToolUse in settings.json sees subagent tool calls. SubagentStart and SubagentStop fire 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:name for 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-checker appears 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. Use plugin:name or plugin:folder:name when 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:

  1. Subagents spawned by an in-process agent team teammate run in the foreground.
  2. CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 forces the foreground everywhere.
  3. With fork mode on (the interactive default), everything Claude spawns runs in the background and Claude cannot ask otherwise.
  4. With fork mode off (default for -p and the SDK), Claude backgrounds by default and uses the foreground when it needs the answer first. background: true in 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.md files loaded as instructions (not for Explore, Plan, or agents with omitClaudeMd)
  • 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
ForkOrdinary subagent
ContextWhole conversationFresh, plus your delegation prompt
Prompt and toolsSame as mainFrom its definition, filtered for background
ModelSame as mainFrom its model field
Prompt cacheSharedSeparate

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).