An agent is preparing a board packet. It reads Q3 financials from one system, drafts a document in a second, and notifies the audit committee through a third: three tool calls, three MCP servers, three authorization domains, behind one sentence of human intent.

Locking each of those calls down to least-privilege is the part we know how to do. There are two ways to do it, both buildable today on existing OAuth and AuthZEN primitives, and most of this post is how each one works on the wire. One mints a narrow token the agent carries to the call (token-side FGA). The other lets the resource decide each call as it happens (resource-side FGA).

That there are two ways is the good news. It is also the trap. Both authorize calls. Neither names the task those calls serve, and nothing else in the protocol stack does either. So the moment the work fans out across servers, the authority for it fragments into per-call grants that no longer add up to something a human approved once or an auditor can reconstruct later. The user sees a prompt per fragment; the logs join by timestamp and luck.

That is the gap. Least-privilege per call is the floor, not the ceiling. The ceiling, the thing neither path reaches, is authorization that knows what the agent is doing, not just which call it is making right now. This post walks both paths in full, shows exactly where they break, and pins down what anything that closes the gap has to provide.

The Setup

Concretely, the board packet takes a sequence of tool calls across three MCP servers, each protecting its resources through its own Authorization Server:

  1. query_financials(period="Q3 2026", entity="Example Corp") on a finance MCP server, with its own AS.
  2. create_doc(template="board-packet", title="Q3 Review") on a document MCP server, with a different AS.
  3. notify_reviewer(group="audit-committee", deadline="...") on a workflow MCP server, with a third AS.

The user approved the broader task before the agent started: “prepare the Q3 board packet for the audit committee.” In today’s systems, that approval is usually application state, not protocol authority. It does not automatically authorize the individual tool calls. The agent may begin with an existing credential or session accepted by a server, but it does not yet hold action-specific authority for all three systems.

Each tool call has to land at the right grain: not “agent can act on these resource servers,” but “agent is approved to read this specific financial period for this specific entity, draft this specific document, notify this specific reviewer group.” The agent does not know up front which actions each server permits, which RAR types each Authorization Server accepts, or which fields are required. Discovery is part of the protocol.

Before walking the two paths, it helps to be precise about what we are actually authorizing.

What We Are Authorizing

Everything that follows turns on one distinction. The model is untrusted reasoning that proposes actions. Each tool invocation is a request to exercise specific authority against a specific resource. The protocol layer’s job is to govern the invocation, not the reasoning. That job is three different authorization problems, often conflated:

  • Tool discovery. Which tools does the agent see in the first place? An MCP server controls its tools/list result, so a deployment can filter the catalog using authorization policy. Catalog filtering improves exposure control and agent behavior, but it is not execution authorization: the server must still authorize every tools/call. MCP does not currently standardize catalog filtering tied to a durable task object.
  • Tool invocation. When the agent calls a tool, does the call sit within the authority the agent already holds? Token-side FGA and resource-side FGA both sit here. Most of this post is about this problem.
  • Tool approval. When the call sits outside current authority but is still potentially permissible, what runtime governance workflow turns the denial into a decision? Approval (a human reviewer authorizes the call), consent (the user grants additional consent in band or out of band), and access request (a general governed escalation that may compose risk evaluation, approval routing, and consent collection) are three different workflow shapes, all expressible through the AuthZEN Access Request and Approval Profile.

Both FGA paths follow the same authorization pipeline, even though they implement it at different boundaries:

  1. Discover need. The task requires a specific action against a specific resource. The agent learns this from the task plan, the tool catalog, or a runtime failure that points at the missing capability.
  2. Request authority. The agent expresses the need on the wire (a Rich Authorization Request to the AS in Path A, a tool invocation against the MCP server in Path B).
  3. Evaluate. Policy evaluates the request against user-approved authority, client registration, deployment rules, and runtime risk posture.
  4. Authorize or escalate. The evaluation either produces a narrow token (Path A), returns a permit decision (Path B), or routes through a runtime governance workflow.
  5. Execute. The MCP server validates the token or enforces the permit and then runs the tool.

