Agent Tools¶
Tools give your agent hands: Python scripts you write, Go-native built-ins, MCP tools, and peer agents, all exposed through one catalog the model picks from.
Quick Start¶
tools:
- name: get_weather
description: Fetch the current weather for a location.
python_file: tools/get_weather.py
timeout: 30s
retries: 1
retry_backoff: 1s
parameters:
type: object
properties:
location: { type: string }
required: [location]
def get_weather(location):
# do work, return a string or anything JSON-serializable
return {"location": location, "temp_c": 21}
The runtime builds each agent's catalog from five sources — Python tools from
SOUL.yaml, Go-native built-ins, MCP tools, peer agents (agent__<id>), and
plugin tools. The model only ever sees tools admitted by the agent definition
and gateway config.
Python Tools¶
Each entry under tools: needs:
| Field | Notes |
|---|---|
name |
Tool name (snake_case). Must match a function of the same name in the Python file. |
description |
What the LLM reads to decide when to call it. Be specific. |
python_file |
Path to the script. Relative paths resolve from the SOUL.yaml directory; ~/ expands to your home directory. |
inline |
Alternative to python_file: a Python script embedded in YAML. |
parameters |
JSON Schema object describing the arguments. |
timeout |
Per-tool override of the global runtime.tool_timeout. Go duration syntax: 30s, 5m, 1h. |
retries |
Number of extra attempts after a Python tool fails. Defaults to 0; capped at 5. |
retry_backoff |
Wait before each retry. Defaults to 1s; capped at 30s. |
How a call executes:
- The engine serializes the model's arguments to JSON and passes them on
stdin; your function is called as
get_weather(**args). - The function's return value becomes the tool result — strings pass through, anything else is JSON-encoded.
print()inside your tool goes to stderr, not the result — and every stderr line streams live into the Activity log as atool.logevent, so long-running tools can report progress withprint("step 2/5…", file=sys.stderr, flush=True).
Tip
Set timeout per tool instead of weakening the global default. A
NotebookLM export that legitimately takes 20 minutes gets timeout: 30m;
everything else keeps the safety net.
Tip
Use retries only for idempotent, transient work such as fetching a URL,
parsing a flaky API response, or reading a remote document. Do not retry
tools that send messages, write durable records, place orders, or perform
any side effect that would be harmful if repeated.
The Sandbox¶
Python tools run inside a resource sandbox — the soulacy binary re-execs itself as a hidden wrapper that applies syscall-level rlimits before running your script. Default caps:
| Limit | Default |
|---|---|
| CPU time | 30 s |
| Memory (address space) | 512 MB |
| Open file descriptors | 256 |
| Largest single file written | 64 MB |
No external sandboxer is needed; it works on any Unix host. Operators can tune
or disable the limits in config.yaml. On multi-user deployments,
runtime.allowed_tool_dirs additionally restricts which paths python_file
may point at — a crafted SOUL.yaml cannot execute arbitrary host files.
Built-ins¶
builtins controls which Go-native tools the engine offers:
| YAML | Mode |
|---|---|
| Field absent | Default — gated built-ins are auto-injected when their prerequisites are met. |
builtins: [] |
None — no built-ins. Right choice for peer-only orchestrators. |
builtins: [web_search, kb_search] |
Restricted — only the listed built-ins, still subject to their gates. |
builtins: ["*"] or ["all"] |
Same as default, written explicitly. |
Common built-ins and their gates:
| Tool | Gate |
|---|---|
web_search |
Web search API key configured. |
kb_search |
Agent declares knowledge:. |
kb_write |
Agent declares knowledge:. |
queue_create, queue_names, queue_put, queue_take, queue_list, queue_clear |
Always available unless builtins restricts them. |
channel.send |
Channel registry configured. |
read_skill, read_skill_file |
Agent declares skills:. |
shell_exec, run_script, read_file, write_file, list_dir, install_library |
System tools — double opt-in (below). |
Ephemeral Queues¶
Use the queue built-ins when an agent or Studio workflow needs temporary handoff state but should not receive filesystem access:
queue_createcreates a named queue.queue_nameslists current queues and item counts.queue_putstores a JSON value in memory.queue_takereturns and removes the oldest item.queue_listinspects queued items without removing them.queue_cleardeletes all items in a queue.
Queues are created automatically by queue_put, so queue_create is optional.
If a queue name is omitted, Soulacy uses the default queue. Queues are
process-local, bounded, and expire items automatically. They are not persisted
across gateway restarts. For durable searchable content, use kb_write; for
generated files, use a deliberately scoped filesystem or artifact tool rather
than broad write_file access.
Example restricted agent:
Typical handoff:
Then a downstream step calls queue_take with {"queue":"pending_docs"}.
For a simple one-buffer workflow, both calls may omit queue and use the
default queue.
System Tools¶
OS-level tools require both sides to opt in:
runtime.allow_system_tools: trueinconfig.yaml.system_tools: truein the agent'sSOUL.yaml.
Warning
System tools execute with the gateway's OS permissions. On shared or internet-exposed deployments, leave them off and use narrower Python or MCP tools instead.
Confirmation Gates¶
confirm_tools lists built-in tools that must pause for explicit approval
before executing:
When the model calls a gated tool, the engine emits a tool_confirm event,
shows an approval card in the Chat page, and waits for your decision before
proceeding. confirm_tools: ["*"] gates every built-in call. Gates apply to
built-in and system tools (Python and MCP tools run without a gate — restrict
those via allowlists instead).
MCP Tools¶
Tools from connected MCP servers are auto-injected under the name
mcp__<server>__<tool> — e.g. mcp__github__search_repositories. With no
allowlist configured, agents see every connected MCP tool. Once either field
below is present, MCP becomes deny-by-default:
mcp_servers: [github] # allow whole servers…
mcp_tools: [mcp__github__search_repositories] # …or individual tools
A tool is allowed when either list admits it. Use mcp_servers: [] to
expose none, or ["*"] / ["all"] to allow everything explicitly.
For browser automation, use the Browser quick-start in the MCP page. It runs a Playwright MCP sidecar and exposes navigation/click/type/snapshot tools to agents. See Browser Automation.
Peer-Agent Tools¶
agents: [researcher] registers agent__researcher as a callable tool. See
Peer Agents & Built-ins for delegation patterns and
llm.tool_choice forcing.
Editing Tools in the GUI¶
The Agents page editor has a full tool builder — no YAML required:
- + Add tool creates a tool card with name, description, timeout, and a JSON parameters-schema editor.
- The Python file field is a picker: type a path or hit 📂 Browse to pick from the tool catalog of discovered scripts (picking one pre-fills the name and description).
- A hint chip row shows which MCP tools are auto-injected for the agent.
- Built-ins are a tri-state radio: Default, None (peer-only orchestrator), or Restricted with a named allowlist.
Safety Checklist¶
- Prefer explicit
builtins,mcp_servers, andmcp_toolsallowlists in production. - Use
builtins: []for orchestrators that should only call peers. - Keep
system_toolsoff unless the agent truly needs host access. - Add
confirm_toolsfor destructive or expensive actions. - Run
sy agent validate path/to/SOUL.yamlbefore deploying.