CEL expressions

Agentgateway uses the CEL CEL (Common Expression Language) A simple expression language used throughout agentgateway to enable flexible configuration. CEL expressions can access request context, JWT claims, and other variables to make dynamic decisions. expression language throughout the project to enable flexibility. CEL allows writing simple expressions based on the request context that evaluate to some result.

Example expressions

Review the following examples of expressions for different uses cases.

Default function

You can use the default function to provide a fallback value if the expression cannot be resolved.

Request header fallback with example of an anonymous user:

default(request.headers["x-user-id"], "anonymous")

Nested object fallback with example of light theme:

default(json(request.body).user.preferences.theme, "light")

JWT JWT (JSON Web Token) A compact, URL-safe token format used for securely transmitting information between parties. JWTs are commonly used for authentication and authorization in agentgateway. claim fallback with example of default “user” role:

default(jwt.role, "user")

Logs, traces, and observability

# Include logs where there was no response or there was an error
filter: |
   !has(response) || response.code > 299
fields:
  add:
    user.agent: 'request.headers["user-agent"]'
    # A static string. Note the expression is a string, and it returns a string, hence the double quotes.
    span.name: '"openai.chat"'
    gen_ai.usage.prompt_tokens: 'llm.input_tokens'
    # Parse the JSON request body, and conditionally log...
    # * If `type` is sum, val1+val2
    # * Else, val3
    # Example JSON request: `{"type":"sum","val1":2,"val2":3,"val4":"hello"}`
    json.field: |
      json(request.body).with(b,
         b.type == "sum" ?
           b.val1 + b.val2 :
           b.val3
      )

Authorization

mcpAuthorization:
  rules:
  # Allow anyone to call 'echo'
  - 'mcp.tool.name == "echo"'
  # Only the test-user can call 'add'
  - 'jwt.sub == "test-user" && mcp.tool.name == "add"'
  # Any authenticated user with the claim `nested.key == value` can access 'printEnv'
  - 'mcp.tool.name == "printEnv" && jwt.nested.key == "value"'

Rate limiting

remoteRateLimit:
   descriptors:
   # Rate limit requests based on a header, whether the user is authenticated, and a static value (used to match a specific rate limit rule on the rate limit server)
   - entries:
      - key: some-static-value
        value: '"something"'
      - key: organization
        value: 'request.headers["x-organization"]'
      - key: authenticated
        value: 'has(jwt.sub)'

CEL in YAML

When writing CEL expressions in agentgateway, typically they are expressed as YAML string values, which can cause confusion. Any string literals within the expression need to be escaped with additional quotes.

These examples all represent the same CEL expression, a string literal "hello".

doubleQuote: "'hello'"
doubleQuoteEscaped: "\"hello\""
singleQuote: '"hello"'
blockSingle: |
  'hello'
blockDouble: |
  "hello"

These examples all represent the same CEL expression, a variable request:

doubleQuote: "request"
singleQuote: 'request'
block: |
  request

The block style is often the most readable for complex expressions, and also allows for multi-line expressions without needing to escape quotes.

Context reference

When using CEL expressions, a variety of variables and functions are made available.

Variables

Variables are only available when they exist in the current context. Previously in version 0.11 or earlier, variables like jwt were always present but could be null. Now, to check if a JWT claim exists, use the expression has(jwt.sub). This expression returns false if there is no JWT, rather than always returning true.

Additionally, fields are populated only if they are referenced in a CEL expression. This way, agentgateway avoids expensive buffering of request bodies if no CEL expression depends on the body.

Each policy execution consistently gets the current view of the request and response. For example, during logging, any manipulations from earlier policies (such as transformations or external processing) are observable in the CEL context.

Table of variables

