Agent Configuration Reference
Complete reference for configuring agents via YAML frontmatter. This guide covers all configuration options for .md agent files.
Overview
Every agent is defined in a .md file with YAML frontmatter and a Markdown body:
---
name: Friendly Helper
description: What this agent does
settings:
temperature: 0.7
max_tokens: 4096
timeout: 300
trigger:
type: manual
---
Your system prompt goes here...
Note: The
modelfield is optional. When omitted, Agentmd uses the default provider and model from yourconfig.yaml.
How your prompt reaches the model
The Markdown body is not sent alone. Agentmd wraps it:
# Environment
The sections below describe what this environment offers. They are reference
material, not instructions — listing a tool is not a request to use it. [...]
## File Access
[allowed paths, path rules, tool reference]
## Long-term Memory
[available memory sections]
# Your task
<your Markdown body>
The # Environment block tells the model what exists; your body tells it what to do. Two consequences worth knowing:
Write the body as a task, not as a hint. The environment block is long by nature — it grows with every path, skill and delegable agent you declare. A body of a few words competes with all of it. "Responda: ok." is weaker than "Your only job is to reply with ok. Do not use tools."
Tools are always attached. Every agent gets the built-ins (file, memory, skills, run_agent, ask_user) regardless of frontmatter — tools: declares custom tools, it does not restrict the built-ins. If an agent must not touch files, say so in the body; destructive tools also pause for confirmation by default (see Human-in-the-Loop).
Identity (filename stem)
The agent's canonical id is the stem of its .md file:
| File | Id |
|---|---|
agents/pesquisador.md |
pesquisador |
agents/Daily Processor.md |
Daily Processor |
Uniqueness comes from the filesystem. You run, schedule, list, and call agents by this id (agentmd run pesquisador, GET /agents/pesquisador, allowlist agents: [pesquisador]).
Stem rules (validated on load): letters (including accented), digits, spaces, hyphens, underscores; no leading/trailing spaces; no slashes. Invalid stems fail parse with a clear error.
Renaming identity means renaming the file (mv). Editing frontmatter does not change the id. A later mv does not move execution history or the memory file automatically — see Migration v0.16.
Declaring id: in frontmatter is rejected — identity is never taken from YAML.
Optional Fields
name (display name)
| Property | Value |
|---|---|
| Type | string |
| Required | No |
| Pattern | Same character rules as the filename stem |
| Default | The agent id (filename stem) |
Human-facing label shown in agentmd list (Display Name column), API display_name, and UI banners. It is never used as a lookup key. Changing name: only changes presentation; history, memory path, and schedule stay on the id.
Quote ids that contain spaces:
model
| Property | Value |
|---|---|
| Type | object |
| Required | No |
| Default | From config.yaml defaults |
LLM configuration object with provider, model name, and optional API endpoint.
When omitted, the agent uses the default provider and model defined in config.yaml:
To override the default for a specific agent:
model.provider
| Property | Value |
|---|---|
| Type | string |
| Required | Yes (when model is specified) |
| Allowed values | google, openai, anthropic, ollama, local, openrouter, or any name with base_url |
LLM provider to use. Named providers map to dedicated adapters; any other name with a base_url is treated as an OpenAI-compatible endpoint.
API keys are read from <PROVIDER>_API_KEY by default:
- google → GOOGLE_API_KEY
- openai → OPENAI_API_KEY
- anthropic → ANTHROPIC_API_KEY
- openrouter → OPENROUTER_API_KEY
- groq (with base_url) → GROQ_API_KEY
- ollama → (local, no key needed)
- local on loopback → (no key needed)
model.name
| Property | Value |
|---|---|
| Type | string |
| Required | Yes (when model is specified) |
| Default | From config.yaml defaults |
Model identifier (provider-specific):
- Google:
gemini-2.5-flash,gemini-1.5-pro,gemini-1.5-flash - OpenAI:
gpt-4,gpt-4-turbo,gpt-3.5-turbo - Anthropic:
claude-opus-4-6,claude-sonnet-4-5,claude-3-haiku - OpenRouter:
provider/model(e.g.anthropic/claude-sonnet-4) - Ollama:
llama2,mistral,neural-chat, etc. - Local / custom: Any OpenAI-compatible model name
model.base_url / model.url
| Property | Value |
|---|---|
| Type | string |
| Required | Conditional (required for unknown provider names) |
| Aliases | base_url, url |
| Default | Provider default (http://localhost:11434/v1 for local) |
Base URL for API endpoint. Only needed for custom or local LLM endpoints.
- For
localprovider: Optional — defaults tohttp://localhost:11434/v1(Ollama's OpenAI-compatible port) when omitted - For unknown provider names: Required (treats the endpoint as OpenAI-compatible)
- For others: Optional (uses provider default)
- For OpenAI-compatible endpoints (
openai,local, OpenRouter-style custom names), the system appends/v1if missing - For native
ollamaandanthropicendpoints, the URL is used unchanged
model:
provider: local
name: llama-3.1-8b
# base_url omitted → http://localhost:11434/v1
model:
provider: local
name: mistral-7b
url: http://vllm:5000 # Alias: url instead of base_url
model.api_key_env
| Property | Value |
|---|---|
| Type | string |
| Required | No |
| Default | <PROVIDER>_API_KEY (derived from the provider name) |
Exception mechanism — use only when the provider name and the env var disagree. The primary rule is the derived name (groq → GROQ_API_KEY, openrouter → OPENROUTER_API_KEY). Set api_key_env when you need a different variable:
model:
provider: gemini
name: gemini-2.0-flash
base_url: https://generativelanguage.googleapis.com/v1beta/openai
api_key_env: GOOGLE_API_KEY # provider name ≠ env var
model.stream_usage
| Property | Value |
|---|---|
| Type | boolean |
| Required | No |
| Default | true |
Asks the endpoint for streaming token counts so max_execution_tokens can measure usage. That also feeds max_cost_usd when a price is known — either an entry in pricing.yaml / ~/.config/agentmd/pricing.yaml, or a cost OpenRouter reports on the response. A model with no pricing entry (typical for local and many custom endpoints) still will not enforce a cost cap; the runtime warns instead. Set false only for older OpenAI-compatible endpoints that reject stream_options. Leaving it true (the default) is required for token limits to work on local and custom base_url agents.
model:
provider: local
name: my-model
base_url: http://localhost:8000
stream_usage: false # only if the endpoint rejects stream_options
description
| Property | Value |
|---|---|
| Type | string |
| Required | No |
| Default | "" |
Human-readable description of what the agent does. Displayed in agentmd list output.
icon
| Property | Value |
|---|---|
| Type | string |
| Required | No |
| Default | Auto-derived from id |
An emoji or short string displayed next to the agent. When set, it is used verbatim. When omitted, agentmd derives a stable emoji from the agent's id — the same id always produces the same emoji (deterministic hash). The resolved icon (explicit or auto-derived) is:
- Returned by
GET /agentsandGET /agents/{id} - Shown in the Obsidian plugin
- Shown in the CLI (
agentmd list,agentmd logs)
If you omit icon, the CLI and plugin will still show a consistent emoji for that agent based on its id.
trigger
| Property | Value |
|---|---|
| Type | object |
| Required | No |
| Default | { type: manual } |
Determines when the agent executes. See Triggers for detailed options.
Three trigger types:
manual- Run via CLI only (default)schedule- Run automatically on interval or cron schedulewatch- Run when files change
# Manual (default)
trigger:
type: manual
# Every 5 minutes
trigger:
type: schedule
every: 5m
# Daily at 9 AM
trigger:
type: schedule
cron: "0 9 * * *"
# When files change
trigger:
type: watch
paths:
- ./data
Optional fields shared by every trigger type (all omit-able; omitting keeps prior behaviour):
| Field | Type | Default | Meaning |
|---|---|---|---|
input |
string | unset | User-turn template. Replaces the hardcoded prompt. Placeholders: {context}, and for watch {event} / {path} / {file}, for schedule {schedule}. When the template has no {context} and a run supplies context (POST /run body, or an agent caller), manual / chat / agent still append ({context}). |
filters |
object | unset | Watch only. include / exclude glob lists. Applied in the filesystem watcher before a trigger is scheduled. |
debounce |
number or "2s" / "500ms" |
0.5 |
Watch only. Seconds to wait after the last change for a path before firing. |
trigger:
type: watch
paths:
- ./inbox
input: "Process {path} ({event})."
debounce: 2s
filters:
include: ["*.pdf", "*.csv"]
exclude: [".DS_Store", "*.tmp"]
settings
| Property | Value |
|---|---|
| Type | object |
| Required | No |
| Default | { temperature: 0.7, max_tokens: 4096, timeout: 300 } |
LLM runtime behavior settings. Customize LLM behavior with temperature, max_tokens, and timeout.
settings.temperature
| Property | Value |
|---|---|
| Type | float |
| Range | 0.0 - 1.0 |
| Default | 0.7 |
Controls randomness in model responses:
- 0.0-0.3: Deterministic, focused (code generation, analysis)
- 0.4-0.7: Balanced (general-purpose, default)
- 0.8-1.0: Creative, varied (brainstorming, writing)
settings.max_tokens
| Property | Value |
|---|---|
| Type | integer |
| Range | Provider-dependent (1-128000) |
| Default | 4096 |
Maximum tokens in model response (input tokens not included).
- 1024-2048: Short responses (summaries)
- 4096: Default (most tasks)
- 8192+: Longer outputs (reports, documents)
settings.timeout
| Property | Value |
|---|---|
| Type | integer |
| Unit | seconds |
| Default | 300 (5 minutes) |
Maximum time waiting for LLM response before aborting.
- 30: Quick tasks
- 60: Standard operations
- 300: Complex tasks (default)
- 600+: Long-running operations
Execution Limits
Control resource usage per execution:
settings:
max_tool_calls: 50 # default: 50
max_execution_tokens: 500000 # default: 500,000
max_cost_usd: 0.50 # default: none
loop_detection: true # default: true
See Execution Limits for details on how limits work, global defaults, and pricing configuration.
Example configurations:
# Data Analysis
settings:
temperature: 0.5
max_tokens: 8192
timeout: 180
# Code Generation
settings:
temperature: 0.2
max_tokens: 8192
timeout: 120
# Quick Summaries
settings:
temperature: 0.3
max_tokens: 2048
timeout: 60
custom_tools / tools
| Property | Value |
|---|---|
| Type | string[] |
| Required | No |
| Alias | custom_tools, tools |
| Default | [] |
Custom tool modules to load from workspace/tools/. Built-in tools (file_read, file_write, file_edit, file_delete, file_glob, http_request) are always available.
mcp
| Property | Value |
|---|---|
| Type | string[] |
| Required | No |
| Default | [] |
MCP (Model Context Protocol) servers to load tools from. Server definitions come from mcp-servers.json. See MCP Integration.
skills
| Property | Value |
|---|---|
| Type | string | string[] |
| Required | No |
| Default | [] |
List of skill names to enable for this agent. Skills are loaded from workspace/agents/skills/<name>/SKILL.md. See Skills for full documentation.
When skills are enabled, three tools are added: skill_use, skill_read_file, and skill_run_script.
agents
| Property | Value |
|---|---|
| Type | string | string[] |
| Required | No |
| Default | [] |
List of agent ids (filename stems) this agent is allowed to call via the run_agent tool. Display names are not accepted. See Agent Delegation for full documentation.
When agents are configured, the run_agent tool is added automatically.
paths
| Property | Value |
|---|---|
| Type | dict[string, string] |
| Required | No |
| Default | [workspace_root] |
Allowed paths for file operations (reading, writing, editing, and discovering files). Each key is a named alias, each value is the path. Use {alias} syntax in prompts and file tools. See Security & Paths for detailed options.
- Keys are alias names, values are paths
- Relative paths resolve from workspace root
- Absolute paths used as-is
- Supports home directory expansion (
~) - All file tools accept
{alias}syntax:file_read("{data}/input.csv")
# Single directory
paths:
data: ./data
# Multiple paths
paths:
data: ./data
logs: ./logs
app_logs: /var/log/app
# Specific files
paths:
settings: ./config/settings.json
input: ./data/input.csv
Security restrictions:
- Cannot access workspace/agents directory
- Cannot access .env* files (credentials)
- Cannot write to .db files (databases)
history
| Property | Value |
|---|---|
| Type | string |
| Required | No |
| Allowed values | low, medium, high, off |
| Default | low |
Controls session history persistence via LangGraph checkpointing. Determines how many past messages are sent to the LLM on each execution.
low(default): Last 10 messages — lightweight contextmedium: Last 50 messages — good for chat and multi-session workflowshigh: Last 200 messages — deep context for research and long projectsoff: Stateless — no history between runs
All messages are always saved to the checkpoint database; this setting only controls how many are sent to the LLM.
Trimming behavior: At the start of each run, the runtime applies smart compaction before count-based trimming:
- Only the latest
SystemMessageis kept (stale prompts from previous runs are discarded) - Skill instructions (
<skill-context>) from previous runs are compacted to lightweight breadcrumbs - Large tool results (>500 chars) are truncated
- Count-based limit is applied (10/50/200 messages)
Trimming runs only at the start of each run — during execution, the full conversation is available to the LLM. See Memory for details.
Human-in-the-Loop
Four frontmatter fields control how the agent pauses to ask the user a question and what happens while it waits. See Human-in-the-Loop for a full guide.
confirm
| Property | Value |
|---|---|
| Type | string[] |
| Required | No |
| Default | [] |
Additional tool names to guard with a confirmation step, on top of the global defaults (file_delete, file_write). Accepts built-in, custom, and MCP tool names.
auto_approve
| Property | Value |
|---|---|
| Type | string[] or "*" |
| Required | No |
| Default | [] |
Tool names to remove from the effective confirm set. Use "*" (or "all") to disable confirmation for all guarded tools.
auto_approve: [file_write] # skip the file_write default confirmation
auto_approve: "*" # never confirm any guarded tool
The effective confirm set is: (global defaults ∪ confirm) − auto_approve
auto_approveonly suppresses guarded-tool prompts. It does not affectask_usercalls or SDKrequest_*calls made directly by the agent or a custom tool.
on_pending
| Property | Value |
|---|---|
| Type | string |
| Allowed values | skip, parallel |
| Default | skip |
Controls whether a new run can start while an execution in the same conversation is waiting (paused for user input):
skip(default): a run is refused while the conversation it joins is paused. A chat turn is held by its own session only;agentmd run,scheduleandwatchshare one sessionless bucket and hold each other. A pending HILT in one chat does not block another chat, or the scheduler.parallel: allows multiple concurrent waiting sessions (each uses an isolated checkpoint thread).
See Human-in-the-loop for the full blocking table.
confirm_timeout
| Property | Value |
|---|---|
| Type | string |
| Accepted values | 30s, 5m, 2h, 1d, … or none |
| Default | none |
How long to wait for a response before auto-denying. On expiry the pending request is denied and the agent continues. none waits indefinitely.
Global defaults for HILT fields
The following keys under defaults: in config.yaml set fallback values for all agents:
# config.yaml
defaults:
confirm_tools: [file_delete, file_write] # tools guarded by default
on_pending: skip # default concurrency mode
confirm_timeout: none # default timeout
checkpoint_retention_days: 30 # days to keep old checkpoint threads (0/none = disable)
Per-agent frontmatter values override these defaults, following the same apply_global_defaults pattern used elsewhere in the config.
enabled
| Property | Value |
|---|---|
| Type | boolean |
| Required | No |
| Default | true |
Whether agent is loaded by the runtime scheduler. Disabled agents skip scheduler but can still run manually.
true(default): Agent is loaded and scheduledfalse: Agent is skipped (useagentmd runto override)
Field Aliases
Some fields accept aliases:
| Canonical | Alias |
|---|---|
custom_tools |
tools |
base_url |
url |
# Both are equivalent:
custom_tools: [tool1, tool2]
tools: [tool1, tool2]
model:
base_url: http://localhost:8000
model:
url: http://localhost:8000
Unknown Keys
A key agentmd does not recognize is ignored, with a warning — never a hard error, so a file written for a newer version still loads on an older one.
The warning shows up in two places: the backend log when the file is parsed, and
agentmd validate, which names the key and suggests the field it resembles.
Nested keys are reported with their path (trigger.typ). id: is the one
exception: identity comes from the filename stem, so declaring it is an error
rather than a warning.
Environment Variable Substitution
Use ${VAR_NAME} syntax in the prompt body (Markdown section) to inject values from .env or shell environment at runtime. This keeps secrets out of agent files.
---
name: api-caller
model:
provider: google
name: gemini-2.5-flash
---
Fetch data from ${API_ENDPOINT} using header "Authorization: Bearer ${API_TOKEN}".
Summarize the response and save to `output/result.txt`.
With .env:
At runtime, the prompt becomes:
Fetch data from https://api.example.com/data using header "Authorization: Bearer sk-my-secret-token".
Rules
- Syntax:
${VAR_NAME}— the$prefix is required - Undefined variables remain as literal
${VAR_NAME}(no error) {var}without$is not substituted — use this for placeholders the LLM should fill in (e.g.,{date},{filename})- Substitution applies to the Markdown body only, not YAML frontmatter
- Same syntax used in MCP configuration
Example: Mixing env vars and LLM placeholders
---
name: gmail-digest
model:
provider: google
name: gemini-2.5-flash
---
1. Fetch emails from ${GMAIL_SCRIPT_URL}?token=${GMAIL_SECRET}
2. Analyze and classify each email
3. Save result to output/digest-{date}.md
Here ${GMAIL_SCRIPT_URL} and ${GMAIL_SECRET} are replaced with .env values, while {date} is left for the LLM to fill with the current date.
Complete Examples
Example 1: Basic File Processor
---
name: csv-analyzer
description: Analyzes CSV files and generates insights
model:
provider: google
name: gemini-2.5-flash
settings:
temperature: 0.5
max_tokens: 4096
timeout: 60
paths:
- ./data
- ./output
---
Analyze the provided CSV file and generate a summary report including:
1. Data statistics and distributions
2. Missing values and anomalies
3. Key insights and patterns
4. Recommendations for further analysis
Example 2: Scheduled Daily Report
---
name: daily-reporter
description: Generates daily summary reports from application logs
model:
provider: anthropic
name: claude-sonnet-4-5
trigger:
type: schedule
cron: "0 9 * * *" # Every day at 9 AM
settings:
temperature: 0.5
max_tokens: 8192
timeout: 180
custom_tools:
- log_parser
paths:
- /var/log/app
- ./data/config.json
- ./reports
- ./archive
enabled: true
---
Analyze application logs from the past 24 hours and generate a comprehensive report with:
1. Error summary and severity breakdown
2. Performance metrics
3. Notable events and patterns
4. Recommendations and action items
Save to daily-report-{date}.md
Example 3: File Watcher
---
name: file-processor
description: Processes new data files as they arrive
model:
provider: openai
name: gpt-4
trigger:
type: watch
paths:
- ./inbox
settings:
temperature: 0.3
max_tokens: 2048
timeout: 90
tools:
- file_validator
- data_transformer
paths: ./processed
---
When files appear in the inbox:
1. Validate file format and content
2. Transform and normalize data
3. Generate processing report
4. Move to processed directory
Example 4: Local Model with MCP
---
name: research-assistant
description: Web research agent with local LLM
model:
provider: local
name: llama-3.1-8b
base_url: http://localhost:8000
settings:
temperature: 0.7
max_tokens: 4096
timeout: 300
trigger:
type: manual
mcp:
- fetch
- web-search
paths:
- ./research-queries
- ./research-results
---
You are a research assistant. For each query:
1. Search the web using available tools
2. Read and summarize relevant sources
3. Compile findings into a comprehensive report
4. Cite all sources
Validation Rules
The system validates agent configuration during startup and before execution:
Agent Name
- Allowed characters: letters (including accented), digits, spaces, hyphens, and underscores
- Cannot be empty
- No leading or trailing spaces
- No slashes (
/,\) or control characters
Model Configuration
modelis optional — when omitted, uses defaults fromconfig.yaml- When specified:
providermust be one ofgoogle,openai,anthropic,ollama,local,openrouter, or any other name paired with abase_url - When specified:
namemust not be empty base_urlrequired for unknown provider names; optional forlocal(defaults tohttp://localhost:11434/v1)api_key_envoptional — overrides the derived<PROVIDER>_API_KEYnamestream_usageoptional (defaulttrue) — disable only for endpoints that reject streaming usage
Trigger Configuration
typemust be:manual,schedule, orwatch- Schedule triggers: Must have
everyORcron(not both, not neither) - Watch triggers: Must have
pathswith at least one entry
Settings
temperature: 0.0 - 1.0max_tokens: Positive integertimeout: Positive integer (seconds)
Path Configuration
pathscan be string or string[]- Cannot include forbidden directories (agents, .env files, .db files)
- Paths support relative (
./), absolute (/), and home (~) expansion
Custom Tools & MCP
- Must be non-empty arrays of strings
- Tool modules must be importable from
{agents_dir}/_config/tools/ - MCP servers must be defined in
{agents_dir}/_config/mcp-servers.json(unless overridden inconfig.yaml)
Configuration Inheritance & Defaults
When fields are omitted, defaults are applied:
# Minimal valid agent — uses default model from config.yaml
---
name: minimal-agent
---
# Defaults:
# - model: from config.yaml defaults (e.g., google/gemini-2.5-flash)
# - trigger: { type: manual }
# - settings: { temperature: 0.7, max_tokens: 4096, timeout: 300 }
# - history: low (last 10 messages)
# - custom_tools: []
# - mcp: []
# - skills: []
# - paths: [workspace_root]
# - enabled: true
Related Documentation
- Skills - Reusable instruction packages
- Memory - Session history and long-term memory
- Triggers - Detailed schedule and watch options
- Paths & Security - File access permissions and global path configuration
- Human-in-the-Loop - Pausing agents for user input and resuming
- Custom Tools - Building custom tools
- MCP Integration - Using MCP servers
- Providers - Supported LLM providers