Claude Desktop spawns duplicate generations of local MCP server processes without terminating the previous generation

Status Open
Maintainer reply None cached
Activity 4 comments · opened Jul 8, 2026

Claude Desktop spawns duplicate generations of local MCP server processes without terminating the previous generation

Summary

Claude Desktop (macOS) spawns a complete second generation of all configured local MCP server child processes shortly after launch, without killing the first generation. Both generations remain alive indefinitely. The app closes the first generation's transports (visible in mcp.log) but never terminates the underlying OS processes, leaking them. Duplicate live clients then collide on downstream resources — reproducibly breaking WordPress-based MCP servers with -32603 "WordPress connection failed during initialization" — and correlate with broader local-MCP unreliability (4-minute tool-call hangs across unrelated servers).

Environment

  • macOS 26.5.1 (25F80), Apple Silicon (arm64)
  • Claude Desktop (current as of 2026-07-07), Max plan
  • Local MCP servers configured: podio (local node script), 3× @automattic/mcp-wordpress-remote (npx), chrome-devtools-mcp (npx), n8n-mcp (npx), @modelcontextprotocol/server-filesystem (npx), mcp-remote ×2 (Rocket.net, custom), plus bundled servers
  • Node v22.22.0 via nvm
  • No VPN or network system extensions present (systemextensionsctl list → 0 extensions), SIP enabled

Reproduction (observed on two separate boots, same day)

  1. Cold boot the machine (or fully quit Claude Desktop).
  2. Launch Claude Desktop with multiple local MCP servers configured.
  3. Within ~30 seconds to a few minutes, inspect processes:

``
ps -eo pid,ppid,lstart,command | grep -E "podio-mcp|wordpress-remote|chrome-devtools-mcp|n8n-mcp" | grep -v grep
``

  1. Observe two distinct start-time clusters of the same server set, all parented by the single Claude Desktop main process.

Instance A (first observation, same app launch)

Single Claude Desktop instance PID 57836 (launched 17:52:02 PDT). Two full child generations alive simultaneously:

  • Generation 1: 17:53:11 (2× podio wrapper/node, 3× wordpress-remote, chrome-devtools, n8n, filesystem, 2× mcp-remote)
  • Generation 2: 18:01:37–38 (complete duplicate set)

mcp.log shows the trigger moment — at 2026-07-08T01:01:37–40Z (18:01:37–40 PDT) every server logs Client transport closed / Server transport closed, immediately followed by generation 2 spawning. Transports were closed; processes were not killed.

Instance B (fresh cold boot, ~4 hours later, after full machine cleanup)

Claude Desktop launched 21:44 PDT after reboot:

  • Generation 1: 21:44:52 — podio + 3× wordpress-remote (all disclaimer-wrapped, parented by the Desktop main process)
  • Generation 2: 21:45:18 — 26 seconds later: duplicate podio + 1× wordpress-remote, gen 1 still alive

Full ps listings for both instances available on request.

Downstream breakage (why this matters)

  1. WordPress MCP servers fail initialization when a duplicate client already holds the session. mcp-server-<name>.log shows repeated:

``
[ERROR] [CLIENT] ❌ Client Error: ListTools failed after 0ms
{ "error": "MCP error -32603: Cannot process tools/list: WordPress connection failed during initialization" }
``
In Instance B, the two WordPress servers that errored at 21:35–21:44 PDT are exactly the ones missing from generation 2 — consistent with gen-1 processes still holding their sessions.

  1. Correlates with the "4-minute hang" failure mode. Across multiple sessions (July 2, July 7; detailed contemporaneous incident reports available), tool calls to unrelated local servers (Chrome extension bridge, Podio, Rocket.net) hung for a fixed ~4 minutes and returned "No result received from the Claude Desktop app," in a works-once-after-restart-then-dies pattern. A broker routing requests to a stale/leaked server generation would produce exactly this signature. Correlation, not yet proven causation — but the leak is the only anomaly found after eliminating all machine-local factors (VPN network extension removed, SIP cycle, clean reboot, no memory pressure).
  1. Resource waste: every leaked generation holds node/npm processes and npx caches; users switching accounts (which forces server respawns) may accumulate multiple leaked generations per day.

Expected behavior

When Claude Desktop closes a server's transport and respawns it (config reload, account switch, or the post-launch respawn observed here), the previous child process tree should be terminated (SIGTERM, then SIGKILL after grace).

Additional diagnostic gap worth noting

While diagnosing this, we also hit the Claude for Chrome pairing service returning an empty browser list for ~30 minutes with zero diagnostics when the extension's signed-in account did not match the chat session's account. An explicit "browser connected under a different account" signal would have saved substantial time. Happy to file separately if preferred.

Support history

For the record: this was reported to Anthropic customer support with all five incident reports attached, across multiple contacts. The refund/credit request was declined per policy, and the response noted that the technical reports "may be valuable for our development teams to address these problems." Filing this issue myself so that they actually reach one. Three days of a paying customer's Max-plan tokens went into producing the diagnosis below — the least it can do is get triaged.

Attachments available on request

  • Full ps -eo pid,ppid,lstart,command listings for both instances
  • mcp.log excerpt showing the mass transport-close + respawn event
  • mcp-server-fluent-crm.log / mcp-server-novamira-*.log -32603 excerpts (July 1–8)
  • Five contemporaneous incident reports covering the July 2–7 failure sessions

