perf(startup): lazy-import SSE/HTTP transport stacks in CLI actions - #408
Merged
Conversation
stdio mode (every claude mcp add config) no longer parses and instantiates express + the SDK's streamableHttp/sse/express modules it never uses; the sse/http command modules are imported inside their commander actions when the subcommand actually runs (issue #400). The handler re-exports from index.ts are dropped (no consumers; tests import the command modules directly), and an import-graph test locks the laziness in. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #400
stdio mode — every
claude mcp addconfig — statically imported all three transport command modules, dragging express and the SDK'sstreamableHttp/sse/expressstacks (body-parser, qs, send, …) into the module graph of a server that never uses them.Change
src/index.ts: the SSE and HTTP command modules are now imported withawait import()inside their commander action closures, so they only load when that subcommand actually runs (same pattern ascheck-rust-binary). The stdio command module stays static — it has no HTTP dependencies and is the default path.handleSSECommand/handleHttpCommandre-exports fromindex.tsare dropped. No consumers existed: tests and tooling import the command modules directly.src/cli/stdio-command.ts:StdioOptionsbecomes animport type(it's a type-only use).Laziness is locked in by a test
tests/unit/index-lazy-imports.test.tsmocksexpresswith a factory that flips a flag on evaluation, imports the entry point, and asserts the flag stayed false. Before this change the test fails (express evaluates on import); after, it passes.vi.mockintercepts dynamic imports too, so the existingindex.test.tshandler-closure assertions keep working unchanged.Measured (mem-bench, this machine, Windows)
node scripts/mem-bench.mjs --target dist --scenario idle— median RSS atafter-initialize, 5 trials:2014358c)−11.1 MB (−10.2%) idle stdio footprint.
Note: in the docker/npx single-file bundles the bytes still ship, but the express module graph is no longer parsed/executed on stdio startup — the win is parse/execute + heap, as scoped in the issue.
Verification
npm testgreen (259 files / 3947 tests),npm run lintcleantests/unit/index.test.ts+tests/unit/cli/*pass unchanged🤖 Generated with Claude Code