Skip to content

fix(link-daintree): always register health_check + smoke-probe new slots#158

Merged
jobordu merged 3 commits into
mainfrom
fix/health-check-always-register
May 12, 2026
Merged

fix(link-daintree): always register health_check + smoke-probe new slots#158
jobordu merged 3 commits into
mainfrom
fix/health-check-always-register

Conversation

@jobordu
Copy link
Copy Markdown

@jobordu jobordu commented May 12, 2026

Summary

Closes a gap where /nf:link-daintree could write Daintree-imported slots to ~/.claude.json / providers.json but the resulting MCP slots silently failed to expose health_check, with no signal until the user manually ran /nf:mcp-status after a Claude Code restart.

  • bin/unified-mcp-server.mjs — drop the if (provider.health_check_args) registration gate for subprocess and ccr provider types. Execution already defaults to ['--version'] at line 603, so the gate was silently stripping the tool whenever providers.json was reconstructed without that field (which is what happens after install.js regenerates it from ~/.claude.json mcpServers).
  • commands/nf/link-daintree.md — add Step 2e: Smoke-probe newly-added slots. For each new MCP slot, spawn unified-mcp-server out-of-band with its exact command/args/env, send initialize + tools/list, and verify both identity and health_check are exposed. Per-slot pass/fail surfaces inline and aggregates into the Step 4 closing summary; Step 4 now tells the user explicitly to restart Claude Code (since MCP tools register only at session start).
  • .secrets.baseline — refresh line-number references for two pre-existing baselined hashes that shifted by 2 lines (486→484, 916→914) due to the gate removal. Hashes unchanged.

Supersedes #157 (which had bundled unrelated stale commits from feature/link-daintree-copilot).

Test plan

  • npm run test:ci — 264 pass, 1 skip, 0 fail
  • npm run check:assets — clean
  • npm run lint:isolation — clean
  • detect-secrets scan --baseline .secrets.baseline — clean against committed baseline
  • Manual: spawned unified-mcp-server with PROVIDER_SLOT=claude-1 (vanilla) and PROVIDER_SLOT=claude-z-ai (Daintree-imported) — health_check appears in both tools/list outputs
  • After merge + Claude Code restart: re-run /nf:link-daintree to re-add Daintree slots; verify Step 2e probe reports per slot and Step 4 surfaces the restart banner

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Health check tool now always enabled for all MCP providers
    • Added automatic validation after import to verify MCP server health and configuration
  • Documentation

    • Updated import process documentation with new validation procedures and conditional restart requirements

Review Change Stack

jobordu and others added 2 commits May 12, 2026 16:29
Why: When /nf:link-daintree fanned out Daintree presets into new MCP slots,
the resulting slots failed to expose `health_check` in tools/list because
unified-mcp-server gated tool registration on `provider.health_check_args`,
which is absent after install.js reconstructs providers.json from
~/.claude.json. Execution already defaults to ['--version'], so the gate
caused silent capability loss with no execution-time fallback symptom.

Changes:
- bin/unified-mcp-server.mjs: drop the health_check_args registration gate
  for subprocess and ccr provider types — health_check is now always
  registered when type warrants it.
- commands/nf/link-daintree.md: add Step 2e "Smoke-probe newly-added slots"
  that spawns unified-mcp-server out-of-band for each new slot, sends
  initialize + tools/list, and verifies identity + health_check are
  exposed. Per-slot pass/fail surfaces in the closing summary and Step 4
  now tells the user explicitly to restart Claude Code.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removing the `if (provider.health_check_args)` gate in bin/unified-mcp-server.mjs
(2 lines for subprocess type + 2 lines for ccr type, indentation collapsed)
shifted two pre-existing baselined hashes by 2 lines each. Hashes unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 12, 2026 15:30
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

Warning

Rate limit exceeded

@jobordu has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 52 minutes and 31 seconds before requesting another review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 39434341-f2e9-4d71-86a1-c8483928189c

📥 Commits

Reviewing files that changed from the base of the PR and between d89a909 and df13026.

📒 Files selected for processing (1)
  • commands/nf/link-daintree.md

Walkthrough

The PR unconditionally registers a health_check tool for MCP server providers and adds a post-import smoke probe to verify newly-added MCP slots expose required tools before session restart.

Changes

Health Check Registration and Validation

Layer / File(s) Summary
Health Check Tool Registration
bin/unified-mcp-server.mjs
For subprocess and ccr provider types, health_check is now unconditionally registered in the tool set with a NO_ARGS_SCHEMA input and documentation stating default ['--version'] execution when health_check_args is absent.
Smoke Probe and Completion Flow
commands/nf/link-daintree.md
Step 2e introduces a post-import smoke probe that spawns each newly-added MCP server out-of-band, issues JSON-RPC initialize plus tools/list requests, and verifies identity and health_check tools are present; Step 4 integrates probe results into a conditional "Slot probe" summary with /nf:mcp-repair guidance on failures; success criteria document the new probe behavior and restart requirement.
Baseline Metadata Sync
.secrets.baseline
Secret keyword detection line numbers for bin/unified-mcp-server.mjs are adjusted (decreased by 2) and the baseline generated_at timestamp is updated to reflect the registration logic changes.

Sequence Diagram

sequenceDiagram
  participant Step2e as Step 2e (Probe)
  participant Subprocess as Subprocess/CCR Provider
  participant Tools as JSON-RPC tools/list
  participant Step4 as Step 4 (Completion)
  Step2e->>Subprocess: Spawn newly-added MCP server
  Subprocess->>Tools: Handle JSON-RPC initialize
  Tools->>Subprocess: Return initialize response
  Subprocess->>Tools: Handle tools/list request
  Tools->>Subprocess: Return tools list with identity, health_check
  Subprocess->>Step2e: Respond with tool list
  Step2e->>Step2e: Verify identity and health_check present
  Step2e->>Step4: Store probe pass/fail counts
  Step4->>Step4: Render Slot probe summary (pass/fail/repair guidance)
  Step4->>Step4: Conditional restart instruction
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • nForma-AI/nForma#146: Adds tests validating slot and health-cache rendering, complementing the health_check registration and probe validation introduced here.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title precisely summarizes the main changes: removing the health_check registration gate and adding smoke-probe validation for newly-added MCP slots.
Description check ✅ Passed The description covers all required sections with comprehensive details. However, the Testing checklist items (macOS, Windows, Linux) are unchecked, and the post-merge manual verification test is marked incomplete.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/health-check-always-register

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
bin/unified-mcp-server.mjs (2)

772-774: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Remove the health_check_args gate to match unconditional registration.

The health_check tool is now always registered (lines 180-186), with documentation stating execution defaults to ['--version'] when health_check_args is missing. However, this dispatcher still gates execution with && slotProvider.health_check_args, causing the tool to return null (unknown tool) when called on slots without that field. The runSubprocessHealthCheck function already implements the default at line 601, so the gate should be removed.

