feat(launch): lazy-MCP loading with opt-in prune (profile + global)#90
Merged
Conversation
Prune a profile's MCP servers to what its skills actually need so unused tool
schemas don't burn always-on context. Interactive launches keep the existing
opt-out picker + remembered per-dir overrides; non-interactive launches stay
fail-open (keep all) unless CUE_PRUNE_MCPS opts in:
CUE_PRUNE_MCPS=auto drop unused PROFILE-declared MCPs (user-global MCPs
stay untouched — the existing tested invariant holds)
CUE_PRUNE_MCPS=all also drop unused GLOBAL servers present in the runtime
.claude.json (heavy ones a coding profile never calls);
a louder opt-in because it removes globally-set config
Precedence: --disable-mcp > remembered override > CUE_PRUNE_MCPS > keep-all.
disabledMcpIds (incl globals under =all) flow to the materializer, which evicts
exactly those keys from .claude.json and never touches an unnamed user MCP.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t hermetic Two CI failures vs main, both from entanglement — neither is the lazy-MCP feature: 1. runtime-materializer.ts had an unrelated proxy health-probe rewrite (TCP-connect → HTTP /health) swept in from branch WIP, but its matching test update lived in a file this PR doesn't carry. Reverted the probe to main's TCP-connect version (keeping all lazy-MCP changes: disabledMcpIds plumbing + syncMcpsIntoClaudeJson). Restores `import net`, drops the now unused http/https imports. 2. skill-dependencies.getNeeded.test.ts was an integration test against the live resources/skills submodule, asserting requires_mcps backfills not present on main's pinned submodule commit. Added an optional skillsRoot param to getNeededMcps/getSkillDependencies/findSkillContent (default unchanged) and rewrote the test to use hermetic temp fixtures covering every detection path (inline + block-sequence requires_mcps, implicit body ref, no-dep, unknown id). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e (review M-1) Review flagged that `--disable-mcp <id>` set reviewed=true, so the choice was written as a remembered per-dir override and silently stuck on every later launch until `--cue-pick-mcps`. The flag's doc reads one-shot, and a CI or debug run must not pollute a shared checkout with override files. Apply the disable for this launch only; persist a choice via the interactive picker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
What
Prune a profile's MCP servers down to what its skills actually need, so unused tool schemas don't burn always-on context every message. Pairs with the honest MCP token accounting from #88 (which can now prove the saving via
cue cost).Behavior
mcp-picker.ts) + remembered per-dir overrides (mcp-overrides.ts).CUE_PRUNE_MCPSopts in:CUE_PRUNE_MCPS=auto— drop unused profile-declared MCPs only (user-global MCPs stay untouched — preserves cue's existing invariant).CUE_PRUNE_MCPS=all— also drop unused global servers present in the runtime.claude.json(heavy ones a coding profile never calls). Louder opt-in: it removes globally-set config.--disable-mcp> remembered override >CUE_PRUNE_MCPS> keep-all.pin: trueon a profile MCP marks it agent-core (never pruned).disabledMcpIdsflow to the materializer, which evicts exactly those keys from.claude.jsonand never touches an unnamed user MCP.Merge note
This unions cleanly with main's newer
when:conditional-MCP feature: the MCP ref schema/types now carry bothwhen(activation gate) andpin(prune-exempt) — seeprofiles/schema.jsonMcpRefandprofiles/_types.ts.Test plan
bunx tsc --noEmit— clean (the when+pin union compiles)bun teston mcp-overrides / mcp-picker / skill-dependencies.getNeeded / runtime-materializer.mcp-prune / profile-loader — pass (CI checks outsubmodules: recursive)🤖 Generated with Claude Code