Skip to main content
Drop a YAML file in ./.graph/tools/ (or ~/.config/graph/tools/ for tools you want everywhere) and it becomes user__<name> — callable by the agent, referenced in plan steps, invocable from the CLI. Three kinds.

exec — wrap any command

Unparseable json output, non-zero exit (with stderr captured), and timeouts all return as structured tool errors.
Exec tools are arbitrary code execution, by design — you author them, graph runs them. Treat the tools directory like you treat your shell profile.

prompt — an LLM call as a tool

With an output_schema, the result is validated JSON; without one, {"text": …}. Useful as a cheap sub-task inside plans — classify, extract, reword — with model controlling cost: a role name (chat default, with the usual fallback to default) or a named model like nano. An unknown name fails the call with the configured names listed.

reshape — project data into a new shape

A reshape tool renders its shape — a JSON tree whose leaf strings are templates — with the same typed splice as step inputs: an exact-tag leaf keeps the source value’s type, mixed text interpolates to a string. No process, no LLM, no side effects, so it’s read_only by default. It moves data — rename, pick, nest, flatten — but is logic-less, so it can’t derive values (sums, casing, conditionals); use exec for those. The shape renders against the tool’s own input root, so a fixed doc shape references its fields as {{input.*}}. Inside a plan the pipeline renders the step input first (against item, E0…, input), so a shape authored as {{item.number}} reaches the tool already resolved and passes through unchanged — one effective render either way, like a map’s over. Set caller_shape: true instead of a fixed shape to take the shape from each call’s shape input — the generic builtin__reshape path, where the planner authors the mapping per step referencing the surrounding step’s roots. Fixed-shape leaves are validated at load time and rendered by the tool against its own input; a caller shape is rendered once, by the pipeline, as part of the calling step’s input (a bad path fails that render), and the tool returns it verbatim — so substituted text containing {{ … }} is never re-parsed as a template. One of shape or caller_shape is required.

Built-in packs

The same YAML format also powers built-in tools: packs compiled into the binary and served under the builtin__ namespace (enable with [tools] packs = ["github"]). To customize one, copy its YAML into a tools directory and reference your user__ copy from plans.

Shared behavior

  • input_schema validates before dispatch — missing fields return actionable errors (in chat, the agent asks and retries).
  • output_schema, when declared, feeds the planner’s shape knowledge just like an MCP output schema.
  • Names must match [a-zA-Z0-9_-]+; templates referencing anything but {{input.*}} are load-time errors.