Skip to main content

Managing Access Policies

Permissions After approving MCPs, you control who can access them through access rules. Runlayer’s policy system combines fine-grained permissions with ease of use, achieving both accuracy and least privilege by default.

How Access Rules Work

Access rules grant permissions to users, groups, roles, or agent accounts for specific MCP servers. Each rule can include multiple principals of the same type — for example, several users or several groups in one rule. Applies To (Who gets access):
  • User: One or more individual employees by email/ID
  • Group: One or more teams synced from your SSO (Engineering, Marketing, etc.)
  • Role: One or more job functions or custom roles
  • Agent Account: One or more machine-to-machine identities (see Agent Accounts)
  • Attributes: Match users or agent accounts by identity attributes (e.g., department, environment). User attributes sync from your IdP; agent account attributes are admin-managed. Attribute rules are subject-type-agnostic — assigning an attribute to an agent account makes it inherit every existing rule scoped to that attribute
  • Everyone: Wildcard principal that matches all subjects, including users and agent accounts (typically used in global deny policies)
Access Scope (What they can access):
  • Entire Server: Full access to all tools in the MCP
  • Specific Tools & Resources: Fine-grained selection of individual tools
Conditions (Advanced - Optional):
  • Add runtime rules based on tool arguments (e.g., restrict SQL queries to specific tables)
  • Enforce network restrictions (e.g., corporate IP ranges only)
  • Validate OAuth session properties (e.g., require verified accounts)
  • Block specific MCP clients
This design ensures you can easily grant appropriate access while maintaining security through least privilege.

How rules combine

Access rules aren’t an ordered list like firewall rules. Runlayer doesn’t read them top to bottom or stop at the first match:
  • No access by default. Apart from a connector’s creator and holders of the connector-management capability, who skip the policy check, a user or agent account reaches a connector or tool only when at least one allow rule matches them.
  • Allows add up. Every matching allow rule counts, so a subject gets everything any of them grants. A broad rule, for example Everyone on the Entire Server, gives everyone full access, and narrower allow rules on the same connector stop restricting anyone.
  • Deny wins. A matching deny rule, server-level or global, blocks the request even when an allow rule also matches.
To give one group less access than everyone else, don’t add a narrow allow next to a broad one. Scope the broad allow to the groups that need it, or add a deny rule for the tools that group shouldn’t use. See Evaluation for the full rules.
Policy conditions evaluate runtime context — tool arguments (payload.*), request metadata like IP and client, OAuth session state, tool annotations, and session history (meta.session.*). They do not perform semantic analysis of what the user is trying to accomplish. For intent-level monitoring — detecting when an agent’s behavior diverges from the user’s intent across a session — see AgentGuard.

How Runlayer Enforces Least Privilege

Policies are one of several enforcement layers that work together. A request must pass all of them:
  1. Platform roles (FGA) — control who can use platform features at all, like creating policies or managing connectors. See Roles & Permissions.
  2. Access policies (PBAC) — server-level allow/deny rules and global deny rules, evaluated by the proxy on every tool invocation (this page).
  3. Identity-scoped tokens — Agent Accounts authenticate with their own credentials; M2M tokens enforce agent-account policies, and OBO tokens enforce the intersection of the agent’s and the delegating user’s policies.
  4. Runtime conditions — argument, network, OAuth, annotation, and session-history checks applied at invocation time.
Example — support agent using Slack: an admin’s Super Admin role lets them create the rule; a server-level rule grants the agent account access to only list_channels, read_messages, and search_messages on the Slack MCP; the agent’s OBO token limits it to what the delegating user is also allowed; and a global deny with meta.tool.annotations.destructiveHint EQUALS true backstops everything. See also Agents for scoping agent workloads.

Policy Types

Runlayer supports allow and deny permission policies, plus approval rules that layer on top. Agent configuration uses those same permissions for defaults, exceptions and hard blocks:

Agent configuration policies

