AI Agent Tool Permissions: Least Privilege, Approvals & Policy-as-Code

What are AI agent tool permissions?
An agent becomes consequential when it can read customer records, send a message, edit a database row, execute code, call an external API, or delegate work to another agent. Tool permissions are the rules that decide which action may be requested, under which identity, against which resources, with which arguments, for how long, and at what risk level.
The reliable boundary is outside the model’s free-form reasoning. The model can propose a structured call, but a trusted authorization layer must return a deterministic decision such as allow, deny, or requires_approval. OWASP treats agentic systems as a threat-modeling problem because model reasoning, tools, identity, and persistent state combine to expand capability and risk.[1]
| Control | Question | Example |
|---|---|---|
| Authentication | Who is making the request? | Verified workload identity or delegated user session |
| Authorization | May this principal perform this action on this resource? | Support agent may read assigned tickets, not close them |
| Validation | Are the arguments safe and well formed? | Amount is numeric and below a policy limit |
| Sandboxing | What can the runtime access if code goes wrong? | Worker cannot write to the production filesystem |
| Approval | Does a person need to approve it? | External message, deletion, or payment |
| Auditability | Can the decision and outcome be reconstructed? | Principal, policy version, argument hash, decision, result |
The six-part permission model
A permission such as “the agent can use email” is too broad to review. A safe policy answers six questions for every call.
- Principal. Identify the agent, user, service, or delegated sub-agent. The model’s statement that it acts for a user is not proof of identity.
- Tenant and resources. Name the workspace, project, repository, folder, record set, or account in scope. Enforce the scope in the authorization and query layers.
- Action. Separate read, create, update, delete, execute, publish, and delegate. A tool name must not hide multiple side effects.
- Arguments. Validate IDs, paths, recipients, amounts, filters, query shapes, destinations, and unexpected fields after the model produces structured arguments.
- Time and quantity. Use expiry, rate limits, token or transaction budgets, retry caps, and batch limits. A safe single call can become dangerous when repeated thousands of times.
- Decision and evidence. Record allow, deny, or approval with policy version, reason, timestamp, principal, resource scope, and execution result.

Allow, deny, or human approval?
Default deny means that an unmatched call is rejected. It does not mean every call requires a person. Use explicit risk tiers so low-risk, reversible reads remain usable while consequential actions receive stronger controls.
| Action | Default | Required controls |
|---|---|---|
| Read public knowledge | Allow with rate limits | Public-resource check, timeout, logging |
| Read private customer data | Allow only in scope | Principal, tenant, record selector, minimization |
| Draft an email | Allow or review | Recipient and data-sensitivity checks |
| Send an external email | Requires approval | Human approval, expiry, content preview, audit |
| Update a production record | Approval or narrow workflow | Exact resource, schema, idempotency, rollback |
| Delete data or infrastructure | Deny by default | Elevated approval, two-person review, recovery plan |
| Transfer funds or approve a refund | Independent approval | Non-model approval path and transaction limits |
| Delegate to another agent | Bounded only | Child scope cannot exceed parent scope |
Approval is not a decorative button. Bind it to a specific principal, tool, resource, argument digest, policy version, and short expiry. If any material field changes after approval, require a new decision.
Permission manifest and policy-as-code
A permission manifest makes the intended boundary reviewable. It should be versioned, tested, and deployed independently from conversational prompts. The following example contains no credentials and is intentionally small:
{
"principal": "support-triage-agent",
"tenant": "acme",
"capabilities": [
{"tool":"ticket_search","action":"read","scope":"tenant:acme","decision":"allow","limit":20},
{"tool":"ticket_create","action":"create","scope":"tenant:acme","decision":"allow"},
{"tool":"email_send","action":"send","scope":"recipient:approved","decision":"requires_approval","expires_in":"5m"},
{"tool":"ticket_delete","action":"delete","scope":"*","decision":"deny"}
],
"policy_version": "support-policy-7"
}
At runtime, verify the principal, resolve the real tenant and resource, validate the arguments, check the approval binding, and enforce the decision through a trusted component. A manifest that is only displayed to the model is documentation, not authorization.
| Rule | Expected decision | Negative test |
|---|---|---|
| Read-only ticket search in active tenant | Allow | Cross-tenant selector returns deny |
| External reply without valid approval | Requires approval | Expired or mismatched approval is rejected |
| Delete ticket | Deny | Model-generated delete is rejected too |
| Unknown tool or argument | Deny | Unexpected field or tool name fails closed |
| Child-agent delegation | Allow only with narrower scope | Child request for parent-only capability is denied |
Enforce before the tool call
- Normalize. Parse the model output into a strict schema; reject malformed or ambiguous calls.
- Authenticate. Resolve the real principal and delegation chain outside the model.
- Authorize. Check tenant, resource, action, argument constraints, time, quantity, and policy version.
- Approve. If required, bind human approval to the exact call digest and expiry.
- Execute. Call the tool with a short-lived capability and a bounded timeout.
- Record. Store the decision, arguments summary or hash, outcome, latency, and error without leaking secrets.
- Reconcile. Verify the external effect where possible and emit a trace linked to the parent task.
This pre-action sequence complements sandboxing and model-based screening; it does not replace them. Vercel’s AI SDK documentation provides one implementation pattern for policy-based tool approvals, while OpenAI’s agent safety guidance discusses approvals, structured outputs, guardrails, and tracing.[3] [5]

Security tests that must fail closed
Test the policy at the trusted boundary, not only through natural-language conversations. Every high-risk capability needs positive and negative cases, and every production incident should create a regression test.
case: cross_tenant_read
principal: support-agent
request: read ticket tenant=other_customer
expect: deny; zero records; audit reason=tenant_mismatch
case: argument_escalation
request: ticket_update id=assigned-17, fields=[status, wildcard_export]
expect: deny; unexpected_field; no tool execution
case: approval_binding
approval: email_send recipient=a@example.com, digest=abc, expires=5m
request: email_send recipient=b@example.com, digest=xyz
expect: requires_approval; old approval cannot be reused
case: delegation_escalation
parent: read-only-support
child_request: delete_ticket
expect: deny; child scope cannot exceed parent
case: replay_and_rate_limit
repeat: 1000 read requests in one minute
expect: rate_limit; bounded cost; trace remains complete
| Test family | Attack or failure | Pass condition |
|---|---|---|
| Identity | Prompt claims a different user or agent | Decision uses verified identity, not model text |
| Scope | Valid action targets another tenant | Fail closed with no data leak |
| Arguments | Wildcard, path traversal, unexpected field, oversized amount | Schema and business constraints reject it |
| Approval | Approval is expired or for different arguments | New approval required |
| Delegation | Child asks for broader capability | Denied; parent boundary preserved |
| Replay | Same capability is repeated in a loop | Budget, rate, and idempotency controls activate |
| Audit | Call fails or tool times out | Decision and outcome remain reconstructable |
Incident response and least-privilege roadmap
- Contain. Quarantine the run, revoke or expire the capability, and pause high-impact tools when the execution state is uncertain.
- Scope. Determine whether the call executed, which resources it touched, which delegated identities were involved, and whether secrets were exposed.
- Recover. Restore a known-good state, rotate exposed credentials, reconcile external effects, and obtain owner approval before reopening the capability.
- Learn. Add a regression test, update the policy version, and link the fix to the incident record.
Pair tool permissions with memory-poisoning prevention, agent observability, model routing, and the MCP guide. Identity, memory, routing, tools, and audit logs must agree about the same scope.
FAQ
Can a system prompt enforce tool permissions?
No. It can guide the model, but the trusted runtime or authorization layer must decide whether a call is allowed.
Should every tool call require a human?
No. Use risk tiers. Low-risk, reversible, narrowly scoped reads can be automated; destructive, financial, externally visible, or high-impact actions need stronger approval.
What does default deny mean?
A call is rejected unless a specific rule matches and allows it. This prevents a newly added tool or malformed request from becoming available by accident.
Is sandboxing enough?
No. Sandboxing reduces the blast radius of code execution, but it does not decide whether a business action is authorized. Combine it with identity, policy, validation, approval, and monitoring.
How do I prevent privilege escalation between agents?
Issue child agents a capability that is a strict subset of the parent’s scope. Do not pass raw credentials or allow the child to request broader permissions from the model.
Conclusion
AI agent tool permissions are an authorization boundary between model output and real-world action. A least-privilege design names the principal, limits the tenant and resources, separates side effects, validates arguments, controls time and quantity, chooses an explicit decision, and records evidence.
Start with one workflow. Split its read and write capabilities, default unmatched calls to deny, bind approval to exact arguments, add negative tests, and make the decision traceable. Then expand the same pattern across APIs, MCP methods, code execution, and agent-to-agent delegation. The goal is not perfect autonomy; it is bounded, reviewable, reversible capability.
Trusted references
- OWASP, Agentic AI — Threats and Mitigations.
- NIST, AI Agent Standards Initiative.
- Vercel AI SDK, Policy-Based Tool Approvals.
- Uchi Uchibeke, Before the Tool Call: Deterministic Pre-Action Authorization for Autonomous AI Agents.
- OpenAI, Safety in Building Agents.
- OWASP, AI Agent Security Cheat Sheet.
Examples are illustrative and contain no real credentials. Test every policy in the actual runtime, identity system, data environment, and recovery workflow before production use.
Join the conversation