Problem
#717 was fixed (#719) by narrowing the window in which DEBUG_MCP_SKIP_AUTO_START is set: the bundled CLI (packages/mcp-debugger/src/cli-entry.ts) sets the flag, dynamically imports src/index.js so the core's auto-start guard sees it, and restores the caller's value in finally before calling main(). That is correct today, but its correctness rests on a bundler detail — esbuild emits a lazy init_src() for the dynamic import — and nothing CI runs would catch the natural regression:
- Converting the
await import('../../../src/index.js') to a static import (the file already spells typeof import('../../../src/index.js') for the type) hoists src/index.ts above the bootstrap body; the guard sees the flag unset and auto-runs main(), then bootstrap() runs entrypoint.main() again — two Commander parses, two servers on one stdin. Verified with the repo's esbuild.
tests/integration/cli/bundled-bootstrap.test.ts runs --version, which process.exit()s inside the first main(), so its output and its env report are identical between the correct and the regressed ordering.
- The one assertion of the real behaviour,
tests/e2e/mcp-server-self-debug.test.ts, lives in the e2e project, which test:ci-coverage (--project unit --project integration) and every workflow except the container job skip.
The env var is an inheritance channel by design; narrowing when it is set does not close the channel (see the sibling leak in the exit-code shim ticket).
Proposed direction
Split main() out of src/index.ts into a side-effect-free module (e.g. src/main.ts) that cli-entry.ts imports directly. Then there is no flag, no window, and isMainModule's heuristics stop being load-bearing for the bundle. Until then, keep a CI-visible guard: an integration test that starts the bundled CLI in stdio mode, sends one initialize, and asserts exactly one response frame (a double main() answers twice).
Separately worth a decision: the pre-push hook prints "full e2e runs in CI", but .github/workflows/ci.yml runs only test:ci-coverage (unit + integration) and the Docker container e2e. Either run the non-Docker e2e project in a CI job or correct the hook's message and CLAUDE.md so nobody relies on it.
Raised in the review of #719.
Problem
#717 was fixed (#719) by narrowing the window in which
DEBUG_MCP_SKIP_AUTO_STARTis set: the bundled CLI (packages/mcp-debugger/src/cli-entry.ts) sets the flag, dynamically importssrc/index.jsso the core's auto-start guard sees it, and restores the caller's value infinallybefore callingmain(). That is correct today, but its correctness rests on a bundler detail — esbuild emits a lazyinit_src()for the dynamic import — and nothing CI runs would catch the natural regression:await import('../../../src/index.js')to a static import (the file already spellstypeof import('../../../src/index.js')for the type) hoistssrc/index.tsabove the bootstrap body; the guard sees the flag unset and auto-runsmain(), thenbootstrap()runsentrypoint.main()again — two Commander parses, two servers on one stdin. Verified with the repo's esbuild.tests/integration/cli/bundled-bootstrap.test.tsruns--version, whichprocess.exit()s inside the firstmain(), so its output and its env report are identical between the correct and the regressed ordering.tests/e2e/mcp-server-self-debug.test.ts, lives in the e2e project, whichtest:ci-coverage(--project unit --project integration) and every workflow except the container job skip.The env var is an inheritance channel by design; narrowing when it is set does not close the channel (see the sibling leak in the exit-code shim ticket).
Proposed direction
Split
main()out ofsrc/index.tsinto a side-effect-free module (e.g.src/main.ts) thatcli-entry.tsimports directly. Then there is no flag, no window, andisMainModule's heuristics stop being load-bearing for the bundle. Until then, keep a CI-visible guard: an integration test that starts the bundled CLI instdiomode, sends oneinitialize, and asserts exactly one response frame (a doublemain()answers twice).Separately worth a decision: the pre-push hook prints "full e2e runs in CI", but
.github/workflows/ci.ymlruns onlytest:ci-coverage(unit + integration) and the Docker container e2e. Either run the non-Docker e2e project in a CI job or correct the hook's message and CLAUDE.md so nobody relies on it.Raised in the review of #719.