fix(mcp): ${CLAUDE_PLUGIN_ROOT} substitution + cortex-doctor mcp diagnostic#22
Merged
Merged
Conversation
Discord user reported MCP server "✘ failed" with no actionable error.
Root cause: .mcp.json used a fragile Python -c one-liner that swallowed
launcher startup errors and depended on installed_plugins.json having a
specific key shape that breaks across plugin upgrades, custom marketplace
names, and missing python3 symlinks.
Fixes:
- .mcp.json now uses ${CLAUDE_PLUGIN_ROOT}/scripts/launcher.py — Anthropic's
documented plugin substitution variable, already used by every hook in
this repo (.claude/hooks/hooks.json). The launcher self-orients via
__file__ so manual installs continue to work (backward compatible).
- New `cortex-doctor mcp` subcommand: end-to-end MCP startup diagnostics.
Tells the user exactly which check failed, what command/path was tried,
and the actual error string — no more silent failures. Use --json for
Discord-paste-friendly output.
- 36 new tests (test_doctor_mcp.py + scripts/test_launcher_resolution.py)
cover happy path + every named failure mode (missing plugins.json,
missing key, stale installPath, missing launcher, broken launcher,
unset DATABASE_URL). Plus 6 contract tests guarding .mcp.json shape
against regression to the inline `-c` wrapper.
Backward compatible:
- `cortex-doctor` (no args) preserves legacy full-setup verification.
- launcher.py still self-orients via __file__ for manual installs.
Platform-agnostic: no Windows/Mac-specific code paths.
Bumps: pyproject.toml + .claude-plugin/marketplace.json → 3.15.2;
CHANGELOG entry added.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Completes the v3.15.2 release started in fbf9354 (which only captured .mcp.json + the contract test due to a staging hiccup): - mcp_server/doctor_mcp.py — new module implementing `cortex-doctor mcp` end-to-end MCP startup diagnostics. Each check reports the exact command/path attempted and the actual error string; supports --json for Discord-paste-friendly output. Covers: python interpreter on PATH, installed_plugins.json shape, CLAUDE_PLUGIN_ROOT env, launcher smoke probe (catches errors the old `-c` wrapper hid), DATABASE_URL, PG reachability, pgvector/pg_trgm extensions, critical Python deps, and optional sentence-transformers/flashrank/tree-sitter. - mcp_server/doctor.py — adds subcommand dispatch. `cortex-doctor` (no args) preserves legacy full-setup verification; `cortex-doctor mcp` routes to doctor_mcp.run_mcp. - tests_py/test_doctor_mcp.py + tests_py/scripts/test_launcher_resolution.py — 49 new tests covering happy path + every named failure mode (missing plugins.json, missing key, stale installPath, missing/broken launcher, unset/invalid DATABASE_URL, env-var-set/unset/invalid for launcher resolution). - pyproject.toml + .claude-plugin/marketplace.json → 3.15.2. - CHANGELOG.md — entry documenting the Discord report and the fix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two CI gates failing on PR #22 — both are latent / dep-drift issues, not introduced by the v3.15.2 fixes themselves. tree-sitter-language-pack 1.7.0+ changed Parser API; pip resolves >=0.24.0 to the latest (1.8.0 on CI right now), breaking 'parser.parse(...)' calls in tests_py/core/test_ast_*.py and tests_py/benchmarks/test_codebase_alteration.py with 'builtins.Parser has no attribute parse'. Pinning <1.7 keeps the contract that v3.15.2 was tested against. The latent break would also hit main on its next CI run; this commit protects both. Lint failure on tests_py/scripts/test_mcp_json_contract.py was a stale ruff format from the parallel-agent merge.
cdeust
added a commit
that referenced
this pull request
May 9, 2026
…5.2 tag (#23) Two issues bundled into one hotfix release. 1. python-multipart 0.0.26 → 0.0.27: patches a DoS vulnerability in MultipartParser header parsing (unbounded part headers cause CPU exhaustion). Affects every ASGI app in the FastMCP dep chain. The dependabot PR (#21) flagged this; closing it was wrong on my part. 2. v3.15.2 was tagged at 308ed41 (pre-PR-#22 merge) instead of 6b19ec4 because a stale uv.lock blocked the local fast-forward during release scripting. The broken v3.15.2 tag stays as a graveyard. v3.15.3 is the canonical version with both the MCP startup robustness work (PR #22 contents) AND this security bump.
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 the silent MCP startup failure reported on Discord. Two complementary fixes on one branch.
What was broken
.mcp.jsonused a Python-cone-liner that:~/.claude/plugins/installed_plugins.jsonand resolvedinstallPathdynamicallycortex@cortex-pluginsto exist-cwrapperThis broke for users with stale
installPath(e.g. installed_plugins.json points at v3.14.12 after upgrade to v3.15.1 — confirmed locally during this work), users on--plugin-dir/--plugin-urlflows, and users hitting any launcher import error (silent fail).Fix 1 — robust
.mcp.json(Liskov contract audit)Replaced the
-cwrapper with the documented Claude Code substitution variable${CLAUDE_PLUGIN_ROOT}. Verified against:.claude/hooks/hooks.jsonusing the same pattern in 13 production hooks.mcp.json(production-deployed)The old wrapper imposed a stronger precondition than the launch contract supports — Liskov violation. Fix returns to the documented contract.
Fix 2 —
cortex-doctor mcpend-to-end diagnostic (Feynman integrity audit)New
cortex-doctor mcpsubcommand that runs 8 checks (Python, installed_plugins.json, installPath, CLAUDE_PLUGIN_ROOT, launcher self-test, DATABASE_URL, PG reachable, critical Python deps) and prints the actual command tried + actual error + remediation for each failure. No silent fails. Output is paste-friendly for Discord.Tests
55 pass:
.mcp.jsoncontract tests (Liskov)cortex-doctor mcptests covering each failure path (Feynman)Backward compat
scripts/launcher.pyalready self-orients viaPath(__file__).resolve().parent.parentwhenCLAUDE_PLUGIN_ROOTis unset. Manual installs continue to work.Discord-debug-friendly
A user pasting
cortex-doctor mcpoutput gets the exact failing check + path + command + remediation — no further conversation needed.Bumped to v3.15.2.