Tools: Overview
Tools are the agent’s hands. A tool is a capability the model can invoke mid-conversation, run a shell command, fetch an HTTP URL, open a browser, write a file, read a sensor. Every tool call is subject to security policy. Successful executions can include a tool receipt when receipts are enabled.
Tools are not to be confused with zeroclaw CLI subcommands. CLI commands are for operators; tools are for the agent.
An agent gets its tools through the skill, knowledge, and MCP bundles it references; see Agents for how bundles attach to an agent. For the turn-level path from provider tool call to approval, dispatch, receipt, observer event, and history entry, see Tool execution lifecycle.
Before adding a built-in tool or replacing one with an external integration, use the Built-In Tool Inventory to choose the smallest durable home. Working built-in integrations stay available until a replacement is real, documented, and independently reviewed; that replacement-first rule is the accepted RFC #6165 policy, recorded in the inventory’s Replacement-First Policy section.
Minimal and full tools
Configurations use full unless they select minimal: every built-in carried
by this binary and allowed by its existing configuration, prerequisites and
permission rules. Setup and config generation write optional = ["*"]. A file
without [tools], or a [tools] section without optional, also loads full,
so upgrading to v0.8.6 keeps existing tool availability. Existing explicit
selections are preserved.
[tools]
optional = ["*"] # full; the default
Minimal is opt-in: eleven built-ins in Chat, or eight in Code/ACP, which
retains its exclusion of persistent-memory tools. Chat’s eleven are shell,
file_read, file_write, file_edit, glob_search, content_search,
memory_recall, memory_store, memory_forget, web_fetch, and
git_operations. Existing policy can narrow either catalog.
[tools]
optional = [] # minimal
For minimal plus selected extras, use their callable names:
[tools]
optional = ["calculator", "cron_list", "sessions_history"]
The wildcard is a union with named entries: ["*", "calculator"] has the same
selection as ["*"], with no duplicate tool. Full is not a fixed tool count;
configured integrations, runtime capabilities and policy determine the effective
catalog. It does not enable every integration. Selecting a tool does not install
a browser or vendor CLI, supply credentials, enable a disabled integration or
grant permission. Full also preserves Code/ACP’s existing memory exclusion.
Selection happens before constructors run. An unselected tool contributes no schema or tool-catalog prompt text. Risk profiles, caller narrowing, approval, path/network policy and receipts still apply. Explicit MCP servers, skills, plugins and peripherals retain their existing activation and authority paths. Progressive schema disclosure is a separate feature, not part of these settings.
To select minimal, set optional = [] in [tools], or run:
zeroclaw config set tools.optional '[]' --no-interactive
Reload the daemon after changing selection. Config schema stays version 3.
zeroclaw config migrate records optional = ["*"] for an existing file
without [tools]; ordinary saves also retain selector intent.
Builds and upgrade path
| Channel | Compiled capability | Runtime selection |
|---|---|---|
| Release archive | Standard dist includes the native tools-compat adapters | Full by default; minimal and named extras work without another download |
| Desktop sidecar | The same target-resolved dist CLI, plus requested desktop features | The same minimal/full settings and prerequisites |
| Homebrew source build | Depends on the formula’s feature selection; a Cargo-default build omits native adapters | Full includes only the adapters the formula carries; use the standard source build below when it omits them |
Docker dist image | Standard dist, including native adapters | Baked configs omit [tools] and load full, the default; set optional = [] for minimal |
One release matrix has ten target legs, with unchanged archive names. Android is
experimental and allowed to fail; publication requires the other nine targets.
There is no second compatibility archive matrix or dedicated compat-tools
image. The historical all-features Docker image remains broader than dist,
including additional channels and hardware features.
dist includes tools-compat (tools-saas, tools-coding-cli, tools-external).
dist-compat is a compatibility alias for the same build. Plain Cargo defaults
remain smaller: custom source builds can omit adapters. A compiled-out external
adapter named in optional gets a clear diagnostic; full selects only the
adapters this binary carries and reports nothing for the rest. Enabled
vendor/CLI sections retain their existing missing-feature diagnostic. Runtime
configuration cannot add compiled-out code. Build the standard selection to
recover it:
# From a source checkout; use the target you intend to run.
TARGET=x86_64-unknown-linux-gnu
FEATURES="$(cargo run --quiet --locked -p xtask --bin generate -- features --selection dist --target "$TARGET")"
cargo build --release --locked --bin zeroclaw --target "$TARGET" --no-default-features --features "$FEATURES"
# For a local Linux image, resolve features for its build target.
docker build -t zeroclaw-dist-local .
For a Homebrew installation whose formula omits adapters, run the standard
source-built binary alongside the package-managed binary and use its own
config, data and service paths. A config setting cannot change a bottle’s
compiled features. Source users can also select individual existing tool-*
features or tools-compat; they still need the runtime selection.
Standard dist includes the plugin host and plugins-wasm-cranelift on the seven supported native
64-bit targets: GNU and musl Linux on x86_64 and aarch64, both macOS architectures,
and x86_64 Windows MSVC. ARMv6/ARMv7 omit Cranelift and Prometheus; experimental
Android omits Cranelift and WhatsApp Web. Cargo defaults do not include a plugin
host. Compiling the host leaves plugins.enabled and plugins.auto_discover
false; consent, trust, grants and configured activation still apply. Runtime-only
precompiled .cwasm support and Pulley do not replace portable registry .wasm
compilation. Target policy comes from package.metadata.zeroclaw in Cargo.toml.
Minimal is a runtime catalog selection. Standard dist still carries the
compiled native adapters, so minimal alone is not binary-size evidence for
the tool extraction work.
Container, Kubernetes and Nix defaults omit [tools] and load full like any
other configuration; set optional = [] in them for minimal.
Built-in tools
The following built-in capabilities are available when compiled and selected, which full does for all of them; the eleven-tool minimal set is listed above. This list is not the core set; see Tiers and the retained core set for the core set and the tier of each tool the runtime registry builds.
| Tool | What it does |
|---|---|
shell | Execute a shell command in the workspace directory. Subject to command allow/deny lists |
file_read | Read a file with line numbers; supports partial reads and base64 encoding for binary files (path must be inside the workspace unless autonomy permits otherwise) |
file_write | Write a file (same path constraint) |
file_edit | Replace an exact string match in a file with new content |
glob_search | List files matching a glob pattern within the workspace |
content_search | Search file contents by regex within the workspace (ripgrep with grep fallback) |
http_request | HTTP GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS to allowlisted domains |
web_search_tool | Web search. Provider is configurable: DuckDuckGo (default, no key), Brave, Tavily, SearXNG, Jina, Bocha, AnySearch, Serply, or Keenable (no key required; optional key lifts rate limits) |
web_fetch | Fetch a page and return clean plain text |
browser | Headless-browser automation. Opt-in: requires [browser] automation_enabled = true. See Browser automation |
memory_recall | Search long-term memory for relevant facts, preferences, or context |
memory_store | Store a fact, preference, or note in long-term memory |
ask_user | Send a question to the active channel and wait for a reply. Supports optional choices for structured responses (inline keyboard on Telegram, numbered list on CLI). On ACP, choices are required: free-form ask awaits the ACP elicitation RFD. Parameters: question (required), choices (optional list), timeout_secs (default 600). |
escalate_to_human | Send a structured escalation message with urgency routing. high / critical urgency additionally notifies any channels listed in [escalation] alert_channels. Parameters: summary (required), context (optional), urgency (low/medium/high/critical, default medium), wait_for_response (bool, default false), timeout_secs (default 600). On ACP, wait_for_response: true fails immediately if the channel cannot receive free-form replies (awaits ACP elicitation RFD). |
AnySearch provider
AnySearch is an explicit, opt-in backend for web_search_tool:
[web_search]
search_provider = "anysearch"
# Optional; omit to use AnySearch's lower, rate-limited anonymous quota.
anysearch_api_key = "..."
Search queries and the configured result limit are sent to
https://api.anysearch.com/v1/search. When anysearch_api_key is configured,
ZeroClaw sends it only as a Bearer authorization header; without a key, no
Authorization header is sent. Selecting this provider therefore sends search
queries to a third-party service even in anonymous mode. It does not change the
default provider and is not used as an automatic fallback.
Additional first-party built-ins, included in full but not in minimal:
| Tool | Notes |
|---|---|
cron_* | Manage scheduled jobs: cron_add, cron_list, cron_remove, cron_update, cron_run, cron_runs |
schedule | Shell-only one-shot/recurring scheduling |
memory_export, memory_purge | Long-term memory management |
spawn_subagent, delegate | Run a subtask in a child agent |
Selected tools also retain these prerequisites:
| Tool | Enabled by |
|---|---|
knowledge | [knowledge].enabled = true. Stores structured relationship memory; see Relationship memory |
| Hardware probes | --features hardware: GPIO reads/writes, device discovery, firmware flashing |
sop_* tools | Registered when the SOP runtime is enabled (sop.sops_dir set to a non-empty value; unset by default, which disables it; the documented value is shared/sops): run and inspect SOPs |
discord_search | Registered when a Discord alias has archive enabled |
Built-in schema discovery
Enable deferred_builtin_tools on the agent’s runtime profile through the ZeroCode Config editor to reduce the built-in tool catalog sent to the model. It defaults to false, which preserves eager schema loading. The setting is independent of MCP’s deferred_loading option.
When enabled, permitted shell, file_read, file_write, file_edit, memory_recall, and memory_store schemas remain visible. Other enabled built-ins appear in a compact discovery list. The model uses tool_search with keywords or select:<tool_name> to retrieve full definitions. Selected schemas appear in subsequent native-tool requests; text providers receive the definition in the search result.
Discovery never grants permission or changes tool approval. Agent policy, principal restrictions, and caller allowlists still apply, including after a session narrows its tools. Built-in executables stay in the existing scoped registry, so memory routing does not retain a separate stale tool instance. If policy does not admit tool_search, remaining built-in schemas stay eager. Tool-listing endpoints still list the complete admitted registry.
Extension protocols
Beyond built-in tools, ZeroClaw supports the MCP (Model Context Protocol) extension surface. Connect any MCP server (Claude Code’s filesystem, Playwright, your own) and the agent picks up its tools at startup.
For IDE-side integration where an editor drives ZeroClaw as a subprocess, see ACP: Agent Client Protocol lives under channels since it’s an inbound session-management surface, not a tool the agent invokes.
Authoring a tool
Implement the Tool trait in zeroclaw-api:
#![allow(unused)]
fn main() {
#[async_trait]
pub trait Tool: Send + Sync + Attributable {
fn name(&self) -> &str;
fn description(&self) -> &str;
fn parameters_schema(&self) -> serde_json::Value; // JSON Schema for args
async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult>;
}
}
Every Tool is also Attributable, so a tool call’s log emissions and audit traces carry the same <kind>.<alias> attribution the rest of the runtime uses.
Register via the runtime’s tool factory. See Developing → Plugin protocol for the full pattern.
Describing tools to the model
Tool descriptions are Mozilla Fluent strings: one per tool, localised per locale. This keeps tool descriptions terse in the model’s context window while allowing UI localisation.
Source of truth: crates/zeroclaw-runtime/locales/en/tools.ftl. Translations are generated and maintained via cargo fluent fill --locale <code> (see Maintainers → Docs & Translations).
Risk and approval
Every tool invocation is classified by risk:
- Low (read-only, no side effects):
file_read,memory_recall,http_request GETto allowed domains - Medium (mutates local state):
file_write,shellwith known safe commands - High (destructive or remote side effects):
shellwith unknown commands,http_request POSTto unconstrained URLs
The autonomy level determines what each risk tier can do without operator approval. Default (Supervised): low runs, medium asks, high blocks.
When receipts are enabled, successful executions receive a tool receipt. Denied, blocked, replaced, failed, or interrupted calls do not receive receipts.
Disabling tools on non-CLI channels
The schema has no per-channel tools_allow / tools_deny field. Tool gating lives on the agent’s risk profile ([risk_profiles.<alias>]):
excluded_toolsremoves the listed tools from every non-CLI channel (Discord, Telegram, Bluesky, Matrix, Slack, etc.) while leaving the local CLI untouched. The granularity is binary (CLI vs non-CLI), not per-channel. It also subtracts from the agentic-delegate allow-list resolved at runtime, which is the only way to block individual<server>__<tool>MCP names that would otherwise be auto-admitted by the rule below.allowed_toolsis an allowlist. Omitted and an explicit empty list (allowed_tools = []) mean the same legacy state: no authorization constraint. A nonempty list is a closed set for built-ins (and, via the MCP exception below, auto-admits namespaced MCP tools). An empty list does not mean deny-all; for that, set the siblingdeny_all_tools = true, which denies built-ins, MCP tools, and skill-defined tools alike. Setting bothdeny_all_tools = trueand a nonemptyallowed_toolsis a configuration error rejected at load.- MCP exception: when
allowed_toolsis non-empty, runtime-discovered MCP tools (any name containing__, the<server>__<tool>convention) are auto-admitted into the effective allow-list without having to be listed there individually. This keeps the post-#7464 eager-MCP default usable for agents that already pin an explicit allow-list. To block individual MCP tools, list them inexcluded_tools. - The MCP exception is scoped to the risk profile’s
allowed_toolsonly. Caller-supplied per-run allow-lists (cron joballowed_tools, narrowed delegate invocations, etc.) are still treated as strict explicit-list intersections. A job that narrows itself toallowed_tools = ["cron_add"]will not surface runtime-discovered MCP wrappers it did not name, even when the agent’s risk profile would auto-admit them.
If you need finer-grained gating under Full autonomy, put sensitive tools in the per-profile always_ask list: they still prompt (or fail closed) even when level = "full". Dropping the profile to read_only or supervised is only required when you want the whole risk-tier matrix, not when you need a handful of exceptions.
See Autonomy levels for the full set of per-profile fields.