coaz-mcp February 2026
Tulshibagwale & Olivier Standards Track [Page]
Workgroup:
OpenID AuthZEN
Published:
Authors:
A. Tulshibagwale
CrowdStrike
A. Olivier
Cerbos

COAZ-MCP: COAZ Binding for the Model Context Protocol - Draft 1

Abstract

This specification defines COAZ-MCP, the COAZ binding — as defined by the COAZ Framework [COAZFW] — for the Model Context Protocol (MCP) [MCP]. It defines how MCP JSON-RPC messages are mapped into requests to the OpenID AuthZEN Authorization API [AUTHZEN], enabling MCP gateways and servers to perform fine-grained, parameter-level authorization through an AuthZEN Policy Decision Point (PDP). The binding defines a fixed default mapping for each MCP method, so that all MCP messages can be authorized without per-operation configuration, and allows MCP servers to override the default for a specific tool by declaring a mapping in the tool's input schema using Common Expression Language (CEL) [CEL]. It also defines how an MCP server advertises declared mappings so that clients can understand how authorization will be performed.

Table of Contents

1. Introduction

The Model Context Protocol [MCP] enables AI agents (MCP clients) to discover and invoke capabilities offered by MCP servers — tools, resources, prompts, and more — over JSON-RPC [JSONRPC]. Authorization in MCP relies on OAuth 2.1 [OAUTH21], but OAuth alone leaves some concerns unaddressed:

The OpenID AuthZEN Authorization API [AUTHZEN] provides standardized, fine-grained authorization using the Subject-Action-Resource-Context (SARC) model. The COAZ Framework [COAZFW] defines, in a protocol-neutral way, how to project the information model of a protocol into an AuthZEN Authorization API request. This specification is COAZ-MCP, the COAZ binding for MCP: it binds that framework to MCP's information model.

This binding authorizes all MCP messages. It does so by defining a fixed default mapping for each MCP method (Section 7), which a PEP applies unless an MCP server has declared a more specific mapping for a particular tool (Section 8).

1.1. Requirements Notation and Conventions

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

1.2. Terminology

This specification uses the terms defined in the COAZ Framework [COAZFW] — notably binding, information model, input variable, mapping, literal, expression, default mapping, declared mapping, PEP, and PDP — and adds the following:

MCP Client:

An AI agent or application that connects to MCP servers and invokes their capabilities. The MCP Client acts as the caller.

MCP Server:

A service that exposes tools, resources, and prompts to MCP clients via the Model Context Protocol.

MCP Gateway:

An optional intermediary between an MCP client and one or more MCP servers. Where present, a gateway MAY act as the PEP on behalf of the MCP server.

Tool:

A callable capability exposed by an MCP server, described by a name, description, and input schema.

COAZ:

Compatible with OpenID AuthZEN (pronounced "cozy"). The framework [COAZFW] of which this specification is a binding.

2. Relationship to the COAZ Framework

This binding fulfils the binding conformance requirements of the COAZ Framework [COAZFW] as summarized below; each row is specified in the referenced section.

Table 1
Framework requirement This binding
Information model params, token (Section 3)
Mapping location x-authzen-mapping in a tool's inputSchema; otherwise the default mapping (Section 8, Section 7)
Literal/expression discriminator framework default: $ prefix, $$ escape (Section 4)
Expression language framework default: Common Expression Language [CEL] (Section 4)
Envelopes evaluation and evaluations (Section 5)
Operations in scope All MCP methods, per the default mapping table; pass-through set listed (Section 7)
Default mapping behavior Per-method default mapping table (Section 7)
Declared mapping behavior MCP server MAY declare a mapping for a tool; overrides the default for that tool (Section 8)
Trust-anchored fields subject.id SHOULD be anchored to the subject-identity claim; when it is, it is enforced by verification against $token.sub, and a mapping MAY override its source for edge cases (Section 8)
Error transport JSON-RPC 2.0 error responses (Section 10)
Discoverability Declared mappings advertised in the tools/list response (Section 6)

2.1. Architecture

The response to tools/list carries each tool's inputSchema, and, for tools that declare a mapping, the x-authzen-mapping within it. Because the mapping travels in the protocol itself, it reaches every party that can act as the PEP. This binding supports both deployment shapes:

  • Gateway as PEP. Where an MCP gateway sits between clients and servers, it obtains each tool's declared mapping by observing the tools/list response it proxies, with no out-of-band configuration. This is what lets a gateway — often operated by a platform or IT team with no knowledge of a specific server's business logic — enforce authorization that the server itself defined: the MCP server declares how its operations are to be authorized, and the gateway enforces it.

  • Server as PEP. Where no gateway is present, the MCP server enforces its own declared mappings, and the default mappings for everything else, calling the PDP directly.

