| 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.
- 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 minute.
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).
@modelcontextprotocol/sdk1.21.0 is the latest release pulled into mcporter.- Open SDK issues related to stdio shutdown:
We keep a local checkout of the SDK under ~/Projects/typescript-sdk/ so we can
diff against upstream and craft repros/patches quickly. Any mcporter-specific
workarounds live in src/sdk-patches.ts until the upstream fixes land.