v1.0.122
Closes 6 community-reported issues and 2 contributed PRs across MCP routing, Windows DEP0190, language-agnostic session extraction, conda Python, Pi lifecycle, and the .mcp.json architectural cleanup that has been bouncing between two contradictory forms since v1.0.108. Every fix is verified against actual source — Codex Rust references, Node.js deprecation docs, Pi extension API, and live runtime probes. No LLM hallucination.
What broke
#531 — .mcp.json ships ./start.mjs, breaking every fresh marketplace install
Traced via git log --follow .mcp.json: the source template flip-flopped 3 times between ${CLAUDE_PLUGIN_ROOT}/start.mjs (correct for end-users) and ./start.mjs (suppresses a dev-time warning for contributors). The most recent regression — commit aea633c (PR #253, 2026-04-13) — assumed "Plugin install path is unaffected — it still uses .claude-plugin/plugin.json". Aragorn2046's report proves that assumption wrong: Claude Code DOES read .mcp.json from the plugin cache, and the relative form throws MODULE_NOT_FOUND on every ctx_* tool because the MCP child inherits session CWD (not pluginRoot).
The fix is architectural — stop letting one file serve two contradictory roles:
git rm --cached .mcp.json(added to.gitignore— can never regress)- Removed from
package.jsonfiles[]— never ships in the npm tarball - New
.mcp.json.exampleis the canonical template; contributors copy it locally scripts/heal-installed-plugins.mjshealMcpJsonArgs()mirrors v1.0.119healPluginJsonMcpServers()exactly — heals legacy poisoned configs on every MCP boot viastart.mjsLayer 5b, on/ctx-upgradeviacli.tspost-bump assertion, and onnpm install -gviapostinstall.mjs- New
scripts/assert-asymmetric-drift.mjsis wired into thebuildchain: asserts.mcp.json.exampleand.claude-plugin/plugin.jsonmcpServers args agree on the placeholder. CI fails loud if the two ever diverge again.
Architectural review across all 15 adapters confirms zero of them read repo-root .mcp.json at runtime — every host uses its own platform-specific path (.cursor/mcp.json, .vscode/mcp.json, ~/.kiro/settings/mcp.json, ~/.omp/agent/mcp.json, .codex-plugin/mcp.json).
#529 — External MCP servers (slack, telegram, gdrive, notion, …) flood context unrouted
Across the 15 adapters that support hooks, only Bash, WebFetch, Read, Grep, Agent, and context-mode's own ctx_* tools fired the PreToolUse context-guidance nudge. Every other MCP namespace — Slack channel history, Telegram messages, Google Drive file content — landed in the model's context window unfiltered.
PR #532 (by @ousamabenyounes) fixed Claude Code with mcp__(?!plugin_context-mode_) negative lookahead. v1.0.122 extends the fix to the remaining 5 adapters that face the same problem with their own MCP naming conventions:
- codex:
mcp__(?!.*context-mode) - gemini-cli: same
mcp__shape, same exclusion - qwen-code: Gemini fork — same shape
- cursor:
MCP:(?!ctx_)— different prefix - kiro:
@(?!context-mode/)— namespace prefix
hooks/core/routing.mjs isExternalMcpTool() now recognises all three wire shapes (mcp__, MCP:, @<server>/). Per-adapter constants EXTERNAL_MCP_MATCHER_PATTERN are kept in sync via README-mirrored tests.
Five adapters that don't need the fix: vscode-copilot, jetbrains-copilot (empty matcher already catches all), opencode, openclaw, omp (plugin paradigm intercepts every call), pi (bridge-mediated, no PreToolUse). Zed and antigravity have no hook surface — MCP-only, can't fix at hook layer.
#537 — DEP0190 deprecation on Windows
Node.js 23.11/24 deprecates child_process.execFile/spawn with shell: true AND args-array form ("DEP0190" warning, becomes runtime error in v24+). Three call sites tripped this on every MCP boot on Windows:
runtime.tsrunnableExists()runtime.tsgetVersion()executor.ts#spawn()(tsx/ts-node/elixir/bun shell paths)
PR #537 by @frhino collapses the args array into a single quoted shell string when shell: true is needed. Windows-shell-safety architect verified arg sources at all 3 sites are internally controlled (hardcoded runtime names + temp-script paths via mkdtempSync) — zero user-controlled values. The quote predicate was widened from /\s/ to /[\s"&|<>^()%!]/ to cover cmd.exe metacharacters per the architect's recommendation.
tests/runtime.test.ts mocks updated to assert the new execSync('"<cmd>" --version') Windows path instead of the legacy execFileSync(cmd, ["--version"], { shell: true }) shape.
#533 — better-sqlite3 build fails when conda python3 is first in PATH
@rpickmans isolated this cleanly: PYTHON=/usr/bin/python3 npm install better-sqlite3@12.9.0 succeeds in 18s on macOS arm64 + Node 26; without the env override, conda's python3 wins, node-gyp picks it up via find-python.js PATH fallback, and the source build aborts.
scripts/heal-better-sqlite3.mjs now:
- Probes
/usr/bin/python3on darwin, filters paths matching/opt/anaconda*,/opt/miniconda*,*/conda/*,$CONDA_PREFIX/* - Builds child env with
PYTHON=<safe>ANDnpm_config_python=<safe>(node-gyp slot 1+2 beat slot 3 PATH perfind-python.jsresolution order) - Strips
CONDA_PREFIX/CONDA_DEFAULT_ENV/CONDA_EXE/CONDA_PROMPT_MODIFIER/CONDA_SHLVLfrom child env - Applies the same fix to both the
execFileSync(package-missing branch from #514) and the Layer AspawnSync(binding-missing branch) - Emits a stderr breadcrumb naming the chosen PYTHON when conda detected and overridden
- Returns reason
python-conda-blockedwhen no safe python is available
#534 — pi --help spawns orphaned MCP servers spinning at 100% CPU
Triage confirmed this is NOT a v1.0.121 regression — git log v1.0.120..v1.0.121 -- src/adapters/pi/ is empty. It is a pre-existing lifecycle defect (#311 and #388 were "fixed" without truly fixing). src/adapters/pi/extension.ts:613 unconditionally called bootstrapMCPTools() on every Pi invocation including short-lived pi --help runs. Pi exits in ~50ms; the spawned MCP child gets reparented to systemd --user, stdin pipe stays half-open, and the MCP SDK's StdioServerTransport loops on stdin reads → 100% CPU on a pegged core, ignores SIGTERM, multi-hour orphans accumulate.
Three layers of defense in v1.0.122:
- Prevention — short-circuit guard in
piExtension()readsprocess.argvfor--help|-h|--version|-v|help(verified againstrefs/platforms/oh-my-pi/packages/coding-agent/src/cli.tsshort-circuit set); on match, returns no-op handle without spawning - Fast recovery — bridge child polls
process.ppid !== originalPpidevery 1s; SIGKILLs self when orphaned (gated onCONTEXT_MODE_BRIDGE_DEPTH > 0to avoid #236 regressions) - Stdin EOF handler —
src/server.tslifecycle entry wiresprocess.stdin.on("end", () => process.exit(0))so parent pipe close terminates cleanly without relying on the MCP SDK loop
Integration test simulates pi --help end-to-end and asserts zero context-mode children survive parent exit.
#535 — Session extraction was English+Turkish only; the language coverage problem
PR #538 (an earlier attempt that landed CJK keyword arrays) was reverted because the architectural approach — hand-curated keyword arrays per language — cannot scale to the thousands of human languages. Adding Chinese needed 5 pattern arrays; the next user reports Arabic, Russian, Spanish, Hindi, Japanese, Indonesian; the maintenance burden grows linearly with every release.
Replacement: language-agnostic universal-rule detectors based on Unicode/structural patterns instead of word lists. Verified against parallel research into Aider, Cline, OpenHands session-resume architectures + architectural deep-dive.
- intent: investigate — Unicode question mark family
[??؟¿]/u(ASCII U+003F, fullwidth U+FF1F, Arabic U+061F, Spanish opening U+00BF). Greek;U+037E and Armenian՞excluded — they collide with ASCII. - intent: implement — codepoint length < 60, no question mark, at least one
\p{L}letter - decision — clause separator
[,;,;、،]/u+ codepoint length 15..500 + no question + letters present - role — first sentence (split on
.!。!\n) of length 8..120 with two space-separated\p{L}+tokens OR a\p{L}{6,}letter run (CJK fallback). Real-world persona prompts that append context paragraphs still match because we check the FIRST CLAUSE only. - blocker — programming-domain error markers (
Error:,Exception:,Traceback, JS/Java stack-frameat <id>(...):line:col) - blocker_resolved — Unicode checkmark family
[✓✔✅☑🎉]/uOR^(fixed|resolved)\s*:prefix
Safety net: new <recent_user_messages count="N"> block in snapshot.ts ships the last 3 raw user prompts verbatim (400-codepoint truncate each). Even if every structural detector misses, the next session's LLM sees the actual user messages and interprets them itself.
87 multilang tests pass across English, Turkish, Chinese, Arabic, Russian, Spanish, Hindi, Japanese × 5 categories. The new detectors meet or beat the old keyword recall for English/Turkish AND extend to the other 6 languages without per-language code.
Tests
- 3094 pass · 8 pre-existing baseline failures (OpenCodeAdapter config-paths — verified present on
origin/nextat v1.0.121, unrelated to this changeset) - 87 new multilang fixtures (
tests/session/extract-multilang.test.ts) - 22 new external-MCP routing tests across 5 adapters
- 10 new
.mcp.jsonheal slices + CI invariant - 14 new tests across Pi lifecycle, conda Python, Windows DEP0190
- Architectural invariant CI:
scripts/assert-asymmetric-drift.mjsblocks any future.mcp.json/plugin.jsondrift
Compatibility
15 adapters / 3 OS. The CJK keyword arrays from the reverted PR #538 do NOT ship — replaced with language-agnostic structural detection. Codex marketplace discovery (v1.0.121) continues to work. Windows DEP0190 warnings eliminated; CI now enforces the bundle-invariant guardrail on all 3 OS.
Upgrade
npm install -g context-mode@latest
# inside Claude Code:
/ctx-upgrade
# restart your sessionIf you were on a v1.0.108-v1.0.121 install with a poisoned .mcp.json in the plugin cache (every fresh marketplace install since #253), the boot heal in start.mjs Layer 5b auto-repairs the manifest on next MCP boot. No manual cleanup required.
Thanks
@Aragorn2046 for the staff-grade .mcp.json diagnosis (#531) — citing start.mjs:10, both prior issues (#411, #523), and even the #523 closing-comment connection that made the architectural fix obvious. @ousamabenyounes for the well-scoped #529 fix that unblocked the 15-adapter extension. @frhino for the Node.js docs verification + before/after stderr byte-count comparison on #537. @rpickmans for the cleanest reproducer of the cycle (#533) — PYTHON=/usr/bin/python3 isolating the variable end-to-end. @gebeer for the #534 reproduction with PID/PPID/CPU evidence. @happy9527 and @maxwell-orange for surfacing the language-coverage problem in #535 that drove the structural-detector rewrite. Every report this cycle came with file:line citations and decoded environment details — that's what makes mechanical fixes possible.