Skip to content

Replace JSONPath with CEL in COAZ MCP profile#466

Open
alexolivier wants to merge 4 commits intoopenid:mainfrom
alexolivier:coaz-cel
Open

Replace JSONPath with CEL in COAZ MCP profile#466
alexolivier wants to merge 4 commits intoopenid:mainfrom
alexolivier:coaz-cel

Conversation

@alexolivier
Copy link
Copy Markdown
Collaborator

@alexolivier alexolivier commented Apr 2, 2026

This PR replaces JSONPath with Common Expression Language (CEL) as the expression language for mapping MCP tool call parameters to the AuthZen SARC model in the COAZ MCP profile.

Motivation

CEL is a widely adopted, well-specified expression language with implementations in Go, Java, C++, Rust, and other languages. It provides a more expressive and standardized alternative to JSONPath for evaluating mapping expressions, including support for complex return types (maps, lists), type checking, and well-defined error semantics.

Changes

Expression language: JSONPath → CEL

  • Replaced all JSONPath references ($properties['id'], $token['sub'], etc.) with CEL expressions (params.arguments.id, token.sub, etc.)
  • Renamed the "Mapping Variables" section to "Mapping Expressions" with new subsections defining CEL input variables, output semantics, and expression syntax

CEL input variables

Two input variables are provided to every CEL expression:

Variable Source Description
params tools/call JSON-RPC params object Includes params.name (tool name) and params.arguments.* (caller-supplied values)
token Decoded JWT access token claims e.g., token.sub, token.client_id

CEL output

CEL expressions may return scalars (string, number, boolean), lists, or maps — enabling richer AuthZen request construction.

New non-normative example (§4.2.3)

Added an example showing a complete tools/call JSON-RPC request and decoded token, with a resolution table mapping each CEL expression to its resolved value.

Expanded error handling (§6)

Restructured into three subsections aligned with MCP's protocol error model:

Error JSON-RPC Code When
COAZ Mapping Errors -32602 (Invalid Params) CEL evaluation failure, missing fields, malformed mapping, mismatched array counts
Authorization Denial -32401 (Unauthorized) AuthZen PDP returns deny decision
PDP Communication Errors -32603 (Internal Error) PDP unreachable or returns invalid response

Each error type includes a non-normative example response.

- Replace JSONPath expressions with Common Expression Language (CEL)
  for mapping tool call parameters to AuthZen SARC model
- Define CEL input variables: `params` (from tools/call JSON-RPC
  request) and `token` (decoded JWT claims), and CEL output semantics
- Add non-normative example showing tools/call message to CEL
  variable resolution
- Expand error handling with three categories: COAZ mapping errors
  (-32602), authorization denial (-32401), and PDP communication
  errors (-32603), aligned with MCP's protocol error model
- Fix incomplete JSON examples that caused kramdown-rfc parse warnings
- Add CEL as normative reference, remove unused RFC6901 (JSON Pointer)
- Add Alex Olivier (Cerbos) as co-author

Signed-off-by: Alex Olivier <alex@alexolivier.me>
Copy link
Copy Markdown
Collaborator

@baboulebou baboulebou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you possibly also provide an additional, more complex and non-normative, example of what a CEL coaz mapping would look like, maybe using CEL conditions?

Copy link
Copy Markdown
Collaborator

@baboulebou baboulebou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also a question...

If the MCP server is coaz enabled, but the external PEP (Gtwy) isn't, then the PEP will just ignore the mapping; OK. But in that case the MCP Tool is expected to enforce Access Control itself.

-> So how can we signal the MCP Tool/Resource that it must enforce access itself in that case?
-> We could say the Tool/Resource must enforce access in all cases. But if the external PEP (Gtwy) is coaz enabled, AND approved the request, then we'd get duplicate evaluations, an extra unnecessary latency hit...

Signed-off-by: Alex Olivier <alex@alexolivier.me>
@alexolivier
Copy link
Copy Markdown
Collaborator Author

alexolivier commented Apr 2, 2026

Could you possibly also provide an additional, more complex and non-normative, example of what a CEL coaz mapping would look like, maybe using CEL conditions?

Added as well as the string literal issue raised on todays call.

re those scenarious

The x-coaz-mapping is declarative metadata that only the PEP layer consumes. The tool itself never touches it. The architecture diagram in Section 2.3 already covers this: arrows 3 and 5 are alternatives, and the spec calls out that having both evaluate would be redundant. We maybe should remove the gateway layer in the example flow - its optional.

So the scenarios shake out like this:

  • Gateway is COAZ-enabled: It reads the mapping, constructs the AuthZen request, gets a decision, forwards or rejects. The tool runs with zero authorization logic. One evaluation.
  • Gateway is NOT COAZ-enabled: It ignores x-coaz-mapping entirely (it's just an extension field in tool defintion). If the MCP server itself acts as the PEP, it can pick up the mapping. If neither does, no COAZ authorization happens. The tool runs under whatever else is in place (OAuth scopes, etc).

The spec doesn't require the tool to enforce access as a fallback. I see that as outside COAZ's scope. COAZ defines the contract between a tool definition and a COAZ-aware PEP, wherever that PEP sits.

If the concern is about guaranteeing that at least one PEP evaluates it, I think that's a deployment/configuration concern as @tulshi pointed out today rather than something the spec should mandate.

"name": "get_weather",
"coaz": true,
"title": "Weather Information Provider",
... more response fields.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why remove this line?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Swapped the ... more response fields placeholder for a full example with description and inputSchema filled in to be referenced in the mapping. Felt like the abbreviated version left too much to the reader's imagination. Happy to trim it back if you think it's too verbose

### CEL Output {#cel-output}

Each CEL expression MUST evaluate to a value. The value MAY be a scalar
(string, number, or boolean), a list, or a map. The resulting value is
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A list can be converted easily into an array, but how does one convert a map to the array input of the evaluations API? Should we specify how to convert it?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each CEL expression targets a single field in a single evaluation mapping. So if you need multiple AuthZen requests via evaluations, you'd define separate mappings for each one. The CEL expression itself never needs to produce the full array; it's always scoped to one field of one request.

I can add a clarifying note to make that clearer if it'd help?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it'll be good to be unambiguous about how a list or map translates to various AuthZen calls.

@tulshi
Copy link
Copy Markdown
Collaborator

tulshi commented Apr 3, 2026

If the concern is about guaranteeing that at least one PEP evaluates it, I think that's a deployment/configuration concern as @tulshi pointed out today rather than something the spec should mandate.

We could include it in a non-normative "Security Considerations" section.

@tulshi
Copy link
Copy Markdown
Collaborator

tulshi commented Apr 3, 2026

@alexolivier I like how in your initial example of CEL you have the "tools/call" part spelled out. I think we should do the same for the single-valued and multi-valued examples.

<p id="section-4.2.3-4">And an access token with the following decoded claims:<a href="#section-4.2.3-4" class="pilcrow">¶</a></p>
<span id="name-example-decoded-access-toke"></span><div id="fig-token-claims">
<figure id="figure-4">
<div class="lang-json sourcecode" id="section-4.2.3-5.1">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another important claim in the case of MCP is the aud, since the agent or MCP host uses the OAuth resource parameter to identify the MCP server, for example, https://domain/mcp
Perhaps we could include this in the example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants