docs/reference/policy-engine.md
Gemini CLI includes a powerful policy engine that provides fine-grained control over tool execution. It allows users and administrators to define rules that determine whether a tool call should be allowed, denied, or require user confirmation.
To create your first policy:
Create the policy directory if it doesn't exist:
macOS/Linux
mkdir -p ~/.gemini/policies
Windows (PowerShell)
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.gemini\policies"
Create a new policy file (for example,
~/.gemini/policies/my-rules.toml). You can use any filename ending in
.toml; all such files in this directory will be loaded and combined:
[[rule]]
toolName = "run_shell_command"
commandPrefix = "rm -rf"
decision = "deny"
priority = 100
Run a command that triggers the policy (for example, ask Gemini CLI to
rm -rf /). The tool will now be blocked automatically.
The policy engine operates on a set of rules. Each rule is a combination of conditions and a resulting decision. When a large language model wants to execute a tool, the policy engine evaluates all rules to find the highest-priority rule that matches the tool call.
A rule consists of the following main components:
allow, deny, or
ask_user).For example, this rule will ask for user confirmation before executing any git
command.
[[rule]]
toolName = "run_shell_command"
commandPrefix = "git"
decision = "ask_user"
priority = 100
Conditions are the criteria that a tool call must meet for a rule to apply. The primary conditions are the tool's name and its arguments.
The toolName in the rule must match the name of the tool being called.
*: Matches any tool (built-in or MCP).mcp_server_*: Matches any tool from a specific MCP server.mcp_*_toolName: Matches a specific tool name across all MCP servers.mcp_*: Matches any tool from any MCP server.Recommendation: While FQN wildcards are supported, the recommended approach for MCP tools is to use the
mcpNamefield in your TOML rules. See Special syntax for MCP tools.
If argsPattern is specified, the tool's arguments are converted to a stable
JSON string, which is then tested against the provided regular expression. If
the arguments don't match the pattern, the rule does not apply.
If interactive is specified, the rule will only apply if the CLI's execution
environment matches the specified boolean value:
true: The rule applies only in interactive mode.false: The rule applies only in non-interactive (headless) mode.If omitted, the rule applies to both interactive and non-interactive environments.
There are three possible decisions a rule can enforce:
allow: The tool call is executed automatically without user interaction.deny: The tool call is blocked and is not executed. For global rules (those
without an argsPattern), tools that are denied are completely excluded
from the model's memory. This means the model will not even see the tool as
an option, which is more secure and saves context window space.ask_user: The user is prompted to approve or deny the tool call. (In
non-interactive mode, this is treated as deny.)[!NOTE] The
denydecision is the recommended way to exclude tools. The legacytools.excludesetting insettings.jsonis deprecated in favor of policy rules with adenydecision.
[!WARNING] The Workspace tier (project-level policies) is currently non-functional. Defining policies in a workspace's
.gemini/policiesdirectory will not have any effect. See issue #18186. Use User or Admin policies instead.
The policy engine uses a sophisticated priority system to resolve conflicts when multiple rules match a single tool call. The core principle is simple: the rule with the highest priority wins.
To provide a clear hierarchy, policies are organized into three tiers. Each tier has a designated number that forms the base of the final priority calculation.
| Tier | Base | Description |
|---|---|---|
| Default | 1 | Built-in policies that ship with Gemini CLI. |
| Extension | 2 | Policies defined in extensions. |
| Workspace | 3 | (Currently disabled) Policies defined in the current workspace's configuration directory. |
| User | 4 | Custom policies defined by the user. |
| Admin | 5 | Policies managed by an administrator (for example, in an enterprise environment). |
Within a TOML policy file, you assign a priority value from 0 to 999. The engine transforms this into a final priority using the following formula:
final_priority = tier_base + (toml_priority / 1000)
This system guarantees that:
For example:
priority: 50 rule in a Default policy TOML becomes 1.050.priority: 10 rule in a Workspace policy TOML becomes 2.010.priority: 100 rule in a User policy TOML becomes 3.100.priority: 20 rule in an Admin policy TOML becomes 4.020.Approval modes allow the policy engine to apply different sets of rules based on
the CLI's operational mode. A rule in a TOML policy file can be associated with
one or more modes (for example, yolo, autoEdit, plan). The rule will only
be active if the CLI is running in one of its specified modes. If a rule has no
modes specified, it is always active.
default: The standard interactive mode where most write tools require
confirmation.autoEdit: Optimized for automated code editing; some write tools may be
auto-approved.plan: A strict, read-only mode for research and design. See
Customizing Plan Mode Policies.yolo: A mode where all tools are auto-approved (use with extreme caution).To maintain the integrity of Plan Mode as a safe research environment,
persistent tool approvals are context-aware. When you select "Allow for all
future sessions", the policy engine explicitly includes the current mode and
all more permissive modes in the hierarchy (plan < default < autoEdit <
yolo).
plan mode: These represent an intentional choice to trust a
tool globally. The resulting rule explicitly includes all modes (plan,
default, autoEdit, and yolo).default mode applies to default, autoEdit,
and yolo.autoEdit mode applies to autoEdit and yolo.yolo mode applies only to yolo. This ensures
that trust flows correctly to more permissive environments while maintaining
the safety of more restricted modes like plan.When a tool call is made, the engine checks it against all active rules, starting from the highest priority. The first rule that matches determines the outcome.
A rule matches a tool call if all of its conditions are met:
toolName in the TOML rule must match the name of the
tool being called.
*, mcp_server_*, or
mcp_*_toolName to match multiple tools. See Tool Name for
details.argsPattern is specified, the tool's arguments
are converted to a stable JSON string, which is then tested against the
provided regular expression. If the arguments don't match the pattern, the
rule does not apply.Policies are defined in .toml files. The CLI loads these files from Default,
User, and (if configured) Admin directories.
| Tier | Type | Location |
|---|---|---|
| User | Custom | ~/.gemini/policies/*.toml |
| Workspace | Custom | (Disabled) $WORKSPACE_ROOT/.gemini/policies/*.toml |
| Admin | System | See below (OS specific) |
Administrators can enforce system-wide policies (Tier 4) that override all user and default settings. These policies can be loaded from standard system locations or supplemental paths.
These are the default paths the CLI searches for admin policies:
| OS | Policy Directory Path |
|---|---|
| Linux | /etc/gemini-cli/policies |
| macOS | /Library/Application Support/GeminiCli/policies |
| Windows | C:\ProgramData\gemini-cli\policies |
Administrators can also specify supplemental policy paths using:
--admin-policy command-line flag.adminPolicyPaths setting in a system settings file.These supplemental policies are assigned the same Admin tier (Base 4) as policies in standard locations.
Security Guard: Supplemental admin policies are ignored if any .toml
policy files are found in the standard system location. This prevents flag-based
overrides when a central system policy has already been established.
To prevent privilege escalation, the CLI enforces strict security checks on the standard system policy directory. If checks fail, the policies in that directory are ignored.
root (UID 0) and NOT writable by group
or others (for example, chmod 755).C:\ProgramData. Standard users (Users, Everyone)
must NOT have Write, Modify, or Full Control permissions. If you see a
security warning, use the folder properties to remove write permissions for
non-admin groups. You may need to "Disable inheritance" in Advanced Security
Settings.[!NOTE] Supplemental admin policies (provided via
--admin-policyoradminPolicyPathssettings) are NOT subject to these strict ownership checks, as they are explicitly provided by the user or administrator in their current execution context.
Here is a breakdown of the fields available in a TOML policy rule:
[[rule]]
# A unique name for the tool, or an array of names.
toolName = "run_shell_command"
# (Optional) The name of a subagent. If provided, the rule only applies to tool
# calls made by this specific subagent.
subagent = "codebase_investigator"
# (Optional) The name of an MCP server. Can be combined with toolName
# to form a composite FQN internally like "mcp_mcpName_toolName".
mcpName = "my-custom-server"
# (Optional) Metadata hints provided by the tool. A rule matches if all
# key-value pairs provided here are present in the tool's annotations.
toolAnnotations = { readOnlyHint = true }
# (Optional) A regex to match against the tool's arguments.
argsPattern = '"command":"(git|npm)'
# (Optional) A string or array of strings that a shell command must start with.
# This is syntactic sugar for `toolName = "run_shell_command"` and an
# `argsPattern`.
commandPrefix = "git"
# (Optional) A regex to match against the entire shell command.
# This is also syntactic sugar for `toolName = "run_shell_command"`.
# Note: This pattern is tested against the JSON representation of the arguments
# (e.g., `{"command":"<your_command>"}`). Because it prepends `"command":"`,
# it effectively matches from the start of the command.
# Anchors like `^` or `$` apply to the full JSON string,
# so `^` should usually be avoided here.
# You cannot use commandPrefix and commandRegex in the same rule.
commandRegex = "git (commit|push)"
# The decision to take. Must be "allow", "deny", or "ask_user".
decision = "ask_user"
# The priority of the rule, from 0 to 999.
priority = 10
# (Optional) A custom message to display when a tool call is denied by this
# rule. This message is returned to the model and user,
# useful for explaining *why* it was denied.
denyMessage = "Deletion is permanent"
# (Optional) An array of approval modes where this rule is active.
# If omitted or empty, the rule applies to all modes.
modes = ["default", "autoEdit", "yolo"]
# (Optional) A boolean to restrict the rule to interactive (true) or
# non-interactive (false) environments.
# If omitted, the rule applies to both.
interactive = true
# (Optional) If true, lets shell commands use redirection operators
# (>, >>, <, <<, <<<). By default, the policy engine asks for confirmation
# when redirection is detected, even if a rule matches the command.
# This permission is granular; it only applies to the specific rule it's
# defined in. In chained commands (e.g., cmd1 > file && cmd2), each
# individual command rule must permit redirection if it's used.
allowRedirection = true
To apply the same rule to multiple tools or command prefixes, you can provide an
array of strings for the toolName and commandPrefix fields.
Example:
This single rule will apply to both the write_file and replace tools.
[[rule]]
toolName = ["write_file", "replace"]
decision = "ask_user"
priority = 10
run_shell_commandTo simplify writing policies for run_shell_command, you can use
commandPrefix or commandRegex instead of the more complex argsPattern.
commandPrefix: Matches if the command argument starts with the given
string.commandRegex: Matches if the command argument matches the given regular
expression.Example:
This rule will ask for user confirmation before executing any git command.
[[rule]]
toolName = "run_shell_command"
commandPrefix = "git"
decision = "ask_user"
priority = 100
You can create rules that target tools from Model Context Protocol (MCP) servers
using the mcpName field. This is the recommended approach for defining MCP
policies, as it is much more robust than manually writing Fully Qualified Names
(FQNs) or string wildcards.
[!WARNING] Do not use underscores (
_) in your MCP server names (for example, usemy-serverrather thanmy_server). The policy parser splits Fully Qualified Names (mcp_server_tool) on the first underscore following themcp_prefix. If your server name contains an underscore, the parser will misinterpret the server identity, which can cause wildcard rules and security policies to fail silently.
1. Targeting a specific tool on a server
Combine mcpName and toolName to target a single operation. When using
mcpName, the toolName field should strictly be the simple name of the tool
(for example, search), not the Fully Qualified Name (for example,
mcp_server_search).
# Allows the `search` tool on the `my-jira-server` MCP
[[rule]]
mcpName = "my-jira-server"
toolName = "search"
decision = "allow"
priority = 200
2. Targeting all tools on a specific server
Specify only the mcpName to apply a rule to every tool provided by that
server.
Note: This applies to all decision types (allow, deny, ask_user).
# Denies all tools from the `untrusted-server` MCP
[[rule]]
mcpName = "untrusted-server"
decision = "deny"
priority = 500
denyMessage = "This server is not trusted by the admin."
3. Targeting all MCP servers
Use mcpName = "*" to create a rule that applies to all tools from any
registered MCP server. This is useful for setting category-wide defaults.
# Ask user for any tool call from any MCP server
[[rule]]
toolName = "*"
mcpName = "*"
decision = "ask_user"
priority = 10
You can secure and govern subagents using standard policy rules by treating the
subagent's name as the toolName.
When the main agent invokes a subagent (e.g., using the unified invoke_agent
tool), the Policy Engine automatically treats the target agent_name as a
virtual tool alias for rule matching.
Example:
This rule denies access to the codebase_investigator subagent.
[[rule]]
toolName = "codebase_investigator"
decision = "deny"
priority = 500
deny_message = "Deep codebase analysis is restricted for this session."
subagent field instead. See
TOML rule schema.Gemini CLI ships with a set of default policies to provide a safe out-of-the-box experience.
read_file, glob) are generally allowed.ask_user to ensure remote agents can
prompt for confirmation, but local sub-agent actions are executed silently and
checked individually.write_file, run_shell_command) default to
ask_user.yolo mode, a high-priority rule allows all tools.autoEdit mode, rules allow certain write operations to happen without
prompting.