Claude in Chrome: native host broadcasts every tool response to all connected MCP clients — concurrent sessions silently receive each other's results

Status Open
Reported on v2.1.229
Maintainer reply None cached
Activity 1 comment · opened Aug 18, 2026

Summary

The Claude in Chrome bridge has no request/response correlation. The native host broadcasts every tool_response to every connected MCP client, and each client resolves on the next frame it receives. When two Claude Code sessions have browser tool calls in flight at the same time, they silently consume each other's responses — one session gets the other's screenshot, page text, or DOM read, with no error.

Silent wrong data is the problem here, not a failed call. An agent acting autonomously can act on a page it never opened.

What I expected

Either responses are routed back to the session that made the request, or a second concurrent session is refused with a clear error.

What actually happens

Session A and session B both call a browser tool. Whichever response arrives first is delivered to both. The session that didn't ask for it resolves its pending call with that payload; the response it was actually waiting for arrives later, finds no pending callback, and is discarded. The leftover surfaces as Tool request timed out after 30000ms.

An idle session is unaffected (no pending callback, frame dropped), so strictly alternating use appears to work — which is why this is easy to miss and hard to attribute when it bites.

Root cause

From the bundled JS in claude.exe (Claude Code 2.1.229). Names are minified, so they'll differ per build:

Native host (class $Bf) — supports many clients by design:

mcpClients = new Map; nextClientId = 1;
this.server = net.createServer((e) => this.handleMcpClient(e));

InboundhandleMcpClient drops the originating client id when forwarding to Chrome:

b2(`Forwarding tool request from MCP client ${t}: ${s.method}`);
qct(Oe({ type: "tool_request", method: s.method, params: s.params }))

The id t is used only for logging. Since the outbound message carries no correlation id, the extension has nothing to echo back.

OutboundhandleMessage broadcasts unconditionally:

case "tool_response": {
  if (this.mcpClients.size > 0) {
    b2(`Forwarding tool response to ${this.mcpClients.size} MCP clients`);
    ...
    for (let [c, u] of this.mcpClients) try { u.socket.write(l) } catch (d) { ... }
  }
}

MCP client (class hIc) — a single response slot, no matching:

this.responseCallback = (d) => { clearTimeout(s), o(d) };   // sendRequest
handleResponse(e) { if (this.responseCallback) { let t = this.responseCallback; this.responseCallback = null; t(e) } }

The log line Forwarding tool response to N MCP clients with N > 1 is the smoking gun.

Also affects a single session

The client instance is per-session, and sendRequest overwrites this.responseCallback unconditionally. Two concurrent in-flight browser calls within one session — e.g. two subagents — clobber the same slot: the first hangs to its 30 s timeout, the second receives the first's response. Same root cause.

Platform notes

The broadcast is in platform-independent code, so this is not Windows-specific. Windows just guarantees the collision, because there is exactly one pipe per user (\.\pipe\claude-mcp-browser-bridge-<username>, no PID component) while Unix gives each host its own <pid>.sock. Multiple MCP clients still share one host socket on Unix, so the same cross-talk applies whenever two sessions attach to the same host.

Related: the Chrome docs' Windows troubleshooting already says "Close any other Claude Code sessions that might be using Chrome." That's written as advice for an EADDRINUSE connection error. This report is a different failure — connection succeeds fine, and the corruption is silent.

Reproduction

  1. Open two Claude Code sessions with Chrome integration enabled.
  2. Have both issue a browser tool call at roughly the same time, targeting clearly different pages (e.g. each takes a screenshot of a distinct site).
  3. Observe one session receiving the other's result, and/or a 30 s tool timeout in the session whose response was consumed.

Suggested fix

Add correlation ids end to end: the host tags each forwarded request with the originating client id (or a request id), the extension echoes it on the response, and the host routes the response to that client only. The MCP client should key pending promises by request id instead of a single responseCallback slot — which also fixes the in-session concurrent-call case.

If per-client routing isn't desirable, the alternative is to refuse a second concurrent MCP client with an explicit error, so the limitation is visible rather than silently corrupting results.

Environment

  • Claude Code 2.1.229 (running inside Claude Desktop 1.32352.1.0)
  • Windows 11 ARM64
  • Claude in Chrome extension 1.0.85

Why this matters

Multiple concurrent sessions is a headline capability of the desktop app, and the browser is one of the surfaces most worth parallelising. Today the Chrome extension is effectively single-session, and there's nothing in the UI or docs that says so — the failure mode is wrong data rather than a refusal. See also #87764, which asks for login state in the Browser pane; today the parallel-safe surface is the one that can't authenticate, and the one that can authenticate isn't safe to run in parallel.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