fix: pre-flight port check and graceful shutdown to prevent EADDRINUSE#1091
Open
btbowman wants to merge 1 commit intomodelcontextprotocol:mainfrom
Open
Conversation
…INUSE race condition Three bugs caused the Inspector to report "PORT IS IN USE" even when ports were free, and to leave ports in CLOSE_WAIT after Ctrl+C: 1. No pre-flight port availability check — both the client (client.js) and server (index.ts) attempted to bind directly without first verifying the port was free, leading to confusing EADDRINUSE errors from stale processes. 2. Missing process.exit(1) in client EADDRINUSE handler — the client's error handler logged the error but never exited, leaving the process hanging indefinitely. 3. No SIGINT/SIGTERM graceful shutdown — neither process registered signal handlers, so Ctrl+C left TCP sockets in CLOSE_WAIT state. The next launch would then fail with EADDRINUSE because the OS hadn't released the port. Fixes: - Add checkPort() pre-flight check to both client/bin/client.js and server/src/index.ts that tests port availability before attempting to bind - Add process.exit(1) to the client's EADDRINUSE error handler - Add SIGINT/SIGTERM handlers with server.close() for graceful shutdown in both client and server, with a 3-second force-exit timeout - Provide actionable error messages with lsof/kill instructions Resolves modelcontextprotocol#1090 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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
Fixes a race condition where the MCP Inspector reports "PORT IS IN USE" even when ports are confirmed free, and leaves ports in CLOSE_WAIT state after Ctrl+C — causing the error to persist across restarts.
Three bugs identified and fixed:
No pre-flight port availability check — Both the client (
client/bin/client.js) and proxy server (server/src/index.ts) attempted to bind directly viaserver.listen()without first verifying the port was free. When a stale process held the port, users received a confusingEADDRINUSEerror with no actionable guidance.Missing
process.exit(1)in client EADDRINUSE handler — The client's error handler logged the error but never calledprocess.exit(1), leaving the process hanging indefinitely. Users had to manually kill the process.No SIGINT/SIGTERM graceful shutdown — Neither the client nor server registered signal handlers, so
Ctrl+Cleft TCP sockets inCLOSE_WAITstate. The OS wouldn't release the port, causing the next launch to fail withEADDRINUSE— even thoughlsofshowed no process on the port.Changes
client/bin/client.jsimport net from "net"checkPort()function that attempts a test bind before the realserver.listen()portandhostdeclarations above the check so they're available for the pre-flight testprocess.exit(1)to the existing EADDRINUSE error handlerserver.close()for graceful shutdown (3s force-exit timeout)lsof/killinstructions in error messagesserver/src/index.tsimport net from "net"with other imports at the top of the filecheckPort()function (Promise<boolean>) beforeapp.listen()server.close()for graceful shutdown (3s force-exit timeout)How to test
npx @modelcontextprotocol/inspectorlsof/killfix instructions and exit cleanlyTest plan
CLIENT_PORT/SERVER_PORTenv var overrides still workResolves #1090
🤖 Generated with Claude Code