Security

How to Scope Each Tool an Agent Can Call

Antonio Evans9 min read

A cutaway diagram of an agent runtime: a central decision loop box connected by arrows to a row of tool boxes (read file, write file, query database, call webhook, send email).
Key points

Key points

  1. 1Build the tool inventory from configuration, credentials, delegation paths and runtime logs, not from the system prompt.
  2. 2For each agent-tool pair, write a scope contract that fixes data fields, action verbs, argument constraints, confirmation gates and limits.
  3. 3Enforce the contract in code at the invocation point so a missing contract means denial, not a default allow.
  4. 4Log every call with the contract version attached and re-review contracts whenever a tool, connector, model or delegation path changes.

Start the inventory from four sources

Least privilege begins with a complete list, because you cannot narrow access to a tool you did not know existed. Agents compound the problem: the Frontier Model Forum describes them as systems that invoke tools, query external services, hold memory across sessions and coordinate with other agents, and notes that this expanding tool access pushes the attack surface well beyond what any single prompt exposes (FMF issue brief). Your inventory has to reflect that, which takes four sources rather than one.

  1. Declared configuration. Tool declarations in your framework, server manifests, plugin registries and function-calling schemas. Export them programmatically; do not read the documentation.
  2. Credential material. Every OAuth grant, API key and service account the runtime can present. A tool you never registered but whose credential sits in the environment is a live tool.
  3. Delegation paths. Sub-agents, orchestrator handoffs and inter-agent messaging. A child agent's toolset belongs in the parent's effective permission set.
  4. Runtime telemetry. Actual calls from logs. This is how you find the connector someone wired in for a demo and never removed.

If your agents live inside SaaS platforms, commercial inventory tooling already maps this. AppOmni's Agent Inventory surfaces agents running in Salesforce Agentforce, ServiceNow and Microsoft 365 Copilot, lists every tool each one may use along with its execution mode, and lets administrators remove permissions platform by platform (AppOmni). For in-house builds, a nightly schema export plus a week of call logs gets you the same view.

Record one row per agent-tool pair, not per tool. The same tool usually needs a different contract for a support agent than for a finance agent, and the pairing is what review actually examines.

ColumnWhat to recordWhy it earns its place
Tool and ownerRegistered name plus maintaining teamAssigns the re-review duty
TransportDirect API, MCP server, plugin, sub-agentShows where enforcement can live
CredentialsAccount or token the tool runs asExposes shared service accounts
Data touchedSystems, tables, fields, document setsDrives the data scope decision
Verbs exposedEvery operation offeredSeparates read from write
Chained dependenciesTools whose output feeds this oneSurfaces composition risk
Live callersAgents that can invoke itFinds orphaned grants

Red teamers reach for the same artefact first: enumerate every tool the agent can call, every external system it can reach and every delegation path, because that defines the blast radius of a successful attack (DeepTeam). If your inventory is thinner than the one a tester would build, it is not finished.

Trace side effects and data reach per tool

A tool's name describes its intent, not its reach. "Search documents" can mean a scoped index or a full-text scan across every tenant. The failure is silent, which is why the Zscaler writeup on agent access is blunt about it: a support agent authorized for a CRM should be scoped to the records its current task involves, not the whole tenant's data and not other tenants' (Zscaler).

Read the implementation and answer five questions in writing for each tool.

  • What does it read? Name tables, fields, document sets, and any join or filter that quietly widens the result.
  • What does it mutate? Writes carry more risk than reads and deserve tighter scoping; the write path should get the tightest scoping of all (Arthur AI).
  • What does it emit beyond its return value? Logs, audit events, webhooks, emails, queue messages and outbound HTTP are all side effects.
  • What is the blast radius of one bad call? If a single incorrect invocation could expose data, move money or change production, you are doing blast-radius engineering and should design for bounded impact rather than best-case behavior (Oso).
  • What does it cost per call? Rate limits and spend caps are scope too; a loop on a metered API is a scope violation with an invoice attached.

A worked example shows how wide the gap can be. A support agent holds a get_customer tool that takes a numeric customer ID. The name implies a narrow lookup. The implementation applies no ownership filter, so it reads every customer in the tenant. The true data reach is the entire customer table, and the correct contract restricts the query to the customer matching the authenticated session, caps results at one record and refuses any other ID.

Do the same trace for memory and retrieval. Poisoned memory means future sessions inherit compromised context, and retrieval bias can shift planning before any tool is called, which is why per-component reviews miss it (Christian Schneider). A retrieval tool is a data-reach question even though it looks like plumbing.

Write the scope contract for each tool

The contract is the artifact this procedure exists to produce. It is a machine-readable record per agent-tool pair with five fields: data scope, verbs, argument constraints, confirmation gates and limits. OWASP's agentic threat work places least privilege and human approval for high-impact actions among the core mitigations against tool misuse, the case where an agent stays inside its permissions but chains calls into data destruction or record export (Idan Habler, OWASP ASI).

The governing principle fits in one line: the model proposes actions, policy authorizes them. If the agent's reasoning becomes your authorization logic, you have deployed an incident generator (Oso). Write the contract so a runtime, not the prompt, makes the decision.

Five fields, filled in for one tool

FieldQuestion it answersExample for update_ticket
Data scopeWhich records and fields may be touchedTickets in the caller's queue; fields status, priority, assignee
VerbsWhich operations are permittedupdate only, never delete, create or bulk_update
Argument constraintsWhat values are legalStatus from a fixed enum; assignee a member of the caller's team
Confirmation gateWhere a human must approveAny status change to closed; any priority escalation above level 2
LimitsVolume and rate20 calls per session, 5 per minute, none after session timeout

An argument constraint outperforms a paragraph of prompt text. With status drawn from an enum, an injected instruction to close every ticket fails validation instead of relying on the model to decline.

Read and write belong in separate contracts even when one tool offers both, because they are different risks granted independently (Zscaler). Identity is the foundation underneath. Give each agent its own discrete identity rather than a shared service account inherited from the deploying team, or attribution and revocation stop working (Okta).

Enforce it where the call is built

The contract must execute at the invocation point. A pattern that holds up: a policy map keyed by agent identity and tool name, consulted inside the same function that assembles the request.

SCOPE = {
    ("support-agent", "update_ticket"): {
        "fields": {"status", "priority", "assignee"},
        "status_enum": {"open", "pending", "resolved", "closed"},
        "needs_human": lambda args: args.get("status") == "closed",
        "max_calls_per_session": 20,
    },
}

def call_tool(agent_id, tool, args, session):
    contract = SCOPE.get((agent_id, tool))
    if contract is None:
        raise PermissionError("no contract for this agent-tool pair")
    unknown = set(args) - contract["fields"]
    if unknown:
        raise PermissionError(f"fields outside scope: {unknown}")
    if "status" in args and args["status"] not in contract["status_enum"]:
        raise ValueError("status value outside enum")
    if contract["needs_human"](args):
        return request_human_approval(agent_id, tool, args)
    if session.calls(agent_id, tool) >= contract["max_calls_per_session"]:
        raise PermissionError("session call cap reached")
    return invoke(tool, args)

Notice what the code never does: it does not ask the model whether the call is appropriate. Deny-by-default means a new agent starts with zero tool permissions, each one added per persona and approved, and the policy engine rather than the model decides what is reachable (Skyrelis). A missing entry is a denial.

Run this checklist before shipping any contract.

CheckPass condition
Every inventory row has a contractNo agent-tool pair without one
Verbs are enumeratedNo wildcard or admin scopes
Read and write granted separatelyTwo contracts when both are needed
Irreversible verbs gatedDelete, send, pay, publish, permission change
Limits presentRate, volume and session caps
Contract in version controlReviewable in a diff

Cut indirect paths and tool chaining

Contracts can each be correct while the composition is dangerous. OWASP's multi-agent threat guide describes attack patterns that end with an agent orchestrating sequences of legitimate tool calls into an unauthorized outcome, and StRIDE-style component reviews miss them because no individual component is broken (Christian Schneider). Treat chaining as a scoping problem in its own right.

Three patterns to hunt down:

  • Read then send. Any tool returning sensitive data paired with any tool reaching the outside world. Restrict the send tool to allowlisted addresses or domains and cap message size.
  • Read then write. A lookup feeding an update. If the lookup returns records the caller does not own, the update inherits the overreach. Scope the lookup, not just the write.
  • Grant then act. A permission-granting tool paired with a tool that uses the grant. Gate the granting tool every time with a human.

Delegation adds a second layer. In multi-agent systems a sub-agent's output is consumed by the orchestrator with less scrutiny than a user input, so an adversarial payload can cross trust boundaries between agents (DeepTeam). The scope rule is intersection, not union: a delegated task runs with the overlap of parent and child contracts, never with whatever the child happens to hold. A read-only research sub-agent cannot pass a write instruction to an executor holding broader grants unless the parent holds them too.

Volume needs governing as well, not only verbs. An agent can stay entirely inside its authorized permission set while querying records one at a time below any threshold, in ways inconsistent with its purpose, and that gap is exactly what least-agency thinking addresses (Zenity). Practical counters: per-session call caps, per-tool record caps, and alerts when a session's call pattern departs from its baseline.

Tool calls must also be tested adversarially, not assumed safe because scope looks tight. Red teaming an agent means probing what it does rather than what it says, because a compromised agent will act without raising an error: it deletes files, leaks credentials or makes unauthorized calls while looking helpful (Fiddler).

Enforce at runtime and log the contract version