View original on GitHub ↗

4 Comments

anthemwild · 1 month ago

Update: root mechanism identified — the app runs two concurrent initialization sequences per launch.

Further log analysis on the same machine (fresh boot, no VPN/network extensions, SIP enabled) shows this is not a teardown/respawn failure — main.log records the entire startup sequence executing twice in parallel within a single Desktop launch. Every init line at 21:44:48–49 PDT is duplicated:

2026-07-07 21:44:48 [info] [Chrome Extension MCP] Installed native host manifest for Chrome at <home>/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.anthropic.claude_browser_extension.json
2026-07-07 21:44:48 [info] [Chrome Extension MCP] Installed native host manifest for Chrome at <home>/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.anthropic.claude_browser_extension.json
2026-07-07 21:44:48 [info] [Chrome Extension MCP] Native host sync complete
2026-07-07 21:44:48 [info] [Chrome Extension MCP] Native host sync complete
2026-07-07 21:44:48 [info] [Chrome Extension MCP] Watching Chrome for extension changes
2026-07-07 21:44:48 [info] [Chrome Extension MCP] Watching Edge for extension changes
2026-07-07 21:44:48 [info] [claude-in-chrome] MCP server registered
2026-07-07 21:44:48 [info] [Chrome Extension MCP] Watching Chrome for extension changes
2026-07-07 21:44:48 [info] [Chrome Extension MCP] Watching Edge for extension changes
2026-07-07 21:44:48 [info] [claude-in-chrome] MCP server registered
2026-07-07 21:44:49 [info] Starting periodic extension update checks (interval: 21600000ms)
2026-07-07 21:44:49 [info] Starting periodic extension update checks (interval: 21600000ms)
2026-07-07 21:44:49 [info] [RemotePluginManager] Starting remote plugins sync
2026-07-07 21:44:49 [info] [RemotePluginManager] Starting remote plugins sync
2026-07-07 21:44:49 [info] [RemotePluginManager] Starting periodic sync (interval: 600000ms)
2026-07-07 21:44:49 [info] [RemotePluginManager] Sync already in progress, waiting...

Note the last line — one init path detecting its twin ("Sync already in progress, waiting..."). The two MCP server generations observed in the original report (spawned at 21:44:52 and 21:45:18, both parented by the same main process) are consistent with these two init sequences each spawning the full configured server set.

Mid-session accretion: the RemotePluginManager periodic sync runs every 600000ms (10 min). In the first documented instance, generation 2 spawned ~8.5 minutes after generation 1 — consistent with the first periodic sync respawning servers without reaping. Additional single MCP server processes continued to appear during the session (23:54, 00:12, 00:22, 00:24) while earlier generations stayed alive, so the process table grows monotonically with app uptime.

Suggested areas to check:

  1. Why two initialization contexts run concurrently at launch (race between two windows/renderers triggering main-process init, or duplicated lifecycle event registration).
  2. Whether RemotePluginManager periodic sync respawns local MCP servers without terminating the prior child process tree.

Same reproduction environment as the original report; full logs available on request.

0xbrainkid · 1 month ago

This looks like a lifecycle ownership bug more than a transport bug: closing stdio/SSE transport is not the same thing as reaping the child process that owns the MCP server.

A robust fix would make each MCP server generation explicit:

  • assign a generation id per Desktop launch / config reload
  • keep pid + command + transport id together in one lifecycle record
  • on reload, close transport, send SIGTERM to the old generation, wait, then SIGKILL after a short grace period
  • only start the replacement generation after the old generation is gone, or mark intentional overlap as such

The downstream collisions here are exactly why duplicate MCP clients are dangerous: many servers hold external state (WordPress auth, browser sessions, local files, payment/API credentials) and are not idempotent when two identical clients initialize at once.

For debugging, a claude://mcp/processes or log section that prints active MCP generations would make this immediately visible: server name, pid, parent pid, start time, transport state, and config hash. Right now the logs prove the transport closed but not whether process ownership was cleaned up.

anthemwild · 1 month ago

Confirming your read with process-table evidence: ownership is never cleaned up. After the transports closed (logged at 01:01:37Z), the prior generation's processes remained alive for hours, still parented by the live Desktop main process — not orphaned, not reaped. On the most recent launch, the duplicate pair spawned 13 seconds apart (00:47:20 / 00:47:33 local), and additional single server processes continued to accrete afterward (00:47:37, 00:48:15) with all earlier ones still running. Process count grows monotonically with app uptime.

Can also confirm the overlap is unintentional rather than a designed handoff: the two concurrent init sequences visibly collide in the log — [RemotePluginManager] Starting remote plugins sync twice, followed by Sync already in progress, waiting....

Agreed on the debugging surface — a generation-aware process view (server name, pid, ppid, start time, transport state, config hash) would have made this diagnosable in minutes instead of days. And the non-idempotent-server point is exactly how this manifested for me: duplicate mcp-wordpress-remote clients initializing simultaneously produced repeatable -32603 "WordPress connection failed during initialization" errors until I pinned package versions to narrow the race window.

sdk451 · 4 days ago

Also occurs on windows os