TOAD — Token-Oriented Agentic Development. A compile-first framework for building AI agents.
You describe an agent in a small, token-efficient .agent file, and the toac
compiler turns it into readable, fully-typed TypeScript that runs on Claude. The
syntax is derived from TOON (Token-Oriented
Object Notation), so an agent reads like compact data rather than boilerplate —
cheap to write and review for humans and for the LLMs that increasingly author
agents.
🐸 Live site + in-browser playground: https://zubeidhendricks.github.io/toad/
The name spells out the idea:
- Token-Oriented — agents are written in TOON-derived syntax that uses far fewer tokens than JSON and hands a model an explicit schema to follow.
- Agentic Development — defining, compiling, type-checking, and running agents is the whole workflow.
researcher.agent:
agent: researcher
model: claude-opus-4-7
description: Research a topic and return a sourced summary.
inputs[1]{name,type}:
topic,string
tools[2]: web_search,fetch_page
prompt: |
You are a research analyst. Research: {inputs.topic}
Use web_search to find sources, then fetch_page to read them.
Return a cited summary.
outputs[2]{name,type}:
summary,string
sources,string[]
toac build researcher.agent emits a typed researcher.ts exporting a runnable
agent. Tool bodies live in a co-located researcher.tools.ts.
A .agent file is a strict superset of TOON. Inside prompt: you get a small,
type-checked template language:
| Construct | Example |
|---|---|
| Interpolation | {inputs.topic} |
| Environment | {env.API_BASE} |
| Object fields | {inputs.user.name} |
| Loops | {#each inputs.items as x, i}{i}. {x}{/each} |
| Empty fallback | {#each xs as x}…{:else}none{/each} |
| Destructuring | {#each rows as {title, score}}…{/each} |
| Conditionals | {#if inputs.verbose}…{:else if inputs.brief}…{:else}…{/if} |
| Literal braces | {{ and }} |
| Optional inputs | detail?,string (omitted → empty / empty list / false) |
| Enum types | verdict,approve|reject → literal union + z.enum |
Types are string / number / boolean, a quoted object like
"{a:string;b:number}", and any of those with []. Every reference is validated
against the agent's typed inputs, with rustc/Elm-style diagnostics — a code frame
under the offending span plus did you mean? suggestions:
error[TOA202]: unknown key "promt"
--> researcher.agent:4:1
|
4 | promt: |
| ^^^^^ did you mean `prompt`?
|
Tools can be bare names (tools[2]: web_search,fetch_page) — the body in
<agent>.tools.ts owns its schema — or typed, where the .agent file owns
the input schema and the body supplies only run:
tools[2]{name,input}:
web_search,"{query:string}"
fetch_page,"{url:string}"
toac then generates the Zod schema and a typed defineTool, so
<agent>.tools.ts is just export const web_search = (i: WebSearchInput) => …,
type-checked against the declared input.
The generated agent runs a tool-use loop over the Anthropic API with:
- Structured output — declared
outputsbecome a typed, validated result. - Composition — use one agent as another's tool (
uses:oragent.asTool()); the parent's cancellation reaches the sub-agent, andasTool({ onUsage })rolls a sub-agent's token usage up into the parent. Any call also takes per-callhooks(merged over the configured ones) viarun(inputs, { hooks }). - Lifecycle —
retries,maxTurns, andonToolCall/onToolResult/onErrorhooks. - Sessions —
agent.session(inputs)keeps multi-turn conversation history (tool calls included), with typed results per send;session.stateis a JSON-serializable snapshot you can persist and resume. - Cancellation & timeouts —
run/send/streamtake anAbortSignal(forwarded to the API call and to tools);toolTimeoutMs/defineTool({ timeoutMs })time-box tool execution. - Streaming —
agent.stream(inputs)yields text deltas;agent.runStream(inputs)streams the whole tool loop as typed events (textdeltas,tool_use/tool_result,usage, and a finaldonecarrying the typed output). - MCP export —
serveMcp([agent])(fromtoad-runtime/mcp) exposes compiled agents as Model Context Protocol tools over stdio, so any MCP client (Claude Desktop, Claude Code, …) can call them. Each agent's declaredinputsbecomes the tool's input schema; the result is returned as text andstructuredContent. - Token accounting — the
onUsagehook reports per-call and cumulative usage, including prompt-cache reads/writes; same-turn tool calls run concurrently. TheonContexthook attributes each call's input tokens across system / tools / history — so you can see history dominate a long loop, which the provider's totals don't break down. (toac costestimates the same fixed prefix statically, before you run.) - Context budgeting — set
maxContextTokens(config or.agentkey) to cap the conversation: when a turn's estimated context exceeds it, the oldest tool results are elided (oldest first, pairing preserved, the current turn kept), so long loops don't grow unbounded — the single biggest recurring cost. Mark a toolephemeralto drop its (one-shot) result on later turns regardless. - Token-efficient tool results — set
toolResultFormat: "auto"to feed tool results back to the model as TOON instead of JSON when it saves tokens (~30–50% on tabular results), so multi-turn loops stay cheap. Defaults to"json";"toon"always encodes. TheonToolResultEncodedhook reports the tokens saved per result, so you can log "saved N tokens this run". Addfields: [...]to a tool to project its result to just the keys the model needs (volume) before encoding (format) — the two compound. - TOON inputs — object/array values interpolated into a prompt render as
TOON automatically, not
[object Object].
toad-compiler— thetoaccompiler (.agent→.ts).toad-runtime—defineTool,createAgent, the tool loop, and the above.
The .agent format is specified in SPEC.md — versioned and
normative, so other tools can target it. Proposals welcome as issues/PRs.
A VS Code extension with full .agent syntax highlighting lives in
editors/vscode (the same TextMate grammar powers the
site's code blocks).
# the compiler + `toac` CLI
npm i -g toad-compiler
# the runtime your compiled agents import
npm i toad-runtime @anthropic-ai/sdkScaffold, format, and compile an agent:
toac init researcher # → researcher.agent + researcher.tools.ts
toac fmt researcher.agent # canonical formatting (use --check in CI)
toac cost researcher.agent # estimate the per-turn token footprint
toac build researcher.agent # → researcher.ts (typed TypeScript)toac lsp runs a standalone Language Server (stdio): the same diagnostics,
hovers, completions, and formatting in any LSP editor — Neovim, Helix, Zed,
Emacs, JetBrains. Setup per editor is in the ecosystem guide.
toac fmt is the canonical formatter — like gofmt/rustfmt. It reorders keys to
the spec's schema order and normalizes indentation, spacing, and blank lines,
while preserving prompt/system block content exactly (it re-parses its own output
and refuses to write if the meaning would change).
pnpm install
pnpm typecheck && pnpm test && pnpm lint && pnpm buildCompile an agent:
node packages/compiler/dist/bin.js build examples/researcher/researcher.agentSee examples/researcher for a complete, type-checked
example, and docs/authoring.md for the full format plus a
copy-paste prompt that turns any LLM into a TOAD author.
214 passing tests, green gate (typecheck · test · lint · build). Design docs live
in _bmad-output/.