The important questions are where each step lives, what step 4 produces, which governance workflows it can invoke, and what protocol object identifies the task across many invocations. Path A and Path B answer the first questions. Neither answers the last, and that omission is the subject of the closing sections.

Who Is “the Agent” on the Wire?

Three identities show up at the protocol layer, and a real deployment has to be explicit about each.

  • User identity. The human (or service principal) who approved the task and consented to it.
  • Client identity and instance attestation. The agent runs as an OAuth client with a client_id. Deployments can add the Client Instance Assertion profile to identify the specific instance making the call, allowing instance-level policy and revocation.
  • Actor chain. When the agent delegates to a sub-agent or crosses workload hops, RFC 8693 Token Exchange defines the act claim used to represent the current actor and nested prior actors. The Actor Profile adds structure for agent delegation chains, including instance identity and per-hop authority constraints.

A real deployment has to be explicit about all three regardless of which path it picks. Sender-constrained tokens identify the client; deployments that adopt instance assertions can also bind the specific instance; and delegated paths preserve the actor chain so the authenticated principal and acting workload are both recoverable from decision evidence.

Path A: Token-side FGA

In Path A, the client carries the authority. Each tool call is authorized by a token narrowed to the relevant action. Steps 2 through 4 of the pipeline happen at the Authorization Server. The client then presents the resulting token when it asks the MCP server to execute.

Discovery and Request

The agent’s first unauthorized call starts a discovery cycle. The MCP server returns an OAuth WWW-Authenticate challenge carrying the Protected Resource Metadata URL and, where possible, scope guidance. This is the layering MCP’s authorization specification adopts. The client fetches PRM, discovers the AS, and follows AS metadata. Action-level authorization_details are an extension: a deployment adopting Path A also publishes RAR-type metadata so the client can discover the relevant schemas.

The 401 and the metadata it points at look like this:

1
2
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://finance.example.com/.well-known/oauth-protected-resource"
1
2
3
4
5
6
{
  "resource": "https://finance.example.com/mcp",
  "authorization_servers": ["https://as.finance.example.com"],
  "scopes_supported": ["financials.read"],
  "bearer_methods_supported": ["header"]
}

The first request for a given task is often approved through a normal OAuth front-channel consent flow. The client submits the request via PAR, the AS renders it to the user in a browser, the user approves, and the AS issues a code the client exchanges for a token. Asynchronous approval is the case the rest of this section focuses on, because it is the part the existing OAuth surface does not natively handle. The front-channel case still applies, especially for the initial request and for high-stakes step-up.

In an RAR flow, the client constructs an authorization_details entry that names the specific action and submits it through PAR. The AS evaluates the request against user-approved authority, client registration, deployment rules, and runtime risk posture. It may consult a PDP through the AuthZEN Authorization API. On approval, the AS completes the authorization flow and mints a narrow access token. An asynchronous flow returns a deferred handle and issues the token only after the required decision completes.

The authorization_details entry names the one action in the resource’s own vocabulary, not a coarse scope. The type and its fields are resource-defined; the client learns them from RAR-type metadata:

1
2
3
4
5
6
7
{
  "type": "financials_query",
  "locations": ["https://finance.example.com/mcp"],
  "actions": ["read"],
  "period": "Q3 2026",
  "entity": "Example Corp"
}
sequenceDiagram participant Agent participant MCP as MCP Server (RS) participant AS as Authorization Server participant PDP Agent->>MCP: tools/call query_financials MCP-->>Agent: 401 WWW-Authenticate
resource_metadata = PRM URL Agent->>MCP: GET PRM MCP-->>Agent: PRM: AS issuer, scopes_supported Agent->>AS: GET AS metadata + RAR-type metadata AS-->>Agent: authorization_details_types_metadata
schemas per type Agent->>AS: Proposed extension:
PAR + action-level RAR AS->>PDP: AuthZEN Evaluation alt In-bounds PDP-->>AS: permit Note over Agent,AS: Complete authorization-code flow AS-->>Agent: action-narrow access token else Approval needed PDP-->>AS: requestable denial AS-->>Agent: deferred handle + polling Note over AS: Access Request out of band Agent->>AS: poll AS-->>Agent: action-narrow access token after approval end Agent->>MCP: tools/call query_financials + token MCP-->>Agent: result

