Repository navigation
fix(client): bound initialized handshake timeout - #3079
YingzuoLiu wants to merge 6 commits into
Conversation
🦋 Changeset detectedLatest commit: 1a45dc8 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: |
|
Read the diff and the two tests. The race is set up correctly (listener and timer both cleaned in
|
Document per-stage timeout semantics and verify the initialized stage receives a fresh budget. Preserve caller abort reasons instead of classifying cancellation as RequestTimeout. Assert transport closure on timeout, in-flight abort, and abort between handshake stages. Validation: client 1084 tests; core-internal 1836 tests; typecheck and lint passed for both packages.
Clarify that only the initialized stage preserves the caller abort reason; initialize retains request() wrapping for non-SdkError reasons. Compare both stages with the same Error object without asserting wrapper messages. Record that this boundary test should change alongside request() cancellation semantics. Validation: client vitest run (1085 passed), typecheck, lint, changeset formatting. Mutation check: restoring timeout wrapping in initialized makes the new boundary test fail; implementation restored.
|
@arhancanli Thanks for the review. Addressed the three points in d7a104f and 753157b:
Validation: all 1,085 client tests, typecheck, and lint pass. Temporarily restoring timeout wrapping in the Could you take another look when you have a chance? |
|
Re-read 753157b against the three points.
Two small things, neither blocking:
|
Move only the timeout error allocation into the timer callback so its stack originates there. Preserve its code, message, timeout data, and abort-before-reject order. Validation: client 1085 tests, typecheck and lint passed; focused handshake file rerun (9 passed). One-off stack probe confirmed the first frame is the timer callback; probe removed.
Summary
notifications/initializedPOST with its own per-stage timeout (options.timeout, or the default request timeout).initializeandinitializeddo not share a total connection budget.connect()promptly even if a custom transport ignores that signal.request()behavior: non-SdkErrorabort reasons are wrapped inRequestTimeout.request()cancellation semantics. The broader discussion is already tracked in AbortSignal cancellation throwsSdkErrorCode.RequestTimeoutinstead of a distinct abort error #2165.Fixes #3078
Tests
vitest run— 45 files, 1,085 tests passed; client typecheck and lint passed.