In the agent configuration beta, Agent defaults allows settings across the workspace. Removing a setting makes it off by default; an explicit allow can enable it for selected agents or owners. A deny blocks it even when an allow exists. Only policy admins can write these rules. Configuration actions use the config: prefix. An ordinary * grant, including an owner’s or super admin’s grant, does not cover them. config:* explicitly allows every setting; model and connector lists allow only the entries they name.

Server-Level Policies

Manage access to specific MCP servers. Navigate to Connectors → [Server Name] → Policies tab.
  • Scope: One or more specific servers
  • Action: Allow or Deny
  • Use for: Granting access, fine-grained tool restrictions
  • Example: Allow Finance group to use database query tools

Global Policies

Organization-wide restrictions that apply everywhere. Navigate to Settings → Policies.
  • Scope: All servers, tools, and resources (*)
  • Action: Deny only
  • Use for: IP blocks, client restrictions, compliance requirements
  • Example: Block all access from outside corporate network
Use global policies for organization-wide security controls, and server-level policies for granting and managing access to specific resources.

Human approval rules

Human approval rules are in beta. Contact your Runlayer account team to enable them.
Approval rules are a separate kind of policy. They do not grant access — they add a human checkpoint on top of access a permission policy already granted. A caller still needs an allow policy to reach a tool; the approval rule decides whether a human has to sign off before the call runs.
  • Scope: All connectors, or specific ones
  • Action: Requires approval
  • Use for: Sensitive tools that should stay available but never run unattended
  • Example: Require approval before any tool annotated as destructive runs
Create them in Settings → Policies → Add approval rule, or per connector in Connectors → [Server Name] → Policies → Add approval rule. Conditions are optional: a rule for Everyone with no conditions requires approval for every tool call in its scope, which is the broadest form. Quick-start templates cover the common cases (destructive tools, external network access, untrusted MCP clients). An approval rule decides whether a call needs sign-off, not who signs off. The person the call runs for approves it themselves; you can’t route requests to an admin, a group or a backup approver yet. See Who approves. Approval rules match on tool calls only. Unlike permission policies they cannot be narrowed to specific resources, because the approval prompt is raised from the tool-call path. For what happens once a rule matches — the run stopping at the call and the requester being asked to decide — see human-in-the-loop approvals.

Unattended agents

Attended clients (Cursor, Claude Desktop, interactive Claude Code) get an approval URL or an in-client card and wait. A run that nobody is watching cannot act on either, so Runlayer treats it differently:
  • No URL prompt or in-client card. The approver gets a Slack DM instead.
  • The tool result is a structured blocked payload (see the contract below), not free text.
  • The payload states the facts and the rules, and leaves the next step to the run’s own instructions: wait and retry, or end the turn and report the request_id so an orchestrator can resume once a decision lands.
How a run becomes unattended. Any one of these is enough:
  • The request carries the header x-runlayer-unattended: true. The value must be exactly true (case-insensitive); any other value counts as attended.
  • The client is Claude Code -p or the Claude Agent SDK. These are detected automatically from their User-Agent; no header needed.
  • The call uses an external agent-account token, standalone or on-behalf-of. Runlayer-hosted agent runs use agent-account tokens too but keep their own approval flow.
Sending the header.
In the Claude Code snippet an unset variable sends false, which is treated as attended. SDK examples add the header next to x-runlayer-api-key in the same headers map shown in Agent frameworks. Payload contract. The tool result is an error result whose single text item is this JSON object. The field names are the stable contract; the message and guidance prose may change at any time, so never match on it. “Pending only” fields appear when code is approval_required. Denied and unavailable results carry just code, message, default_decision and retryable. Resuming after a decision. Runlayer matches the retry against the original request by session id, client, and arguments, so resume the same session rather than starting a new one. The session is the x-runlayer-session-id header; send a UUID (any other value is canonicalised to one), and the payload echoes it as session_id next to request_id:
  1. One pattern for orchestrated runs: a hook spots code == "approval_required", records request_id and the session id, and ends the run.
  2. Wait for the decision: an agent runtime webhook, the Slack DM, the webapp Inbox, or poll GET /api/v1/approvals with the approver’s own user API key until the request leaves the pending list. Agent-account and on-behalf-of tokens cannot call this endpoint. The list only shows pending requests waiting on that user and does not say whether one was approved, denied, or expired; the retry result tells you (success, or code approval_denied).
  3. Resume the same session and retry the identical call:
