Custom Agents
You can create your own agents or customize the built-in ones. An agent is a markdown file with YAML frontmatter and a system prompt body, optionally paired with a behavior tree that drives its execution. This page covers the template format, where files live, inheritance, attaching a behavior tree, and wiring up tools, MCP servers, and skills.
The agent template
An agent definition is a single markdown file: YAML frontmatter on top, system prompt body below.
---
name: security-reviewer
description: Security-focused code reviewer
model: opus
extends: engineer
tools:
- +mcp__security-scanner__scan
triggeredByColumns:
- review
triggerPriority: 50
scope: worktree
---
# Security Code Review Agent
You are a security-focused code reviewer. Inspect the diff for injection
flaws, secrets, and unsafe defaults before approving.The filename must match the name field — an agent named security-reviewer lives in .agentloop/agents/security-reviewer/security-reviewer.md.
Frontmatter fields
Every field except name is optional.
| Field | Type | Description |
|---|---|---|
name | string | Unique agent identifier (kebab-case recommended). Must match the filename. |
description | string | Human-readable purpose, one or two sentences. |
model | string | LLM to use: sonnet, opus, haiku, or inherit. Defaults to inheriting from the project. |
extends | string | Parent agent name to inherit from. See Inheritance. |
tools | string[] | Tool IDs the agent may call (e.g. read, edit, bash, mcp__server__tool). |
mcpServers | string[] | MCP server names this agent connects to. |
mcpServerConfigs | object | Full MCP server definitions keyed by name ({ command, args, env }). |
mcp | object | Per-server tool instructions, keyed by server name. |
triggeredByColumns | string[] | Kanban column names that auto-trigger this agent. |
triggerCondition | string | Gate condition such as hasFrontendChanges (checks the task diff). |
triggerPredicate | string | Project-shape gate: isNodeProject, isPythonProject, etc. Applies everywhere if omitted. |
triggerPriority | number | Ordering for column triggers; lower runs earlier. Defaults to 100. |
scope | string | Execution scope: global (project root), worktree (isolated), or auto. Defaults to auto. |
supervisorMode | string | on pairs a supervisor sidecar with the agent (default); off disables it. |
instanceCount | number | How many continuous instances to run. Demand-based if omitted. |
showInLiveView | boolean | Whether the agent appears in the live view. Defaults to true. |
bt | object | Behavior-tree runtime pacing: { tickDelayMs, maxIterations }. |
The prompt body
Everything below the frontmatter is the agent’s system prompt, written in Markdown. It is rendered with template context so you can reference values such as useWorktrees, worktreesDir, and maxParallelAgents. Keep it concrete: describe the agent’s role, its constraints, and how it should use the tools you granted it.
Where agents live
User-authored agents go in your project under .agentloop/agents/:
.agentloop/agents/security-reviewer/security-reviewer.md # definition
.agentloop/agents/security-reviewer/security-reviewer.bt.json # optional behavior treeAgents are loaded from several sources and merged into one registry at startup, lowest to highest priority:
- Built-in agents bundled with AgentLoop
- Runtime overrides registered programmatically
- Project agents in
.agentloop/agents/— your highest-priority, editable source - Agent plugins
- Internal infrastructure agents (chat, orchestrator)
A project agent that shares a name with a built-in agent (for example engineer.md) completely replaces the built-in version. This is a whole-file override, not a merge. To layer on top of a built-in instead of replacing it, give your agent a new name and use extends.
Inheritance with extends
The extends field pulls in another agent as a base. Use it to build on a built-in agent without copying its whole prompt.
---
name: security-engineer
extends: engineer
tools:
- +mcp__security-scanner__scan
- +mcp__sast-linter__audit
---
Additional security hardening instructions. This text is appended after the
parent's prompt body.Merge rules when a child extends a parent:
| Field | Merge behavior |
|---|---|
tools | If any child tool is prefixed with +, those are appended to the parent’s tools (the + is stripped). If no + is present, the child’s list replaces the parent’s. |
mcpServers | Union — the parent’s servers first, then the child’s new ones. |
mcpServerConfigs | Deep-merged per server; the child’s config wins for the same key. |
mcp | Deep-merged per server; the child’s tool instructions win. |
model, description, name | The child’s value replaces the parent’s. |
| Prompt body | Appended — parent text first, then child text. |
| Other fields | The child overrides the parent. |
In the example above, security-engineer inherits all of engineer’s tools and prompt, then adds two scanner tools and extra instructions.
Two important caveats:
extendsworks only between agents with different names. A same-name project file is a full replacement, not an inheritance base.- Behavior trees are never merged. If both the parent and child have a
.bt.json, only the child’s tree is used and the parent’s is ignored. Inheritance applies to the markdown definition, not the behavior tree.
Inheritance depth is capped (circular extends chains are detected and rejected), and the parent must exist in the merged registry.
Attaching a behavior tree
A behavior tree defines the agent’s execution logic — when it calls tools, how it reacts to results, and what it does next. It is an optional .bt.json file co-located with the agent definition and named after the agent.
{
"name": "security-engineer-tree",
"description": "Implement, then run a security pass before committing",
"version": "1.0.0",
"mode": "reactive",
"tree": {
"type": "root",
"child": {
"type": "sequence",
"children": []
}
}
}If a .bt.json is present, it loads automatically when the agent runs. If absent, the agent runs LLM-only — a single call with no orchestrated loop. For the full node vocabulary and authoring guidance, see Behavior Trees and Behavior Tree JSON.
Tools, MCP, and skills
The tools field is the agent’s allowlist. List built-in tool IDs (such as read, edit, write, bash) and any MCP tools using their full mcp__<server>__<tool> IDs. When extending another agent, prefix entries with + to add to the inherited set rather than replace it.
Connect MCP servers with mcpServers (names) and, if the server is not already configured project-wide, mcpServerConfigs to supply the command, args, and env. Use the mcp field to attach per-tool instructions, mark tools required or deprecated, and point at alternatives.
Skills are managed separately from the agent file — they are enabled per agent rather than declared in frontmatter. See Skills for how to enable and scope them.
Syncing a built-in template
To start from a built-in agent rather than a blank file, copy its template into your project with the sync_agent_template tool:
{ "agentType": "engineer", "target": "project" }This copies the agent’s .md (and .bt.json if present) into .agentloop/agents/, and updates any active worktree copies. Sync when you want to customize a built-in agent, pick up improvements after upgrading AgentLoop, or vendor a standardized copy into git. You can also skip syncing entirely and author a brand-new agent from scratch.
Project agents are discovered and merged at daemon startup, so no manual sync step is needed for agents you author yourself.
Agent definitions belong in .agentloop/agents/, not in .agentloop/config.yaml. The config file holds orchestrator and integration settings — see Configuration.