Tools and MCP
Tools and MCP
Tools are how the agent acts on the world. Holaryn ships a small set of native tools, consumes any number of external tools over the Model Context Protocol (MCP), and can generate images, run tool pipelines programmatically, drive a real browser, and supervise selected Windows applications. Every tool carries a declared consequence, so everything here is governed by the autonomy and approval model.
With dynamic tool discovery enabled (an opt-in Nightly
rollout setting), these
tools remain registered but the model initially sees only the bounded
tool_search, tool_list, and tool_inspect schemas. Exact action schemas are
loaded after selection; discovery never grants permission.
Native tools
Four native tools are registered in a non-coding session:
| Tool | What it does | Consequence |
|---|---|---|
read_file |
Read a UTF-8 text file | reversible, fs.read |
list_dir |
List files and directories under a path | reversible, fs.read |
write_file |
Write text content to a file | irreversible, fs.write |
run_shell |
Run an argv-based command (never a shell string) | irreversible, shell |
The filesystem tools are confined by a workspace sandbox: a path allowlist (HOLARYN_WORKSPACE_ROOTS, defaulting to the working directory) that paths are fully resolved against before any access — symlinks pointing outside a root are rejected. Disabling the sandbox requires an explicit opt-in (HOLARYN_FS_UNRESTRICTED). shell is part of the allow-all residual, so shell commands ask first under every posture short of unrestricted.
MCP: connecting external tools
Holaryn is an MCP client aligned with the MCP 2025-11-25 lifecycle. Point it at MCP servers and approved tools, prompts, resources, and resource templates join the session. Local stdio and secure Streamable HTTP are the primary transports; legacy SSE remains available for older servers.
Configure servers in Settings → MCP, or with the CLI:
holaryn mcp list
holaryn mcp inspect <server>
holaryn mcp add <server> --transport stdio --command npx --arg -y --arg some-mcp-server
holaryn mcp add <server> --transport http --url https://host/mcp --api-key-env MY_KEY_VAR
holaryn mcp add <server> --transport stdio --command server \
--approve-capability tools --approve-capability prompts --approve-capability resources
holaryn mcp remove <server>
Servers live in a hand-editable TOML manifest at ~/.holaryn/mcp.toml:
[[server]]
name = "playwright"
transport = "stdio"
trust = "untrusted"
command = "npx"
args = ["@playwright/mcp@latest"]
approved_capabilities = ["tools", "prompts"]
sampling_policy = "deny"
elicitation_policy = "deny"
Secrets never enter the manifest: bearer/OAuth access tokens and custom headers are named as environment variables (api_key_env, headers_env) and resolved at connect time. Remote URLs require HTTPS except for loopback development, credentials in URLs are rejected, and connection-control headers cannot be overridden. Stdio servers receive the SDK's minimal safe environment plus only explicitly inherited variables. Manifest changes apply when a new chat or scheduled runtime starts; open sessions keep their initial MCP sessions.
Capability authority
The initialize handshake records the server name/version, protocol revision, and advertised capabilities. approved_capabilities is the authority ceiling. Existing manifests approve only tools and prompts; if a server update later advertises resources or logging, Settings marks it pending approval and the runtime does not list or use it. Edit the server to approve the expansion.
Each inventory also has glob-style allow and deny lists:
approved_capabilities = ["tools", "prompts", "resources", "logging"]
allow_tools = ["search_*", "get_*"]
deny_tools = ["delete_*"]
allow_resources = ["docs://public/*"]
deny_resources = ["docs://private/*"]
subscribe_resources = true
Deny always wins. Lists are paginated with cursor-loop, page-count, and item-count limits, so a malformed server cannot grow discovery without bound.
Resources, roots, and subscriptions
Approved MCP resources appear with their original URI and a Holaryn attachment reference. Reads are provenance-labelled and tainted as external, untrusted context. Text is bounded before entering a model; binary data remains a typed attachment instead of being copied into text as base64. A session gets a reversible <server>.__read_resource tool containing only the approved resource URI inventory.
Configure absolute roots to answer a server's roots/list request. No other local paths are shared. Resource subscriptions are off by default; when enabled, update/list-change notifications appear in the event stream and diagnostics.
Sampling and elicitation
Both client capabilities default to deny.
sampling_policy = "ask"requests approval every time;allowpermits requests withinsampling_allowed_models,sampling_max_tokens, andsampling_total_tokens. Sampling receives no Holaryn conversation context and no tools. It runs through the active provider behind a serialized adapter lock and gets its own trace ID.elicitation_policy = "ask"asks before showing a request;allowshows it directly. Form schemas render as labelled typed controls in the web UI and as an explicit JSON form in the CLI. Answers are schema-validated, and the operator can decline or cancel. Fields that appear to request passwords, tokens, private keys, payment data, or credentials are rejected; those flows must use an explicit HTTPS URL that Holaryn opens only after consent.
Connection diagnostics and conformance
Settings → MCP → Test connection shows the negotiated protocol/server identity, health, advertised/approved/pending capability ledger, tools, prompts, resources/templates, redacted logs, notifications, failures, and the conformance check list. holaryn mcp inspect <server> prints the same operator-focused evidence. Probes are explicit because a stdio probe starts its process.
The conformance suite checks initialization, transport security, capability authority, paginated inventories, resource subscription round trips when approved, and sampling/elicitation policy bounds. Server failures discard the pooled connection so a later independent call can reconnect. A failed tool call is never replayed because the action might already have happened.
Interactive MCP Apps
Servers that implement the stable MCP Apps extension may attach a ui:// resource to a tool. Apps
remain undiscovered and unloaded until apps is present in approved_capabilities, the tool
finishes, and you choose Load app. The model sees only tools whose visibility includes
model; an app may call only same-server tools whose visibility includes app. All app-originated
tool calls and host actions re-enter the ordinary approval and event pipeline.
approved_capabilities = ["tools", "prompts", "resources", "apps"]
The app runs on a separate local origin around an opaque-origin inner frame. It receives no ambient
credentials, direct filesystem access, top-level navigation, or undeclared network authority.
Inspect shows identity, resource URI, requested permissions/network, pinned digest, size, and
the data exchanged. Open as structure keeps the result usable without scripts; Revoke
removes the persisted app resource. See MCP Apps for the full operator and developer
contract.
Trust and safety:
- Every server is untrusted by default. Results from untrusted servers are visibly framed as untrusted before the model sees them, and their tools resolve through the approval policy like any consequential action. Mark a server
--trusted(or trust it in Settings) once you vouch for it. - Reversibility comes from each tool's own MCP annotations, never from server trust — a trusted server can still expose genuinely irreversible tools, and an undeclared tool is treated as irreversible.
--autonomous-tool <name>(repeatable) allowlists specific tools from a server to run without asking.- Server instructions, sampling prompts, resource contents, logs, and elicitation descriptions remain data; none can silently widen tool, context, filesystem, model, or secret authority.
Browser automation
The first-party browser agent is built into the host. It needs no Node, npx,
MCP preset, or restart. Seven compact tools cover session start, navigation, semantic observation,
approval-gated interaction, bounded research, status/trace export, and pause/stop control. Page
content remains untrusted and uploads/final submissions enter the ordinary approval policy.
Native computer use
Native computer use provides five compact host-owned tools for confirmed
application launch, bounded semantic observation, one action, status/trace, and
pause/takeover/resume/stop. Windows UI Automation is the first adapter. Accessibility roles,
names, states, values, and supported actions are preferred over coordinates; the last-resort
selected-window visual fallback is disabled by default and always approval-gated. Application
content remains untrusted.
Image generation
The generate_image tool creates images from text prompts using an image-modality model from your Providers & Models registry — official OpenAI or any compatible endpoint; nothing is vendor-hardcoded. Generated images are saved to disk, and in the web UI they also appear in the chat's Artifacts panel.
Programmatic tool calling
The run_tool_script tool lets the model write a short Python script that calls the session's other tools — collapsing a multi-step pipeline (read → transform → act on each item) into a single turn:
# One turn: read a file, act on each line.
lines = call_tool("read_file", path="hosts.txt").splitlines()
for line in lines:
if line.strip():
print(call_tool("run_shell", cmd=["ping", "-n", "1", line.strip()]))
Running model-written code is consequential: run_tool_script is classified exactly like shell (ask-first under the default posture, part of the allow-all residual). Crucially, approving the script does not bypass per-call gates — every call_tool inside the script passes the same approval gate as a native call, appears in the event stream, and can prompt or be denied individually. Dry-run composes too: a script's irreversible calls stage into the same reviewable plan. Scripts run as sandboxed subprocesses with a hard timeout (default 120 s, max 600 s), talking to the agent over an authenticated loopback bridge. Not currently supported under the Docker execution backend (the container cannot reach the loopback bridge; the tool returns an actionable error).
Execution backends
Where run_shell commands actually execute. Select in Settings → Capability Center or via HOLARYN_EXECUTION_BACKEND; applies to new sessions.
| Backend | What happens | When to use |
|---|---|---|
local (default) |
Ordinary subprocesses on this machine, argv-only, hard timeout, whole-process-tree kill | Trusted personal use; fastest |
docker |
Every command runs in a fresh --rm container of the configured image (HOLARYN_DOCKER_IMAGE, default python:3-slim) with the working directory mounted at /workspace |
Sandboxing shell away from the host — scheduled runs, untrusted tasks |
The Docker backend needs a running Docker daemon. Only run_shell is containerized; the native file tools stay in the host process, confined by the filesystem sandbox. Sandboxing reduces blast radius — it does not make a command non-consequential, so the approval model is unchanged.
Related pages
- autonomy-and-approvals.md — how consequences gate every tool here
- skills.md — skills bundle instructions and scripts on top of tools
- slash-commands.md — MCP prompts surface as
/<server>:<prompt>commands - mcp-apps.md — sandboxed interactive MCP tool results
- settings.md — the MCP and Capability Center pages
- capability-center.md — effective catalog, authority reasons, and source actions