Windows. A pending request lives 24 hours. An approval lets the identical call through once within 1 hour. A denial blocks identical retries for 15 minutes. Who approves. The requester; for on-behalf-of agent tokens, the delegating user. They need a linked Slack account, otherwise delivery is url_only and someone must open the link. Standalone agent-account (machine-to-machine) calls cannot be approved today; use on-behalf-of tokens for approval-gated tools.

Agent runtime webhook

Runlayer can POST the approval events of unattended runs to an HTTPS endpoint you run, so your runtime manager resumes a session as soon as someone decides instead of polling. It is a workspace-wide setting, reached from any rule that requires approval: open the rule in Settings → Policies and follow the Approval settings link under Approvers (requires Manage security policies). There you set a public HTTPS URL and optional custom headers, for example your receiver’s API key. The signing secret is shown once, when you add the endpoint or regenerate the secret. Regenerating has no overlap: the old secret stops verifying at once and queued retries are signed with the new one, so update your receiver right away. Send test posts a signed webhook.test event and shows your receiver’s status code. Who owns the endpoint. The endpoint, its signing secret and its custom headers belong to the workspace, not to the admin who saved them. Deactivating or deleting that admin leaves the endpoint live: Runlayer keeps posting events, with the stored headers, to the configured URL, and the secret they were shown still verifies. Deleting the workspace deletes the endpoint. When an admin with Manage security policies leaves, regenerate the secret or delete the endpoint, and rotate any receiver key in the custom headers. The organization-settings-updated audit event records who saved or changed it. Events. approval.requested when an unattended call is held, approval.approved and approval.denied when the approver decides. Attended calls send nothing. A call made with an external agent-account token, standalone or on behalf of a user, counts as unattended even without the header, so it sends events too. Runlayer-hosted agent runs keep their own approval flow and send none.
status is pending, approved or denied; decided_at is null on approval.requested. session_id is the session from x-runlayer-session-id (canonical form), or null. server_id is the connector’s ID, the same one as in its /servers/<id> URL, or null. expires_at is when the pending request lapses or, once decided, when the approval’s retry window or the denial’s block ends. Ignore fields you don’t know: more may be added. Verifying. Requests follow Standard Webhooks: webhook-id (the event id), webhook-timestamp and webhook-signature (v1,<base64 HMAC-SHA256> of <webhook-id>.<webhook-timestamp>.<body>, keyed with your whsec_ secret), plus Content-Type: application/json and your custom headers, which cannot replace these. Verify with any Standard Webhooks library and reject timestamps older than 5 minutes. Delivery. A 2xx response is success. Timeouts, connection errors, 408, 429 and 5xx are retried after about 1, 5, 15 and 60 minutes (5 attempts); other 4xx responses are not retried. Redirects (3xx) are not followed and not retried. Delivery is at-least-once and unordered: retries reuse the same webhook-id. approval.approved and approval.denied are retried on the full schedule even if the grant or denial window has already lapsed. approval.requested is delivered only while the request is still pending, so a late retry of a request that was already decided is dropped. Dedupe on webhook-id and reconcile on data.status and occurred_at. There is no expiry event: run a timer from expires_at. If every attempt fails, resume the session anyway: the retried call returns the decision.

Unified Policies

Connector rules are one instance of a broader model. A unified policy grants or denies named actions on a resource to a principal. The resource can be a connector, agent, skill, plugin, artifact, agent account, team, or the organization itself. Sharing an agent, giving a role an admin capability, and letting a group call a connector tool are all the same shape. Unified policies on non-connector resources become available as the unified policy engine rolls out to your workspace. Until then a unified body for one of those resources answers 409 with code unified_policy_engine_inactive for an admin with the security-policies capability, and for a resource owner or anyone holding share on the resource once your workspace’s policy data has been migrated to the unified store. Before that migration the owner door described below is closed and everyone but the admin gets 403. Connector-shaped bodies are unaffected.

Request shapes

POST /api/v1/policies accepts two body shapes. The connector shape is unchanged and keeps working. Both use the RUNLAYER_URL and API_KEY conventions from the agent account cookbook.
principal accepts user, group, role, agent, attributes, or any (shown as Everyone in the UI). action is allow or deny. scope names servers, tools, and resources (* for all).

Resource types and actions

Every resource type except org, policy and agent_account carries the shared actions view, edit, share, manage_access, delete, and transfer_ownership. Each type adds its own. An agent account is not shareable and carries only view, use, edit, edit_security, edit_permissions, rotate_credentials and delete. Its use is reserved and grants or denies nothing yet. edit_security covers the account’s server scoping, DPoP, strict mode and login redirect URIs, token and delegation lifetimes, and re-enabling it, on top of edit (identity attributes need the agent account administrator capability, whatever the grants); rotate_credentials issues a new client secret. A grant of edit alone reaches neither. * matches every action on the type. Actions are flat, so granting manage_access does not imply view. The policy type is reserved. The org actions are manage_admins, view_user_info, manage_org_settings, manage_identity_providers, manage_mdm_config, view_audit_logs_own, view_audit_logs_all, view_unredacted_content, manage_audit_redaction, view_metrics_own, view_metrics_all, view_ai_outputs_all, view_agent_universe, export_usage_data, manage_security_policies, view_shadow_mcp, view_catalog, manage_catalog, access_deploy, use_management_api, manage_policies, create_connector, create_agent, create_skill, create_plugin, create_artifact, create_agent_account, create_team. The vocabulary also lists five synthetic org actions, use_assigned_connectors, use_teams, override_connector_access, use_procedures and manage_procedures. They mirror legacy role capabilities and grant nothing on their own.

Principals

These are the only spellings the API accepts, in either shape or in the unified shape alone as marked. agent_account, all: true and attrs are the engine’s internal forms and answer 422 on the wire, as does "ids": ["*"] for now. Responses echo the spellings above, except that a derived every-user or every-agent row reads back as "ids": ["*"].

Evaluation

  • Default deny. A subject holds an action on a resource only when a matching allow policy exists.
  • Any matching allow grants.
  • An explicit deny overrides every allow the engine evaluates, the owner’s and any share included. Checks that run before the engine are not subject to it. A role capability such as Manage security policies is read from the role itself, and a connector’s creator or a holder of the connector-management capability reaches the connector without a policy check. “Everyone except group X” is an org allow plus a group deny. A deny also beats public visibility, so a skill, plugin or agent shared with the whole organization stays closed to a denied subject.
  • Enforcement is live on every open, run, call and download. List pages do not yet subtract deny policies, so a public resource you have been denied can still appear in a list; opening it answers 403.

Presets

Viewer, Editor, and Manager are named preset bundles. They expand to raw actions and the engine only sees actions. The share dialog offers Viewer and Manager, its two permission levels; Editor is a policy builder and API bundle. delete and transfer_ownership are never in a preset. Admins hold them. A resource’s owner holds every action on that resource. Connectors are the exception: the owner template grants view only, and connector access comes from connector policies.

Who can write policies

  • Admins with the security-policies capability write any policy.
  • The owner of a skill, plugin, agent, artifact or team, or anyone granted share on it, creates grants on exactly that resource. A grant is bounded by what its author holds, so the owner can grant any action on the resource and everyone else only the actions they hold. Connectors are the exception again. The owner template grants view alone, so connector access is granted through connector policies by admins with the security-policies capability, or by someone an admin has explicitly granted share on the connector, within what they hold. An agent account is not shareable, so only admins with the security-policies capability write unified policies on it.
  • Anyone granted manage_access on a resource edits or removes others’ grants on it.
  • An agent account’s connector policy (a connector-shape allow naming only agent accounts, on one connector, without conditions) can also be written by agent account administrators and, under the unified engine, by anyone holding edit_permissions on every account it names, the account’s owner included. Unless they administer the connector or created it, the tools are limited to the ones they can use themselves ("*" is saved as that list). Writing one for an account and connector that already have one updates that policy’s tools; a policy also covering other accounts or connectors answers 409. The same callers can change the tools of, or delete, a policy naming one of their accounts on one connector. See Agent accounts.
Two guardrails apply. System-seeded role and owner-template policies are read-only. A write that would leave the organization with no administrator able to manage policies is rejected.

Creating Access Rules

1

Navigate to MCP Policies

Go to the MCP you want to grant access to → Policies tab
2

Click 'New Access Rule'

Opens the access rule creation dialog
3

Select Who Gets Access

Choose a principal type (User, Group, Role, Agent Account, or Attributes), then select one or more from the dropdown. A single rule can include multiple principals of the same type.
4

Define Access Scope

Choose between:
  • Entire Server: Grant access to all tools (faster, simpler)
  • Specific Tools & Resources: Select individual tools (more secure, granular)
5

Add Rule

Click “Add Rule” to save - takes effect immediately

Access Rule Examples

Team-Wide Server Access

Scenario: Give engineering and devops teams access to the GitHub MCP
When to use:
  • Multiple teams need comprehensive access
  • Server tools are all appropriate for the groups
  • Simplicity is preferred over granularity

Fine-Grained Tool Access

Scenario: Give finance analysts read-only access to the Database MCP
When to use:
  • Sensitive MCPs require least privilege
  • Only specific tools are needed
  • Different roles need different capabilities
This is how admins approve or deny individual tools inside an already-approved connector: connector approval decides whether the server is available at all, then server-level rules with Specific Tools scope (allow or deny) control each tool, enforced at invocation time and effective immediately. For a human checkpoint on specific tool calls during agent runs, see human-in-the-loop approvals.

Annotation-Based Access (Read-Only Tools)

Scenario: Give data analysts access to all tools, but only allow read-only ones at invocation time
When to use:
  • Restrict access based on tool capabilities (read-only, destructive, etc.)
  • Enforce safety boundaries at invocation time
  • Works across any server with annotated tools

Individual User Access

Scenario: Give a contractor temporary access to specific Slack tools
When to use:
  • Individual exceptions needed
  • Temporary access for specific users
  • Contractors or external collaborators
  • Testing new MCPs with a single user first

Conditional Access by Tool Arguments

Scenario: Allow finance team to query only sales and finance tables
When to use:
  • Database access needs table-level restrictions
  • File systems need path-based access control
  • APIs need parameter validation
  • Sensitive data requires fine-grained control

Global Policy: Block External IPs

Scenario: Require all users to access MCPs from corporate network
When to use:
  • Compliance requires network-based access control
  • Remote work must use VPN
  • Sensitive data needs IP restrictions
  • Organization-wide security policy

Restrict Gmail to Internal Recipients Only

Scenario: Prevent outgoing Gmail messages to external recipients, allowing only emails sent to your internal domain (e.g., @runlayer.com)
You can extend this policy to also check payload.cc and payload.bcc fields using additional condition rules to ensure internal-only communication across all recipient types.
When to use:
  • Prevent data exfiltration via email in AI agent workflows
  • Enforce internal-only communication for sandbox or development environments
  • Provide guardrails for agents with Gmail access
  • Meet compliance requirements for regulated data
  • Test new AI workflows safely before allowing external communication

Block Tools That Accept Sensitive Parameters

Scenario: Prevent any tool that requires a password parameter from being invoked
When to use:
  • Prevent credential-handling tools from running without approval
  • Block tools with dangerous parameter shapes (e.g., sql, command)
  • Works with any meta.tool.input_schema.* subfield (e.g., type, required)
