Local Tools

Local tools run directly inside the Contenox process — no network, no subprocess, no protocol overhead. They are the fastest way to give a model access to the machine it's running on.

local_fs — Filesystem access

Always available. Provides read, write, search, and metadata operations scoped to a configured directory. All paths are validated against the allowed directory; attempts to escape with ../ are rejected.

Configure the allowed root via contenox config set local-exec-allowed-dir /path/to/project or the --allowed-dir flag.

Tools

ToolParametersDescription
read_filepathRead the full content of a file. Also satisfies the read-before-mutate prerequisite for write_file / sed against the same path.
write_filepath, contentWrite content to a file (creates parent dirs, overwrites). For existing files, requires a prior read_file or read_file_range against the same path in this session.
list_dirpath (optional)List entries in a directory (dirs marked with /)
read_file_rangepath, start_line, end_lineRead a specific line range. Like read_file, satisfies the read-before-mutate prerequisite.
greppath, patternFind lines containing a string (returns line_number: content)
sedpath, pattern, replacementReplace all occurrences of a string in a file. For existing files, requires a prior read_file or read_file_range of the same path in this session.
count_statspathCount lines, words, and bytes (like wc)
stat_filepathGet file metadata: name, size, mod time, isDir

Read-before-write contract

write_file and sed against an existing file are blocked unless the same session has previously called read_file or read_file_range on that path. The model receives a soft denial it can act on: it sees a tool result instructing it to read the file first, then retry the write. New files (paths that do not yet exist) are unaffected.

This is a deterministic guard — no LLM judgement involved — designed to prevent confabulated edits to files the model has never seen. The contract is scoped per session: a read in one contenox session does not satisfy a write in another. The state lives in a private local_fs_reads table the tool maintains itself; the chain engine has no visibility into it.

If the model uses local_shell (cat, head, grep, sed) instead of local_fs.read_file, the guard does not count it as a satisfying read — by design. The shell tools are not bounded the same way and broadening the guard to recognise their output reliably is impractical. Prefer local_fs.* tools for file inspection (the default chains include a TOOL PREFERENCE system-prompt addendum that nudges the model toward this).

tools_policies.local_fs keys

Tighten the sandbox per task by adding a tools_policies.local_fs block to execute_config:

KeyTypeDefaultDescription
_max_read_bytesint1048576 (1 MiB)Max file size for read_file / read_file_range / grep / sed / count_stats. 0 or negative = unlimited. Larger files return an error so the model can narrow with read_file_range.
_max_output_bytesint524288 (512 KiB)Max byte size of any tool result returned to the model (UTF-8 bytes). Prevents listing a huge directory or grepping a large file from blowing up context. 0 or negative = unlimited.
_max_list_depthint6Cap on list_dir(recursive=true). Hard-capped at 32 regardless of policy.
_max_grep_matchesint5000Stops grep after this many matching lines and returns an error so the model narrows the pattern. Hard-capped at 500000.
_denied_path_substringscomma-sepemptyPath substrings that always reject (e.g. node_modules,.git/,dist/). Matched against the path relative to the allowed root.
"tools_policies": {
  "local_fs": {
    "_allowed_dir": ".",
    "_max_read_bytes": "1048576",
    "_max_output_bytes": "524288",
    "_max_list_depth": "6",
    "_max_grep_matches": "5000",
    "_denied_path_substrings": "node_modules,.git/,dist/,/.next/,/out/,package-lock.json"
  }
}

Values are strings even when conceptually numeric — tools_policies is the chain's policy carrier and uses string values uniformly across tools. The default chains (default-chain.json, default-run-chain.json) ship with the table above as their local_fs block.

Chain example

"execute_config": {
  "model": "qwen2.5:7b",
  "provider": "ollama",
  "tools": ["local_fs"]
}

webtools — HTTP calls

Always available. Lets the model call any HTTP endpoint via per-verb tools. Unlike remote tools (which require an OpenAPI spec), webtools exposes six generic verb tools — the model picks the verb, URL, query params, headers, and body at call time.

Caution

Because the model controls the destination URL, every request is gated by SSRF and size limits configured via tools_policies.webtools (see below). The defaults block link-local / loopback / cloud-metadata addresses and cap response size at 1 MiB. Mutating verbs (web_post, web_put, web_patch, web_delete) trigger a HITL approval prompt by default. Do not point chains at untrusted user input without tightening the policy further.

Tools

ToolParametersDescription
web_geturl, headers?, query?HTTP GET. Use for read-only retrieval. Default-allow under HITL.
web_headurl, headers?, query?HTTP HEAD. Inspect headers / status without fetching the body. Default-allow under HITL.
web_posturl, headers?, query?, body?HTTP POST. HITL-approve by default.
web_puturl, headers?, query?, body?HTTP PUT. HITL-approve by default.
web_patchurl, headers?, query?, body?HTTP PATCH. HITL-approve by default.
web_deleteurl, headers?, query?, body?HTTP DELETE. HITL-approve by default.

Parameter shapes:

ParameterTypeDescription
urlstringAbsolute URL. Scheme must be in _allowed_schemes (default http,https). Host is checked against _allowed_hosts / _denied_hosts.
headersobject | stringJSON object {"X-Foo":"bar"} (preferred). A JSON-encoded string is also accepted for back-compat.
querystringURL-encoded query string (e.g. a=1&b=2). Merged with the URL's existing query.
bodyanyUsed by mutating verbs only. Strings sent as-is; any other JSON value is marshalled. Capped by _max_request_body_bytes (default 256 KiB).

tools_policies.webtools keys

KeyTypeDefaultDescription
_allowed_hostscomma-sepempty (any host)When set, only listed hosts pass.
_denied_hostscomma-sep169.254.169.254,169.254.170.2,localhost,127.0.0.1,0.0.0.0,::1,metadata.google.internal,metadata.azure.comEmpty string "" opts out of the SSRF baseline.
_allowed_schemescomma-sephttp,httpsBlock file://, gopher://, ftp://, etc.
_max_response_bytesint1048576 (1 MiB)0 or negative = unlimited. Truncated responses include a marker.
_max_request_body_bytesint262144 (256 KiB)0 or negative = unlimited. Oversized body blocks the call before sending.
_request_timeout_secondsint30Per-call timeout.
_max_attemptsint3Retries 5xx and transport errors only — never 4xx.
_initial_backoff_msint250Exponential backoff with jitter.
_max_backoff_msint5000Cap on the exponential backoff.
_disallow_redirectsbool-string"false"When "true", blocks all 3xx redirect-following.

Chain example

"execute_config": {
  "model": "qwen2.5:7b",
  "provider": "ollama",
  "tools": ["webtools"],
  "tools_policies": {
    "webtools": {
      "_allowed_hosts": "api.github.com,api.openai.com",
      "_max_response_bytes": "524288",
      "_request_timeout_seconds": "20"
    }
  }
}

local_shell — Shell command execution

Caution

local_shell gives the model direct access to run arbitrary commands on your machine. Never enable it in public-facing deployments or when processing untrusted user input.

Opt-in only — disabled by default. Enable per-invocation with the --shell flag:

contenox run --shell "clean up unused imports in the codebase"
contenox chat --shell "run the tests and fix anything that breaks"

Command policy is set in the chain, not on the CLI. Add a tools_policies block to execute_config:

"execute_config": {
  "model": "{{var:model}}",
  "provider": "{{var:provider}}",
  "tools": ["local_shell"],
  "tools_policies": {
    "local_shell": {
      "_allowed_commands": "git,go,make,ls,cat",
      "_denied_commands":  "sudo,su,dd,mkfs"
    }
  }
}
  • _allowed_commands — comma-separated list of permitted command names. When set, any command not on this list is rejected before it runs.
  • _denied_commands — comma-separated commands that are always blocked, regardless of the allowlist.
  • _allowed_dir — if set, the command (or its absolute path) must reside under this directory.

The default chains (default-chain.json, default-run-chain.json) ship with sensible defaults: common dev tools allowed, privilege-escalation and raw-disk commands denied.

To use local_shell with no policy restrictions (fully open), omit tools_policies entirely. Only do this in fully-trusted, local-only environments. Review tool use in your chain and use --shell only when you intend to grant shell access.

Tool

local_shell

ParameterTypeRequiredDescription
commandstringExecutable path or name
argsstringSpace-separated arguments
cwdstringWorking directory
timeoutstringDuration e.g. 30s
shellbooleanRun via /bin/sh -c (allows pipes, redirects, $VAR). Disabled when _allowed_commands or _allowed_dir is set.


ssh — Remote command execution

Always available. Runs a single command on a remote host over SSH. Supports password and private-key authentication.

Caution

The model controls the command string and target host. Restrict ssh to chains with tightly scoped system instructions and trusted user input only.

Tool

execute_remote_command

ParameterTypeRequiredDescription
hoststringRemote hostname or IP
userstringSSH username
commandstringCommand to execute
portintSSH port (default: 22)
passwordstringPassword authentication
private_keystringPEM-encoded private key content
private_key_filestringPath to a private key file
timeoutstringDuration, e.g. "30s" (default: "30s")
host_keystringExpected host key fingerprint (SHA-256)
strict_host_keyboolEnforce host key verification (default: true)

Returns a JSON object with fields: exit_code, stdout, stderr, duration_seconds, command, host, success.

Use output_template on the task to extract specific fields:

"output_template": "Exit <span v-pre>{{.exit_code}}</span>: <span v-pre>{{.stdout}}</span>"

Chain example

"execute_config": {
  "model": "qwen2.5:7b",
  "provider": "ollama",
  "tools": ["ssh"]
}

Always available. Appends a message to the chat history as a system message, or returns the message as a plain string when no chat history is active.

Tool

print

ParameterTypeRequiredDescription
messagestringText to append or return

Chain example

"execute_config": {
  "model": "qwen2.5:7b",
  "provider": "ollama",
  "tools": ["print"]
}

echo — Debug passthrough

Always available. Echoes the input back, prefixed with "Echo: ". Useful for verifying what a task receives during chain development.

Tool

echo

ParameterTypeRequiredDescription
inputstringText to echo

Chain example

"execute_config": {
  "model": "qwen2.5:7b",
  "provider": "ollama",
  "tools": ["echo"]
}

Adding custom local tools

Adding new local tools types requires modifying the Contenox Go source code and implementing the taskengine.HookRepo interface. For custom capabilities without writing Go, build a small HTTP service (FastAPI, Express, etc.) and register it as a Remote Tools instead — no code changes required.