Skip to content

fix(doctor): distinguish configuration from live health#4485

Merged
Hmbown merged 1 commit into
mainfrom
agent/091-issue4406
Jul 17, 2026
Merged

fix(doctor): distinguish configuration from live health#4485
Hmbown merged 1 commit into
mainfrom
agent/091-issue4406

Conversation

@Hmbown

@Hmbown Hmbown commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

Advance #4406 by making the diagnostic evidence boundary truthful without introducing a broad or side-effecting deep probe.

  • human MCP doctor output now says configuration only and explicitly leaves process, protocol, and backend/tool health unchecked
  • doctor JSON retains compatibility fields while adding probe_scope, live_health_checked, and per-stage checks including command availability
  • ordinary doctor no longer contacts self-hosted or loopback providers unless --probe-local is explicitly supplied, because that request may wake a desktop-managed daemon such as Ollama
  • setup inventory calls static MCP entries configured; live manager snapshots can say protocol_ready; neither is mislabeled as backend healthy
  • tools-only MCP servers remain valid without resources or templates

This intentionally references rather than closes #4406. Generic backend/tool invocation, Moraine backend-down versus fully healthy evidence, and inactive-route reporting remain follow-up work.

Refs #4406

Verification

  • doctor MCP tests: 11 passed
  • local-provider probe policy: 3 passed
  • setup MCP tests: 9 passed
  • setup detail regression: 1 passed
  • full codewhale-tui binary suite: 7,119 passed, 0 failed, 2 ignored
  • cargo build -p codewhale-tui --locked
  • manual built CLI: doctor --help and doctor --json evidence checked
  • formatting and diff checks

Only the known local macOS __eh_frame section too large linker warning appeared.

Keep MCP static validation, protocol readiness, and backend health as distinct evidence in human, JSON, and setup output. Require an explicit local-provider probe so ordinary diagnostics cannot wake a desktop-managed daemon.

Refs #4406

Signed-off-by: Hunter B <hmbown@gmail.com>
Copilot AI review requested due to automatic review settings July 17, 2026 18:54
@Hmbown Hmbown added this to the v0.9.1 milestone Jul 17, 2026
@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@claude

claude Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 5m 31s —— View job


Review of PR #4485

  • Gather context (read diff, changed files)
  • Understand the request (code review per custom instructions)
  • Review crates/tui/src/main.rs changes
  • Review crates/tui/src/tui/setup/mod.rs and tools_mcp.rs changes
  • Review doc changes
  • Check correctness, provider/model safety, reuse, tests, security
  • Post review feedback

Summary: This is a well-scoped, non-side-effecting change that does exactly what it says: it relabels doctor/setup output so "configuration valid" is never conflated with "process/protocol/backend healthy," and it correctly gates the local-provider live probe (--probe-local) behind an explicit flag rather than inferring anything from a provider prefix — that gating is done via ApiProvider::is_self_hosted() and base_url_uses_local_host(), both explicit config-derived signals, so it doesn't run afoul of the provider/model-prefix-inference concern. Docs (CONFIGURATION.md, RUNTIME_API.md) accurately reflect the new JSON shape and the codewhale mcp validate pointer is a real command. Test coverage for the new opt-in probe policy and JSON scoping looks solid, and the static_mcp_check_never_starts_the_configured_command test is a nice concrete guarantee of the "no side effects" claim.

Two findings posted inline:

  1. crates/tui/src/main.rs:6797-6889 — new command-availability check ignores server.env PATH overrides. doctor_mcp_command_status now resolves bare commands (node, npx, uvx, …) against the doctor process's own $PATH only. But McpServerConfig.env supports a PATH override that's allowlisted (child_env.rs) and actually applied at spawn time (mcp/stdio.rs:83-91) — a documented pattern for GUI-launched setups where the default PATH lacks nvm/uv binaries. A server that works today via that override will now get a spurious command not found Error from codewhale doctor. This is also why the PR had to rewrite 3 existing tests to use std::env::current_exe()/sh instead of "node"/"python"/"codewhale" — a signal that the new check is stricter than the old one in a way plain bare-command configs can trip on.
  2. crates/tui/src/tui/setup/tools_mcp.rs:338,387classify_config_server drifted out of alignment with doctor_check_mcp_server. Its doc comment still claims alignment, but only the doctor path gained the new PATH-availability check. The same server config can now show configured in the setup TUI while codewhale doctor reports command not found, which undercuts the PR's own goal of not mislabeling evidence.