input_schema is the tool’s JSON Schema object. Use dot-path subfields like meta.tool.input_schema.required with the list_contains operator, or meta.tool.input_schema.type with equals.

Block Destructive Tools Organization-Wide

Scenario: Prevent all users from invoking tools that may delete or overwrite data
When to use:
  • Enforce safety boundaries across the organization
  • Protect production data from accidental deletion
  • Compliance requirements for data preservation
  • Gradual rollout (start with deny-all, grant exceptions)
Where annotations come from: readOnlyHint, destructiveHint, idempotentHint, and openWorldHint are standard MCP tool annotations declared by the MCP server author in each tool’s definition. Runlayer reads them from the server’s tool listing and exposes them as meta.tool.annotations.* at invocation time. An annotation-based policy only matches tools whose server actually declares the annotation — for unannotated or untrusted servers, combine with name-based conditions (e.g., meta.tool.name REGEX "(?i).*(delete|remove|drop).*"; a condition regex must match the whole name, so delete|remove|drop alone only matches a tool named exactly delete) or explicit Specific Tools scoping.

Dynamic Value References ($meta.* / $payload.*)

Condition values can reference runtime context dynamically by prefixing with $. This enables policies that compare one context field against another — for example, comparing a tool argument against values recorded earlier in the session. Supported prefixes:
  • $payload.<field> — Resolves to the current tool call’s argument value
  • $meta.<path> — Resolves to any meta.* context field
A reference is the whole value. Nothing is filled into a longer string: $meta.server.id:tool:create_issue looks for a field with that literal name and never matches. Behavior when a reference cannot be resolved:
  • If the referenced path doesn’t exist (e.g., the tool argument isn’t present), the rule fails (does not match) — even for negated operators like not_equals or list_not_contains. This prevents accidental allows when data is missing.
Example:
This rule matches when the current repo argument has not been used earlier in the session.

Session Payload Tracking

Runlayer can track which distinct tool argument values an agent uses during a session. Combined with deny policies and dynamic value references, this enables session isolation — once an agent accesses a resource (e.g., a specific repository), it cannot access different ones for the rest of the session. How it works:
  1. A deny policy references $payload.<field> in its value — this tells Runlayer to track that field
  2. On each allowed tool call, the argument’s value is recorded for the current session
  3. On subsequent calls, meta.session.payload_values_used.<field> contains all previously-used distinct values
  4. The deny policy evaluates against that list to block divergent values
Tracking is limited to the argument names your active policies reference, and records distinct values only (no counts or ordering).

Repository Isolation (Need-to-Know)

Scenario: Once an agent accesses a repository, block all other repositories for the rest of the session. Prevents context leakage across repos in shared agent sessions.
How the two rules work together: Both rules must match (AND) for the deny to activate. On the first call, the second rule fails (no previous values), so the call is allowed and the repo is recorded. On subsequent calls with the same repo, the first rule fails (it IS in the list), so the deny doesn’t activate. On calls with a different repo, both rules match and the call is denied.
For Runlayer agents, each run is its own session: every connector call in a run carries the run’s session ID, and continuing a conversation keeps the session of the run it continues. A new run starts with no repository recorded, so one agent can work on a different repository in each run while every single run stays on one.
Edit the field name (repo) to match your MCP server’s argument name — common alternatives include repository, project, or owner/repo. The same pattern works for any field: database, account_id, workspace, etc.

Limit bulk actions and runaway agents

