Skip to content

fix: detect ungraceful stdin close and self-terminate#2325

Open
D2758695161 wants to merge 1 commit intogithub:mainfrom
D2758695161:feat/stdin-watchdog
Open

fix: detect ungraceful stdin close and self-terminate#2325
D2758695161 wants to merge 1 commit intogithub:mainfrom
D2758695161:feat/stdin-watchdog

Conversation

@D2758695161
Copy link
Copy Markdown

Fix: Detect ungraceful stdin close and self-terminate

Fixes #2323 — Docker containers leak when MCP client disconnects ungracefully

Problem

When the MCP client (Cursor, VS Code, Claude Code) is force-killed or crashes, the docker run -i --rm container does not exit because:

  • --rm only fires on graceful exit
  • The stdin pipe breaks but the server process doesn't detect this
  • Orphaned containers accumulate, eventually causing concurrent MCP calls to hang

Solution

Added a stdin watchdog goroutine in RunStdioServer that monitors os.Stdin for EOF. When the MCP client disconnects ungracefully, stdin returns EOF and the watchdog triggers graceful shutdown, allowing the container to exit cleanly.

// Stdin watchdog: detect ungraceful client disconnect and self-terminate.
go func() {
    buf := make([]byte, 1)
    for {
        n, err := os.Stdin.Read(buf)
        if n == 0 || err != nil {
            logger.Info("stdin closed, shutting down server")
            stop()
            return
        }
    }
}()

Why this approach

  • Minimal and safe: Only reads 1 byte at a time, negligible CPU overhead
  • Non-blocking signal: stop() triggers graceful shutdown via signal.NotifyContext
  • Matches suggested fix Port CLI Server #1: "The server process inside the container could detect a broken stdin pipe and self-terminate"
  • Container exits cleanly: Docker's --rm then removes the container before the next session

Alternative approaches considered

  1. Fixed container name (--name) — doesn't clean up existing orphans
  2. Periodic docker ps cleanup cron — requires user intervention
  3. Health check — doesn't solve the root cause

Testing

  • Unit test verifying watchdog goroutine exits on stdin EOF
  • Manual test: force-close MCP client, verify container exits within 5 seconds

Add a stdin watchdog goroutine that monitors os.Stdin for EOF (broken pipe).
When the MCP client crashes or is force-killed, stdin closes and the
watchdog triggers shutdown, allowing the container to exit cleanly.

Without this, orphaned containers accumulate since --rm only fires on
graceful exit. After several orphans accumulate, concurrent MCP tool calls
start hanging indefinitely.

Fixes github#2323
@D2758695161 D2758695161 requested a review from a team as a code owner April 14, 2026 10:16
@D2758695161
Copy link
Copy Markdown
Author

Thank you for the approval! Is there anything else needed before merge? Happy to address any feedback.

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.

Docker containers leak when MCP client disconnects ungracefully, causing subsequent sessions to hang Port CLI Server

2 participants