[BUG] MCP tool result spuriously reports "user denied" without showing a permission prompt — affects mcp__codebrain__reindex_codebaseandmcp__codebrain__save_memory mid-session

Resolved 💬 2 comments Opened Apr 25, 2026 by monk0062006 Closed May 28, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

In a single Claude Code session on Windows, two specific MCP tools — mcp__codebrain__reindex_codebase and mcp__codebrain__save_memory — start returning the system signal

The user doesn't want to proceed with this tool use. The tool use was rejected

without any permission prompt ever being displayed to the user, and without any user denial action. Other tools from the same MCP server (ask_codebase, search_symbol, get_symbol_context, recall_memories)
keep working fine in the same session.

The bug is per-tool, not per-server. The first reindex_codebase call of the session succeeded normally (70 seconds, 5,084 files). Subsequent calls — and every save_memory call — were silently auto-rejected.

The model interprets the rejection signal as user denial and per its rules stops retrying, so the session appears "frozen" with no diagnostic from either side. Repro lives in transcript
33bfcdd6-9dc8-4f71-954b-e204f979968f.jsonl — false-rejection lines at 802, 819, 839, 859, 877, 883, 1410, 1421, 1428, 1439-1459. First successful call at 165–179 for contrast.

Likely candidates: per-tool denial cache being set by an invisible code path, or a transport / timeout error being mis-formatted as a user-denial message before it reaches the model. The two affected tools
are the heaviest on the server (full reindex + memory write), so a timeout-into-fake-denial path is plausible.

codebrain-tool-rejection-bug-report.md

What Should Happen?

Expected behavior, in order of preference

  1. The tool just runs.