🐛 Proposed fix to remove the gate
-    if (toolName === 'health_check' && slotProvider.health_check_args) {
+    if (toolName === 'health_check') {
       return runSubprocessHealthCheck(slotProvider);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bin/unified-mcp-server.mjs` around lines 772 - 774, The dispatcher currently
checks "if (toolName === 'health_check' && slotProvider.health_check_args)"
which prevents the always-registered "health_check" tool from executing when a
slot lacks health_check_args; remove the extra gate so the condition reads only
based on toolName (i.e., "if (toolName === 'health_check')") and let
runSubprocessHealthCheck(handle) apply its built-in default args; update the
conditional around toolName to call runSubprocessHealthCheck(slotProvider)
unconditionally when toolName === 'health_check'.

788-795: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Remove the health_check_args gate for CCR providers.

Same issue as the subprocess branch: health_check is now always registered (lines 206-211) but execution is still gated. This breaks the contract that the tool defaults to ['--version'] when health_check_args is missing.

🐛 Proposed fix to remove the gate
-    if (toolName === 'health_check' && slotProvider.health_check_args) {
+    if (toolName === 'health_check') {
       const result = await runSubprocessHealthCheck(slotProvider);
       // Override type field to 'ccr' for clarity
       try {
         const parsed = JSON.parse(result);
         return JSON.stringify({ ...parsed, type: 'ccr' });
       } catch (_) { return result; }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bin/unified-mcp-server.mjs` around lines 788 - 795, The health_check
execution is incorrectly gated by slotProvider.health_check_args; remove that
gate so CCR providers run the health_check branch whenever toolName ===
'health_check' (i.e., delete the "&& slotProvider.health_check_args" condition
around the block that calls runSubprocessHealthCheck), allowing
runSubprocessHealthCheck to use its default ['--version'] args when
health_check_args is absent; keep the JSON parse/override logic (parsing result
and forcing type: 'ccr') intact.
🧹 Nitpick comments (1)
commands/nf/link-daintree.md (1)

693-758: ⚡ Quick win

Probe validates registration but not execution.

The smoke probe correctly verifies that newly-added MCP slots expose identity and health_check in their tools/list response. However, it doesn't invoke the tools—the code only sends initialize and tools/list messages, never tools/call. This means a slot can pass the probe yet fail at runtime if the tool implementation has conditional logic that rejects execution (e.g., missing configuration parameters like health_check_args).

This scope is acceptable given the probe's stated goal (verify registration), but be aware of the gap between registration validation and execution testing.

Consider whether the probe should invoke health_check with an empty args object to verify end-to-end functionality, or add a note to the success criteria documenting that the probe checks registration only (not execution).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@commands/nf/link-daintree.md` around lines 693 - 758, The probe currently
only sends initialize and tools/list (initReq, listReq) and therefore verifies
registration but not execution; modify the probe (the PROBE_RESULT spawning
block that uses spawnSync and parses stdout) to also send a tools/call request
for health_check (e.g., add a callReq after listReq with jsonrpc id 3 and method
'tools/call' and params { name: 'health_check', args: {} }), parse the
corresponding JSON response (id === 3) and treat a non-success or missing
response as a failure for that slot; ensure the results array and downstream
counts (probePassedCount/probeFailedCount) reflect failures from execution
checks, and if you prefer not to execute, instead update the success message to
explicitly state the probe only validates registration (not execution) and link
to /nf:mcp-repair.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@bin/unified-mcp-server.mjs`:
- Around line 772-774: The dispatcher currently checks "if (toolName ===
'health_check' && slotProvider.health_check_args)" which prevents the
always-registered "health_check" tool from executing when a slot lacks
health_check_args; remove the extra gate so the condition reads only based on
toolName (i.e., "if (toolName === 'health_check')") and let
runSubprocessHealthCheck(handle) apply its built-in default args; update the
conditional around toolName to call runSubprocessHealthCheck(slotProvider)
unconditionally when toolName === 'health_check'.
- Around line 788-795: The health_check execution is incorrectly gated by
slotProvider.health_check_args; remove that gate so CCR providers run the
health_check branch whenever toolName === 'health_check' (i.e., delete the "&&
slotProvider.health_check_args" condition around the block that calls
runSubprocessHealthCheck), allowing runSubprocessHealthCheck to use its default
['--version'] args when health_check_args is absent; keep the JSON
parse/override logic (parsing result and forcing type: 'ccr') intact.

---

Nitpick comments:
In `@commands/nf/link-daintree.md`:
- Around line 693-758: The probe currently only sends initialize and tools/list
(initReq, listReq) and therefore verifies registration but not execution; modify
the probe (the PROBE_RESULT spawning block that uses spawnSync and parses
stdout) to also send a tools/call request for health_check (e.g., add a callReq
after listReq with jsonrpc id 3 and method 'tools/call' and params { name:
'health_check', args: {} }), parse the corresponding JSON response (id === 3)
and treat a non-success or missing response as a failure for that slot; ensure
the results array and downstream counts (probePassedCount/probeFailedCount)
reflect failures from execution checks, and if you prefer not to execute,
instead update the success message to explicitly state the probe only validates
registration (not execution) and link to /nf:mcp-repair.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3e22226c-68e2-4e40-94ab-858887769f94

📥 Commits

Reviewing files that changed from the base of the PR and between 3b4438f and d89a909.

📒 Files selected for processing (3)
  • .secrets.baseline
  • bin/unified-mcp-server.mjs
  • commands/nf/link-daintree.md

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to prevent Daintree-imported MCP slots from silently missing the health_check tool by (1) always registering health_check for subprocess and ccr slot mode in unified-mcp-server, and (2) adding a “smoke probe” step to /nf:link-daintree docs so newly-added slots are verified (via initialize + tools/list) before users restart Claude Code.

Changes:

  • Always register health_check in slot-mode tool listings for subprocess and ccr providers.
  • Add Step 2e documentation to out-of-band probe newly-added MCP slots for identity + health_check.
  • Refresh .secrets.baseline line-number references and generated_at timestamp.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 1 comment.

File Description
commands/nf/link-daintree.md Documents a new Step 2e smoke-probe workflow and updates the closing summary messaging.
bin/unified-mcp-server.mjs Removes conditional registration gates so health_check is always listed for subprocess/ccr slot mode.
.secrets.baseline Updates baseline metadata (line numbers + generated timestamp) after code shifts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread commands/nf/link-daintree.md Outdated
…tency

Copilot flagged the block-style `{if condition:}` placeholder as inconsistent
with the inline `{cond ? "..." : "..."}` pattern used throughout the rest of
the file. Rewriting inline so implementers read one consistent rendering form.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 12, 2026 15:38
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 3 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants