Skip to content
agentgateway has joined the Agentic AI Foundation — Learn more

For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.

CEL in YAML and example expressions

Page as Markdown

How to embed CEL expressions in YAML configuration, plus worked examples for common use cases.

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.

Example expressions

Review the following examples of expressions for different use 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")

JWTJWT (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"'
    # Cache-inclusive and consistent across providers. For the provider's own
    # count, use 'llm.providerInputTokens' instead.
    gen_ai.usage.prompt_tokens: 'llm.inputTokens'
    # 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"'

MCP authorization rules control both which tools, prompts, and resources a client can see and which ones it can use. When a client sends a list request, such as tools/list, the rules run once for each item in the response. In each run, mcp.methodName is the list method, and the target field, such as mcp.tool, contains that item. Any item that no rule allows is removed from the list. A rule that checks only the tool, such as mcp.tool.name == "echo", therefore both lists echo and allows calls to it. To give listing and calling different access, check mcp.methodName in the rule. The following rules let every client see all tools, but let only test-user call them. Because no rule matches the prompt or resource methods, prompts and resources are removed from list responses, and requests for them, such as prompts/get and resources/read, are denied.

mcpAuthorization:
  rules:
  # Anyone can see every tool in the tools/list response
  - 'mcp.methodName == "tools/list"'
  # Only test-user can call tools
  - 'mcp.methodName == "tools/call" && jwt.sub == "test-user"'

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)'
Was this page helpful?
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/.