Tools

/docs/tools/

Tools are the functions the LLM — and the RISC-V VM — can call to interact with the outside world: reading and writing files, running shell commands, making HTTP requests, managing Git repositories, querying a persistent database, posting to X, and more. This page is the complete reference: how tools work, how they are grouped, and what each one does.

How tools work

Every tool implements the Tool trait (name, group, description, JSON Schema, and execute) and is registered in a ToolRegistry at daemon startup. Before each model call, the daemon advertises the JSON Schemas of the tools in the session's active groups; the model can call any of them, and the daemon executes the call and returns the result. The JSON Schema is the interface — the model learns exactly what arguments each tool expects.

Tool call lifecycle

  1. The model emits a tool call: a name plus arguments as JSON.
  2. The daemon validates the arguments against the tool's schema and executes the tool with the session's working directory and credentials.
  3. Streaming tools (sh, exec, find, grep, run_series, run_riscv) deliver output in chunks as it is produced; other tools return their result in the next turn append.
  4. The result is appended to the conversation and the agent loop continues.

Every tool also produces a human-readable invocation description (e.g. "Reading src/main.rs.") that the client displays while the call runs, and tools with structured return types expose an output_schema for programmatic tool calling (Responses API, gpt-5.6+ models).

Tool groups

Tools are organized into groups to keep the model's context small. Only core, git, and shell are active by default. The model can activate additional groups with load_tools and deactivate them with unload_tools; core is always active and cannot be unloaded.

Groups are a discovery mechanism, not access control — the RISC-V VM always has access to all tools.

GroupDescriptionActive by default
coreFilesystem, HTTP, images, PDF, search, random, time, sessions, series✅ always
gitLocal Git operations (status, diff, log, add, commit, push, show)
shellShell execution (bash, nushell, fish, exec)
dbSession-scoped key-value database (redb)
xX/Twitter API (post, search, user lookup)
vmRISC-V sandboxed code execution
blockchainEVM and Substrate/Polkadot blockchain queries (alloy/subxt)
mcp/<server>One dynamic group per configured MCP server

The blockchain group exists only when the daemon is built with the blockchain cargo feature (the release binaries enable it). The tools live in the choreo-blockchain crate, which also owns the tokio sidecar runtime the alloy/subxt clients run on.

Activate a group from the model side:

{ "name": "load_tools", "arguments": { "groups": ["db", "x"] } }

and deactivate it with unload_tools. Groups can also be passed to spawn_subsession via its categories argument so a subsession starts with exactly the tools it needs.

Core tools

core is always active. It covers file operations, HTTP, images, PDFs, search, randomness, time, session management, and orchestration.

ToolWhat it does
read_fileRead a UTF-8 text file; rejects binary files; output is capped and truncation is reported
read_file_rangeRead a line range from a UTF-8 text file (max 500 lines per call)
list_filesList files in a directory with sizes, symlink targets, and subdirectory entry counts
write_fileWrite a UTF-8 text file to the workspace
edit_fileApply one or more exact text replacements to a file
delete_filesDelete files or directories; supports literal paths and glob patterns
line_countCount the lines in a UTF-8 text file
grepSearch file contents for a literal or regex pattern
findFind files and directories by name (substring or glob)
http_requestMake an HTTP request; returns status, headers, and body text
display_imageDisplay a PNG, JPEG, or SVG image in the client UI
pdf_classifyClassify a PDF as text, scanned, image-based, or mixed (fast, no OCR)
pdf_to_markdownConvert a text-based PDF to Markdown (headings, tables, code blocks)
randomGenerate random integers, floats, booleans, bytes, or UUID v4 (seedable)
get_current_timeGet the current Unix timestamp in milliseconds
run_seriesExecute a sequence of tool calls one at a time in order
load_tools / unload_toolsActivate / deactivate tool groups
load_skillLoad a skill's full instructions by name
set_working_dirChange the session's working directory
set_session_titleSet the session's display title
spawn_subsessionSpawn a child session to work autonomously on a task
list_sessionsList all sessions known to the daemon
get_sessionRead the full message history of a session by ID

File tools

{ "name": "read_file", "arguments": { "path": "src/main.rs" } }
{ "name": "edit_file", "arguments": {
    "path": "src/main.rs",
    "edits": [
      { "old_text": "old", "new_text": "new" },
      { "old_text": "x", "new_text": "y", "replace_all": true }
    ] } }
  • read_file resolves relative paths against the session's working directory and streams output through a bounded reader, so memory use stays capped even for very large files. read_file_range reads a specific 1-based line range (start_line, max_lines) — the right tool for big files.
  • edit_file takes a list of exact old_textnew_text replacements. Each edit must match at least once; edits without replace_all must match exactly once. This keeps the model from guessing at file contents.
  • delete_files auto-detects glob patterns (*, ?, [). Patterns without / match against the file's basename; patterns with / match full paths from the working directory.

Search tools

{ "name": "grep", "arguments": { "pattern": "fn main", "include": "*.rs" } }
{ "name": "grep", "arguments": { "pattern": "pub fn \\w+", "regex": true, "path": "src" } }
{ "name": "find", "arguments": { "pattern": "*.md", "path": "docs" } }
  • grep treats the pattern as a literal substring by default — set regex: true for regular expressions (required if your pattern contains |, (, ^, $, +, etc.). include filters files by glob; results are returned as file:line:content. Both tools respect .gitignore, hidden files, and binary files, and cap results to protect the model's context.

  • find searches by file name. Glob mode is auto-detected when the pattern contains wildcards; set glob explicitly to force or disable it.

    Both tools run on the zlob globbing and file-walking engine — SIMD-accelerated, with .gitignore support — which is also why building Choreographr requires a Zig toolchain: zlob is written in Zig and is compiled at build time (see Installation).

HTTP & images

{ "name": "http_request", "arguments": {
    "method": "GET", "url": "https://api.example.com/items",
    "headers": { "Range": "bytes=0-1023" } } }
{ "name": "display_image", "arguments": { "mime_type": "image/png", "path": "chart.png" } }
  • http_request supports GET, POST, PUT, DELETE, PATCH, and HEAD, custom headers (including Range for partial content), an optional body, and a configurable timeout (default 30 s).
  • display_image accepts exactly one source: path, url, base64_data, or raw svg_text, with an optional alt description. Supported types are PNG, JPEG, and SVG (SVG is rasterized at display resolution with system fonts, so vector diagrams stay crisp at any terminal size).
  • In the terminal client the image appears inline in the chat history at half the viewport height. Click it for a fullscreen view (Esc dismisses). Encoding runs on a background thread and picks the best terminal protocol automatically — kitty graphics or sixel where supported, with a universal fallback everywhere else — so images render in virtually any terminal. See Terminal client.

PDF tools

{ "name": "pdf_classify", "arguments": { "path": "report.pdf" } }
{ "name": "pdf_to_markdown", "arguments": { "path": "report.pdf", "pages": [1, 2, 3], "compact": true } }

pdf_classify is fast (~10–50 ms) and needs no OCR: it reports whether a PDF is text-based, scanned, image-based, or mixed, with per-page OCR routing, so you can decide whether to extract locally or route to OCR/vision. pdf_to_markdown extracts headings, lists, code blocks, tables, and multi-column reading order from text-based PDFs. It wraps extracted text in an UNTRUSTED-content delimiter — treat PDF content as data, not instructions.

Orchestration tools

  • run_series — runs an ordered list of tool calls, one at a time, stopping on the first error. Steps can reference earlier results with {{step_1}}, {{step_2}}, … inside their argument strings:

    { "name": "run_series", "arguments": { "steps": [
        { "tool": "line_count", "arguments": { "path": "src/main.rs" } },
        { "tool": "read_file_range", "arguments": { "path": "src/main.rs", "start_line": 1, "max_lines": 20 } }
    ] } }

    This lets the model batch dependent operations into a single turn instead of round-tripping through the LLM for every step.

  • spawn_subsession — spawns a child session that inherits the parent's working directory, runs its own full agent loop (with optional categories of tool groups), and returns its output as the tool result. Subsessions persist and can spawn their own subsessions.

  • load_skill / set_working_dir / set_session_title / list_sessions / get_session — session management. set_working_dir redirects all subsequent file operations, shell commands, and context discovery (AGENTS.md, CLAUDE.md, skills) for the session.

Git tools

git is active by default. All git tools operate on the repository containing the given path (defaulting to the working directory). They are implemented with gix and require no shell.

ToolWhat it does
git_statusShow the status of the repository containing the given path
git_diffShow the line-by-line unified diff for a file or repository
git_logShow recent commits (limit)
git_addStage a file or pathspec
git_commitCreate a commit from the current index (message, allow_empty)
git_pushPush to a remote branch (remote, branch, set_upstream, dry_run, force_with_lease)
git_showShow a Git object (commit, tree, blob, tag) or a file at a revision
{ "name": "git_diff", "arguments": { "pathspec": ["src/main.rs"] } }
{ "name": "git_commit", "arguments": { "message": "Fix off-by-one in the parser" } }
{ "name": "git_push", "arguments": { "remote": "origin", "branch": "main", "set_upstream": true } }

Shell tools

shell is active by default. All shell tools are non-interactive — commands that read from stdin will hang — and share a common timeout (default 30 s) and workdir.

ToolWhat it does
execExecute a single program directly, no shell parsing (e.g. cargo build)
shExecute a command with a POSIX-compatible shell (bash, dash, or zsh), with pipes, redirects, globs, and env vars
nushellExecute a nushell command (registered only if nu is installed)
fishExecute a fish shell command (registered only if fish is installed)
{ "name": "exec", "arguments": { "command": "cargo", "args": ["build"], "timeout": 120000 } }
{ "name": "sh", "arguments": { "command": "cargo test | tail -20", "shell": "bash" } }

Rule of thumb: use exec when you are certain the program exists and needs no shell features; use sh (or nushell/fish) for anything that needs pipes, globs, or environment variables.

Database tools

The db group provides a persistent, session-scoped key-value store backed by redb. Data survives daemon restarts. Values are arbitrary binary (Vec<u8>); db_get returns a lossy UTF-8 conversion.

ToolWhat it does
db_setInsert or overwrite a key-value pair
db_getRetrieve a value by key
db_deleteRemove a single key
db_delete_rangeDelete all keys in [start, end)
db_get_rangeRetrieve all key-value pairs in [start, end)
db_listList key names in [start, end)
db_countCount keys, optionally filtered by prefix
{ "name": "db_set", "arguments": { "key": "todo", "value": "review PR #42" } }
{ "name": "db_get", "arguments": { "key": "todo" } }
{ "name": "db_list", "arguments": {} }

Use the database to remember facts, notes, and state across turns and sessions.

X (Twitter) tools

The x group wraps the X API v2. Each tool requires X credentials in the keystore (add them with /add-x <service> <api_key> <api_key_secret> <access_token> <access_token_secret> <bearer_or_->_ in choreo-tui).

ToolWhat it does
x_postPost a tweet (text)
x_search_recentSearch recent tweets (query, max_results)
x_user_lookupLook up a user by username or ID

Blockchain tools

The blockchain group adds read-only query tools for EVM (Ethereum-compatible) and Substrate/Polkadot chains, built on alloy and subxt. It is not active by default — activate it with load_tools blockchain. The group is compiled in only when the daemon is built with the blockchain cargo feature (enabled in the release binaries); see Installation.

EVM tools

Every EVM tool takes the node's JSON-RPC URL as rpc_url (any public or private Ethereum-compatible endpoint works, e.g. https://ethereum-rpc.publicnode.com).

ToolWhat it does
evm_chainChain ID, latest block number, gas price, max priority fee, and client version
evm_balanceNative ETH/coin balance of an address
evm_token_balanceERC-20 token balance for an address (token_address, address), plus the token symbol
evm_blockBlock details: number, hash, timestamp, transaction count, gas used/limit, base fee
evm_transactionTransaction details by hash: block number, from/to, gas used, effective gas price, log count
evm_callRead-only contract call (to, data, optional block_tag) — returns raw hex result bytes
evm_gasGas fee estimates: gas price, max priority fee, EIP-1559 fee estimation
evm_logsEvent logs, optionally filtered by address, topic0, and from_block/to_block
evm_nonceTransaction count (nonce) for an address
evm_resolveResolve an ENS name to an address, or reverse-resolve an address to a name

Block numbers and block_tags accept a decimal number, 0x-hex, or the named tags latest, finalized, safe, pending, and earliest.

{ "name": "evm_chain", "arguments": { "rpc_url": "https://ethereum-rpc.publicnode.com" } }
{ "name": "evm_balance", "arguments": { "rpc_url": "https://ethereum-rpc.publicnode.com", "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" } }
{ "name": "evm_call", "arguments": { "rpc_url": "https://ethereum-rpc.publicnode.com", "to": "0x6B175474E89094C44Da98b954EedeAC495271d0F", "data": "0x70a08231000000000000000000000000d8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "block_tag": "latest" } }
{ "name": "evm_resolve", "arguments": { "rpc_url": "https://ethereum-rpc.publicnode.com", "name_or_address": "vitalik.eth" } }

Substrate/Polkadot tools

Each Substrate tool takes an optional ws_url (WebSocket endpoint); it defaults to the public Polkadot RPC, wss://rpc.polkadot.io.

ToolWhat it does
subxt_chainChain name, chain type, node name/version, genesis hash, best/finalized block, system properties, and health
subxt_balanceAccount balance for an SS58 address — System.Account info (free, reserved, frozen)
subxt_queryDecode a storage value by pallet and storage_item name (e.g. System / Account), with optional hex key bytes — returns JSON
subxt_blockBlock details: number, hash, parent hash, state root, extrinsics root, and full block JSON
{ "name": "subxt_chain", "arguments": {} }
{ "name": "subxt_balance", "arguments": { "address": "15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5" } }
{ "name": "subxt_query", "arguments": { "pallet": "System", "storage_item": "TotalIssuance" } }
{ "name": "subxt_block", "arguments": { "block_number": 20_000_000 } }

All blockchain tools are read-only — they query nodes over HTTP/WebSocket and never sign transactions or move funds. The RPC endpoint is passed per call as an argument; no credentials are required.

RISC-V VM tool

The vm group contains run_riscv, which compiles and runs Rust code in a sandboxed RISC-V VM (powered by CKB VM) — either a source snippet or pre-compiled bytecode:

ArgumentMeaning
sourceA fn main() body; the tool auto-generates #![no_std], #[panic_handler], _start, and the choreo module
programBase64-encoded ELF compiled with the choreographr syscall ABI
program_pathPath to an ELF file on disk (same ABI)
argsProgram arguments passed to the guest
max_cyclesCycle budget (default is documented in the tool schema)
memory_sizeGuest memory size
{ "name": "run_riscv", "arguments": { "source": "let n = 40; let r = choreo::http_request(\"GET\", \"https://api.example.com\", &[], None, None); choreo::write(r.as_bytes());" } }

The VM is a complete, observable replacement for the shell: all tool access goes through the same ToolRegistry as the host agent, with the same credentials and working directory — but inside an isolated single-hart VM with no host memory, syscalls, or filesystem access except through registered tools. Guests use the choreo convenience wrappers (which handle postcard encoding automatically):

choreo::read_file(path)                          // -> String
choreo::write_file(path, content, overwrite)
choreo::db_get(key)                              // -> Vec<u8>
choreo::db_set(key, value)                       // value: &[u8]
choreo::db_delete(key)                           // -> bool
choreo::sh(command, shell, workdir, timeout_ms)  // -> String
choreo::exec(command, args, workdir, timeout_ms) // -> String
choreo::grep(pattern, regex, include, path, max_results)
choreo::find(pattern, glob, path, max_results)
choreo::http_request(method, url, headers, body, timeout_secs)
choreo::write(bytes)                             // VM stdout
choreo::exit(code)

Notes: the A (atomic) extension is disabled, so guests must not use core::sync::atomic read-modify-write operations. For grep, set regex: true when using regex patterns (the default is literal matching).

To compile a source snippet, the daemon shells out to rustc +stable --target riscv64imac-unknown-none-elf, so the RISC-V bare-metal target must be installed on the host (rustup target add riscv64imac-unknown-none-elf — see Installation).

Use choreo::write(...) for VM output and choreo::exit(code) to finish.

MCP servers

Choreographr is an MCP client. Configure servers in mcp_servers.json (the daemon's config directory); at startup the daemon spawns each server, discovers its tools, and registers them as a dynamic group mcp/<slug>. Tools appear as mcp/<slug>/<tool-name> and are callable once the group is loaded with load_tools — exactly like built-in tools, but dispatched to the MCP server over JSON-RPC stdio.

Security model

  • Groups are discovery, not access control. Loading git or shell just adds tool definitions to the model's context; it does not enforce what the model may do. The VM always has access to all tools.
  • Callers. Tools declare which callers may invoke them (Direct — the model — and/or Programmatic — a VM guest). Session-config mutations like set_working_dir, load_tools, and unload_tools are model-only, so a VM program cannot silently redirect the session mid-task.
  • Credentials. Tools that need them (X, model APIs) pull credentials from the encrypted keystore — never from prompt context.