Part III — The Tool System

This part covers Chapters 9–13: an overview of the tool system first, then the smart-coding and security-analysis tools in turn, followed by how external tool services are connected, and the permission and approval mechanism that runs through every tool.


9. Tools overview

A tool is what lets the AI "do something" on your computer.

9.1 Three categories

  • Smart coding tools: read / write files, run shell, version control, find, etc. (Chapter 10).
  • Security analysis tools: hash, strings, IOC, executable parsing, disassembly, rule matching, traffic metadata (Chapter 11).
  • External tool services: tools from external systems like Notion, internal APIs, private knowledge bases (Chapter 12).

9.2 Tool switches

Each tool can be set to:

  • Allow: pass through.
  • Ask: pop an approval modal on every call.
  • Deny: reject directly.

Entry: the gear next to the mode slider in the input area (agent manager) → the "Configure tools" tab, per work mode. The three states actually constrain the run: "Deny" takes effect on both the main agent and sub-agents; "Ask" is wired into the real confirmation flow, pausing the call until you approve; uninstalling a plugin or external tool service also cleans up leftover tool config, keeping on-disk config in line with the tools actually available.

A steadier tool list: if fetching the tool list fails, it retries with backoff and only alerts after repeated failures; the fallback tool table is generated from the same source as the built-in tools, so the list no longer comes up mysteriously short.

9.2.1 Tool category lazy loading (on by default)

There are now well over a hundred tools; sending every tool description to the model on every turn would both eat context and slow responses. So only a commonly-used subset (a couple of dozen) is carried by default, and the rest are loaded by category when they're needed.

  • Always-resident categories: file read/write and search, command execution, tool introspection, sub-task delegation and todo planning — needed on almost every turn.
  • Categories loaded on demand (16): code intelligence, git, security analysis, threat intelligence, SBOM, external tool services, web search, skills, tests, lint, SAST, plugin marketplace, plugin management, GitHub, system info, time.
  • Two ways to load: relevant words in your question preload the matching categories automatically; the assistant can also call tools.load to pull in a category by name. External tool services connected mid-conversation are admitted automatically.
  • The assistant knows the list is incomplete: the system prompt says so and includes the category directory, so the assistant won't conclude a capability doesn't exist just because it isn't listed; when it queries its own available tools, the result also notes that "not visible ≠ not there — load the category first".

The switch: agent manager → the "Configure tools" tab → "Tool category lazy loading". Turn it off and every tool stays resident. When there aren't many tools available anyway (32 or fewer), the whole table is admitted directly and no lazy loading happens.

If you use a particular external tool service often, you can turn lazy loading off for just that service in the same panel so its tools are always available — see §12.4.

9.3 Tool namespaces

External-service tools are auto-prefixed (e.g. notion.*) to coexist with built-in tools cleanly.

9.4 Tool-call visibility

Each tool call appears as a collapsible card in the message stream containing:

  • Tool name + parameter summary
  • Elapsed time
  • Result (success / failure / truncated)

You can inspect each call independently for audit and traceability.

9.4.1 Hook execution visibility

Each hook execution now appears in the conversation stream as a system message, rendered as a collapsible block (Markdown content) similar to a tool call. You can see whether a hook ran, what it produced, and whether it affected the next step — no more black box.

9.4.1.1 Tool-execution idle timeout (default 10 min)

A safety net was added to tool execution: if a tool or sub-task makes no progress (no stream output / state change) for an extended time, it's wound up safely instead of leaving the whole turn hanging.

  • Trigger: continuous idle ≥ 10 minutes by default. Normal long tasks that keep producing output are unaffected.
  • Exemption: the sub-Agent family uses its own idle window + absolute ceiling (see §15.2.1), and is not killed by this 10-minute idle timeout by mistake.

9.4.1.2 Repeated-call notice (suspected spinning)

The assistant occasionally falls into a loop of calling the same tool with the same arguments over and over — it keeps moving, so the idle timeout never catches it, yet nothing is actually progressing while your quota keeps draining.

  • After 3 consecutive identical calls to the same tool, a note in the tool result asks the assistant to try a different approach; the wording escalates as the count climbs.
  • The session is marked "suspected spinning" in the session list, with the repeat count and tool name on hover; both the sidebar and the top-tabs layout show the mark. A notification is sent as well, so you notice even when you've switched to another session.
  • The mark survives an application restart, so it isn't lost on the way; it clears automatically once you send a new message in that session.
  • Polling tools (waiting on a sub-task, pulling command output, and the like) inherently need repeated calls and are excluded.

9.4.2 Trace panel

The stats bubble at the top of the session (StatsPopover) gains a new Trace panel next to the original Stats view: it lays out which tools the AI called in one run, in what order, with each call's inputs / outputs and duration — a timeline view that makes it easy to see how the AI walked through the work, and to spot "where did it go off the rails / where is the time being spent".

Each step shows:

  • Sequence / role / kind / source / tool name
  • Duration, prompt / completion / total tokens, model, finish reason
  • Success or not / error message / content preview

The summary card at the top shows the whole run: total steps, assistant turns, tool calls / failures, errors, gate injections, token usage, total tool time, wall-clock time, and the list of models used. Below it, a per-tool stats table lists each tool's call count / failures / P50 / P95 / max duration.

Data comes from server-side aggregation over the session JSONL (GetSessionTrace), loaded on demand when you open the popover — no live polling.

Timeline steps are clickable: click a step on the trace timeline to jump straight to the corresponding spot in the message stream, so reviewing "which step went wrong / where the budget went" is easier.

9.5 Let the assistant configure MCP servers itself

In addition to manually adding external services from the Settings panel, the assistant can configure MCP servers for you mid-conversation. AVL Code provides 9 mcp.* tools, typically used together with the built-in mcp-admin agent:

Tool Purpose
mcp.list_servers List all MCP servers
mcp.describe_server Detail one server (secrets masked)
mcp.list_tools Tools a server exposes
mcp.test_server Real-handshake probe with a temporary connection (30 s budget), distinguishing validate_args / connect / list_tools / ok
mcp.add_server Add (write, two-step)
mcp.remove_server Remove (write, two-step). The default MCP backend can also be removed now (the gateway used to refuse this, leaving the default backend undeletable)
mcp.toggle_server Enable / disable (write, two-step)
mcp.import_config Bulk import; recognises native format and the Claude Desktop {mcpServers:{...}} shape
mcp.export_config Export the current config (secrets masked)

Two-step (dry-run): write tools (add / remove / toggle / import) require an explicit confirm=true to actually persist; the first call returns a dry-run preview (full command line, routing, default-backend changes, conflicts) for you to review. This is a fallback gate when no PreToolUse defer hook is configured — the LLM can't single-step into a destructive action.

Secret masking: read-side tools only show the last 4 characters of authToken (the rest as asterisks), and environment variable values are fully masked, so credentials never leak into the conversation.

Typical conversation:

"Hook up mcp-trends-hub@1.6.0 for me."

Assistant → calls mcp.add_server (dry-run) → returns preview → you confirm → assistant calls mcp.add_server(confirm=true) → config written → calls mcp.test_server for a real handshake → reports 21 trending tools are available.