fix(mcp-health): deterministic MCP namespace attachment (Closes #689)
This commit is contained in:
+45
-3
@@ -104,6 +104,8 @@ def classify_namespace_probe(
|
||||
profile: str | None = None,
|
||||
configured: bool = True,
|
||||
probe_source: str | None = None,
|
||||
expected_parity_sha: str | None = None,
|
||||
bound_cohort: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Classify whether a required tool is callable through a live namespace.
|
||||
|
||||
@@ -135,20 +137,50 @@ def classify_namespace_probe(
|
||||
else:
|
||||
error_type = "namespace_call_failed"
|
||||
|
||||
# Extract cohort metadata
|
||||
cohort_meta = bound_cohort or probe.get("cohort") or probe.get("bound_cohort") or {}
|
||||
cohort_id = str(
|
||||
cohort_meta.get("cohort_id") or probe.get("cohort_id") or ""
|
||||
).strip() or None
|
||||
startup_sha = str(
|
||||
cohort_meta.get("startup_sha")
|
||||
or cohort_meta.get("git_head")
|
||||
or probe.get("startup_sha")
|
||||
or probe.get("git_head")
|
||||
or ""
|
||||
).strip() or None
|
||||
endpoint = str(
|
||||
cohort_meta.get("endpoint") or probe.get("endpoint") or ""
|
||||
).strip() or None
|
||||
config_fingerprint = str(
|
||||
cohort_meta.get("config_fingerprint") or probe.get("config_fingerprint") or ""
|
||||
).strip() or None
|
||||
|
||||
expected_sha = (expected_parity_sha or "").strip().lower() or None
|
||||
stale_cohort = False
|
||||
if expected_sha and startup_sha:
|
||||
if startup_sha.lower() != expected_sha:
|
||||
stale_cohort = True
|
||||
error_type = "stale_cohort_refused"
|
||||
|
||||
if not configured:
|
||||
error_type = "namespace_not_configured"
|
||||
elif registered is False:
|
||||
error_type = "tool_missing"
|
||||
elif not probe_result:
|
||||
error_type = "live_probe_missing"
|
||||
elif stale_cohort:
|
||||
error_type = "stale_cohort_refused"
|
||||
elif not probe_success and not error_type:
|
||||
error_type = "namespace_call_failed"
|
||||
|
||||
callable_live = bool(configured and probe_result and probe_success)
|
||||
callable_live = bool(configured and probe_result and probe_success and not stale_cohort)
|
||||
# Probe-path health (spawn or client). IDE-proven only for client path.
|
||||
healthy = bool(configured and registered is not False and callable_live)
|
||||
ide_namespace_proven = bool(healthy and source == PROBE_SOURCE_CLIENT)
|
||||
process_pid = process.get("pid") if isinstance(process, dict) else None
|
||||
process_pid = process.get("pid") if isinstance(process, dict) else (
|
||||
cohort_meta.get("pid") if isinstance(cohort_meta, dict) else None
|
||||
)
|
||||
profile_name = profile or (
|
||||
process.get("profile") if isinstance(process, dict) else None
|
||||
)
|
||||
@@ -161,7 +193,13 @@ def classify_namespace_probe(
|
||||
reasons.append(
|
||||
f"Required tool '{tool}' is not registered in namespace '{ns}'."
|
||||
)
|
||||
if error_type == "live_probe_missing":
|
||||
if error_type == "stale_cohort_refused":
|
||||
reasons.append(
|
||||
f"Bound cohort startup SHA '{startup_sha[:12] if startup_sha else 'unknown'}' "
|
||||
f"does not match expected parity SHA '{expected_sha[:12] if expected_sha else 'unknown'}' "
|
||||
"(stale cohort refused)."
|
||||
)
|
||||
elif error_type == "live_probe_missing":
|
||||
reasons.append(
|
||||
f"No live client invocation proof was supplied for '{ns}.{tool}'."
|
||||
)
|
||||
@@ -248,6 +286,10 @@ def classify_namespace_probe(
|
||||
"env": env_summary,
|
||||
"config_path": config_path,
|
||||
"probe_source": source,
|
||||
"cohort_id": cohort_id,
|
||||
"startup_sha": startup_sha,
|
||||
"endpoint": endpoint,
|
||||
"config_fingerprint": config_fingerprint,
|
||||
},
|
||||
"blocks_merge_workflow": blocks,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user