Mods: a mod's own $.mcp.call passes auto mode in a terminal but is refused in the Desktop app, on the same build

Status Open
Reported on v2.1.295
Maintainer reply None cached
Activity 0 comments · opened Oct 13, 2026

Versions: Claude Code 2.1.295 (the build bundled with the Desktop app) and 2.1.296 (CLI), macOS, direct Anthropic API connection.

What the mod does

A plugin ships an HTTP MCP server in .mcp.json and a mod. After each turn the mod calls one tool on that server with $.mcp.call, from a $.clock.after callback started in its turn.complete hook. It is a background network call. Nobody asks Claude for it.

What happens, all in auto mode

  • Interactive terminal session, 2.1.296 and 2.1.295: the call runs. The debug log says Skipping auto mode classifier for mcp__plugin_myplugin_api__refresh: called by plugin myplugin.
  • claude -p --input-format stream-json --output-format stream-json --permission-mode auto --permission-prompt-tool stdio on 2.1.295: the call runs, with the same debug line.
  • Desktop app, Code tab, 2.1.295: the call is refused:

> $.mcp.call(plugin:myplugin:api, refresh) refused: The server-side auto mode classifier gave no verdict for mcp__plugin_myplugin_api__refresh: the request that produced this action did not ask for one.

Expected

The same decision on every surface. The mods API declaration of $.mcp.call says "No permission prompt: the plugin's call, seen by the hooks above it, is the grant", which is how the terminal behaves.

What the mod cannot do about it

The docs say a mod that handles tool.check can approve an action before the classifier. That is not available for a mod's own call: its tool.check hook is skipped (tool.check skipped: re-entry (the plugin's own code raised it; origin myplugin)) and the hook's .catch handler is not asked either.

Shape of the mod

I saw the refusal with a larger plugin of this shape, not with this exact file.

import type { Register } from 'claude-code'

export const register: Register = on => {
  on('turn.complete', async ($, e, next) => {
    const result = await next(e)

    if (e.agentId === undefined) {
      $.clock.after(0, async () => {
        try {
          const connection = await $.mcp.connect('api')

          if (connection.isConnected) await $.mcp.call(connection.server, 'refresh', {})
        } catch (error) {
          $.ui.log(String(error))
        }
      })
    }

    return result
  })
}

Related: #100575, #99214, #100633 report the same refusal. None of them notes that the terminal exempts the call.

View original on GitHub ↗