The baseline Path A shape is a token the MCP server validates against authorization_details, without requiring a PDP call at execution time. The client carries the proof that the AS granted the authority. A deployment can still add introspection, event-driven invalidation, or per-call PDP evaluation. The acquisition cycle repeats whenever the agent needs authority outside its current grant.

Asynchronous Approval Options

When the AS cannot grant the request without an out-of-band decision, Path A needs three things: a way to say denied, but you may ask, a way to carry that request to whoever decides, and a way to resume once they have. Several existing mechanics cover pieces of this, and the obvious candidate covers the wrong piece.

The AuthZEN-oriented shape composes the AuthZEN Access Request and Approval Profile with OAuth 2.0 Deferred Code Processing. The AS returns a deferred code for a requestable denial, the client polls the token endpoint, and the AS reevaluates before issuing the token. The reason to start here is the requestable-denial signal itself: a structured “denied, here is how to ask” that the client can act on, bound to the exact request that was denied.

CIBA is the mechanism most people reach for first, and it earns the look, because it already solves the genuinely hard-looking part: decoupled approval. The client initiates, a human approves out-of-band on their own device, and tokens come back by poll, ping, or push, with no front-channel redirect at the agent. For an agent with no browser and a human somewhere else, that is precisely the hop that is otherwise missing. But it is not a foundation for this, because the flow does not begin where CIBA begins. CIBA begins with a client asking to authenticate a user; this flow begins with a resource refusing a specific call and signaling that the refusal is requestable. CIBA has nowhere to carry that signal. It expresses “the user authenticated and consented,” not “the RS denied this call, here is how to ask,” and it cannot bind an approval to the call that triggered it. Its model authenticates the login_hint user, while agent governance often routes a given decision to a supervisor, an on-call approver, or a risk engine deciding about the task rather than about the user’s login. And like every Path A mechanic, it still walks away with a token that names no task. CIBA is the right tool for one step inside an approval workflow, getting an out-of-band human to say yes, and the wrong thing to build the workflow on.

RFC 8628 Device Grant and RFC 9470 Step-up cover adjacent interaction and authentication cases, and the pattern repeats: each solves one hop, none originates or carries the requestable-denial signal that starts this flow, and none names the task the approval is for.

The Ontology Problem

The “AS evaluates the request” step is where Path A meets domain reality. For the AS to mint a token whose RAR entries describe read Q3 financials for Example Corp, the AS has to be able to evaluate whether the request is sensible: whether the client may make this kind of request, whether the user’s role permits the action, whether the financial period and entity identifier are valid for this user, whether the constraints are compatible. That is Resource-Server-domain knowledge.

The AS in a typical multi-tenant SaaS IdP is generic. It knows OAuth, RAR, scope, and client registration. It does not natively know what financials means, what Q3 2026 resolves to in the finance system, or which entity identifiers are valid for this user. The RAR type and actions strings are opaque to the AS unless the AS has been taught them. There are three ways out, and each has a cost:

  1. Tight coupling. The AS is purpose-built for the RS. The finance system’s AS is part of the finance system. This is common for first-party deployments and does not generalize to the open-world MCP scenario where any agent can call any tool on any MCP server.
  2. Standardized vocabulary. A shared operation language across resources, such as a common RAR-type registry. Real progress, but the vocabulary itself is a slow, committee-driven artifact, and not every RS adopts it.
  3. Delegated evaluation. The AS calls the RS or an RS-domain PDP to validate the RAR at issuance time. This works but reintroduces an RS-side decision into Path A. The “carry the authority” property weakens because the RS is back in the loop, just earlier than execution.

In practice, many RAR deployments rely on tight AS/RS coupling or delegated evaluation, and clients often know supported RAR types through prior integration. The Zehavi metadata draft can surface the schemas, but it does not tell the AS how to validate the domain semantics behind them. RAR improves the expression of authority; it does not eliminate the need for a shared vocabulary or resource-domain validation.

Path B: Resource-side FGA

In Path B, the resource server evaluates. The client presents whatever credential or authenticated session the MCP server accepts, and the component controlling execution acts as the Policy Enforcement Point. It calls the PDP for the concrete request and executes only on a permit.

This is the path proposed by the draft, experimental MCP SEP-2848 “Asynchronous Approval for Tool Calls”. The agent calls the tool. The MCP server evaluates the request through a PDP. On a requestable denial, the server can bind an AuthZEN Access Request to an MCP task and return a working task result instead of the tool result. After approval, it reevaluates before execution. At-most-once dispatch and exactly-once business effects still require careful server design and application-level idempotency; a task handle alone cannot provide those guarantees.

sequenceDiagram participant Agent participant MCP as MCP Server (PEP) participant PDP as PDP / Access Request participant Approver Agent->>MCP: tools/call query_financials MCP->>PDP: AuthZEN Evaluation alt In-bounds PDP-->>MCP: permit MCP-->>Agent: tool result else Approval needed PDP-->>MCP: requestable denial MCP->>PDP: AuthZEN Access Request PDP-->>MCP: access_request_id MCP-->>Agent: CreateTaskResult, status = working Note over PDP,Approver: Approval out of band Approver->>PDP: approve Agent->>MCP: tasks/get MCP->>PDP: reevaluate PDP-->>MCP: permit MCP-->>Agent: completed + tool result end

Concretely, the PEP sends the PDP an AuthZEN evaluation for the exact call. The subject is the user, with the client and actor in context, so the decision turns on the user’s authority and the agent’s together:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "subject": { "type": "user", "id": "alice@example.com" },
  "action": { "name": "read" },
  "resource": {
    "type": "financials",
    "id": "Example Corp:Q3 2026",
    "properties": { "period": "Q3 2026", "entity": "Example Corp" }
  },
  "context": { "client_id": "renewal-assistant", "act": "agent:renewal-assistant/7f2a" }
}

An in-bounds call returns {"decision": true}. A requestable denial returns decision: false with an access_request the agent can act on, the same shape the Access Request and Approval Profile carries:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
  "decision": false,
  "context": {
    "evaluation_id": "eval_01K3FN2P8Q",
    "reason": "approval_required",
    "access_request": {
      "endpoint": "https://as.finance.example.com/access/v1/requests",
      "template": "financials_read_approval",
      "expires_at": "2026-06-04T20:25:00Z",
      "binding_token": "eyJhbGciOiJFUzI1NiJ9..."
    }
  }
}

The server submits that to the advertised endpoint, presenting the denial binding so the request cannot be replayed against a different call, and gets back a task with a status_endpoint to poll (status: pending until a decision):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "subject": { "type": "user", "id": "alice@example.com" },
  "resource": { "type": "financials", "id": "Example Corp:Q3 2026" },
  "action": { "name": "read" },
  "denial": {
    "evaluation_id": "eval_01K3FN2P8Q",
    "binding_token": "eyJhbGciOiJFUzI1NiJ9...",
    "expires_at": "2026-06-04T20:25:00Z"
  }
}

In the proposed SEP, the client carries only an MCP taskId. Authorization artifacts remain on the server side, and no new client-held access token is minted. Approval is an input to a fresh authorization decision, not a standing grant. The server reevaluates with freshness-bounded policy state before execution, reducing TOCTOU exposure between approval and use.

As a concrete example, the agent is approved to read Q3 financials for Example Corp. Between approval and execution the user is offboarded from the finance system, or Example Corp is reclassified as restricted. An offline-validating Path-A server may still accept a token that remains valid on its face, handing an offboarded user’s agent confidential data; an introspecting or event-subscribed server can reject it. Path B narrows the window by requiring a fresh per-call evaluation immediately before execution.

