-
Notifications
You must be signed in to change notification settings - Fork 1.6k
fix(core): properly cleanup MCP server subprocesses on exit #1285
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Thank you for your contribution! I've already tested it and everything is fine. We will merge this PR. |
|
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. |
|
I test this PR as well and it looks everything is fine. Thank you for your contribution. |
c1ed4dd to
1507749
Compare
|
@liqiongyu good catch — I routed |
3caffba to
3719104
Compare
|
@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! |
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:
ToolRegistry.stop()to stop all MCP clientsConfig.shutdown()as a unified cleanup entry point (idempotent, handles uninitialized state)gemini.tsximmediately after config creationThe 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 clientsstop()is idempotent (can be called multiple times safely)