Skip to content

Conversation

@rwjblue-glean
Copy link
Contributor

Motivation and Context

Implement KeepAlive features from the design doc

Closes #24

How Has This Been Tested?

Tested locally (unit tests).

Breaking Changes

N/A

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

I can send a follow-up PR adding an example with a StreamableHTTP server that uses the keepalive if that would be helpful.

@findleyr findleyr requested review from findleyr and jba June 25, 2025 19:34
// from being handled, and waiting for ongoing requests to return. Close then
// terminates the connection.
func (cs *ClientSession) Close() error {
if cs.keepaliveCancel != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like a user Close and the Close from the startKeepalive goroutine could race.
I don't think they can, but it's worth an explanation, in terms of happens-before relations, why no mutex is needed for keepaliveCancel. (If you don't understand what I'm talking about, I can explain further.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ya, I agree, I'm pretty sure that we do not need a mutex here, I've added the following comment explaining the flow and why it's safe:

	// Note: keepaliveCancel access is safe without a mutex because:
	// 1. keepaliveCancel is only written once during startKeepalive (happens-before user Close calls)
	// 2. context.CancelFunc is safe to call multiple times and from multiple goroutines
	// 3. The keepalive goroutine calls Close on ping failure, but this is safe since
	//    Close is idempotent and conn.Close() handles concurrent calls correctly

Please double check my reasoning though!

@rwjblue-glean rwjblue-glean changed the title feat: Implement KeepAlive for client and server mcp: Implement KeepAlive for client and server Jun 25, 2025
@rwjblue-glean rwjblue-glean mentioned this pull request Jun 25, 2025
// that sends ping messages at the specified interval.
func startKeepalive(session keepaliveSession, interval time.Duration, cancelPtr *context.CancelFunc) {
ctx, cancel := context.WithCancel(context.Background())
*cancelPtr = cancel
Copy link
Contributor

Choose a reason for hiding this comment

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

Add a comment saying that we must assign this before starting the goroutine, so we cannot return it.

(I first thought "why not just return the cancel func instead of passing in a pointer?" But it would be racy.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good suggestion, thank you! Updated with this comment:

	// Assign cancel function before starting goroutine to avoid race condition.
	// We cannot return it because the caller may need to cancel during the
	// window between goroutine scheduling and function return.

@rwjblue-glean rwjblue-glean requested a review from jba June 26, 2025 15:48
Copy link
Contributor

@samthanawalla samthanawalla left a comment

Choose a reason for hiding this comment

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

LGTM

@jba jba merged commit c47dbcd into modelcontextprotocol:main Jun 27, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Keepalive support

3 participants