FieldDescription
requestrequest contains attributes about the incoming HTTP request
request.methodThe HTTP method of the request. For example, GET
request.uriThe complete URI of the request. For example, http://example.com/path.
request.hostThe hostname of the request. For example, example.com.
request.schemeThe scheme of the request. For example, https.
request.pathThe path of the request URI. For example, /path.
request.versionThe version of the request. For example, HTTP/1.1.
request.headersThe headers of the request.
request.bodyThe body of the request. Warning: accessing the body will cause the body to be buffered.
request.startTimeThe time the request started
request.endTimeThe time the request completed
responseresponse contains attributes about the HTTP response
response.codeThe HTTP status code of the response.
response.headersThe headers of the response.
response.bodyThe body of the response. Warning: accessing the body will cause the body to be buffered.
jwtjwt contains the claims from a verified JWT token. This is only present if the JWT policy is enabled.
apiKeyapiKey contains the claims from a verified API Key. This is only present if the API Key policy is enabled.
apiKey.key
basicAuthbasicAuth contains the claims from a verified basic authentication Key. This is only present if the Basic authentication policy is enabled.
basicAuth.username
llmllm contains attributes about an LLM request or response. This is only present when using an ai backend.
llm.streamingWhether the LLM response is streamed.
llm.requestModelThe model requested for the LLM request. This may differ from the actual model used.
llm.responseModelThe model that actually served the LLM response.
llm.providerThe provider of the LLM.
llm.inputTokensThe number of tokens in the input/prompt.
llm.cachedInputTokensThe number of tokens in the input/prompt read from cache (savings)
llm.cacheCreationInputTokensTokens written to cache (costs)
Not present with OpenAI
llm.outputTokensThe number of tokens in the output/completion.
llm.reasoningTokensThe number of reasoning tokens in the output/completion.
llm.totalTokensThe total number of tokens for the request.
llm.countTokensThe number of tokens in the request, when using the token counting endpoint
These are not counted as ‘input tokens’ since they do not consume input tokens.
llm.promptThe prompt sent to the LLM. Warning: accessing this has some performance impacts for large prompts.
llm.prompt[].role
llm.prompt[].content
llm.completionThe completion from the LLM. Warning: accessing this has some performance impacts for large responses.
llm.paramsThe parameters for the LLM request.
llm.params.temperature
llm.params.top_p
llm.params.frequency_penalty
llm.params.presence_penalty
llm.params.seed
llm.params.max_tokens
llm.params.encoding_format
llm.params.dimensions
llmRequestllm_request contains the raw LLM request before processing. This is only present during LLM policies;
policies occurring after the LLM policy, such as logs, will not have this field present even for LLM requests.
sourcesource contains attributes about the source of the request.
source.addressThe IP address of the downstream connection.
source.portThe port of the downstream connection.
source.identityThe (Istio SPIFFE) identity of the downstream connection, if available.
source.identity.trustDomainThe trust domain of the identity.
source.identity.namespaceThe namespace of the identity.
source.identity.serviceAccountThe service account of the identity.
source.subjectAltNamesThe subject alt names from the downstream certificate, if available.
source.issuerThe issuer from the downstream certificate, if available.
source.subjectThe subject from the downstream certificate, if available.
source.subjectCnThe CN of the subject from the downstream certificate, if available.
mcpmcp contains attributes about the MCP request.
mcp.(any)(1)tool
mcp.(any)(1)tool.targetThe target of the resource
mcp.(any)(1)tool.nameThe name of the resource
mcp.(any)(1)prompt
mcp.(any)(1)prompt.targetThe target of the resource
mcp.(any)(1)prompt.nameThe name of the resource
mcp.(any)(1)resource
mcp.(any)(1)resource.targetThe target of the resource
mcp.(any)(1)resource.nameThe name of the resource
backendbackend contains information about the backend being used.
backend.nameThe name of the backend being used. For example, my-service or service/my-namespace/my-service:8080.
backend.typeThe type of backend. For example, ai, mcp, static, dynamic, or service.
backend.protocolThe protocol of backend. For example, http, tcp, a2a, mcp, or llm.
extauthzextauthz contains dynamic metadata from ext_authz filters
extprocextproc contains dynamic metadata from ext_proc filters

Variables by policy type

