Why
An audit against docs/design/agent-tui-terminal.html (sections 24 · MCP browser, 32 · slow-server toast, 37 · MCP lifecycle) shows the runtime UX has drifted from the documented design.
| design says |
runtime does |
gap |
Interactive /mcp browser modal — ↑↓ pick, ⏎ inspect, r reconnect, d disable |
/mcp is a text dump into scrollback |
no modal |
Six lifecycle one-liners on state changes: ↻ handshake… / ✓ connected / ◌ slow / ↻ reconnect 2/5 / ✖ failed / ○ disabled |
Two startup-only lines: ▸ MCP[label]: N tool(s)… and ▸ MCP setup SKIPPED… |
wrong vocab + 4 missing states |
/mcp disable / enable / reconnect slash subcommands |
none |
missing |
p95 slow-call detection + toast (⚠ MCP \notion` slow · 8.4s p95 over the last 5 calls`) |
one-shot elapsedMs at startup only |
no runtime tracking |
Stages
Stage A — Lifecycle vocabulary alignment
Reshape the existing startup-time strings to match design §37 byte-for-byte. No new lifecycle states wired this stage — just the three that already fire (handshake… pre-bridge, connected post-bridge, failed in the catch).
Touch:
src/cli/commands/chat.tsx:206-277 (the for (const raw of requestedSpecs) loop)
src/cli/commands/run.ts:90-115 (same pattern)
- New
src/cli/ui/mcp-lifecycle.ts exporting formatMcpLifecycleEvent({ name, state, detail }) so chat / run / future code share the formatter
Acceptance:
Estimate: 2-3 hours.
Stage B — /mcp interactive browser
Replace the text-dump /mcp with a keyboard-driven modal matching design §24. Layout per the mockup; r and d are visible-but-stub keybinds in this stage (active in Stage C).
Touch:
- New
src/cli/ui/McpBrowser.tsx wrapping the existing Select.tsx for picker behavior
src/cli/ui/App.tsx — add modal slot + mcpBrowserOpen state
src/cli/ui/slash/handlers/mcp.ts — flip /mcp from returning { info: ... } to setting modal state
- Keep the rich text dump as a non-TTY / replay fallback
Acceptance:
Depends on: Stage A (the lifecycle formatter is reused for row health text).
Estimate: 1 day.
Stage C — /mcp disable / enable / reconnect + slow-toast
Three slash subcommands and the p95 latency tracker that drives the warn toast.
Subcommands:
/mcp disable <name> — write disabled: true to the server's config entry; emit ○ disabled lifecycle card; bridge skips it on next session and the r key in the browser flips it back
/mcp enable <name> — inverse
/mcp reconnect <name> — tear down the live McpClient, re-handshake, re-bridge; emits ↻ reconnect N/5 then either ✓ connected or ✖ failed
Slow detection:
- Wrap
dispatch in src/mcp/registry.ts to record per-tool elapsed_ms in a per-server ring buffer (size 5)
- After 5 calls, compute p95; if
> mcpSlowThresholdMs (default 4000), emit toast ⚠ MCP \` slow · 8.4s p95 over the last 5 calls`
- Idempotent within a turn (don't re-emit on every call)
Touch:
src/config.ts — disabled?: boolean per MCP entry, mcpSlowThresholdMs?: number global
src/mcp/registry.ts — latency ring buffer + p95 helper
- New
src/cli/ui/slash/handlers/mcp-control.ts for the three subcommands
- New
src/cli/ui/mcp-toast.ts for the warn emission
Acceptance:
Depends on: A (lifecycle vocab), B (browser surface for live state).
Estimate: 1.5-2 days.
Stage D — (defer) tools sub-pane on browser ⏎
Inside the browser, ⏎ on a server opens its tools / resources / prompts list. Deferred because reasonix mcp inspect <spec> already covers this from the CLI; in-browser inspection is polish, not core. Pull in 0.21.1 if there's demand.
Constraints
- One stage per PR. Stage A unblocks the consistent lifecycle vocabulary that B and C reuse.
- All PRs in this stack reference this issue with
Closes part of #<this>.
- Every visual element must trace back to a section number in
docs/design/agent-tui-terminal.html. No design-token improvisation.
Versioning
Stages A + B + C target 0.21.0. Stage D may slip to 0.21.1.
Why
An audit against
docs/design/agent-tui-terminal.html(sections 24 · MCP browser, 32 · slow-server toast, 37 · MCP lifecycle) shows the runtime UX has drifted from the documented design./mcpbrowser modal — ↑↓ pick, ⏎ inspect,rreconnect,ddisable/mcpis a text dump into scrollback↻ handshake…/✓ connected/◌ slow/↻ reconnect 2/5/✖ failed/○ disabled▸ MCP[label]: N tool(s)…and▸ MCP setup SKIPPED…/mcp disable / enable / reconnectslash subcommands⚠ MCP \notion` slow · 8.4s p95 over the last 5 calls`)elapsedMsat startup onlyStages
Stage A — Lifecycle vocabulary alignment
Reshape the existing startup-time strings to match design §37 byte-for-byte. No new lifecycle states wired this stage — just the three that already fire (
handshake…pre-bridge,connectedpost-bridge,failedin the catch).Touch:
src/cli/commands/chat.tsx:206-277(thefor (const raw of requestedSpecs)loop)src/cli/commands/run.ts:90-115(same pattern)src/cli/ui/mcp-lifecycle.tsexportingformatMcpLifecycleEvent({ name, state, detail })so chat / run / future code share the formatterAcceptance:
⌘ MCP · <name> ✓ connected 12 tools · 8 resources · 142ms)handshake…(before bridge),connected(after bridge with counts + ms),failed(catch path with reason)tests/mcp-lifecycle.test.tspinning the formatterEstimate: 2-3 hours.
Stage B —
/mcpinteractive browserReplace the text-dump
/mcpwith a keyboard-driven modal matching design §24. Layout per the mockup;randdare visible-but-stub keybinds in this stage (active in Stage C).Touch:
src/cli/ui/McpBrowser.tsxwrapping the existingSelect.tsxfor picker behaviorsrc/cli/ui/App.tsx— add modal slot +mcpBrowserOpenstatesrc/cli/ui/slash/handlers/mcp.ts— flip/mcpfrom returning{ info: ... }to setting modal stateAcceptance:
/mcpopens the modal;Esccloses it↑↓navigates rows;⏎is a no-op stub (Stage D will do tool inspection)●healthy,◌slow,✗failed,○disabledDepends on: Stage A (the lifecycle formatter is reused for row health text).
Estimate: 1 day.
Stage C —
/mcp disable / enable / reconnect+ slow-toastThree slash subcommands and the p95 latency tracker that drives the warn toast.
Subcommands:
/mcp disable <name>— writedisabled: trueto the server's config entry; emit○ disabledlifecycle card; bridge skips it on next session and therkey in the browser flips it back/mcp enable <name>— inverse/mcp reconnect <name>— tear down the liveMcpClient, re-handshake, re-bridge; emits↻ reconnect N/5then either✓ connectedor✖ failedSlow detection:
dispatchinsrc/mcp/registry.tsto record per-toolelapsed_msin a per-server ring buffer (size 5)> mcpSlowThresholdMs(default 4000), emit toast⚠ MCP \` slow · 8.4s p95 over the last 5 calls`Touch:
src/config.ts—disabled?: booleanper MCP entry,mcpSlowThresholdMs?: numberglobalsrc/mcp/registry.ts— latency ring buffer + p95 helpersrc/cli/ui/slash/handlers/mcp-control.tsfor the three subcommandssrc/cli/ui/mcp-toast.tsfor the warn emissionAcceptance:
reconnect)/mcp browsereflects updated state without restartDepends on: A (lifecycle vocab), B (browser surface for live state).
Estimate: 1.5-2 days.
Stage D — (defer) tools sub-pane on browser ⏎
Inside the browser,
⏎on a server opens its tools / resources / prompts list. Deferred becausereasonix mcp inspect <spec>already covers this from the CLI; in-browser inspection is polish, not core. Pull in 0.21.1 if there's demand.Constraints
Closes part of #<this>.docs/design/agent-tui-terminal.html. No design-token improvisation.Versioning
Stages A + B + C target 0.21.0. Stage D may slip to 0.21.1.