4.9 KiB
Writing Ollie Tools
Tools are executables 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 file with a corresponding .meta sidecar is a tool. The tool name is the filename (without .meta).
Tool Metadata
Each tool has a .meta JSON sidecar file alongside the executable. This is the sole source of metadata — the registry reads only .meta files.
{
"description": "Does something useful.",
"prompt": "## my_tool\n\nDoes something useful.\n\n**Args**: `path` (required), `verbose` (optional)\n\n```\nmy_tool(path=\"/foo\", verbose=true)\n```",
"args": {
"type": "object",
"required": ["path"],
"properties": {
"path": {"type": "string", "description": "File path"},
"verbose": {"type": "boolean", "description": "Enable verbose output"}
}
},
"tier": "cold",
"readOnly": true
}
| Field | Type | Purpose |
|---|---|---|
description |
string | One-liner shown in the tool listing (system prompt) |
prompt |
string | Full documentation injected when the tool is loaded |
args |
JSON Schema | Input schema exposed to the model |
tier |
"hot"|"warm"|"cold" |
Context retention tier (default: hot) |
readOnly |
bool | Safe for parallel execution with other read tools |
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
Go tools
Compiled Go binaries work the same way — read JSON from stdin, write result to stdout. No _lib dependency needed; use encoding/json directly.
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 data/tools/ for real examples:
file_read— reads files with line numbersfile_edit— fuzzy text replacementfile_grep— ripgrep wrappergui_*— KDE desktop automationlsp_*— language server protocol clients