Skip to content

Commit f637900

Browse files
committed
docs(skill): add UI support check and data handling gotchas
1 parent f6ee5d5 commit f637900

1 file changed

Lines changed: 52 additions & 0 deletions

File tree

  • plugins/mcp-apps/skills/create-mcp-app

plugins/mcp-apps/skills/create-mcp-app/SKILL.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,34 @@ Host calls tool → Server returns result → Host renders resource UI → UI re
4141
- Register tools and resources
4242
- Configure build system with `vite-plugin-singlefile`
4343

44+
### Checking Host UI Support
45+
46+
Not all hosts support MCP Apps. Use `hasUiSupport()` to conditionally register UI-enabled tools:
47+
48+
```typescript
49+
import { hasUiSupport, registerAppTool } from "@modelcontextprotocol/ext-apps/server";
50+
51+
server.oninitialized = ({ clientCapabilities }) => {
52+
if (hasUiSupport(clientCapabilities)) {
53+
// Register tool with UI
54+
registerAppTool(server, "weather", {
55+
description: "Get weather with interactive dashboard",
56+
_meta: { ui: { resourceUri: "ui://weather/dashboard" } },
57+
}, weatherHandler);
58+
} else {
59+
// Register text-only fallback for hosts without UI support
60+
server.registerTool("weather", {
61+
description: "Get weather as text",
62+
}, textWeatherHandler);
63+
}
64+
};
65+
```
66+
67+
**Key points:**
68+
- Check `clientCapabilities` in `oninitialized` callback
69+
- `hasUiSupport()` checks both `experimental` and `extensions` fields
70+
- Provide text-only fallback for non-UI hosts
71+
4472
## Getting Reference Code
4573

4674
Clone the SDK repository for working examples and API documentation:
@@ -312,6 +340,30 @@ See `examples/shadertoy-server/` for complete implementation.
312340
6. **No text fallback** - Always provide `content` array for non-UI hosts
313341
7. **Hardcoded styles** - Use host CSS variables for theme integration
314342
8. **No streaming for large inputs** - Use `ontoolinputpartial` to show progress during generation
343+
9. **Non-visual binary data in `content` or `structuredContent`** - Use `_meta` instead
344+
345+
## Data Handling: content vs structuredContent vs _meta
346+
347+
| Field | Goes to model? | Use for |
348+
|-------|---------------|---------|
349+
| `content` | ✅ YES | Text + images (model sees with vision) |
350+
| `structuredContent` | ✅ YES (as text) | Structured JSON for model + UI |
351+
| `_meta` | ❌ NO | PDFs, audio, large blobs - UI only |
352+
353+
**Critical**: Non-visual binary data (PDFs, audio, etc.) in `content` or `structuredContent` wastes tokens as nonsense base64. Use `_meta`.
354+
355+
```typescript
356+
// ❌ WRONG - model sees meaningless base64 string
357+
return {
358+
structuredContent: { pdfData: base64Pdf }
359+
};
360+
361+
// ✅ CORRECT - model gets summary, UI gets PDF from _meta
362+
return {
363+
content: [{ type: "text", text: "Generated invoice #1234" }],
364+
_meta: { pdfData: base64Pdf, mimeType: "application/pdf" }
365+
};
366+
```
315367

316368
## Testing
317369

0 commit comments

Comments
 (0)