Guide · AI and GenAI
AI Agent Security: Least Privilege and Fixing Tool Bloat
28 min readUpdated By Saket Bhatnagar
Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.
In short
- Least privilege for AI agents means an agent gets only the tools, data and permissions its current task needs, only while it needs them. An agent is a program where an AI model picks its own next step.
- Tool bloat is when an agent can call far more tools than its job needs. A tool is an action your code runs when the model asks. More tools mean more wrong picks, and each wrong pick reaches further.
- OWASP, a non-profit that publishes security guidance, calls this risk Excessive Agency. It names three causes: too many functions, too many permissions, and too much freedom to act without a check.
- The fix is layers your code enforces. Give a short tool list per task and tight parameters (the values the model fills in). Add a short-lived token (a digital pass that expires) for one user and a few actions, plus approvals and logs.
- Beginners can stop after the OWASP section. Engineers then get a runnable tool-audit script, per-task tool sets, a token check with expected output, and approvals bound to the exact change.
What goes wrong when an AI agent has too many tools?
When an AI agent has too many tools, it picks the wrong one more often, and each wrong pick can do more damage. Least privilege for AI agents is the fix: give the agent only what the current task needs. This guide builds that fix step by step on one example.
Picture a common setup. A company sells HR software that other companies use to track leave, payslips and salaries. Its team adds a chat assistant so employees can ask things like how many leaves they have left. The setup is made up, but the problem is real.
The naive first try goes like this, and it is the wrong way. The HR platform already has an API, a set of addresses that other programs can call. It has 40 internal endpoints, and an endpoint is one address that does one job, like fetching a payslip. An engineer turns every endpoint into a tool in one weekend, including salary_calc, salary_update and delete_employee.
Three weeks later, an employee asks what their take-home pay would be if their basic pay rose by Rs 4,000. The model sees two tools with similar descriptions: "Calculate salary for an employee" and "Update salary for an employee". It picks salary_update. The employee's basic pay in the payroll system changes from Rs 38,000 to Rs 42,000.
A payroll officer catches it in the monthly check, three days before salary day, so no money moves. In the review meeting, the head of security asks why a chat assistant for employees could edit payroll at all. The rest of this guide answers that question.
Which words do you need before fixing tool bloat?
You need nine short definitions for the rest of this guide. Each one comes with an example from the HR assistant.
- Model: the AI itself, a program that reads text and writes text back.
- AI agent: a program where the model picks the next action itself, in a loop, until the job is done.
- Tool: an action your program lets the model ask for, like a button it may press. The model asks; your code does the work.
- Least privilege: a security rule that every person or program gets only the access its job needs. Before the assistant, the HR desk could read payslips but not change salaries.
- Tool bloat: an agent with many more tools than its job needs. The assistant answers leave and payslip questions, yet it carried 40 tools, including delete_employee.
- Credential: a secret, like an API key or a token, that a program shows another system to get in. A short-lived credential stops working after a set time, such as 15 minutes.
- Scope: a label that limits what a token may do. A token with the scope payslip:read can read payslips but cannot change anything.
- Blast radius: how much damage one mistake can do. With an admin key, one wrong call could touch every client company's payroll.
- Excessive agency: OWASP's name for giving an AI system more power than its job needs, so a wrong or tricked model output turns into real damage.
What does least privilege for AI agents mean?
Least privilege for AI agents means the agent can reach only the tools, data and actions the current task needs, and only while the task runs. You apply it in three places your code controls. They are which tools the model sees, which values each tool accepts, and what the credential behind each tool allows.
Think of an office peon who opens the office every morning. He carries the main door key, not the cash locker key. If he loses his key bunch, the locker is still safe. The agent's tool list is its key bunch, and each key should open one door.
Where the picture breaks: a peon knows which key is which, and a note slipped under the door cannot talk him into opening the locker. A model can mix up similar tools, and text it reads can steer it. So you cannot rely on the agent's judgement. You take the keys away.
The diagram below follows the employee's take-home question through the fixed design. Look at the three gates in the middle. Each gate can refuse the call on its own, even if the gate before it made a mistake.
Three gates every tool call passes
Drag to exploreNotice that all three gates are your code or the HR platform, never the model, so a confused model cannot talk its way past them.
Diagram connections:
- Employee asks a question to Model picks a tool
- Model picks a tool to Gate 1: tool list
- Gate 1: tool list to Gate 2: parameters (Yes)
- Gate 2: parameters to Gate 3: token scope (Yes)
- Gate 3: token scope to HR platform (Yes)
- Gate 1: tool list to Refused and logged (No)
- Gate 2: parameters to Refused and logged (No)
- Gate 3: token scope to Refused and logged (No)
- Employee asks a question: "What will my take-home be?"
- Model picks a tool: From the short list it was given
- Gate 1: tool list: Is this tool in the set for this task?
- Gate 2: parameters: Is every value inside the allowed range?
- Gate 3: token scope: Does this user's 15-minute token allow it?
- HR platform: Returns only this employee's data
- Refused and logged: The model gets a short error note
Each gate catches a different kind of mistake. The tool list stops the model from reaching for the wrong kind of action. The parameter check stops silly values, like a basic pay of Rs 4 crore (40 million). The token check runs at the HR platform itself, so it holds even if your own code has a bug.
Why does tool bloat hurt AI agent accuracy and safety?
Tool bloat hurts accuracy because the model has more chances to pick the wrong tool. It hurts safety because every extra tool makes a wrong pick more costly. The salary mistake shows both: a wrong pick, among tools that should never have been there.
How do extra tools hurt accuracy?
In each round of the agent loop, your program usually sends the model the name and description of every tool it may use. With 40 tools, that is a long menu to read before every choice. When two entries read alike, like salary_calc and salary_update, the model can confuse them.
A 2025 research paper on tool selection describes the same problem. Its authors say models struggle to use a growing number of tools because of prompt bloat and harder selection. Their method first searches for the few relevant tools and sends only those to the model.
In the paper's tests, that raised tool selection accuracy from 13.62% to 43.13%, more than three times as high. It also cut prompt tokens by over half; tokens are the small chunks of text a model reads. These numbers come from the authors' own benchmark, a fixed set of test tasks, so measure your own agent before quoting them.
How do extra tools hurt safety?
Models make mistakes. They misread questions, invent values, or follow instructions hidden in a document or message, an attack called prompt injection. You cannot bring that risk to zero. You can control what a mistake is able to reach.
With 40 tools and an admin key, one bad choice could edit payroll or delete an employee. With 7 tools and a token for one employee, the worst bad choice is small. It shows the employee their own payslip, files a leave request their manager can reject, or opens an HR ticket. The blast radius shrinks, whatever made the model go wrong.
Here are signs your agent has tool bloat:
- Tools were added because an API endpoint already existed, not because a task needed them.
- Two or more tools have names or descriptions that a new team member would confuse.
- Some tools can write, delete or move money, but the agent's job is mostly answering questions.
- Your logs show tools that were never called in the last month.
How does OWASP Excessive Agency describe this risk?
OWASP describes Excessive Agency as the weakness that lets an AI system take damaging actions after an unexpected, ambiguous or manipulated model output. Its detailed web entry is LLM06:2025 in the Top 10 for LLM Applications. LLM means large language model, the kind of model behind chat apps.
OWASP names three root causes, and the HR assistant had all three. The table maps each cause to the assistant before and after the fix. Read it row by row: each row is a separate way an agent can hold too much power.
| Root cause | What it means | Before the fix | After the fix |
|---|---|---|---|
| Excessive functionality | The agent can call tools it does not need | 40 tools, including salary_update and delete_employee | 7 tools in total, and at most 4 for any one question |
| Excessive permissions | A tool's access goes beyond the task | One admin key for every client company that never expires | A token for the asking employee only: read scopes plus leave:apply, 15 minutes |
| Excessive autonomy | High-impact actions run with no check | Payroll edits ran the moment the model asked | Payroll edits removed from the assistant; officers draft, a second person approves |
OWASP's mitigations follow the same three lines. Offer only the tools the agent needs, and build each tool with only the functions it needs. Avoid open-ended tools, such as one that runs any shell command (a command typed to the operating system) or fetches any web address.
Grant each tool the least access to other systems, and run actions in the user's own security context, not as an all-powerful account. Ask a person to approve high-impact actions before they run.
OWASP also calls for complete mediation. The downstream system, here the HR platform, makes the authorization decision itself on every request, instead of relying on the model to decide what is allowed. Logging and rate limits (a cap on actions per minute) do not prevent Excessive Agency, OWASP notes. They limit the damage and help you spot it.
How do you audit an AI agent's tools?
You audit an agent's tools by listing every tool, checking which tasks actually need each one, and sorting them by what they can change. Then you remove, narrow or move each tool based on that sort.
The team's head of security asks for the audit within one week. These steps work for any agent:
List every tool
Export every tool's name, description and parameters into one sheet. Add which API endpoint and which credential each one uses.
Pull 30 days of logs
Count how often each tool was called. In this example's made-up logs, 6 tools handled most calls and 22 were never called at all.
Ask what each task needs
List the real jobs: leave questions, payslip questions, policy questions and hand-offs to HR. Mark which tools each job truly needs.
Sort by what a tool can change
Label each tool read, low-risk write or dangerous. Dangerous means it moves money, changes pay, deletes data or affects many people at once.
Decide and record
For each tool, write keep, narrow, move or remove, with a one-line reason and an owner. The security head signs the sheet so the decisions are not lost.
A short script can do the first pass for you. It runs on a sample of six of the 40 tools. TOOLS holds your own risk label and the description the model sees for each tool. The call counts are made up; in practice you read them from your logs.
from difflib import SequenceMatcher
from itertools import combinations
# Your own risk labels. Never copy them from a tool's self-description.
TOOLS = {
"get_my_payslip": ("read", "Get the latest payslip for an employee"),
"salary_calc": ("read", "Calculate salary for an employee"),
"salary_update": ("dangerous", "Update salary for an employee"),
"delete_employee": ("dangerous", "Delete an employee record"),
"apply_for_leave": ("low-risk write", "Apply for leave for an employee"),
"export_all_data": ("dangerous", "Export all employee data as a file"),
}
CALLS_LAST_30_DAYS = {"get_my_payslip": 912, "salary_calc": 41,
"salary_update": 3, "apply_for_leave": 388}
for name, (risk, _) in sorted(TOOLS.items()):
calls = CALLS_LAST_30_DAYS.get(name, 0)
note = " (never called)" if calls == 0 else ""
print(f"{name:16} {risk:15} {calls:4} calls{note}")
for a, b in combinations(sorted(TOOLS), 2):
score = SequenceMatcher(None, TOOLS[a][1], TOOLS[b][1]).ratio()
if score >= 0.8:
print(f"Easy to confuse: {a} and {b} ({score:.2f})")The first loop prints each tool's label and call count, and marks tools nobody called. The second loop compares every pair of descriptions and flags pairs that read almost alike.
Save it as audit_tools.py and run python3 audit_tools.py. We ran it on Python 3.13, and it printed exactly this:
apply_for_leave low-risk write 388 calls
delete_employee dangerous 0 calls (never called)
export_all_data dangerous 0 calls (never called)
get_my_payslip read 912 calls
salary_calc read 41 calls
salary_update dangerous 3 calls
Easy to confuse: salary_calc and salary_update (0.85)What just happened? Two dangerous tools were never called, so they are easy wins to remove. salary_update was called 3 times, which shows usage alone can mislead: all three were mistakes like the one in the opening. The calc and update descriptions scored 0.85 out of 1, and no other pair reached 0.8.
The 0.8 cut-off is our choice for this list, not a standard. Text similarity is a rough screen, so a person still reads every pair it flags. Keep the risk labels in your own file instead of trusting a tool's description of itself.
The chart below shows how the 40 tools were sorted. Start at the top and follow the questions. Notice that a dangerous tool is not simply deleted: it moves somewhere safer.
Sorting the assistant's 40 tools
Drag to exploreMost tools leave the assistant; the few that stay are small and checked, and dangerous ones move behind an approval.
Diagram connections:
- 40 tools to Needed by an employee task?
- Needed by an employee task? to Remove from the assistant (No)
- Needed by an employee task? to What can it change? (Yes)
- What can it change? to Keep as a read tool (Read)
- What can it change? to Keep with checks (Low-risk write)
- What can it change? to Move behind approval (Dangerous)
- 40 tools: One per internal API endpoint
- Needed by an employee task?: Leave, payslip, policy or hand-off
- Remove from the assistant: The API stays; it is just not a tool
- What can it change?: Read, low-risk write or dangerous
- Keep as a read tool: For example get_my_payslip
- Keep with checks: apply_for_leave: the manager still approves
- Move behind approval: Payroll edits go to a separate officer tool
After the sort, the employee assistant kept 7 tools. Five only read data. Two write something small: apply_for_leave, which still needs the manager's yes, and hand_to_hr, which opens a ticket for a person. hand_to_hr uses the helpdesk's own narrow key, which can only create a ticket. Every payroll write tool left the employee assistant.
How do you narrow an AI agent's tools for each task?
You narrow tools in two ways: make each tool do one small job with tight parameters, and give the agent only the tools for the current task. The first shrinks what a tool can do. The second shrinks what the model can see.
How do you narrow a tool's parameters?
A parameter is a value the model fills in when it calls a tool, like a month or an amount. Every parameter is a door the model can push. So keep as few as possible, and check each one in code.
The old salary_calc tool took an employee_id, so the model could ask about anyone. The team replaced it with preview_salary_change. In the code below, hr_api and calculate_take_home are made-up helpers. The session object holds the logged-in employee and their short-lived token.
def preview_salary_change(session, new_basic):
"""Shows take-home pay for a new basic pay. Never saves anything."""
if not isinstance(new_basic, int):
return "Error: new_basic must be a whole number of rupees."
if not 10_000 <= new_basic <= 500_000:
return "Error: new_basic must be between 10000 and 500000."
payslip = hr_api.get_latest_payslip(session.token) # token cannot edit pay
return calculate_take_home(payslip, new_basic) # maths onlyThe name and description say preview and never saves, so the model is far less likely to confuse it with a write. There is no employee_id parameter at all. The tool takes the person from the session, so an employee can only preview their own pay.
What just happened? We ran this function with stand-in helpers on Python 3.13. The whole number 42000 returned a preview. The text version, the decimal 42000.0 and Rs 4 crore were each refused with the error notes in the code.
Each error goes back to the model as a short note instead of a crash, so the model can fix the value and try again. Even if this tool had a bug, the employee's token has no scope that can change pay, so no salary could be saved.
How do you give an agent a different tool set per task?
A per-task tool set is a small group of tools that you load only for one kind of request. Back to the peon: on a normal day he carries the door key. On audit day someone hands him the record room key, and takes it back in the evening.
The team adds a first step that labels each question, then loads only that label's tools. The helper ask_ai is made up: it sends one prompt to the model and returns its short text answer.
TOOL_SETS = {
"leave": [get_my_leave_balance, list_holidays, apply_for_leave],
"payslip": [get_my_payslip, preview_salary_change],
"policy": [search_hr_policy],
}
def tools_for(question):
label = ask_ai("Answer with one word: leave, payslip, policy or other.",
question).strip().lower()
return TOOL_SETS.get(label, []) + [hand_to_hr] # hand-off is always thereTOOL_SETS is a plain dictionary. Each key is a task, and each value is the short list of tools for it. Inside tools_for, .strip().lower() cleans up spaces and capital letters. TOOL_SETS.get returns the matching list, or an empty list for any other answer, and hand_to_hr is always added.
Pause and predict: the labelling step is itself a model call, so it can be wrong or be tricked. What is the worst a bad label can do here?
We tested it with stand-in answers. A label with extra spaces and a capital P still loaded the payslip set. The answers payroll, salary_update and a mixed payslip, salary_update each loaded only hand_to_hr. So did an answer with a trailing full stop. A wrong label gives the model the wrong small set, which is annoying but safe, because no set holds a payroll write tool.
If your tools come from an MCP server, the server can filter too. MCP, the Model Context Protocol, is an open standard for connecting tools to AI apps. Its current version, 2026-07-28 as of 19 October 2026, lets a server's tool list return only the tools the caller's granted scopes permit.
The diagram shows the full path for one question. Follow the payslip question down the middle branch, and notice that every branch reaches the HR platform through the same token check.
Per-task tool sets in the HR assistant
Drag to exploreThe model sees one short tool list per question, never all of them, and every branch still passes the same token check.
Diagram connections:
- Employee question to Label the question
- Label the question to Leave set (leave)
- Label the question to Payslip set (payslip)
- Label the question to Policy set (policy)
- Label the question to Other (other)
- Leave set to Token check
- Payslip set to Token check
- Policy set to Token check
- Token check to HR platform
- Employee question: Asks about take-home pay
- Label the question: One word: leave, payslip, policy or other
- Leave set: 3 tools plus hand_to_hr
- Payslip set: 2 read tools plus hand_to_hr
- Policy set: 1 search tool plus hand_to_hr
- Other: Only hand_to_hr: a person takes over
- Token check: One user, small scopes, 15 minutes
- HR platform: Refuses anything outside the token
How do scoped, short-lived credentials limit an AI agent?
Scoped, short-lived credentials limit an agent by making each token work for one person, a few actions and a short time. If the model misbehaves or the token leaks, the damage stays inside those limits.
Before the fix, the assistant used one shared admin API key. It worked for all 200 client companies (a made-up number), it could read and write everything, and it never expired. That key was the peon carrying every key in the building, forever.
Now, when an employee opens the chat, the company's login system issues a fresh token for that session. It acts only for that employee. It carries only read scopes plus leave:apply. It expires after 15 minutes, a number the team chose to match a typical chat.
The IETF, the body that writes internet standards, published RFC 9700 in January 2025. It is the best current practice for OAuth security, and OAuth is the common standard for handing out access tokens. Its section 2.3 says token privileges should be restricted to the minimum required, partly to reduce the impact of a leaked token.
The same section says a token should be meant for one resource server, the system that holds the data, or a small set of them. That server must refuse tokens not meant for it. Tokens should also be limited to certain resources and actions.
MCP applies the same thinking to tool servers. Its 2026-07-28 security best practices say an MCP server must not accept tokens that were not issued for it. They recommend starting with a minimal scope set and asking for more only when a privileged operation is first attempted.
That page also lists common mistakes. Two of them are wildcard scopes such as all or full-access, and treating the scopes in a token as enough without authorization checks on the server.
One detail is easy to miss. A challenge is a server's reply asking for more access. The same page says that when a server's first challenge names no scope, clients fall back to requesting every scope the server lists. So name the exact scope in each challenge. It also lists publishing every possible scope in scopes_supported, the server's public list of scopes, as a common mistake. So keep that list short too.
How do you check what a token can do?
Many systems issue access tokens as JWTs, short for JSON Web Tokens, in the format RFC 9068 describes. JSON is a common text format for data. A JWT carries claims, which are named fields. They include sub for the user, aud for the intended server, scope, and iat and exp for the issue and expiry times.
You can read those claims with a few lines of Python. Save this as token_check.py:
import base64, json, sys
def peek(jwt):
"""Reads a JWT's claims WITHOUT checking its signature. Inspection only."""
payload = jwt.split(".")[1]
payload += "=" * (-len(payload) % 4) # put back the base64 padding
return json.loads(base64.urlsafe_b64decode(payload))
claims = peek(sys.argv[1])
print("for user: ", claims["sub"])
print("audience: ", claims["aud"])
print("scopes: ", claims.get("scope", "(none)"))
print("lifetime: ", claims["exp"] - claims["iat"], "seconds")Run python3 token_check.py followed by a token from your test environment. For a session token like the one in this example, you should see:
for user: E1043
audience: hr-api
scopes: leave:read payslip:read policy:read leave:apply
lifetime: 900 secondsHere 900 seconds is the 15-minute limit. An admin scope, a wildcard, an audience that is not your API, or a lifetime of days all mean the credential is too broad.
This script skips the signature check, so use it only to look. A signature is a seal from the login system that proves nobody changed the token. On every request, your API must still check what RFC 9068 section 4 requires: the token type, the issuer, the signature, the audience and the expiry. Some systems issue opaque tokens, random strings with nothing inside to read; check those in your login system instead.
Why keep read tools and write tools apart?
Keep them apart because a read changes nothing, while a wrong write can be hard to undo. When reads and writes sit behind separate tools and separate credentials, you can hand out the read side more easily and guard the write side closely.
Reads still need limits, because they can expose private data. That is why the employee's token reads only that employee's records. Payroll writes need a payroll:write scope. The login system never issues payroll:write to the assistant, so even a step-up request for more scope is refused. Only a separate approval service holds that scope. No credential in the chat can make a payroll write, however the model is tricked.
When should an AI agent need human approval, and what should you log?
An agent should need human approval for any action that moves money, changes pay, deletes data or affects many people at once. You should log every tool call, with enough detail to rebuild later who asked for what, what ran, and why it was allowed.
Payroll officers still need help with salary changes, so the team built them a separate officer assistant. Its only write tool is draft_salary_change, which creates a draft and nothing else. A second person must approve the draft before payroll changes.
The diagram follows one salary change from the request to the payroll system. Look at who holds the payroll:write credential: the approval service, not the agent.
How a payroll change gets approved
Drag to exploreThe agent can only draft; a person approves the exact change, and a separate service holding the write scope saves it.
Diagram connections:
- Payroll officer asks to Officer assistant
- Officer assistant to Draft saved
- Draft saved to Second person approves
- Second person approves to Approval service (Approved)
- Approval service to Payroll system
- Officer assistant to Audit log
- Second person approves to Audit log
- Approval service to Audit log
- Payroll officer asks: Raise E1043's basic to Rs 42,000 from April
- Officer assistant: Has draft_salary_change, no write scope
- Draft saved: Exact employee, amount and start date
- Second person approves: Sees the exact change; expires in 24 hours
- Approval service: The only holder of payroll:write
- Payroll system: Change saved once
- Audit log: Every step: who, what and when
Three details make the approval real. It is bound to the exact change: employee, amount and start date. It expires, so an old yes cannot be reused months later. And it is used up when it is checked, so it cannot run twice. The OWASP AI Agent Security Cheat Sheet recommends all three.
The sketch below shows the idea. A fingerprint is a short code computed from the exact change, and any edit to the change gives a different fingerprint.
import hashlib, json, time
def fingerprint(action):
text = json.dumps(action, sort_keys=True)
return hashlib.sha256(text.encode()).hexdigest()
APPROVALS = {} # in a real system: a database table
def approve(approval_id, action, approver, valid_for_s=24 * 3600):
APPROVALS[approval_id] = {"fp": fingerprint(action), "by": approver,
"expires": time.time() + valid_for_s}
def execute(approval_id, action):
record = APPROVALS.pop(approval_id, None) # used up on the first try
if record is None:
return "Refused: no approval, or it was already used"
if time.time() > record["expires"]:
return "Refused: approval expired"
if record["fp"] != fingerprint(action):
return "Refused: action differs from what was approved"
return f"Saved: {action['employee']} basic {action['new_basic']}"
change = {"employee": "E1043", "new_basic": 42000, "from": "2027-04-01"}
approve("A1", change, approver="second_officer")
print(execute("A1", {**change, "new_basic": 45000}))
approve("A2", change, approver="second_officer")
print(execute("A2", change))
print(execute("A2", change))
approve("A3", change, approver="second_officer", valid_for_s=-1)
print(execute("A3", change))Save it as approvals.py and run python3 approvals.py. It prints these four lines:
Refused: action differs from what was approved
Saved: E1043 basic 42000
Refused: no approval, or it was already used
Refused: approval expiredWhat just happened? The first try asked to save Rs 45,000 under an approval for Rs 42,000, so it was refused. That failed try also used up approval A1, which is the safe default. The exact change then saved once, a second try with the same approval was refused, and an expired approval was refused.
This sketch keeps approvals in memory inside one program. In a real system, store them in a database. A transaction is a group of database steps that all happen or none do. Check and use up the approval in one transaction, right before the write, as the cheat sheet advises. A real approval service also records who drafted the change and refuses an approval from that same person.
Logs are your record of what really happened. The team's assistants log these things for every tool call:
- Who asked, and which session and token the call used.
- Which tool set was loaded, and which tool the model picked.
- The parameters, with secrets and personal details hidden or masked.
- Whether each gate allowed or refused the call, and why.
- The approval ID for any write that needed one, and who approved it.
- The result, and how long the call took.
Logs only help if someone reads them. The team gets an alert when an assistant is refused many times in a row, or calls tools far faster than a person types. The cheat sheet likewise recommends anomaly detection and alerts for unusual tool use. Our separate guide on prompt injection covers the attack side.
How do you explain least privilege for AI agents in a system design interview?
A strong answer treats least privilege as layers you add on purpose, not a single setting. Walk the interviewer from the tool list down to the logs, using one concrete example.
State the principle
Say the agent gets only the tools, data and permissions its current task needs, for only as long as it needs them.
Shrink the tool list
Audit the tools, remove the unused ones, and load a small set per task, like the leave, payslip and policy sets here.
Narrow each tool
Explain that tools take few parameters, check them in code, and get the user from the session, not from the model.
Scope the credentials
Describe a short-lived token for one user and one API, with read scopes, checked by the downstream system on every call.
Guard the dangerous actions
Move high-impact writes behind a draft and a human approval bound to the exact change, used once.
Log and alert
List what you log for each tool call and which patterns raise an alert.
Questions people ask
- What is excessive agency in AI agents?
- Excessive agency is when an AI system has more power than its job needs, so a wrong or manipulated model output causes real damage. OWASP's detailed web entry is LLM06:2025, and its 2026 edition lists it as LLM03:2026. Both name three causes: too many functions, too many permissions and too much autonomy. The fix is to cut all three down to what the task needs.
- Is Excessive Agency still in the OWASP Top 10 in 2026?
- Yes, at number three in the 2026 download. OWASP's 2026 edition of the Top 10 for LLM Applications, a download whose resource page is dated 3 August 2026, lists it as LLM03:2026. As of 19 October 2026, the website's detailed entry pages still show the 2025 list, where it is LLM06:2025. The download's cover still leaves its publication date blank, so check the number before you quote it.
- How many tools should an AI agent have?
- As few as the current task needs; no standard gives a magic number. A useful pattern is a small tool set per task, so the model sees only a handful at a time. The HR assistant in this guide has 7 tools in total, but any one question sees at most 4.
- Is a system prompt enough to stop an agent from misusing a tool?
- No. A system prompt guides the model, but the model can still ignore it or be tricked out of it. If a tool must never run, remove it from the agent's list and make sure the credential cannot perform that action either.
- Should an AI agent act as the user or have its own account?
- For actions on a user's data, acting as that user with a narrow, short-lived token is usually safer. OWASP recommends running actions in the user's security context with minimal privileges. If an agent needs its own account for background work, give that account only the exact access the job needs, never a shared admin key.
- How long should an AI agent's credentials last?
- No longer than the task they serve. There is no single right number; the team in this guide chose 15 minutes to match a typical chat. Shorter tokens mean a leaked token stops working sooner, at the cost of issuing them more often.
- Do read-only labels on a tool make it safe?
- Not by themselves. The MCP specification, version 2026-07-28, says clients must treat tool annotations, such as a hint that a tool only reads, as untrusted unless they come from trusted servers. Real safety comes from the credential and the downstream system refusing writes.
- How does least privilege help against prompt injection?
- It does not stop the trick, but it limits what a tricked agent can do. If the agent has no payroll tool and its token has no write scope, a hidden instruction to change salaries has nothing to call. That is why least privilege is one layer in a wider defence.
Sources and further reading
- OWASP Top 10 for LLM Applications: LLM06:2025 Excessive Agency (root causes and mitigations)
- OWASP GenAI Security Project: LLM Top 10 2026 resource page and download (page dated 3 August 2026; Excessive Agency is LLM03:2026)
- OWASP Cheat Sheet Series: AI Agent Security Cheat Sheet (tool least privilege, approvals, monitoring)
- NIST Computer Security Resource Center glossary: least privilege
- RFC 9700: Best Current Practice for OAuth 2.0 Security, section 2.3 (IETF, January 2025)
- RFC 9068: JWT Profile for OAuth 2.0 Access Tokens (claims and validation)
- Model Context Protocol specification 2026-07-28: Tools
- Model Context Protocol specification 2026-07-28: Security Best Practices (token passthrough, scope minimization)
- Research paper on retrieving the relevant tools first to cut prompt bloat in tool selection (arXiv 2505.03275, May 2025)