In both shapes, carrying the mapping in tools/list also makes it available to the MCP client, so that the client (or the LLM driving it) can understand how a call will be authorized and shape tool arguments appropriately.

When an MCP message is processed, the PEP — the MCP gateway or the MCP server itself — selects the applicable mapping (declared, if present for the tool; otherwise the default mapping for the method), constructs the corresponding AuthZEN request, and calls the PDP before allowing the message to take effect.

+-------------+      +-------------+        +-------------+      +-------------+
| MCP Client  |      | MCP Gateway |        | MCP Server  |      | AuthZen PDP |
+-------------+      +-------------+        +-------------+      +-------------+
       |                    |                      |                    |
       |  1. tools/list     |    1. tools/list     |                    |
       +------------------->+--------------------->+                    |
       |                    |  tools incl. any     |                    |
       |  tools incl. any   |  x-authzen-mapping   |                    |
       |  x-authzen-mapping +<---------------------+                    |
       +<-------------------+                      |                    |
       |                    |                      |                    |
       |  2. MCP request    |   3. authorize       |                    |
       +------------------->+----------------------+------------------->|
       |                    |                      |   permit / deny    |
       |                    +<---------------------+--------------------+
       |                    |  4. forward request  |                    |
       |                    +--------------------->|  (5. server MAY    |
       |                    |                      |   re-authorize)    |
       |                    |   6. response        |                    |
       |  6. response       +<---------------------+                    |
       +<-------------------+                      |                    |
       v                    v                      v                    v
Figure 1: COAZ enforcement for MCP messages

Steps 3 and 5 are alternatives. If a gateway acts as the PEP it calls the PDP; otherwise the server does. It is possible, but redundant, for both to call the PDP.

3. Information Model

This binding exposes two input variables to expressions:

params:

A map corresponding to the params object of the MCP JSON-RPC request being authorized. For tools/call this includes name (the tool name) and arguments (the caller-supplied values); for other methods it contains that method's parameters as defined by [MCP]. Fields are accessed using standard CEL field or index notation (e.g., params.name, params.arguments.id, params.uri, or params.arguments["customer-id"]).

token:

A map corresponding to the complete set of decoded claims of the JWT-formatted [RFC7519] OAuth access token used to authorize the request. All claims are available, including but not limited to sub, iss, aud, exp, and client_id. Claims are accessed using standard CEL field or index notation (e.g., token.sub, token.aud, token.client_id).

3.1. The Subject-Identity Claim

The AuthZEN subject identifies the principal on whose behalf authorization is requested. Throughout this binding, subject.id is derived from a single token claim, the subject-identity claim, which by convention is sub — hence the expression $token.sub used in the mappings below.

Where an access token is issued with the agent as the principal (so sub identifies the agent, not the human user on whose behalf it acts), a deployment MAY designate a different claim — an on-behalf-of claim — as the subject-identity claim, so that subject.id carries the human user. When a deployment designates an on-behalf-of claim C, every use of $token.sub as subject.id in this binding (in default mappings, in declared mappings, and in the verification of Section 8) is read as $token.C. The agent identity remains in context.agent (typically $token.?client_id) regardless. The designated claim MUST be agreed between the PEP and the token issuer; absent any designation, the subject-identity claim is sub.

4. Expressions and Literals

This binding uses the framework's defaults unchanged ([COAZFW]): expressions are written in Common Expression Language [CEL], and the leading-$ discriminator with its $$ escape distinguishes them from literals. A string value beginning with $ is a CEL expression evaluated against the params and token input variables (e.g., $token.sub, $params.arguments.id, $params.arguments.amount > 10000 ? 'high' : 'standard'); any other value is a literal, used verbatim (e.g., the string customer, the number 10, the boolean true); $$50 denotes the literal string $50. Within an expression, ordinary CEL syntax applies, including CEL's own single-quoted string literals.

Per the COAZ expression contract ([COAZFW]), a CEL expression MUST evaluate to a single JSON value — a scalar, list, or map. A list or map returned by an expression is a single field value and does not, by itself, produce multiple evaluations. To populate an OPTIONAL field from a claim or parameter that may be absent, an expression MUST use CEL optional selection (the .? operator, e.g. $token.?client_id), which omits the field from the request when the key is missing; plain field selection on a missing key is an evaluation error.

An expression whose evaluation results in an error, or that yields absent or null for a REQUIRED field (subject, action, or resource), is a mapping error (Section 10.1). Every mapping in this binding — including every default mapping — contains CEL expressions, so a conforming PEP MUST include a CEL evaluator; there is no expression-free conformance level.

The following example illustrates how the input variables are populated. Given the tools/call request:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "get_customer",
    "arguments": { "id": "cust-12345", "case": "case-67890" }
  }
}
Figure 2: Example tools/call JSON-RPC request

and an access token with decoded claims:

{
  "sub": "alice@example.com",
  "client_id": "http://agentprovider.com/agent-app-id",
  "iss": "https://auth.example.com",
  "aud": "https://mcp.example.com",
  "exp": 1750000000
}
Figure 3: Example decoded access token claims

expressions resolve as follows:

Table 2: Expression and literal resolution
Expression Resolved value
$params.name "get_customer"
$params.arguments.id "cust-12345"
$token.sub "alice@example.com"
$token.?client_id "http://agentprovider.com/agent-app-id"
customer "customer" (literal)

5. Mapping Envelopes

As defined by the COAZ Framework ([COAZFW]), a mapping is a JSON object with a single top-level member — its envelope — whose key names the AuthZEN API to call. This binding permits both envelope keys defined by the framework:

A mapping whose top-level structure is anything other than exactly one of these two keys is malformed, and the PEP MUST treat it as a mapping error (Section 10.1).

A PDP used with this binding MUST support the Access Evaluation API. Where a declared mapping uses the evaluations envelope, the PEP MUST either send the constructed request to the Access Evaluations API or, if its PDP does not support that API, issue one Access Evaluation request per entry — applying the top-level defaults to each entry exactly as [AUTHZEN] defines — and allow the message only if every decision is a permit.

6. Declaring a Mapping

An MCP server declares a mapping for a tool by including an x-authzen-mapping object within that tool's inputSchema in the tools/list response. The presence of x-authzen-mapping indicates the tool carries a declared mapping; its absence means the default mapping for tools/call applies. No separate marker field is used.

Because the declared mapping is carried in the tools/list response, it is available to any MCP gateway on the path — which can therefore enforce it as the PEP without out-of-band configuration (Section 2) — and to the MCP client, satisfying the framework's discoverability capability. When MCP Server Cards become available, x-authzen-mapping SHOULD also be included there.

The following non-normative example shows a tools/list response with one tool that declares a mapping and one that does not:

{
  "tools": [
    {
      "name": "get_customer",
      "description": "Get customer details",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id":   { "type": "string", "description": "The customer identifier" },
          "case": { "type": "string", "description": "The case being worked on" }
        },
        "required": ["id"],
        "x-authzen-mapping": {
          "evaluation": {
            "subject": { "type": "identity", "id": "$token.sub" },
            "action": { "name": "get_customer" },
            "resource": { "type": "customer", "id": "$params.arguments.id" },
            "context": { "agent": "$token.?client_id", "case": "$params.arguments.case" }
          }
        }
      }
    },
    {
      "name": "get_local_weather",
      "description": "Get weather for the local area",
      "inputSchema": {
        "type": "object",
        "properties": { "zip": { "type": "string", "description": "Zip code" } },
        "required": ["zip"]
      }
    }
  ]
}
Figure 4: Example tools/list response with a declared mapping

get_customer declares a mapping; get_local_weather does not and is therefore authorized by the default tools/call mapping (Section 7).

7. Default Mappings

This binding defines a default mapping for each MCP method. A PEP MUST apply the default mapping for a method unless a declared mapping applies to the specific operation (Section 8).

Every default mapping uses the evaluation envelope (Section 5) and shares the following subject and context, which establish the subject (the user on whose behalf the agent acts) and the agent context:

{
  "evaluation": {
    "subject": { "type": "identity", "id": "$token.sub" },
    "context": { "agent": "$token.?client_id" },
    "action":   { "...": "per-method" },
    "resource": { "...": "per-method" }
  }
}
Figure 5: Shared default-mapping shape

For methods that target the MCP server as a whole rather than a specific item, resource.id identifies this MCP server. MCP requires access tokens to be audience-bound to the target server via resource indicators [RFC8707], so the server identity is carried in the aud claim. The default mappings express this as $token.aud, with the following normative resolution: resource.id MUST be a single string identifying this server. When aud is a single string, that value is used directly. When aud is an array (as permitted by [RFC7519]), the PEP MUST select the single member that identifies this server (its own resource identifier per [RFC8707]) and use that value; resource.id MUST NOT be set to an array. If the PEP cannot resolve aud to exactly one server-identifying value, it is a mapping error (Section 10.1). A PEP that knows its own resource identifier from configuration MAY use it directly instead of deriving it from aud.

The default mappings are:

7.1. Lifecycle

