claude-code - 💡(How to fix) Fix claude -p with --output-format text hangs after completing — MCP servers not cleaned up [3 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#50777Fetched 2026-04-20 12:13:19
View on GitHub
Comments
3
Participants
3
Timeline
7
Reactions
0
Timeline (top)
commented ×3labeled ×3cross-referenced ×1

Root Cause

MCP server child processes spawned during the session stay alive after Claude finishes its work. Node.js won't exit until all child process stdio pipes close. The MCP server (in this case @upstash/context7-mcp via npx) keeps running indefinitely, holding the event loop open.

Additionally, a zsh -c child process from a Bash tool call that didn't complete cleanly also remained alive.

Fix Action

Workaround

External timeout wrapper: timeout 3600 claude -p ...

Code Example

Process: sleeping in epoll_wait, 0 API TCP connections, 7 threads
Response file (stdout redirect): 0 bytes (only written on process exit)
Conversation JSONL: contains the full completed response
Child processes still alive:
  - PID 4352: npm exec @upstash/context7-mcp  (MCP server)
  - PID 7270: zsh -c <bash tool call>          (stuck tool execution)
RAW_BUFFERClick to expand / collapse

Problem

When running claude -p "<prompt>" --output-format text --mcp-config mcp.json > output.txt, the process completes its work (writes full response to conversation JSONL) but the Node.js process never exits.

Root Cause

MCP server child processes spawned during the session stay alive after Claude finishes its work. Node.js won't exit until all child process stdio pipes close. The MCP server (in this case @upstash/context7-mcp via npx) keeps running indefinitely, holding the event loop open.

Additionally, a zsh -c child process from a Bash tool call that didn't complete cleanly also remained alive.

Observed State

Process: sleeping in epoll_wait, 0 API TCP connections, 7 threads
Response file (stdout redirect): 0 bytes (only written on process exit)
Conversation JSONL: contains the full completed response
Child processes still alive:
  - PID 4352: npm exec @upstash/context7-mcp  (MCP server)
  - PID 7270: zsh -c <bash tool call>          (stuck tool execution)

The process ran for 2+ hours in this state before being manually killed.

Environment

  • CLAUDE_CODE_EXIT_AFTER_STOP_DELAY=10000 was set but did not help — the MCP server keeps the process alive past the delay
  • Model: claude-opus-4-6
  • MCP config: single server (@upstash/context7-mcp)
  • Running inside a container (Podman) with --dangerously-skip-permissions

Expected Behavior

When claude -p completes its response in prompt mode:

  1. All MCP server child processes should be terminated (SIGTERM → SIGKILL)
  2. Any stuck Bash tool child processes should be cleaned up
  3. The process should exit and flush stdout (so --output-format text actually writes the response)
  4. CLAUDE_CODE_EXIT_AFTER_STOP_DELAY should force-kill child processes if they don't exit within the delay

Questions

  1. Does -p mode have explicit MCP server cleanup on completion?
  2. Is CLAUDE_CODE_EXIT_AFTER_STOP_DELAY supposed to handle MCP server shutdown, or only the main process?
  3. Would --output-format stream-json avoid this issue by streaming output incrementally rather than buffering until exit?

Workaround

External timeout wrapper: timeout 3600 claude -p ...

extent analysis

TL;DR

Implement a workaround using an external timeout wrapper, such as timeout 3600 claude -p ..., to force the process to exit after a specified time.

Guidance

  • Investigate if -p mode has explicit MCP server cleanup on completion to understand why child processes remain alive.
  • Verify if CLAUDE_CODE_EXIT_AFTER_STOP_DELAY is intended to handle MCP server shutdown or only the main process to clarify its role in process termination.
  • Consider using --output-format stream-json instead of --output-format text to stream output incrementally and potentially avoid buffering issues.
  • Explore internal implementation changes to ensure all child processes, including MCP servers and stuck Bash tool executions, are properly terminated upon completion.

Example

No code snippet is provided as the issue does not imply a specific code fix but rather a configuration or implementation adjustment.

Notes

The provided workaround using an external timeout may not be a permanent solution but can help mitigate the issue by forcing the process to exit after a specified time. Understanding the intended behavior of CLAUDE_CODE_EXIT_AFTER_STOP_DELAY and the cleanup mechanism in -p mode is crucial for a more robust fix.

Recommendation

Apply the workaround using an external timeout wrapper, such as timeout 3600 claude -p ..., as it provides a immediate solution to force the process to exit after a specified time, allowing for further investigation into the root cause and a more permanent fix.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix claude -p with --output-format text hangs after completing — MCP servers not cleaned up [3 comments, 3 participants]