Stelladocs
Agent Tools

Custom Tools

Extend the Stella agent with your own script tools by dropping a TOML manifest into your workspace or user config directory.

Beyond the built-in tools, you can give the agent your own developer-defined script tools. A custom tool is described by a small TOML manifest; once discovered, it appears alongside the built-ins and can be called by the agent like any other tool.

Where manifests live

Drop a <name>.toml manifest file into one of these directories:

  • Workspace.stella/tools/ (checked in with the project, shared with your team).
  • User global~/.config/stella/tools/ (personal, available across all your workspaces).

If the same tool name is defined in both locations, the workspace manifest wins on collision. This lets a project override a personal default while keeping your global tools available everywhere else.

Run stella tools at any time to see which custom tools were discovered, alongside the built-ins.

Manifest shape

A manifest declares the tool's name, description, the command to run, and an input_schema:

# .stella/tools/wordcount.toml

name = "wordcount"
description = "Count the words in a file and print the total."

# The command Stella runs when the agent calls this tool. It is an argv array,
# spawned directly with no shell; command[0] resolves against the workspace root.
command = ["scripts/wordcount.sh"]

# Optional. Default 30000 (30 seconds), hard-capped at 600000 (10 minutes) —
# a manifest asking for more is clamped. Past the timeout, the script's whole
# process group is killed and the tool returns a named error.
timeout_ms = 60000

# Optional extra environment variables for the child process, applied on top
# of the inherited environment.
[env]
WORDCOUNT_LOCALE = "en_US"

# Tool inputs are a JSON Schema, written as TOML under [input_schema].
[input_schema]
type = "object"
required = ["path"]

[input_schema.properties.path]
type = "string"
description = "Path to the file whose words should be counted."

command must be an argv array (["scripts/wordcount.sh"]), not a bare string — a string fails to parse and the tool never loads. Inputs go under [input_schema] (a JSON Schema), not [parameters]; an unknown table like [parameters] is silently ignored, leaving the tool with an empty schema.

The timeout_ms default is 30 seconds — half of the 60-second default that hooks get. Both share the same 10-minute hard cap.

How input reaches your script

When the agent calls a custom tool, Stella spawns command directly from the argv array — never through a shell, so the tool's input has no injection surface — with the working directory set to the workspace root. The model's input JSON is then delivered two ways, so trivial scripts need no JSON parser:

  1. The whole input object is written to the script's stdin as one JSON document, then stdin is closed — your script sees EOF.
  2. Each top-level scalar property (string, number, bool) is exported as STELLA_INPUT_<UPPER_SNAKE_KEY>path becomes STELLA_INPUT_PATH, dry_run becomes STELLA_INPUT_DRY_RUN. Nested objects and arrays are delivered on stdin only.

The manifest's [env] table is applied on top of the inherited environment, before the STELLA_INPUT_* exports. Input keys come from the model but are namespaced under STELLA_INPUT_, so they can never clobber PATH or your [env] values.

A script consuming both channels:

scripts/wordcount.sh
#!/usr/bin/env bash
set -euo pipefail

# Scalar inputs arrive as environment variables…
path="$STELLA_INPUT_PATH"

# …and the full input object arrives as one JSON document on stdin —
# read it (e.g. with jq) when you need nested objects or arrays.
input="$(cat)"    # {"path": "src/lib.rs"}

wc -w < "$path"

Exit 0 returns the captured stdout as the tool result; a non-zero exit returns an error carrying the exit code and the stderr tail. Both streams are truncated middle-out past a byte cap, same as the built-in bash tool.

Validating manifests

Before a run, check every manifest with the strict pre-flight validator:

stella tools --validate            # scans .stella/tools/ and ~/.config/stella/tools/
stella tools --validate ./mytools  # or an explicit directory

It parses each <name>.toml and reports errors (bad TOML, missing/invalid/reserved name, empty description or command, a non-table input_schema), warnings (e.g. a timeout_ms over the 10-minute cap is clamped, a name that shadows a built-in or another manifest), and info. It exits non-zero if any manifest has errors — handy in CI.

How custom tools appear

Once a manifest is discovered, its tool is registered into the session and shows up next to the built-ins — including in the output of stella tools. The agent can then call it as part of its normal step loop. Like every other tool, a custom tool is subject to the per-tool permission model, and its calls can be gated or blocked with a PreToolUse hook.

Stella is totally free and open source, dual-licensed MIT or Apache 2.0— your choice: MIT's three-paragraph simplicity, or Apache 2.0's explicit patent grant that enterprises prefer. Use it, fork it, embed it, ship it — no account, no phone-home, no strings. What that means →

On this page