-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(core): add opt-in periodic ping for connection health monitoring #1717
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
Open
travisbreaks
wants to merge
2
commits into
modelcontextprotocol:main
Choose a base branch
from
travisbreaks:feat/periodic-ping
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| --- | ||
| "@modelcontextprotocol/core": minor | ||
| "@modelcontextprotocol/client": minor | ||
| "@modelcontextprotocol/server": minor | ||
| --- | ||
|
|
||
| feat: add opt-in periodic ping for connection health monitoring | ||
|
|
||
| Adds a `pingIntervalMs` option to `ProtocolOptions` that enables automatic | ||
| periodic pings to verify the remote side is still responsive. Per the MCP | ||
| specification, implementations SHOULD periodically issue pings to detect | ||
| connection health, with configurable frequency. | ||
|
|
||
| The feature is disabled by default. When enabled, pings begin after | ||
| initialization completes and stop automatically when the connection closes. | ||
| Failures are reported via the `onerror` callback without stopping the timer. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔴 Bug: Periodic pings are not restarted after client reconnection. The reconnection path (line 497-501) returns early when transport.sessionId is set, skipping the startPeriodicPing() call at line 546. Since _onclose() clears the ping timer during disconnection, connection health monitoring silently stops working after any reconnect cycle. Fix: add this.startPeriodicPing() before the return at line 501.
Extended reasoning...
What the bug is
In
Client.connect(), there are two code paths: the initial connection path (which performs the full MCP initialization handshake) and the reconnection path (whentransport.sessionIdis already set). ThestartPeriodicPing()call at line 546 is only reached via the initial connection path. The reconnection path at lines 497-501 returns early, skipping it entirely.How it manifests
When a client disconnects and reconnects (e.g., due to a transient network failure with an HTTP transport), the following sequence occurs:
_onclose()inProtocol, which callsstopPeriodicPing()— this clears_pingTimerviaclearInterval.client.connect(newTransport)wherenewTransport.sessionIdis already set (since it is a reconnection to an existing session).super.connect(transport)is called, setting up the new transport.transport.sessionId \!== undefinedevaluates totrue, so the method sets the protocol version and returns at line 501.startPeriodicPing()at line 546 is never reached._pingTimerwas cleared in step 1 and_pingIntervalMsis still set from construction,startPeriodicPing()would successfully start a new timer — but it is simply never called.Why existing code does not prevent it
The
startPeriodicPing()method has a guardif (this._pingTimer || \!this._pingIntervalMs)that prevents duplicate timers, but after_onclose()runs,_pingTimerisundefined, so the guard would allow the timer to be started. The problem is purely that the method is never invoked in the reconnection path. Neithersuper.connect()(i.e.,Protocol.connect()) nor any other part of the reconnection flow calls it.Impact
Any client that uses
pingIntervalMsfor connection health monitoring will silently lose that monitoring after the first reconnection. This is particularly problematic because reconnections are exactly the scenario where health monitoring is most valuable — the connection was already unstable once, and the user wants to know if it becomes unresponsive again. The client will believe pings are active (since_pingIntervalMsis still set), but no pings will be sent.Fix
Add
this.startPeriodicPing()to the reconnection branch inClient.connect(), just before thereturnstatement at line 501:The idempotency guard in
startPeriodicPing()ensures this is safe even if called multiple times.