SecureAuth AI Gateway
Guides

Policies

Define security policies that control which tools agents can use

Policies are the security rules that govern how agents interact with tools through the gateway. They control which tools are available, to whom, and under what conditions. Without any rules, all requests are denied by default.

Policy rules table showing evaluation order, effects, and tool patterns
Rules are evaluated top-to-bottom — the first matching rule wins

Evaluation model

When an agent makes a tool call, the gateway evaluates rules in priority order (top-to-bottom). The first matching rule wins — its effect is applied and no further rules are checked. If no rule matches, the request is denied.

  • Only active rules are evaluated. Draft and disabled rules are skipped.
  • Rules with invalid conditions are skipped (fail-closed) — the system never accidentally grants access due to a malformed expression.

Each rule has one of two effects:

  • Allow — permit the tool call to proceed
  • Deny — block the tool call

The effect is only applied when all of the rule's scope filters and tool patterns match the request, and any condition evaluates to true.

Rule lifecycle

Rules carry a status to support safe rollout:

  • Draft — saved but not enforced; useful for testing before going live
  • Active — the rule is enforced during evaluation
  • Disabled — the rule is temporarily turned off without being deleted, useful for debugging or incident response

You can change a rule's status at any time by editing it.

Creating a rule

  1. Navigate to Policies in the dashboard
  2. Click Add Rule
  3. Enter a name (for example, "Allow read-only tools")
  4. Set the effect — Allow to permit or Deny to block matching requests
  5. Set the status — start with Draft to test first, switch to Active to enforce
  6. Add one or more tool patterns (see below)
  7. Click Create Rule

New rules are added at the bottom of the list. Drag rows to reorder them.

New Policy Rule dialog with name, effect, status, and tool patterns
The rule editor with effect, status, tool patterns, and optional MCP/agent scope

Scope filters

By default, a rule matches all MCP servers, all agents, and all agent instances. To narrow scope, click the + MCP, + Agent, or + Agent Instance chips in the rule editor:

  • MCP server — restrict the rule to tools from a specific MCP server
  • Agent — restrict to a specific agent (for example, claude-code or cursor); applies to every instance of that agent, current and future
  • Agent instance — restrict to a single connected client

All scopes are AND-ed — every filter must match for the rule to apply. Filters that are not set act as wildcards.

Tool patterns

Type patterns into the tool patterns input and press Enter or comma to add them. A rule matches a tool if any of its patterns match (OR logic).

PatternMatches
*All tools
send_*Tools starting with send_
*_messageTools ending with _message
*_send_*Tools containing _send_
read_fileOnly the exact tool read_file

When an MCP server scope is set, autocomplete filters to show only tools from that server.

Conditions

Click Add condition to attach a CEL expression to a rule. The expression must return true for the rule to apply; if it errors at runtime, the rule is skipped.

The inline editor supports syntax highlighting and autocompletion. For more complex expressions, the full CEL Workspace provides:

  • Samples — pre-built expressions you can insert as a starting point
  • Fields — a reference panel listing all available context fields and operators
  • Summary — a human-readable description shown in the policy table (for example, "Only Claude Code agents on the filesystem server")
CEL Workspace with expression editor, samples, fields, and test cases
The CEL Workspace provides an expression editor with validation, sample expressions, and test cases

Available fields

FieldDescription
agent.id, agent.name, agent.slugAgent identity and type
user.id, user.name, user.emailUser identity
user.groups (list<string>)Groups the user is a member of
mcp.id, mcp.name, mcp.slugMCP server identity
request.args.<field>A field from the live tool-call arguments — fields and types are inferred from each matched tool's schema. See Argument-level conditions.

Operators

== != in && || .startsWith() .endsWith() .contains() .matches() has()

Examples

ExpressionDescription
agent.slug == "claude-code"Match Claude Code agents
mcp.slug == "filesystem"Match the filesystem MCP server
agent.slug == "claude-code" && mcp.slug == "github"Claude Code agents on GitHub only
agent.slug in ["claude-code", "cursor"]Match multiple agents
agent.name.startsWith("prod-")Agents with names starting with prod-
user.name != "service-account"Exclude a specific user
"admin" in user.groupsMatch users in the admin OIDC group
has(agent.slug)Only match when an agent slug is set

Testing conditions

The CEL Workspace includes a Test Cases panel on the right:

  • Test cases are auto-generated from your connected agents and MCP servers
  • Click Run All to evaluate your expression against every test case
  • Each case shows MATCH, NO MATCH, or ERROR
  • Click a case to expand and edit its JSON context, or click + Add to create custom cases

Test conditions before activating a rule to verify they work as expected.

Argument-level conditions

agent, user, and mcp decide who can call which tool. The request.args variable goes one step further — it decides what payload the agent is allowed to send. Every field on every matched tool's input schema becomes part of the policy language, evaluated on each live tool call.

This turns the gateway from a tool-level firewall into a per-call authorization layer. The decision isn't just which tool the agent is allowed to call — it's whether the agent can call this tool with this channel, in this thread, from this user. Without writing custom integration code, you can enforce things like:

  • Slack channel allowlistmcp.slug == "slack" && request.args.channel_id in ["C_GENERAL", "C_DEVREL"]
  • Pin agents to your orgrequest.args.owner == "acme-corp"
  • Protect main branch!has(request.args.ref) || request.args.ref != "refs/heads/main"
  • Sandbox filesystem accessrequest.args.path.startsWith("/workspace/")
  • Block destructive SQL!request.args.query.matches("(?i)\\bdrop\\b")
  • Keep email internalrequest.args.to.endsWith("@acme.com")
  • Jira project allowlistrequest.args.project_key in ["ENG", "INFRA"]
  • Combine with agent identityagent.slug == "claude-code" && request.args.channel_id == "C_GENERAL"

A condition that references a missing field — for example, request.args.owner on a tool whose schema has no owner — evaluates to an error. Allow rules with an erroring condition fall through to the next rule (often default-deny); deny rules with an erroring condition still deny, fail-closed. Combine with has() (has(request.args.owner) && request.args.owner == "acme-corp") when the field is optional on some tools.

Authoring with autocomplete

The CEL editor reads the input schemas of every tool matched by the rule's scope and surfaces those fields as you type request.args.. The dropdown is more than a list:

  • Coverage chips on each field show how many of the matched tools define it. A green in all chip means safe to reference unconditionally; an amber 3 / 7 chip means the field is partial and you should guard it with has(). A red chip means the field is rare across the scoped tools.
  • Per-MCP info panel groups occurrences by MCP server, with the description from each tool's schema and the tools that define the field. When the same field name means different things on different MCPs, this is how you spot it.
  • CEL string methods (.startsWith(), .endsWith(), .contains(), .matches(), .size()) autocomplete on string-typed leaves, so common shapes like prefix and regex checks are one keystroke away.

Narrow the rule's MCP and tool-pattern scope before writing the condition. The tighter the scope, the more focused the autocomplete and the more meaningful the coverage chips.

CEL editor with request.args. autocomplete dropdown showing fields, coverage chips, and a per-MCP info panel
`request.args.` autocompletes from each scoped tool's input schema — with coverage chips and per-MCP grouping in the info panel

How it's evaluated

Argument-level conditions evaluate at tool-call time, not at discovery time. That means:

  • At discovery time (when an agent connects and lists its tools), a tool that's conditionally allowed shows up to the agent — agents see the tool exists, but each call is still gated. The audit log records these as conditionally_allowed_tools on the agent.connected event so you can see exactly which tools are gated for each connection.
  • At call time, the gateway evaluates the condition against the actual arguments the agent sent. If the condition is false, the rule is skipped (next rule wins). If the condition errors (for example, references a field the tool doesn't have), behavior is fail-closed — allow rules are skipped, deny rules still deny. If no rule allows the call, it is denied.

Impact preview

Click the Impact button in the top-right of the rule editor to open the preview panel:

  • Tools — how many tools match your patterns, with a scrollable list
  • Agents — how many agents are affected
  • Overlap — warnings when existing rules already cover the same tools, with the rule name and its effect

The preview updates in real time as you change tool patterns and scope filters.

Rule editor with Impact Preview panel showing matching tools, agents, and overlapping rules
The Impact Preview shows which tools and agents are affected, plus any overlapping rules

Rule order

Rules are evaluated top-to-bottom, so order matters. Drag rows in the table to reorder.

  • Put more specific rules above more general ones
  • A rule at position 1 is checked first — if it matches, all later rules are ignored
  • Example: a deny rule for delete_* at position 1 blocks deletes even if an allow-all rule exists below

Common patterns

Allow-then-restrict

Start with an allow-all rule at the bottom, then add deny rules above it for specific tools or servers.

  • Position 1: Denydelete_*, drop_* on postgres-prod
  • Position 2: Allow*

Deny-then-allow

Rely on the default deny (no catch-all rule). Add targeted allow rules for specific agents and tools.

  • Position 1: Allowlist_*, get_*, search_* for all agents
  • Position 2: Allow* scoped to agent claude-code on GitHub

Read-only access

  • Tool patterns: list_*, get_*, search_*, read_*, describe_*
  • Permits browsing and querying while blocking writes and deletes

Agent-specific access

Scope rules to grant different access levels by agent:

  • Position 1: Allow * scoped to agent claude-code on GitHub (full access for Claude Code)
  • Position 2: Allow list_issues, list_pull_requests on GitHub (read-only for everyone else)

Argument-level guardrails

Allow a powerful tool, but only with arguments you trust. The rule above the open allow blocks the dangerous shapes; the rule below lets everything else through.

  • Position 1: Denycreate_pull_request on GitHub when request.args.base == "main" (no agent writes to main)
  • Position 2: Denysend_message on Slack when !(request.args.channel_id in ["C_GENERAL", "C_DEVREL"]) (channel allowlist)
  • Position 3: Allow* for the agents that need broad access

See Argument-level conditions for the full menu.

On this page