MCP defines elicitation as a server-to-user interaction primitive with form and URL modes. It can gather structured input, obtain confirmation, or direct the user to an external flow. Elicitation is not authorization or durable consent by itself: the server must bind the interaction to the authenticated user and feed the result into its policy or approval workflow.

Form-mode elicitation is tied to the active interaction; URL mode can launch an out-of-band flow and optionally signal completion. For supervising agents, automated risk engines, or reviewers who are not the active user, a durable task or access-request workflow is the better fit. An elicited value changes standing authority only if it goes through a governed re-authorization, not by virtue of being collected.

Tool Discovery Can Share the Policy

A second strength of Path B is that the MCP server already owns the tool catalog. It can filter tools/list through the same policy system that gates invocation, giving the agent a catalog appropriate to its current context.

That filtering is an exposure and usability control, not a durable authorization grant. Catalogs can be stale, policy can change, and a client can call a tool by name without first listing it. The MCP server must therefore authorize every tools/call. Path A does not prevent the server from applying the same filtering; it simply does not give the AS control over the catalog.

Iteration across Tool Calls

Path B’s iteration shape is symmetric to Path A’s but lives at a different boundary. Each new MCP server fronts its own PDP, with policy authored against its own domain. The client credential may remain unchanged while each concrete call is evaluated locally. Without a shared task object, however, every PDP independently determines whether the call is in-bounds, every requestable denial creates a local task and workflow, and audit records accumulate per server. The token churn of Path A is absent; approval and audit fragmentation remain.

Shape and Ontology

Path B produces no new client-held token. Its least-privilege result is a PDP decision over the specific call, plus whatever evidence the deployment records. The MCP server enforces, the PDP decides, and any approval is an input to that decision rather than a standing grant.

Path B sidesteps the ontology problem at the AS layer. The MCP server is the domain expert for its own tools. It already knows what query_financials(period, entity) means because it implements the call. The PDP it consults can be authored against the RS’s actual domain model. There is no AS in the request path that needs to learn the resource’s vocabulary.

Concretely, the PDP can be Cedar, OpenFGA, OPA, or another policy engine. The wire contract between the PEP and PDP can use the final AuthZEN Authorization API 1.0, including its PDP metadata discovery, so the MCP integration need not depend on one policy engine. Where traffic already passes through an API gateway, the gateway may be the PEP if it controls execution and has the full request context.

The cost is portability. The decision lives at the MCP server and its PDP, and is not directly verifiable by another party without re-running the evaluation against the same domain model.

A Bridge: Transaction Challenge

Path A and Path B place the decisive authorization step in different places. In Path A, the AS grants portable authority before execution. In Path B, the resource-side PDP decides at execution. The individual OAuth Transaction Challenge proposal sketches a hybrid.

The shape goes like this. The MCP server (or any RS) detects that the agent’s existing token does not authorize the specific transaction in front of it. The RS issues a transaction challenge that names the resource, the action, and the transaction-specific context. The client takes the challenge to the AS. The AS evaluates the transaction, requesting user consent or step-up if needed. The AS issues a transaction-specific token bound to that exact transaction. The client retries the call.

The RS is back in the trigger path, like Path B. The AS still mints the token, like Path A. The token is per-transaction, not per-session. The decision involves both sides instead of being owned by one.

The tradeoff is shape against fit. Transaction Challenge is more disciplined than Path A’s WWW-Authenticate + RAR cycle, because the RS hands the AS a precise transaction description rather than relying on the client to construct a correct RAR from discovered metadata. It is less efficient than Path B for cases where the agent should never hold a transaction-bearer token in the first place, because the AS round-trip and token issuance remain in the request path.

The three shapes can be combined: narrow tokens for portable authority, per-call PDP evaluation for context-sensitive actions, and a transaction challenge where the RS must describe a call-specific transaction to the AS. Transaction Challenge is an individual proposal, so this is an architectural option rather than an established interoperability pattern.

What Each Path Optimizes

The two paths are not enemies. They place the same five pipeline steps at different boundaries, and the tradeoffs follow from where each step lives.

Pipeline stepPath A: token-side FGAPath B: resource-side FGA
Discover needAgent infers from task plan or a WWW-Authenticate challengeAgent infers from task plan or a tools/list catalog the MCP server already filtered
Tool discoveryOut of scope for the AS; the MCP server can filter independentlyCan use the same PDP as invocation, but still requires execution-time authorization
Request authorityRAR authorization_details to the AS, narrowed to one actionTool invocation directly to the MCP server
EvaluateAt the AS, optionally consulting a PDP. The AS needs domain knowledge or must delegateAt the MCP server’s PDP, with the resource’s own domain model in scope
Authorize or escalateNarrow access token at the AS, or deferred handle on requestable denialPermit decision at the PDP, or an MCP task handle on requestable denial
ExecuteBaseline uses local token validation; higher-assurance deployments add introspection, events, or runtime evaluationMCP server runs the tool only on a freshness-bounded permit
Carried artifactSender-constrained access token (DPoP or mTLS), portable proofNo new artifact on permit; a server-generated MCP taskId while asynchronous approval is pending
Latency profileToken acquisition up front, then local validation unless stronger freshness is requiredOne PDP evaluation per consequential call; caching must remain within policy freshness bounds
EasyOffline validation, token portability, low execution-time latencyDomain-specific policy, TOCTOU-resistant execution, no token explosion
HardAS ontology, RAR vocabulary convergence, token churnCross-server audit, approval fragmentation, portable proof
MissingDurable task context across callsDurable task context across calls

The choice is per call, not per deployment, and it follows from where the hard part lives:

  • Reach for token-side FGA (Path A) when authority has to travel: several downstream hops, offline or low-latency validation, or an audience that cannot call back to a PDP on every request. The cost you accept is the AS ontology problem and token churn.
  • Reach for resource-side FGA (Path B) when the decision has to stay where the meaning lives: the resource owns rich domain semantics, the action is consequential, or TOCTOU drift between grant and use is unacceptable. The cost you accept is a per-call evaluation and a decision that is not portable.
  • Reach for the hybrid (Transaction Challenge) when the resource must hand the AS a precise transaction the client cannot construct from discovered metadata, but you still want an AS-minted, per-transaction token.

Carry when you can, evaluate when you must. But the choice only governs a single call. Once the task spans more than one call, server, or authorization domain, neither path stands alone, because neither names the task that connects them.

What to Get Right

Both paths can be implemented in ways that look like least-privilege without being it. A few failure modes are worth designing against on purpose, whichever path you pick.

  • Authority at the wrong grain. A token or policy that says “may call the finance MCP server” is not least-privilege; “may read Q3 financials for Example Corp” is. Path A puts the grain in the authorization_details entry; Path B puts it in the PDP’s view of the concrete call. Coarse authority is the most common way least-privilege quietly decays into ambient access.
  • Bearer tokens that travel. A Path A access token that is not sender-constrained is replayable by anyone who captures it. Bind it to the client with DPoP (RFC 9449) or mTLS (RFC 8705) so a stolen token is useless off the agent that earned it.
  • Enforcement off the choke point. Filtering tools/list is exposure control, not authorization, and a client can call a tool it never listed. The PEP has to sit where execution actually happens and authorize every tools/call, or the decision is bypassable by design.
  • Stale or replayed approvals. An approval is an input to a fresh decision, not a standing grant. Bind it to the exact subject, action, resource, and request (the binding_token above), bound it in time, and reevaluate at execution. Path B’s per-call evaluation narrows the TOCTOU window that an offline-validated Path A token leaves open.
  • Confused-deputy delegation. The agent authenticates as itself but acts for a user; the decision must turn on both, not collapse to one. Preserve the actor chain (RFC 8693 act) so the principal and the acting workload stay distinct in the decision and in the evidence.

Where Both Paths Break

Both paths can work well for one tool call to one MCP server. The board-packet agent, however, makes at least three calls across three MCP servers and three Authorization Servers, and may iterate within each server. What does that look like at the protocol layer?

In Path A, each new MCP server starts its own discovery cycle, and each AS sees only its own calls and operation vocabulary. Unless the deployment adds a shared task identifier and governance service, consent and audit remain local to each authorization domain. The client may accumulate several narrow, short-lived tokens, while each AS resolves the ontology problem independently.

In Path B, each MCP server’s PDP evaluates calls with only its local context. Unless the deployment supplies a shared task identifier and lifecycle, requestable denials create separate tasks and audit records. Token churn disappears; cross-server approval and audit fragmentation do not.

flowchart TB Agent([Agent]) User([User / Approver]) subgraph PA["Path A: token-side"] AS1[AS-1] AS2[AS-2] AS3[AS-3] end subgraph PB["Path B: resource-side"] PDP1[MCP-1 PDP] PDP2[MCP-2 PDP] PDP3[MCP-3 PDP] end Agent -->|RAR tool 1| AS1 Agent -->|RAR tool 2| AS2 Agent -->|RAR tool 3| AS3 Agent -->|call tool 4| PDP1 Agent -->|call tool 5| PDP2 Agent -->|call tool 6| PDP3 AS3 -.->|isolated approval prompts| User PDP3 -.->|isolated approval prompts| User

The root cause is structural. Both paths build per-call authorization on top of a protocol stack with no standardized first-class task object. Deployments can correlate approvals, tokens, decisions, and audit records with proprietary transaction identifiers, but those identifiers do not provide an interoperable governance lifecycle.

The user-visible failure mode is concrete:

  • The user sees repeated approval prompts for fragments of one task.
  • The agent accumulates narrow tokens or pending tasks with no shared lifecycle.
  • Audit joins depend on timestamps, client identifiers, and log correlation instead of a task identifier.
  • Out-of-bounds requests create new isolated approvals instead of governed expansion of the original task.
  • Each AS or PDP independently reconstructs what the board-packet task means, often inconsistently.

Fixing this with better client orchestration, smarter SDKs, batched RAR requests, or richer log shipping reduces symptoms. None of those moves close the structural gap. The protocol still does not name the task, so anything built on top of it has to infer the task from indirect signals every time.

That is the gap from the top of this post, now with the mechanism that produces it on the table. Least-privilege per call is necessary, but it is not sufficient once a task spans many calls, and neither path closes that alone. What is missing is a first-class task object: a durable, governed thing both paths can bind to, that the user approves once and that every token and decision can point back to.

Whatever fills that gap, the requirements are the same on either substrate. Any candidate has to answer five questions that per-call authorization cannot:

  • Identity. Does the task have a stable identifier that every participating server and authorization domain can reference, without itself becoming a cross-context correlator?
  • Approval. Did the user approve the task once, against a description of what it will do, instead of re-approving each fragment?
  • Binding. Can every token and every PDP decision be tied back to that task, so a credential or a permit names the work it serves?
  • Expansion. When the agent needs authority the task did not anticipate, is there a governed way to widen it that preserves lineage, instead of spawning an unrelated approval?
  • Audit. Can a reviewer reconstruct everything done under the task by its identifier, deterministically, instead of stitching logs by timestamp and client?

These are the questions, not the answer. The answer is its own design problem: open-world OAuth needs a shaping step that turns a request into a governed task (Open-World OAuth Still Needs Mission Shaping), a session is not that task (Sessions Are Not Missions), and AAuth’s native Mission, below, is one substrate’s concrete take. This post’s job is to establish why per-call authorization, on either substrate, needs one.

Comparison with AAuth

The gap this post just named, that nothing in the stack names the task, is not a law of nature. It is specific to OAuth. AAuth Protocol -02 reaches the same per-call question from a clean-slate substrate, and it already carries a task object across hops. The Agent proposes a Mission to its Person Server; once approved the Mission is immutable, bound by the SHA-256 hash of its JSON, and the Agent presents a Mission Reference, the approver URL plus that hash, on every call through the AAuth-Mission header. Resource tokens and auth tokens carry the same reference, so per-call authorization binds to one named task without translating it into OAuth RAR. I covered that in AAuth Now Has a Mission Layer. That is exactly the primitive the OAuth stack is missing, sitting in the substrate from the start, which is why the gap shows up on the OAuth side first.

