Skip to content

MCP tool invocation card

ai-ui specs/ai-ui/mcp-tool-invocation.kmd

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

When this spec applies

Primary triggers

All triggers

Specification body

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. 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]                  │
└─────────────────────────────────────────────┘
SlotConteúdoNotas
Tool icontools/list[].icon se MCP server fornecer; fallback ícone genérico 🔧Resolution: 24×24dp
Tool nametools/list[].name (snake_case do MCP)Display em title-small-emphasized (cross-link themes/typography.kmd R9)
Server origin chipSlug do server (de mcp-registry)Trusted state: chip color do mcp-server-state.kmd R1
Status badgeper R3Always visible
ArgumentsJSON pretty-printedToggle: raw JSON ↔ form view (R5)
Resultper R2Auto-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:

typeRendererNotes
textMarkdown render (cross-link code-block.kmd para fenced)Default fallback
imageKoderImage widget; mime-detection via headerMax width = card width; tap-to-fullscreen
resource_linkChip clickable → opens em new surface ou inline previewResolve URI via mcp-registry resource fetcher
embedded_resourceInline render of embedded MIME (text/markdown/code/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:

EstadoBadgeColor (per themes/color-roles.kmd)
Pending"⏳ Waiting"surface-variant
Running"⚙ Running…" + breathing dotaccent
Success"✓ Done"success
Error"✗ Error" + retry buttonerror
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

SurfaceAPI
FlutterKoderMCPToolCard 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 AndroidKoderMCPToolCard em engines/sdk/koder-design-compose (quando shipar)
SwiftUI iOSKoderMCPToolCard em engines/sdk/koder-design-swift (quando shipar)
CLI / TUITexto: 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

PresetCard style
material3 / material_expressiveDefaults; shape-corner-medium
material2Filled card, shape-corner-small
terminal_classicBracket-style [tool_name], monospace, no shadow
brutalistSharp corners, 2px border, no gradient
cyberpunk_neonGlow outline em accent color, gradient backdrop
glassmorphismBackdrop blur + 20% surface tint
minimalist_monoMono 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, resource_link, embedded_resource (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.

References