CEL vocabulary¶
An XAccessPolicy rule can authorize requests with a CEL expression by
setting authorization.type: CEL and authorization.cel.expression.
The API does not standardize the variables and functions available to CEL expressions; each implementation documents its own (see the Dynamic Authorization proposal). This page describes what the reference implementation supports. Expressions written for it may not work with other implementations.
How CEL rules are evaluated¶
The controller translates each CEL rule into an Envoy RBAC policy, and Envoy evaluates the
expression at request time. A CEL rule only applies to MCP tools/call requests: it matches
a tools/call request from the rule's source when the expression evaluates to true. If the
expression evaluates to false, to a non-boolean value, or to an error (for example, reading a
header the request does not have), the rule does not match.
Because a CEL rule never matches other requests, including the MCP initialize and
tools/list calls an agent makes before it can call a tool, a policy with action Allow
usually needs a second, Inline rule that allows the base MCP protocol methods:
apiVersion: agentic.networking.x-k8s.io/v1alpha1
kind: XAccessPolicy
metadata:
name: allow-verify-tools
namespace: default
spec:
targetRefs:
- group: agentic.networking.x-k8s.io
kind: XBackend
name: my-backend
action: Allow
rules:
- name: allow-verify-tools
source:
type: ServiceAccount
serviceAccount:
name: my-agent
authorization:
type: CEL
cel:
expression: "request.mcp.tool_name.startsWith('verify_')"
- name: allow-base-mcp
source:
type: ServiceAccount
serviceAccount:
name: my-agent
authorization:
type: Inline
mcp:
mcpBaseProtocolMethodsOption: MATCH_BASE_PROTOCOL_METHODS
Variables¶
| Variable | Type | Description |
|---|---|---|
request.mcp.tool_name |
string |
Name of the tool being called. |
request.path |
string |
Request path, including the query string. |
request.url_path |
string |
Request path without the query string. |
request.method |
string |
Request method, for example POST. |
request.host |
string |
Host portion of the URL, taken from the :authority or Host header. |
request.headers |
map<string, string> |
Request headers, keyed by lower-case header name. Repeated headers are joined with a comma. |
request.time |
timestamp |
Time the first byte of the request was received. |
All of these except request.mcp.tool_name are Envoy
request attributes
and behave as Envoy documents them. request.mcp.tool_name is specific to this implementation:
before the expression is sent to Envoy, the controller replaces it with
metadata.filter_metadata['mcp_proxy'].params.name, the tool name that the MCP filter extracts
from the tools/call request.
Reading a key that is not in request.headers is an error, so check that an optional header is
present first, for example 'x-user' in request.headers && request.headers['x-user'] == 'alice'.
The controller rejects expressions that reference any other request attribute, such as
request.query or request.scheme. Top-level variables such as source or identity are not
defined, so expressions that use them fail to compile.
Functions¶
Use the functions and macros from the
CEL standard definitions,
for example startsWith, endsWith, contains, matches, size, in and has.
Envoy's CEL evaluator does not support the following, even though the controller accepts expressions that use them. Do not use them in policies:
- macros that iterate over a list or map:
all,exists,exists_one,mapandfilter - string and list concatenation with
+ - conversion to a string with
string() - the cel-go string extension functions, such as
lowerAscii,splitandreplace
Validation errors¶
The controller compiles every CEL expression when an XAccessPolicy is created or updated. If
an expression references an unsupported variable or does not compile, the policy is rejected
for all of its targetRefs: the Accepted condition of each ancestor in its status is set to
False with reason InvalidCEL, and the message contains the error, for example:
Failed to compile CEL expression "request.foo == 'bar'": unsupported CEL variable: request.foo
A policy that is not accepted is not applied until the expression is fixed.