This repository was archived by the owner on May 26, 2026. It is now read-only.
feat(kora): KR-MCP-CLIENTS-FLIP — stub → real catalog#108
Merged
rafe-walker merged 1 commit intoMay 22, 2026
Conversation
Wires CC#2's KR-MCP-3 stub at `GET /api/mcp/clients/list` (PR #106) to CC#1's KR-MCP-1 ST2 real catalog at `kora_mcp/catalog.py` (PR #105). FE STUB banner auto-disappears when `stub: false` per CC#2's panel implementation. # Catalog helper added `kora_mcp/catalog.py:load_effective_catalog()` — one-call wrapper that reads `~/.kora/config.yaml` via `kora_cli.config.load_config` and merges with DEFAULT_CATALOG. Defaults still surface when no operator config exists; operator entries override by name + new names are appended. Pydantic `extra="forbid"` rejects unknown keys at load. Tolerates unreadable config (returns just the defaults) so the endpoint surfaces "no clients configured" rather than crashing. # Endpoint body swap `kora_cli/web_server.py:list_mcp_clients` — replaces the hardcoded github+cloudflare stub with: - Read live registry via `load_effective_catalog()` - Per-endpoint compute `EndpointHealth` via ST2's `check_endpoint_health` - Build the FE payload row matching the TS `MCPClient` interface in `web/src/lib/api.ts` exactly: name / transport / endpoint / status / auth_token_env / auth_token_present / allowed_tools_regex / tools_count - `status` mapping: auth env unset / empty → "unhealthy" auth env set → "configured_but_unconnected" The "connected" status surfaces once the daemon's MCP-client- pool listener is wired (deferred to KR-MCP-CONSUMPTION per spec §3). - `tools_count` stays `null` for now (same deferral). - `auth_token_present`: True only when auth_token_env is configured AND the env var resolves to a non-empty stripped value. Empty string / whitespace-only treated as unset per ST2's contract (Doppler injection corner case). - `stub: False` + `generated_at` set to current UTC ISO # HARD CONSTRAINT preserved Token VALUES never appear in the response. `auth_token_env` is the env-var NAME; `auth_token_present` is a bool. CC#2's two security tests stay green: 1. test_auth_token_env_carries_env_var_name_not_value (regex pin: `^[A-Z][A-Z0-9_]*$` shape; rejects token-value- looking field values) 2. test_no_token_value_shaped_keys_leak_in_response (walk-all-keys guard against token/secret/api_key/etc. bare OR auth_token without _env/_present suffix) # Test plan `tests/kora_cli/test_web_server_mcp_clients.py` — 14 tests pass: - 1 contract assertion FLIPPED: `stub: True` → `stub: False` - 2 stub-period tests REPLACED with status-mapping tests: - status=unhealthy when auth env unset - status=configured_but_unconnected when auth env set - 3 new auth_token_present tests: - false when env unset - true when env set - false when env empty / whitespace-only - 2 new operator-override tests: - operator entry by name REPLACES default - operator entry with new name ADDS to catalog - 2 security tests PRESERVED (regex pin + walk-all-keys) - Existing shape / enums / required-keys / cron-regression tests pass without change # LOC summary - Endpoint body swap: ~30 LOC net (web_server.py) - Catalog helper: ~25 LOC (load_effective_catalog) - Tests: ~120 LOC net (new tests + flipped assertions) Manual smoke: `curl localhost:9119/api/mcp/clients/list` (no Doppler-injected tokens locally) returns github + cloudflare with `auth_token_present: false` + `status: "unhealthy"`. Verified the JSON shape matches the TS `MCPClient` interface byte-for-byte. # Closes - KR-MCP-CLIENTS-FLIP bucket - Open follow-on from KR-MCP-1 ST2 dispatch ("real /api/mcp/clients/list endpoint" that was missed in the original bucket) - FE STUB banner in MCPClientsPanel.tsx auto-disappears once this merges Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
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
Wires CC#2's KR-MCP-3 stub at `GET /api/mcp/clients/list` (PR
#106 / `b34b2686`) to CC#1's KR-MCP-1 ST2 real catalog (PR #105
/ `42ca5bc3`). FE STUB banner in MCPClientsPanel.tsx
auto-disappears when `stub: false` per CC#2's panel impl.
Bucket: `17_cc_bucket_prompts/KR-MCP-CLIENTS-FLIP_stub_to_real.md`
Change footprint
Prod surface ~25 LOC net swap + 23 LOC new helper. Under the
bucket's "<100 LOC + tests" budget.
Payload contract — byte-for-byte compatible
TS `MCPClient` interface (web/src/lib/api.ts:1446):
```typescript
interface MCPClient {
name: string;
transport: "stdio" | "streamable_http";
endpoint: string;
status: "connected" | "configured_but_unconnected" | "error" | "unhealthy";
auth_token_env: string;
auth_token_present: boolean;
allowed_tools_regex: string | null;
tools_count: number | null;
}
```
All 8 fields surfaced; field shapes preserved. Manual smoke
verifies the JSON exactly matches.
Status mapping (post-flip)
`connected` + non-null `tools_count` deferred to KR-MCP-
CONSUMPTION per bucket §3.
Security tests PRESERVED
Both CC#2 security tests stay green without modification:
regex pin `^[A-Z][A-Z0-9_]*$` on auth_token_env shape;
rejects any token-value-looking field
walk-all-keys guard against token / secret / access_token /
api_key / etc. bare OR auth_token without _env/_present suffix
Token VALUES never appear in the response — `auth_token_env` is
the env-var NAME; `auth_token_present` is a bool.
Test plan
on non-empty
tests
Cascade
Base: `feature/phase2-upgrades` (Phase 2 long-lived integration
branch).
🤖 Generated with Claude Code