Skip to content

fix: handle cancellation for request ID 0 - #2654

Merged
KKonstantinov merged 4 commits into
modelcontextprotocol:mainfrom
pshah19:fix/cancellation-request-id-zero
Aug 16, 2026
Merged

KKonstantinov merged 4 commits into
modelcontextprotocol:mainfrom
pshah19:fix/cancellation-request-id-zero

fix(core): treat relatedRequestId 0 as present in the debounce guard

26bc4a2
Select commit
Loading
Failed to load commit list.
Claude / Claude Code Review completed Aug 14, 2026 in 7m 30s

Code review found 4 potential issues

Found 2 candidates, confirmed 4. See review comments for details.

Details

Severity Count
🔴 Important 0
🟡 Nit 2
🟣 Pre-existing 2
Severity File:Line Issue
🟣 Pre-existing packages/core-internal/src/shared/protocol.ts:724-732 Client cancels its own initialize request (spec MUST NOT), now acted on by SDK servers after the id-0 fix
🟣 Pre-existing packages/core-internal/src/shared/protocol.ts:724-732 Number() id coercion conflates string id with numeric id 0 in _onresponse/_onprogress

Annotations

Check notice on line 732 in packages/core-internal/src/shared/protocol.ts

See this annotation in the file changed.

@claude claude / Claude Code Review

Client cancels its own initialize request (spec MUST NOT), now acted on by SDK servers after the id-0 fix

Pre-existing spec-conformance issue this PR makes newly observable SDK-to-SDK: the outbound cancel closure in `_requestWithSchemaViaCodec` sends `notifications/cancelled` unconditionally for any in-flight request — including `initialize`, which the spec forbids cancelling ("A client MUST NOT attempt to cancel its initialize request", spec.types.2025-11-25.ts:243). Since initialize is id 0 on the plain legacy connect, the old truthiness guard accidentally swallowed the forbidden cancel on SDK ser

Check notice on line 732 in packages/core-internal/src/shared/protocol.ts

See this annotation in the file changed.

@claude claude / Claude Code Review

Number() id coercion conflates string id with numeric id 0 in _onresponse/_onprogress

Pre-existing (untouched by this PR, but it completes the invariant this PR's changeset states): `_onresponse` and `_onprogress` correlate inbound messages via `Number(response.id)` / `Number(progressToken)`, and `Number('') === 0` — so a response or progress notification carrying the legal string id `''` passes schema validation and silently settles (or resets the timeout / fires `onprogress` of) the pending request with numeric id `0`, which is the first request every peer sends. Fix by matchin