# Subagents in Claude Code

Define a saved read-only reviewer subagent for ticket-service in .claude/agents/reviewer.md, with restricted tools and a second-opinion model, and learn what a subagent inherits.

Source: https://ai-sw-factory.mellicci.dev/fundamentals/subagents/claude-code

## At a glance

| | Claude Code |
|---|---|
| Term | Subagents |
| Configured in | Markdown files with YAML frontmatter in `.claude/agents/` (project) or `~/.claude/agents/` (user); the body is the system prompt |
| Loads / runs | Claude delegates by the `description`, or you name or @-mention the subagent; it runs in its own context and returns a summary |
| Scope & precedence | Same name: managed settings, then `--agents` flag, then project, then user, then plugin; nested project directories: closest wins |

## Build the scenario

| Step | What you write or run | Where |
|---|---|---|
| 1 | Create `reviewer.md` with the definition below | `.claude/agents/` |
| 2 | Run `/mcp` and confirm the real name of the GitHub issue-read tool | Claude Code session |
| 3 | Ask: `Have the reviewer subagent check this change against issue 42` | Claude Code session |

```markdown
---
name: reviewer
description: Reviews the current change in ticket-service against the issue's acceptance criteria and the team conventions. Use after implementing an issue, before opening a pull request. Read-only.
tools: Read, Grep, Glob, Bash, mcp__github__get_issue
model: opus
---
1. Run `git diff main...HEAD`. Use Bash for nothing else.
2. Read the issue the prompt names with the GitHub tool.
3. Check each acceptance criterion against the diff.
4. Check conventions: a changed route in `src/routes/` has a test in `test/`;
   files in `src/db/migrations/` are never hand-edited.
5. Return pass or fail per criterion, then findings as `file:line`.
6. Never edit files.
```

The body is the system prompt. `get_issue` is an assumed tool name: the docs don't list GitHub's tool names, so use what `/mcp` shows. If the server ships inside a plugin, its tools are named `mcp__plugin_<plugin>_<server>__<tool>` instead; see [plugins in Claude Code](https://ai-sw-factory.mellicci.dev/fundamentals/plugins/claude-code). Because `model: opus` resolves to the main conversation's own model when that is also Opus, it differs only when the main conversation is not on Opus.

Invoke it in any of three ways:

| How | Example | Effect |
|---|---|---|
| Automatic | Finish an issue; Claude matches the `description` | Claude decides whether to delegate |
| Natural language | `Use the reviewer subagent on my changes` | Claude typically delegates |
| @-mention | `@"reviewer (agent)" check issue 42` | Guarantees this subagent runs for the task |

## Specifics

### Built-in and saved agents

| Agent | Model | Tools | Used for |
|---|---|---|---|
| Explore | Main model, capped at Opus on the Claude API | Read-only | Searching and analyzing the codebase |
| Plan | Main model | Read-only | Research during plan mode |
| general-purpose | Main model, or `CLAUDE_CODE_SUBAGENT_MODEL` if nothing else sets one | All tools available to subagents | Multi-step research and changes |
| claude | Follows the model order | All tools available to subagents | Catch-all when no agent fits |

Explore and Plan skip your CLAUDE.md files and the git status snapshot. Saved agents come from five places:

| Location | Scope | Priority |
|---|---|---|
| Managed settings | Organization | 1 (highest) |
| `--agents '<json>'` | Current session only, not saved | 2 |
| `.claude/agents/` | Project; walked up to the repository root | 3 |
| `~/.claude/agents/` | All your projects | 4 |
| Plugin `agents/` | Where the plugin is enabled | 5 |

Plugin subagents ignore `hooks`, `mcpServers` and `permissionMode`. Edits to existing agent directories apply within seconds.

### Definition fields

Only `name` and `description` are required. Claude Code ignores unknown field names silently, and multi-word names are camelCase.

| Field | Purpose |
|---|---|
| `name`, `description` | Identifier (no `:`); when Claude should delegate |
| `tools`, `disallowedTools` | Allowlist and denylist; both take `mcp__<server>` patterns; omit `tools` to inherit everything |
| `model` | `sonnet`, `opus`, `haiku`, `fable`, a full model ID, or `inherit` |
| `permissionMode` | `default`, `acceptEdits`, `auto`, `dontAsk`, `bypassPermissions`, `plan` |
| `maxTurns`, `effort` | Turn cap; effort level that overrides the session's |
| `skills`, `mcpServers`, `hooks`, `memory` | Preloaded skills, scoped MCP servers, scoped hooks, persistent memory |
| `background`, `isolation`, `omitClaudeMd` | Always background; run in a git worktree; skip CLAUDE.md |

### What a subagent inherits

| | Inherited | Override |
|---|---|---|
| Conversation | Nothing: it gets the system prompt, environment details and the delegation message | None; a fork copies the conversation instead |
| Permission rules | The parent's | None in the definition |
| Permission mode | The parent's | `permissionMode`, ignored if the parent is in `bypassPermissions`, `acceptEdits` or auto mode |
| Sandbox | The parent's configuration | None documented |
| Tools | Built-in and MCP tools, minus a short always-removed list (for example `AskUserQuestion`) | `tools`, `disallowedTools` |
| Model | Order: per-call parameter, `model` field, `CLAUDE_CODE_SUBAGENT_MODEL`, main model | `model`; `CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1` overrides all |
| Thinking, effort | Extended-thinking setting of the session; effort inherits | `effort`; no per-subagent thinking setting |
| CLAUDE.md, git status | Loaded (Explore and Plan skip both) | `omitClaudeMd: true` |

A `disallowedTools` entry with a specifier such as `Bash(git push *)` still removes the whole tool. The documentation doesn't show a specifier such as `Bash(git diff *)` in `tools`. To keep Bash and limit it, add a `PreToolUse` hook to the subagent's frontmatter that exits 2 for other commands, as the docs' read-only database example does, or add session-wide Bash deny rules.

### Invocation, foreground and background

| Situation | Runs in |
|---|---|
| Interactive session, fork mode on (the default) | Background; Claude can't ask for foreground |
| Fork mode off (`-p`, Agent SDK) | Background by default; foreground when Claude needs the result |
| `background: true` in the definition | Stays background even when Claude wants the result |
| `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1` | Foreground |

A foreground subagent blocks the conversation and passes permission prompts to you. A background one runs concurrently and surfaces prompts in your main session, naming the subagent. Background subagents get a smaller built-in tool set (Read, Grep, Glob and Bash stay, and all MCP tools stay), so `reviewer` works either way. `Ctrl+B` backgrounds a running task; `/tasks` lists them. `claude --agent reviewer` or the `agent` setting runs a whole session as the subagent.

### Limits and cost

| Topic | Behavior |
|---|---|
| Nesting | Up to three layers below the main conversation; `CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH` changes it; omit `Agent` from `tools` to stop one from spawning |
| Concurrency | 20 running subagents by default; `CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS` changes it; no cap on the session total |
| Descriptions | A warning appears when combined custom descriptions exceed 15,000 tokens |
| Cost | Each subagent sends its own requests on top of the main conversation's; a smaller `model` lowers it |
| `/agents` | As of v2.1.198 it only prints a reminder to ask Claude or edit the files |

Agent teams are a different feature: separate, experimental sessions that message each other, not helpers inside one session.

## Gotchas

- `Bash` in `tools` allows every Bash command, not only `git diff`. The prompt's "use Bash for nothing else" is advice; a hook or deny rules enforce it.
- A misspelled MCP tool name in `tools` silently resolves to nothing. If no entry resolves, the subagent usually fails to launch with an error naming them.
- The subagent can't see your conversation. Name the issue number in the request, because Claude writes the brief from your message.
- `model: opus` from an Opus session is the same model, so you don't get a second opinion. Check with `/tasks` while it runs.
- A file with no `name` or no `description` is skipped without any message in the session. Run with `--debug` to see why.