Once a permission has been granted in a session (or the user is in a permission mode that doesn't require approval — e.g. acceptEdits), subsequent calls to the same tool should execute and return their real
result. A 70-second reindex should run for 70 seconds and return its {files_parsed, total_nodes, ...} payload, exactly like the first successful call earlier in the same session did.

  1. If a permission really is required, the user is prompted.

If the harness has decided this specific call needs re-confirmation, it should display the standard permission prompt to the user with the tool name and arguments, and wait for an explicit approve/deny click
— not silently auto-deny.

  1. If something goes wrong at the transport / server level, the model is told the truth.

A timeout, MCP server hang, or transport error should surface to the model as something like:

Tool call timed out after 60s — the MCP server did not respond.

or

MCP transport error: connection closed unexpectedly.

not as "The user doesn't want to proceed with this tool use. The tool use was rejected." That string has a specific meaning (the user clicked Deny) and using it for any other failure mode poisons the model's
decision-making — it sees a clear "user denied" signal and per its operational rules will stop retrying and back off, when in reality nothing user-initiated happened at all.

Concrete observable acceptance criteria for the fix

A reasonable test the maintainers could ship:

┌────────────────────────────────────────┬───────────────────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────────────────────┐
│ Scenario │ Current behavior │ Expected behavior │
├────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────┤
│ Heavy MCP tool call, user does nothing │ False "user denied" returned to model within seconds, no prompt shown │ Either tool runs to completion, or a permission prompt is shown, or a real error │
│ │ │ message reaches the model │
├────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────┤
│ Same tool succeeded once already in │ First call works, later calls auto-rejected │ All calls behave consistently │
│ this session │ │ │
├────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────┤
│ User presses Esc to interrupt a long │ Future calls to the same tool return false "user denied" without prompts │ The single in-flight call is cancelled with an interrupt error message; future │
│ tool call │ │ calls behave normally │
├────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────┤
│ Other tools from the same MCP server │ Continue to work │ Continue to work (matches today) │
├────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────┤
│ Model receives a denial signal │ Stops retrying (correct response to actual denial, but wrong because the │ Stops retrying ONLY when the user actually denied, not on transport / silent-state │
│ │ denial is fabricated) │ failures │
└────────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────────────────────┘

Why this matters

The "user denied" message is a load-bearing signal in the model's instruction set. The model is told to take it seriously and not re-attempt. Quietly emitting that string for non-denial failure modes turns
it into a footgun: from inside the model there's no way to tell a real denial from a fabricated one, so the model has to either trust it (and stop, frustrating the user who never denied anything) or distrust
it (and ignore real denials, which is worse). The right fix is to keep that string reserved for actual user denials and use distinct error messages for everything else.

Error Messages/Logs

What the model sees as the tool result

  Every call to either affected tool returns this exact payload:

  The user doesn't want to proceed with this tool use. The tool use was rejected (eg. if it was a file edit, the new_string was NOT written to the file). STOP what you are doing and wait for the user to tell
  you how to proceed.

  Note: The user's next message may contain a correction or preference. Pay close attention — if they explain what went wrong or how they'd prefer you to work, consider saving that to memory for future
  sessions.

  No accompanying error code, no transport error, no timeout signal — just the canonical "user denied" message that's normally only emitted when the user clicks Deny in a permission prompt.

  What the user sees on screen

  - No permission prompt is displayed at any point.
  - The Claude Code UI appears to "freeze" on the tool call for ≥10 seconds with no spinner update or diagnostic.
  - After the user presses Esc / sends a new message to interrupt, the next assistant turn includes the false-denial result above.

  Working call for contrast (transcript line 165-179)

  The same tool succeeded earlier in the same session:

  Tool: mcp__codebrain__reindex_codebase
  Result: {"files_found": 5084, "files_parsed": 5084, "files_skipped": 0,
           "total_nodes": 49863, "total_edges": 330020, "errors": [],
           "elapsed_seconds": 70.273}

  70 seconds, no errors, normal behavior.

  Failing calls in the same session (transcript)

  Transcript: C:\Users\ASUS\.claude\projects\C--Users-ASUS-PycharmProjects-SUSA\33bfcdd6-9dc8-4f71-954b-e204f979968f.jsonl

  False-rejection events at lines:

  ┌───────────┬──────────────────────────────────┬──────────────────────────────────────────────┐
  │   Line    │               Tool               │                    Result                    │
  ├───────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
  │ 802       │ mcp__codebrain__reindex_codebase │ false denial, no prompt shown                │
  ├───────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
  │ 819       │ mcp__codebrain__reindex_codebase │ false denial                                 │
  ├───────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
  │ 839       │ mcp__codebrain__reindex_codebase │ false denial                                 │
  ├───────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
  │ 859       │ mcp__codebrain__reindex_codebase │ false denial                                 │
  ├───────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
  │ 877       │ mcp__codebrain__reindex_codebase │ false denial                                 │
  ├───────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
  │ 883       │ mcp__codebrain__reindex_codebase │ false denial                                 │
  ├───────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
  │ 1410      │ mcp__codebrain__reindex_codebase │ false denial                                 │
  ├───────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
  │ 1421      │ mcp__codebrain__save_memory      │ false denial (large payload)                 │
  ├───────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
  │ 1428      │ mcp__codebrain__save_memory      │ false denial (smaller retry)                 │
  ├───────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
  │ 1439-1459 │ mixed                            │ false denials continuing through session end │
  └───────────┴──────────────────────────────────┴──────────────────────────────────────────────┘

  Tools that worked normally in the same session (no false denials)

  mcp__codebrain__ask_codebase           — many calls, all successful
  mcp__codebrain__get_symbol_context      — many calls, all successful
  mcp__codebrain__search_symbol           — many calls, all successful
  mcp__codebrain__recall_memories         — successful
  mcp__codebrain__list_memories           — (not called this session)

  What was NOT in the logs (and arguably should have been)

  - No transport-level error from the codebrain MCP server process
  - No timeout signal
  - No "permission required, prompt was suppressed" diagnostic
  - No is_error: true flag on the tool result — it's framed as a successful tool result whose contents are the denial string
  - No client-side log entry showing a permission decision was made

  The complete absence of any error trail is the worst part — neither the user nor the model has anything to diagnose from.

Steps to Reproduce

Steps to Reproduce

Prerequisites

  1. OS: Windows 11 (Bash + PowerShell environment, but Claude Code's standard install)
  2. Claude Code: any recent version that supports MCP servers
  3. An MCP server with at least one heavy/long-running tool. I reproduced this with codebrain (a code-graph indexer), but the failure pattern (heavy tool, mid-session) suggests it may apply to any MCP server

with similar tools. The two affected tools were:

  • reindex_codebase — full repo reindex, takes ~70 seconds on a 5,000-file project
  • save_memory — writes a large memory payload to a persistent store
  1. A repository large enough that a reindex takes meaningful time. Mine: ~5,000 source files, ~50,000 graph nodes, ~330,000 edges.

Reproducer steps

  1. Start a new Claude Code session in a project with the MCP server configured. Confirm the MCP server connects (/mcp shows it as healthy).
  2. Have the model run several lighter MCP tools first. I used:

mcp__codebrain__ask_codebase
mcp__codebrain__get_symbol_context
mcp__codebrain__search_symbol

  1. These should all succeed and return tool results normally. Do not observe any failure here — this is the "control" phase.
  2. Have the model run the heavy tool once. In my session this was mcp__codebrain__reindex_codebase. First call succeeds. Returns a normal payload (e.g. 70 seconds, files indexed, no errors). Note this in

case the bug is "second call onward."

  1. Continue the session — keep the model active for ~10–20 minutes of normal interaction. Mix of edits, file reads, light MCP calls. No special action needed; just normal Claude Code usage.
  2. Have the model call the heavy tool again. This is when the failure manifests.
  3. Observe:
  • Expected: Either the tool runs (and returns its normal payload after ~70s), or the user is shown a permission prompt to approve.
  • Actual: Within seconds, the tool result returned to the model is "The user doesn't want to proceed with this tool use. The tool use was rejected" — with no permission prompt ever displayed to the user.
  1. Confirm the bug is per-tool, not per-server, by re-running a lighter tool. In the same broken session, calling mcp__codebrain__ask_codebase or mcp__codebrain__get_symbol_context continues to work

normally. Only the two heavy tools (reindex_codebase and save_memory) keep returning false denials.

  1. Confirm no user action triggers the rejection by leaving the keyboard alone for the duration of a tool call. Bug still fires.

Minimal MCP server config (mine)

{
"mcpServers": {
"codebrain": {
"command": "<path to codebrain MCP server binary>",
"args": [],
"env": {}
}
}
}

(I don't have a public minimal MCP server I can hand over, but any MCP server exposing one tool that takes >30 seconds to respond should be enough to repro. Possible reduction: a stub MCP that sleeps 70s
before returning a result, then a session that calls it twice ~15 minutes apart.)

Files / artifacts to capture for debugging

If you want to reproduce against my actual session:

  • Transcript: C:\Users\ASUS\.claude\projects\C--Users-ASUS-PycharmProjects-SUSA\33bfcdd6-9dc8-4f71-954b-e204f979968f.jsonl
  • Project working directory: C:\Users\ASUS\PycharmProjects\SUSA
  • Detailed bug report I wrote during the session: C:\Users\ASUS\.claude\projects\C--Users-ASUS-PycharmProjects-SUSA\memory\codebrain-tool-rejection-bug-report.md

What I'd ask the maintainers to look at first

  1. Mid-session permission cache: Is there a per-(tool name) denial cache that gets populated by something other than a visible permission decision? E.g. by a UI keystroke that's interpreted as "deny this

tool" without re-showing the prompt.

  1. Timeout → fake denial path: Is there a code path that converts a transport timeout or stalled MCP call into the canonical "user doesn't want to proceed" string before sending it to the model? If so the

model can never distinguish an actual denial from a transport error.

  1. Why these two tools specifically? Both are the heaviest on the codebrain server (full reindex + persistent write). Other tools on the same server work fine throughout the session. Heaviness correlates

strongly enough that I'd start there.

Claude Model

Opus

Is this a regression?

Yes, this worked in a previous version

Last Working Version

_No response_

Claude Code Version

2.1.119

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

Windows Terminal

Additional Information

Platform: Windows 11 (build 26200), Bash shell, Python 3.11.0, MCP servers configured.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