Skip to content

perf: defer the mcp import to the mcp commands - #373

Draft
cloudsmith-iduffy wants to merge 3 commits into
masterfrom
perf/defer-mcp-import
Draft

perf: defer the mcp import to the mcp commands#373
cloudsmith-iduffy wants to merge 3 commits into
masterfrom
perf/defer-mcp-import

Conversation

@cloudsmith-iduffy

@cloudsmith-iduffy cloudsmith-iduffy commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Description

Defer the mcp import so that only the mcp commands 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 pull through docker-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 .envrc default; end-user installs with warm .pyc caches are ~3x faster in both columns).

Command Before After Delta
cloudsmith --version 2.72 s 0.38 s -86%
echo docker.cloudsmith.io | cloudsmith credential-helper docker get 2.94 s 0.47 s -84%
Total import time (-X importtime) 2548 ms 309 ms -88%

Timings are the median of 3+ runs of /usr/bin/time -p.

Methodology (how to reproduce)

  1. Measure the wall time. Run each command 3+ times and take the median:

    /usr/bin/time -p cloudsmith --version
    echo "docker.cloudsmith.io" | /usr/bin/time -p cloudsmith credential-helper docker get
  2. Attribute the time. CPython prints a per-module import tree with self and cumulative microseconds:

    python -X importtime -m cloudsmith_cli --version 2> importtime.txt
    sort -t'|' -k2 -n importtime.txt | tail -40   # sort by cumulative time

    Baseline result: cloudsmith_cli.cli.commands costs 2117 ms of the 2548 ms total. Inside it, cli/decorators.py costs 1925 ms, of which:

    • cloudsmith_cli.core.mcp.server961 ms (mcp 638 ms, which pulls pydantic, starlette, uvicorn, jsonschema, anyio; plus httpx 242 ms)
    • cloudsmith_cli.core.api.init871 ms (the cloudsmith_api SDK; addressed in a follow-up PR)
  3. Confirm the runtime is not the problem. cProfile around main(['credential-helper', 'docker', 'get']) shows the post-import runtime is only 0.22 s (mostly the OS keyring roundtrip). Imports dominate.

  4. Fix. cli/decorators.py imported core.mcp.server at module level, and every command module imports decorators. Move the import into the initialise_mcp wrapper, which only the mcp subcommands execute. cli/commands/mcp.py only uses the names in type annotations, so it imports them under typing.TYPE_CHECKING with from __future__ import annotations.

  5. Guard against regression. The new test cli/tests/test_startup_imports.py imports the CLI in a subprocess and asserts that no mcp/httpx module lands in sys.modules. It was written first and failed with 106 heavy modules loaded.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Refactoring
  • Other (please describe)

Performance: startup-time reduction, no behaviour change.

Additional Notes

  • mcp start, mcp list_tools, mcp list_groups still import the stack lazily and work as before (cloudsmith mcp --help and the 28 mcp tests pass).

Flame graphs

Probe: cloudsmith --version. Icicle charts from python -X importtime: parents above children, width = cumulative import time. Totals include the interpreter's own site imports and vary a few ms between runs.

Before:

before

After:

after

cloudsmith-iduffy and others added 2 commits August 21, 2026 23:22
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>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant