Runnable samples for the three operational shapes of agentd — a one-shot run, an event-reactive daemon, and a polling/work-until-done loop — plus the instruction files and MCP server config they use.
Status. Implemented and released (v2.0.0). The agentic ReAct loop, the supervisor + subagent process tree, the MCP client, served self-MCP over HTTP(S), and all four run modes ship; the commands below run real agent runs (given an intelligence endpoint + MCP servers). Every flag and env var used here exists in
crates/agentd/src/config.rs(the authoritative surface).
| File | What it is |
|---|---|
instructions/triage.md |
An instruction file with an output contract — classify an inbox item, take one action, emit JSON. Used by the reactive and loop samples. |
instructions/research.md |
An instruction file with an output contract — research a topic to a single sourced answer. Used by the once sample. |
mcp-servers.json |
An illustrative MCP server config (name + remote endpoint + auth headers + tags), the shape a --config JSON file carries. |
run-once.sh |
--mode once: run the instruction to a terminal status, then exit. Job / CLI shape. |
run-reactive.sh |
--mode reactive: idle, wake on MCP resource changes, never exit on its own. Deployment shape. |
run-loop.sh |
--mode loop: re-enter on a cadence until a bound or a drain signal. Job-with-deadline / Deployment shape. |
All three scripts assume agentd is on $PATH (override with AGENTD=/path/to/agentd)
and that an intelligence endpoint is reachable. Build the binary with
cargo build --release; the binary is at target/release/agentd.
agentd ships no tools of its own and runs no local code — every tool comes from an MCP server it reaches over the network. It talks to one intelligence endpoint. So every sample needs two things wired:
-
An intelligence endpoint —
--intelligence <URI>orAGENT_INTELLIGENCE, one HTTPS URI:https://host/v1/...— a direct HTTPS endpoint (tlsfeature, on by default).http://127.0.0.1:PORT/v1— a loopback-only plaintext carve-out for a same-host TLS-terminating sidecar (dev / no-TLS image). A non-loopbackhttp://is rejected at startup (exit2).
The wire is OpenAI-compatible
/chat/completionswith native tool-calling (RFC 0006). The credential is passed by env/flag only, never read from a config file, and is redacted everywhere agentd logs:export AGENT_INTELLIGENCE=https://gw.example/v1 export AGENT_INTELLIGENCE_TOKEN=... # or --intelligence-token
-
MCP servers for the tools/resources the instruction needs — declared with the repeatable
--mcp name=<endpoint>flag (remote Streamable HTTP, RFC 0004). For example--mcp "fs=https://mcp-fs.internal/mcp". agentd connects to that URL and speaks JSON-RPC 2.0 over HTTP(S); it spawns no process. The endpoint is trusted config and is never built from model- or server-controlled strings (RFC 0012).
A bad config exits 2 in milliseconds, before any LLM round-trip — agentd
validates everything up front (e.g. --mode reactive with no --subscribe, or
an intelligence URI with an unsupported scheme, both fail fast).
Instructions are plain text passed with --instruction "<text>",
--instruction-file <path>, or the INSTRUCTION env var. A good instruction
ends with an explicit output contract so the run has a crisp terminal state
the supervisor can map to an exit code.
-
instructions/triage.md— reads the changed resource's current state (the wake notification carries only a URI, never a body — RFC 0004/0008), classifies it, takes exactly one action, and emits a single JSON object as its final message. It also treats the item's text as untrusted data, not as instructions — the right posture for anything reactive. -
instructions/research.md— gathers sources over MCP, cross-checks load-bearing claims, and emits a fixed Markdown structure (Summary/Findings/Open questions/Sources) with every claim attributed. Use it as a template: pass the concrete topic with--instructionor edit the<TOPIC>placeholder.
mcp-servers.json shows the shape of a declarative MCP server list: each server
has a name, a remote endpoint (an https://host/mcp Streamable-HTTP URL),
optional auth headers (carrying {{secret:NAME}} references resolved at connect
time, never inlined or logged), and tags that scope the Rule-of-Two trust budget
(RFC 0009/0012). Load it with --config <path> (or AGENTD_CONFIG); the
intelligence token still stays env/flag only.
Config precedence.
--configis the lowest non-default layer (default < FILE < env < flag, RFC 0017 §3). Repeatable list flags like--mcpadd to the file'smcp_servers, so a file can declare the base set and a flag can append one for a one-off run.
The --mcp flag equivalents of the sample file (agentd connects to each URL):
--mcp "fs=https://mcp-fs.internal/mcp" \
--mcp "search=https://mcp-search.internal/mcp" \
--mcp "tickets=https://mcp-tickets.internal/mcp" \
--mcp "inbox=https://mcp-inbox.internal/mcp"Run an instruction to a terminal status, then exit. This is the Job / CLI shape: result on stdout, telemetry on stderr, no daemon, no served surface.
export AGENT_INTELLIGENCE=https://gw.example/v1
export AGENT_INTELLIGENCE_TOKEN=...
./run-once.shThe script runs (abbreviated):
agentd \
--mode once \
--instruction-file instructions/research.md \
--model claude-opus-4 \
--mcp "search=https://mcp-search.internal/mcp" \
--mcp "fs=https://mcp-fs.internal/mcp" \
--max-steps 40 --max-tokens 150000 --deadline 5m \
--run-id "research-20260625-101500"The exit code maps the root subagent's terminal status: completed→0,
refused→5, budget/exhausted (steps / tokens / the run's own deadline)→7
(RFC 0007/0011). Exit 124 is reserved for the supervisor's hard-kill backstop —
a child that won't self-terminate — not the deadline terminal status itself.
Setting an explicit --run-id makes retries idempotent.
Idle at near-zero CPU; wake on notifications/resources/updated; triage the
changed item; return to idle. The daemon never exits on its own — only a
drain signal (SIGTERM) or a fatal/limit class stops it. Deploy it as a
long-lived Deployment.
export AGENT_INTELLIGENCE=https://gw.example/v1
export AGENT_INTELLIGENCE_TOKEN=...
./run-reactive.shAbbreviated:
agentd \
--mode reactive \
--instruction-file instructions/triage.md \
--model claude-opus-4 \
--mcp "inbox=https://mcp-inbox.internal/mcp" \
--mcp "tickets=https://mcp-tickets.internal/mcp" \
--subscribe "inbox:///items/new" \
--max-steps 25 --max-tokens 2000000 \
--health-file /run/agentd/health --drain-timeout 25s--mode reactive requires at least one --subscribe <uri>; without it the
config fails validation and exits 2. The token ceiling is tree-wide and
lifetime-scoped — it is the ultimate backpressure. --health-file gives an
orchestrator a liveness heartbeat to probe; --drain-timeout (default 25s)
bounds graceful shutdown and should stay under the pod's termination grace.
How reactivity works. agentd subscribes over the MCP servers' Streamable-HTTP transport and wakes on pushed
notifications/resources/updated(HTTP/SSE) — the subscribed servers are the same remote HTTP endpoints declared with--mcp(RFC 0013).
Re-enter the instruction on a cadence until a bound — max iterations (via the
step cap), the wall-clock --deadline, or the tree-wide token ceiling — or a
drain signal. The Job-with-deadline / Deployment shape.
export AGENT_INTELLIGENCE=https://gw.example/v1
export AGENT_INTELLIGENCE_TOKEN=...
./run-loop.shAbbreviated:
agentd \
--mode loop \
--interval 5m \
--instruction-file instructions/triage.md \
--model claude-opus-4 \
--mcp "inbox=https://mcp-inbox.internal/mcp" \
--mcp "tickets=https://mcp-tickets.internal/mcp" \
--max-steps 25 --max-tokens 1000000 --deadline 2h \
--drain-timeout 25s--interval D sets the re-entry cadence: D>0 polls every D; D=0
re-enters immediately on completion (work-until-done). A --deadline turns the
loop into a bounded run; omit it (and let the orchestrator own lifecycle) for a
kept-alive Deployment.
Scheduling note. For production cron, the recommended path is an external scheduler (e.g. a k8s CronJob) invoking
agentd --mode once …— robust to clock skew and restart. agentd also has a--mode schedule(per-fire identical toonce, requires--interval <dur>or--cron <expr>) for non-orchestrated deployments (RFC 0008).
agentd emits structured JSON lines on stderr (one event per line), illustrative:
{"ts":"2026-06-25T10:15:00.142Z","level":"info","event":"run.start","run_id":"research-20260625-101500","mode":"once","model":"claude-opus-4"}
{"ts":"2026-06-25T10:15:00.310Z","level":"info","event":"mcp.connect","server":"search","proto":"2025-11-25"}
{"ts":"2026-06-25T10:15:02.880Z","level":"info","event":"subagent.spawn","route":"root","depth":0}
{"ts":"2026-06-25T10:15:09.501Z","level":"info","event":"run.exit","run_id":"research-20260625-101500","status":"completed","exit_code":0}Credentials never appear in any log line — the --intelligence-token value is
redacted (***) in all agentd output, including panic messages.
Every flag below is in crates/agentd/src/config.rs; run agentd --help for the
full list. Anything env-settable (12-factor) is shown with its env var. The
neutral AGENT_* env prefix is accepted as an alias for the branded AGENTD_*
one (branded wins on conflict).
| Flag | Env | Meaning |
|---|---|---|
--instruction <TEXT> |
INSTRUCTION |
the task |
--instruction-file <PATH> |
— | read the instruction from a file |
--intelligence <URI> |
AGENT_INTELLIGENCE |
https://host/… (or loopback http://127.0.0.1:PORT for a dev sidecar) |
--intelligence-token <T> |
AGENT_INTELLIGENCE_TOKEN |
bearer / api key (redacted) |
--model <NAME> |
AGENT_MODEL |
model id |
--mcp name=<endpoint> |
— | declare a remote MCP server URL (repeatable; Streamable HTTP) |
--config <PATH> |
AGENT_CONFIG |
load a declarative JSON config file (mcp_servers[], limits, …) |
--mode once|loop|reactive|schedule |
AGENT_MODE |
the driver (default once) |
--subscribe <uri> |
— | subscribe to an MCP resource (repeatable; required for reactive) |
--interval <dur> |
— | loop/schedule cadence (e.g. 5m, 0=immediate) |
--max-steps <N> |
AGENT_MAX_STEPS |
per-run step cap (default 50) |
--max-tokens <N> |
AGENT_MAX_TOKENS |
token budget (default 200000) |
--deadline <dur> |
AGENT_DEADLINE |
wall-clock deadline (default 600s) |
--max-depth <N> |
— | subagent tree depth cap (default 4) |
--run-id <ID> |
AGENT_RUN_ID |
idempotency key (auto-generated if unset) |
--log-level <L> |
AGENT_LOG_LEVEL |
trace|debug|info|warn|error (default info) |
--drain-timeout <dur> |
AGENT_DRAIN_TIMEOUT |
graceful drain budget (default 25s) |
--health-file <PATH> |
— | liveness heartbeat file |
--serve-mcp https://host:port |
AGENT_SERVE_MCP |
serve agentd's own MCP over HTTP(S) with mTLS/bearer (serve-https; loopback http:// for dev) |
Durations accept ms / s / m / h, or a bare integer (seconds): 250ms,
30, 5m, 2h.
- All transports are HTTP(S). Intelligence, the MCP client, the served
self-MCP, and A2A / operator control are HTTP(S) with mTLS/bearer auth;
plaintext
http://is a loopback-only dev carve-out. agentd links no unix/vsock of its own. - agentd ships no tools and runs no local code. There is no
exectool; every tool comes from a remote MCP server it connects to. - Agent-authored cyclic workflows ship under
--features workflow— the model self-authors aGraphand agentd drives it (seedocs/workflows.md). - MCP
tasks/sampling/rootsas a client are (deferred), RFC 0013.