Skip to content

permissions: ask rules for MCP tools are silently degraded to allow #51504

Open

Description

@eltPlay

Summary

Permission rules with effect: "ask" targeting MCP-server tools are silently degraded to allow: the tool executes immediately (~400ms) with no confirmation prompt. The same rule shape did prompt twice earlier in the same session, then stopped working permanently. effect: "deny" on the same action string works correctly, so <server>_<tool> name matching is fine.

Environment

  • opencode version: 2.0.18
  • OS: Windows 11, Windows 10.0.26200.9457 (win32 x64)
  • Terminal: OpenCode desktop app (TERM=xterm-256color)
  • Shell: C:\Windows\system32\cmd.exe
  • Install/channel: release (desktop app; CLI resolved from electron-userdata\cli)
  • Active plugins: @prevalentware/opencode-goal-plugin, \publish-pipeline\plugins\handoff-on-compact

Reproduction

  1. Configure a local stdio MCP server (here: windows-mcp) and confirm its tools appear in the Code Mode catalog.

  2. Add an ask rule for one of its tools:

    { "action": "wincontrol_PowerShell", "resource": "*", "effect": "ask" }
  3. Call that tool (either via a Code Mode nested call, or as a native tool).

  4. Observe: the call executes immediately, no confirmation dialog.

Expected Behavior

A permission confirmation ("Allow once / Always allow / Reject") should be shown, and the tool call should block until the user decides.

Actual Behavior

The tool runs immediately with no prompt. Measured call latency ~400ms, i.e. no wait for user input.

Same session timeline, identical rule shape:

  • WORKED — 2 calls at 21:07 / 21:10 local: dialog appeared, user chose "Allow once".
  • FAILED — 6 calls from 21:32 onward: all executed in ~400ms, no dialog.

Additional Context

Variables tested to try to restore the prompt — none made a difference:

  • rule shape: wildcard wincontrol_* vs exact wincontrol_PowerShell
  • config hot reload vs full opencode service restart
  • codemode: false (native tool list) vs default Code Mode
  • Code Mode nested call (tools["wincontrol"].X(...)) vs direct native tool call
  • a brand-new subagent session
  • desktop setting "auto-accept permissions" (cli.json session.permissions) — confirmed OFF via screenshot, also toggled on/off

What does work: effect: "deny" with the same action string reliably hides the tool from the Code Mode catalog, so matching itself is correct and deny is enforced. This suggests ask specifically is not reaching the client for MCP tools.

Minimal config used:

{
  "mcp": {
    "servers": {
      "wincontrol": {
        "type": "local",
        "command": [
          "C:\\Users\\<user>\\.local\\bin\\windows-mcp.exe",
          "serve",
          "--exclude-tools",
          "App,Registry,FileSystem,Scrape,Notification"
        ]
      }
    }
  },
  "permissions": [
    { "action": "wincontrol_PowerShell", "resource": "*", "effect": "ask" }
  ]
}

Impact: dangerous MCP tools (arbitrary command execution, process kill) cannot be gated behind confirmation. A configured ask that silently behaves as allow is worse than no rule at all, because it implies a safeguard that does not exist.

Workaround: do not rely on ask for MCP tools — remove the tool at the MCP layer via --exclude-tools instead.

