Plan mode: admit an MCP tool that declares readOnlyHint and is allow-listed

Status Open
Maintainer reply None cached
Activity 0 comments · opened Aug 27, 2026

Problem

In plan mode, an MCP tool that is explicitly allow-listed still prompts on every call if plan mode's classifier considers it write-capable.

Concrete symptom, MariaDB MCP server started in read-only mode (MCP_READ_ONLY=true):

  • mcp__mariadb-production-readonly__list_tables — passes silently in plan mode
  • mcp__mariadb-production-readonly__get_table_schema — passes silently
  • mcp__mariadb-production-readonly__execute_sql — prompts on every call, although mcp__mariadb-production-readonly__execute_sql is in permissions.allow in our organisation's managed settings

The server physically cannot write: with MCP_READ_ONLY=true it enforces a statement allowlist and rejects anything that is not a read. Answering a schema question while planning therefore costs one permission prompt per query, for a tool that is both incapable of writing and pre-approved by the user.

There is no workaround. Plan mode's write-capability gate is a classifier check, not a permission rule, so neither an allow entry nor a PreToolUse hook returning {"permissionDecision": "allow"} suppresses it.

Proposal

In plan mode, admit an MCP tool without a prompt when both hold:

  1. the tool declares readOnlyHint: true in its MCP ToolAnnotations, and
  2. a permissions.allow entry matches it.

Both signals already exist. readOnlyHint has been part of the MCP spec since revision 2025-03-26 (ToolAnnotations), and permissions.allow is today's config surface. No new rule kind, no planModeAllow, no new configuration to document.

The two-signal requirement is what makes this safe. ToolAnnotations carries the spec warning that clients "should never make tool use decisions based on ToolAnnotations received from untrusted servers" — the spec deliberately leaves trust establishment to the client. The user's allow entry is that trust establishment. A rogue server cannot self-grant plan-mode access by asserting readOnlyHint: true, because it still needs the user to have allow-listed the tool. Conversely, an allow entry alone stays insufficient for a tool that does not declare itself read-only, so today's behaviour is unchanged for everything else.

That is the difference from #79484, which asks for an exemption for write-capable tools and needs a new opt-in surface. This asks only that an existing declaration be consulted.

Relation to prior issues

Supply side

The annotation has to come from somewhere, so we are upstreaming it: a PR to https://github.com/MariaDB/mcp adds readOnlyHint to every tool, with readOnlyHint=self.is_read_only on execute_sql — the same flag that enforces the read-only statement allowlist. The annotation reports enforced behaviour rather than promising good behaviour.

Servers can add the annotation today. Nothing improves for any user until Claude Code consults it.

View original on GitHub ↗