This repository was archived by the owner on May 26, 2026. It is now read-only.
feat(kora): KR-MCP-1 ST2 — initial MCP catalog + CLI#105
Merged
Conversation
Phase 2 Feature 1 ST2. Ships the default outbound MCP catalog
(github + cloudflare) + operator-facing CLI for listing /
inspecting endpoint health.
# Catalog (`kora_mcp/catalog.py`)
- DEFAULT_GITHUB_ENDPOINT: stdio via `npx -y @modelcontextprotocol/server-github`
per §4 Q1 ruling (hosted streamable_http not yet GA from upstream
GitHub MCP, as of 2026-05).
- DEFAULT_CLOUDFLARE_ENDPOINT: streamable_http at
https://mcp.cloudflare.com/sse (Cloudflare's hosted MCP gateway).
Operators with workspace-specific gateway URLs override via
`mcp_clients.endpoints` in ~/.kora/config.yaml.
- Auth env vars: KORA_MCP_GITHUB_TOKEN / KORA_MCP_CLOUDFLARE_TOKEN
(Doppler-injected at runtime; no Python SDK call).
- EndpointHealth dataclass + check_endpoint_health(): config-level
health WITHOUT opening a transport. Missing auth env → unhealthy
(degrade, don't crash per §3 contract).
- load_registry_from_config(): merges defaults with operator
overrides (operator entry by name REPLACES default; new names
ADD); Pydantic extra="forbid" rejects unknown keys.
# CLI (`kora_cli/mcp_config.py` + `kora_cli/main.py`)
Two new sub-actions on the existing `kora mcp` parser:
- `kora mcp clients list` (alias `ls`) — table of every endpoint
+ health status (✓ ready / ⚠ unhealthy). Doesn't open transports.
- `kora mcp clients status <name>` — detail card for one endpoint:
transport, endpoint string, timeouts, auth env, allowlist regex,
health verdict + reason.
Naming: `clients` is the new SUB-action distinguishing outbound
(`mcp_clients.endpoints` config — Kora calling external MCPs) from
the existing inbound `mcp add/remove/list/test` actions (which
manage `mcp_servers` config — agent-side MCP servers Kora consumes
via tools/mcp_tool.py).
# K-DG subcommand-dispatcher-check (locked rule)
Per `feedback_k_dg_subcommand_dispatcher_check`: both halves wired
in this PR — argparse `add_parser("clients")` + clients-subparser
with `list`/`status` (in main.py); action-dispatcher branch
`"clients": cmd_mcp_clients` (in mcp_config.py:752). Test
`test_cli_mcp_clients_dispatcher_routes_to_handler` pins the
contract that the dispatcher actually routes — silent no-op was
the failure mode the rule catches.
# §4 PM rulings applied verbatim
- Q1 GitHub MCP transport: npx stdio (current default; re-verify
in ST2 work — confirmed no hosted GA endpoint as of 2026-05)
- Q2 allowlist regex: case-insensitive (re.IGNORECASE), pinned
in ST1's test_allowlist_is_case_insensitive
- Q3 active health-check cadence: NO active ping. EndpointHealth
is config-level only; daemon's heartbeat scheduler can opt in
to per-endpoint active checks later via register_periodic_task
+ pool.has_open_connection(prefix)
# Tests
23 new tests in `tests/kora_mcp/test_catalog.py`:
- 5 default catalog shape tests (github + cloudflare presence;
transport, endpoint prefix, auth env naming convention pinned)
- 7 health tests (no auth env → healthy; env set → healthy; env
unset → unhealthy; empty string → unhealthy; whitespace-only
→ unhealthy; dataclass immutability; registry health returns
one-per-endpoint)
- 7 load_registry_from_config tests (empty config returns
defaults; include_defaults=False returns empty; operator
override by name; operator add-new; extra="forbid" rejects
typo; malformed block / malformed endpoints list tolerated
→ defaults)
- 2 lookup tests
- 1 K-DG dispatcher routing test (`feedback_k_dg_subcommand_dispatcher_check`)
- 1 integration test gated behind `KORA_INTEGRATION_TEST=1`
(skipped in CI; run locally with real Doppler-injected tokens
to validate the catalog against real MCPs)
54 pass + 1 skip (integration). Ruff clean.
Manual CLI smoke-test verified — `kora mcp clients list` /
`kora mcp clients status github` render correctly with health
warnings when auth env unset.
# Non-scope (per §3 carried forward)
- MCP-picker UI (CC#2 / KR-MCP-3)
- Dynamic registration via kora__install_mcp (council deferral)
- kora.mcp.tool_called audit chain events (deferred until
daemon ST2 consumes the pool)
- Cross-MCP tool-name collision policy (prefix routing IS the
disambiguation today)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8 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
KR-MCP-1 ST2 — closes the bucket. Default outbound MCP catalog
(github + cloudflare) + operator-facing CLI (`kora mcp clients
list` / `status `).
Bucket: `kora_docs/17_cc_bucket_prompts/KR-MCP-1_multi_mcp_registry.md`
Catalog (`kora_mcp/catalog.py`)
Operators override via `mcp_clients.endpoints` in
`~/.kora/config.yaml`. Override-by-name semantics: same `name`
REPLACES the default; new names ADD.
Pydantic `extra="forbid"` rejects unknown YAML keys at load time
(K-DG drift discipline).
CLI
K-DG subcommand-dispatcher-check wired (both halves): argparse
`add_parser("clients")` in `main.py` + dispatcher branch
`"clients": cmd_mcp_clients` in `mcp_config.py`. Test
`test_cli_mcp_clients_dispatcher_routes_to_handler` pins the
silent-no-op failure mode the rule catches.
§4 PM rulings applied
confirmed as of 2026-05)
heartbeat scheduler opts in later via `has_open_connection`
Health contract
Missing auth env → endpoint marked unhealthy via `EndpointHealth`.
Does NOT crash. Empty string + whitespace-only env values also
treated as unset (Doppler sometimes injects empty values; explicit
guard).
Test plan
`KORA_INTEGRATION_TEST=1`)
env naming `KORA_MCP__TOKEN`)
render correctly
Cascade
Base: `feature/phase2-upgrades` (Phase 2 long-lived integration
branch). Closes the KR-MCP-1 bucket.
🤖 Generated with Claude Code