Local Tools

Local tools run on the same machine as Contenox. Most run directly inside the process; local_shell starts subprocesses when shell access is enabled. They are the fastest way to give a model controlled 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.

The filesystem root is set when Contenox registers the local tool:

  • contenox run / contenox chat: --local-exec-allowed-dir <dir> sets the root. Without a root, local_fs rejects file paths.
  • ACP and VS Code sessions use the editor/client workspace context where available.

tools_policies.local_fs controls read/output limits, denied path substrings, list filtering, and can override the root for a specific task with _allowed_dir.

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 full read_file against the same current file version 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. Satisfies targeted sed mutations, but not full-file write_file overwrites.
greppath, patternFind lines containing a string (returns line_number: content)
find_filespattern, path (optional)Find paths by glob pattern under the allowed root.
search_repopattern, path (optional), glob (optional), regex (optional)Search file contents across the repo or a subtree.
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 against an existing file is blocked unless the same session has previously called full read_file on that exact current file version. A line-range read is not enough for full-file overwrite because unseen content could be destroyed.

sed is a targeted mutator, so either read_file or read_file_range against the same path can satisfy its prerequisite. New files (paths that do not yet exist) are unaffected.

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 mutation.

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

Set per-task read/output limits and denied path substrings by adding a tools_policies.local_fs block to execute_config:

KeyTypeDefaultDescription
_allowed_dirpathregistration rootOverride the allowed filesystem root for this task. Relative paths resolve against the active workspace/cwd where available.
_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.
_max_find_resultsint200Caps find_files path results. Hard-capped at 5000.
_skip_dir_namescomma-sep.git,node_modules,.venv,__pycache__,.next,dist,.cache,vendor,target,.idea,.vscodeDirectory basenames omitted by recursive list_dir, find_files, and search_repo. Set to empty string to disable filtering.
_list_extensionscomma-sepemptyOptional file extension filter for recursive list_dir output, e.g. .go,.md,.json.
_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",
    "_max_find_results": "200",
    "_skip_dir_names": ".git,node_modules,.venv,__pycache__,.next,dist,.cache,vendor,target,.idea,.vscode",
    "_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 conservative limit, root, and deny-substring defaults.

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.

For direct CLI use, local_shell is opt-in. Enable it per invocation with --shell:

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 executable or script path must reside under this directory. The global --local-exec-allowed-dir flag sets the same executable/script boundary for an invocation.

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 enable shell only when you intend to grant command execution.

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.

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.