Tools and approvals

Tools are how a model reaches outside its own context window. A tool reads a file, writes one, queries a database, calls an API. Without tools, the model is a closed system; with tools, it can act on the world. Polycode treats tools as a separate authorization surface from providers — configuring a provider lets a model talk; the approval gate decides what it’s allowed to do — and asks for explicit permission before a mutating tool runs against your machine. This article walks the model end-to-end.

What counts as a tool

Polycode exposes two families:

  • Built-in file tools — a fixed set of nine scoped filesystem tools shipped with the app: Read file, Write file, Edit file, Delete file, Rename file, File info, List directory, Find files, and Grep search. They run inside Polycode’s sandbox and operate only on the folders you grant as projects. There is no shell or arbitrary-exec tool — the Mac App Store sandbox prohibits spawning user binaries, so Polycode doesn’t ship one.
  • MCP server tools — anything reachable through the Model Context Protocol. You add a server, Polycode speaks MCP to it, and the server’s tools appear inline in the catalog next to the built-in tools. This is the extension surface: Postgres, GitHub, your internal API gateway, a homegrown service — anything that exposes an HTTP MCP endpoint. Unlike the file tools, MCP tools don’t need a project — they’re offered even in a session with no project open.

Polycode is a first-class MCP host

The Model Context Protocol is how a host application advertises tools to a model and routes the model’s tool-call decisions back to those tools. In Polycode it is the tool layer — a model that wants to call a function calls it the same way whether the tool came from a built-in or from an MCP server.

Polycode connects to MCP servers over the streamable-HTTP transport — a server is an HTTP endpoint, local or remote. (Stdio/child-process MCP servers, which require spawning arbitrary binaries, are not supported under the App Sandbox.) Add a server two ways from Settings → MCP servers:

  • Browse registry opens a searchable browser over the official MCP registry (registry.modelcontextprotocol.io). Type to search server-side, page through results, and click Add on any HTTP-capable entry. If the registry is unreachable, the browser shows an error banner and an empty state — retry, or close it and add a server manually.
  • Add opens a sheet for a custom endpoint — a name and a base URL like https://mcp.example.com/server, plus an Offer tools to every model toggle. On, every model answering in parallel can use the server’s tools; off, only the primary model sees them. The toggle governs who is offered the tools, never whether they run — per-tool Always / Ask / Deny approval settings apply either way.

Each server row in Settings → MCP servers carries a chip naming its tool audience — All models for a server offered to every parallel model, Primary only otherwise (a disabled row makes no claim). The row’s menu holds Edit…, which renames the server or flips the every-model toggle in place; the base URL is the server’s identity, so changing the endpoint means removing and re-adding it. Enable/Disable, Refresh, and Remove live in the same menu.

A status strip at the bottom of the sidebar summarizes your servers — MCP · N available, with an error count and a status dot, and a gear that opens the Settings window, where MCP Servers is one of the tabs. The count reads availability, not live connections — an idle server with a retained tool roster still counts. Per-server detail lives in Settings → MCP servers and in the inspector, not in the sidebar strip.

Per-tool approval defaults

Polycode does not have a “trust everything” switch, nor a per-conversation on/off list. Instead, every tool carries a default approval you set once, globally, in Settings → Tools“Default approval per tool. Native and MCP-hosted tools share this surface.” The control is a three-way segmented picker:

  • Always — skip the prompt and run the call (treated as approved for the session).
  • Ask — present the approval card every time a new call comes up.
  • Deny — skip the prompt and reject the call outright.
Settings
Default approval per tool. Native and MCP-hosted tools share this surface.
Read file
file_read
AlwaysAskDeny
Write file
file_write
AlwaysAskDeny
Delete file
file_delete
AlwaysAskDeny
query
Run a read-only SQL query
postgresAlwaysAskDeny
Tool budget
How many tools one answer may call before Polycode checks in, and where it stops. Asked once per answer.
FocusedStandardExtended
Checks in after 75 tool calls; stops at 250.
Settings → Tools — per-tool Always / Ask / Deny defaults

The defaults are deliberately asymmetric: read-only tools (Read file, List directory, Grep search, Find files, File info) default to Always, so reads run silently; mutating tools (Write, Edit, Delete, Rename) default to Ask, so you see a prompt before anything on disk changes. You can override any tool either direction — flip a read to Ask to review every new path, or set a tool to Deny to block it everywhere. MCP-hosted tools sit in the same list with the same picker; a tool whose server doesn’t annotate it as read-only is treated as mutating (defaults to Ask).

The inline approval card

When a tool defaults to Ask and there’s no session grant covering it, Polycode pauses that peer’s stream and surfaces an inline approval card in the conversation. The card titles itself “Approve read?” for a read or “Approve toolname?” for a write, names the provider that asked (“…wants to run”), and shows a details well — the target path, the scope (read-only or read/write), and the tool’s action description.