Activity

  1. github-actions commented on Sep 26, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    • #51223: MCP tool permission asks invoked inside Code Mode do not surface to the TUI.
    • #47177: the Code Mode dispatcher silently bypasses ask permissions for nested tools.
  2. eltPlay commented on Sep 26, 2026

    @eltPlay
    Author

    Follow-up: not reproducible ~1h later — ask fires reliably for MCP tools (2/2), including when the app window is unfocused

    Controlled re-test on the same machine and version (opencode 2.0.18, desktop app, windows-mcp local stdio server, Code Mode on):

    • Added exact-name ask rules (e.g. { "action": "wincontrol_Wait", "resource": "*", "effect": "ask" }), applied via config hot reload only (no service restart).
    • Test 1 — window focused, user watching: call blocked; dialog appeared; user clicked "Allow once". Total 4962 ms for a 2 s tool (~3 s human latency).
    • Test 2 — deliberately Alt+Tabbed away before the call: call blocked for 43 s awaiting a decision. The dialog rendered inside the OpenCode window without stealing system focus, so it was invisible on the active (other) window. User came back, clicked Allow, call completed. No silent pass-through.

    So the "silently degraded to allow" behavior from the original report did not reproduce. Facts that still stand:

    1. deny on the same action strings always worked (tool disappears from the Code Mode catalog).
    2. The original 6 failures were measured as ~400 ms returns (tool executed with no pending request). One environmental difference then vs now: the MCP server was still exposing its full 15-tool set (before --exclude-tools PowerShell,Process was applied); during the re-test it ran with 13 tools.
    3. A real UX gap this re-test did surface: when OpenCode is in the background, a pending permission request is invisible (no focus steal, no notification on the active window). Calls can sit blocked indefinitely with no clue for the user. If the original ~400 ms observations were actually a different failure mode (e.g. request routed to a stale client), I can't determine that from here.

    Fine to close as non-reproducible, or repurpose for the invisible-when-unfocused gap (or I can split that into its own issue).

  3. samvallad33 commented on Oct 8, 2026

    @samvallad33

    the ask rule prompted twice, then silently became an allow, and the tool just executed. a permission that decays without a sound is worse than a broken one.

    this is the failure I built a small gate for. it computes the verdict fresh on every call, reads each command the way the shell is about to run it, and stops the destructive ones it knows before they run. the mode lives in one file that only changes from the owner's terminal, and the change writes a receipt.

    it is free, it runs on your machine, and it is part of vestige.

    npx operator-lite

    it starts in shadow mode and only records what it would have stopped, so nothing changes at first.

    npx operator-lite mode enforce

    run that once from your own terminal and the blocking is on.

  4. opencodejr commented on Oct 28, 2026

    @opencodejr

    Thanks for the detailed report and the list of things you already tried.

    I couldn't reproduce this in the TUI. On 2.0.18 I set up a local stdio MCP server named wincontrol with a PowerShell tool and added { "action": "wincontrol_PowerShell", "resource": "*", "effect": "ask" }. I then called the tool from Code Mode three times in a row. Every call showed Permission required – Call tool wincontrol_PowerShell (Allow once / Always allow / Reject), and the tool only ran after I chose "Allow once". So the server does raise the ask for MCP tools.

    You're on the desktop app on Windows, which our sandbox can't run, so a maintainer with that setup will need to look at this. Two things would help narrow it down:

    1. Check for a saved approval. In v2, choosing "Always allow" stores a project-wide allow that overrides a configured ask in later sessions, including subagents (see permissions: V2 “Always allow” silently overrides configured “ask” across future project sessions #48456 / permissions: allow-always is durable and project-scoped with no opt-out or TUI reset #50600). That would match "prompted twice, then never again", even if you only remember choosing "Allow once". Please check opencode api get /api/permission/saved for a wincontrol_* entry in that project. If there is one, DELETE /api/permission/saved/{id} removes it.
    2. Try the TUI. Run the same config and call from the opencode TUI in a terminal against the same service. If the TUI prompts and the desktop app doesn't, the problem is in the desktop client.

    Related issues, ordered by likelihood:

  5. sealwright commented on Nov 3, 2026

    @sealwright

    @eltPlay, your ask rule on wincontrol_PowerShell prompted twice, then let calls through, and an hour later worked again. Has it happened since, and would a record of which calls actually ran have helped you tell?
    I wrote Provared, a free, open-source guard that sits between the client and a local stdio MCP server such as windows-mcp. It refuses any call outside a permission you sign, whatever the client does, and countersigns a receipt for each call it lets through. It refuses rather than asking you: https://github.com/provared/provared/tree/main/mcp-executor
    If you would rather not hear about this, say so and I will not write again.

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions