Who Authorized This Tool Call? OpenID AuthZEN, the MCP Profile, and the Standards Race to Govern AI Agents
A valid OAuth token proves an AI agent is a legitimate caller but never authorizes the tool call. How OpenID AuthZEN and its MCP profile answer at runtime.
Permalink1. The token was valid. The tool ran.
An MCP agent holds a perfectly ordinary OAuth 2.1 access token: correct issuer, unexpired, carrying the crm:write scope, and -- because the server did its RFC 8707 homework [1] -- an audience bound to exactly this MCP server and no other. The agent calls delete_customer(id="9931"); the token check passes, the tool runs, the customer record is gone. And no component anywhere in that path ever decided whether this user's agent was allowed to delete this particular customer. The token answered "is this a valid caller?" It never answered "is this action, on this resource, for this user, allowed?"
The failure is invisible precisely because nothing is broken. The call is valid, in-scope, and audience-bound, and the tool did exactly what it was asked. That is not a missing check; it is a category error.
Authentication and coarse scopes answer "is this token valid, and does the agent hold the right scope?" They structurally cannot answer "is THIS agent, acting for THIS user, allowed to perform THIS action on THIS resource in THIS context?" Four distinct layers hide inside "authorization" here: OAuth 2.1 token validity, RFC 8707 audience binding, the OAuth scope, and a per-call decision on the specific arguments. The first three are real and necessary. None of them is the fourth. Set the clock from the first screen: the OpenID AuthZEN Authorization API 1.0 is a Final Specification approved January 12, 2026 [2], but the MCP Tool Authorization Profile that maps tool calls to decisions is only Draft 1, dated February 13, 2026 [3]. Final API, draft profile -- that gap is the whole standards race.
If that sounds like a hypothetical, consider the case where the answer to "who authorized this tool call?" was, literally, "no one." On June 18, 2026, Microsoft disclosed AutoJack: AutoGen Studio's Model Context Protocol control plane, reachable over a WebSocket, that skipped authentication entirely on its /api/mcp/* surface [4]. This is a missing-authentication control-plane weakness (CWE-306), chained through a permissive origin check (CWE-1385) and verbatim command execution of a query parameter (CWE-78) into remote code execution on the host running the agent.
Microsoft hardened it in commit b047730 [5]; it never shipped in a PyPI release and carries no CVE -- a research demonstration, not mass exploitation. It is not, and this matters for later, a skill-scanner bypass. It is the limit case of a tool call with no enforcement point at all.
"The MCP WebSocket accepted connections without any authentication regardless of the auth mode configured for the rest of the app." -- Microsoft Security, on AutoJack [4]
Between the two scenarios sits this article's subject. In the first, every OAuth check passed and still no layer expressed the per-resource decision. In the second, there was no check to pass. Both point at the same missing thing: a component whose only job is to decide, freshly, at the moment of use, whether this call is allowed. That idea is not new. It is fifty years old. Who first insisted that every access be checked, and who first named the exact hazard that an autonomous program holding someone else's token represents?
2. Complete mediation, the confused deputy, and the birth of the decision/enforcement split
The governing principle arrived in 1975. Jerome Saltzer and Michael Schroeder, cataloguing the design principles of secure systems, stated the one that matters most here: complete mediation -- "every access to every object must be checked for authority" [6]. Not once at login, not cached and reused, but freshly, at the point of use, every time. Read forward half a century, a per-call authorization decision on a tools/call is complete mediation applied to an autonomous agent. The whole history that follows is a series of attempts to honor that principle for programs that insist on holding ambient authority.
A design principle from Saltzer and Schroeder (1975): every access to every object must be checked for authority, as a fresh decision at the point of use -- not a cached grant and not an ambient token carried from an earlier moment [6].
The vocabulary came a few years earlier. Butler Lampson's 1971 access matrix framed protection as subjects against objects against permitted operations [7] -- the same subject/object/operation substrate that Attribute-Based Access Control and, later, AuthZEN's Subject-Action-Resource model still inherit. Saltzer and Schroeder added the governing principles: complete mediation and least privilege. But real systems honored complete mediation mostly in the breach. They cached decisions and handed programs ambient authority -- authority a program carries by virtue of who it is, independent of the request it is currently serving.
Norm Hardy named the failure that follows. In The Confused Deputy (1988), he described a program that "runs with authority stemming from two sources" and gets tricked into wielding the more powerful one on an attacker's behalf [8] [9]. The concrete incident: a compiler holding a "home files license" so it could update its own statistics file was induced to overwrite the system billing file when a user named that file as the compiler's debug-output destination. Hardy dates the incident to "about eleven years ago" relative to his 1988 paper -- so roughly 1977, at Tymshare, on an operating system he says was "much like Unix" [8]. Two common secondary errors are worth avoiding: it was not the "early 1970s," and it was not a Burroughs B5700.
Read 2026-forward, the confused deputy is the AI agent: an autonomous program holding the user's token, one prompt-injected instruction away from a tool call the user never intended.
Hardy's 1988 name for a program that holds authority from two sources and can be tricked into misusing the more powerful one, because the system cannot tell which authority it is acting under. It is the structural ancestor of an AI agent that holds a user's access token [8].
Containing a confused deputy requires separating the act of deciding whether an access is allowed from the act of enforcing that decision. The terminology was consolidated by the Informational RFC 3198 in 2001 -- Policy Decision Point and Policy Enforcement Point [10], which draws both definitions from the earlier RFC 2753 (2000) [11] -- and operationalized by the eXtensible Access Control Markup Language and NIST's ABAC guidance, which add a Policy Information Point for attributes and a Policy Administration Point for authoring [12] [13]. RFC 3198, "Terminology for Policy-Based Management," is an Informational glossary by Westerinen, Schnizlein, Strassner and others, published November 2001 -- it consolidates the terms rather than coining them, drawing both definitions from the earlier RFC 2753 (2000), and it predates XACML, which cites it directly for the PDP definition [10] [11] [12].
The externalized-authorization split: the PEP intercepts an access and asks; the PDP decides against policy and returns a permit or deny. The terminology was consolidated by RFC 3198 (2001), drawing on the earlier RFC 2753 (2000), and operationalized by XACML and NIST ABAC, which add a Policy Information Point (attributes) and Policy Administration Point (authoring) [11] [10] [12].
Diagram source
flowchart LR
A[Agent request] --> PEP[PEP intercepts the access]
PEP -->|asks| PDP[PDP decides]
PIP[PIP supplies attributes] --> PDP
PAP[PAP authors policy] --> PDP
PDP -->|permit or deny| PEP
PEP -->|enforces| B[Tool runs or is refused] This split is the precondition for everything that follows: you cannot standardize the interface between a decider and an enforcer until you have separated them. And yet the split was named around 2000 and had a full standard by 2003. So why, a quarter-century later, is "who authorized this tool call?" still an open question? Because the first serious attempt to answer it was too heavy to deploy, and the thing that won the world instead was too coarse to express it.
3. XACML's heavyweight PDP/PEP and OAuth's lightweight scopes
Two standards took opposite bets on the decision/enforcement split, and both failed the per-call agent question -- one because it was too heavy to deploy, the other because it was too coarse to express it.
The first bet was to externalize everything. The eXtensible Access Control Markup Language became an OASIS Standard in February 2003, with the still-current 3.0 ratified in January 2013 [12]. It was the serious, interoperable externalized-authorization standard of the 2000s: a full XML policy language on top of the PDP/PEP/PIP/PAP architecture, correct in principle and auditable in the center.
Its failure was deployability, not correctness -- and the evidence is the standard's own remediation. OASIS later published a JSON Profile for XACML whose stated motivation is that "it becomes important for XACML to be easily understood in order to increase the likelihood it will be adopted" [14]: a primary-source admission that the XML itself was the adoption barrier.
A whole shorthand language, ALFA, exists mainly to escape the XML. To answer one yes-or-no question a team had to stand up a PDP, author XML policy sets, wire a PIP, and marshal an XML request and response -- so most teams never deployed it end to end.
The second bet was to externalize nothing. OAuth 2.0 (RFC 6749, 2012) took the opposite path: after the user authenticates, bake coarse, authorization-server-defined entitlements directly into the bearer token as scope -- per Section 3.3, "a list of space-delimited, case-sensitive strings ... defined by the authorization server" [15]. It won the web because it is nearly free to enforce: a local, constant-time, string-set membership check, no PDP and no round trip.
But scopes are static (frozen into the token at issuance), opaque (read:documents names no specific document), and resource-blind. They cannot express a per-call, per-resource, user-contextual constraint. The AuthZEN working-group charter names this exact pattern -- "embedding entitlements into OAuth2 bearer tokens after user authentication" -- as the problem it exists to fix [16]. And worse for agents: a bearer token is ambient authority, which is precisely Hardy's confused-deputy hazard wearing a modern uniform.
Per RFC 6749 Section 3.3, a coarse, authorization-server-defined entitlement: a space-delimited string baked into a bearer token after authentication. It is static, opaque, and resource-blind -- it names a class of operation, never a specific object [15].
Per NIST SP 800-162, authorization decided by evaluating attributes of the subject, the resource, the requested operation, and the environment against policy -- the attribute-and-context model that AuthZEN's Subject-Action-Resource-Context request sits inside [13].
Make the failure executable. A crm:write scope check happily returns true for delete_customer(id="9931") -- and there is no way to widen it into "only customers in this user's own tenant," because the tenant relationship is not in the token and cannot be.
// A scope is a static string, frozen into the token at issuance time.
const token = { sub: "user-42", scope: "crm:read crm:write", aud: "urn:crm-api" };
function hasScope(token, needed) {
return token.scope.split(" ").includes(needed);
}
// The check the tool call actually performs before running delete_customer:
console.log("hasScope crm:write ->", hasScope(token, "crm:write")); // true, so the call proceeds
// The decision we actually wanted to make:
function canDeleteCustomer(user, customer) {
// Needs the customer's tenant AND the user's tenant.
// Neither value is present in the token, and a static scope cannot carry them.
return customer.tenantId === user.tenantId;
}
const customer = { id: "9931", tenantId: "tenant-A" };
const user = { id: "user-42", tenantId: "tenant-B" };
console.log("canDeleteCustomer ->", canDeleteCustomer(user, customer)); // false
console.log("The scope check already said yes. The token could never carry customer.tenantId."); Press Run to execute.
Two dead ends, opposite in every way, frame the same gap: externalize the decision and it is too heavy to deploy; bake it into the token and it is too coarse to express. The obvious repair -- keep externalization but make the engine lightweight and developer-friendly -- is exactly what a whole generation built next. So why did that fail too?
4. Five generations, each forced by the last one's failure
Generation 1 -- XACML PDP/PEP (2003 to 3.0 in 2013). It solved interoperable policy expression and failed on deployability, for the reasons Section 3 gave: the XML was too heavy, and the architecture survived while the language was routed around [12] [14].
Generation 2 -- OAuth 2.0 scopes (2012). It solved lightweight, ubiquitous, coarse authorization and failed on granularity and dynamism: scopes are static, baked in, and resource-blind [15]. The charter that eventually answered this names the pattern outright [16].
Generation 3 -- policy-as-code engines (OPA in 2016, Zanzibar in 2019, Cedar in 2023, alongside Topaz and Cerbos). This generation technically succeeded. Open Policy Agent evaluates Rego against input JSON and graduated from the CNCF in February 2021 [17] [18].
Google's Zanzibar answers relationship checks at a scale nothing else has published: it "scales to trillions of access control lists and millions of authorization requests per second," and "has maintained 95th-percentile latency of less than 10 milliseconds and availability of greater than 99.999% over 3 years" (lead author Ruoming Pang) [19] [20]. AWS Cedar, open-sourced in May 2023, trades raw expressiveness for a deliberately analyzable language [21] [22]. Vendor authorizers -- Aserto's Topaz, Cerbos -- sit here too [23] [24].
And yet the generation failed on interoperability: every engine spoke its own PDP wire API. Capability everywhere, interoperability nowhere. This is the SAML-before-OIDC moment for authorization, and the AuthZEN charter names the fragmentation directly. Throughout the base MCP authorization work, OAuth 2.1 is cited as an IETF draft -- draft-ietf-oauth-v2-1-13 in the base MCP spec, and draft-12 in the AuthZEN MCP profile [25] [26]. It has no RFC number, and attaching one is a common error worth not making.
"Interoperability between different architectures and between different implementations are particularly challenging." -- the OpenID AuthZEN Working Group charter, on the Generation 3 fragmentation [16]
Generation 4 -- the OpenID AuthZEN Authorization API 1.0. The working group formed in 2023; the specification became an OpenID Foundation Final Specification on January 12, 2026 [2]. It standardized the PDP-to-PEP wire protocol itself, engine-neutral -- the OIDC moment for authorization [27]. Section 5 unpacks exactly how.
Generation 5 (in flight) -- the AuthZEN MCP Tool Authorization Profile, "COAZ." Draft 1, dated February 13, 2026, extends that wire protocol to per-tool-call, parameter-level decisions for AI agents [3]. Note the maturity gap right here: a Draft profile, thirty-two days after the Final API, with no production corpus of tool mappings yet. Section 6 is its mechanics.
Diagram source
flowchart TD
G1["Gen 1 XACML PDP/PEP, 2003 to 2013: interoperable policy, but XML too heavy to deploy"] --> G2
G2["Gen 2 OAuth 2.0 scopes, 2012: lightweight and ubiquitous, but too coarse to express the call"] --> G3
G3["Gen 3 policy engines OPA, Zanzibar, Cedar, 2016 to 2023: fine-grained and fast, but every engine its own API"] --> G4
G4["Gen 4 AuthZEN Authorization API 1.0, Final 2026: the PDP-to-PEP interface, standardized"] --> G5
G5["Gen 5 AuthZEN MCP profile COAZ, Draft 2026: the interface, profiled to the tool call"] | Generation | Year | Key idea | Fatal limit | Status |
|---|---|---|---|---|
| XACML PDP/PEP | 2003 to 2013 | Externalize the decision; XML policy language over PDP/PEP | XML too heavy; rarely deployed end to end | Superseded (architecture survives) |
| OAuth 2.0 scopes | 2012 | Bake coarse entitlements into a bearer token after login | Static, opaque, resource-blind | Active, insufficient alone |
| Policy-as-code engines | 2016 to 2023 | Fine-grained, dynamic decisions in code (OPA, Zanzibar, Cedar) | Every engine speaks its own wire API | Active |
| AuthZEN Authorization API 1.0 | Final, 2026 | Standardize the PDP-to-PEP wire protocol, engine-neutral | Generic; needs a profile per domain | Final Specification |
| AuthZEN MCP profile (COAZ) | Draft, 2026 | Profile the wire protocol down to the tool call | Draft; no production corpus yet | Draft 1 |
Watch the human hand-off, because it is the tell. The founders of the very engines that fragmented Generation 3 are the editors and authors of the AuthZEN specifications that unify them: Omri Gazitt of Aserto, behind Topaz [28]; Alex Olivier of Cerbos; David Brossard, carrying the XACML and ABAC heritage; Atul Tulshibagwale of SGNL, who spans both the base API and the MCP profile [27] [3]. The people who built the silos are the ones standardizing the interface between them.
Four generations of adding more -- more policy language, more scopes, more engines -- and the interoperability gap only widened. Generation 4 closed it by doing the opposite. What is the one subtractive idea that makes AuthZEN succeed where XACML and every siloed engine did not?
5. AuthZEN 1.0 and the SARC model: standardize the interface, not the engine
Here is the whole idea in one sentence: AuthZEN standardizes the wire protocol between the decision point and the enforcement point, and nothing else. The specification's abstract says PDPs and PEPs "communicate authorization requests and decisions to each other without requiring knowledge of each other's inner workings" [27]. The decisive move is subtractive. The spec explicitly places the "policy language, architecture, and state management aspects of a PDP" beyond its own scope [27]. That omission is not a gap in the standard. It is the standard.
Build the mechanism in three parts.
The information model. The request names a Subject, an Action, a Resource, and a free-form Context -- the model the MCP profile abbreviates SARC -- and the PDP's response adds a fifth entity, the Decision [27] [3]. The canonical example makes it concrete: Can Alice view document #123 at 16:30 on Tuesday? A PEP builds a small JSON request -- subject with a type and id, an action name, a resource with a type and id, and a context object -- and POSTs it. The PDP answers with a boolean.
The AuthZEN information model that the MCP profile abbreviates SARC: a request naming the subject (who), the action (what operation), the resource (on what object), and a free-form context (under what circumstances), to which the PDP's response adds a Decision. SARC is the profile's shorthand; the core Authorization API adds the Decision entity in the response [27] [3].
The three endpoint families. Access Evaluation takes one subject, action, and resource and returns one decision. Access Evaluations batches many decisions into a single round trip -- "boxcarring." Search asks the inverse question: which resources, subjects, or actions are permissible [27]. The API is transport-agnostic with a normative HTTPS binding, and the PEP ships only the SARC attributes -- never the policy, never the engine's state.
Why the omission is the breakthrough. Because AuthZEN standardizes only the interface, Topaz, OPA, Cedar, Cerbos, or Axiomatics can all sit behind the same PEP, exactly as OIDC let any identity provider sit behind the same relying party -- an analogy the charter draws itself [16]. The PDP becomes swappable. That is the interoperability the Generation 3 engines never had, and it arrives by subtraction, not addition.
Policy Decision Points and Policy Enforcement Points "communicate authorization requests and decisions to each other without requiring knowledge of each other's inner workings." -- AuthZEN Authorization API 1.0 [27]
AuthZEN succeeds by standardizing less than XACML, not more. It ratifies only the PDP-to-PEP interface -- the Subject-Action-Resource-Context request and the Decision response -- and deliberately leaves the policy language, the architecture, and the state out of scope. That is precisely how OIDC beat SAML-era fragmentation for authentication: not a better identity provider, but a standard interface any identity provider could sit behind. The interoperability the fine-grained engines never had comes from the subtraction.
The milestone is what makes the next section possible. The Authorization API 1.0 became an OpenID Foundation Final Specification on January 12, 2026 -- "not subject to further revision," carried on 107 total votes [2]. A ratified, engine-neutral substrate is exactly what an agentic profile can now build on. Hold the maturity gap in view even at the moment of triumph: the API is Final and "not subject to further revision" as of January 12, 2026 [2], but the profile that maps tool calls onto it is Draft 1, dated February 13, 2026 -- thirty-two days later, and still moving [3].
Diagram source
flowchart LR
S[Subject: the user] --> REQ[AuthZEN request]
A[Action: the operation] --> REQ
R[Resource: the object] --> REQ
C[Context: agent, time, environment] --> REQ
REQ --> PDP[AuthZEN PDP, engine-neutral]
PDP --> D[Decision: permit or deny]
PDP --> EP["Endpoints: Access Evaluation, Access Evaluations, Search"] But a ratified PDP-to-PEP protocol is, by design, generic. Hand a PEP a raw MCP tools/call -- a JSON-RPC method name and an arguments object -- and it has no standard recipe for turning those parameters and the caller's token claims into a Subject, an Action, a Resource, and a Context. Something has to specify that mapping. That something is the profile.
6. The MCP Tool Authorization Profile (COAZ), in two layers
You now believe the idea. Here is the whole machine -- and it lives in two distinct layers that must never be run together into one. Layer 1 answers "is this token even valid for THIS server?" Layer 2 answers "is THIS subject allowed THIS action on THIS resource in THIS context?" They are different questions, anchored in different specifications, and the most common factual error in this whole area is putting a Layer 1 control into Layer 2.
The load-bearing requirement: the spec says MCP clients "MUST implement Resource Indicators for OAuth 2.0 as defined in [RFC 8707]," and MCP servers "MUST validate that access tokens were issued specifically for them as the intended audience" [26]. That is audience binding -- the anti-confused-deputy control that keeps a token minted for one server from being spent at another.
The Layer 1 control that binds an OAuth token to a specific MCP server as its intended audience, so a token minted for one server cannot be replayed at another. The base MCP authorization spec requires it; it is the anti-confused-deputy check, and it belongs to the base spec, not to the AuthZEN profile [1] [26].
Layer 2 -- the AuthZEN MCP profile (COAZ, Draft 1). A tool advertised in tools/list marks itself with "coaz": true and carries an x-coaz-mapping object inside its inputSchema [3]. The mapping's fields are Common Expression Language expressions over two inputs: token.*, the complete decoded claims of the JWT access token, and params.arguments.*, the tool-call arguments [33]. The profile's own worked example maps resource.id = params.arguments.location and subject.id = token.sub.
At call time, a Policy Enforcement Point -- the MCP gateway or the MCP server itself -- validates the token, evaluates the CEL to construct the SARC request, and calls the AuthZEN PDP's Access Evaluation endpoint. Only on a permit Decision does the tools/call execute [3].
The AuthZEN MCP profile's mechanism: a tool marks itself coaz: true and carries a declarative mapping so a PEP can turn an MCP tool call into a SARC decision, evaluated by an AuthZEN PDP before the tool runs [3].
The extension object inside a tool's inputSchema whose fields are CEL expressions over the token claims and the call arguments. It defines, declaratively, how a tools/call becomes a Subject-Action-Resource-Context request [3].
A non-Turing-complete expression language, originally a Google project, used by x-coaz-mapping to derive SARC attributes from token.* and params.arguments.* -- for example token.sub or params.arguments.location [33].
The load-bearing design choice is the Zero-Trust separation. The profile represents the human user "as the AuthZen Subject" and the AI agent "in the AuthZen Context," so policy "can independently evaluate the trust level of both the user and the agent" [3]. This is the wire-format resolution of the user-versus-agent attribution gap. Walk it: token.sub -- the user -- becomes the Subject; params.name becomes the Action by default; params.arguments.location becomes the Resource; the agent's own identity goes into the Context; and the PDP's answer is the Decision.
Diagram source
sequenceDiagram
participant C as MCP client
participant P as Gateway PEP
participant S as MCP server
participant PDP as AuthZEN PDP
C->>P: tools/call with access token
Note over P: Layer 1, validate token, check audience per RFC 8707
P->>P: evaluate x-coaz-mapping into a SARC request
P->>PDP: Access Evaluation, subject action resource context
PDP-->>P: Decision, permit or deny
alt Decision is permit
P->>S: forward tools/call
S-->>C: tool result
else Decision is deny
P-->>C: request refused
end The second diagram shows the data transformation the first hides inside one step: how the mapping builds the four SARC entities.
Diagram source
flowchart TD
T["token.* decoded JWT claims"] --> CEL[CEL evaluation]
A["params.arguments.* tool-call arguments"] --> CEL
CEL --> SUB["Subject from token.sub, the user"]
CEL --> ACT["Action from params.name"]
CEL --> RES["Resource from params.arguments.location"]
CEL --> CTX["Context carrying the agent identity"]
SUB --> PDP[AuthZEN PDP]
ACT --> PDP
RES --> PDP
CTX --> PDP
PDP --> D[Decision gates the tool call] // An MCP tools/call: the method name and its arguments.
const params = { name: "read_weather", arguments: { location: "Seattle" } };
// The decoded, already-validated JWT access-token claims.
const token = { sub: "user-42", client_id: "agent-7", scope: "weather:read" };
// A tiny x-coaz-mapping (in the real profile these fields are CEL expressions).
const mapping = {
subject: (p, t) => ({ type: "user", id: t.sub }), // the user
action: (p, t) => ({ name: p.name }), // default: the tool name
resource: (p, t) => ({ type: "location", id: p.arguments.location }),
context: (p, t) => ({ agent: t.client_id }) // the agent goes here
};
// The PEP assembles the SARC request from token claims and call arguments.
const sarc = {
subject: mapping.subject(params, token),
action: mapping.action(params, token),
resource: mapping.resource(params, token),
context: mapping.context(params, token)
};
console.log(JSON.stringify(sarc, null, 2));
// A mock AuthZEN PDP: this user may read weather only for Seattle.
function accessEvaluation(req) {
return { decision: req.subject.id === "user-42" && req.resource.id === "Seattle" };
}
const result = accessEvaluation(sarc);
console.log("decision ->", result.decision); // true -> the tool call is allowed to run Press Run to execute.
Here are the same three moves as the literal artifacts a server and PEP exchange, not an analogy. First, the x-coaz-mapping the server publishes inside the tool's inputSchema; every value is a CEL expression over token.* and params.arguments.*, and static types are written as single-quoted CEL string literals [3]:
"x-coaz-mapping": {
"subject": [{ "type": "'user'", "id": "token.sub" }],
"resource": [{ "type": "'location'", "id": "params.arguments.location" }],
"context": [{ "agent": "token.client_id" }]
}
With action left unmapped it defaults to the tool name. Evaluating those expressions against the call above -- token.sub is user-42, params.arguments.location is Seattle, token.client_id is agent-7 -- the PEP sends this AuthZEN Access Evaluation request to the PDP [3]:
{
"subject": { "type": "user", "id": "user-42" },
"action": { "name": "read_weather" },
"resource": { "type": "location", "id": "Seattle" },
"context": { "agent": "agent-7" }
}
The PDP returns the minimal AuthZEN Decision, and only a permit lets the tool call run [27]:
{
"decision": true
}
| Dimension | Layer 1: base MCP authorization | Layer 2: AuthZEN MCP profile (COAZ) |
|---|---|---|
| Question answered | Is this token even valid for THIS server? | Is THIS subject allowed THIS action on THIS resource in THIS context? |
| Specs and RFCs | OAuth 2.1 draft, RFC 9728, RFC 8414, RFC 7591, RFC 8707 | AuthZEN Authorization API 1.0 plus the COAZ profile and CEL |
| Where the decision lives | In the token and its audience claim | In an external AuthZEN PDP, called per tool call |
| What it cannot do | Express per-resource, per-user, contextual decisions | Decide anything if no PEP in the path processes the mapping |
| SARC entity | CEL source example | What it represents | Constraint from the profile |
|---|---|---|---|
| Subject | token.sub | The human user on whose behalf the call is made | At least one field must come from the token |
| Action | params.name | The tool being invoked | Defaults to the tool name if unmapped |
| Resource | params.arguments.location | The specific object the call targets | Drawn from the call arguments |
| Context | agent identity from token | The AI agent and runtime circumstances | Must carry the agent identity |
Two more properties matter for later. COAZ is opt-in and backward compatible: non-COAZ clients "will simply ignore these fields." And the deployment-coverage caveat is stated in the profile itself: "If no PEP in the request path processes the mapping, no AuthZen authorization occurs and access control falls back to whatever other mechanisms are in place" [3]. Hold onto that sentence; it becomes a hard ceiling in Section 8.
That is one coherent, two-layer stack. But it is not the only body standardizing agent authorization in 2026. Some want to encode more in the token; most MCP servers ship Layer 1 alone; others govern an entirely different slice. Which layer does each own, where do they collide, and which one actually answers the tool-call question?
7. The standards race to govern AI agents
AuthZEN and COAZ (externalize the decision). The per-call decision lives in a policy engine reached over a standard API. It is the only approach that both expresses the runtime, per-resource question and standardizes the interface [3] [27].
OAuth-native encoding (put more in the token). Rich Authorization Requests replace coarse scope strings with a structured authorization_details array, so a token can carry something like "may transfer up to $500 from account X"; the AuthZEN profile references RAR only informatively [32].
Token Exchange lets one token be swapped for another -- an agent trading its own token for a downscoped, user-delegated one -- the leading candidate answer to "where does the user identity in the token come from?" [34]. Both add zero hot-path round-trips, because the decision is pre-computed into the token. Both are therefore static at issuance: the token cannot reflect state that changes between issuance and the call.
Base MCP OAuth-only (the deployed baseline). The base MCP spec standardizes only scopes plus RFC 8707 audience binding and no per-call PDP [26] -- and, though no MCP-adoption survey exists to measure this, that baseline is almost certainly what most servers currently implement. This is the baseline the article argues is structurally insufficient for the tool-call question -- necessary, cheap, and blind to the specific resource.
Siloed engines and adjacent agentic standards. OPA, Zanzibar, and Cedar can each be the AuthZEN PDP behind a COAZ PEP -- competitors that become components the moment the interface is standardized [17] [19] [22].
And a different slice entirely: Google's Agent Payments Protocol and the FIDO agentic-authentication work govern verifiable intent for commerce. AP2 builds trust from Mandates -- "tamper-proof, cryptographically-signed digital contracts ... signed by verifiable credentials" -- proving a human authorized a purchase [35]. That is not a per-tool decision; it composes with AuthZEN rather than competing with it. A companion article in this series covers the AP2 and FIDO slice in depth. Common Expression Language, the language the COAZ mapping is written in, is a Google project with evaluators in Go, C++, and Java. The article shows one expression but does not teach the grammar -- the mapping layer's tooling already exists across runtimes [33].
| Dimension | AuthZEN COAZ | MCP OAuth-only | OAuth-native (RAR + exchange) | Siloed engines (OPA, Zanzibar, Cedar) | AP2 + FIDO |
|---|---|---|---|---|---|
| Added round-trips per call | One PDP call (or one boxcarred for many) | None | None (encoded at issuance) | One engine call | None (local credential verify) |
| Decision timing | Per call, runtime | Issuance (static) | Issuance (static) | Per call, runtime | Per mandate (signing time) |
| Granularity | Parameter and resource level | Coarse scope plus audience | Structured but pre-baked | Parameter and relationship level | Transaction intent |
| Expresses "this action on this object now"? | Yes | No | Partial, if known at issuance | Yes | Only for a purchase |
| User-vs-agent separation | Yes (Subject vs Context) | No | Via token exchange (indirect) | Engine-dependent | Yes (user signs mandate) |
| Interface standardized? | Yes (AuthZEN wire API) | Yes (OAuth) | Yes (RFC 9396, RFC 8693) | No (per-engine API) | Yes (AP2) |
| Maturity | Final API, Draft profile | Final (2025-06-18) | RFC-Final | Production-proven | Launched 2025 |
Layered or rival, every one of these leans on the same promise: that some component can decide, at the point of use, whether a call is authorized. So ask the uncomfortable question. Even with a ratified standard, fully deployed everywhere, what can that decision never buy?
8. What no standard, fully deployed, can buy
This is the deliberate cold shower. A per-call PDP is the most faithful realization of complete mediation the field has yet produced -- it moves the check to the exact point of use. And it still cannot buy four things, true even if COAZ were Final and deployed everywhere tomorrow.
The intent gap. A PDP decides only on the attributes the PEP hands it. It cannot know the user's true intent. A call that is syntactically authorized -- correct subject, correct resource, permitted action -- but driven by a prompt-injected instruction is still authorized. No standardization closes this; it is a fundamental limit of attribute-based mediation. The whole reason agent tool calls are dangerous, prompt injection, lives in this gap, and no wire protocol reaches into it.
The confused-deputy ceiling. Hardy proved in 1988 that a deputy holding ambient authority can be induced to wield it for another principal [8]. Audience binding and per-call decisions contain the confused deputy -- they narrow where and whether the authority applies -- but they do not eliminate it. The PDP trusts the PEP's SARC construction, and a compromised PEP or a maliciously authored x-coaz-mapping re-opens the hole. The profile concedes exactly this in its mapping-integrity discussion [3].
The static-analysis ceiling. You cannot scan your way to runtime authorization. Trail of Bits bypassed every mainstream agent-skill scanner -- ClawHub's detector, Cisco's scanner, and "all three of the scanners integrated into skills.sh" -- using tricks as blunt as prepending 100,000 newlines, and archived the working payloads publicly [36] [37]. Their conclusion is the ceiling: a scanner's "static nature gives an adversary unlimited bites at the apple." Pre-install scanning and per-call authorization are orthogonal; neither substitutes for the other.
A scanner's "static nature gives an adversary unlimited bites at the apple." -- Trail of Bits, on why scanning is not authorization [36]
| Question | AutoJack | Trail of Bits |
|---|---|---|
| Layer attacked | Control plane (the MCP surface) | Supply chain (skill distribution) |
| Failure class | CWE-306 missing-authentication RCE | Detection and scanner bypass |
| What it proves | No PEP means no authorization | Scanning is not authorization |
| Fix that does NOT help | Better tokens | Better scanners |
| Primary source | Microsoft Security, June 18, 2026 [4] | Trail of Bits, June 3, 2026 [36] |
State the gap plainly. The ideal -- every call mediated, at every enforcement point, over a standard interface, informed by verified intent and live state -- is asymptotically approachable and never reachable, because true intent is not a property the PEP can observe. Being precise about that irreducible gap is what separates a real trust layer from a marketing claim. These are the structural ceilings, true even if the specs were finished tomorrow. But the specs are not finished. What is still genuinely unsettled as of mid-2026?
9. What mid-2026 leaves unsolved
This is a standards frontier, not a complexity-theory one, so each open problem is a precise engineering or governance question with a current partial state -- not a conjecture.
-
The maturity and adoption gap. The Authorization API is Final; the MCP profile is only Draft 1; and, as you would expect of a profile that new, effectively no production MCP server ships
x-coaz-mappingyet [3]. The "with authorization" world is still largely aspirational, and, on the current evidence, the deployed majority still authorizes with scopes alone. The partial state is genuinely encouraging: a Final API is exactly the substrate on which a Draft profile can stabilize, and a multi-vendor interop program already shows the API is testable across engines [27]. -
The user-identity-in-the-token gap. OAuth issues the token to the agent, and, in the profile's own words, "there is no standard mechanism to capture the identity of the user on whose behalf the tool is being invoked" [3]. Yet the entire user-as-Subject separation assumes that user claim exists to map. The leading candidates are Token Exchange and identity-assertion or on-behalf-of grants [34], none yet profiled for MCP.
-
Mapping integrity and trust. The
x-coaz-mappingis authored by the MCP server, so a malicious server can craft a mapping that misrepresents the SARC request to the PDP -- substituting a benign resource id for a sensitive one. The profile requires PEPs to validate that the mapping is well-formed, but that is a SHOULD, not signing or attestation [3]. -
PDP latency and availability on the hot path. Every COAZ tool call adds a synchronous PDP round-trip, making the PDP a tier-0 dependency of every action. The Access Evaluations endpoint amortizes many checks into one request, and Zanzibar demonstrates the engine layer can hold 95th-percentile latency under 10 milliseconds and availability above 99.999% [19] -- but there is no standardized decision-caching or time-to-live profile for MCP yet.
-
Multi-hop agent chains. When an agent calls another agent that calls a tool, how does the SARC Context -- which agent, acting on whose behalf -- propagate across hops? The profile fixes user-as-Subject and agent-as-Context for a single hop; nothing yet standardizes propagation across a chain [3].
-
Revocation and continuous evaluation. A decision made at call time can be stale by the next call, and a long-running agent makes thousands of calls per session. Per-call evaluation already re-checks every time -- a real advantage over decide-once-at-issuance -- but push-based revocation between the token layer and the PDP is unstandardized. This is the bridge to Continuous Access Evaluation, treated in a companion article: the revocation channel that supersedes a decision before the next call.
-
The supply-chain layer is orthogonal and unsolved. Per-call authorization does nothing about which tool code got installed. Trail of Bits showed scanning does not catch malicious skills [36] [37], and an authorized call to a malicious tool is still harmful. Runtime authorization and supply-chain integrity are two separate unsolved problems, and solving one does not touch the other.
Seven open problems -- and yet the API spec, the profile draft, and conformant PDPs are all public today. The companion AuthZEN Access Request and Approval Profile is a separate Draft that specifies what happens on a deny: an approval workflow, so a refused call can escalate to a human rather than simply failing [38]. So what can an MCP server author, a gateway operator, or a platform team actually build this quarter, and what should they refuse to assume?
10. What you can build this quarter
Concrete guidance, keyed to the layer you occupy, each item tied to a primary source.
-
Get Layer 1 right first. Implement OAuth 2.1, RFC 9728 Protected Resource Metadata discovery, and RFC 8707 Resource Indicators so your tokens are audience-bound to your server [26] [29] [1]. This is the cheapest, highest-impact step, and skipping it is how you become the confused deputy.
-
Put a PEP in every tool-call path. Never let a tool execute with no enforcement point. The AutoJack lesson is verbatim: "control planes must be authenticated, authorized, and isolated" [4]. A per-call PDP upstream is worthless on any path that routes around it.
-
For fine-grained needs, adopt the AuthZEN profile. Mark sensitive tools
coaz: true, author anx-coaz-mapping(token claims into the Subject, tool arguments into the Resource and Context -- the literal shape is the one in Section 6), and stand up a conformant AuthZEN PDP -- Topaz, OPA, Cerbos, Cedar with Verified Permissions, or Axiomatics [3] [23] [17] [24]. The engine is swappable precisely because only the interface is standardized. -
Separate the user from the agent. Put the human in the Subject and the agent in the Context, per the profile's Zero-Trust guidance, so policy can evaluate the trust level of both independently [3].
-
Validate the token before you map it. Verify signature, issuer, audience, and expiry before extracting any claims into a SARC request [3]. Extracting
token.subfrom an unverified token authorizes on a forged identity -- the mapping step assumes a token that Layer 1 already vouched for. -
Do not treat pre-install skill scanning as authorization. Trail of Bits proves scanning is necessary but insufficient [36]. Pair it with runtime, per-call enforcement; the two are orthogonal controls, and neither covers the other's gap.
-
Track the Draft and expect churn. The profile is early -- dated February 13, 2026 -- and companion work is still moving, including the Access Request and Approval Profile for denials and the anticipated MCP Server Cards [3] [38]. Build against it, but pin versions and expect revisions.
Seven things to build, and a handful of misconceptions worth dismantling before you brief your team -- several are the exact confusions this article was written to prevent.
11. Frequently confused, finally separated
Frequently asked questions
Doesn't OAuth already authorize the tool call?
No. OAuth authenticates the agent and carries coarse scopes. A scope is a static, authorization-server-defined string, frozen into the token at issuance [15]. It cannot express a per-call, per-resource, user-contextual decision -- which is exactly the pattern the AuthZEN charter names as the problem to fix [16]. OAuth proves you are a valid caller; it never decides whether this action on this object is allowed.
Does the AuthZEN MCP profile use RFC 8707?
No -- and the profile's own reference list settles it: RFC 8707 appears nowhere in it, while Rich Authorization Requests (RFC 9396) is listed only as informative [3] [32]. Audience binding via RFC 8707 is a Layer 1 control defined by the base MCP authorization spec, not by the profile [26]. The profile's job is the per-call SARC decision; treating RFC 8707 as part of it collapses Layer 1 into Layer 2.
Isn't AuthZEN just XACML again?
Is this production-ready?
Partly. The Authorization API 1.0 is a Final Specification as of January 12, 2026 [2]. The MCP Tool Authorization Profile is Draft 1, dated February 13, 2026 [3]. Treat the interface as ratified and the agentic profile as emerging -- as befits a profile this new, there is effectively no production corpus of tool mappings yet.
Is AutoJack a skill-scanner bypass?
No. AutoJack is a missing-authentication control-plane weakness (CWE-306) in AutoGen Studio that chains to remote code execution [4]. The skill-scanner bypass is separate Trail of Bits research at the supply-chain layer [36]. One is a hole where a call reaches a tool with no authorization at all; the other is malicious tool code slipping past detection. Blurring them hides which control you actually need -- enforcement coverage or supply-chain integrity.
Who is the Subject, the user or the agent?
The profile's Zero-Trust guidance makes the human user the Subject and the AI agent the Context, so policy can evaluate both independently [3]. The CEL mapping is technically flexible, but that separation is the design's whole point -- it is the wire-format resolution of the user-versus-agent attribution gap.
Does the PDP see all my data?
No. The PEP passes only the SARC attributes it constructs; the policy and the engine's state stay on the PDP side, and the API is transport-agnostic with a normative HTTPS binding [27]. The PDP sees the subject, action, resource, and context for the one decision it is asked to make -- not your data store.
How does this relate to Conditional Access and agentic identity?
They answer different questions at different moments. Conditional Access decides "is this token good?" at login. AuthZEN decides "is this action authorized?" at each call. And the agentic-identity work named the user-versus-agent gap that COAZ's Subject-versus-Context separation resolves on the wire. Companion articles in this series treat the login-time decision and the agent-identity principal in depth.
Trace the argument one last time, with the evidence now in hand. Complete mediation (1975) demanded a fresh decision at every access [6]. The confused deputy (1988) named the exact hazard an autonomous program holding someone else's token represents [8]. Twenty years of PDP/PEP architecture could decide but never interoperably -- until AuthZEN's subtractive move, standardizing the interface and not the engine, finally gave "who authorized this tool call?" a runtime, interoperable answer [27]. COAZ profiles that answer down to the tool call, with the user as Subject and the agent as Context [3].
The industry is converging on this answer unevenly, under a live standards race -- a Final API beneath a Draft profile so new that almost no server ships it yet -- but it is converging on externalized, per-call authorization, not on better tokens and not on better scanners. The "without authorization" world is what its absence already costs: a control plane that authenticated no one [4], and a scanner that an adversary gets unlimited tries to beat [36].
The answer to "who authorized this tool call?" is not a better token and not a better scanner. It is an externalized, per-call authorization decision, made at the point of use, over an interface any engine can sit behind -- with the human user and the AI agent held apart on the wire, so policy can reason about both. That decision now has a standard shape. What it does not yet have, everywhere it needs to be, is a PEP to ask it.
References
- (2020). RFC 8707: Resource Indicators for OAuth 2.0. https://www.rfc-editor.org/rfc/rfc8707.html - Resource Indicators for OAuth 2.0: the audience binding that anchors Layer 1 against the confused deputy. ↩
- (2026). Authorization API 1.0 Final Specification Approved. https://openid.net/authorization-api-1-0-final-specification-approved/ - OIDF announcement that the AuthZEN Authorization API 1.0 reached Final Specification status on 107 votes, January 12, 2026. ↩
- (2026). AuthZEN Profile for Model Context Protocol Tool Authorization (Draft 1). https://openid.github.io/authzen/authzen-mcp-profile-1_0.html - The MCP Tool Authorization Profile (COAZ, Draft 1, February 13, 2026): the x-coaz-mapping and CEL that turn a tools/call into a SARC decision. ↩
- (2026). AutoJack: How a single page can RCE the host running your AI agent. https://www.microsoft.com/en-us/security/blog/2026/06/18/autojack-single-page-rce-host-running-ai-agent/ - Microsoft disclosure of AutoJack, the AutoGen Studio MCP WebSocket RCE (CWE-1385, CWE-306, CWE-78); primary evidence for the world with no enforcement point. ↩
- (2026). AutoGen hardening commit b047730. https://github.com/microsoft/autogen/commit/b0477309d2a0baf489aa256646e41e513ab3bfe8 - The upstream microsoft/autogen hardening commit b047730 referenced by the AutoJack disclosure. ↩
- (1975). The Protection of Information in Computer Systems. https://web.mit.edu/Saltzer/www/publications/protection/ - Saltzer and Schroeder (1975): complete mediation and least privilege, the theoretical floor of the field. ↩
- (1971). Protection. https://bwlampson.site/08-Protection/WebPage.html - The 1971 Protection paper by Butler Lampson: the access matrix and the subject/object/operation substrate. ↩
- (1988). The Confused Deputy (or why capabilities might have been invented). http://cap-lore.com/CapTheory/ConfusedDeputy.html - The original confused-deputy paper by Norm Hardy on the free cap-lore host, including the Tymshare origin. ↩
- (1988). The Confused Deputy: (or why capabilities might have been invented). ACM SIGOPS Operating Systems Review 22(4). https://doi.org/10.1145/54289.871709 - ACM Digital Library bibliographic record (DOI) for the Hardy 1988 confused-deputy paper. ↩
- (2001). RFC 3198: Terminology for Policy-Based Management (Informational). https://www.rfc-editor.org/rfc/rfc3198.txt - Informational glossary that consolidated the PDP and PEP terminology in 2001. ↩
- (2000). RFC 2753: A Framework for Policy-based Admission Control (Informational). https://www.rfc-editor.org/rfc/rfc2753.txt - A Framework for Policy-based Admission Control (2000): the earlier source RFC 3198 drew the PDP and PEP definitions from. ↩
- (2013). eXtensible Access Control Markup Language (XACML) Version 3.0. https://docs.oasis-open.org/xacml/3.0/xacml-3.0-core-spec-os-en.html - XACML 3.0: the PDP/PEP/PIP/PAP glossary and the externalized-authorization architecture AuthZEN re-standardizes on the wire. ↩
- (2014). NIST SP 800-162: Guide to Attribute Based Access Control (ABAC) Definition and Considerations. https://csrc.nist.gov/pubs/sp/800/162/final - NIST SP 800-162: the ABAC definition and the attribute-and-context evaluation model. ↩
- JSON Profile of XACML 3.0 Version 1.0. https://docs.oasis-open.org/xacml/xacml-json-http/v1.0/xacml-json-http-v1.0.html - JSON Profile of XACML 3.0: a primary-source admission that XACML XML verbosity was the adoption barrier. ↩
- (2012). RFC 6749: The OAuth 2.0 Authorization Framework. https://datatracker.ietf.org/doc/html/rfc6749 - The OAuth 2.0 framework; source for scopes as coarse, static, authorization-server-defined strings. ↩
- (2023). AuthZEN Working Group Charter. https://openid.net/wg/authzen/ - AuthZEN Working Group charter and problem statement: coarse OAuth entitlements and the OPA/XACML/Zanzibar fragmentation the group set out to standardize over. ↩
- Open Policy Agent (OPA) source repository. https://github.com/open-policy-agent/opa - Open Policy Agent source repository (Styra, 2016): the Rego policy engine that can sit behind a COAZ PEP. ↩
- (2021). Cloud Native Computing Foundation Announces Open Policy Agent Graduation. https://www.cncf.io/announcements/2021/02/04/cloud-native-computing-foundation-announces-open-policy-agent-graduation/ - CNCF announcement of Open Policy Agent graduation, February 2021. ↩
- (2019). Zanzibar: Google's Consistent, Global Authorization System. https://research.google/pubs/zanzibar-googles-consistent-global-authorization-system/ - The Google Zanzibar paper: ReBAC and the only published hard latency and scale benchmarks for the engine layer. ↩
- (2019). Zanzibar: Google's Consistent, Global Authorization System (USENIX ATC 2019). https://www.usenix.org/conference/atc19/presentation/pang - The USENIX ATC 2019 presentation of Zanzibar, lead author Ruoming Pang. ↩
- (2023). Cedar, an open-source language for access control. https://aws.amazon.com/about-aws/whats-new/2023/05/cedar-open-source-language-access-control/ - AWS announcement open-sourcing Cedar in May 2023; the language behind Amazon Verified Permissions. ↩
- (2024). Cedar: A New Language for Expressive, Fast, Safe, and Analyzable Authorization. https://arxiv.org/abs/2403.04651 - The Cedar research paper: its expressive yet decidably analyzable design. ↩
- Topaz: open-source authorization. https://www.topaz.sh/ - Topaz, the Aserto open-source authorizer combining OPA with a Zanzibar-style directory. ↩
- Cerbos: scalable, stateless authorization. https://www.cerbos.dev/ - Cerbos, a stateless authorization PDP co-founded by profile author Alex Olivier. ↩
- (2025). The OAuth 2.1 Authorization Framework (draft-ietf-oauth-v2-1-13). https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13 - The OAuth 2.1 draft, cited to show OAuth 2.1 remains an IETF draft and never became an RFC. ↩
- (2025). MCP Authorization Specification (revision 2025-06-18). https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization - MCP authorization spec, revision 2025-06-18: defines Layer 1 (OAuth 2.1 draft, RFC 9728, RFC 8414, RFC 7591, and RFC 8707 audience binding). ↩
- (2026). AuthZEN Authorization API 1.0. https://openid.net/specs/authorization-api-1_0.html - The AuthZEN Authorization API 1.0 itself: the standardized PDP-to-PEP wire protocol (SARC request, Decision response) and its engine-neutral scope. ↩
- About Aserto. https://www.aserto.com/about - Aserto company page; co-founder Omri Gazitt is an AuthZEN Authorization API editor. ↩
- (2025). RFC 9728: OAuth 2.0 Protected Resource Metadata. https://datatracker.ietf.org/doc/html/rfc9728 - OAuth 2.0 Protected Resource Metadata, MUST-cited by the MCP spec for Layer 1 discovery. ↩
- (2018). RFC 8414: OAuth 2.0 Authorization Server Metadata. https://datatracker.ietf.org/doc/html/rfc8414 - OAuth 2.0 Authorization Server Metadata, cited by the MCP spec for Layer 1. ↩
- (2015). RFC 7591: OAuth 2.0 Dynamic Client Registration Protocol. https://datatracker.ietf.org/doc/html/rfc7591 - OAuth 2.0 Dynamic Client Registration, referenced by the MCP spec for Layer 1. ↩
- (2023). RFC 9396: OAuth 2.0 Rich Authorization Requests. https://datatracker.ietf.org/doc/rfc9396/ - Rich Authorization Requests (RAR): the OAuth-native fine-grained lane, referenced only informatively by the profile. ↩
- Common Expression Language (CEL). https://cel.dev/ - Common Expression Language: the non-Turing-complete expression language used in the x-coaz-mapping. ↩
- (2020). RFC 8693: OAuth 2.0 Token Exchange. https://datatracker.ietf.org/doc/rfc8693/ - OAuth 2.0 Token Exchange: the delegation grant that is the leading candidate for the user-identity-in-token gap. ↩
- (2025). Announcing Agent Payments Protocol (AP2). https://cloud.google.com/blog/products/ai-machine-learning/announcing-agents-to-payments-ap2-protocol - Google Cloud Agent Payments Protocol (AP2): an adjacent agentic standard governing signed purchase mandates, not per-tool decisions. ↩
- (2026). The sorry state of skill distribution. https://blog.trailofbits.com/2026/06/03/the-sorry-state-of-skill-distribution/ - Trail of Bits analysis of agent-skill scanner bypass; shows static scanning is not runtime authorization. ↩
- (2026). overtly-malicious-skills. https://github.com/trailofbits/overtly-malicious-skills - Trail of Bits repository archiving the overtly-malicious skill artifacts from the blog post. ↩
- AuthZEN Access Request and Approval Profile 1.0 (Draft). https://openid.github.io/authzen/authzen-access-request-approval-profile-1_0.html - Companion AuthZEN Access Request and Approval Profile, cited for the deny-path approval workflow. ↩