Read the README and summarize the build steps
Approve read?
Anthropic wants to run file_read
pathREADME.md
scoperead-only
actionRead a file within the project. Supports byte-range pagination via `offset` and `length`; large files truncate with a `nextOffset` continuation hint so the model can paginate without throwing.
Read-only preview not required
A tool wants to run — Deny, Approve, and a Remember-for-this-session checkbox

It offers a Deny button, an Approve button (the default action — Return approves), and a Remember for this session checkbox:

  • Deny — the call doesn’t run; the peer is told and continues without that information.
  • Approve (unchecked) — the call runs this once. The next matching call prompts again.
  • Approve with Remember for this session checked — the call runs and Polycode caches the grant for the rest of the conversation.

If several peers in a fan-out request the same call, you see one card — “Approving here applies to all N peers” — and resolving it resolves them all.

Session grants and the cache

A “remember” grant is scoped to the project, the tool, and the target directory — not to a single file and not globally. Approving Edit file on src/foo.swift caches a grant for that tool anywhere under src/ in that project; a call to a different directory prompts again. In a session with no project open — where MCP tools are still offered — the grant is keyed to that session instead of a project, with the same approval flow. Either way the grant lasts until the conversation changes — opening or switching to another session clears the whole session cache, so a fresh chat starts clean.

Settings → Tools surfaces these grants under Active session approvals (each row shows the tool and its path prefix — (any path) when no path was involved). A Clear session cache button drops every cached grant at once; clearing is non-destructive — the next matching call simply prompts again. There is no global “trusted forever” state: the persistent part is the per-tool Always / Ask / Deny default, and session grants always evaporate with the conversation.

Reviewing file changes (the diff viewer)

When a write-class file tool (Write, Edit, Rename, Delete) wants to run, its approval card carries a View diff button. Opening it shows exactly what the call will change before you approve:

  • Create / Edit / Delete render a unified before/after diff with @@ hunk headers, + added lines (green) and - removed lines (red).
  • Rename shows From: and To: paths.

Read-only tools show “Read-only preview not required” and MCP tools show “No diff preview for MCP tool” — neither offers a diff. The diff is the surface that lets you see a mutating tool’s exact effect before granting it.

The tool budget

When models run agentic tool loops — calling tools, reading results, calling again — Polycode budgets how far one answer runs before checking in with you. The budget counts every tool call across every model answering, not any single model’s loop. With the default Standard budget, after 75 tool calls the answer pauses and a warning banner appears above the conversation:

Polycode has called 75 tools while answering. Continue?

Two choices: Continue (Return) releases every model for the rest of that answer — no fixed further allowance, no second check-in — or Stop and summarize (Escape) ends every model’s tool use, wrapping each loop with one final text turn. The check-in is asked at most once per answer. Past it, a hard ceiling still applies: at 250 calls (Standard) the answer stops calling tools outright — the backstop for a runaway loop.

The budget is configurable in Settings → Tools → Tool budget as a three-way preset: Focused checks in at 25 and stops at 100, Standard (the default) at 75 and 250, Extended at 200 and 600. The setting is global — it applies to every conversation, not per-project. If the window isn’t focused when an answer pauses, Polycode can post a notification — toggle it under Settings → Privacy → Notifications (“Notify when an agent loop pauses at the tool-call soft cap”).

Tools and fan-out

In a fan-out, peers and the primary do not get the same tool set. Under Polycode’s scoping rule (Pattern C):

  • Native file tools split by what the tool does. A peer can read a file, list a directory, or grep — and different peers may make different read calls on the same prompt — but a peer can never call Write, Edit, Delete, or Rename. Those four are matched by name and reserved for the primary, so when the round closes and your primary synthesizes, it’s the one that can act on disk.
  • MCP tools split by server, not by tool. The audience is whatever the server’s Offer tools to every model setting says — the same thing the All models / Primary only chip reports on each server row. A server set to All models offers every tool it exposes to every model answering in parallel, including tools that write; a server left at Primary only offers none of them to peers. Polycode does not inspect what an individual MCP tool does, so if a server can make changes and you don’t want peers reaching it, leave it Primary only.

Your per-tool Always / Ask / Deny settings apply on both paths — scope decides who may ask, approvals decide what actually runs.

This keeps a four-way fan-out from issuing four conflicting edits to the same file. The audit trail still lives in the inspector: each peer’s trace card carries a TOOLS (N) disclosure listing the calls it made — the tool name, a best-effort target path, and one status per row (a latency, cached, failed (Nms), or pending / running / rejected for a call that never ran), attributed per peer.

Revoking access

  • Block a tool globally — set its default to Deny in Settings → Tools. It stops running in every conversation.
  • Drop remembered grantsClear session cache in Settings → Tools (or just start/switch conversations).
  • Remove an MCP server — disconnect or delete it in Settings → MCP servers. That removes its tools from every conversation at once.

What’s next

  • The inspector — read the full per-tool, per-peer audit trail for any past exchange.
  • Setting up providers — providers and tools are separate surfaces; this is where you configure the model side.
  • Fan-out and consensus — how peers’ parallel tool calls fold back into a single synthesized reply.