Skip to content

fix(vscode-ide-companion): resolve stop() with an MCP stream open - #29088

Open
chiruu12 wants to merge 1 commit into
google-gemini:mainfrom
chiruu12:fix/28785-ide-server-stop
Open

fix(vscode-ide-companion): resolve stop() with an MCP stream open#29088
chiruu12 wants to merge 1 commit into
google-gemini:mainfrom
chiruu12:fix/28785-ide-server-stop

Conversation

@chiruu12

Copy link
Copy Markdown

Fixes #28785.

IdeServer.stop() awaits this.server.close(). That callback fires only once every established connection has drained, and the MCP transport holds a long-lived streaming response on GET /mcp, so nothing drains. stop() never resolves and extension deactivate blocks behind it. VS Code then reports the extension as unresponsive on shutdown.

What changed

stop() now clears the keep-alive intervals, closes the open transports, and calls closeAllConnections() alongside close(). Closing the transports is what lets the stream drain; closeAllConnections() releases anything still holding a socket.

Keep-alive intervals are tracked in a Set that is written immediately after setInterval returns, not in onsessioninitialized. A transport whose handshake never completes is in neither transports nor any session map, so an interval registered at initialization time would leave that timer running against a server that is already gone.

The missedPings >= 3 branch has always logged Closing connection and cleaning up interval, but only cleared the interval. It now closes the transport too, which is what the message says and what drops the session.

What I did not change

The missedPings = 0 reset on success. The issue I filed lists it, and on reflection consecutive-miss counting is a reasonable reading of "missed pings" rather than a bug. Changing the threshold semantics is a policy call, it is what took the previous attempt at this out of scope, and the leak it was worried about is closed by stop() clearing every interval regardless.

On the test, and what it does not prove

The raw mechanism does reproduce on this Node. A plain server holding one streaming response, then close():

stream open; calling server.close()
close callback NEVER fired -> bug reproduces on node v24.10.0

The added test opens a real session, holds a real GET /mcp stream, and asserts stop() resolves. Being straight about it: that assertion alone passes with or without this change in the vitest harness, so on its own it is a regression guard, not a reproduction.

What makes it fail-first is the second assertion, that stop() logs Session closed: <id>. That only happens if stop() actually closes the transport, and onclose is what clears the keep-alive interval and drops the session. Reverting stop() to its previous body and keeping the test fails on exactly that line. An idle socket does not reproduce anything either, since close() destroys those, which is why the test does the full handshake.

Gates

npm run lint and npm run typecheck clean. vitest run src/ in packages/vscode-ide-companion: 41 passed, 1 skipped.

Two files, no API changes.

@chiruu12
chiruu12 requested a review from a team as a code owner August 25, 2026 21:01
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical issue where the IDE server would fail to shut down properly because long-lived MCP streaming connections prevented the server from closing. By explicitly managing keep-alive intervals and forcing the closure of active transports and sockets before server shutdown, the changes ensure that IdeServer.stop() resolves reliably, allowing the VS Code extension to deactivate without becoming unresponsive.

Highlights

  • Resolved shutdown hang: Updated IdeServer.stop() to explicitly close active transports and connections before awaiting the server shutdown, preventing the extension from hanging during deactivation due to un-drained MCP streams.
  • Improved keep-alive management: Introduced a keepAliveIntervals set to track all active timers, ensuring they are properly cleared during shutdown or when a session is closed, even if the transport handshake was never completed.
  • Enhanced connection cleanup: Updated the ping-missed logic to actually close the transport rather than just clearing the interval, ensuring sessions are fully dropped when connectivity is lost.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions Bot added the size/m A medium sized PR label Aug 25, 2026
@github-actions

Copy link
Copy Markdown

📊 PR Size: size/M

  • Lines changed: 108
  • Additions: +108
  • Deletions: -0
  • Files changed: 2

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request addresses an issue where stopping the IDE server would block indefinitely when an MCP stream is open. It introduces tracking of keep-alive intervals via a keepAliveIntervals set, ensures active transports are closed, and calls closeAllConnections() on the server during shutdown to allow connections to drain properly. A comprehensive integration test has also been added to verify this behavior. There are no review comments, and I have no additional feedback to provide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core Issues related to User Interface, OS Support, Core Functionality size/m A medium sized PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IdeServer.stop() never resolves while an MCP session is open

1 participant