Skip to content

Commit a971af4

Browse files
committed
fix(core): preserve related request id zero for debounce
1 parent 78fabea commit a971af4

3 files changed

Lines changed: 22 additions & 1 deletion

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@modelcontextprotocol/core': patch
3+
---
4+
5+
Preserve `relatedRequestId: 0` when deciding whether notifications can be debounced. Request id `0` is valid, so request-associated notifications with that id now bypass debounce like other related notifications.

packages/core-internal/src/shared/protocol.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1610,7 +1610,8 @@ export abstract class Protocol<ContextT extends BaseContext> {
16101610
const debouncedMethods = this._options?.debouncedNotificationMethods ?? [];
16111611
// A notification can only be debounced if it's in the list AND it's "simple"
16121612
// (i.e., has no parameters and no related request ID that could be lost).
1613-
const canDebounce = debouncedMethods.includes(notification.method) && !notification.params && !options?.relatedRequestId;
1613+
const canDebounce =
1614+
debouncedMethods.includes(notification.method) && !notification.params && options?.relatedRequestId === undefined;
16141615

16151616
if (canDebounce) {
16161617
// If a notification of this type is already scheduled, do nothing.

packages/core-internal/test/shared/protocol.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,21 @@ describe('protocol tests', () => {
656656
expect(sendSpy).toHaveBeenCalledWith(expect.any(Object), { relatedRequestId: 'req-2' });
657657
});
658658

659+
it('should NOT debounce a notification that has relatedRequestId 0', async () => {
660+
// ARRANGE
661+
protocol = new TestProtocolImpl({ debouncedNotificationMethods: ['test/debounced_with_options'] });
662+
await protocol.connect(transport);
663+
664+
// ACT
665+
const firstNotification = protocol.notification({ method: 'test/debounced_with_options' }, { relatedRequestId: 0 });
666+
const secondNotification = protocol.notification({ method: 'test/debounced_with_options' }, { relatedRequestId: 0 });
667+
await Promise.all([firstNotification, secondNotification]);
668+
669+
// ASSERT
670+
expect(sendSpy).toHaveBeenCalledTimes(2);
671+
expect(sendSpy).toHaveBeenCalledWith(expect.any(Object), { relatedRequestId: 0 });
672+
});
673+
659674
it('should clear pending debounced notifications on connection close', async () => {
660675
// ARRANGE
661676
protocol = new TestProtocolImpl({ debouncedNotificationMethods: ['test/debounced'] });

0 commit comments

Comments
 (0)