Documentation

Build agents with TOAD

A .agent file is a small, indentation-based document (2 spaces, never tabs) — a strict superset of TOON. You describe what the agent is; toac emits the how.

How it works

flowchart TD
  A["your.agent"]:::file --> S1["1 · lower<br/>prompt: | blocks become valid TOON"]
  S1 --> S2["2 · decode<br/>parsed by the real @toon-format/toon decoder"]
  S2 --> S3["3 · validate<br/>keys, types, tools, inputs.x refs → a typed agent model"]
  S3 --> S4["4 · emit"]
  S4 --> B["your.ts<br/>readable, typed TypeScript on toad-runtime, over Claude"]:::file
  classDef file fill:#0c1411,stroke:#4ade80,color:#b8f3cf,stroke-width:1.5px;

Real logic (what a tool actually does) lives in plain TypeScript, in a co-located <agent>.tools.ts.

The .agent format

Key Req Form Meaning
agent yes identifier the agent's name (also the export + filename)
model yes string a Claude model id, e.g. claude-opus-4-7
description no string one line on what it does
inputs no inputs[N]{name,type}: + N rows typed call parameters
tools no tools[N]: a,b tool names, implemented in <agent>.tools.ts
prompt yes prompt: | + block the instruction prompt
outputs no outputs[N]{name,type}: + N rows typed structured result
system no system: | + block system prompt (defaults to the description)
uses no uses[N]: a,b sub-agents wired in as tools via asTool()
maxTurns / retries no number tool-use turn cap / model-call retries

A header's count must match its rows: inputs[2]{...} has exactly two rows; tools[2]: a,b lists exactly two names.

Types

string, number, boolean, or a quoted object type like "{title:string;score:number}". Append [] for an array (string[], or "{...}[]"). Read object fields with {inputs.x.field} or, in a loop, {item.field}.

Interpolation

Inside prompt, {inputs.<name>} inserts a declared input and {env.<NAME>} inserts an environment variable (process.env.<NAME>, empty string if unset). {{ and }} are literal braces. Every reference is validated against the agent's typed inputs, with located file:line:col diagnostics.

Loops

Iterate an array input with {#each inputs.<name> as <item>}{/each}. Add a 0-based index with {#each … as <item>, <i>}, an empty-list fallback with {:else}, and destructure object elements with {#each rows as {a, b}}.

prompt: |
  Summarize these notes:
  {#each inputs.notes as note, i}
  {i}. {note}
  {:else}
  No notes provided.
  {/each}

Conditionals

Include a section based on a boolean input with {#if inputs.<flag>}{:else if inputs.<other>}{:else}{/if} (a leading ! negates).

prompt: |
  {#if inputs.detailed}
  Write a thorough analysis.
  {:else}
  Keep it brief.
  {/if}

A complete example

A kitchen-sink agent using loops, conditionals, object fields, and destructuring — all type-checked:

Try it live in the playground.

The runtime (toad-runtime)

The generated agent runs a tool-use loop over the Anthropic API with:

Structured outputdeclared outputs become a typed, validated result.
Compositionuse one agent as another's tool via uses: or asTool().
Lifecycleretries, maxTurns, and onToolCall / onToolResult / onError hooks.
Streamingagent.stream(inputs) yields text deltas.

The tool-use loop, end to end:

flowchart TD
  I["inputs (typed)"]:::file --> M["call Claude<br/>system + prompt"]
  M --> Q{"wants a tool?"}
  Q -->|yes| T["run the tool<br/>your .tools.ts"]
  T --> M
  Q -->|no| O["validate against outputs<br/>→ typed result"]:::file
  classDef file fill:#0c1411,stroke:#4ade80,color:#b8f3cf,stroke-width:1.5px;

Composition

An agent can be used as a tool by another — call asTool() and list it in the parent's tools, or skip the wiring and declare it with uses; toac imports it and calls asTool() for you.

# planner.agent
agent: planner
model: claude-opus-4-7
uses[1]: researcher
prompt: |
  Plan an article. Use the researcher tool to gather sources first.
flowchart TD
  P["planner agent"]:::file -->|"uses: researcher"| R["researcher.asTool()<br/>typed inputSchema"]
  R --> L["researcher runs its<br/>own tool-use loop"]
  L --> O["typed result<br/>returned to planner"]
  O --> P
  classDef file fill:#0c1411,stroke:#4ade80,color:#b8f3cf,stroke-width:1.5px;

Write agents with AI

TOAD's syntax is small and explicit on purpose — so an LLM can author it. Copy this prompt into Claude, describe your agent, and paste the result into the playground.

Full reference also lives in docs/authoring.md.