This repository was archived by the owner on May 26, 2026. It is now read-only.
feat(kora): KR-MCP-CLIENTS-HEALTH-DISPLAY — render last_check_at + last_error#117
Merged
rafe-walker merged 1 commit intoMay 22, 2026
Conversation
…st_error CC#1's KR-MCP-CONSUMPTION ST2 (PR #113 94e572d) added last_check_at + last_error to the /api/mcp/clients/list payload and the MCPClient TS interface. The cockpit's MCPClientsPanel.tsx didn't yet render either field; this PR closes that loop. Per-row surface (collapsed): * "checked Xs/Xm/Xh/Xd ago" relative-time label next to the status pill (italic + muted "never checked" when null) * Stale chip when last_check_at is older than 10 min — that's 2x the default 5-min KORA_MCP_HEALTH_CHECK_INTERVAL_SEC cadence, so the chip signals "heartbeat scheduler stopped firing" rather than tripping on every normal probe cycle * Red error line under the row (truncated to 80 chars, full text in the tooltip) when last_error is non-null Per-row surface (expanded detail view): * New "Last Check" section: absolute last_check_at timestamp + relative phrase + stale chip * Full last_error rendered in a <pre> for multi-line readability Aggregate strip: * "errors" count now unions status=error/unhealthy with last_error!==null (catches the connected-but-flapping case) * New "stale (>10m)" count for endpoints whose heartbeat is silent 3-layer security contract (continuing the panel-by-panel pattern, this iteration's specific risk = last_error being untrusted text from a misbehaving MCP probe): 1. Plain-text rendering via React's default child escaping — <script> tags in last_error render as text, never execute. 2. NO dangerouslySetInnerHTML anywhere in the panel. Backend test pins this via a comment-stripped grep that catches a future edit switching to "rich error rendering". 3. TS type for last_error is plain `string | null` — no pre-parsed `_html` companion field exists on MCPClient. Helpers factored to web/src/lib/mcpHealth.ts so the stale threshold (10 * 60 * 1000 ms) + error truncation cap (80 chars) live as named constants the source-pin tests can reference symbolically. Tests: * tests/kora_cli/test_mcp_clients_health_display.py — 16 tests: source-file existence, dangerouslySetInnerHTML ban (with comment-stripping so the warning comment in the source doesn't self-trigger), last_error as JSX-child text node, stale threshold = 10min, truncation cap = 80, helper exports + panel-import-from-helpers chain (so source-pins stay load-bearing), aggregate errors filter includes last_error, cron-regression sanity. * tsc --noEmit + vite build both clean. * Pre-existing test_web_server_mcp_clients.py failures (13 + 5 errors) confirmed present on bare feature/phase2-upgrades — NOT introduced by this PR; flagged for separate cleanup. Spec note (no test framework): repo has no vitest/jest. Per the established CC#2 pattern, FE invariants pin via Python source-grep tests + tsc/vite for type/compile correctness. Adding a real component test runner is out of scope at <150 LOC. Refs: rafe-walker/kora-docs 17_cc_bucket_prompts/KR-MCP-CLIENTS-HEALTH-DISPLAY.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
rafe-walker
added a commit
that referenced
this pull request
May 22, 2026
…tigation (#125) Two-part cleanup. Part A (tsc drift): HeartbeatPanel.tsx + DashboardPage.tsx now handle all 4 fields KR-FEAT-HEARTBEAT ST2 (#118) added: unknown status arm (muted probe-pending pill + CircleDashed icon), nullable last_check_at (never checked — matching #117 convention), error field rendered as destructive <pre> in expanded view (plain text, no dangerouslySetInnerHTML), cache_warming Probes warming up… banner + dashboard headline-tone suppression. tsc -b clean. Part B (mcp_clients tests): investigation only, STOPPED per spec rule. Root cause is NOT stale assertions — all 13 failures + 5 errors collapse on ModuleNotFoundError: No module named slowapi from the unconditional import chain web_server → listeners/__init__ → listeners/webhooks → slowapi. Verified by installing --extra web: 19/19 mcp_clients tests pass. This is task NousResearch#265 (slowapi placement). Recommended 1-line fix: move slowapi to runtime deps. Will dispatch as standalone KR-SLOWAPI-DEP-FIX bucket. 3 files, +267/-14 panel + dashboard + 11 new source-pin tests. 260/260 admin-panel tests pass across 22 suites (with --extra web installed). tsc -b + vite build both clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
CC#1's KR-MCP-CONSUMPTION ST2 (PR #113,
94e572d6) addedlast_check_at+last_errorto the/api/mcp/clients/listpayload and theMCPClientTS interface. The cockpit'sMCPClientsPanel.tsxdidn't yet render either field — this PR closes that loop.Bucket:
rafe-walker/kora-docs→17_cc_bucket_prompts/KR-MCP-CLIENTS-HEALTH-DISPLAY.md.What's in here
Clockicon + "checked Xs/Xm/Xh/Xd ago" next to the status pill (italic + muted "never checked" when null).TimerOff"stale" chip whenlast_check_atis older than 10 min (2x the default 5-minKORA_MCP_HEALTH_CHECK_INTERVAL_SECcadence — signals "heartbeat scheduler stopped firing" rather than tripping on every normal probe).last_erroris non-null (truncated to 80 chars, full text intitle=tooltip).last_errorin a<pre>for multi-line readability.errorscount now unionsstatus=error|unhealthywithlast_error !== null(catches the connected-but-flapping case). Newstale (>10m)count for endpoints whose heartbeat is silent.web/src/lib/mcpHealth.tsso the threshold (STALE_CHECK_THRESHOLD_MS) + truncation cap (ERROR_TRUNCATE_LEN) live as named exports the source-pin tests can reference symbolically.3-layer security contract
This iteration's specific risk:
last_erroris operator-readable text sourced from an MCP catalog probe (subprocess stderr / HTTP body) — i.e., untrusted input from a misbehaving MCP server.<script>tags inlast_errorrender as text, never execute.dangerouslySetInnerHTMLanywhere in the panel. Backend test pins this via a comment-stripped grep so the warning comment in the source doesn't self-trigger, AND so a future "rich error rendering" edit gets caught.last_erroris plainstring | null— no pre-parsed_htmlcompanion field exists onMCPClient.Test plan
tests/kora_cli/test_mcp_clients_health_display.py— 16 source-pin tests covering: file existence,dangerouslySetInnerHTMLban (with comment-stripping),last_erroras JSX-child text node, stale threshold = 10 min, truncation cap = 80, helper exports + panel-import chain (so source-pins stay load-bearing), aggregate errors filter includeslast_error, cron-regression sanity.pnpm tsc --noEmitclean.pnpm buildclean./mcp-clients, confirm relative timestamps, simulate stale by mocking the snapshot, confirmlast_errorrenders as text not HTML.Notes
test_web_server_mcp_clients.py(13 failed + 5 errors) confirmed present on barefeature/phase2-upgrades— NOT introduced by this PR. Likely fallout from PR feat(kora): KR-MCP-CONSUMPTION ST2 — active health-check + payload extension #113's status-derivation changes; flagging for a separate cleanup bucket.web/package.json). Per the established CC#2 pattern (HB-PANEL, MCP-3, WEBHOOK-EVENTS, AGENT-ACTIVITY), FE invariants pin via Python source-grep tests + tsc/vite for type/compile correctness. Standing up a real component test runner is out of scope at <150 LOC.🤖 Generated with Claude Code