Skip to content
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
rafe-walker merged 1 commit into
feature/phase2-upgradesfrom
feat/kora-KR-MCP-1-ST2
May 22, 2026
Merged

feat(kora): KR-MCP-1 ST2 — initial MCP catalog + CLI#105
rafe-walker merged 1 commit into
feature/phase2-upgradesfrom
feat/kora-KR-MCP-1-ST2

Conversation

@rafe-walker

Copy link
Copy Markdown
Owner

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`)

Endpoint Transport Endpoint string Auth env
github stdio `npx -y @modelcontextprotocol/server-github` `KORA_MCP_GITHUB_TOKEN`
cloudflare streamable_http `https://mcp.cloudflare.com/sse\` `KORA_MCP_CLOUDFLARE_TOKEN`

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

  • `kora mcp clients list` (alias `ls`) — endpoint table + health
  • `kora mcp clients status ` — detail card

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

  • Q1 GitHub MCP transport — npx stdio (no hosted GA endpoint
    confirmed as of 2026-05)
  • Q2 allowlist regex — case-insensitive (pinned in ST1)
  • Q3 active health-check cadence — NO active ping; daemon
    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

  • 54 pass + 1 skip (integration test gated behind
    `KORA_INTEGRATION_TEST=1`)
  • Default catalog shape (github + cloudflare; transport, auth
    env naming `KORA_MCP__TOKEN`)
  • Health: no auth env / set / unset / empty / whitespace
  • Operator config override merges + replaces by name
  • Pydantic `extra="forbid"` rejects unknown YAML keys
  • Malformed mcp_clients block tolerated → falls back to defaults
  • K-DG dispatcher routing pinned
  • Manual CLI smoke-test: `kora mcp clients list/status`
    render correctly
  • Ruff clean

Cascade

Base: `feature/phase2-upgrades` (Phase 2 long-lived integration
branch). Closes the KR-MCP-1 bucket.

🤖 Generated with Claude Code

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>
@rafe-walker rafe-walker merged commit 42ca5bc into feature/phase2-upgrades May 22, 2026
@rafe-walker rafe-walker deleted the feat/kora-KR-MCP-1-ST2 branch May 22, 2026 05:40
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant