Scoping the toolset: four dials

Lesson 2 of 5 in Tool Scoping and Least Privilege: Making the Dangerous Thing Impossible.

An agent’s permission surface is not what its system prompt says it may do. It is the union of everything reachable through its registered tools, with the credentials those tools hold, from the network it sits on. Write that sentence on the review checklist, because teams routinely audit the prompt and never audit the surface.

Four dials shrink it, in descending order of strength. Reach for them in order — each one is cheaper to reason about than the one below it.

The scoping ladder — strongest control at the apex

  1. The tool is not registered — the capability does not exist in this session

    The strongest control available, and the most under-used. A tool the model was never given cannot be requested, injected into, or mis-parameterised. Ask of every tool in the manifest: is this needed for the task in front of it right now? Task-specific toolsets beat one omnibus agent with forty tools, because the omnibus agent carries every tool’s blast radius into every conversation.

  2. Read-only by default — writes are opt-in, per task

    Split every integration into a read half and a write half, register the read half by default, and make the write half something a human deliberately turns on for a specific job. Most agent value is in reading; almost all agent damage is in writing. A read-only agent that gets fully hijacked leaks — bad, and bounded by the egress layer. A write-capable one deletes.

  3. Parameter constraints enforced in code — the model cannot pass what the schema rejects

    The tool exists and writes, but its arguments are bounded by validation the runtime performs: enums instead of free strings, amount ceilings, path prefixes, row limits, recipient lists the runtime fills in itself. The strongest version of this dial is removing the parameter — if the recipient always equals the ticket requester, the model should not be able to name a recipient at all.

  4. Scoped, short-lived credentials per task — the token expires and reaches almost nothing

    The tool and its parameters are open, but the identity behind them is narrow: one repository, one tenant, one customer record, valid for minutes. This is what turns “the agent shouldn’t look at other customers” into “the agent gets a 404 like anybody else.” It also bounds credential theft: a leaked token that expires in ten minutes and reads one repo is a nuisance, not a breach.

  5. Unscoped tool plus a prompt rule — no control at all — this is the baseline you are replacing

    A broad tool, a long-lived credential, and prose asking the model to be careful. This is the default shape of most demos and a large share of production agents. It is not a tier of the ladder so much as the ground the ladder stands on: everything above it exists because this one fails the moment the model reads hostile text.

Tool inventory

Start with a list, not a diagram. For every tool the agent can call, record: who it acts as, what it can reach, whether it writes, whether the write is reversible, and whether its output leaves your boundary.

Two questions do most of the work:

  • Which tools would I delete if I only had to support the top three tasks? Delete them. Add them back per task.
  • Which tool would I least like an attacker to hold? That one gets the parameter constraints, the scoped credential, and the gate — in that order.

Count MCP servers as part of the inventory. Connecting a server is a bulk permission grant: you have added every tool it exposes, now and after its next update.

Read-only default

Ship the read half first and measure. In practice this means splitting tools that vendors bundle:

# before                          # after
crm(action, record, fields)       crm_read(record)
                                  crm_update(record, fields)   # registered per task

The bundled version is worse than it looks: a single action parameter carrying "delete" is a write tool that reads like a read tool in the manifest, in the logs, and to the reviewer who approved it.

Read-only is also the cheapest way to satisfy the “at most two of three” design constraint you will meet in the next lesson — a session that cannot change state or send is a session where untrusted content and private data can safely coexist.

Parameter constraints

The tool schema is a contract the runtime enforces. Validate on the server side of the tool boundary and reject, loudly, into the trace:

issue_refund:
  order_id: must match an order read earlier in this session
  amount:   number, 0 < amount <= 100, and <= order.total
  reason:   enum [carrier_loss, damage, late_delivery]

Three habits worth stealing:

  1. Enums over strings wherever the value space is closed. It removes an entire injection surface (free-text fields that flow into queries, commands, or URLs).
  2. Relate arguments to session state — “an order this session actually looked up” is far stronger than “a well-formed order id.”
  3. Canonicalise before you compare. /workspace/../etc/passwd starts with /workspace. Resolve the path, then check the prefix.

Scoped credentials

The agent should never hold a credential broader than its task, and never for longer than the task.

  • Narrow the audience: one repo, one bucket prefix, one tenant, one customer.
  • Narrow the verbs: read-only tokens for read-only tools.
  • Narrow the clock: minutes, not months. Short TTLs turn credential leakage from a breach into an inconvenience.
  • Never reuse the operator’s identity by default. An agent running as a human admin inherits every permission that human accumulated over five years — the classic confused deputy setup, where the attacker supplies the intent and your agent supplies the authority.

When the agent acts on behalf of an end user, propagate that user’s authorization rather than a service account, so the agent cannot see data the requester could not.

Interactive checkpoint quiz (2 questions) — open this page in a browser to take it.