Skip to content

feat(client/stdio): opt-in process-tree teardown on close() - #2596

Open
siddheshbandgar wants to merge 1 commit into
modelcontextprotocol:mainfrom
siddheshbandgar:fix/stdio-kill-process-tree-2023
Open

feat(client/stdio): opt-in process-tree teardown on close()#2596
siddheshbandgar wants to merge 1 commit into
modelcontextprotocol:mainfrom
siddheshbandgar:fix/stdio-kill-process-tree-2023

Conversation

@siddheshbandgar

Copy link
Copy Markdown

Fixes #2023.

Problem

StdioClientTransport.close() signals only the direct child. When a server is launched through a wrapper — npx, uvx, python -m — the wrapper dies and the real server is orphaned. On Windows ChildProcess.kill() cannot terminate a tree at all.

Change

Adds an opt-in killProcessTree option to StdioServerParameters (Options A/B from the issue, combined):

  • POSIX: the child is spawned with detached: true so it leads its own process group, and close() signals -pid, reaching every descendant.
  • Windows: teardown goes through taskkill /pid <pid> /T /F.
  • Both paths fall back to the existing proc.kill(signal) if the group/taskkill call fails.

Default is false, so signal propagation and Ctrl-C behaviour are unchanged for anyone who doesn't opt in — this is deliberate, since detached: true by default would turn a crashed host into a permanent orphan factory (the trade-off called out as Option B in the issue).

Test

stdioKillProcessTree.test.ts spawns a wrapper that forks a long-lived grandchild, records its pid, closes the transport, and asserts the grandchild is reaped. Skipped on Windows in CI; the taskkill path was verified manually.

Happy to change the default, or move to Option C (a consumer-supplied cleanup hook), if you'd prefer a different direction.

A changeset is included (client: minor).

StdioClientTransport.close() signals only the direct child, so servers launched
through a wrapper (npx/uvx/python -m) leave the real server orphaned; on Windows
ChildProcess.kill() cannot terminate a tree at all.

Add an opt-in `killProcessTree` option: on POSIX the child leads its own process
group (detached) and close() signals the group; on Windows teardown goes through
`taskkill /T /F`. Both fall back to the plain kill. Defaults to false.

Fixes modelcontextprotocol#2023
@siddheshbandgar
siddheshbandgar requested a review from a team as a code owner July 31, 2026 17:12
@changeset-bot

changeset-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cb22c83

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

This PR includes changesets to release 10 packages
Name Type
@modelcontextprotocol/client Minor
@modelcontextprotocol/core Minor
@modelcontextprotocol/server Minor
@modelcontextprotocol/server-legacy Minor
@modelcontextprotocol/codemod Minor
@modelcontextprotocol/core-internal Patch
@modelcontextprotocol/express Major
@modelcontextprotocol/fastify Major
@modelcontextprotocol/hono Major
@modelcontextprotocol/node Major

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 Jul 31, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

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

@modelcontextprotocol/codemod

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

@modelcontextprotocol/core

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

@modelcontextprotocol/server

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

@modelcontextprotocol/server-legacy

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

@modelcontextprotocol/express

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

@modelcontextprotocol/fastify

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

@modelcontextprotocol/hono

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

@modelcontextprotocol/node

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

commit: cb22c83

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

StdioClientTransport.close() does not kill the process tree, leaving orphan processes

1 participant