Skip to content

Built-in tools

This page is the catalogue of everything an agent can call out of the box: what each tool does, what it costs you in risk, and what has to be true before it appears in the model's tool list at all.

Tool names are provider-safe snake_case and are the exact strings the model calls. Each tool carries a risk_level that decides whether a call needs approval.

A bare Options() — that is, allowed_tools=None — mounts ten tools: the fs pack (Read, Glob, Grep, Edit, Write, Bash, BashOutput, KillShell) and the web pack (WebFetch, WebSearch).

python
from noeta.sdk import Options
options = Options(system_prompt="…")          # allowed_tools defaults to None
# the agent sees: Read, Glob, Grep, Edit, Write,
#                 Bash, BashOutput, KillShell, WebFetch, WebSearch

Ten of those need no configuration; WebSearch needs an API key. Everything else on this page is gated somewhere else — memory and browser on an agent activation, open_app on a host-wired gateway, run_skill_script on the skills plugin config, MCP on a per-session registration.

Filesystem tools

Declared by the fs built-in plugin manifest (packages/noeta-sdk/noeta/builtins/fs/__init__.py).

ToolRiskWhat it doesSource
ReadlowRead a file (UTF-8), optionally sliced by line offset / limit. The full body is always offloaded as an artifact ref. Reads are unfenced — see below.noeta/builtins/fs/impl/read.py
GloblowMatch a glob pattern (** recurses) under path and return the matching paths, sorted and capped. The walk is rg --files: gitignore-aware, hidden skipped.noeta/builtins/fs/impl/read.py
GreplowContent search with a ripgrep regex, scoped by path, filtered by glob / type, opened up by -u. Runs rg through the ExecEnv, which must have it installed.noeta/builtins/fs/impl/read.py
EdithighReplace an exact old substring in an existing file; replace_all switches from unique-match to every occurrence.noeta/builtins/fs/impl/edit.py
WritehighWrite a file — create it (missing parent directories are created), or overwrite one already Read this session. content caps at 8 MB.noeta/builtins/fs/impl/edit.py
BashhighRun a command in the workspace; run_in_background detaches it and returns a job_id.noeta/builtins/fs/impl/shell.py
BashOutputlowRead status (running / exited), exit code, and a fresh output snapshot of a background job.noeta/builtins/fs/impl/shell.py
KillShellhighStop a background job you started (SIGTERM, then SIGKILL after a grace period).noeta/builtins/fs/impl/shell.py

The three write tools stage a proposed diff instead of touching disk while HostConfig.write_mode is "dry_run" (the default); "apply" performs real writes.

Reads are unfenced

The workspace root fences writes. For Read, Glob and Grep it only anchors relative paths: an absolute path is read where it points — a neighbouring checkout, a skill pack's bundled reference, anything the server process can read. This is deliberate (an agent routinely needs to read outside its workspace) and it is why the boundary that matters is the process's own file permissions, not the workspace root. A deployment that must not expose a path should not run the agent as a user who can read it.

Writes are the fenced half: Write / Edit resolve inside the workspace root. HostConfig.write_roots answers "may this task write here, outside its workspace?" per call; with no resolver an out-of-workspace write simply fails. Write additionally honours an optional workspace-relative allowed_path_globs whitelist bound at construction (empty = unrestricted); Edit ignores it.

Shell modes

ShellMode (noeta/runtime/shell_policy.py) is bound when the pack is built:

ModeEffect
OFFBash is not in the pack at all.
ALLOWLISTDefault. Only the structural allowlist below passes, argv-only.
ARBITRARYAny command without shell metacharacters runs through bash.

Under ALLOWLIST these argv patterns pass (noeta/builtins/fs/impl/shell_rules.py):

  • git status / git diff
  • pytest / uv run pytest
  • npm test / pnpm test
  • Grep / rg / find / ls — read-only search and listing, so an ALLOWLIST-mode agent without its own Grep / Glob tool can still search the workspace. Their validators reject the flags that shell out to another program or mutate the filesystem.

Host config can append more rules ({"program": …, "subcommand": …}); the built-ins are always kept. An operator-configured rule is looser than the curated built-ins: it means "this program may run", accepting any tail args that survive the metachar scan.

Shell metacharacters (|, ;, &&, >, …) are rejected before tokenization. This is path-containment plus an allowlist, not a process sandboxBash spawns external programs in the trusted workspace.

Web tools

Declared by the web built-in plugin manifest.

ToolRiskWhat it doesSource
WebFetchlowFetch a public web page, render it to Markdown, and answer the call's prompt against it with an auxiliary model call (Options.webfetch_model, defaulting to the session's main model) — the calling model reads the answer, not the raw page. HTTP upgrades to HTTPS, cross-host redirects are returned rather than followed, and fetched pages are cached for 15 minutes per URL. Always available.noeta/builtins/web/impl/fetch.py
WebSearchlowRun a web search and return ranked hits as Markdown. Mounted only when NOETA_WEB_SEARCH_API_KEY is set.noeta/builtins/web/impl/search.py

