anthropics / anthropics/claude-code

[BUG] CCR proxy returns -32003 needs_approval and the retroactive approval card cannot succeed — root cause analysis from the shipped client (2.1.220)

Open
#81,362 2 comments 1 reaction 0 assignees View on GitHub
area:mcp bug
Dominant language
Python
Stars
145k
Forks
23.1k
PR merge metrics
PR metrics pending

Description

### Preflight Checklist

- [x] I have searched [existing issues](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug) 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?

MCP tool calls fail with `MCP error -32003: MCP tool call requires approval` *after* the user has already approved them — twice.

`-32003` is not raised by the client's permission layer. It is returned by the CCR MCP proxy (`api.anthropic.com/v2/ccr-sessions/{session}/mcp`) as `needs_approval`. The client answers it by surfacing a **second, retroactive approval card** and retrying the identical arguments. In the failing case the retry is refused again, so the user has approved twice and still sees `-32003`.

That card is constructed with `suppressAlwaysAllowRule: true` **hardcoded**, so no "Always allow" is ever offered and `permissions.allow` entries in `settings.json` cannot affect this path. Several open issues advise users to fix their allowlist; the allowlist provably cannot help here.

What I see inside a single session:

| Server registration | Result | Approval dialogs |
| --- | --- | --- |
| Friendly name (e.g. `Claude_Code_Remote`) early in the session | works | one |
| Same server, UUID registration, after a mid-session reconnect | `MCP error -32003` | **two** |
| Friendly name after that reconnect | `No such tool available` | zero |
| A different connector (Gmail, Drive) under **its** UUID, same moment | works | one |

The last row matters: the UUID naming itself is harmless. Something else distinguishes this server — see Additional information.

Existing reports of the same fault: #61015, #61027, #61044, #61097, #61143, #79711, #79983.

### What Should Happen?

Either the retroactive approval should actually satisfy the proxy on retry, or — better — a connector the user has already authorised should not require per-call approval at all.

Today the user approves twice and the call still fails, with an error message that names approval as the problem when approval has already been given.

Specifically:

1. Once the retroactive approval card is accepted, the retry should be accepted by the proxy rather than returning `needs_approval` again.
2. The friendly-name registration of a `statsig`-origin server should be restored after an MCP reconnect, instead of leaving only the UUID-keyed duplicate that cannot be approved.
3. If per-call approval genuinely is required by design, the error should say so, and `permissions.allow` should either work or be documented as inapplicable — right now users are repeatedly advised to fix an allowlist that cannot reach this code path.

For unattended Routines this is the difference between annoying and fatal: there is nobody present to tap the retroactive card, so an affected connector cannot be used at all.

### Error Messages/Logs

```shell
The error returned to the model, verbatim:

MCP error -32003: MCP tool call requires approval

Recognised by the client as `error.code === -32003` together with `"args_sha256" in error.data`.

Log line in the shipped bundle (2.1.220):

Tool '${tool}' returned -32003 needs_approval (tool_name=…) — surfacing retroactive approval card

Telemetry: `tengu_mcp_proxy_needs_approval_retry`
Metric: `mcp_ccr_needs_approval`, with outcomes `retry_failed`, `no_prompt_surface`, `arm_not_fired`, `edited_input`, `denied`

The permission card the client raises:

{
behavior: "ask",
suppressAlwaysAllowRule: true,
message: `The ${fullyQualifiedName} connector requires approval for this call.`,
decisionReason: { type: "other", reason: "This connector call requires your approval to proceed." }
}

On `allow` it retries the **identical** arguments; any edited input aborts with `ccr_proxy_needs_approval_edited_input`. The retry is armed once per call.

For the same server under its friendly name after the reconnect, no dialog appears at all and the call returns:

No such tool available
```

### Steps to Reproduce

