Tool Policy

Govern what an agent may do with the tools it holds — by behaviour rather than by name, with a real gate instead of a sentence in a prompt.

Ticking a tool on an agent decides whether it has the tool. Tool policy decides what it may do with it.

The two are different questions, and only having the first one caused real problems. run_sql_query is one door for SELECT and for DROP, so a name-based allowlist admits or refuses both together. The old workaround was to ask in the system prompt — "confirm with the user before any write query" — which is a sentence, not a gate. A model can decline to follow it, and a model told to call again with confirm=true can simply do that.

Behaviour, not names

Tools declare what they do, and a policy speaks about those declarations. It has three rows, riskiest first:

On the screenCategoryMeans
Tools that change data permanentlydestructiveThe tool declares that it modifies data in a way that cannot be undone
Tools that reach outside this workspaceopen_worldThe tool declares that it talks to systems this workspace does not control
Tools that don't say what they doundeclaredMost tools: nothing declared, an outside server's "read-only" claim, or a tool that changes something without reaching outside

Which row a tool lands in follows from what it says it does. A custom tool says it in three answers (What it does: Reads only, Changes something or Deletes something; Safe to repeat; Reaches outside this workspace):

  1. A rule for one tool (below) decides first.
  2. Deletes something → change data permanently.
  3. Reaches outside this workspace, whatever else it does, reads included → reach outside. Every HTTP tool reaches outside until you untick it.
  4. Reads only and stays inside, from a tool you made or one of SynapseOrch's own → it runs, with no row to pass.
  5. Changes something and stays inside, or answers Not set → don't say what they do.

The categories are ordered riskiest first, and the first one a tool matches decides. Each maps to one of three outcomes:

DecisionOn the screenWhat happens
allowAllowThe call runs
confirmAsk firstThe run pauses and waits for a person
denyBlockThe call is refused and the agent is told so

For one tool

Under For one tool, Add a rule for one tool… picks a tool by name and gives it its own Allow, Ask first or Block, whatever its row says. It is stored beside the categories as tool:<name>, and it wins over the tool's category.

A tool's answers come from different places, and are trusted differently:

  • Your own custom tools say what they do in the tool editor; see Custom tools. Imported from a file, a tool's answers are a claim until someone saves it.
  • Synapse AI may mark a tool it makes as riskier than its method suggests (a GET that cancels an order), never safer.
  • An outside MCP server's answers are a claim (below).

Where a policy comes from

Three layers, each overriding the last:

  1. Built-in defaults — allow for every category.
  2. The deployment — the tool_policy_defaults setting, workspace-wide.
  3. The agent — its own tool_policy, set in the agent editor.

A category nobody mentions inherits from the layer above it.

The default is allow, on purpose

No existing agent changes behaviour when you upgrade. A closed default would have silently broken every agent already running — the same reasoning as the empty database allowlist. Opt in deliberately, starting with destructive.

A good first policy is destructive: confirm workspace-wide, then relaxing it per agent for the ones whose whole job is to write.

What confirm does

In chat, the turn stops and the pending call is parked together with the loop's last turn-boundary snapshot. You approve or reject it in the conversation, and the turn resumes from that snapshot. This is the one pause a chat turn cannot come back from on its own, which is why the call and the snapshot are stored together.

In a workflow, the run raises the same request and checkpoints, exactly as a human gate does, so an unattended run does not die holding a half-finished turn — it waits, and resumes when somebody answers.

In a Tool step, the step's one call is checked the same way — against the workspace's policy, or the agent's when the step's JSON names one. A blocked tool fails the step before any model call; one that asks pauses the run, and approving runs that exact call, once. See the Tool step.

For a helper (spawn_subtask), a tool the agent's policy blocks or would ask about is simply not handed over, and the agent is told which, so it can make the call itself — where it will be asked. A member of a Team agent asks under its own policy, and approving its call runs it as that member.

Where nobody can answer, asking is not possible, so it becomes refusing: a chat widget visitor's turn treats confirm as deny.

Saving to memory asks too, outside these categories: once a turn has read a tool's result, a file or an image, its remember waits for a person. See Memory.

MCP servers are trusted asymmetrically

A tool hint from an external MCP server is a claim, not a boundary — the MCP specification says so plainly. SynapseOrch treats it that way:

  • A third-party server saying "I am destructive" is believed.
  • A third-party server saying "I am safe" is not believed — a read-only claim is treated as undeclared.
  • SynapseOrch's own servers are declared in the repository and are believed either way.

So the undeclared category is where most third-party tools land, and setting it to confirm is the strongest single lever you have over a server you do not control.

Setting it

Per workspace — Settings → the tool_policy_defaults entry. Per agent — the Tool policy section of the agent editor, which overrides the workspace for that agent only.

An agent saved through the API keeps whatever policy it had; the field is explicit on the agent model precisely so a round trip does not quietly drop it.

On this page