Scenario: Stop an assistant or agent from sending a message to many Slack channels or people, or from deleting Jira issues in a loop, while normal use keeps working.
Policies don’t count calls. Today there’s no per-tool, per-user or per-agent rate limit you can set, no cap of “N actions, then ask a human”, and no limit on how many items one bulk call touches. The only rate limit is a platform abuse ceiling of 600 MCP requests a minute per credential (see Rate Limiting). Use the rules below to make each risky call need a person, or to keep it inside a known set.
Require a person for every destructive call. Approval allows one retry of that exact call, so an agent in a delete loop needs a new approval for each issue. The Atlassian connector sends deletes through executeDestructive, which declares destructiveHint.
Drop the meta.actor.id rules to cover people too. Approval rules are in beta. An agent account calling with no user behind it (M2M) has no one to approve, so its call is refused. Keep Slack messages inside known destinations. These rules read the channel_id argument of slack_send_message and slack_schedule_message (Slack connector), so scope them to those two tools. A DM is a channel_id that starts with D, U or W.
One destination per session. This Deny rule lets an agent message one channel or person per session and blocks the next new one, like repository isolation:
Session rules only see calls that carry a session ID. Runlayer agents send one on every call (each run is one session). Calls without one, for example Claude or ChatGPT connected straight to a connector URL, never match, so use the approval or destination rules for those clients. Use a Deny rule here: approval rules don’t record argument values. Stop it while it runs. Stop the session, cancel the run, or turn off the agent account, as in Respond to a compromised agent. Agent run limits and cost budgets only reject new runs. Close other paths. Rules apply only to calls that go through Runlayer. Native connectors that Claude or ChatGPT host in their own cloud skip them; restrict those at the vendor and in the client, as in Lock down other paths.

Condition Field Reference

Every field you can use in a condition, with sample values. Fields marked invoke-only are evaluated at tool-invocation time and are not available during tool discovery (listing).

Tool Arguments (payload.*)

Server-scoped payload conditions cannot be used with connectors that have Skip Tool Input Validation enabled (global wildcard policies can still use them).

Request Context (meta.request.*)

Subject Attributes (meta.subject.*)

Acting Agent (meta.actor.*)

Set only when an Agent Account is making the call. For OBO (on-behalf-of) tokens, meta.subject.* stays the delegating user while meta.actor.* is the agent — so one rule can constrain both identities (e.g., require meta.actor.attributes.environment = production AND meta.subject.attributes.department = Finance). For non-agent subjects these fields are absent: positive operators (equals, contains, …) never match, but negative operators treat the absent value as satisfied — a deny with meta.actor.attributes.environment not_equals production fires for every plain-user call too. Pair negative meta.actor.* rules with meta.subject.type = agent to scope them to agent traffic.

Client Provenance (meta.client.*)

How the MCP client obtained its Runlayer credentials. Set only for connector OAuth clients; absent for dashboard sessions, API keys and agent-account tokens (same absent-value semantics as meta.actor.*). The per-connector DCR / CIMD switches govern direct connections only. A self-registered client can still reach the connector through a plugin’s execute_tool. To apply the same restriction on every path, write a deny on the connector with meta.client.kind regex dcr|cimd. Pre-registered clients and agent accounts are unaffected.

Access Path (meta.plugin.*)

Whether the connector is reached directly or through an aggregate endpoint — a plugin (execute_tool), the Runlayer Plugin, or an agent account’s aggregate endpoint. Absent when the connector is called directly.
Absent satisfies every negated operator, so a deny on meta.plugin.id not_equals <A> also fires for direct calls, and one on meta.client.kind not_equals pre_registered fires for dashboard and agent traffic too. Prefer positive regex matches, or AND with a regex .+ presence check.

Server Attributes (meta.server.*)

OAuth Context (meta.oauth.*)

Tool Context (meta.tool.*) — invoke-only

Session Context (meta.session.*) — invoke-only

Resource Context (meta.resource.*)

Values starting with $ are dynamic value references: $meta.<path> and $payload.<field> resolve against the current call at evaluation time, so one rule can compare session history against whatever server or argument the call targets.
Common recipes:
  • Block access from outside the corporate network: meta.request.ip not_ip_range 10.0.0.0/8
  • Only allow read-only tools: meta.tool.annotations.readOnlyHint equals true
  • Restrict a connector to one Claude client: meta.request.user_agent contains claude
  • Read-only from one client, read-write from the others: see Plugins and security
  • Deny tools that accept credentials: meta.tool.input_schema.required list_contains password
  • Pin an agent session to a single repo: see Repository Isolation

Available Operators

Conditions use operators in snake_case. All operators are listed below:
String operators (contains, begins_with, ends_with, and their negations) accept a single string or a list of strings. With a list, positive operators match if any pattern matches; negated operators match only if none do.
Negated operators (not_* and list_not_*) evaluate to true when the field is missing or unavailable, following the principle that an absent value cannot satisfy the positive condition.
The regex operators (regex, not_regex, list_regex, list_not_regex, list_any_not_regex) use RE2 syntax. RE2 is linear-time and immune to ReDoS, but does not support lookahead ((?=...)), lookbehind ((?<=...)), or backreferences (\1). Literal patterns with unsupported syntax are rejected at save time; a dynamic pattern (e.g. $payload.pattern) is only checked at evaluation time; if it can’t compile the condition is indeterminate, so a deny or approval rule built on it enforces restrictively and an allow rule does not grant — see Condition Value Matching. Existing policies that used lookahead patterns from the Quick Start templates continue to work during a transition period — they are automatically rewritten to equivalent RE2 patterns. If you have custom patterns with unsupported syntax, update them to use RE2-compatible alternatives (e.g., replace a negative lookahead deny with a list_any_not_regex complement).See Regular Expression Syntax for the full set of supported features and the differences from Python re (notably that \d, \w, \s are ASCII-only — use \p{Nd} / [\p{L}\p{N}_] for Unicode).

Condition Value Matching

When writing conditions, keep these matching rules in mind:

Best Practices

When in doubt, grant access to specific tools first. You can always expand later if needed.Why?
  • Easier to add permissions than remove them
  • Users won’t miss tools they didn’t know existed
  • Reduces attack surface if credentials are compromised
Create access rules for groups whenever possible, even if the group only has one member initially.Why?
  • Scales better as teams grow
  • Easier to audit (who’s in “Engineering” vs 50 individual rules)
  • Syncs automatically with SSO changes
  • Simplifies onboarding/offboarding
Runlayer automatically syncs groups from your identity provider (IDP) via SCIM, mirroring your org structure.What this means:
  • Groups from Okta, Azure AD, or Google Workspace sync automatically
  • New employees added to groups get access immediately
  • Employees removed from groups lose access instantly
  • No manual group management needed
Best practice:
  • Create access rules using your existing IDP groups
  • Engineering, Sales, Finance groups work out of the box
  • Changes in your IDP reflect in Runlayer instantly
Review access rules quarterly:
  • Who has access to sensitive MCPs?
  • Are there users who no longer need access?
  • Can we tighten tool permissions?
Why?
  • Prevents scope creep
  • Maintains least privilege
  • Required for compliance
Add conditions when scope alone isn’t enough:Good use cases:
  • Restrict database queries to specific tables
  • Limit file access to certain paths
  • Validate API parameters
  • Enforce network restrictions
Available context: tool arguments (payload.*), request metadata (meta.request.*), subject identity (meta.subject.*), server and OAuth attributes, tool annotations, and session history (meta.session.*). See the Condition Field Reference for every field with sample values.Start simple: Begin without conditions, add them only when needed for sensitive data or compliance.
Apply security controls across all resources:Common use cases:
  • Block access from outside corporate network
  • Restrict access to approved clients only (e.g., allow only Cursor)
  • Require OAuth verification
  • Enforce compliance requirements
Remember:
  • Global policies can only DENY (not allow)
  • They apply to all servers, tools, and resources
  • Manage them in Settings → Policies
  • Test with small groups before applying to everyone
Combine with server policies: Use global denies for security, server-level allows for access grants.

Approvals

Review MCP requests

Security Best Practices

Security guidelines

Roles & Permissions

Admin capabilities behind org actions

Teams

Team ownership and sharing