perf: defer the mcp import to the mcp commands - #373
Draft
cloudsmith-iduffy wants to merge 3 commits into
Draft
Conversation
The mcp dependency costs ~1s to import. Import it in the initialise_mcp decorator so only the mcp commands pay that cost. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6 tasks
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6 tasks
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.
Description
Defer the
mcpimport so that only themcpcommands pay its ~1 s import cost.The CLI is now a credential helper for Docker, npm and pnpm. Package managers run the helper on every request, so startup time is the dominant cost. Before this change, every invocation — including
docker pullthroughdocker-credential-cloudsmith— imported the full MCP server stack (mcp,httpx,pydantic,starlette,uvicorn,jsonschema,anyio).Results
Measured on macOS (M-series), Python 3.14.7, editable install.
PYTHONDONTWRITEBYTECODE=1(this repo's.envrcdefault; end-user installs with warm.pyccaches are ~3x faster in both columns).cloudsmith --versionecho docker.cloudsmith.io | cloudsmith credential-helper docker get-X importtime)Timings are the median of 3+ runs of
/usr/bin/time -p.Methodology (how to reproduce)
Measure the wall time. Run each command 3+ times and take the median:
Attribute the time. CPython prints a per-module import tree with self and cumulative microseconds:
Baseline result:
cloudsmith_cli.cli.commandscosts 2117 ms of the 2548 ms total. Inside it,cli/decorators.pycosts 1925 ms, of which:cloudsmith_cli.core.mcp.server→ 961 ms (mcp638 ms, which pullspydantic,starlette,uvicorn,jsonschema,anyio; plushttpx242 ms)cloudsmith_cli.core.api.init→ 871 ms (thecloudsmith_apiSDK; addressed in a follow-up PR)Confirm the runtime is not the problem.
cProfilearoundmain(['credential-helper', 'docker', 'get'])shows the post-import runtime is only 0.22 s (mostly the OS keyring roundtrip). Imports dominate.Fix.
cli/decorators.pyimportedcore.mcp.serverat module level, and every command module importsdecorators. Move the import into theinitialise_mcpwrapper, which only themcpsubcommands execute.cli/commands/mcp.pyonly uses the names in type annotations, so it imports them undertyping.TYPE_CHECKINGwithfrom __future__ import annotations.Guard against regression. The new test
cli/tests/test_startup_imports.pyimports the CLI in a subprocess and asserts that nomcp/httpxmodule lands insys.modules. It was written first and failed with 106 heavy modules loaded.Type of Change
Performance: startup-time reduction, no behaviour change.
Additional Notes
mcp start,mcp list_tools,mcp list_groupsstill import the stack lazily and work as before (cloudsmith mcp --helpand the 28 mcp tests pass).Flame graphs
Probe:
cloudsmith --version. Icicle charts frompython -X importtime: parents above children, width = cumulative import time. Totals include the interpreter's ownsiteimports and vary a few ms between runs.Before:
After: