# 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 `.meta` beside an executable named ``. - **Metadata only:** set `cmd` in `.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. | | `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. | ## 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 can load tools at startup with `autoLoad`. 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.