Skip to content

fix(core): properly cleanup MCP server subprocesses on exit - #1285

Merged
tanzhenxin merged 6 commits into
QwenLM:mainfrom
tt-a1i:fix/mcp-subprocess-cleanup
Feb 3, 2026
Merged

fix(core): properly cleanup MCP server subprocesses on exit#1285
tanzhenxin merged 6 commits into
QwenLM:mainfrom
tt-a1i:fix/mcp-subprocess-cleanup

Conversation

@tt-a1i

@tt-a1i tt-a1i commented Dec 17, 2025

Copy link
Copy Markdown
Contributor

Summary

Fixes #1213

MCP server subprocesses were not being terminated when qwen-code exits, causing orphaned processes to accumulate. Each startup/exit cycle left behind zombie processes still holding WS ports.

Root cause: mcpClientManager.stop() was never called during the exit cleanup flow.

Changes:

  • Add ToolRegistry.stop() to stop all MCP clients
  • Add Config.shutdown() as a unified cleanup entry point (idempotent, handles uninitialized state)
  • Register cleanup in gemini.tsx immediately after config creation

The cleanup now covers all execution modes (interactive, non-interactive, stream-json) since it's registered at the CLI entry point before any mode-specific branching.

Test

Added unit tests for:

  • stop() disconnects all connected MCP clients
  • stop() is idempotent (can be called multiple times safely)

@BlockHand

Copy link
Copy Markdown
Contributor

Thank you for your contribution! I've already tested it and everything is fine. We will merge this PR.

@liqiongyu

Copy link
Copy Markdown
Contributor

Thanks for the fix — wiring cleanup into the CLI exit flow via Config.shutdown looks solid.

One small potential gap: ToolRegistry.discoverToolsForServer() currently uses connectAndDiscover() (not McpClientManager/McpClient), so any client/transport started via that path won’t be tracked by mcpClientManager.stop() and thus won’t be closed by ToolRegistry.stop()/Config.shutdown. Today this path is used by /mcp auth and likely targets network servers, but if a command-based server ever goes through this flow it could still leave a subprocess around until process exit.

Might be worth a follow-up to route single-server re-discovery through McpClientManager (or otherwise track/stop those connections) so shutdown is comprehensive.

@DennisYu07

Copy link
Copy Markdown
Collaborator

I test this PR as well and it looks everything is fine. Thank you for your contribution.

@tt-a1i

tt-a1i commented Jan 4, 2026

Copy link
Copy Markdown
Contributor Author

@liqiongyu good catch — I routed ToolRegistry.discoverToolsForServer() through McpClientManager (new discoverMcpToolsForServer()) so the connection is tracked and gets cleaned up on shutdown. added a few tests around the single-server rediscovery/replace path too. thanks!

宇溯 added 5 commits February 1, 2026 23:49
This reverts commit d7b003076e8a5f6b387b7313ecab0cd7ebea0997.
This reverts commit 61421e65872acebe20d88c2d40c9fea40fa3fefa.
@DennisYu07

Copy link
Copy Markdown
Collaborator

@tt-a1i I've rebased this PR and updated some unit tests. All tests are passing now. Could you please take another look when you have a moment? If everything looks good, we're ready to merge. Thank you!

@tanzhenxin
tanzhenxin merged commit a38a5ba into QwenLM:main Feb 3, 2026
14 checks passed
xaelistic pushed a commit to xaelistic/qwen-code that referenced this pull request Jun 7, 2026
fix(core): properly cleanup MCP server subprocesses on exit
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

退出 qwen 进程时没有退出启动的 mcp server 子进程

5 participants