5.2 KiB
Writing Ollie Tools
Tools are executable scripts that extend ollie's capabilities. They are loaded lazily via tool_load and become first-class callable functions with JSON schemas.
Tool Discovery
Tools live in $OLLIE_TOOLS_PATH (default: ~/.config/ollie/tools/). Any executable file in that directory with the ollie:prompt annotation is a tool. The tool name is the filename.
Files without ollie:prompt (e.g., shared libraries in _lib/) are ignored by the registry.
Tool Metadata
Tools carry metadata in header comments. These are parsed by the registry and exposed to the agent:
#!/usr/bin/env bash
# args_json: {"type":"object","required":["path"],"properties":{"path":{"type":"string","description":"File path"},"verbose":{"type":"boolean","description":"Enable verbose output"}}}
# ollie:prompt
# ## my_tool
#
# Does something useful.
#
# ```
# call_tool: calls=[{tool: "my_tool", args: {"path": "/foo", "verbose": true}}]
# ```
# ollie:end
# ollie:parallel read
# ollie:tier cold
| Annotation | Purpose |
|---|---|
args_json: <schema> |
JSON Schema for the tool's input (required for typed tools) |
ollie:prompt ... ollie:end |
Usage documentation shown to the agent |
ollie:parallel read |
Tool is safe to run concurrently with other read tools |
ollie:tier cold|warm|hot |
Context retention tier (default: hot) |
Input Protocol
Tools receive their arguments as a JSON object on stdin. The dispatcher pipes the model's tool call arguments directly — no positional argument translation.
Python tools
#!/usr/bin/env python3
import os, sys
sys.path.insert(0, os.environ.get('OLLIE_TOOLS_PATH', os.path.dirname(os.path.abspath(__file__))))
from _lib.args import parse_args
args = parse_args()
path = args.require("path") # exits with error if missing
verbose = args.get_bool("verbose") # False if missing
count = args.get_int("count", 10) # default 10
items = args.get_list("items") # [] if missing
The _lib/args.py module provides:
args.require(key)— get required string arg (exits on missing)args.get(key, default)— get optional string argargs.get_bool(key, default)— get boolean argargs.get_int(key, default)— get integer argargs.get_list(key)— get list argargs.raw()— get the raw parsed dict
Bash tools
#!/usr/bin/env bash
source "${OLLIE_TOOLS_PATH:-$(dirname "$0")}/_lib/args.sh"
path=$(arg_require "path") # exits with error if missing
verbose=$(arg_bool "verbose") # "false" if missing
mode=$(arg_get "mode" "default") # "default" if missing
raw=$(arg_json "config") # raw JSON value
The _lib/args.sh module provides:
arg_require key— get required arg (exits on missing)arg_get key [default]— get optional argarg_bool key [default]— get boolean ("true"/"false")arg_json key— get raw JSON valuearg_raw— get the full stdin JSON
Schema declaration
Declare the input schema with # args_json: followed by a JSON Schema object on a single line:
# args_json: {"type":"object","required":["path","pattern"],"properties":{"path":{"type":"string","description":"Search directory"},"pattern":{"type":"string","description":"Regex pattern"},"case_insensitive":{"type":"boolean","description":"Ignore case"}}}
This schema is exposed to the model as the tool's inputSchema, telling it exactly what JSON object to construct.
Sandboxing
Every tool runs inside a Landlock sandbox by default. Elevation is the escape hatch — when the agent passes "elevated": true, the tool runs unsandboxed (but still as the current user). This means by default:
- Filesystem access is restricted by the sandbox profile (default:
~/.config/ollie/sandbox/default.yaml) - Network access may be restricted depending on the profile
- System calls are filtered by Landlock rules
Elevation
If your tool needs to run outside the sandbox (e.g., access paths the sandbox restricts), it must request elevation explicitly. The agent passes "elevated": true in the tool call, which the dispatcher extracts before piping the remaining args to the tool.
From the tool's perspective, elevation is transparent — it simply runs outside the Landlock sandbox (but still as the current user, not root). The tool script itself does not need to handle elevation logic.
Output Protocol
Tools communicate results through stdout/stderr. The output is returned as a text content block.
Success: print the result to stdout and exit 0.
Error: print error information to stdout/stderr and exit non-zero. The dispatcher wraps the error.
Structured output: if a tool returns {"content": [...]} JSON, it is passed through as-is.
Environment Variables
Tools receive these environment variables:
| Variable | Purpose |
|---|---|
OLLIE_TOOLS_PATH |
Path to the tools directory |
OLLIE_ELEVATE_SOCKET |
Unix socket for elevation broker |
PWD / working directory |
Session's current working directory |
Examples
See the tools in $OLLIE_TOOLS_PATH for real examples:
file_read— reads files with line numbersfile_edit— fuzzy text replacementfile_grep— ripgrep wrappergui_*— KDE desktop automationlsp_*— language server protocol clients