1. Start a Claude Code on the web session with connectors attached.
2. Within the first minutes, call a tool on the harness-injected remote/meta MCP server (e.g. a routine tool such as `list_triggers`). Approve the single dialog — it succeeds.
3. Keep working. At some point the MCP servers reconnect on their own — no restart, no config change, no user action.
4. Call the same tool again:
- under the friendly name → `No such tool available`, **no dialog at all**
- under the connector UUID → **two** dialogs, both approved, then `MCP error -32003`
5. In the same session, call a Gmail or Drive tool under **its** UUID — it succeeds with one dialog, which shows the UUID naming itself is not the fault.

Measured in one session: last success at **+4m36s** from session start, first failure at **+16m**, with the reconnect in between. After that the affected server stays unusable for the rest of the session; only starting a new session restores it.

### Claude Model

Opus

### Is this a regression?

I don't know

### Last Working Version

_No response_

### Claude Code Version

2.1.220

### Platform

Anthropic API

### Operating System

Ubuntu/Debian Linux

### Terminal/Shell

Other

### Additional Information

## Why one server dies and the others survive the same reconnect

The client writes `/tmp/mcp-config-{session_id}.json`. In an affected session it contains:

| Server key | Proxied `mcp_url` | `X-MCP-Server-Origin` |
| --- | --- | --- |
| `bf7c680d-…` (routine/meta server) | `https://api.anthropic.com/v1/code/mcp/meta` | **`statsig`** |
| `2de66761-…` (Gmail) | `https://gmailmcp.googleapis.com/mcp/v1` | `custom` |
| `github` | — (its own path `/ccr-sessions/{id}/github/mcp`) | — |

1. **Every proxied connector is keyed by connector UUID; only `github` gets a literal name.** That is exactly why, after a reconnect, connectors reappear as `mcp____*` while GitHub keeps `mcp__github__*`. The "servers renamed themselves" effect several issues mention is just this file.
2. **The friendly-named registration is not in that file at all.** The working registration comes from session bootstrap; what survives a reconnect is the UUID-keyed, proxy-routed duplicate of the same backend — i.e. the one that must pass `needs_approval`.
3. **The meta/routine server is injected with `X-MCP-Server-Origin: statsig`**, not as a user connector. That is the clearest difference from the connectors that keep working.

## Impact

- On web and mobile there is no terminal to fall back to, so once this hits, the affected server is unusable for the rest of the session.
- For **Routines** (unattended runs) it is fatal rather than annoying: there is nobody to tap the retroactive card, so an affected connector simply cannot be used. I believe that is what #61015 / #61027 / #61044 are describing.
- `permissions.allow` in `settings.json` cannot help, for the hardcoded reason above. Several issues contain advice to fix the allowlist; it is a dead end and worth correcting in those threads.

## Questions that would confirm or refute this

1. Under what conditions does the proxy return `needs_approval` for a connector the user has already authorised — is it keyed to the tool's `_meta["anthropic/requiresUserInteraction"]`?
2. On the retry, what is `args_sha256` compared against, and does a change of server registration (bootstrap name → UUID key) invalidate the match?
3. Is the friendly-name registration of a `statsig`-origin server expected to be restored after a reconnect? Right now it is not, and the surviving duplicate is the one that cannot be approved.

## Related

#61015 (closed, "should be addressed", no cause or version given), #61027, #61143, #61097, #61044 (open — "no approval UI shown, reconnect does not resolve"), #79711, #79983. I think this analysis explains all of them; happy to have it merged into whichever is the canonical thread.

Account context: personal claude.ai subscription, no organization tool policies. Affected server: the harness-injected remote/meta MCP server (routine tools).

---
_Generated by [Claude Code](https://claude.ai/code)_

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the shipped 2.1.220 client bundle and the generated /tmp/mcp-config-{session_id}.json, then reproduce the reconnect using the statsig-origin server and compare its friendly-name and UUID registrations. Done means the approved retry succeeds, the expected registration is restored after reconnect, and the behavior is covered for unattended calls or clearly reported when approval is required.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, backend
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.