App tools

ToolRiskWhat it doesSource
open_applowPublish a workspace HTML app through the host's preview gateway. Mounted only when the host wires HostConfig.app_gateway.noeta/builtins/app/impl/__init__.py

Memory tools

Mounted only when the agent activates memory. Among the official presets that is main (and the internal consolidation curator).

ToolRiskWhat it doesSource
memory_writemediumWrite a Markdown memory file to the store. Optional description (one-line index summary), type (user / project / procedural / reference) and keywords (comma-separated retrieval aliases — the cross-lingual recall bridge) are stored as a frontmatter block the tool composes itself, merged per-field with any fence the text carries; the tool also stamps created / updated dates and a source_task ledger receipt, and a write under a new name reports similar existing memories so the model can merge instead of duplicating.noeta/builtins/memory/impl/store.py
memory_readlowRead the full text of a stored memory on demand.noeta/builtins/memory/impl/store.py
memory_searchlowCase-insensitive substring match over names and full text, with grep-style excerpts (up to 3 lines per memory, 10 memories; a truncated flag reports when more matched).noeta/builtins/memory/impl/store.py
memory_archivemediumRetire an outdated memory into the store's archive/ subdirectory — it drops out of the index, recall and search, but the file is never deleted, so a human can restore it.noeta/builtins/memory/impl/store.py

Browser tools

Mounted only when both hold: the agent activates browser ("browser" in AgentSpec.plugins), and the session is bound to a live sandbox container. Among the official presets that is the web subagent alone — main stays browser-free and delegates to it, so a non-sandbox deployment's tool set and stable prefix are untouched.

All five are high risk (any browser action can egress to any site), so they route through approval unless the session bypasses permissions.

ToolRiskWhat it doesSource
browser_navigatehighGo to a url; returns the page snapshot.noeta/builtins/browser/impl/__init__.py
browser_clickhighClick the interactive element at index (from the snapshot's numbered list).noeta/builtins/browser/impl/__init__.py
browser_typehighType text into the element at index.noeta/builtins/browser/impl/__init__.py
browser_extracthighRe-read the current page as a snapshot (no arguments).noeta/builtins/browser/impl/__init__.py
browser_screenshothighCapture a PNG and store it as a workspace artifact, returning its ContentRef. It is not fed to the model as vision.noeta/builtins/browser/impl/__init__.py

The four text tools return a page snapshot: page text plus numbered interactive elements. That numbering is what browser_click / browser_type address, so a snapshot must precede them.

Name, schema, and description are pinned by noeta, not by the container image — the model-facing contract (and therefore the stable-prefix cache bytes) must not drift when the sandbox changes its own tool names. Each tool delegates to a BrowserBackend, the one place the container's browser wire is pinned. It is a per-session tool pack injected like the fs pack, not an MCP connector.

Skill tools

ToolRiskWhat it doesSource
run_skill_scripthighRun an active skill's bundled script via an allowlisted interpreter. Present only when the skills plugin config sets allow_skill_scripts and an active skill ships a script.noeta/builtins/skills/impl/script.py

Control tools

Control tools are model-facing schemas that translate into engine decisions rather than into a Tool.invoke. Each is a control_tool contribution that self-gates: mounting is enablement.

ToolMounted whenPlugin
Taskthe agent activates delegation (derived automatically when it has children)delegation
TodoWritethe agent activates TodoWriteTodoWrite
AskUserQuestionthe agent activates AskUserQuestionAskUserQuestion
skillthe agent activates skill_invocation and the merged skill menu is non-emptyskills
run_workflowHostConfig.workflow_allowed is on (and the agent can delegate)react
structured_outputthe agent is a subtask / workflow helper spawned with a per-helper schema (not Options.output_schema, which is served natively by the provider)react

MCP tools

Remote MCP tools appear dynamically as mcp__<alias>__<tool> when MCP servers are registered and enabled per session. See ADR: MCP connectors.

In-process SDK MCP servers (create_sdk_mcp_server) are different: their tools keep their bare @tool names, with no mcp__ prefix. See Build custom tools.

Tool risk levels

There are exactly three levels, ordered low < medium < high.

LevelMeaning
lowNo side effects outside the agent's own state. Always allowed.
mediumMutates durable state, but only inside a confined directory — the memory store, for example.
highModifies the filesystem, spawns external processes, or reaches the live web. Goes through the approval gate.

Options.permission_mode decides which levels actually gate: "default" gates everything above low, "acceptEdits" exempts the three edit-class tools, and "bypassPermissions" gates nothing.

Next

Released under the Apache License 2.0.