fix(server): close stdio transport on stdin close - #2473
Conversation
🦋 Changeset detectedLatest commit: 5576d73 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: |
|
Pushed a small test-fixture follow-up after the Node 22 CI failure. The failing job crashed because Local verification:
|
|
@jspahrsummers gentle maintainer ping on this one. I pushed a follow-up for the Node 22 teardown failure and revalidated the targeted integration test locally. Most checks are green; the remaining failing |
|
Updated the PR branch with current |
|
I rechecked the current failing Node 20 CI after the branch refresh. The remaining failure still looks unrelated to the stdio transport change here. Failing check: Evidence from the job log:
I tried to rerun the failed job from my side, but GitHub refused the rerun for this upstream workflow. Could a maintainer please rerun the failed Node 20 job when convenient? |
Problem
StdioServerTransportdid not treat stdin EOF as the server shutdown signal. When an MCP client closes/restarts and drops its end of the stdio pipe, a server with any other live handle can keep running indefinitely instead of closing the transport. That is the process-leak path reported in #2002.Root cause
The server stdio transport listened for stdin
dataanderror, but not forendorclose. Node can therefore signal that stdin is gone without the transport ever invoking its close path. The transport also needed to remove the new lifecycle listeners when closed so repeated close paths do not leave stale listeners behind.Focused change
StdioServerTransportwhen stdin emitscloseorend.Fixes #2002.
@jspahrsummers, tagging you for review because this is a narrow server stdio lifecycle fix for a P1/ready-for-work issue.
Verification
npx -y pnpm@10.26.1 --filter @modelcontextprotocol/server test -- packages/server/test/server/stdio.test.tsnpx -y pnpm@10.26.1 --filter @modelcontextprotocol/server lintcorepack pnpm --dir test/integration test -- processCleanup.test.ts(integration suite passed locally: 18 files / 353 tests)main:corepack pnpm --filter @modelcontextprotocol/server exec vitest run test/server/stdio.test.tsmain:corepack pnpm --dir test/integration exec vitest run test/processCleanup.test.ts test/server/cloudflareWorkers.test.tsgit diff --checkCurrent CI status
Most checks are green. The remaining failing check is
test (20), and the failure does not appear to be in the stdio transport path touched here: the job log showed the targeted stdio/server tests andtest/processCleanup.test.tspassing, with the only failure intest/server/cloudflareWorkers.test.ts > Cloudflare Workers compatibility (no nodejs_compat) > should handle MCP requestsdue toSdkHttpError: Error POSTing to endpoint: Error: Network connection lost.I attempted to rerun that failed upstream job, but GitHub would not allow rerunning it from my account.Risk / reviewer notes
Low-risk lifecycle fix for stdio servers. It only adds shutdown handling for stdin EOF/close, which matches the stdio transport expectation that servers should exit promptly when standard input is closed. The close path continues to be idempotent and removes listeners during cleanup.