On the per-call question itself, AAuth gives the Resource four modes, each adding parties and capabilities, and they line up against the same axis the two paths do. The Resource stays authoritative for its policy in every one:

AAuth modeWhere authorization landsClosest analogue in this post
Identity-basedResource verifies the agent’s signed identity (the Agent Provider-issued agent token) and applies its own access-control policy. The wire carries no call-specific authorityPath A where the credential carries who, and local policy decides what
Resource-managed (two-party)Resource handles authorization itself, through interaction, existing infrastructure, or internal policy. It is its own decision point at executionPath B at the MCP boundary
PS-asserted (three-party)Resource issues a resource token describing the access it needs; the Person Server asserts identity and consent; the Resource applies its own policyPath B with a validating server supplying authenticated context, similar to an introspection response
Federated (four-party)Resource has its own Access Server; the Person Server federates with it to obtain an audience-specific auth tokenPath A with a Resource AS that mints a local credential

Past that, the two constructions differ mostly in substrate, not in pipeline. Both walk the same discover, request, evaluate, authorize-or-escalate, execute steps, and tool discovery, invocation, and approval keep their shapes across both. The difference that matters is the one at the top of this section: one substrate names the task, and the other does not, yet.

Agents Do Not Only Call Tools

The MCP tool boundary is one place where this pattern surfaces. Agents also reach the open web through an egress proxy that enforces a destination allowlist. When the agent reaches a destination the allowlist does not cover, the result today is the same shape as a flat 401 with no machine-actionable recovery.

The same five-step pipeline applies at the egress boundary. Discover need (the agent tries to connect). Request authority (the connection attempt). Evaluate (the egress proxy is the PEP, calling its PDP). Authorize or escalate (allow the connection or route through a requestable-denial workflow). Execute (the connection completes).

The only changes are vantage point and visibility. The egress proxy only sees what its position reveals. Behind an opaque CONNECT tunnel it can govern hosts. At a TLS-terminating proxy it can match on HTTP method and URL. At an API-aware gateway it can govern named operations. The ontology problem reappears, with the proxy now the domain expert for what it can see at its altitude.

I wrote about that boundary in A Blocked Agent Is a Captive Client, which sketches analogous approval architectures. Destination- or operation-level approval can ride on the AuthZEN Access Request profile in an OAuth deployment. An AAuth deployment can instead use its identity-based, resource-managed, PS-asserted, or federated mode according to the Resource’s selection. The captive-portal transport can reuse RFC 8908.

Real agents touch both boundaries on every non-trivial task. The board-packet agent does not only call MCP tools. It also fetches reference data from partner APIs and pulls public reports. Both boundaries face the same fan-out gap, and whatever task object closes it has to span both to keep the audit trail coherent.

Where to Start

The smallest useful pieces are independent. Each one pays for itself before the rest arrives.

  • If you build MCP servers, put an authorization check at tool execution and use the same policy system to filter tools/list. Treat filtering as exposure control; always authorize tools/call. SEP-2848 proposes an experimental binding for asynchronous, server-side approval using MCP Tasks.
  • If you run an Authorization Server for agent clients, publish RAR-type metadata so clients can discover supported authorization_details schemas. Add resource-domain validation rather than assuming schema discovery gives the AS semantic knowledge. For asynchronous decisions, compose the AuthZEN Access Request profile with an explicit OAuth completion mechanism such as Deferred Code Processing.

These pieces pay for themselves independently. But notice what none of them fixes: once a task spans more than one call, server, or authorization domain, you are still missing the object that names the task. Carry authority when it must travel, evaluate locally when context must stay local, and treat the absent task object as the next thing to build, not a detail to paper over with log correlation. Per-call least-privilege without it is not half the problem solved; it is a liability that compounds with every tool you let the agent reach.