Toolbox
Toolbox tools are simple executables that describe themselves and accept structured input. They are easier than running an MCP server and safer than letting the model improvise a shell command every time.
Toolbox Directories
Section titled “Toolbox Directories”Tell Anode where to find toolbox executables.
Environment variable:
export ANODE_TOOLBOX="/home/user/tools:/home/user/project/.tools"The value is an OS-native path list (colon-separated on Unix).
Config file:
{ "tools": { "toolboxDirs": [ "/home/user/tools", ".tools" ] }}Anode scans these directories at startup. Earlier directories win on name conflicts.
Protocol
Section titled “Protocol”Toolbox tools communicate through environment variables and stdio. Anode calls each tool twice during its lifecycle: once to describe, once to execute.
Describe
Section titled “Describe”Anode sets TOOLBOX_ACTION=describe and runs the executable. The tool prints JSON metadata to stdout:
{ "name": "run-tests", "description": "Run the project test suite with optional filter", "args": { "filter": "Test name pattern to match", "verbose": "Show detailed output (true/false)" }, "permission": "confirm_execute", "timeout_seconds": 60}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Tool name. Registers as tb__<sanitized_name> |
description | string | Yes | Short description shown to the model |
args | map | No | Argument keys and descriptions, or a JSON schema-like object |
permission | string | No | Permission level (default: confirm_execute) |
timeout_seconds | int | No | Execution timeout in seconds. If omitted, tools.externalTimeout applies. |
For tools that need complex parameters (objects, arrays), the describe output can return a full JSON schema instead of the simple key-value args map.
Invalid describe output is skipped with a diagnostic. Anode continues loading other tools.
Execute
Section titled “Execute”Anode sets TOOLBOX_ACTION=execute and TOOLBOX_WORKSPACE (the workspace root path). It passes a JSON object of arguments on stdin. The tool reads stdin, does its work, and prints results to stdout.
Tool Names
Section titled “Tool Names”Toolbox tools register as tb__<sanitized_name>. The name from the describe output is lowercased and restricted to alphanumeric characters, dashes, and underscores.
View all registered tools (built-in, toolbox, and MCP) with:
anode tools listPermission Policy
Section titled “Permission Policy”Toolbox tools go through the same permission rules as built-in and MCP tools. The permission field in the describe output sets the default level. Your permission policy can override it.
Example: Bash test runner
Section titled “Example: Bash test runner”A single Bash script that describes itself and runs tests.
#!/usr/bin/env bashset -euo pipefail
if [ "$TOOLBOX_ACTION" = "describe" ]; then cat <<'EOF'{ "name": "run-tests", "description": "Run Go tests with optional package filter", "args": { "package": "Package path to test (default: ./...)", "verbose": "Show verbose output (true/false)" }, "permission": "confirm_execute", "timeout_seconds": 120}EOF exit 0fi
# Executecd "$TOOLBOX_WORKSPACE"INPUT=$(cat)PACKAGE=$(echo "$INPUT" | jq -r '.package // "./..."')VERBOSE=$(echo "$INPUT" | jq -r '.verbose // "false"')
ARGS=("-count=1" "$PACKAGE")if [ "$VERBOSE" = "true" ]; then ARGS=("-v" "${ARGS[@]}")fi
go test "${ARGS[@]}" 2>&1Save this as run-tests in a toolbox directory, make it executable, and restart Anode. The model can now call tb__run-tests.
Example: Node.js database query tool
Section titled “Example: Node.js database query tool”A Node.js script for running read-only database queries.
#!/usr/bin/env nodeconst { execSync } = require("child_process");
if (process.env.TOOLBOX_ACTION === "describe") { console.log(JSON.stringify({ name: "db-query", description: "Run a read-only SQL query against the development database", args: { query: "SQL SELECT statement to execute", limit: "Max rows to return (default: 50)" }, permission: "confirm_execute", timeout_seconds: 30 })); process.exit(0);}
// Executelet input = "";process.stdin.on("data", (chunk) => { input += chunk; });process.stdin.on("end", () => { const { query, limit = "50" } = JSON.parse(input);
if (!/^\s*SELECT\b/i.test(query)) { console.error("Error: only SELECT queries are allowed"); process.exit(1); }
const fullQuery = `${query.replace(/;\s*$/, "")} LIMIT ${limit};`; const result = execSync( `psql "$DATABASE_URL" -c ${JSON.stringify(fullQuery)} --csv`, { cwd: process.env.TOOLBOX_WORKSPACE, encoding: "utf-8" } ); console.log(result);});Save as db-query in a toolbox directory and make it executable.
Recommendations
Section titled “Recommendations”Use toolbox tools for project-local, deterministic behavior:
- Test runners - wrap
go test,pytest,jestwith project-specific flags. - Database queries - safe, read-only access to development data.
- Build actions - compile, lint, or bundle with locked configurations.
- CLI wrappers - expose project-specific CLI tools to the agent with constrained interfaces.
Toolbox tools live alongside your code. They are versioned, reviewable, and predictable.
Keep Going
Section titled “Keep Going”- MCP Integration - connect to full MCP servers for richer tool capabilities
- Permissions - control what toolbox tools are allowed to do
- Tools - reference for all built-in tools
- Configuration -
tools.toolboxDirsand other settings