Skip to content

Custom Agents

Build custom agents with frontmatter, lifecycle, and tools

13 min read

You write a prompt so specific it only applies to one workflow — security review of Dockerfiles. Pasting it into every claude -p call is tedious and error-prone. You need a reusable agent you can invoke by name, with its own tool restrictions and permissions baked in.

Custom agents are specialized AI assistants defined as Markdown files with YAML frontmatter. Each agent runs in its own context window with a custom system prompt, tool restrictions, and independent permissions. You can scope them to a project, share them across all your work, or define them inline for one-off use.

Agent File Format

An agent file is a Markdown document where the YAML frontmatter defines the agent’s identity and capabilities, and the Markdown body becomes the agent’s system prompt. Save it to .claude/agents/ for project-level access or ~/.claude/agents/ for user-level access.

---
name: code-reviewer
description: Expert code review specialist. Use proactively after code changes.
tools: Read, Grep, Glob, Bash
model: sonnet
---
You are a senior code reviewer ensuring high standards of code quality.
When invoked:
1. Run git diff to see recent changes
2. Focus on modified files
3. Begin review immediately
Review checklist:
- Code is clear and readable
- No duplicated code
- Proper error handling
- No exposed secrets
- Input validation implemented

Frontmatter Fields Reference

FieldRequiredDescription
nameYesUnique identifier (lowercase, hyphens)
descriptionYesWhen Claude should delegate to this agent — vague descriptions cause inconsistent delegation
toolsNoAllowed tools (inherits all if omitted)
disallowedToolsNoTools to explicitly deny
modelNosonnet, opus, haiku, inherit, or a full model ID
permissionModeNodefault, acceptEdits, dontAsk, bypassPermissions, plan, auto
maxTurnsNoMaximum agentic turns before the agent stops
memoryNoPersistent memory scope: user, project, or local
mcpServersNoMCP servers scoped to this agent
hooksNoLifecycle hooks scoped to this agent
backgroundNotrue to always run as a background task
isolationNoworktree for git worktree isolation
Try This

Create a minimal custom agent right now. Create the file .claude/agents/summarizer.md with:

---
name: summarizer
description: Summarize files in 3 bullet points
tools: [“Read”, “Glob”]
---
You are a code summarizer. Read the requested file and return exactly 3 bullet points summarizing what it does.

Test it by running claude /agents summarizer and asking it to summarize a file in your project.

Built-in Agents

Claude Code ships with five built-in agents. You cannot edit these, but you can override them by creating an agent file with the same name.

Built-in Agents

AgentModelToolsPurpose
ExplorehaikuRead-onlyFast codebase exploration and search when a simple Glob or Grep is not enough
PlaninheritRead-onlyResearch agent for plan mode — reads files and reports findings without making changes
general-purposeinheritAll toolsHandles complex multi-step tasks that require reading, writing, and running commands
claude-code-guidehaikuRead-onlyAnswers questions about Claude Code features and usage
statusline-setupsonnetRead, EditConfigures status line display settings

Custom Agent Creation

Create a Markdown file in one of two directories depending on the scope you need:

  • Project-level: .claude/agents/code-reviewer.md — commit to git so your team shares the agent
  • User-level: ~/.claude/agents/code-reviewer.md — available in every project on your machine

The filename (minus the .md extension) becomes the agent name, so code-reviewer.md creates an agent named code-reviewer. The name field in frontmatter must match.

When names conflict, the resolution order is:

Agent Name Priority

PrioritySourceExample
1 (highest)—agents CLI flag—agents ’{“reviewer”: {…}}‘
2.claude/agents/ (project).claude/agents/reviewer.md
3~/.claude/agents/ (user)~/.claude/agents/reviewer.md
4 (lowest)Plugin agentsInstalled via plugins

Inline Agents

For quick testing or CI/CD pipelines, define agents inline with the --agents flag instead of creating files. The flag accepts a JSON object where each key is an agent name and each value defines the agent’s configuration:

Terminal window
claude --agents '{
"code-reviewer": {
"description": "Expert code reviewer. Use proactively after code changes.",
"prompt": "You are a senior code reviewer. Focus on quality, security, and best practices.",
"tools": ["Read", "Grep", "Glob", "Bash"],
"model": "sonnet"
},
"debugger": {
"description": "Debugging specialist for errors and test failures.",
"prompt": "You are an expert debugger. Analyze errors, identify root causes, and provide fixes."
}
}'

The JSON fields mirror the YAML frontmatter fields, with one difference: use prompt instead of the Markdown body for the system prompt. Inline agents are session-scoped and are not saved to disk.

To run an entire session as a specific agent, use --agent (singular):

