Skip to content

fix(client): honor the connect signal during the server/discover probe - #3134

Open
JosephDoUrden wants to merge 1 commit into
modelcontextprotocol:mainfrom
JosephDoUrden:fix/connect-signal-probe
Open

JosephDoUrden wants to merge 1 commit into
modelcontextprotocol:mainfrom
JosephDoUrden:fix/connect-signal-probe

Conversation

@JosephDoUrden

Copy link
Copy Markdown
Contributor

Fixes #3133.

connect() documents that timeout and signal both apply to the handshake or probe, but only the timeout reached the server/discover probe. Aborting the signal did nothing until the probe timed out (60 s by default), and an already-aborted signal still sent the probe.

What changed in packages/client:

  • NegotiationDeps carries the connect signal. ProbeWindow.exchange returns early if it is already aborted, listens for abort before send(), and passes it to the transport as requestSignal, so an in-flight HTTP POST is torn down too.
  • negotiateEra checks the signal before opening the window, after every exchange whatever the reply was, and once more before handing the transport over, so a reply landing with the abort still rejects, no retry or corrective probe goes out after an abort, and a failed connect leaves the transport's start untouched.
  • negotiateStdioViaSibling checks it after the sibling is reaped, next to the existing caller-close() check, so an abort landing during disposal never starts the session transport.
  • client.ts only passes options.signal into the negotiation deps.
  • The error is the one the legacy path produces for the same signal: the SdkError reason as is, otherwise SdkError(RequestTimeout, String(reason)), mirroring Protocol.request. fix: distinguish AbortSignal cancellation from a real timeout #3046 wants to change that mapping; the helper here is one place to update with it.

Not in this PR: connect({ prior: { kind: 'modern', … }, signal }) still ignores the signal. There is no probe or handshake on that path, so it is arguably documented out, but it is a separate decision.

Tests: test/client/connectSignalProbe.test.ts, built on the cases in @Floriferous's repro branch (legacy control, auto, pin, pre-aborted, stdio sibling, real HTTP) plus same-tick reply-then-abort, abort between verdict and handover, abort during sibling disposal, abort inside send() under maxRetries, listener removal, requestSignal forwarding and the error mapping. On real stdio the rejection waits for the sibling to be reaped (up to about 1 s if it ignores SIGTERM); the changeset says so.

  • pnpm typecheck:all, pnpm lint:all
  • pnpm test:all: all packages green; one e2e timing scenario (protocol:timeout:max-total [sse 2025-11-25], fake timers) failed once under full load and passed on rerun, unrelated to this change

AI Disclosure

AI assistance (Claude) was used for issue research and test scaffolding. The implementation was written and reviewed by the author.

The signal passed to connect() never reached the version-negotiation
probe, so an aborted connect waited out the probe timeout and an
already-aborted signal still sent the probe. Thread it through
NegotiationDeps into ProbeWindow.exchange (early return, abort listener
before send, requestSignal on the transport), re-check it after every
exchange and before super.connect, and map the abort to the same error
the legacy path produces.

Fixes modelcontextprotocol#3133
@JosephDoUrden
JosephDoUrden requested a review from a team as a code owner October 30, 2026 20:44
@changeset-bot

changeset-bot Bot commented Oct 30, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b7111c5

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@modelcontextprotocol/client Patch
@modelcontextprotocol/codemod Patch
@modelcontextprotocol/core Patch
@modelcontextprotocol/server-legacy Patch
@modelcontextprotocol/server Patch
@modelcontextprotocol/core-internal Patch

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

@pkg-pr-new

pkg-pr-new Bot commented Oct 30, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

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

@modelcontextprotocol/codemod

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

@modelcontextprotocol/core

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

@modelcontextprotocol/server

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

@modelcontextprotocol/server-legacy

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

@modelcontextprotocol/express

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

@modelcontextprotocol/fastify

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

@modelcontextprotocol/hono

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

@modelcontextprotocol/node

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

commit: b7111c5

@claude claude Bot added the v2 Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes label Oct 31, 2026

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

v2 Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[v2] Client.connect(): signal is ignored during the server/discover probe

1 participant