@@ -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
4674Clone the SDK repository for working examples and API documentation:
@@ -312,6 +340,40 @@ See `examples/shadertoy-server/` for complete implementation.
3123406 . ** No text fallback** - Always provide ` content ` array for non-UI hosts
3133417 . ** Hardcoded styles** - Use host CSS variables for theme integration
3143428 . ** 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