Skip to content

MCP server state

ai-ui specs/ai-ui/mcp-server-state.kmd

Connection state visibility for every MCP server attached to a Koder client. Status chip (color-coded) + capabilities drawer + auto-reconnect + error-clear-on-success. Required for any debuggable MCP-aware product.

When this spec applies

Primary triggers

All triggers

Specification body

Spec — MCP server state

Histórico: Cursor #149363 e Claude Code #36308 — error state visualmente persistente após reconnect. Lição: state visual MUST clear automaticamente quando connection succeeds.

Princípios

  1. Always-visible status — todo MCP server attached é representado por um chip persistente em algum surface (header, sidebar, settings panel).
  2. Self-healing visual — error state NUNCA persiste após reconnect succeed; transitions são automáticas.
  3. Capabilities transparency — user vê quais tools/resources/prompts o server expõe.
  4. Manual override — restart, disable, remove sempre disponível.

R1 — Status chip

Compact representation: dot (8×8dp) + server name + count badge.

🟢 my-server [12 tools]
🟡 my-server [reconnecting…]
🔴 my-server [error: timeout]
⚪ my-server [disabled]

4 states (per color-roles.kmd):

StateDot colorMeaningTriggers
connectedsuccess (green)Server reachable, capabilities loadedSuccessful initialize + at least one tools/list response
connectingwarning (yellow/amber)Initial handshake OR reconnect in progressRight after attach OR after error + within retry backoff
errorerror (red)Connection failed; retries exhausted OR fatal protocol errorTimeout / TLS error / protocol mismatch / server rejected
disabledtext-muted (gray)User-disabled OR auto-disabled (too many failures)Manual toggle OR 5 consecutive errors

State transitions:

[new attach] → connecting → connected (T1 success)
                         ↘ error (T1 fail) → connecting (auto-retry) → connected (T2 success — CLEAR error!)
                                                                     ↘ error (T2 fail) ...
[manual disable] → disabled
[manual enable] → connecting
[5 consec errors] → disabled (auto)

Critical regression contract (R4.1): error → connecting → connected transition MUST clear the visual error state immediately upon connected. NO stale red dot. NO "still showing error after success".

R2 — Tooltip (hover / long-press)

Mouse hover (desktop) ou long-press (mobile) reveal:

my-server
status: connected
capabilities: 12 tools, 3 resources, 5 prompts
last sync: 2s ago
ping: 45ms

Error state inclui error message + last attempt timestamp + next retry ETA.

R3 — Drawer (expanded view)

Tap chip → opens drawer (side-panel desktop, bottom-sheet mobile):

┌───────────────────────────────────────────┐
│  my-server                  [status_chip] │
│  https://my-server.example.com            │
├───────────────────────────────────────────┤
│  Capabilities                             │
│  ▼ Tools (12)                             │
│    • search · readOnly · low risk         │
│    • fetch_url · readOnly · low risk      │
│    • write_file · destructive · high risk │
│    ...                                    │
│  ▼ Resources (3)                          │
│    ...                                    │
│  ▼ Prompts (5)                            │
│    ...                                    │
├───────────────────────────────────────────┤
│  Settings                                 │
│  [Auto-reconnect: ON ]                    │
│  [Keep-alive ping: 30s]                   │
├───────────────────────────────────────────┤
│  [ Restart ]   [ Disable ]   [ Remove ]   │
└───────────────────────────────────────────┘

R3.1 — Capability lists

  • Tools: name + annotation tags (readOnly / destructive / idempotent) + risk tier (cross-link mcp-permission-prompt.kmd R2).
  • Resources: URI + MIME + size.
  • Prompts: name + description + argument schema preview.

R3.2 — Settings

  • Auto-reconnect (default ON): exponential backoff (1s, 2s, 4s, 8s, 16s, then 30s ceiling).
  • Keep-alive ping (default 30s): interval para ping request; failed ping triggers reconnect.
  • Per-tool permission overrides: shortcut to permission store (cross-link mcp-permission-prompt.kmd R4).

R3.3 — Actions

  • Restart: graceful disconnect + new attach. UI mostra connecting → connected (T1).
  • Disable: stop reconnect attempts; state → disabled. Persists across sessions.
  • Remove: full uninstall from registry; permission entries auto-purged (cross-link policies/identity-data-retention.kmd R5).

