fix(mcp): guard unified-mcp-server spawn target against null CLI#193
Conversation
unified-mcp-server.mjs spawned providers via `provider.resolvedCli ?? provider.cli`, with no `mainTool` fallback and no guard. providers.json carries `cli: null` for mainTool-only slots, so when the startup resolution loop didn't populate `resolvedCli`, spawn received `null` and threw the opaque `TypeError: The "file" argument must be of type string. Received null` — the same failure class fixed in call-quorum-slot.cjs by PR #161/#164. Add a shared `resolveSpawnTarget(provider)` helper applying the same 3-tier fallback (resolvedCli -> cli -> mainTool) and throwing a named error when none resolve, so an unresolvable provider surfaces as a diagnosable `[spawn error: provider <name> has no resolvable CLI ...]` instead of an opaque TypeError. Both provider spawn sites and the deep health-check binary lookup now use the 3-tier fallback. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
WalkthroughThis PR centralizes subprocess executable resolution in ChangesSpawn target resolution and health check integration
🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR hardens bin/unified-mcp-server.mjs against spawning subprocess providers with a null executable by aligning its spawn-target resolution behavior with the already-hardened quorum dispatch path.
Changes:
- Introduces a shared
resolveSpawnTarget(provider)helper that resolves spawn executables viaresolvedCli → cli → mainTooland throws a descriptive error if nothing resolves. - Updates both subprocess spawn sites to use
resolveSpawnTarget(...)instead ofprovider.resolvedCli ?? provider.cli. - Updates deep health check binary lookup to apply the same 3-tier fallback and documents the fix in the changelog.
Reviewed changes
Copilot reviewed 1 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| CHANGELOG.md | Adds an Unreleased “Fixed” entry describing the spawn-target fallback/guard change. |
| bin/unified-mcp-server.mjs | Adds resolveSpawnTarget() and uses it at subprocess spawn sites; updates deep health check binary lookup fallback. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…sertion The resolveSpawnTarget helper added ~27 lines to unified-mcp-server.mjs, shifting two pre-existing baseline-tracked keyword matches (ANTHROPIC_API_KEY env-var name literals, already audited is_verified: true) from lines 527/957 to 554/985. Refresh the recorded line numbers so the detect-secrets baseline check passes. No new secrets — content of the flagged lines is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Problem
A user reported quorum running fail-open for an entire task: all 7 slots failed with
TypeError: The "file" argument must be of type string. Received null, leaving plan review and verification review with no external voters.Root cause for that incident was a stale local
~/.claude/nf-bin/install (resolved by re-syncing). But investigating it surfaced a latent version of the same bug still inunified-mcp-server.mjs.unified-mcp-server.mjsspawned providers via:— no
mainToolfallback, no guard.providers.jsoncarriescli: nullformainTool-only slots (the current schema after the providers.json consolidation, #167/#186). The startup resolution loop normally populatesresolvedCli, but it only runs fortype === 'subprocess'providers and skips any with nocli/mainTool. When it doesn't run,resolvedCli ?? cliisnull→spawn(null)→ opaqueTypeError.call-quorum-slot.cjswas already hardened against this exact case in #161 / #164 with a 3-tier fallback (resolvedCli → cli → mainTool) plus a guard throw.unified-mcp-server.mjsnever got the matching fix — this PR closes that asymmetry.Change
resolveSpawnTarget(provider)helper applying the same 3-tier fallback ascall-quorum-slot.cjs, throwing a named error when nothing resolves.runProvider,runSubprocessWithArgs) use the helper. The throw lands inside their existingtry/catch, so an unresolvable provider now surfaces as a diagnosable[spawn error: provider <name> has no resolvable CLI ...]instead of an opaqueTypeError.runDeepHealthCheck) uses the sameresolvedCli || cli || mainToolfallback somainTool-only slots resolve there too.No behavior change for correctly-resolved providers —
resolveSpawnTargetreturns exactly whatresolvedCli ?? clireturned before. The only difference is a clear, named failure when a provider has no resolvable CLI.Testing
node --check bin/unified-mcp-server.mjs— passes.initialize+tools/list+pingagainstPROVIDER_SLOT=codex-1) — boots, resolves CLIs, responds correctly.node --test test/resolve-cli-integration.test.cjs test/providers-path-consolidation.test.cjs— 43/45 pass. The 2 failures (providers.jsonempty) are pre-existing onmain(repo shipsbin/providers.jsonempty by design) and unrelated to this change.🤖 Generated with Claude Code
Summary by CodeRabbit