Terminal window
claude -p "Review this code" \
--agents '{"reviewer": {"description": "Code reviewer", "prompt": "You are a strict code reviewer. Always start with REVIEWER:"}}' \
--agent reviewer \
--output-format json

The agent’s system prompt replaces the default Claude Code system prompt entirely. CLAUDE.md files still load normally.

Agent(type) Tool

Within a running session, Claude can spawn subagents using the Agent tool. You do not call this tool directly — you ask Claude to delegate work to an agent by name:

Use the code-reviewer subagent to review the auth module

Or with an @-mention:

@"code-reviewer (agent)" look at the auth changes

You can restrict which agents a coordinator can spawn using the Agent(type) syntax in the tools frontmatter field:

---
name: coordinator
description: Coordinates work across specialized agents
tools: Agent(worker, researcher), Read, Bash
---

Only the worker and researcher agents can be spawned from this coordinator. Using Agent without parentheses allows spawning any available agent.

Key constraints for subagents:

  • Subagents cannot spawn other subagents — no nesting is allowed
  • Subagents get their own system prompt, not the full Claude Code prompt
  • Foreground subagents block the main conversation until complete; permission prompts pass through
  • Background subagents run concurrently with permissions pre-approved upfront; press Ctrl+B to background a running task

claude agents Command

Run claude agents to see all configured agents, both built-in and custom:

claude agents
$ claude agents
5 active agents
Built-in agents:
claude-code-guide · haiku
Explore · haiku
general-purpose · inherit
Plan · inherit
statusline-setup · sonnet

In interactive mode, use the /agents slash command to create, edit, and delete agents without leaving your session. New agents created this way are available immediately without restarting.

Gotcha

Agent files are Markdown — they look like documentation but act like configuration. The YAML frontmatter defines the agent’s identity, tools, and model, while the Markdown body becomes the system prompt verbatim. If you edit the prose, you are changing how the agent behaves, not just its description.

Gotcha

The built-in Explore and claude-code-guide agents default to the haiku model for speed and cost. Haiku is roughly 20x cheaper than Opus, which makes it ideal for fast exploration tasks. However, if you override a built-in agent with a custom file and omit the model field, it will inherit the session’s model — potentially running on Opus at significantly higher cost.

The --brief Flag

The --brief flag modifies Claude’s system prompt to produce more concise responses. Despite documentation suggesting it enables the SendUserMessage tool, testing confirms it does not add any new tools to the session.

What --brief actually does:

  • Modifies the system prompt — confirmed by different cache creation tokens (9,245 new tokens vs 0 for the standard prompt)
  • Does NOT enable SendUserMessage — the stream-json init event shows the same tool list with or without --brief
  • Does NOT change output format — JSON and stream-json output structure is identical
  • Compatible with agents — works alongside --agent and --agents without conflict
Terminal window
# With --brief (different system prompt cached)
claude -p "Summarize this file" --brief --output-format json
# Output: same structure, same fields, potentially shorter result text
# Without --brief (standard system prompt)
claude -p "Summarize this file" --output-format json
# Output: same structure, result may be more verbose
Note

The —brief flag’s effect is primarily visible in interactive mode where it produces shorter status messages and more concise responses. In headless -p mode with simple prompts, output is indistinguishable from the non-brief version. The flag is safe to use and does not break any functionality.

Agent Selection with --agent

The --agent flag (singular) selects a named agent for the entire session. It discovers agents from three sources in priority order:

  1. CLI inline--agents '{"name": {...}}' (highest priority)
  2. Project agents.claude/agents/name.md
  3. User agents~/.claude/agents/name.md
Terminal window
# Select a file-based agent
claude -p "Review this code" --agent code-reviewer --output-format json
# Select an inline agent
claude -p "Review this code" \
--agents '{"reviewer": {"description": "...", "prompt": "..."}}' \
--agent reviewer --output-format json
# List all discoverable agents
claude agents

The claude agents command shows all available agents grouped by source:

6 active agents
Project agents:
code-reviewer · sonnet
Built-in agents:
claude-code-guide · haiku
Explore · haiku
general-purpose · inherit
Plan · inherit
statusline-setup · sonnet
Gotcha

Invalid agent names are silently ignored. If you pass —agent nonexistent, Claude falls back to the default system prompt with no error or warning. In CI/CD, verify agent loading by checking for expected markers in the output (e.g., a prefix like REVIEWER: that your agent’s prompt always produces).

Now Do This

Create .claude/agents/reviewer.md with a system prompt for code review and tools: [“Read”, “Glob”, “Grep”]. Give it read-only access. Test it on your last commit — you now have a reusable review agent scoped to your project.