MCP tool invocation card
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
- *isibility-first*— todo tool call MUST aparecer na UI imediatamente, mesmo durante streaming.
- *o silent invocations*— tool calls não documentados visualmente violam spec MCP.
- *rgs + result side
byside*— usuário consegue auditar input e output. - *runcation com escape*— outputs grandes truncados mas copy
toclipboard 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 |
Default fallback |
image |
KoderImage widget; mime-detection via header |
Max width = card width; tap |
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
tofullscreen 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 é read
only (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). - Cross
tenant 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; shape |
material2 |
Filled card, shape |
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*Copy
toclipboard: copy ação copia full args (não truncado). - *8*A11y: screen reader announces status change; reduced-motion skips breathing dot.
Cross-link
- Companion:
mcp-permission-prompt.kmd,mcp-server-state.kmd - Consumer pattern:
chat-message-bubble.kmdhospeda tool cards inline - Code rendering:
code-block.kmd(referenciado em R2 text type) - Tokens visuais:
themes/color-roles.kmd,themes/typography.kmdR9 - Backend:
services/ai/mcp/,services/ai/mcp-registry/ - MCP normative: https://modelcontextprotocol.io/specification/2025-11-25/server/tools