This repository was archived by the owner on May 26, 2026. It is now read-only.
feat(kora): KR-MCP-3 — MCP-picker UI frontend shell (stub)#106
Merged
Conversation
Operator-facing view of EXTERNAL MCPs Kora consumes
(Kora-as-MCP-client). Distinct concept from KR-P2-C ST2's existing
/mcp surface, which is Kora-as-MCP-server admin (per-tool gating
for tools Kora EXPOSES). Routes + types disambiguated:
/api/mcp/servers → Kora-as-server admin (existing, /mcp)
/api/mcp/clients/list → Kora-as-client picker (new, /mcp-clients)
Branch base: feature/phase2-upgrades per bucket §0. Same stub-then-
real pattern as KR-HB-PANEL; CC#1's KR-MCP-1 ST2 swaps the body
using the same payload shape this PR pins.
SECURITY contract (bucket §5 + ship-checklist hard-rule):
auth_token_env carries the env-var NAME (e.g. KORA_MCP_GITHUB_TOKEN);
auth_token_present is a bool. Token VALUES never appear in the
response shape, never render in the UI (no tooltips, no copy-to-
clipboard buttons, no dev-console). Tokens live in Doppler;
operator rotates via `doppler secrets set <env>`. Two test-side
guards catch any future drift:
* regex-pin auth_token_env to UPPER_SNAKE env-var name shape
* walk the response keys for token-VALUE-shaped names (token,
secret, access_token, api_key, password, bearer, bare
auth_token without _env/_present suffix) and assert zero
matches
§2 K-DG verifications (grep'd vs bucket assumptions):
* kora_mcp/ pool/registry/routing — confirmed (CC#1 ST1 1c495da)
* No collision with existing /mcp route — new /mcp-clients route
+ MCPClientsPanel (not MCPPage) avoid namespace overlap
* Cable icon (lucide-react) distinct from MCPPage's Plug — operator
visually distinguishes the two MCP surfaces in nav
Backend (kora_cli/web_server.py):
* GET /api/mcp/clients/list — stub returns 2 clients matching
bucket §3 verbatim (github stdio, cloudflare streamable_http),
both in configured_but_unconnected + auth_token_present:false.
stub:true flag drives FE banner.
Frontend:
* pages/MCPClientsPanel.tsx —
- Aggregate summary strip: total + connected/configured/error
counts + italic reminder "Token values never displayed —
managed in Doppler"
- Per-client row: status icon + name + transport badge (with
Terminal vs Network icon for stdio vs streamable_http) +
status pill + tools_count when connected + KeyRound icon
with green check / red x for auth presence (NEVER the value)
- Expandable detail: endpoint (truncated, full on hover) +
auth_token_env name + presence indicator + italic Doppler
rotation hint copy + allowed_tools_regex + tools_count
- STUB banner with KR-MCP-1 ST2 flip-in note
- Defensive empty-state for clients: []
* lib/api.ts — MCPClientTransport + MCPClientStatus type aliases +
MCPClient + MCPClientsListResponse interfaces + getMCPClients
client. Tokens explicitly absent from the TS shape (only
auth_token_env: string + auth_token_present: boolean).
* App.tsx — /mcp-clients route + nav entry (Cable icon) between
/heartbeat and /boot-status. Cable distinct from MCPPage's Plug
so operator distinguishes "Kora as MCP CLIENT" (Cable) from
"Kora as MCP SERVER" (Plug) at nav-glance.
* DashboardPage.tsx — new MCP Clients card on row 2 (6th card now;
grid bumped from lg:grid-cols-5 to lg:grid-cols-3 for cleaner
2-row visual symmetry on desktop). Card body aggregates total +
connected/errors pills; headline tone goes destructive when any
error/unhealthy client exists. ALL_SOURCES extended → footer
count adds 1 stubbed source.
Tests: tests/kora_cli/test_web_server_mcp_clients.py — 9 tests
covering all 6 §4 scenarios plus security/contract guards:
* Both-clients-present pin (github + cloudflare)
* Per-entry shape + transport/status enum validation
* SECURITY: auth_token_env regex-pinned to env-var NAME shape
* SECURITY: walk response keys; reject any token-VALUE-shaped key
(token, secret, access_token, api_key, password, bearer, or
bare auth_token without _env/_present suffix)
* Stub stays configured_but_unconnected (dashboard "0 connected"
aggregate depends on it)
* Stub stays auth_token_present:false (FE red-x indicator)
* Cron-regression sanity
168/168 across 16 admin-panel test files (was 159/159, +9 new).
tsc -b + vite build clean.
FE test framework still absent (same as KR-HB-PANEL); component
tests skipped per established pattern. Backend security guards +
type system + manual smoke cover the contract.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 22, 2026
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
Operator-facing view of EXTERNAL MCPs Kora consumes (Kora-as-MCP-client). Distinct concept from the existing `/mcp` surface, which is Kora-as-MCP-server admin (per-tool gating for tools Kora EXPOSES).
Branch base: `feature/phase2-upgrades` per bucket §0. Same stub-then-real pattern as KR-HB-PANEL; CC#1's KR-MCP-1 ST2 swaps the body using this PR's pinned payload shape. Bucket spec: `kora_docs/17_cc_bucket_prompts/KR-MCP-3_mcp_picker_ui_stub.md`.
SECURITY contract (bucket §5 hard-rule)
`auth_token_env` carries the env-var NAME (e.g. `KORA_MCP_GITHUB_TOKEN`); `auth_token_present` is a bool. Token VALUES never appear in the response shape, never render in the UI (no tooltips, no copy buttons, no dev-console). Tokens live in Doppler; operator rotates via `doppler secrets set `.
Two test guards catch any future drift:
Backend
`GET /api/mcp/clients/list` — stub returns 2 clients matching bucket §3 verbatim:
Both `configured_but_unconnected` + `auth_token_present: false`. `stub: true` flag drives FE banner.
Frontend
`pages/MCPClientsPanel.tsx`:
`lib/api.ts` — `MCPClientTransport` + `MCPClientStatus` type aliases + `MCPClient` + `MCPClientsListResponse` interfaces + `getMCPClients` client. Tokens explicitly absent from the TS shape (`auth_token_env: string` + `auth_token_present: boolean` — type-level guarantee).
`App.tsx` — `/mcp-clients` route + nav entry (Cable icon) between `/heartbeat` and `/boot-status`. Cable distinct from MCPPage's Plug so operator distinguishes "Kora as MCP CLIENT" (Cable) from "Kora as MCP SERVER" (Plug) at nav-glance.
`DashboardPage.tsx` — new MCP Clients card on row 2 (6th card now; grid bumped from `lg:grid-cols-5` to `lg:grid-cols-3` for cleaner 2-row visual symmetry on desktop). Card body aggregates total + connected/errors pills; headline tone goes destructive when any error/unhealthy client exists. `ALL_SOURCES` extended → footer stubbed count adds 1.
Test plan
Flip-over plan
When CC#1's KR-MCP-1 ST2 ships the real catalog read, the endpoint body swaps to project from the live `kora_mcp/` pool. Payload shape is already pinned; FE renders real data automatically and the STUB banner auto-disappears once `stub:false` flows through.
🤖 Generated with Claude Code