// initialize
{ "evaluation": {
    "subject": { "type": "identity", "id": "$token.sub" },
    "context": { "agent": "$token.?client_id", "protocol_version": "$params.protocolVersion" },
    "action": { "name": "initialize" },
    "resource": { "type": "mcp_server", "id": "$token.aud" } } }

ping is a pass-through operation: the PEP MUST NOT call the PDP and MUST allow it to proceed.

7.2. Tools

// tools/list
{ "evaluation": {
    "subject": { "type": "identity", "id": "$token.sub" },
    "context": { "agent": "$token.?client_id" },
    "action": { "name": "tools/list" },
    "resource": { "type": "mcp_server", "id": "$token.aud" } } }

// tools/call  (applies when the tool declares no mapping)
{ "evaluation": {
    "subject": { "type": "identity", "id": "$token.sub" },
    "context": { "agent": "$token.?client_id" },
    "action": { "name": "tools/call" },
    "resource": { "type": "tool", "id": "$params.name" } } }

7.3. Resources

// resources/list
{ "evaluation": {
    "subject": { "type": "identity", "id": "$token.sub" },
    "context": { "agent": "$token.?client_id" },
    "action": { "name": "resources/list" },
    "resource": { "type": "mcp_server", "id": "$token.aud" } } }

// resources/read   (resources/subscribe and resources/unsubscribe use the
//                   same shape; only action.name differs)
{ "evaluation": {
    "subject": { "type": "identity", "id": "$token.sub" },
    "context": { "agent": "$token.?client_id" },
    "action": { "name": "resources/read" },
    "resource": { "type": "resource", "id": "$params.uri" } } }

7.4. Prompts

// prompts/list
{ "evaluation": {
    "subject": { "type": "identity", "id": "$token.sub" },
    "context": { "agent": "$token.?client_id" },
    "action": { "name": "prompts/list" },
    "resource": { "type": "mcp_server", "id": "$token.aud" } } }

// prompts/get
{ "evaluation": {
    "subject": { "type": "identity", "id": "$token.sub" },
    "context": { "agent": "$token.?client_id" },
    "action": { "name": "prompts/get" },
    "resource": { "type": "prompt", "id": "$params.name" } } }

7.5. Completion

// completion/complete
{ "evaluation": {
    "subject": { "type": "identity", "id": "$token.sub" },
    "context": { "agent": "$token.?client_id" },
    "action": { "name": "completion/complete" },
    "resource": {
      "type": "$params.ref.type == 'ref/prompt' ? 'prompt' : 'resource'",
      "id":   "$params.ref.type == 'ref/prompt' ? $params.ref.name : $params.ref.uri" } } }

7.6. Logging

// logging/setLevel
{ "evaluation": {
    "subject": { "type": "identity", "id": "$token.sub" },
    "context": { "agent": "$token.?client_id", "level": "$params.level" },
    "action": { "name": "logging/setLevel" },
    "resource": { "type": "mcp_server", "id": "$token.aud" } } }

7.7. Tasks

// tasks/get   (tasks/result and tasks/cancel use the same shape; only
//              action.name differs)
{ "evaluation": {
    "subject": { "type": "identity", "id": "$token.sub" },
    "context": { "agent": "$token.?client_id" },
    "action": { "name": "tasks/get" },
    "resource": { "type": "task", "id": "$params.taskId" } } }

// tasks/list
{ "evaluation": {
    "subject": { "type": "identity", "id": "$token.sub" },
    "context": { "agent": "$token.?client_id" },
    "action": { "name": "tasks/list" },
    "resource": { "type": "mcp_server", "id": "$token.aud" } } }

7.8. Pass-through Operations

