Debugging Hanging mcporter Calls
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.
#Quick Checklist
- Run under tmux – launch the command inside tmux so you can inspect the
- Enable hang diagnostics – set
MCPORTER_DEBUG_HANG=1when invoking the - Inspect the handle list – look for
ChildProcess (pid=…)entries. If a - Capture the pane output – run `tmux capture-pane -p -t <session> -S
- Retry with
--timeout– if the tool itself hangs, use - Clamp OAuth waits – when the browser-based sign-in never completes,
pane even after Cursor or another agent times out.
CLI. mcporter will dump the active handles/requests after the tool finishes and around the runtime.close() call.
child remains, mcporter will now unref and force-kill it, but the debug list tells you exactly what was keeping the event loop alive.
-200` to save the diagnostic log for later review.
--timeout <ms> or MCPORTER_CALL_TIMEOUT to fail fast while still gathering diagnostics.
run with --oauth-timeout <ms> (or MCPORTER_OAUTH_TIMEOUT_MS) so the CLI tears down the pending flow instead of waiting the full 5 minutes.
#Example Session
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 -200
Sample 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.
#Notes
- The diagnostics only appear when
MCPORTER_DEBUG_HANG=1is set. - Killing residual children is best-effort; if you see repeated
kill-failed - Always keep tmux sessions tidy after debugging: `tmux kill-session -t
- The CLI now forces
process.exit(0)after cleanup by default so Node never - You can still set
MCPORTER_FORCE_EXIT=1explicitly when you want to force - Stdio servers have their stderr output suppressed by default; set
messages, manually terminate the PID listed in the log.
<session>`.
lingers on leaked handles. Export MCPORTER_NO_FORCE_EXIT=1 if you’re debugging and need the process to stay alive.
termination even with MCPORTER_NO_FORCE_EXIT in play.
MCPORTER_STDIO_LOGS=1 to print their logs (they’re also surfaced whenever a child exits with a non-zero status).