Skip to content

fix: deliver progress notifications that share a read with their response - #3023

Open
Steeve-Crypto wants to merge 2 commits into
modelcontextprotocol:mainfrom
Steeve-Crypto:fix/progress-notification-same-read
Open

Steeve-Crypto wants to merge 2 commits into
modelcontextprotocol:mainfrom
Steeve-Crypto:fix/progress-notification-same-read

Conversation

@Steeve-Crypto

Copy link
Copy Markdown

Summary

Fixes #3018.

_onnotification deferred handlers with Promise.resolve(), while _onresponse runs synchronously and deletes the matching progress handler. When a progress notification and its response arrive in the same read/turn, the response cleared the handler before the deferred notification ran, so progress was dropped and onerror reported an unknown token.

This dispatches notification handlers synchronously (same posture as _onresponse). Synchronous throws and async rejections still go to _onerror.

Test plan

  • Added regression test: progress + response delivered in the same turn without an intervening microtask flush; progress callback is invoked and no unknown-token error is raised
  • pnpm test in packages/core-internal (1526 passed, including the new case)
  • Pre-push: typecheck, lint, and build all passed

AI disclosure

Used AI assistance to locate the race in _onnotification vs _onresponse, draft the sync dispatch change and regression test, and write this PR description. I reviewed the diff and ran the tests above myself.

…onse

_onnotification deferred handlers via Promise.resolve(), while _onresponse
runs synchronously and deletes the progress handler. When both messages
arrive in one turn, the response won the race and the progress was dropped.

Dispatch notification handlers synchronously (async rejections still go to
_onerror) and add a regression test for the same-turn case.

Fixes modelcontextprotocol#3018
@Steeve-Crypto
Steeve-Crypto requested a review from a team as a code owner October 14, 2026 14:18
@changeset-bot

changeset-bot Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 9af4566

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/@modelcontextprotocol/client@3023

@modelcontextprotocol/codemod

npm i https://pkg.pr.new/@modelcontextprotocol/codemod@3023

@modelcontextprotocol/core

npm i https://pkg.pr.new/@modelcontextprotocol/core@3023

@modelcontextprotocol/server

npm i https://pkg.pr.new/@modelcontextprotocol/server@3023

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/@modelcontextprotocol/server-legacy@3023

@modelcontextprotocol/express

npm i https://pkg.pr.new/@modelcontextprotocol/express@3023

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/@modelcontextprotocol/fastify@3023

@modelcontextprotocol/hono

npm i https://pkg.pr.new/@modelcontextprotocol/hono@3023

@modelcontextprotocol/node

npm i https://pkg.pr.new/@modelcontextprotocol/node@3023

commit: 9af4566

The sync notification dispatch in modelcontextprotocol#3023 delivers progress that shares a
read with its response, so the SSE progress scenarios should pass.
@0xamlab

0xamlab commented Oct 14, 2026

Copy link
Copy Markdown

Verified the fix locally. On main, applying only the new regression test fails with 1525 passed and 1 failed in packages/core-internal—the progress callback is never called. On this branch the full suite passes with 1526 passed. The synchronous dispatch in packages/core-internal/src/shared/protocol.ts:933 closes the race by handling the notification before _onresponse deletes the progress handler. The Promise.resolve() deferral was the culprit.

This branch has not been deployed

No deployments
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.

Progress notifications are dropped when they arrive in the same read as their request's response

2 participants