Tools¶
pkg/agent/tool/ defines the tool framework an agent uses to take action
— interfaces, the registry, middleware (logging, retry, idempotency,
schema-validation), human-input plumbing, and interrupt support.
pkg/agent/builtin/ is the catalogue of ready-to-use tools the
framework ships with — helix.shell.exec, helix.fs.read,
helix.web.fetch, the helix.* developer helpers, and more — that
agents reference from YAML as type: function tools.
What it does¶
A tool, to the agent, is a JSON-shaped function the LLM can call:
type Tool interface {
Info() *ToolInfo // name, description, JSON Schema, categories
Invoke(ctx context.Context, arguments string) (string, error)
}
Two variants extend the base interface for richer protocols:
type StreamableTool interface {
Tool
StreamInvoke(ctx context.Context, arguments string) (*llm.StreamReader, error)
}
type MultimodalTool interface {
Tool
InvokeMultimodal(ctx context.Context, arguments string) ([]llm.ContentPart, error)
}
The agent calls Info().ToDefinition() to build the llm.ToolDefinition
passed to the model with WithTools, then calls Invoke on whichever
tool the LLM picks. Tools are stored on the agent in a Registry
(thread-safe map). FuncTool[T] wraps a typed Go function (auto-unmarshals
args into T); SimpleTool wraps a function that takes a raw JSON string.
Middleware family¶
Middleware = func(Tool) Tool — compose cross-cutting concerns
around any tool without modifying it:
WithLogging()— slog Add / Invoke / result logging.WithRetry(policy)— configurable retry (MaxAttempts,Backoffexponential/linear/constant,RetryOnclauses fortransienterrors andhttp_statuslists).NewTransient(err, after)wraps an error as retryable with aRetryAfterhint. HTTP errors can be classified viaClassifyHTTPError.WithIdempotency(policy)— caches results by key extractor + scope (run/session/global) in a pluggableIdempotentStore(in-memory ships built-in). Eliminates duplicate work when the LLM re-issues the same call.WithResultSchema(schema)— JSON-Schema-validates the tool's STRING result against a small subset of JSON Schema (top-level type, required, properties.type, enum). Fails fast on malformed results.
Interrupt + human input¶
tool.Interrupt(info)— return this error from inside a tool to pause the agent's run cleanly. The agent surfaces an*InterruptErrorto the caller, who can checkpoint and resume via theRunner.tool.WithHumanInput(ctx, fn)/tool.RequestHumanInput(ctx, req)— a tool can ask the human for input mid-execution. The agent'sOnToolHumanInputcallback is plumbed onto the context for the run.
Tool definitions in YAML¶
AgentDefinition.Tools is a list of ToolDefinition:
tools:
- name: read-file
type: function # function | pipeline | mcp | a2a | connector
function: fs.read # builtin function ID
description: Read a file from disk
- name: query-db
type: pipeline
pipeline_id: sql-query
description: Query the application database
Tool types:
function— bind to apkg/agent/builtinfunction by ID.pipeline— invoke a Helix route bypipeline_id. The agent sends the args as the exchange body and reads the route's output back; useful for "the tool IS itself a Helix flow".mcp— call an external MCP server tool (pkg/component/mcp).a2a— call an external A2A agent (pkg/component/a2a).connector— invoke a registered connector (pkg/connector/connectors/all).
Built-in tool catalogue¶
pkg/agent/builtin/ registers a global catalogue. Reference any of
these IDs from YAML as type: function + function: <id>.
File system (fs)¶
| ID | Description |
|---|---|
fs.read |
Read a file. Optional offset (start line, 0-based) and limit (line count). |
fs.write |
Write content to a file; creates the path, overwrites if it exists. |
fs.edit |
Find-and-replace text in a file. Requires old_string to match exactly; replace_all: true allowed when multiple matches exist. |
fs.list |
List files / directories. recursive: true descends; auto-skips node_modules, __pycache__, target, vendor, hidden dirs. Result capped at 500 entries. |
fs.glob |
Find files matching a glob (e.g. *.go). Result capped at 200 matches. |
fs.grep |
Text search across files. include glob filter, max_results (default 50). Skips binaries (.exe, .bin, .so, …). |
fs.mkdir |
Create a directory and its parents. |
fs.delete |
Delete a file or empty directory. |
All fs.* tools respect SetAllowedPaths([...]) — empty (default) =
no restriction; non-empty = paths must be under one of the allowed
base directories. The working directory is honored from
ShellConfig.WorkDir.
Shell (shell)¶
| ID | Description |
|---|---|
shell.exec |
Execute a shell command and return combined stdout + stderr. Used for builds, tests, git commands. |
Web (web)¶
| ID | Description |
|---|---|
web.fetch |
HTTP GET / POST with optional headers and body. Returns the response body. |
Geo (geo)¶
Pure-Go geospatial helpers backed by pkg/geo (the same core as the geo:// component and the sl::gis Starlang module). Points are [lon, lat] (WGS84); geometries are WKT or GeoJSON.
| ID | Description |
|---|---|
geo.distance |
Great-circle distance in meters between two [lon,lat] points. |
geo.bearing |
Initial compass bearing in degrees (0–360, clockwise from north) from point a to point b. |
geo.geohash |
Encode a [lon,lat] point as a geohash string. precision is the character count (default 7, ~150 m cells). |
geo.within |
Report whether a [lon,lat] point lies inside an area (a Polygon/MultiPolygon as WKT or GeoJSON). |
geo.centroid |
Return the centroid of a geometry (WKT or GeoJSON) as a GeoJSON Point. |
geo.area |
Geodesic area of a polygonal geometry (WKT or GeoJSON) in square meters. |
geo.parse |
Parse a geometry (WKT or GeoJSON) and return it in the requested form. to=geojson (default) or to=wkt. |
Data + math (data, math)¶
| ID | Description |
|---|---|
data.json_parse |
Parse a JSON string; returns a formatted version. Validates structure. |
data.json_format |
Pretty-print a JSON string with indentation. |
data.csv_parse |
Parse CSV text into JSON records (first row treated as headers). |
math.eval |
Evaluate +, -, *, /, parentheses. |
XLSX (xlsx)¶
Constant-memory, paged readers for .xlsx workbooks — built on the same
streaming row iterator as the xlsx:// component's
stream=true mode. Use these for agent-driven inspection of large
spreadsheets (page a window of rows on demand, without loading the file);
use the xlsx:// component for route-based bulk ingestion. Unlike
fs.read (which is line-based and loads the whole file), these decode the
workbook and stream rows.
| ID | Description |
|---|---|
xlsx.read_rows |
Read a bounded page of rows from one sheet (file, sheet, offset, limit, has_header). Streams row-by-row, so paging deep into a million-row sheet stays constant-memory. Returns rows keyed by header plus has_more for pagination. |
xlsx.list_sheets |
List a workbook's sheet names in order — discover which sheets to page with xlsx.read_rows. |
CSV (csv)¶
Constant-memory, paged reader for delimited-text (CSV/TSV) files — the same
encoding/csv streaming core as the csv://
component's stream=true mode. Prefer it over fs.read for CSV: fs.read
loads the whole file and returns raw text, while this pages parsed records.
| ID | Description |
|---|---|
csv.read_rows |
Read a bounded page of rows from a CSV/TSV file (file, offset, limit, has_header, delimiter). Streams row-by-row, so paging deep into a million-row file stays constant-memory. Returns rows keyed by header plus has_more for pagination. |
Time (time)¶
| ID | Description |
|---|---|
time.now |
Current UTC time. Default RFC3339; pass a Go layout to override. |
time.now_in_tz |
Current time in a named IANA timezone (e.g. America/New_York). |
Helix developer tooling (helix)¶
Designed for the Helix Studio "build me a flow" workflow.
| ID | Description |
|---|---|
helix.validate |
Run helix validate against a flow.yaml / .json and return stdout+stderr. |
helix.run |
Run a flow in the foreground with a short timeout (default 10s). Returns combined output and exit code. |
helix.test |
Run declarative helix test cases. Deterministic tests need no LLM key. |
helix.docs_search |
Substring search across docs/ Markdown. |
helix.examples_search |
Substring search across examples/ YAML/JSON. |
helix.init |
Scaffold a Helix project via helix create <name> — agents/, tools/, pipelines/, routes/, mcps/, prompts/, etc. |
helix.schema |
Return the authoritative Helix DSL schema (workflow / step / agent / tool / transform / test / rag). |
helix.check_env |
Walk YAML and report every ${ENV_VAR} reference, marking which are set / missing. |
helix.write_env_template |
Generate .env.example listing every referenced env var. |
helix.explain_error |
Diagnose a Helix runtime / validation error against known patterns; returns CAUSE / FIX / WHERE / NEXT. |
helix.propose_architecture |
Read a spec / PRD and emit a structured planning outline (sections, requirement IDs, component layout). |
helix.track_requirements |
Cross-reference FR-/NFR-/REQ- IDs in a spec against the project's flow files. |
helix.write_workflow |
Emit a guaranteed-valid workflow YAML from structured args. Auto-validates. |
helix.write_app |
Assemble a runnable app.yaml by merging agent / workflow / tool YAMLs. Auto-discovers files. |
helix.write_agent |
Emit a guaranteed-valid agent definition YAML. |
helix.write_test |
Emit a deterministic test case under tests/<name>.yaml, deriving input.uri from the referenced workflow. Auto-runs helix test. |
helix.write_pipeline |
Emit a guaranteed-valid pipeline YAML (including RAG ingest/retrieve). |
helix.fix_starlang |
Lint + autofix a Starlang transform expression (Python-dict literals, missing body = read(payload, ...), etc.). |
helix.write_rag_ingest |
Emit a complete rag-ingest → rag-chunk → rag-embed → rag-store pipeline with dimension alignment. |
helix.write_rag_retrieve |
Emit a complete rag-embed → rag-retrieve → rag-synthesize pipeline. |
The full registry is enumerated via
builtin.Global().List() and serialised by builtin.ListJSON()
(used by the Studio Designer dropdown).
Skill tools (registered when an agent has skills)¶
When skills: is present, the framework injects three more tools
via the SkillToolset (pkg/agent/skilltoolset):
list_skills— list available skill names + descriptions.load_skill— load a skill's full instructions.load_skill_resource— read a file under a skill'sreferences/,assets/, orscripts/directories.
See skills.
Configuration¶
AgentDefinition.Tools per-entry shape (full schema in
pkg/workflow/definition.go):
| Field | Type | Description |
|---|---|---|
name |
string | Name exposed to the LLM. |
type |
string | function / pipeline / mcp / a2a / connector. |
function |
string | (for type: function) builtin ID. |
pipeline_id |
string | (for type: pipeline) route to invoke. |
description |
string | What the tool does — the LLM reads this to decide when to call. |
parameters |
block | JSON Schema for the args. Inferred from the builtin when omitted. |
categories |
list | Policy labels (payment, pii, external_write) — approval_categories: can refer to these. |
retry |
block | Per-tool override of tool_retry. |
idempotent |
block | { enabled, scope, ttl } — wraps the tool in WithIdempotency. |
Behavior notes¶
- Tool categories are coarse policy labels.
ApprovalCategoriesmatches them;Tool.Info().HasCategory(cat)is the runtime check. RequiresApprovalflag (legacy) is honored whenpolicy: tagged, but the category-based approach is preferred — it scales without having to mark every sensitive tool individually.- The middleware family is also exposed at the YAML level:
tool_retry:on the agent, plusidempotent:andretry:per tool definition. Hook order is fixed by the reifier so behaviour is deterministic across reloads. - For full developer ergonomics in Studio, every
helix.*tool has aLabel(Designer dropdown) and richDescription(what the LLM sees) — they're the same struct.
Example¶
A minimal coder agent with shell + filesystem access:
agents:
- id: coder
type: react
model: { provider: anthropic, model: claude-sonnet-4-7 }
system_prompt: |
You are an expert Go developer working in the user's repo.
tools:
- { name: read-file, type: function, function: fs.read }
- { name: write-file, type: function, function: fs.write }
- { name: edit-file, type: function, function: fs.edit }
- { name: list-dir, type: function, function: fs.list }
- { name: grep, type: function, function: fs.grep }
- { name: shell, type: function, function: shell.exec,
categories: [external_write] }
approval_policy: tagged
approval_categories: [external_write]
Source¶
pkg/agent/tool/tool.go— interfaces,FuncTool/SimpleTool,Registry,WithLogging.pkg/agent/tool/retry.go—RetryPolicy, backoffs,WithRetry,ClassifyHTTPError.pkg/agent/tool/idempotent.go—IdempotentPolicy,MemoryIdempotentStore,WithIdempotency.pkg/agent/tool/schema.go—WithResultSchema, JSON-Schema lite validator.pkg/agent/tool/interrupt.go—tool.Interrupt,*InterruptError.pkg/agent/tool/human_input.go—RequestHumanInput,WithHumanInput.pkg/agent/tool/context.go—WithRunID,WithSessionID.pkg/agent/builtin/registry.go—BuiltinFunc, global registry.pkg/agent/builtin/{fs,shell,web,data,time,helix,helix_write,helix_env,helix_arch,helix_debug}.go— the registered functions.
See also¶
- react, supervisor — how the loop invokes tools.
- skills —
list_skills/load_skill/load_skill_resourceand progressive disclosure. - guardrails —
tool_argsandtool_resultguard stages. - memory — the other agent state subsystem.