| summary | Checklist for diagnosing mcporter commands that never exit, using tmux and active-handle dumps. | |
|---|---|---|
| read_when |
|
When mcporter call prints a tool response but the process never exits, it
usually means Node still has active handles it is waiting on. The most common
culprit is a child MCP server process that keeps the stdio transport alive.
List timeouts close pending discovery connections, including uncached setup. runtime.close() aborts outstanding setup and waits for late results to be retired before finishing; superseded callers receive an error rather than a closed connection. Reported cleanup failures are retained alongside the list timeout.
- Run under tmux – launch the command inside tmux so you can inspect the pane even after Cursor or another agent times out.
- Enable hang diagnostics – set
MCPORTER_DEBUG_HANG=1when invoking the CLI. mcporter will dump the active handles/requests after the tool finishes and around theruntime.close()call. - Inspect the handle list – look for
ChildProcess (pid=…)entries. If a child remains, mcporter will now unref and force-kill it, but the debug list tells you exactly what was keeping the event loop alive. - Capture the pane output – run
tmux capture-pane -p -t <session> -S -200to save the diagnostic log for later review. - Retry with
--timeout– if the tool itself hangs, use--timeout <ms>orMCPORTER_CALL_TIMEOUTto fail fast while still gathering diagnostics. - Clamp OAuth waits – when the browser-based sign-in never completes,
run with
--oauth-timeout <ms>(orMCPORTER_OAUTH_TIMEOUT_MS) so the CLI tears down the pending flow instead of waiting the full 5 minutes.
tmux new-session -d -s mcphang \
'cd /path/to/project && \
MCPORTER_DEBUG_HANG=1 \
CHROME_DEVTOOLS_MCP_BROWSER_URL=http://127.0.0.1:9222 \
pnpm --dir "$HOME/projects/mcporter" exec tsx \
"$HOME/projects/mcporter/src/cli.ts" call \
--config "$PWD/config/mcporter.json" \
--root "$PWD" \
chrome-devtools --tool list_pages'
sleep 5
tmux capture-pane -p -t mcphang -S -200Sample diagnostic output (abridged):
[mcporter] [debug] after call (object result): 6 active handle(s), 0 request(s)
[mcporter] [debug] handle => ChildProcess (pid=78480)
[mcporter] [debug] beginning runtime.close()
[mcporter] [debug] after runtime.close: 6 active handle(s), 0 request(s)
[mcporter] [debug] forcibly killed child pid=78480 (runtime.finally)
This confirms the CLI response completed and that the lingering handle was a child process, which mcporter will now terminate during shutdown.
- The diagnostics only appear when
MCPORTER_DEBUG_HANG=1is set. - Killing residual children is best-effort; if you see repeated
kill-failedmessages, manually terminate the PID listed in the log. - Always keep tmux sessions tidy after debugging:
tmux kill-session -t <session>. - The CLI now forces
process.exit(0)after cleanup by default so Node never lingers on leaked handles. ExportMCPORTER_NO_FORCE_EXIT=1if you’re debugging and need the process to stay alive. - You can still set
MCPORTER_FORCE_EXIT=1explicitly when you want to force termination even withMCPORTER_NO_FORCE_EXITin play. - Stdio servers have their stderr output suppressed by default; set
MCPORTER_STDIO_LOGS=1to print their logs (they’re also surfaced whenever a child exits with a non-zero status).