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."

Spec — MCP tool invocation card

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. *isibility-first*— todo tool call MUST aparecer na UI imediatamente, mesmo durante streaming.
  2. *o silent invocations*— tool calls não documentados visualmente violam spec MCP.
  3. *rgs + result sidebyside*— usuário consegue auditar input e output.
  4. *runcation com escape*— outputs grandes truncados mas copytoclipboard 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 (crosslink `codeblock.kmd` para fenced) Default fallback
image KoderImage widget; mime-detection via header Max width = card width; taptofullscreen
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; taptofullscreen overlay.
  • Tool call args > 100 lines: collapse com "Expand args".

*opytoclipboard 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 é readonly (já invocado); modo editandretry vai em ticket separado #reinvocation (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).
  • Crosstenant lookup retorna 404 (não 403, per multitenancy 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; shapecornermedium
material2 Filled card, shapecornersmall
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:

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

Source: ../home/koder/dev/koder/meta/docs/stack/specs/ai-ui/mcp-tool-invocation.kmd