213 lines
7.8 KiB
Markdown
213 lines
7.8 KiB
Markdown
# Tools Architecture and Authoring
|
||
|
||
A tool is declared by a `.meta` file. It may also have an accompanying executable, but neither form is required in every case:
|
||
|
||
- **Metadata plus binary/script:** place `<name>.meta` beside an executable named `<name>`.
|
||
- **Metadata only:** set `cmd` in `<name>.meta` to a shell command. The registry executes that command directly.
|
||
|
||
Both approaches are valid. The `.meta` file is always the source of discovery, model-facing documentation, and the JSON argument schema.
|
||
|
||
The registry reads the metadata, presents the tool to the model, and executes the resolved command when called. No integration with the Ollie source tree is required. Put the `.meta` file in `$OLLIE_TOOLS_PATH`; the tool is available on the next discovery or session start.
|
||
|
||
## Quick start
|
||
|
||
### Metadata plus an executable
|
||
|
||
```text
|
||
~/.config/ollie/tools/
|
||
├── my_tool # executable in any language
|
||
└── my_tool.meta # JSON metadata
|
||
```
|
||
|
||
### Metadata-only tool
|
||
|
||
```json
|
||
{
|
||
"description": "Extract a value from JSON.",
|
||
"prompt": "## json_value\n\nRead `.path` from JSON input.",
|
||
"cmd": "input=$(cat); echo \"$input\" | jq -r '.path'",
|
||
"args": {
|
||
"type": "object",
|
||
"required": ["path"],
|
||
"properties": {"path": {"type": "string"}}
|
||
}
|
||
}
|
||
```
|
||
|
||
A metadata-only tool needs no script or binary. Its `cmd` is run through the shell with the tool-call JSON on standard input.
|
||
|
||
## The `.meta` sidecar
|
||
|
||
The `.meta` file is the sole source of discovery and schema. The registry never inspects an accompanying executable to infer its interface.
|
||
|
||
```json
|
||
{
|
||
"description": "One-liner shown in tool listings.",
|
||
"prompt": "## my_tool\n\nFull documentation shown when loaded.",
|
||
"args": {
|
||
"type": "object",
|
||
"required": ["path"],
|
||
"properties": {
|
||
"path": {"type": "string", "description": "File path"}
|
||
}
|
||
},
|
||
"tier": "hot",
|
||
"cmd": "my_tool"
|
||
}
|
||
```
|
||
|
||
| Field | Type | Purpose |
|
||
|---|---|---|
|
||
| `description` | string | One-liner shown in the tool listing. |
|
||
| `prompt` | string | Documentation injected when the tool is loaded. |
|
||
| `keywords` | array of strings | Additional user-language phrases used only for semantic discovery. Include synonyms, task verbs, file types, domains, and representative requests. |
|
||
| `args` | JSON Schema | Input schema exposed to the model. |
|
||
| `tier` | `hot`, `warm`, or `cold` | Context retention tier. |
|
||
| `cmd` | string | Shell command or executable path/name. |
|
||
| `variants` | array | Conditional definitions for heterogeneous hosts. |
|
||
| `scope` | string | Tool scope metadata. |
|
||
| `outputFormat` | string | Output format metadata. |
|
||
| `resetsCounter` | bool | Marks tools that reset the relevant counter. |
|
||
|
||
## Tool and skill discovery
|
||
|
||
Tool discovery remains metadata-driven: `toolsrv` scans `.meta` files and manages
|
||
which tools are loaded for an agent. Ollie adds a separate semantic ranking
|
||
layer before prompt assembly. The ranking text combines `description`, `prompt`,
|
||
and optional `keywords`. Keywords are discovery-only vocabulary: they should
|
||
contain the words users use to request the operation, including synonyms,
|
||
workflow phrases, file types, and domain terms. They are not shown as a second
|
||
model-facing schema and do not change execution. See [`architecture-embedding.md`](architecture-embedding.md)
|
||
for the embedding model, index lifecycle, matching thresholds, and failure
|
||
behavior.
|
||
|
||
## Command resolution
|
||
|
||
When the registry resolves a tool named `name`, it checks:
|
||
|
||
1. `$OLLIE_TOOLS_PATH/name` — a co-located executable or script.
|
||
2. The resolved `.meta` `cmd` field — an absolute path, bare executable name, or shell command.
|
||
3. `name` through `$PATH`.
|
||
|
||
A co-located executable takes precedence over `cmd`. Metadata-only tools use the `cmd` value directly through the shell. This supports scripts, compiled binaries, wrappers, system commands, and pipelines.
|
||
|
||
## Variants
|
||
|
||
The first variant whose conditions all match determines the tool’s schema, documentation, and command. If no variant matches, the tool is hidden.
|
||
|
||
| Key | Semantics | Example |
|
||
|---|---|---|
|
||
| `binary` | Binary exists in `$PATH` or at an absolute path. | `journalctl` |
|
||
| `file` | File or directory exists. | `/var/log/syslog` |
|
||
| `os` | Matches `runtime.GOOS`. | `linux` |
|
||
| `arch` | Matches `runtime.GOARCH`. | `amd64` |
|
||
| `env` | Environment variable is non-empty. | `DISPLAY` |
|
||
| `nenv` | Environment variable is empty or unset. | `SSH_CONNECTION` |
|
||
|
||
All keys in one `match` object use AND logic. An array value uses OR logic. Variant fields override top-level fields; unset fields inherit. If a remote toolsrv receives the metadata, it evaluates variants against the remote host.
|
||
|
||
```json
|
||
{
|
||
"description": "Read system logs.",
|
||
"variants": [
|
||
{"match": {"binary": "journalctl"}, "cmd": "system_logs_journald"},
|
||
{"match": {"file": "/var/log/syslog"}, "cmd": "system_logs_syslog"}
|
||
]
|
||
}
|
||
```
|
||
|
||
## Input and output contract
|
||
|
||
Both tool forms use the same contract:
|
||
|
||
- Input is one JSON object on standard input.
|
||
- Successful output is written to standard output and exits with status 0.
|
||
- Errors may be written to standard output or standard error and exit non-zero.
|
||
- Structured output may be returned as `{"content": [...]}` JSON.
|
||
|
||
The dispatcher does not require flags, argv conventions, or language-specific integration.
|
||
|
||
### Executable example
|
||
|
||
```python
|
||
#!/usr/bin/env python3
|
||
import json
|
||
import sys
|
||
|
||
args = json.load(sys.stdin)
|
||
print(f"result: {args['path']}")
|
||
```
|
||
|
||
### Metadata-only example
|
||
|
||
```json
|
||
{
|
||
"description": "Search with ripgrep.",
|
||
"cmd": "input=$(cat); p=$(echo \"$input\" | jq -r '.pattern'); rg \"$p\"",
|
||
"args": {
|
||
"type": "object",
|
||
"required": ["pattern"],
|
||
"properties": {"pattern": {"type": "string"}}
|
||
}
|
||
}
|
||
```
|
||
|
||
For subcommand-style tools, parse the JSON with `jq` and construct the command explicitly. Avoid unsafe `eval` when arguments can be passed as arrays or quoted shell parameters.
|
||
|
||
## Sandboxing and bypass
|
||
|
||
Toolsrv owns sandbox enforcement and the bypass approval path. See [`architecture-toolsrv.md`](architecture-toolsrv.md#sandbox). Tool metadata declares the command interface; it does not define the complete security policy.
|
||
|
||
## Environment
|
||
|
||
Tools may receive:
|
||
|
||
| Variable | Purpose |
|
||
|---|---|
|
||
| `OLLIE_TOOLS_PATH` | Tool metadata and executable directory. |
|
||
| `OLLIE_BYPASS_SOCKET` | Bypass broker socket. |
|
||
| `PWD` | Session working directory. |
|
||
|
||
## Installation and testing
|
||
|
||
Copy a `.meta` file and, when using the binary approach, its executable to the tools directory:
|
||
|
||
```sh
|
||
cp my_tool.meta ~/.config/ollie/tools/
|
||
cp my_tool ~/.config/ollie/tools/
|
||
chmod +x ~/.config/ollie/tools/my_tool
|
||
```
|
||
|
||
A metadata-only tool needs only the first command. Test its command contract with JSON on standard input:
|
||
|
||
```sh
|
||
echo '{"pattern":"TODO"}' | bash -c 'YOUR_CMD'
|
||
```
|
||
|
||
Agent profiles load tools at startup with `autoLoad`. Each profile specifies
|
||
the tools it needs — there is no lazy loading or automatic tool discovery at
|
||
call time:
|
||
|
||
```json
|
||
{
|
||
"autoLoad": ["shell", "file_read", "file_edit", "file_grep", "file_glob"]
|
||
}
|
||
```
|
||
|
||
To add a tool to a running agent, use the `ctl` file:
|
||
|
||
```text
|
||
client_9p(op="rdwr", path="session/$OLLIE_SESSION_ID/agent/$OLLIE_UNAME/ctl", data="tool_load <name>")
|
||
```
|
||
|
||
After loading, the agent refreshes its model-facing schemas when the toolsrv
|
||
registry revision changes. See [`architecture-toolsrv.md`](architecture-toolsrv.md)
|
||
for discovery, loading, registry, and execution architecture.
|
||
|
||
## Repository examples
|
||
|
||
The repository includes executable and metadata-backed tools under `data/tools/`, including filesystem, GUI, LSP, and memory tools. Compiled tools are valid alongside scripts and metadata-only definitions.
|
||
|
||
See [`architecture.md`](architecture.md#deliberately-unimplemented-agent-patterns) for the design rationale, including why Ollie does not implement native MCP, embedded tool frameworks, workflow engines, parallel frontend control planes, or competing memory stores.
|
||
|