A contract in a wiki is documentation. A contract in the request path is a control. Three enforcement points, in order of preference:

  1. The tool wrapper. Every invocation passes a single function that loads the contract and validates. Cheapest to build, easiest to audit.
  2. The MCP server or connector. For tools you host, validate server side so no client can bypass the rule. The OWASP agentic initiative maintains a practical guide for securing MCP servers, the connection point between assistants and external systems, alongside its broader resource set (OWASP ASI).
  3. The identity layer. Discrete per-agent identities, short-lived per-session tokens instead of standing credentials, and revocation that actually revokes (Okta).

Logging needs four fields on every call: agent identity, tool name, the contract version that authorized it, and the outcome (allowed, gated, denied). Add argument keys rather than values, so a denial is diagnosable without copying sensitive data into logs.

Two alerts pay for themselves quickly. A denial spike on one agent-tool pair means someone is probing or a prompt changed. A first-ever call from a pair the inventory marked unused means an unreviewed grant went live. Microsoft's red teaming tooling takes the same posture, supporting scheduled adversarial runs after deployment rather than only before it, with findings logged and tracked over time (Microsoft Foundry).

Drive re-review from change events

Calendar reviews miss the thing that actually changes agent risk: the tool surface. Trigger re-review from events, with a slower scheduled sweep as a backstop.

TriggerWhat to re-check
New tool or connector registeredInventory row, contract, chaining pairs, egress allowlist
Tool schema or API version changeData scope and verbs, since fields and operations drift
Model or prompt changeGates and argument constraints, via adversarial rerun
New sub-agent or delegation pathIntersection rule, child contracts
Denial or drift alert firesThe contract, and whether the grant is still needed
Quarterly sweepEvery contract; retire tools with no calls in 90 days

Scope creep is the predictable failure. Research from Entro Security cited by Zenity found that 97 percent of non-human identities carry excessive privileges, a pattern that accumulates through deadline pressure and missing governance checkpoints rather than one bad decision (Zenity). Industry survey work points the same way: Gartner found 74 percent of IT application leaders regard AI agents as a new attack vector into their organization (Okta). Your re-review is where that drift gets caught.

Give every contract a named owner and require a written justification to keep scope. The review question is not whether the agent still works but what one incorrect call could do under this contract, and whether that is still acceptable. If you want the inventory or the enforcement design pressure-tested before the next change window, a free 30-minute discovery call with Quanta Consulting is a low-cost second opinion (https://quantaintelligence.ai/calendar). Where several models sit behind one gateway and you want contracts enforced consistently across vendors, Quanta Inference offers text, image, video, audio and embedding models through one OpenAI-compatible API key (https://quantaintelligence.ai/inference).

FAQ

FAQ

My tool is third-party and I cannot change its scoping. What now?

Enforce at the boundary you control: the wrapper that builds the request, the credential the tool receives, or the egress path. Prefer short-lived, narrowly scoped credentials over configuration changes, and record the residual risk in the inventory so the next reviewer sees it.

How do I scope a general-purpose tool such as a shell or code interpreter?

Treat it as the highest-risk entry in the inventory and isolate it. Run it in a sandbox without credentials to other systems, allowlist reachable destinations, cap execution time and output size, and gate anything touching production behind human review. One shell tool with ambient credentials undoes every narrow contract around it.

Does a prompt instruction count as enforcement?

No. Instructions are advisory to a model that treats retrieved content as commands, which is why goal hijack and tool misuse sit in OWASP's agentic risk list (OWASP Top 10 for Agentic Applications). Keep prompt text as documentation and put the binding rule in the runtime.

How many confirmation gates is too many?

Gate only irreversible or externally visible actions: deletes, sends, payments, publishes and permission changes. Gating reads trains users to approve reflexively, which destroys the value of the gate. If your approval rate sits near 100 percent, you have a rubber stamp rather than a control.

What is the smallest logging setup that still helps?

Agent identity, tool name, contract version, argument keys, decision and latency on every call, stored where your security team can query it. That answers who called what, whether it was allowed and by which rule, which is where every incident review starts.

Sources

Sources

  1. Emerging Security Practices for AI Agents - Frontier Model Forum
  2. Agent Inventory for SaaS AI Agents
  3. Complete Guide to Agentic AI Red Teaming | DeepTeam
  4. How to Establish Least-Privilege for AI Agents and Assistants
  5. Access Management for AI Agents: Scope What They Touch
  6. How to Prevent Over-Permissioned Agents
  7. Threat modeling agentic AI: a scenario-driven approach
  8. Demystifying OWASP Top 10 for Agentic AI - Medium
  9. How to implement least privilege for AI agents - Okta
  10. Least-Privilege Access for AI Agents
  11. Least Agency: Why Least Privilege Isn't Enough to Secure AI Agents - Zenity
  12. AI Agent Red Teaming: Techniques and Attack Surfaces | Fiddler AI Blog
  13. Agentic Security Initiative - OWASP Gen AI Security Project
  14. AI Red Teaming Agent - Microsoft Foundry
  15. OWASP Top 10 for Agentic Applications for 2026
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles