Agentic Security / agent / human factors / controls
Approval Fatigue as a Decaying Control
That human-in-the-loop is a control with a fixed strength you can put in a threat model. It is a rate, and the rate is set by the volume of benign prompts, not by the reviewer's diligence. Every standard — OWASP, the MCP specification — recommends it, and none of them price the decay. Two things then finish the job: the prompt shows a truncated action, so the approver reviews a label rather than the arguments; and 'always allow' converts one tired decision into a permanent grant that no later prompt will revisit. Chrome's SSL interstitial, a far starker warning shown far less often, still had a 70.2% click-through rate.
Human approval is a detector, and its catch rate is not a property of the human. It is a function of how often the detector fires against a base rate of nothing being wrong. Every standard recommends it. None of them price the decay, and the decay is set by the volume of ordinary work your agent does.
The claim to be careful with is the one that goes into threat models: "a human approves high-impact actions, so this risk is mitigated." That sentence treats approval as a fixed-strength control, like a signature check. It is not. A reviewer's attention is a budget that refills with time, and their willingness to read is a belief about whether reading has ever been worth it — a belief the agent updates several hundred times a day, always in the same direction.
Below is one working session, eight hours long, run four hundred times. Each run contains exactly one genuinely dangerous tool call, dropped at a random point among the benign ones, and the question is whether the human approves it. Move benign tool calls per hour first, and watch the two readouts that are supposed to be about the reviewer: how many prompts they read, and how many they wave through.
The reviewer has two independent limits. Attention is a bucket that refills at the rate above and is spent one token per prompt genuinely read. Willingness is a belief: after a run of prompts that turned out to be nothing, the chance they read the next one is 1 divided by (1 + run length ÷ tolerance). Both start full at the beginning of the session, which makes every number here a best case — it models the reviewer's first day, not their third month.
Benign tool calls per hour, at 1, 2, 5, 10, 20, 40, 80 and 120 — not evenly spaced, because the interesting part is at the bottom. the volume closest to the one you have set. Grid lines are 25 percentage points apart. Nothing about the reviewer changes across these bars.
read · approved without reading · the dangerous one. Up to the first 200 prompts of the session, left to right.
What is real and what is a model. The event stream is simulated rather than derived: calls arrive, a policy decides which of them prompt, the bucket drains and refills, the belief updates, and the outcome falls out of where the dangerous call happened to land. What is modelled is the human. The token bucket and the belief update are plausible shapes fitted to nothing; the one measured number this page leans on is external, and it is in the prose below. Treat the absolute percentages as a way to compare configurations, not as a prediction about your reviewer.
At the defaults — forty tool calls an hour, a prompt on every one of them — the dangerous call is approved in 88.5% of runs. The session raised 321 prompts. The reviewer read 16% of them and approved 84.3% without reading. Nothing in that sentence is about the reviewer being careless. It is about 321.
Now set benign tool calls per hour to 20. The approved without being read readout says 70.3%. That is very close to the one hard number in this whole area: in Alice in Warningland, a 2013 field study by Devdatta Akhawe and Adrienne Porter Felt covering over 25 million real warning impressions, users clicked through 70.2% of Google Chrome's SSL warnings — SSL being the browser's certificate check. Twenty tool calls an hour, which is a quiet agent, puts a reviewer at the click-through rate of the most-ignored warning in the study. The dangerous call is approved in 77.3% of runs at that volume.
Take the volume down to 1 call an hour and the picture inverts: nine prompts in a session, 12.1% of them waved through, and the dangerous call approved 37.0% of the time. Human approval is a real control there. The whole question is whether you can get the volume down to where it lives, and the rest of this lesson is about the four ways people try.
The two dials that are about the reviewer, and how little they move
Go back to the defaults and try to fix this by improving the human. Push prompts per hour the reviewer can read from 8 to 60 — seven and a half times the review capacity, which in an organisation means a team rather than a person. The hero number goes from 88.5% to 83.8%. Now put the budget back and push prompts in a row before they stop expecting anything from 25 to 60, modelling a reviewer who stays alert well past the point anybody actually does. 85.5%. Do both at once: 72.0%.
Seven times the staffing and a reviewer with more than twice the patience, together, buy 16 percentage points. Meanwhile the volume slider, moved from 40 to 1 with the original reviewer, buys 51. That ratio is the entire argument. Diligence is not the variable. Frequency is, and frequency is a property of your agent's workload — a number set by product decisions, not by the security team.
Now put both back — the budget to 8, the tolerance to 25 — because every number from here on is measured against that reviewer, and these two sliders are the easiest on the page to leave somewhere by accident. Watch which limit is binding as you move them. At the defaults the reviewer reads 16% of prompts, which is about 51 reads in a session, and the attention budget could have paid for 64. They are not out of time. They are out of expectation. That is why raising the budget alone barely registers, and it is why "we'll put a second person on the approval queue" is a plan that does not survive contact with the mechanism it is meant to address.
Alice in Warningland is worth reading rather than just citing, because its actual finding is more optimistic than the number everyone quotes. Akhawe and Felt reported click-through rates of 9% to 23% for malware and phishing warnings across both browsers, and 33.0% for Mozilla Firefox's SSL warning — and concluded that browser security warnings can effectively protect most users in practice. Google Chrome's SSL warning at 70.2% was the outlier, and they attributed the gap to the user experience of that particular warning rather than to users being hopeless. What they did not vary, because a browser cannot, is how often the warning fires. An agent can fire an approval prompt several hundred times a day, which is a regime no browser warning has ever operated in, and it is the axis this simulation moves.
Truncation is a real bug, and fixing it is worth almost nothing at volume
The Model Context Protocol — MCP, the standard interface for handing an agent a set of tools — is specific about this, and it stops being specific in an instructive place. Its Tools specification says there SHOULD always be a human in the loop with the ability to deny tool invocations, and its Security Considerations list says clients SHOULD "show tool inputs to the user before calling the server, to avoid malicious or accidental data exfiltration". That is the entire requirement for a tool call: show the inputs. Nothing says how much of them.
The blunt version does exist — show the exact command that will be executed, without truncation, including arguments and parameters — and it is a MUST rather than a SHOULD. But it is in the Security Best Practices document, under Local MCP Server Compromise, and it governs a different dialog: the one-click consent shown before you connect a new local MCP server, where the thing on screen is that server's launch command. You see that prompt once per server. The per-call approval prompt, which is the one that fires three hundred times a day, carries no no-truncation requirement anywhere in the specification. In practice it is commonly satisfied with a truncated one-line summary.
Switch on show the exact command in full at the defaults. The hero number goes from 88.5% to 85.3%. Three points, for giving the reviewer the strictest thing any of these documents asks for anywhere, because the reviewer is only reading 16% of the prompts and truncation can only matter on the ones they read.
Then set the volume to 5 calls an hour and toggle it again. Without the full command, 56.5%. With it, 38.5%. Eighteen points, from the same change. Prompt design is worth what the reviewer's attention is worth, and their attention is worth what the volume leaves of it. This is the general shape of every improvement in this area: the fixes multiply with each other rather than adding, so the order you apply them in decides whether they look like anything at all. Fix the volume first or the rest of the work will measure as noise and get cancelled.
One number in the simulation is not yours to tune. Switch the full command back off for a moment — the readout only means anything while there is a cut. The read, but the giveaway was past the cut readout hovers around a fifth to a third, because the attacker chooses where in the argument the interesting part sits and the sensible choice is "late". You choose the cut; they choose the offset. A prompt that shows the first 200 characters of a 240-character call is not showing 83% of the risk.
"Always allow" is not a compromise, it is the exit
Put the volume back to 40, with the full command still off, and switch on offer "always allow". Prompts in the session fall from 321 to 40, an 88% reduction in interruption that anybody would sign off on, and the reviewer's read share more than doubles because they are no longer drowning. Every measure of the human's performance improves. The hero number goes to 97.3%.
The mechanism is not subtle and it is worth stating flatly: a grant is permanent and a prompt is not. Five approvals of a tool — five decisions made, in this model, mostly without reading — convert into a standing authorisation that no later prompt revisits. When the dangerous call arrives on a tool that already has a grant, the human is not slow to respond. They are not consulted. The why it got through readout stops saying anything about reading and starts saying the grant silenced it.
Users expect exactly this, and they file bugs when it is not true: an issue on one agent's tracker reports tools failing with "MCP tool call requires approval" despite "Always allow" being set, which is a report that a permanent, total grant is the intended semantics. Meanwhile the two loudest-by-reactions issues in a sweep of agent tooling are a feature request for a flag that skips permissions entirely, at 93 reactions, and a bug report that such a flag stopped working, at 57 with 75 comments. Those are not reckless users. They are the decay curve, filed as tickets.
There is a second volume source people do not count. A client that correctly re-prompts whenever a server's tool descriptions change — which is the right defence against a poisoned tool description or a rug pull — generates a prompt every time any server on the list ships an update. The control that protects you from one attack is a supplier of the volume that disables the control protecting you from the others.
Making it rare, and where the bottleneck moves when you do
Switch "always allow" back off — it is now the loudest thing on the page and it will swamp everything below — and set which calls raise a prompt to calls that mutate state or touch the network. Prompts fall to 112 and the hero number to 69.3%. Set it to only what a risk classifier flags and prompts fall to 26 — three an hour, a queue a person can actually hold — while the hero number is 71.0%, barely different.
Read the why it got through readout and it now says something new: the classifier never prompted. The reviewer, given only 26 prompts, is reading 70% of them and is no longer the weak part. The failure moved into the router. Push classifier flags a dangerous call from 60% to 95% and the hero number goes to 52.3% without a single prompt being added, which tells you where to spend: not on the human, and not on the prompt, but on the recall of the thing deciding what to show them.
That is the honest shape of a working design, and you can reach it on the page. Put classifier flags a dangerous call back to 60%, keep the policy on the risk classifier, switch on the full command, and take the volume to 5 calls an hour: the hero number is 41.0% with 4 prompts in the whole session. Almost every remaining failure is the classifier missing, not the human. Or remove the classifier's error entirely — prompt on every call, 1 call an hour, the full command shown, and the reviewer given a budget of 60 reads an hour and a tolerance of 60 — and it is 7.0%. A 7% miss rate is what a human-in-the-loop control is worth under conditions no production agent will give you, and it is still a 7% miss rate. Put that number in the threat model rather than the word "mitigated".
None of this makes approval useless. It makes it a control with a price in prompts per hour, which means it has to be spent rather than sprinkled. The corollary is uncomfortable: every action that cannot be made safe by construction competes for the same tiny budget, so the list of actions that genuinely require a human has to be shorter than anyone wants. That list is the excessive agency question asked from the other end, and it is answered by removing capability rather than by adding review. A control at a lower layer is boring and constant, which is the argument for pushing as much as possible down into an enforcement point that does not get tired.
Checking this on a system you actually run
Measure your own click-through rate, because you almost certainly already have the data. Log every approval prompt with three fields: the time it was shown, the time it was answered, and the answer. The distribution of the gap is the whole story. A mode under 1.5 seconds is not a decision — nobody reads a shell command and forms a judgement in a second — and the fraction of prompts under that threshold is your measured click-through rate. Put it on the same dashboard as the number of prompts per user per day, because those two series move together and neither means anything alone.
Then audit the grants. For every standing "always allow", record when it was created, which prompt created it, how long that prompt was on screen before it was answered, and how many calls it has silenced since. A grant created by a 0.8-second decision that has since silenced four thousand calls is a finding you can write up, and it is invisible today because grants are stored as a boolean with no provenance. Expire them: a grant with a 30-day lifetime and a re-prompt that shows the count of calls it silenced turns a permanent authorisation back into a periodic decision, at a volume cost you can calculate in advance.
Fix the prompt itself with the arithmetic above in hand. Show the full argument, and diff it against what the same tool was called with last time, because a reviewer scanning for a change performs far better than one reading a wall of text. Put the identity being used and the blast radius on the prompt — "this will run as the deploy service account, which can write to 14 repositories" — since that is the sentence that makes a tired person stop. And never let the prompt's own text come from the server: a tool description is attacker-controlled data, which is the tool poisoning problem landing directly in your approval dialog.
Finally, budget prompts like an error budget. Pick a number of prompts per user per day you believe a person can genuinely read — the study above suggests it is small, and this simulation suggests under ten — and treat any feature that pushes past it as spending from a shared account. When it is exceeded, the correct response is to remove an action from the approval list by making it safe or impossible, not to add a reviewer. Every prompt you add to protect one action is taken from the attention available to every other one, and that trade is currently made by nobody, in no meeting, with no number attached.