Skip to content

Conversation

@stephentoub
Copy link
Contributor

The StreamableHttpHandler was flushing HTTP response headers before calling HandleGetRequestAsync, which sets _getHttpRequestStarted = true. This allowed a race where SendNotificationAsync could be called after the client received headers but before _getHttpRequestStarted was set, causing the notification to be silently dropped.

Move the flush inside HandleGetRequestAsync so it occurs after _getHttpRequestStarted is set, while still holding the lock. This ensures SendNotificationAsync will either:

  • Block on the lock until initialization completes, or
  • See _getHttpRequestStarted = true after acquiring the lock

The priming write path already flushes via SseEventWriter.WriteAsync, so the explicit flush is only needed in the non-priming case.

Fixes #1211 (hopefully)

The StreamableHttpHandler was flushing HTTP response headers before calling
HandleGetRequestAsync, which sets _getHttpRequestStarted = true. This allowed
a race where SendNotificationAsync could be called after the client received
headers but before _getHttpRequestStarted was set, causing the notification
to be silently dropped.

Move the flush inside HandleGetRequestAsync so it occurs after _getHttpRequestStarted
is set, while still holding the lock. This ensures SendNotificationAsync will either:
- Block on the lock until initialization completes, or
- See _getHttpRequestStarted = true after acquiring the lock

The priming write path already flushes via SseEventWriter.WriteAsync, so the
explicit flush is only needed in the non-priming case.
Copy link
Collaborator

@MackinnonBuck MackinnonBuck left a comment

Choose a reason for hiding this comment

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

We had a similar fix queued up in #1179, but we held off at the time due to potential layering concerns. I also could've done a better job explaining the motivation / why the reordering matters.

Given that we keep seeing flakiness on these tests, I’m 👍 on merging this as-is. cc @halter73

@stephentoub stephentoub merged commit f9dec13 into modelcontextprotocol:main Jan 30, 2026
11 of 13 checks passed
@stephentoub stephentoub deleted the fixflush branch January 30, 2026 04:04
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.

GetRequest_Receives_UnsolicitedNotifications is hanging in CI

2 participants