fix: async onclose, stdin EOF detection, SIGTERM in examples#1814
Open
MayCXC wants to merge 1 commit intomodelcontextprotocol:mainfrom
Open
fix: async onclose, stdin EOF detection, SIGTERM in examples#1814MayCXC wants to merge 1 commit intomodelcontextprotocol:mainfrom
MayCXC wants to merge 1 commit intomodelcontextprotocol:mainfrom
Conversation
🦋 Changeset detectedLatest commit: 3f70c00 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 |
5 tasks
8cff2e5 to
5559b99
Compare
@modelcontextprotocol/client
@modelcontextprotocol/server
@modelcontextprotocol/express
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
5559b99 to
3d3234b
Compare
Three related improvements to server lifecycle handling: 1. Allow async onclose callbacks on Transport and Protocol. MCP servers that hold external resources (browser sessions, database connections) need to await cleanup before the process exits. The onclose signature changes from `() => void` to `() => void | Promise<void>`, matching the existing pattern used by onsessionclosed in StreamableHTTPServerTransport. All transports and Protocol._onclose now await the callback. 2. Close StdioServerTransport when stdin ends. The transport listened for data and error but not EOF. When the MCP client disconnects, the transport stays open and onclose never fires. This is especially visible with containerized servers using docker run with automatic removal: without onclose the server never exits and the container accumulates. 3. Add SIGTERM handlers alongside SIGINT in all examples. MCP servers run as background processes spawned by clients, not interactively. SIGTERM is what container runtimes and process managers send to stop a process.
3d3234b to
3f70c00
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three related improvements to server lifecycle handling.
1. Allow async
onclosecallbacksMCP servers that hold external resources (browser sessions, database connections) need to await cleanup before the process exits.
oncloseis the only transport/protocol callback called from an awaitable context (transport.close()is async, awaited byserver.close()). The other callbacks (onmessage,onerror) fire from event emitters that cannot await.The
onclosesignature changes from() => voidto() => void | Promise<void>, matching the existing pattern used byonsessionclosedinStreamableHTTPServerTransport. All transports andProtocol._onclosenow await the callback.Changed files:
Transportinterface,Protocol,StdioServerTransport,StreamableHTTPServerTransport,StdioClientTransport,WebSocketClientTransport,StreamableHTTPClientTransport,SSEClientTransport,InMemoryTransport, and mock transports in tests.2. Close
StdioServerTransportwhen stdin endsThe transport listened for
dataanderroron stdin but not EOF. When the MCP client disconnects (closing stdin), the transport stays open andonclosenever fires. This prevents servers from cleaning up resources.This is especially visible with containerized MCP servers using
docker run --rm: withoutonclose, the server process never exits, the container never stops, and containers accumulate on each client reconnect.3. Add SIGTERM handlers in examples
All 10 examples only handle SIGINT (Ctrl+C). MCP servers run as background processes spawned by clients, not interactively. SIGTERM is what container runtimes and process managers send to stop a process. Added SIGTERM handlers alongside SIGINT in all examples.
Test plan
should close when stdin ends(push null to stdin, verifyonclosefires)should await async onclose callback(async cleanup completes beforeclose()resolves)