Describe the bug
At startup, the CLI spawns its configured MCP servers, then — once GitHub authentication completes — tears down and rebuilds the entire MCP client, re-spawning every configured server. The first generation of stdio child processes is neither killed nor reaped, so each affected session permanently leaks one orphaned MCP server process.
Remote HTTP MCP servers simply reconnect and leave no trace, so the leak is invisible unless you have a local/stdio server configured.
This appears to be a race: only servers that finish their handshake before the post-auth rebuild get orphaned. Servers still cold-starting at that moment are unaffected.
Affected version
GitHub Copilot CLI 1.0.78 (Node v24.18.1)
Steps to reproduce the behavior
My setup: one local (stdio) MCP server (a Python CLI with a slow cold start, roughly 6–50 s) plus two remote HTTP MCP servers.
- Configure a stdio MCP server in
~/.copilot/mcp-config.json that completes its handshake within a few seconds of launch.
- Start
copilot interactively and wait until the session is ready.
- Count the child processes for that server:
ps -eo pid,ppid,lstart,command | grep <server>
- Observe two live child processes under the same CLI pid, spawned roughly 15–50 s apart. Only the second one is actually in use.
Note on reproduction: I could not produce a minimal repro on this machine, because my org's managed MCP allow list rejects any server not on the corporate whitelist (Skipping MCP server "<name>": not permitted by enterprise managed allow list), so a throwaway stdio test server never gets spawned at all. Anyone on an unrestricted account should be able to reproduce with any trivial stdio MCP server.
Expected behavior
When the MCP client is rebuilt after authentication, the previous generation of stdio child processes should be terminated and reaped — the same teardown that 1.0.51 added for session.disconnect().
Ideally the post-authentication reconfiguration would only (re)start the GitHub MCP server rather than recycling every configured server, since the other servers' configuration has not changed.
Additional context
Timeline from one real session (1.0.78), from --log-level debug:
17:16:11 spawn stdio server #1 (pid A); connect HTTP servers
17:17:02 stdio server #1 completes handshake ("Service initialized as client")
17:17:02 [ERROR] GitHub MCP server configured after authentication
17:17:03 → entire MCP client rebuilt:
- spawn stdio server #2 (pid B)
- HTTP servers: "worker quit with fatal: Transport channel closed"
- "Handling tools refresh for <server>"
17:17:14 stdio server #2 completes handshake
pid A is never killed. It keeps running with its stdin pipe dangling.
Decisive evidence that the child is abandoned, not merely idle:
I killed the 6 suspected orphans across my running sessions. All 6 became Z+ (zombie) rather than disappearing:
$ kill 29835 56680 26768 92092 83359 43758
$ ps -p 29835 -o pid=,stat=
29835 Z+
A child only stays a zombie if its parent never called wait()/waitpid(). The CLI processes were still running and still owned these PIDs, but had no outstanding wait on them. After the kill, every session was left with exactly one live MCP child and all sessions kept working normally — confirming the killed ones were dropped, not in use.
Correlation check. Across 4 sessions I compared, the counts matched exactly every time:
occurrences of "GitHub MCP server configured after authentication"
== number of "Service initialized as client" handshakes for that server
== number of spawned child processes
Impact. 18 concurrent interactive sessions accumulated 6 orphans. Because this particular stdio server is heavyweight (~25 MB idle, ~570 MB warm), total MCP server residency reached ~3 GB, of which the orphans were pure waste. Long-lived terminal sessions make this monotonically worse — the oldest orphan had been running for 2 days 15 hours before I noticed.
Related issues:
Environment: macOS 26.6 (darwin-arm64), Node v24.18.1, zsh, Ghostty/iTerm2.
Describe the bug
At startup, the CLI spawns its configured MCP servers, then — once GitHub authentication completes — tears down and rebuilds the entire MCP client, re-spawning every configured server. The first generation of stdio child processes is neither killed nor reaped, so each affected session permanently leaks one orphaned MCP server process.
Remote HTTP MCP servers simply reconnect and leave no trace, so the leak is invisible unless you have a local/stdio server configured.
This appears to be a race: only servers that finish their handshake before the post-auth rebuild get orphaned. Servers still cold-starting at that moment are unaffected.
Affected version
GitHub Copilot CLI 1.0.78 (Node v24.18.1)
Steps to reproduce the behavior
My setup: one local (stdio) MCP server (a Python CLI with a slow cold start, roughly 6–50 s) plus two remote HTTP MCP servers.
~/.copilot/mcp-config.jsonthat completes its handshake within a few seconds of launch.copilotinteractively and wait until the session is ready.ps -eo pid,ppid,lstart,command | grep <server>Note on reproduction: I could not produce a minimal repro on this machine, because my org's managed MCP allow list rejects any server not on the corporate whitelist (
Skipping MCP server "<name>": not permitted by enterprise managed allow list), so a throwaway stdio test server never gets spawned at all. Anyone on an unrestricted account should be able to reproduce with any trivial stdio MCP server.Expected behavior
When the MCP client is rebuilt after authentication, the previous generation of stdio child processes should be terminated and reaped — the same teardown that 1.0.51 added for
session.disconnect().Ideally the post-authentication reconfiguration would only (re)start the GitHub MCP server rather than recycling every configured server, since the other servers' configuration has not changed.
Additional context
Timeline from one real session (1.0.78), from
--log-level debug:pid A is never killed. It keeps running with its stdin pipe dangling.
Decisive evidence that the child is abandoned, not merely idle:
I killed the 6 suspected orphans across my running sessions. All 6 became
Z+(zombie) rather than disappearing:A child only stays a zombie if its parent never called
wait()/waitpid(). The CLI processes were still running and still owned these PIDs, but had no outstanding wait on them. After the kill, every session was left with exactly one live MCP child and all sessions kept working normally — confirming the killed ones were dropped, not in use.Correlation check. Across 4 sessions I compared, the counts matched exactly every time:
Impact. 18 concurrent interactive sessions accumulated 6 orphans. Because this particular stdio server is heavyweight (~25 MB idle, ~570 MB warm), total MCP server residency reached ~3 GB, of which the orphans were pure waste. Long-lived terminal sessions make this monotonically worse — the oldest orphan had been running for 2 days 15 hours before I noticed.
Related issues:
session.disconnect()does not kill stdio MCP server processes spawned for that session #3440 —session.disconnect()does not kill stdio MCP server processes. Closed as fixed in 1.0.51 for the SDK path, but a later comment reports the same leak on 1.0.68 via/newand/resume. This report is a third trigger path: the automatic post-authentication rebuild at startup, which affects plain interactive use with no user action at all. It may be worth fixing the teardown at the MCP-client-rebuild layer rather than per-entry-point.GitHub MCP server configured after authenticationlog line for a different symptom, which may help locate the code path.Environment: macOS 26.6 (darwin-arm64), Node v24.18.1, zsh, Ghostty/iTerm2.