Describe the bug
MCP servers are frequently missing from a session, and the ones that are present can vanish mid-session.
Two user-visible symptoms, which turn out to be the same subsystem:
-
Servers silently never connect. A session starts with a fraction of the configured MCP servers — in my case 3 of 18 — with no error, no warning, and no retry. The missing tools simply do not exist as far as the agent is concerned. The runtime reports MCP loading as complete ~9 s after start and then never revisits it. One of my runtimes has now sat at 3/18 for over 25 minutes.
-
Servers disappear mid-session. Tool calls that worked a minute ago start failing with:
MCP server '<name>': Error: NativeMcpHostHandle has been destroyed
or the tool silently drops out of the available set. This happens while the session is otherwise healthy, and it is triggered by activity in a different session.
The practical effect is that MCP is unreliable in a way the user cannot see, diagnose, or fix — there is no error surfaced and no supported way to force a reload (see #505 on the app tracker).
Affected version
- Copilot CLI
1.0.80 (copilot --version), running as the engine inside GitHub Copilot desktop app 1.1.12
- Windows 11 Enterprise 25H2 (build 26200.9106), AMD64
Full environment block below.
Steps to reproduce the behavior
Reproduces most reliably with a large MCP server set (mine merges to 18 across user config, repo .mcp.json, and plugin configs). Server names below are redacted; identities are irrelevant to the failure.
Symptom 1 — incomplete startup:
- Configure a large number of MCP servers (≈15+) across several sources.
- Start a session in a working directory that has no existing runtime process (a fresh clone, a new git worktree, or a forked session — anything with a new
cwd).
- Wait. Do not interact.
- Observe: only the first handful of servers ever connect. Enable debug logging and grep for
session.mcp_servers_loaded — it fires once, seconds after start, with only those few connected.
- There is no retry. The remaining servers stay missing indefinitely.
Symptom 2 — mid-session teardown:
- With the above session running, cause the app to subscribe to MCP status from any session sharing that runtime (opening MCP settings does this).
- ~25 s later the runtime tears down every MCP connection and respawns all of them.
- Any tool call landing in that window fails with
NativeMcpHostHandle has been destroyed, or the tool is missing from the tool list entirely.
Expected behavior
- MCP startup should not report completion while configured servers are still unconnected. Servers that miss the initial window should be retried, or their failure surfaced to the user.
- A status subscription should not tear down live MCP connections, and certainly should not do so for sessions that did not request it.
- If a reload is genuinely required, in-flight tool calls should get a retriable error, not a destroyed handle.
Additional context
Environment
App version 1.1.12
OS Windows 10 Enterprise 25H2 (build 26200.9106)
Architecture AMD64
WebView2 runtime 151.0.4129.107
Copilot CLI 1.0.80
Agency 2026.8.24.6
GPU 0 Intel(R) Graphics (32.0.101.8132)
GPU 1 NVIDIA GeForce RTX 5090 (32.0.16.1088)
WebView2 flags --disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection,CalculateNativeWinOcclusion,UiaProvider
--force-renderer-accessibility
Frequency
Always, on this configuration. Every runtime I inspected showed an incomplete first generation. The mid-session teardown fired 3 times in 11 minutes in one runtime.
Extensions
Not tried with extensions/plugins disabled — but the failure is in MCP client startup and occurs before any agent turn, and two of the three config sources are plugin-independent. Happy to test a clean profile if useful.
Suggested labels
area:mcp (or nearest equivalent), type: Bug. I can't set labels as an outside contributor.
Related issues
github/copilot-cli#4392 — same teardown-and-rebuild mechanism, but reported for the orphaned process leak it causes. This report is about two different consequences of that rebuild: the incomplete first generation, and the cross-session trigger.
github/copilot-cli#4542 — workspace .mcp.json detected but not connected. Different layer (config plumbing vs. startup race), same user-visible outcome of "configured server is missing".
- On the app tracker, where several reports of this landed because the symptom appears in the app UI:
github/app#2411 (the exact NativeMcpHostHandle has been destroyed string, open, no maintainer response), github/app#2139 (stale binding, mcp_stale=true, observed_session_mcp=0), github/app#1012 (spawn deadline too tight with many servers), github/app#505 (no way to reload MCP).
In-depth investigation
All timestamps UTC, from --log-level all. Paths, usernames and internal server names redacted.
Setup. MCP fleets are owned by the runtime process, which the app pools per working directory (cli_pool: spawned new CLI process for pool working_directory=…). All sessions sharing a cwd share one MCP fleet; a new cwd cold-starts the whole fleet. Three live runtimes were observed, one per cwd.
An enterprise wrapper merges six config sources before handing the runtime a single merged set:
Loaded 3 MCP server(s) from plugin <redacted>
Merging built-in MCPs → 3 server(s) total
Loaded 13 MCP server(s) from <user>/.copilot/mcp-config.json
Skipping default MCP '<redacted>' from user config — wrapper manages default MCPs
User global MCP config merged; 14 server(s) total
Loaded 7 MCP server(s) from <repo>/.mcp.json
Repo-local MCP configs merged; 17 server(s) total
Plugin MCPs merged; 18 server(s) total
MCP source merge complete; 18 server(s) total
Eight of those are additionally fronted by localhost HTTP proxies on sequential ports.
Finding 1 — startup finalizes early and never retries.
On the runtime with the new cwd, 18 servers were in the merged config, yet:
16:22:43.527 All 1 configured MCP registry/registries permit all servers (allow_all)
16:22:43.645 MCP server stderr {"server_name":"<a>", …}
16:22:43.645 MCP server stderr {"server_name":"<b>", …}
16:22:43.645 MCP server stderr {"server_name":"<c>", …}
16:22:44.827 [rust:rmcp::service] Service initialized as client
16:22:44.869 [rust:rmcp::service] Service initialized as client
16:22:48.026 [rust:rmcp::service] Service initialized as client
16:22:48.028 session.mcp_servers_loaded (ephemeral)
16:22:48.030 session.mcp_server_status_changed (ephemeral)
Exactly three Service initialized as client events, then mcp_servers_loaded — 9 s after start. Grepping the whole runtime log confirms only 3 distinct server_name values ever appear, and only 9 lines total from the copilot_runtime::mcp module.
Two checks that rule out the obvious explanations:
- The other servers were reachable. The 8 HTTP proxies were listening the entire time (verified with a port check). The runtime never connected to any of them — filtering the log for those ports yields zero hits, while an unrelated internal port shows 138.
- It is not slow startup. That runtime has remained at 3/18 for 25+ minutes with no further attempts. There is no retry path.
Not a policy block either — the registry explicitly logged allow_all.
Finding 2 — a "reload" is a full teardown, not a top-up.
On a second runtime, a reload looks like this:
16:11:50.050 Loaded workspace MCP config from <repo>/.mcp.json
16:11:50.050 Loaded MCP config … (project): 7 server(s)
16:11:50.055 Loaded MCP config from plugin-dir plugins: 3 server(s)
16:11:50.475 [rust:rmcp::service] input stream terminated
16:11:50.475 [rust:rmcp::service] serve finished {"quit_reason":"Closed"}
16:11:50.515 [rust:rmcp::service] task cancelled
16:11:50.515 [rust:rmcp::service] serve finished {"quit_reason":"Cancelled"}
16:11:50.846 MCP server stderr {"server_name":"<redacted>", …} ← respawn begins
Every live connection is closed and every server respawned. A tool call landing in that window is what produces NativeMcpHostHandle has been destroyed — the handle is genuinely gone, which is why it is not retriable from the caller's side.
Finding 3 — the trigger is subscribe_mcp_status, from an unrelated session.
Correlating the app's websocket messages against the runtime's MCP generations:
subscribe_mcp_status |
resulting mcp_servers_loaded |
| 16:11:35 |
16:12:00 |
| 16:18:04 |
16:18:30 |
| 16:19:18 |
16:19:44 |
Three subscriptions, three full teardown/respawn cycles, ~25 s lag, no other correlated events. The subscription came from a different session id than the one whose tools broke — the runtime is shared per cwd, so the blast radius is every session on it.
Cost. That single runtime performed 51 Service initialized as client operations across 4 generations in 11 minutes:
| Generation |
Time |
Clients initialized |
| 1 |
16:08:47 |
3 |
| 2 |
16:12:00 |
16 |
| 3 |
16:18:30 |
16 |
| 4 |
16:19:44 |
16 |
Note generation 1 shows the same 3-server under-load as Finding 1 — the incomplete first generation appears to be systematic, and the only reason that runtime recovered is that an unrelated status subscription happened to force a rebuild later.
Why this is user-hostile in combination: the first generation is silently wrong, there is no retry, there is no user-facing reload (github/app#505), and the only thing that fixes it is an unrelated side effect. Users experience this as "MCP randomly doesn't work", and the natural workaround — restart the app — often lands on another incomplete first generation.
Suggested fixes, in rough order of value:
- Do not emit
mcp_servers_loaded until every configured server has either connected or failed; surface failures.
- Retry (with backoff) servers that miss the initial window, instead of dropping them permanently.
- Make a status subscription read-only — it should not be able to recycle live connections.
- If a rebuild is unavoidable, reuse still-healthy connections rather than tearing down all of them (this would also fix the process leak in
#4392).
Describe the bug
MCP servers are frequently missing from a session, and the ones that are present can vanish mid-session.
Two user-visible symptoms, which turn out to be the same subsystem:
Servers silently never connect. A session starts with a fraction of the configured MCP servers — in my case 3 of 18 — with no error, no warning, and no retry. The missing tools simply do not exist as far as the agent is concerned. The runtime reports MCP loading as complete ~9 s after start and then never revisits it. One of my runtimes has now sat at 3/18 for over 25 minutes.
Servers disappear mid-session. Tool calls that worked a minute ago start failing with:
or the tool silently drops out of the available set. This happens while the session is otherwise healthy, and it is triggered by activity in a different session.
The practical effect is that MCP is unreliable in a way the user cannot see, diagnose, or fix — there is no error surfaced and no supported way to force a reload (see #505 on the app tracker).
Affected version
1.0.80(copilot --version), running as the engine inside GitHub Copilot desktop app1.1.12Full environment block below.
Steps to reproduce the behavior
Reproduces most reliably with a large MCP server set (mine merges to 18 across user config, repo
.mcp.json, and plugin configs). Server names below are redacted; identities are irrelevant to the failure.Symptom 1 — incomplete startup:
cwd).session.mcp_servers_loaded— it fires once, seconds after start, with only those few connected.Symptom 2 — mid-session teardown:
NativeMcpHostHandle has been destroyed, or the tool is missing from the tool list entirely.Expected behavior
Additional context
Environment
Frequency
Always, on this configuration. Every runtime I inspected showed an incomplete first generation. The mid-session teardown fired 3 times in 11 minutes in one runtime.
Extensions
Not tried with extensions/plugins disabled — but the failure is in MCP client startup and occurs before any agent turn, and two of the three config sources are plugin-independent. Happy to test a clean profile if useful.
Suggested labels
area:mcp(or nearest equivalent),type: Bug. I can't set labels as an outside contributor.Related issues
github/copilot-cli#4392— same teardown-and-rebuild mechanism, but reported for the orphaned process leak it causes. This report is about two different consequences of that rebuild: the incomplete first generation, and the cross-session trigger.github/copilot-cli#4542— workspace.mcp.jsondetected but not connected. Different layer (config plumbing vs. startup race), same user-visible outcome of "configured server is missing".github/app#2411(the exactNativeMcpHostHandle has been destroyedstring, open, no maintainer response),github/app#2139(stale binding,mcp_stale=true, observed_session_mcp=0),github/app#1012(spawn deadline too tight with many servers),github/app#505(no way to reload MCP).In-depth investigation
All timestamps UTC, from
--log-level all. Paths, usernames and internal server names redacted.Setup. MCP fleets are owned by the runtime process, which the app pools per working directory (
cli_pool: spawned new CLI process for pool working_directory=…). All sessions sharing acwdshare one MCP fleet; a newcwdcold-starts the whole fleet. Three live runtimes were observed, one percwd.An enterprise wrapper merges six config sources before handing the runtime a single merged set:
Eight of those are additionally fronted by localhost HTTP proxies on sequential ports.
Finding 1 — startup finalizes early and never retries.
On the runtime with the new
cwd,18servers were in the merged config, yet:Exactly three
Service initialized as clientevents, thenmcp_servers_loaded— 9 s after start. Grepping the whole runtime log confirms only 3 distinctserver_namevalues ever appear, and only 9 lines total from thecopilot_runtime::mcpmodule.Two checks that rule out the obvious explanations:
Not a policy block either — the registry explicitly logged
allow_all.Finding 2 — a "reload" is a full teardown, not a top-up.
On a second runtime, a reload looks like this:
Every live connection is closed and every server respawned. A tool call landing in that window is what produces
NativeMcpHostHandle has been destroyed— the handle is genuinely gone, which is why it is not retriable from the caller's side.Finding 3 — the trigger is
subscribe_mcp_status, from an unrelated session.Correlating the app's websocket messages against the runtime's MCP generations:
subscribe_mcp_statusmcp_servers_loadedThree subscriptions, three full teardown/respawn cycles, ~25 s lag, no other correlated events. The subscription came from a different session id than the one whose tools broke — the runtime is shared per
cwd, so the blast radius is every session on it.Cost. That single runtime performed 51
Service initialized as clientoperations across 4 generations in 11 minutes:Note generation 1 shows the same 3-server under-load as Finding 1 — the incomplete first generation appears to be systematic, and the only reason that runtime recovered is that an unrelated status subscription happened to force a rebuild later.
Why this is user-hostile in combination: the first generation is silently wrong, there is no retry, there is no user-facing reload (
github/app#505), and the only thing that fixes it is an unrelated side effect. Users experience this as "MCP randomly doesn't work", and the natural workaround — restart the app — often lands on another incomplete first generation.Suggested fixes, in rough order of value:
mcp_servers_loadeduntil every configured server has either connected or failed; surface failures.#4392).