claude -p with --output-format text hangs after completing — MCP servers not cleaned up
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=10000was 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:
- All MCP server child processes should be terminated (SIGTERM → SIGKILL)
- Any stuck Bash tool child processes should be cleaned up
- The process should exit and flush stdout (so
--output-format textactually writes the response) CLAUDE_CODE_EXIT_AFTER_STOP_DELAYshould force-kill child processes if they don't exit within the delay
Questions
- Does
-pmode have explicit MCP server cleanup on completion? - Is
CLAUDE_CODE_EXIT_AFTER_STOP_DELAYsupposed to handle MCP server shutdown, or only the main process? - Would
--output-format stream-jsonavoid this issue by streaming output incrementally rather than buffering until exit?
Workaround
External timeout wrapper: timeout 3600 claude -p ...
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