Skip to content

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 model field is optional. When omitted, Agentmd uses the default provider and model from your config.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.

# File: agents/pesquisador.md  →  id = pesquisador
name: Research Bot
agentmd run pesquisador          # id
# agentmd run "Research Bot"   # fails — display names are not aliases

Quote ids that contain spaces:

agentmd run "Daily Processor"

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:

# config.yaml
defaults:
  provider: google
  model: gemini-2.5-flash

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: - googleGOOGLE_API_KEY - openaiOPENAI_API_KEY - anthropicANTHROPIC_API_KEY - openrouterOPENROUTER_API_KEY - groq (with base_url) → GROQ_API_KEY - ollama → (local, no key needed) - local on loopback → (no key needed)

model:
  provider: google

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:
  provider: openai
  name: gpt-4

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 local provider: Optional — defaults to http://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 /v1 if missing
  • For native ollama and anthropic endpoints, 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 (groqGROQ_API_KEY, openrouterOPENROUTER_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.

description: Analyzes CSV files and generates statistical reports

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 /agents and GET /agents/{id}
  • Shown in the Obsidian plugin
  • Shown in the CLI (agentmd list, agentmd logs)
icon: "📅"

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:

  1. manual - Run via CLI only (default)
  2. schedule - Run automatically on interval or cron schedule
  3. watch - 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:
  temperature: 0.2  # Precise code generation

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:
  max_tokens: 8192  # Comprehensive reports

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
settings:
  timeout: 120  # Allow 2 minutes

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.

custom_tools:
  - my_custom_tool
  - another_tool

# Or alias:
tools:
  - email_sender
  - database_client

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.

mcp:
  - fetch
  - filesystem
  - web-search

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.

skills:
  - analyze-pr
  - generate-report

# Or single skill:
skills: review-code

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.

agents:
  - web-researcher
  - summarizer

# Or single agent:
agents: summarizer

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 context
  • medium: Last 50 messages — good for chat and multi-session workflows
  • high: Last 200 messages — deep context for research and long projects
  • off: 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:

  1. Only the latest SystemMessage is kept (stale prompts from previous runs are discarded)
  2. Skill instructions (<skill-context>) from previous runs are compacted to lightweight breadcrumbs
  3. Large tool results (>500 chars) are truncated
  4. 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.

# Chat agent with extended history
history: medium

# Stateless one-shot agent
history: off

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.

confirm: [file_edit, send_email]

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_approve only suppresses guarded-tool prompts. It does not affect ask_user calls or SDK request_* 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, schedule and watch share 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.

on_pending: parallel

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.

confirm_timeout: 1h   # auto-deny after 1 hour

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 scheduled
  • false: Agent is skipped (use agentmd run to override)
enabled: false  # Won't run on schedule, but can run manually

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.

---
triger:            # typo: parsed, ignored, agent stays `manual`
  type: schedule
  every: 1h
---

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.

  Frontmatter
    ⚠ unknown frontmatter key 'triger' (ignored) — did you mean 'trigger'?

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:

API_ENDPOINT=https://api.example.com/data
API_TOKEN=sk-my-secret-token

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

  • model is optional — when omitted, uses defaults from config.yaml
  • When specified: provider must be one of google, openai, anthropic, ollama, local, openrouter, or any other name paired with a base_url
  • When specified: name must not be empty
  • base_url required for unknown provider names; optional for local (defaults to http://localhost:11434/v1)
  • api_key_env optional — overrides the derived <PROVIDER>_API_KEY name
  • stream_usage optional (default true) — disable only for endpoints that reject streaming usage

Trigger Configuration

  • type must be: manual, schedule, or watch
  • Schedule triggers: Must have every OR cron (not both, not neither)
  • Watch triggers: Must have paths with at least one entry

Settings

  • temperature: 0.0 - 1.0
  • max_tokens: Positive integer
  • timeout: Positive integer (seconds)

Path Configuration

  • paths can 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 in config.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