fix(client): clear resumption state on empty SSE event IDs - #2771
fix(client): clear resumption state on empty SSE event IDs#2771znnnnnnn-wil wants to merge 1 commit into
Conversation
🦋 Changeset detectedLatest commit: bf24200 The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@modelcontextprotocol/client
@modelcontextprotocol/codemod
@modelcontextprotocol/core
@modelcontextprotocol/server
@modelcontextprotocol/server-legacy
@modelcontextprotocol/express
@modelcontextprotocol/fastify
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
|
@felixweinberger Hey Felix, would you mind taking a look at this when you get a chance? It fixes the empty SSE id: resumption case from #2769. All checks are green. Thanks! |
|
I checked the ID-only case that the description leaves for a follow-up, using the locked import { EventSourceParserStream } from 'eventsource-parser/stream';
for (const reset of ['id:\ndata: \n\n', 'id:\n\n']) {
const input = `id: old\ndata: \n\n${reset}`;
for (const chunks of [[input], [...input]]) {
let token;
const source = new ReadableStream({
start(controller) {
for (const chunk of chunks) controller.enqueue(chunk);
controller.close();
}
});
for await (const event of source.pipeThrough(new EventSourceParserStream())) {
if (event.id !== undefined) token = event.id;
}
console.log(JSON.stringify({ reset, chunks: chunks.length, token }));
}
}The first two runs end with |
Summary
Fixes #2769.
An explicitly empty SSE
id:is a last-event-ID reset, not an absent ID. The truthyevent.idguard currently ignores that reset and reuses the previous token on reconnect.onresumptiontoken('')when the ID is explicitly empty.Last-Event-ID.The tests exercise the real SSE parser through the transport with mocked fetch and fake timers: GET header/callback behavior for empty versus absent IDs, and POST stream termination with an empty ID both with and without an earlier non-empty ID. The original empty-ID regression failed before the fix.
This follows the WHATWG SSE empty-ID semantics. ID-only blocks without a dispatched data event and retry-accounting changes in #2684 are outside this PR's scope.
Verification
On Windows, Node.js 22.19.0:
--end-of-line autobecause the checkout uses Windows line endings.git diff --check: passed.AI assistance was used for investigation, implementation, tests, and this PR description.