This repository was archived by the owner on May 26, 2026. It is now read-only.
feat(kora): KR-MCP-RUNTIME-SURFACE ST1 — 5 read-only agent-facing tools#112
Merged
rafe-walker merged 1 commit intoMay 22, 2026
Merged
Conversation
Extends the /mcp pipe from PR #101's proof-of-pipe (kora__daemon_status only) with 5 read-only tools so other agents — IsoKron PM via Slack #claude-pms, drone tickets, future PMs — can introspect Kora's runtime state programmatically. ## New tools - `kora__get_operational_state` — current PrimaryState (BOOTING / READY / ACTIVE / PAUSED / STOPPED) + degradation_reasons + claim_permission + recent transitions (ring buffer, last 20) + state_entered_at (computed from most-recent transition matching current state). - `kora__get_health_rollup` — overall/control_plane/worker statuses + the 8 per-subsignal projections with last_seen + extra-bag detail. - `kora__get_recent_ledger_entries(limit, status?)` — last N kora_operation_ledger rows, default 50, max 200, filterable by status enum. - `kora__get_recent_chain_events(limit, event_kind?)` — last N kora.* chain events via existing read_recent_kora_events helper; in-process prefix filter on event_kind. - `kora__list_active_sea_tickets(limit)` — Kora's currently-active Sea_Tickets (claimed / in_progress / failed_retryable) assigned to her actor. All five are READ-ONLY: no substrate writes, no ledger rows, no state mutations. ST2 brings the mutating surface + capability gating. ## Files - **`kora_cli/listeners/mcp_tools.py`** (NEW, ~570 lines) — Pydantic response models per tool, async executor functions, JSON-RPC dispatch table (TOOL_DISPATCH). - **`kora_cli/listeners/mcp.py`** (+37 lines) — extends TOOLS list with the 5 new descriptors at module-import time; tools/call now routes to mcp_tools.TOOL_DISPATCH for the new tools; tool exception → JSON-RPC -32603 envelope (not 500). - **`tests/kora_cli/test_listeners/test_mcp_tools.py`** (NEW, 20 tests, all passing) — descriptor surface + holder-unavailable + provider-unavailable paths + limit clamping (1/50/200/-5) + bearer auth still enforced + unknown tool -32602 + tool-exception -32603. ## K-DG drift findings (surfaced + corrected in this PR) - **PrimaryState enum**: spec said "STARTING / RUNNING / DRAINING / STOPPED"; actual is BOOTING / READY / ACTIVE / PAUSED / STOPPED per `agent/operational_state.py:70-74` (KR-P2-I-skeleton). ST1 surfaces actual values. - **HealthRollup attribute names**: spec assumed overall_status / control_plane_status / worker_status; actual bare names overall / control_plane / worker per `agent/health_rollup_holder.py:174-176`. - **TransitionRecord keys**: spec assumed transitioned_at / from_primary_state / to_primary_state; actual timestamp / from_state / to_state / trigger per `agent/operational_state_holder.py:73-82`. - **`holder.current`** is a `@property`, not a method call — caught + fixed during testing (initial draft did `holder.current()`). ## Drift findings affecting ST2 (PRE-FILED, not addressed here) - **`actor_has_capability(capability: str)`** is IMPLICITLY Kora-only per `plugins/memory/isokron/capability_check.py:69`. The bucket spec's 2-arg `actor_has_capability(actor_kind, capability)` shape doesn't exist. ST2 will need either a new substrate-side caller-aware function (heavy lift) OR a bearer-token-→-role ACL via the same `~/.kora/mcp_callers.yaml` PM Q1 chose for caller identity (lighter, composes with Q1 default). PM ruling needed before ST2 ships. - **`sea__create_ticket` "blocked" comments**: still in `agent/cron_work_class.py:34, 137, 206` but tool is LIVE on prod per prior PM acks + `feedback_dont_trust_stub_comments_for_liveness.md` memory. ST2 will use `IsoKronMCPClient.invoke("sea__create_ticket", ...)` via the generic dispatcher. ## §5 ship checklist - [x] Base `feature/phase2-upgrades` - [x] Title format `feat(kora): KR-MCP-RUNTIME-SURFACE STn — <scope>` - [x] §4 questions surfaced + reads aligned with PM defaults - [x] ST1: NO state mutations, NO ledger writes (enforced by code structure — no writer imports in mcp_tools.py) - [x] Tests pass locally (113/113 across full suite) - [x] K-DG: every drift surface grepped + flagged in PR body ## End-to-end smoke `kora daemon` with --listener filtering + KORA_MCP_BEARER_TOKEN set: - `GET /mcp/tools/list` → 6 tools (kora__daemon_status + 5 new) - `POST /mcp` tools/call kora__get_operational_state → valid Pydantic JSON; holder_available=false (no holder in `kora daemon` standalone) - `POST /mcp` tools/call kora__get_recent_ledger_entries → valid Pydantic JSON; provider_unavailable=true (no provider in test daemon) - SIGTERM exits 0 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
Extends the /mcp pipe from PR #101's proof-of-pipe (`kora__daemon_status` only) with 5 read-only tools so other agents — IsoKron PM via Slack #claude-pms, drone tickets, future PMs — can introspect Kora's runtime state programmatically.
Bucket spec: `kora_docs/17_cc_bucket_prompts/KR-MCP-RUNTIME-SURFACE_agent_facing_tools.md` (commit 54032c6).
Base: `feature/phase2-upgrades` — NOT main.
New tools (all READ-ONLY)
All five are READ-ONLY: no substrate writes, no ledger rows, no state mutations. ST2 brings the mutating surface + capability gating.
Files
K-DG drift findings (surfaced + corrected in this PR)
Drift findings affecting ST2 (PRE-FILED for PM ruling before ST2 lands)
`actor_has_capability(capability: str)` is IMPLICITLY Kora-only per `plugins/memory/isokron/capability_check.py:69`. The spec's 2-arg `actor_has_capability(actor_kind, capability)` shape doesn't exist.
ST2 needs either:
Recommend (b) — semantically cleaner: Kora's own caps reflect what she's allowed to do; MCP callers' caps reflect what we let them ask her to do. PM ruling needed before ST2 ships.
`sea__create_ticket` "blocked" comments still present in `agent/cron_work_class.py` but tool IS live on prod per prior PM acks + memory `feedback_dont_trust_stub_comments_for_liveness.md`. ST2 will use `IsoKronMCPClient.invoke("sea__create_ticket", ...)` via the generic dispatcher.
§4 PM-opens — my reads
§5 ship checklist
End-to-end smoke
```
KORA_DEV=1 KORA_MCP_BEARER_TOKEN=tok KORA_SLACK_SIGNING_SECRET=s \
KORA_PUREMAIL_HMAC_SECRET=s KORA_WEB_PORT=9389 \
KORA_WEBHOOK_PORT=9388 KORA_WEBHOOK_HOST=127.0.0.1 \
kora daemon
```
What's next
ST2 (after PM rules on the capability-gating drift — finding (b) above): 3 mutating tools (`kora__request_state_transition`, `kora__create_sea_ticket`, `kora__send_webhook_test_event`) + capability gating + bearer-token-→-actor_kind resolution + multi-token rotation support + dev_only flag in descriptor + prd-environment refusal for the test webhook tool.
🤖 Generated with Claude Code