Skip to content

Commit 581cbf8

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

1 file changed

Lines changed: 62 additions & 0 deletions

File tree

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

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

Lines changed: 62 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,40 @@ 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. **Base64 in `structuredContent`** - Goes to model as nonsense text. Use `_meta` for binary data
344+
10. **No text in `content`** - Always return text describing the UI so model knows what was rendered
345+
346+
## Data Handling: content vs structuredContent vs _meta
347+
348+
Both `content` and `structuredContent` go to model context. `_meta` does not.
349+
350+
- **`content`**: Text, images, audio, video (model processes if capable)
351+
- **`structuredContent`**: JSON for model + UI (no base64 - model sees garbage text)
352+
- **`_meta`**: Binary blobs, large data - UI only, never sent to model
353+
354+
```typescript
355+
// ❌ structuredContent with base64 = model sees nonsense
356+
return { structuredContent: { pdfData: base64Pdf } };
357+
358+
// ✅ Summary for model, binary in _meta for UI
359+
return {
360+
content: [{ type: "text", text: "Generated invoice #1234" }],
361+
_meta: { pdfData: base64Pdf }
362+
};
363+
```
364+
365+
### Always describe UI in content
366+
367+
Return text in `content` so the model knows a UI was rendered:
368+
369+
```typescript
370+
return {
371+
content: [{ type: "text", text: "Displaying weather dashboard for San Francisco" }],
372+
structuredContent: { temperature: 72, conditions: "sunny" }
373+
};
374+
```
375+
376+
Without this, the model has no idea the tool rendered anything.
315377

316378
## Testing
317379

0 commit comments

Comments
 (0)