Your agent's permissions do not live in the prompt
Find where an AI agent's real limits are enforced, why the model is not that place, and how to set boundaries that hold when the model is wrong.
on this page · 0 / 0 checked
You connected your inbox to ChatGPT, gave Claude access to a project folder, and wired a Zapier action into an assistant so it could file things for you. Somewhere in the setup you wrote instructions. Never email a client without checking with me. Never delete anything. Only touch the 2026 folder. That text is the part most people think of as the safety layer. It is not. It is a request, written in the one place in the system that has no enforcement power, sitting a layer above the code that actually holds your credentials.
This guide is about where the real limits live and how to set them when you have no security team and no appetite for a project. It assumes you are a solo operator or a small team running agents on accounts you own. It is not for people building agent frameworks. If you are writing the runtime yourself, the specifications cited below are your reading, not this page.
A prompt is a request, and a permission is a fact
An agent loop has two halves that are easy to blur together. The model reads your request and produces structured output naming a tool and its arguments. Then separate code, the runtime, takes that output and runs the thing. The model does not send the email. It emits a description of an email, and something else with your OAuth token sends it.
That split is the whole subject. Everything you write in a system prompt lands on the first half. Everything that actually touches your data happens in the second. When the two agree, nothing looks wrong and the arrangement feels safe. The gap only shows when the model is confused, manipulated by text it read in a document, or simply wrong about which folder it is in.
OWASP names this failure class directly. Excessive Agency is “the vulnerability that enables damaging actions to be performed in response to unexpected, ambiguous or manipulated outputs from an LLM, regardless of what is causing the LLM to malfunction” [3]. The phrase to sit with is the last clause. The cause does not matter. Prompt injection, a stale instruction, a misread filename, a model update that shifted behaviour. If the damaging action was available, it happens. OWASP traces the root cause to three things you control and the model does not: excessive functionality, excessive permissions, excessive autonomy [3].
What you actually granted when you clicked Connect
Connecting a tool is a one-time consent screen for a standing grant. You approve it in four seconds while thinking about the task in front of you, and it persists until you go back and revoke it, which almost nobody does.
OpenAI is precise about the shape of this in its ChatGPT apps documentation: “App permissions do not grant an app new access. The data and actions available to an app are determined by the app, the access granted when it was connected, and any workspace controls” [7]. Read that as a statement about you rather than about the product. The ceiling on what your agent can do was set at connection time, by you, in a dialog you skimmed.
The MCP specification, which is the closest thing the field has to a shared contract for tool access, treats overly broad grants as a named vulnerability rather than a style issue. Its scope minimization section lists the consequences of a stolen broad token, starting with “expanded blast radius: stolen broad token enables unrelated tool/resource access”, and lists “using wildcard or omnibus scopes (*, all, full-access)” among the common mistakes [2]. The recommended shape is a minimal starting scope, with more requested only when an operation actually needs it. The protocol even defines the mechanism: a server that needs more should respond with HTTP 403 and a WWW-Authenticate header carrying error="insufficient_scope" and the scopes the operation needs, and the client steps up from there [1].
You will rarely configure scopes by hand. What you can do is notice, at the consent screen, that “read and write” and “read” are different products, and pick the smaller one when the job is reading.
The layer that runs the tool is the only place a rule holds
Once you accept that the model is a proposer rather than a decider, the practical question stops being how to word the instruction and becomes which layer refuses.
OWASP’s mitigation is a single sentence and it is the most useful sentence in this guide: “Implement authorization in downstream systems rather than relying on an LLM to decide if an action is allowed or not. Enforce the complete mediation principle so that all requests made to downstream systems via extensions are validated against security policies” [3]. Complete mediation means every request gets checked, every time, at the point of execution, with no fast path for requests that look routine.
Google’s Agent Development Kit documentation puts the same idea in an operational form: “by giving read-only permissions to a resource, no matter what the model decides, the tool will be prohibited from performing write actions” [5]. That is what a real control feels like. It does not depend on the model’s judgment being good, on your prompt being well worded, or on the model having read your instructions at all. The same documentation puts the grant on the developer’s side of the line: the agent identity “must be explicitly authorized in the external system access policies, like adding an agent’s service account to a database’s IAM policy for read access”, and such policies “constrain the agent in only performing actions that the developer intended as possible” [5]. For anything that runs generated code, it is blunter: “sandboxing must be used to prevent model-generated code to compromise the local environment” [5].
Runtimes have shipped without checking that the model decided anything
The uncomfortable version of this lesson is that the check can be missing entirely, and not only in hobby projects.
On 6 August 2026, at Black Hat USA, Hedi Ingber and Aviyam Ivgi of Stealth presented a cross-platform pattern they call CoreBreak, affecting agent infrastructure at three well-resourced vendors [8]. Amazon Bedrock AgentCore was tracked as CVE-2026-18830 at CVSS 8.6 and patched on 31 July 2026. Google’s Agent Development Kit for Python was CVE-2026-18236 at CVSS 9.3, fixed in ADK 2.5.0 on 16 July 2026. Vercel’s AI SDK harness packages carried CVE-2026-64650 and CVE-2026-64651, both CVSS 6.3, patched in @ai-sdk/harness-codex 1.0.29 and @ai-sdk/harness-opencode 1.0.28 on 10 July 2026 [8].
The AWS variant shows the mechanism plainly. “An authenticated remote user could place a tool-use content block in the final message of an InvokeHarness request. The event loop could then dispatch the named tool directly without asking the model” [8]. The researchers’ summary of the whole pattern is the part worth keeping: “An attacker did not have to persuade the model to break its rules; the attacker could reach the dispatch or authorization path without a legitimate model turn” [8]. The runtime accepted something shaped like a model decision and ran it. Every system prompt, content filter and refusal behaviour in the stack was irrelevant, because the model was never asked.
Three separate companies landing in the same place suggests the gap is structural rather than careless. The MCP specification writes the missing check into normative language for exactly this reason: servers “MUST validate that access tokens were issued specifically for them as the intended audience”, and “MUST NOT accept or transit any other tokens” [1]. The companion security document forbids the related shortcut outright: servers “MUST NOT accept any tokens that were not explicitly issued for the MCP server” [2]. Both are versions of the same discipline. Verify at the hop where you act that the thing you are acting on really came from where you assume, rather than that it merely looks the part.
Approval prompts are a control only while you still read them
Human approval is the other real enforcement point. OWASP recommends it explicitly: “utilise human-in-the-loop control to require a human to approve high-impact actions before they are taken” [3]. The catch is that approval decays. A prompt you click through 40 times a day stops being a decision.
Claude Code’s permission system is a good concrete model because it publishes its rules, and because it states the division of labour outright: “Permission rules are enforced by Claude Code, not by the model. Instructions in your prompt or CLAUDE.md shape what Claude tries to do, but they don’t change what Claude Code allows” [4]. Read-only tools such as file reads and Grep do not ask for approval inside the working directory; file modification and shell commands do [4]. Permission modes change the shape of the deal: default prompts on first use of each tool, acceptEdits auto-accepts file edits, plan reads and explores without editing your source files, auto auto-approves with background safety checks, dontAsk auto-denies anything not pre-approved, and bypassPermissions skips prompts [4]. The documentation attaches a warning to that last one telling you to “only use this mode in isolated environments like containers or VMs where Claude Code can’t cause damage” [4].
The design detail worth copying into your own thinking is precedence. Deny rules are evaluated before allow rules, from any scope, so “a user-level deny blocks a project-level allow” [4]. A broad deny such as Bash(aws *) blocks every matching call including ones a narrower allow rule also matches, which means a deny rule cannot carry exceptions [4]. That is what a control that survives contact with a busy operator looks like: the restrictive rule wins by construction, not by you remembering which setting you edited last.
Approval is not free, and it is worth knowing what it costs before you decide how much of it to keep.
write actions × seconds × 22 working days. Computed in the page; nothing is sent anywhere.
If the answer is small, keep approving and stop worrying. If it is large enough that you know you are clicking without reading, the fix is not more prompts. It is fewer write-capable grants, so that the prompts you do see are rare enough to mean something.
A setup a one-person business can maintain
Start with an inventory, because most people cannot name every connector they have approved. Open the connected-apps list in each tool and each account, and mark which grants can write, delete or send rather than only read.
Then reduce. Give the agent its own account or its own API key wherever the vendor allows it, so revoking it does not lock you out of your own login. Where a workflow only reads, use a read-only credential and confirm the workflow still runs; that single swap converts a prompt-level rule into an enforced one [5]. Where the platform has account-level controls, use them rather than configuring each automation separately. Zapier’s MCP documentation notes that Zapier MCP “automatically enforces any app and action restrictions set at the account level” and that “permissions set within apps themselves also apply” [6]. It also notes a boundary people misread: sharing server access “does not allow others to connect an AI client or run tools on another user’s behalf” [6].
Treat locally installed tools as a separate category. The MCP security guidance is direct that local servers “run with the same privileges as the client” and that a client offering one-click setup must show “the exact command that will be executed, without truncation” before running it [2]. That screen is the one moment you get to see what you are installing. Read it once, properly, rather than never.
What still goes wrong
The controls in this guide reduce blast radius. They do not stop an agent from doing something stupid inside the permissions you granted it. A read-only credential will not stop a summary from being wrong, and a scoped send-email permission will not stop a badly worded email to the wrong client. Most of the damage a small operator actually suffers is of that kind, and it is a supervision problem rather than an authorization one.
There is also a layer you cannot inspect. CoreBreak was a set of flaws in vendor runtimes, and how much any given deployment was exposed depended on the application above it: “Exposure depends on whether an application permits untrusted callers to submit structured conversation messages, alter stored history, or otherwise place a toolUse block in the position consumed by the event loop” [8]. If you are a customer rather than the person who wrote that application, that is not a dial you own. The reporting also notes that the advisories and CVE records behind the fixes “do not address whether any of the paths was used against a live deployment before it was patched” [8]. Your defence there is ordinary and boring: keep the tools updated, prefer vendors who publish their security model and patch it in public, and assume that the vendor’s execution layer is a trust decision you are making whether or not you thought about it. The MCP specification’s habit of writing the checks as MUST statements exists because these are the places implementations quietly skip [1][2].
Finally, the permission model itself moves. Modes get renamed, connectors add scopes, a platform ships an update that changes what a grant covers. Anything in this guide that names a specific setting is accurate on the date at the top of the page and should be re-checked against the vendor’s own documentation rather than trusted from here. The durable part is the shape of the thing: find the layer that executes, and put the rule there.
- 01Model Context Protocol — Authorization (2025-11-25)modelcontextprotocol.io
- 02Model Context Protocol — Security Best Practices (2025-11-25)modelcontextprotocol.io
- 03OWASP — LLM06:2025 Excessive Agencygenai.owasp.org
- 04Anthropic — Claude Code: Configure permissionscode.claude.com
- 05Google — Agent Development Kit: Safety and Securityadk.dev
- 06Zapier — MCP securitydocs.zapier.com
- 07OpenAI — Apps in ChatGPThelp.openai.com
- 08The Hacker News — AWS, Google, and Vercel Agent Flaws Let Attackers Trigger Tools Without Running the Modelthehackernews.com