The following are pass-through: the PEP MUST NOT call the PDP for them and MUST allow them to proceed. They are listed explicitly so that the absence of a mapping is never interpreted as a deny.

  • ping

  • all notifications (notifications/*), which carry no id and expect no response.

7.9. Unknown Methods

A request whose method has neither a default mapping defined by this binding nor an applicable declared mapping, and that is not in the pass-through set above, MUST be denied: the PEP MUST NOT allow it to proceed and MUST return an authorization denial (Section 10.2). This ensures that methods introduced by future MCP versions or extensions fail closed rather than bypassing authorization. The pass-through set is the only set of methods that proceed without a PDP decision.

7.10. Server-initiated Requests

The MCP requests sampling/createMessage, elicitation/create, and roots/list are initiated by the server toward the client. The PEP model in this binding authorizes client-to-server requests using the client's access token, which is not the appropriate identity for server-initiated requests. Authorization of server-initiated requests is therefore out of scope for this version of the binding.

8. Declared Mappings

An MCP server MAY declare a mapping for a tool by including x-authzen-mapping in the tool's inputSchema (Section 6). A declared mapping has the same shape as a default mapping — an envelope naming the AuthZEN API and a template for that API's request body (Section 5) — and uses the same expression and literal rules (Section 4). A declared mapping MAY use either the evaluation or the evaluations envelope: evaluation suffices for most tools, while evaluations serves tools whose single invocation requires multiple decisions.

A declared mapping for a tool overrides the default tools/call mapping for that tool only. It does not affect the default mapping for any other method or for tools/call of any other tool.

The subject identifier (subject.id) SHOULD be anchored to the subject-identity claim of the validated access token. A declared mapping SHOULD include a subject — the request's subject under the evaluation envelope, or the top-level subject under the evaluations envelope — whose id is set to an expression resolving to the subject-identity claim (Section 3.1) — that is, $token.sub (or $token.C where an on-behalf-of claim C is designated). Where subject.id is set to the subject-identity claim, the PEP MUST verify that its resolved value equals that claim in the validated access token, treating a mismatch as a mapping error (Section 10.1); this is the trust-anchored, verification-enforced case defined by the COAZ Framework ([COAZFW]), and it prevents an MCP server — the party being authorized — from asserting the identity of a different subject.

Some deployments cannot convey the subject identity in a token claim — for example, an agentic deployment whose access token is issued to the agent while the identity of the acting user is carried outside the token. For these edge cases a declared mapping MAY override subject.id with a value derived from elsewhere in the operation's inputs; because full override via a CEL expression is already available, any field of the incoming request — token claim, request parameter, or otherwise — can be mapped to subject.id. An overridden subject.id SHOULD still be derived from the validated token rather than from caller-controlled request parameters. When a declared mapping sets subject.id from a source the PEP cannot verify against a token claim, the subject identity is asserted by the mapping author rather than anchored to the token; a PEP — particularly a gateway enforcing a mapping authored by an MCP server — SHOULD emit a warning in this case, and deployments SHOULD account for the additional trust this places in the mapping author (see Section 11).

If a declared mapping omits subject or subject.id, the PEP MUST supply the default subject identifier, $token.sub (or $token.C), so that every request still carries a token-anchored subject.

To close off identity smuggling, a declared mapping using the evaluations envelope MUST NOT set subject within any entry of its evaluations array; the single top-level subject applies to all evaluations. A PEP MUST reject a declared mapping that places subject inside an evaluations entry as a mapping error.

A declared mapping MAY otherwise shape the subject: it MAY set subject.type and additional subject attributes (under properties, per [AUTHZEN]), so that different invocations can produce different subject objects. If a declared mapping omits subject.type, the PEP MUST supply identity. Any subject attribute other than subject.id — including subject.type — is untrusted input, exactly like declared action, resource, and context attributes; only the verified subject.id is trustworthy as the authenticated identity. See Section 11.

For autonomous-agent use cases the context MUST include the agent identity (typically $token.?client_id).

8.1. Single-evaluation Example

The declared mapping for get_customer in Figure 4 uses the evaluation envelope. Applied to the request and token in Figure 2 and Figure 3, it produces the following Access Evaluation request, sent to the Access Evaluation API:

{
  "subject": { "type": "identity", "id": "alice@example.com" },
  "action": { "name": "get_customer" },
  "resource": { "type": "customer", "id": "cust-12345" },
  "context": { "agent": "http://agentprovider.com/agent-app-id", "case": "case-67890" }
}
Figure 6: Resulting Access Evaluation request (single decision)

8.2. Multi-evaluation Example

A tool that copies an object requires two checks: read on the source and write on the destination. The mapping therefore uses the evaluations envelope: the two checks are entries in the evaluations array, and the shared subject and context remain at the top level of the request body. As in every declared mapping, subject.id is set to $token.sub and verified by the PEP.

{
  "name": "copy_object",
  "description": "Copy a storage object from one location to another",
  "inputSchema": {
    "type": "object",
    "properties": {
      "source":      { "type": "string", "description": "Source object location" },
      "destination": { "type": "string", "description": "Destination object location" }
    },
    "required": ["source", "destination"],
    "x-authzen-mapping": {
      "evaluations": {
        "subject": { "type": "identity", "id": "$token.sub" },
        "context": { "agent": "$token.?client_id" },
        "evaluations": [
          { "action": { "name": "read" },
            "resource": { "type": "storage_object", "id": "$params.arguments.source" } },
          { "action": { "name": "write" },
            "resource": { "type": "storage_object", "id": "$params.arguments.destination" } }
        ]
      }
    }
  }
}
Figure 7: Declared mapping with multiple evaluations

For a tools/call to copy_object with arguments {"source": "/bucket/reports/q1.pdf", "destination": "/bucket/archive/q1.pdf"}, the resulting Access Evaluations request, sent to the Access Evaluations API, is:

{
  "subject": { "type": "identity", "id": "alice@example.com" },
  "context": { "agent": "http://agentprovider.com/agent-app-id" },
  "evaluations": [
    { "action": { "name": "read" },
      "resource": { "type": "storage_object", "id": "/bucket/reports/q1.pdf" } },
    { "action": { "name": "write" },
      "resource": { "type": "storage_object", "id": "/bucket/archive/q1.pdf" } }
  ]
}
Figure 8: Resulting Access Evaluations request (multiple evaluations)

8.3. Conditional Expression Example

CEL conditionals and built-ins MAY be used in any expression. A transfer_funds tool derives values from arguments and token claims, including a subject.type that varies with the caller's roles. The mapping sets subject.id to $token.sub, which the PEP verifies against the token (Section 8). Because the declared subject.type is untrusted, a policy that grants elevated access MUST do so on the basis of the verified subject.id, not the declared subject.type:

{
  "name": "transfer_funds",
  "description": "Transfer funds between accounts",
  "inputSchema": {
    "type": "object",
    "properties": {
      "from_account": { "type": "string" },
      "to_account":   { "type": "string" },
      "amount":       { "type": "number" },
      "currency":     { "type": "string" }
    },
    "required": ["from_account", "to_account", "amount", "currency"],
    "x-authzen-mapping": {
      "evaluation": {
        "subject": {
          "type": "$token.roles.exists(r, r == 'treasury') ? 'treasury_user' : 'standard_user'",
          "id": "$token.sub"
        },
        "action": { "name": "$params.arguments.currency == 'USD' ? 'domestic_transfer' : 'international_transfer'" },
        "resource": {
          "type": "account",
          "id": "$params.arguments.from_account",
          "properties": {
            "sensitivity": "$params.arguments.amount > 10000 ? 'high' : 'standard'"
          }
        },
        "context": { "agent": "$token.?client_id", "target_account": "$params.arguments.to_account" }
      }
    }
  }
}
Figure 9: Declared mapping using CEL conditional expressions

9. PEP Behavior

When an MCP message is processed, the PEP MUST:

  1. Determine the JSON-RPC method. If it is a pass-through operation (Section 7), allow it without calling the PDP.

  2. Select the applicable mapping: for a tools/call whose tool declares an x-authzen-mapping, use the declared mapping; otherwise use the default mapping for the method. A method that is neither in the pass-through set nor has a mapping MUST be denied (Section 7).

  3. Populate params from the request's params object and token from the decoded, validated access token claims.

  4. Resolve the mapping: literals verbatim, $-prefixed values by evaluating the CEL expression (Section 4).

  5. Anchor the subject identity: where a mapping sets subject.id to the subject-identity claim (Section 3.1) — as every default mapping does — the effective subject.id of every decision in the constructed request — the request's subject under the evaluation envelope; the top-level subject and, after any override, the subject of each evaluations entry under the evaluations envelope — MUST equal the value of that claim in the validated access token, and the PEP MUST treat a mismatch as a mapping error (Section 10.1) and not call the PDP. Where a declared mapping instead overrides subject.id from another source (Section 8), the PEP cannot perform this verification and SHOULD emit a warning. A declared mapping MUST NOT carry a per-evaluation subject; if one is present, treat the request as a mapping error and do not call the PDP.

  6. Construct the AuthZEN request from the resolved mapping and send it to the API named by the mapping's envelope (Section 5): the Access Evaluation API for evaluation, the Access Evaluations API for evaluations.

  7. Enforce the response: if every decision is true (permit), allow the message to proceed; if any decision is false (deny), do not allow it and return a JSON-RPC error (Section 10.2).

10. Error Handling

This binding reports the COAZ denial and error categories ([COAZFW]) as JSON-RPC 2.0 [JSONRPC] error responses. In every case the PEP MUST NOT allow the message to proceed. As required by [JSONRPC], the id of each error response MUST equal the id of the request that caused it, or be null if the request id could not be determined.

10.1. Mapping Errors

A mapping error occurs when the PEP cannot construct a valid AuthZEN request — for example, an expression references a missing field, an expression fails to evaluate, or the mapping is malformed. The PEP MUST return error code -32602 (Invalid params). The message SHOULD describe the failure.

{
  "jsonrpc": "2.0",
  "id": 456,
  "error": {
    "code": -32602,
    "message": "COAZ mapping error: expression '$params.arguments.region' failed: no such key 'region'"
  }
}
Figure 10: JSON-RPC error response for a mapping failure

10.2. Authorization Denial

A denial is the normal course of policy enforcement, not a fault; it is conveyed as a JSON-RPC error response because that is the channel [JSONRPC] provides for refusing a request. When one or more decisions are deny, the PEP MUST return an error response with code -32001. This code is drawn from the JSON-RPC 2.0 [JSONRPC] range reserved for implementation-defined server errors (-32000 to -32099); a code outside that range, such as -32401, is non-conformant with [JSONRPC] and MUST NOT be used. The message MAY be populated from an implementation- defined reason conveyed in the decision context of the [AUTHZEN] response; note that [AUTHZEN] does not define a standard key for this, so the PEP MUST NOT assume a specific field name.

{
  "jsonrpc": "2.0",
  "id": 789,
  "error": {
    "code": -32001,
    "message": "Access denied: insufficient permissions for customer record"
  }
}
Figure 11: JSON-RPC error response for an authorization denial

Implementations MAY alternatively surface an authorization denial as a tool result with isError set to true, where the MCP message and deployment make that distinction meaningful; the choice between a protocol-level error and a tool-level error result is left to the implementation.

10.3. PDP Communication Errors

If the PEP cannot reach the PDP or receives an invalid response, it MUST return error code -32603 (Internal error).

{
  "jsonrpc": "2.0",
  "id": 101,
  "error": { "code": -32603, "message": "Authorization service unavailable" }
}
Figure 12: JSON-RPC error response for a PDP communication failure

11. Security Considerations

11.1. Token Integrity

The access token referenced by the token input variable MUST be validated by the PEP before its claims are used. The PEP MUST verify the token signature, issuer, audience, and expiration in accordance with [RFC7519] and the OAuth framework in use. Because default mappings for server-scoped operations use the aud claim as the resource identifier, correct audience validation and resource-indicator binding [RFC8707] are essential.

11.2. Zero-Trust for AI Agents

This binding represents the human user as the AuthZEN Subject and the AI agent as part of the Context. This separation lets policies evaluate the trust level of the user and the agent independently, supporting zero-trust architectures for AI agent interactions.

11.3. Subject Identity

The subject identifier (subject.id) SHOULD be anchored to the subject-identity claim of the validated access token. Every default mapping sets it to $token.sub, and where a declared mapping sets it to the subject-identity claim the PEP verifies that the resolved value equals the subject of the validated access token, rejecting any mismatch (Section 8). This verification is essential where the PEP is an MCP gateway and the declared mapping is authored by the MCP server: without it, the server — the party being authorized — could assert the identity of any principal and obtain a decision for a user the caller never authenticated as.

This binding uses SHOULD rather than MUST so that agentic deployments whose acting-user identity is not carried in a verifiable token claim can still map a subject.id (Section 8). This flexibility is a deliberate trade-off: a subject.id that the PEP cannot verify against a token claim is only as trustworthy as the mapping author and the source it is drawn from. Deployments SHOULD prefer token-anchored subject identities, SHOULD surface a warning when a mapping overrides subject.id from an unverifiable source, and MUST weigh, in their threat model, the trust placed in whoever authored such a mapping. Where the PEP is a gateway enforcing a mapping authored by a less-trusted MCP server, an unverifiable subject.id SHOULD NOT be relied upon as an authenticated identity.

11.4. Untrusted Declared-Mapping Attributes

A declared mapping is supplied by the MCP server, which is the party whose operation is being authorized. Every attribute it produces — action, resource, context, and all subject attributes other than the verified subject.id, including subject.type — is untrusted input to the PDP. PDP policies MUST NOT treat an attribute that originates from a declared mapping as authoritative for identity or privilege; for example, a policy MUST NOT grant access on the basis of a subject.type or context attribute that the mapping could freely set. The trustworthy authorization inputs are the verified subject.id and any attributes the PDP itself obtains from trusted sources.

11.5. Mapping Integrity

A declared mapping is supplied by the MCP server. A PEP SHOULD validate that a declared mapping is well-formed and that its expressions reference only defined properties, and MUST verify the trust-anchored subject.id (Section 8), before relying on it. The trust placed in the server as the author of declared mappings MUST be considered in the deployment's threat model.

11.6. Fail-Closed Enforcement

As required by the framework, the PEP MUST fail closed: a mapping error, a denial, or a PDP communication failure all result in the message being refused.

11.7. Transport Security

All communication between the PEP and the PDP MUST use TLS, as specified in the transport requirements of [AUTHZEN].

11.8. Deployment Coverage

Implementers SHOULD ensure that at least one COAZ-aware PEP in the deployment path authorizes each in-scope MCP message. If no PEP processes the mapping, no AuthZEN authorization occurs and access control falls back to other mechanisms.

12. IANA Considerations

This specification has no IANA actions.

13. References

13.1. Normative References

[AUTHZEN]
Gazitt, O., Brossard, D., and A. Tulshibagwale, "Authorization API 1.0", , <https://openid.net/specs/authorization-api-1_0.html>.
[CEL]
Google, "Common Expression Language", , <https://cel.dev/>.
[COAZFW]
Olivier, A. and A. Tulshibagwale, "COAZ: A Framework for Mapping Information Models to AuthZEN Authorization Requests", , <https://openid.net/specs/authzen-coaz-framework-1_0.html>.
[JSONRPC]
"JSON-RPC 2.0 Specification", , <https://www.jsonrpc.org/specification>.
[MCP]
Anthropic, "Model Context Protocol Specification", , <https://spec.modelcontextprotocol.io/>.
[RFC2119]
Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/rfc/rfc2119>.
[RFC7519]
Jones, M., Bradley, J., and N. Sakimura, "JSON Web Token (JWT)", RFC 7519, DOI 10.17487/RFC7519, , <https://www.rfc-editor.org/rfc/rfc7519>.
[RFC8174]
Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/rfc/rfc8174>.

13.2. Informative References

[OAUTH21]
"The OAuth 2.1 Authorization Framework", , <https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-12>.
[RFC8707]
Campbell, B., Bradley, J., and H. Tschofenig, "Resource Indicators for OAuth 2.0", RFC 8707, DOI 10.17487/RFC8707, , <https://www.rfc-editor.org/rfc/rfc8707>.

Appendix A. Relationship to Other Specifications

A.1. COAZ Framework

This specification is a binding of the COAZ Framework [COAZFW]. The framework defines the protocol-neutral model — mappings shaped as AuthZEN Access Evaluations requests, the literal/expression distinction, the expression contract, and the conformance requirements. This binding binds that model to MCP's information model and JSON-RPC transport.

A.2. OpenID AuthZEN Authorization API

The constructed request and the decision response are defined by [AUTHZEN]. This binding uses the Access Evaluation API for single-decision mappings — including every default mapping — and the Access Evaluations API where a declared mapping requires multiple decisions (Section 5).

A.3. Model Context Protocol

This binding extends the MCP [MCP] tool schema with the x-authzen-mapping extension to inputSchema. It is backward compatible: servers and clients that do not understand it ignore the field, and the default mappings still allow a PEP to authorize their messages.

A.4. OAuth 2.1

This binding complements OAuth 2.1 [OAUTH21]. OAuth provides authentication and coarse-grained authorization via scopes; this binding enables fine-grained, parameter-level decisions that consider the specific resources and context of each message.

Appendix B. Acknowledgements

The authors would like to thank Martin Besozzi for the original proposal on AuthZEN integration for fine-grained authorization in MCP, and the members of the OpenID AuthZEN Working Group for their ongoing work on the Authorization API standard.

Appendix C. Notices

Copyright (c) 2025 The OpenID Foundation.

The OpenID Foundation (OIDF) grants to any Contributor, developer, implementer, or other interested party a non-exclusive, royalty free, worldwide copyright license to reproduce, prepare derivative works from, distribute, perform and display, this Implementers Draft, Final Specification, or Final Specification Incorporating Errata Corrections solely for the purposes of (i) developing specifications, and (ii) implementing Implementers Drafts, Final Specifications, and Final Specification Incorporating Errata Corrections based on such documents, provided that attribution be made to the OIDF as the source of the material, but that such attribution does not indicate an endorsement by the OIDF.

The technology described in this specification was made available from contributions from various sources, including members of the OpenID Foundation and others. Although the OpenID Foundation has taken steps to help ensure that the technology is available for distribution, it takes no position regarding the validity or scope of any intellectual property or other rights that might be claimed to pertain to the implementation or use of the technology described in this specification or the extent to which any license under such rights might or might not be available; neither does it represent that it has made any independent effort to identify any such rights. The OpenID Foundation and the contributors to this specification make no (and hereby expressly disclaim any) warranties (express, implied, or otherwise), including implied warranties of merchantability, non-infringement, fitness for a particular purpose, or title, related to this specification, and the entire risk as to implementing this specification is assumed by the implementer. The OpenID Intellectual Property Rights policy (found at openid.net) requires contributors to offer a patent promise not to assert certain patent claims against other contributors and against implementers. OpenID invites any interested party to bring to its attention any copyrights, patents, patent applications, or other proprietary rights that may cover technology that may be required to practice this specification.

Authors' Addresses

Atul Tulshibagwale
CrowdStrike
Alex Olivier
Cerbos