R4 — Error state lifecycle (lição Cursor + Claude Code bugs)

R4.1 — Auto-clear contract (NORMATIVE)

Quando state transitions de connectingconnected:

  1. Visual dot MUST update synchronously (next render frame).
  2. Error message MUST be removed from tooltip and drawer.
  3. Last error timestamp MUST be cleared.
  4. Capability lists MUST be reloaded from tools/list + resources/list + prompts/list (server may have changed).
  5. NO race condition where dot stays red while internal state is green.

R4.2 — Auto-disable threshold

After 5 consecutive connection errors within 10 minutes: state → disabled (not error). User MUST manually re-enable. Prevents zombie retry loops draining battery / quota.

Counter resets after one connected state.

R4.3 — Error categorization

Error categoryRecoverable?Display
Network timeoutYes (auto-retry)"Connection timeout · retrying in 8s"
TLS/certificateNo"TLS error · check server config"
Protocol mismatchNo"Protocol version unsupported"
Server rejected (401/403)No (user action needed)"Authentication failed · check credentials"
Server unreachable (DNS/refused)Yes (auto-retry)"Server unreachable · retrying"
Rate limit (429)Yes (delayed retry)"Rate limited · retrying in 30s"

R5 — Multi-tenant scoping

Server registry e state per (koder_user_id, workspace_id):

  • User A em workspace 1 attach my-server → visible só pra user A em workspace 1.
  • Cross-workspace switch: state chip atualiza automaticamente.
  • Shared workspace (multi-user): server visible para todos members; state global; permission decisions per-user (cross-link mcp-permission-prompt.kmd R4).

Storage: kdb-kv table mcp_servers:<koder_user_id>:<workspace_id>:<server_id>.

R6 — Surface bindings

SurfaceAPI
FlutterKoderMCPServerChip (compact) + KoderMCPServerDrawer (expanded) em koder_kit/lib/src/ai/
Web<koder-mcp-server-chip> + <koder-mcp-server-drawer>
Compose AndroidKoderMCPServerChip em koder-design-compose (futuro)
SwiftUI iOSidem em koder-design-swift (futuro)
CLI / TUIStatus line: MCP: 🟢 server1 🟡 server2 🔴 server3; koder mcp status lists detalhe

R7 — Acessibilidade

  • Chip é role="status" aria-live="polite"; state change announced.
  • Dot tem aria-label="connected" etc.
  • Drawer é role="dialog" quando aberto.
  • Keyboard: Tab to chip, Enter to expand drawer, ESC to close, arrows nav lista.
  • Reduced-motion: state transitions sem animação.
  • Color contrast: dot color MUST atender AAA contrast em ambos themes (light/dark) per themes/color-roles.kmd.

R8 — i18n

Keyen-USpt-BR
mcp.server.state.connected"Connected""Conectado"
mcp.server.state.connecting"Connecting…""Conectando…"
mcp.server.state.error"Error""Erro"
mcp.server.state.disabled"Disabled""Desativado"
mcp.server.action.restart"Restart""Reiniciar"
mcp.server.action.disable"Disable""Desativar"
mcp.server.action.remove"Remove""Remover"

T-suite

  • T1 Initial attach: state shows connecting → connected; capability count populates from tools/resources/prompts list.
  • T2 Network outage: connected → error (timeout); chip color red; tooltip mostra error message.
  • T3 Auto-retry: error → connecting → connected; R4.1 regression: error message + red dot CLEAR immediately upon connected.
  • T4 Manual restart: tap restart → connecting → connected.
  • T5 Disable: tap disable → state disabled; reconnect loop stops; capability lists hidden but cached.
  • T6 Re-enable: tap enable → connecting → connected; lists reload from server (may have changed).
  • T7 Auto-disable after 5 errors: simulate 5 timeouts within 10min → state → disabled (not error); counter resets after successful connect.
  • T8 Multi-tenant: user A attach server X → user B no workspace 2 NÃO vê server X.
  • N1 Stale state regression (Cursor #149363): connection succeeds while old error visible → assert visual updates within 1 frame.
  • N2 TLS error não dispara auto-retry (R4.3); display permanent error message.

References