Skip to content

feat(plan): opt-in trust for MCP readOnlyHint via general.plan.trustReadOnlyHint#27156

Open
emersonbusson wants to merge 1 commit into
google-gemini:mainfrom
emersonbusson:feat/plan-mode-silent-reads
Open

feat(plan): opt-in trust for MCP readOnlyHint via general.plan.trustReadOnlyHint#27156
emersonbusson wants to merge 1 commit into
google-gemini:mainfrom
emersonbusson:feat/plan-mode-silent-reads

Conversation

@emersonbusson
Copy link
Copy Markdown
Contributor

@emersonbusson emersonbusson commented May 16, 2026

Summary

Adds an opt-in setting (general.plan.trustReadOnlyHint, default false) that lets users allow MCP tools declared with readOnlyHint = true to run silently in Plan Mode, instead of prompting on every call.

Default behavior is unchanged — Plan Mode keeps the secure-by-default ASK_USER for MCP read-only-annotated tools. The user opts in only for MCP servers they fully trust.

Why this revision

The first revision of this PR (initial commit) flipped the plan.toml rule unconditionally from ASK_USER to ALLOW. @gemini-code-assist[bot] correctly flagged this as a security bypass: a malicious MCP server could declare readOnlyHint: true on a write-tool and silently execute in Plan Mode.

This revision keeps plan.toml unchanged and gives users explicit, per-installation control via a setting. The setting is documented as "Enable only for MCP servers you fully trust" so the trust decision is informed.

How it works

When general.plan.trustReadOnlyHint = true, a dynamic rule is injected at policy-engine construction time with priority TRUST_READ_ONLY_HINT_PRIORITY = USER_POLICY_TIER + 0.5 = 4.5. That priority:

  • above plan.toml's 1.05 ASK_USER rule → overrides it to ALLOW
  • below MCP_EXCLUDED_PRIORITY (4.9) → admin/security server-blocks still win
  • below EXCLUDE_TOOLS_FLAG_PRIORITY (4.4) → wait, actually higher. Let me re-check.

Priorities used (see packages/core/src/policy/config.ts):

MCP_EXCLUDED_PRIORITY            4.9
TRUST_READ_ONLY_HINT_PRIORITY    4.5   ← new
EXCLUDE_TOOLS_FLAG_PRIORITY      4.4
ALLOWED_TOOLS_FLAG_PRIORITY      4.3
TRUSTED_MCP_SERVER_PRIORITY      4.2
ALLOWED_MCP_SERVER_PRIORITY      4.1

So MCP_EXCLUDED_PRIORITY (admin/security excluding an MCP server) beats this setting — which is the correct ordering for fail-closed security.

Built-in read tools (read_file, glob, grep_search, list_directory, ...) already run silently in Plan Mode via the explicit allow-list in read-only.toml and are unaffected.

Files

  • packages/cli/src/config/settingsSchema.ts — new general.plan.trustReadOnlyHint boolean, default false
  • packages/cli/src/config/policy.ts — pipes the setting through to PolicySettings
  • packages/core/src/policy/types.ts — adds trustReadOnlyHintInPlanMode?: boolean to PolicySettings
  • packages/core/src/policy/config.ts — new priority constant + conditional rule injection in createPolicyEngineConfig
  • packages/core/src/policy/config.test.ts — 3 test cases (setting unset, false, true)

Related Issues

Closes #27163

Part of #25406 (Epic: Plan Mode Post-Launch Work).

How to Validate

  1. Default (setting unset): gemini --approval-mode=plan. Prompt the agent to call an MCP tool annotated readOnlyHint: true — approval dialog appears (unchanged from current behavior).
  2. Enabled: ~/.gemini/settings.json{ "general": { "plan": { "trustReadOnlyHint": true } } }. Restart and repeat — runs silently.
  3. With both trustReadOnlyHint: true AND mcp.excluded: ["my-trusted-server"] — server-block wins (server is denied entirely).
  4. Built-in read_file / glob / grep_search in Plan Mode — silent regardless of the setting (governed by read-only.toml).

Pre-Merge Checklist

  • Tests added in config.test.ts (3 cases covering the new setting)
  • No breaking changes — default is the existing ASK_USER behavior
  • Setting documented with clear "trust" framing in settingsSchema.ts
  • Priority placement keeps MCP_EXCLUDED_PRIORITY (security-critical) above the new opt-in
  • Validated on Linux (Ubuntu 24.04, Node 24.15.0)

@emersonbusson emersonbusson requested a review from a team as a code owner May 16, 2026 21:05
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request improves the user experience in Plan Mode by reducing unnecessary friction. Previously, tools marked as read-only were triggering an approval dialog on every call. By changing the policy decision from ask_user to allow for these specific tools, the agent can now perform read-only operations silently while maintaining strict gating for write operations and tools with side effects.

Highlights

  • Policy Update: Updated the Plan Mode policy to allow tools annotated with readOnlyHint = true to execute silently instead of prompting the user.
  • Test Suite Alignment: Updated unit tests in toml-loader.test.ts to reflect the new silent execution behavior for read-only tools.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request modifies the plan.toml policy to automatically allow tools with the readOnlyHint = true annotation during Plan Mode, removing the previous requirement for user confirmation. The reviewer identified a high-severity security concern, noting that granting automatic permissions based on unvalidated metadata from external MCP servers could lead to a security bypass and violates the principle of being secure by default.

readOnlyHint: true,
});
expect(annotationRule!.decision).toBe(PolicyDecision.ASK_USER);
expect(annotationRule!.decision).toBe(PolicyDecision.ALLOW);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The change from PolicyDecision.ASK_USER to PolicyDecision.ALLOW for tools with the readOnlyHint annotation in Plan Mode introduces a security bypass. Security decisions should be managed by the policy engine based on verified tool identity (tool.name), not by unvalidated hints from external MCP servers. Following a 'secure by default' methodology, the default policy for non-allowlisted tools should be DENY, and any security check should be 'fail-closed', rejecting items if metadata is missing or untrusted.

References
  1. When a core tools allowlist is configured, the default policy for non-allowlisted tools should be DENY to enforce a 'secure by default' methodology.
  2. Tool security policies are applied based on tool.name, not tool.kind.
  3. Tool availability and security restrictions are managed by the policy engine, not by functions that synchronize specific tools like syncPlanModeTools.
  4. Security checks, such as an extension allowlist, should be implemented in a 'fail-closed' manner. If an item's validity cannot be verified (e.g., due to missing metadata), it should be rejected by default.

).toBe(PolicyDecision.ASK_USER);
allowResult.decision,
'MCP tool with readOnlyHint=true should be ALLOWED silently in Plan Mode',
).toBe(PolicyDecision.ALLOW);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

This assertion confirms that MCP tools with readOnlyHint=true are allowed silently in Plan Mode. This allows a malicious MCP server to bypass Plan Mode's protection by providing false metadata. Security decisions must be fail-closed and based on the policy engine's configuration, not on unvalidated hints from untrusted external components.

References
  1. Security checks, such as an extension allowlist, should be implemented in a 'fail-closed' manner. If an item's validity cannot be verified (e.g., due to missing metadata), it should be rejected by default.

@emersonbusson
Copy link
Copy Markdown
Contributor Author

Related: #27157, #27158 — sibling Full Access UX improvements opened together. Each is independently reviewable and mergeable.

…eadOnlyHint

Adds an opt-in setting that lets users allow MCP tools annotated
`readOnlyHint = true` to run silently in Plan Mode, instead of
prompting on every call. **Default remains the secure-by-default
ASK_USER from plan.toml** — Plan Mode should not trust unvalidated
metadata from external MCP servers by default.

Addresses @gemini-code-assist[bot] feedback on the previous attempt
(google-gemini#27156 first revision) which changed plan.toml's decision
unconditionally to ALLOW. The bot correctly flagged that as a
security bypass: a malicious MCP server could declare `readOnlyHint:
true` on a write-tool and silently execute in Plan Mode. This
revision puts the user in control instead.

When `general.plan.trustReadOnlyHint = true`, a dynamic rule with
priority `TRUST_READ_ONLY_HINT_PRIORITY = USER_POLICY_TIER + 0.5 = 4.5`
is injected at policy-engine construction time. That priority is
comfortably above plan.toml's 1.05 ASK_USER rule (overriding it to
ALLOW) and below `MCP_EXCLUDED_PRIORITY` (4.9), so admin/security
server-blocks still win.

Built-in read tools (read_file, glob, grep_search, list_directory,
...) already run silently in Plan Mode via the explicit allow-list
in read-only.toml — they are unaffected by this setting.

Files:
  - packages/cli/src/config/settingsSchema.ts: new
    `general.plan.trustReadOnlyHint` boolean (default false)
  - packages/cli/src/config/policy.ts: pipes the setting through
    PolicySettings
  - packages/core/src/policy/types.ts: adds
    `trustReadOnlyHintInPlanMode?: boolean` to PolicySettings
  - packages/core/src/policy/config.ts: new
    `TRUST_READ_ONLY_HINT_PRIORITY` constant; conditional rule
    injection in createPolicyEngineConfig
  - packages/core/src/policy/config.test.ts: 3 cases (unset, false,
    true) covering rule shape and priority
@emersonbusson emersonbusson force-pushed the feat/plan-mode-silent-reads branch from 50ee0e2 to 927b172 Compare May 16, 2026 22:37
@emersonbusson emersonbusson changed the title feat(core): allow read-only tools silently in plan mode feat(plan): opt-in trust for MCP readOnlyHint via general.plan.trustReadOnlyHint May 16, 2026
@emersonbusson
Copy link
Copy Markdown
Contributor Author

Thanks @gemini-code-assist for catching this — you're right that the original commit was a security bypass.

Force-pushed 927b17202 (single clean commit) replacing the unconditional ASK_USER → ALLOW flip with an opt-in setting:

  • plan.toml is restored to its original ASK_USER rule — Plan Mode stays secure-by-default for any MCP server's unvalidated metadata.
  • New general.plan.trustReadOnlyHint setting (default false) lets users explicitly allow MCP readOnlyHint = true tools to run silently, for MCP servers they trust.
  • When enabled, a dynamic rule is injected at priority 4.5 — above plan.toml's 1.05 ASK_USER (so it overrides), and below MCP_EXCLUDED_PRIORITY = 4.9 (so admin/security server-blocks still win).
  • Built-in read tools (read_file/glob/grep_search/list_directory) are unaffected — they remain silent via read-only.toml's explicit allow-list.

Updated the title to reflect the new design (feat(plan): opt-in trust for MCP readOnlyHint…). Body has the full rationale, priority table, and validation steps.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Thanks for the update, @emersonbusson. This revised approach is much improved. By moving from an unconditional policy change to an opt-in setting (general.plan.trustReadOnlyHint), you have effectively addressed the security concerns while still providing the requested UX improvement. The priority-based rule injection (priority 4.5) correctly balances user intent with the existing security hierarchy, ensuring that admin/security server-blocks (4.9) still take precedence. The updated test suite provides good coverage for the new configuration and validates the intended behavior. This looks like a solid, secure solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/agent Issues related to Core Agent, Tools, Memory, Sub-Agents, Hooks, Agent Quality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Plan Mode opt-in to trust MCP server readOnlyHint annotations

1 participant