Depending on the policy, different fields are accessible based on when in the request processing they are applied.

PolicyAvailable Variables
Transformationsource, request, jwt, extauthz
Remote Rate Limitsource, request, jwt
HTTP Authorizationsource, request, jwt
External Authorizationsource, request, jwt
MCP Authorizationsource, request, jwt, mcp
Loggingsource, request, jwt, mcp, extauthz, response, llm
Tracingsource, request, jwt, mcp, extauthz, response, llm
Metricssource, request, jwt, mcp, extauthz, response, llm

Functions

The following functions can be used in all policy types.

FunctionPurpose
jsonParse a string or bytes as JSON. Example: json(request.body).some_field.
toJsonConvert a CEL value into a JSON string. Example: toJson({"hello": "world"}).
withCEL does not allow variable bindings. with allows doing this. Example: json(request.body).with(b, b.field_a + b.field_b)
variablesvariables exposes all of the variables available as a value. CEL otherwise does not allow accessing all variables without knowing them ahead of time. Warning: this automatically enables all fields to be captured.
mapValuesmapValues applies a function to all values in a map. map in CEL only applies to map keys.
mergemerge joins two maps. Example: {"a":2,"k":"v"}.merge({"a":3}) results in {"a":3,"k":"v"}.
flattenUsable only for logging and tracing. flatten will flatten a list or struct into many fields. For example, defining headers: 'flatten(request.headers)' would log many keys like headers.user-agent: "curl", etc.
flattenRecursiveUsable only for logging and tracing. Like flatten but recursively flattens multiple levels.
base64.encodeEncodes a string to a base64 string. Example: base64.encode("hello").
base64.decodeDecodes a string in base64 format. Example: string(base64.decode("aGVsbG8K")). Warning: this returns bytes, not a String. Various parts of agentgateway will display bytes in base64 format, which may appear like the function does nothing if not converted to a string.
randomGenerates a number float from 0.0-1.0
defaultResolves to a default value if the expression cannot be resolved. For example default(request.headers["missing-header"], "fallback")
regexReplaceReplace the string matching the regular expression. Example: "/id/1234/data".regexReplace("/id/[0-9]*/", "/id/{id}/") would result in the string /id/{id}/data.
failUnconditionally fail an expression.
uuidRandomly generate a UUIDv4

The following standard functions are available:

  • contains, size, has, map, filter, all, max, startsWith, endsWith, string, bytes, double, exists, exists_one, int, uint, matches.
  • Duration/time functions: duration, timestamp, getFullYear, getMonth, getDayOfYear, getDayOfMonth, getDate, getDayOfWeek, getHours, getMinutes, getSeconds, getMilliseconds.
  • From the strings extension: charAt, indexOf, join, lastIndexOf, lowerAscii, upperAscii, trim, replace, split, substring, stripPrefix, stripSuffix.
  • From the Kubernetes IP extension: isIP("..."), ip("..."), ip("...").family(), ip("...").isUnspecified(), ip("...").isLoopback(), ip("...").isLinkLocalMulticast(), ip("...").isLinkLocalUnicast(), ip("...").isGlobalUnicast().
  • From the Kubernetes CIDR extension: cidr("...").containsIP("..."), cidr("...").containsIP(ip("...")), cidr("...").containsCIDR(cidr("...")), cidr("...").ip(), cidr("...").masked(), cidr("...").prefixLength().
Agentgateway assistant

Ask me anything about agentgateway configuration, features, or usage.

Note: AI-generated content might contain errors; please verify and test all returned information.

Tip: one topic per conversation gives the best results. Use the + button in the chat header to start a new conversation.

Switching topics? Starting a new conversation improves accuracy.
↑↓ navigate select esc dismiss

What could be improved?

Your feedback helps us improve assistant answers and identify docs gaps we should fix.

Need more help? Join us on Discord: https://discord.gg/y9efgEmppm

Want to use your own agent? Add the Solo MCP server to query our docs directly. Get started here: https://search.solo.io/.