MCP tool invocation card

mandatory

Visual indicator + arguments preview + result rendering for every MCP tool call executed by a Koder client. Implements the SHOULD-level UI requirement from MCP spec 2025-11-25 (Tools section): "Applications SHOULD provide UI that makes clear which tools are being exposed to the AI model, insert clear visual indicators when tools are invoked, and present confirmation prompts."

MCP normative source: https://modelcontextprotocol.io/specification/2025-11-25/server/tools. Companion: mcp-permission-prompt.kmd (consent gate antes do invoke), mcp-server-state.kmd (status do server que hospeda o tool).

Princípios

  1. Visibility-first — todo tool call MUST aparecer na UI imediatamente, mesmo durante streaming.
  2. No silent invocations — tool calls não documentados visualmente violam spec MCP.
  3. Args + result side-by-side — usuário consegue auditar input e output.
  4. Truncation com escape — outputs grandes truncados mas copy-to-clipboard sempre full.

R1 — Anatomia

┌─────────────────────────────────────────────┐
│  [🔧] tool_name           [status_badge]    │  ← header (clickable)
│  Tool from <server_origin_chip>             │
├─────────────────────────────────────────────┤
│  ▼ Arguments                                │  ← body (collapsed by default)
│  { "query": "...", "limit": 10 }            │
├─────────────────────────────────────────────┤
│  ▼ Result                                   │  ← footer (auto-expand on success)
│  [content rendered per R2]                  │
└─────────────────────────────────────────────┘
Slot Conteúdo Notas
Tool icon tools/list[].icon se MCP server fornecer; fallback ícone genérico 🔧 Resolution: 24×24dp
Tool name tools/list[].name (snake_case do MCP) Display em title-small-emphasized (cross-link themes/typography.kmd R9)
Server origin chip Slug do server (de mcp-registry) Trusted state: chip color do mcp-server-state.kmd R1
Status badge per R3 Always visible
Arguments JSON pretty-printed Toggle: raw JSON ↔ form view (R5)
Result per R2 Auto-expand on success; collapsed on error

R2 — Result content types

MCP tools/call retorna content[] array. Cada item tem type ∈ {text, image, resource_link, embedded_resource}. Card MUST renderizar todos os 4 tipos:

type Renderer Notes
text Markdown render (cross-link code-block.kmd para fenced) Default fallback
image KoderImage widget; mime-detection via header Max width = card width; tap-to-fullscreen
resource_link Chip clickable → opens em new surface ou inline preview Resolve URI via mcp-registry resource fetcher
embedded_resource Inline render of embedded MIME (textmarkdowncode/image) Same renderer pipeline as standalone content

Multiple content items: render sequencialmente em ordem, separados por divider de 1dp.

R3 — Status states

Badge no header per estado do tools/call:

Estado Badge Color (per themes/color-roles.kmd)
Pending "⏳ Waiting" surface-variant
Running "⚙ Running…" + breathing dot accent
Success "✓ Done" success
Error "✗ Error" + retry button error
Cancelled "⊘ Cancelled" text-muted

Transições: pending → running → (success | error | cancelled). Card NUNCA pula direto de pending pra success (sempre passa por running).

R4 — Truncation

Outputs grandes (>2000 chars text, >500KB image):

  • Text: collapse com "Show more" link após 30 linhas; full text mantido em DOM mas visualmente clipped.
  • Image: thumbnail por default; tap-to-fullscreen overlay.
  • Tool call args > 100 lines: collapse com "Expand args".

Copy-to-clipboard SEMPRE full, independente do estado visual.

R5 — Schema-driven form view (opcional)

Quando MCP tools/list[].inputSchema está presente (JSON Schema), card SHOULD oferecer toggle "Raw JSON" ↔ "Form view":

  • Form view renderiza inputs schema-aware (string → text field, number → spinner, enum → segmented control, boolean → switch).
  • Required fields marcados com asterisco; help text de description.
  • Form é read-only (já invocado); modo edit-and-retry vai em ticket separado #re-invocation (futuro).

R6 — Surface bindings

Surface API
Flutter KoderMCPToolCard em engines/sdk/koder_kit/lib/src/ai/mcp_tool_card.dart
Web <koder-mcp-tool-card> web component em engines/sdk/koder_web_kit
Compose Android KoderMCPToolCard em engines/sdk/koder-design-compose (quando shipar)
SwiftUI iOS KoderMCPToolCard em engines/sdk/koder-design-swift (quando shipar)
CLI / TUI Texto: header em 1 linha + args + result; bubbletea expandable em TUI

Surfaces MUST exportar API consistente (constructor + state callbacks), diferindo apenas em props específicos do framework.

R7 — Multi-tenant + storage

Tool invocation persistida pra conversation history (cross-link #115) respeita policies/multi-tenant-by-default.kmd:

  • Storage key: (koder_user_id, workspace_id, conversation_id, message_id, tool_call_id).
  • Cross-tenant lookup retorna 404 (não 403, per multi-tenancy contract).
  • Audit log da invocation persistido em services/foundation/audit/ com user + workspace + tool + args hash.

R8 — Acessibilidade

  • Card é semanticamente <article role="region" aria-label="Tool invocation: <name>">.
  • Status badge anuncia mudança via aria-live="polite".
  • Args e result são <details><summary> em Web; equivalente expansível em Flutter/Compose.
  • Reduced-motion: skip breathing dot animation; status muda direto.
  • Screen reader: anuncia tool name + server origin + status; args só ao expandir.

R9 — Per-preset variation

Preset Card style
material3 / material_expressive Defaults; shape-corner-medium
material2 Filled card, shape-corner-small
terminal_classic Bracket-style [tool_name], monospace, no shadow
brutalist Sharp corners, 2px border, no gradient
cyberpunk_neon Glow outline em accent color, gradient backdrop
glassmorphism Backdrop blur + 20% surface tint
minimalist_mono Mono fonts, single border, no fill

T1 — Test contract

Toda surface implementadora MUST passar:

  • T1 Mount: render card com estado pending + tool name visível + args collapsed.
  • T2 Expand args: tap header → args visible; tap novamente → collapse.
  • T3 Status transition: pending → running → success → render result; assert badge atualiza.
  • T4 Error + retry: status error → retry button visible → tap → status volta a running.
  • T5 Each content type: text, image, resourcelink, embeddedresource (4 sub-tests).
  • T6 Truncation: text > 2000 chars → "Show more" visible; expand → full text.
  • T7 Copy-to-clipboard: copy ação copia full args (não truncado).
  • T8 A11y: screen reader announces status change; reduced-motion skips breathing dot.