Minor/non-blocking:

  • doctor_mcp_server_json's "enabled": server.enabled && !server.disabled (main.rs:6842) duplicates McpServerConfig::is_enabled() — could just call server.is_enabled().
  • No CHANGELOG.md "Unreleased" entry, unlike recent merged PRs (e.g. fix(tools): make apply_patch replacement explicit and compatible #4476/fix: don't let Vim Normal mode swallow Space for thinking block expan… #4477 style entries) — likely fine for a maintainer PR but flagging since the convention exists.
  • Could not run cargo build/cargo test in this sandboxed environment (Bash access is restricted to git/read-only operations here) — findings are from static reading of the diff plus the referenced call sites (child_env.rs, mcp/stdio.rs), not from executing the test suite. The PR description's own verification (11+3+9+1 passed, full 7,119-test suite) is the authoritative signal for compile/test correctness; if --allowedTools is expanded to include cargo, I can independently re-verify.

Comment thread crates/tui/src/main.rs
Comment on lines +6797 to +6829
fn doctor_mcp_command_status(server: &McpServerConfig) -> McpCommandDoctorStatus {
if server.url.is_some() {
return McpCommandDoctorStatus::NotApplicable;
}
let Some(cmd) = server.command.as_deref().map(str::trim) else {
return McpCommandDoctorStatus::NotChecked;
};
if cmd.is_empty() {
return McpCommandDoctorStatus::Missing;
}

let path = Path::new(cmd);
let is_absolute = path.is_absolute() || cmd.starts_with('/');
if is_absolute {
return if path.is_file() {
McpCommandDoctorStatus::Available
} else {
McpCommandDoctorStatus::Missing
};
}

if is_relative_stdio_path_arg(cmd) {
let Some(cwd) = server.cwd.as_deref() else {
return McpCommandDoctorStatus::NotChecked;
};
return if cwd.join(path).is_file() {
McpCommandDoctorStatus::Available
} else {
McpCommandDoctorStatus::Missing
};
}

if is_command_available(cmd) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

doctor_mcp_command_status resolves a bare command (e.g. node, npx, uvx) only against the doctor process's own std::env::var_os("PATH"). It never looks at server.env.

That's a real gap: McpServerConfig.env supports a PATH override (allowlisted through child_env::sanitized_mcp_env, see crates/tui/src/child_env.rs:143 and the explicit_child_env_values_win_over_parent_allowlist test), and StdioTransport::spawn (crates/tui/src/mcp/stdio.rs:83-91) applies the expanded server.env — including a PATH override — before spawning. This is a documented, commonly-recommended MCP pattern for GUI-launched apps where the default PATH doesn't include nvm/uv-managed binaries.

With this change, a server that adds "env": {"PATH": "..."} specifically to make its command resolvable will now get a command not found Error from doctor_check_mcp_server (line 6887-6889) even though it would actually launch fine — a false positive that didn't exist before this PR (previously only absolute-path commands were checked for existence at all).

Consider expanding+merging server.env's PATH (mirroring the allowlist/expansion StdioTransport::spawn already does) before doing the PATH search here, or downgrading a PATH-miss to a Warning rather than Error given this blind spot.

[Fix this →](https://claude.ai/code?q=In%20crates%2Ftui%2Fsrc%2Fmain.rs%2C%20doctor_mcp_command_status()%20(around%20line%206797)%20checks%20command%20availability%20using%20only%20std%3A%3Aenv%3A%3Avar_os(%22PATH%22)%20of%20the%20doctor%20process%2C%20ignoring%20any%20PATH%20override%20in%20server.env.%20StdioTransport%3A%3Aspawn%20in%20crates%2Ftui%2Fsrc%2Fmcp%2Fstdio.rs%20applies%20an%20expanded%2Fallowlisted%20server.env%20(including%20a%20PATH%20override)%20before%20spawning%2C%20so%20a%20server%20relying%20on%20a%20per-server%20PATH%20override%20to%20resolve%20its%20command%20will%20now%20incorrectly%20get%20a%20%27command%20not%20found%27%20doctor%20error.%20Fix%20by%20merging%2Fexpanding%20server.env%27s%20PATH%20override%20into%20the%20search%2C%20or%20downgrade%20a%20PATH-miss%20to%20a%20Warning%20instead%20of%20an%20Error.&repo=Hmbown/CodeWhale

match classify_config_server(server) {
InventoryStatus::Healthy => {
healthy += 1;
configured += 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

classify_config_server (line 387, doc comment: "Safe static probe aligned with doctor_check_mcp_server") is now out of sync with doctor_check_mcp_server/doctor_mcp_command_status in crates/tui/src/main.rs (6797-6889): the doctor path now also validates bare/relative commands against $PATH via is_command_available, but this classifier still only checks is_absolute && !path.exists() (line 400 area).

Net effect: for a stdio server whose command isn't on $PATH, codewhale doctor will now report command not found (Error), while this setup-TUI inventory still classifies the same server as configured/healthy — the two "configuration" evidence sources can disagree about the same server, which cuts against the PR's stated goal of consistent evidence labeling. Consider reusing doctor_mcp_command_status here too, or updating the doc comment to note the two checks are intentionally different in scope.

@Hmbown
Hmbown merged commit 3df8ea6 into main Jul 17, 2026
20 checks passed
@Hmbown
Hmbown deleted the agent/091-issue4406 branch July 17, 2026 19:15
pull Bot pushed a commit to TheTechOddBug/DeepSeek-TUI that referenced this pull request Jul 17, 2026
Follow up on the unresolved review findings from Hmbown#4485 by making doctor and setup resolve MCP stdio commands against the same expanded, sanitized environment used at spawn time.

This is a partial Hmbown#4406 health slice: it aligns static configured-command evidence but intentionally does not claim live process reachability, protocol initialization, backend tool health, or provider readiness.

Signed-off-by: Hunter B <hmbown@gmail.com>
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.

v0.9.2: Distinguish configured providers and MCP servers from live health

2 participants