modelcontextprotocol / modelcontextprotocol/ext-apps

Bug Report: MCP Apps UI Resource Not Rendering in Claude Desktop / claude.ai

Open
#671 23 comments 9 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
TypeScript
Stars
2.9k
Forks
387
Avg merge
3h 21m
Merged PRs (30d)
6

Description

Bug Report: MCP Apps UI Resource Not Rendering in Claude Desktop / claude.ai
Reporter: Ernst Weyhausen (AMFXT — amfxt.com)
Date: 27 May 2026
Severity: Feature-blocking
Component: Claude Desktop (Windows) / claude.ai — MCP Apps iframe rendering


Summary
Claude (both Desktop and claude.ai) successfully negotiates io.modelcontextprotocol/ui capability with our MCP server, receives structuredContent alongside content in tool results, fetches the UI resource via resources/read, but does not render the HTML resource in an iframe. Instead, the text-only fallback message is shown:
[This tool call rendered an interactive widget in the chat. The user can already see the result — do not repeat it in text or with another visualization tool.]
No iframe or embedded UI is visible to the user.


Environment
• Claude Desktop: Latest Windows version (installed via winget install Anthropic.Claude, confirmed up to date 27 May 2026)
• claude.ai: Web interface, same account
• MCP Server: Custom Node.js/Express JSON-RPC handler at https://amfxt.com (also tested on http://localhost:1000)
• MCP Protocol Version: 2025-11-25
• MCP Apps Spec: SEP-1865, stable 2026-01-26
• SDK reference version: @modelcontextprotocol/ext-apps@1.7.2
• Connection method (claude.ai): Connected via claude.ai MCP connector settings
• Connection method (Desktop): mcp-remote proxy in claude_desktop_config.json
• OS: Windows 11


What Works (verified independently)

  1. Capability Negotiation ✅
    Claude sends io.modelcontextprotocol/ui in the initialize request. Server logs confirm:
    Initialize [ui=true]
  2. Resource Discovery ✅
    Claude calls resources/list and receives the UI resource declaration:
    {
    "resources": [{
    "uri": "ui://amfxt/slidetune",
    "name": "SlideTune Flight Comparison",
    "description": "Interactive fare comparison with bags, pets, equipment pricing, sorting and filtering",
    "mimeType": "text/html;profile=mcp-app"
    }]
    }
  3. Tool Registration with _meta.ui ✅
    tools/list returns the tool with the correct UI linkage:
    {
    "name": "AMFXT",
    "_meta": {
    "ui": {
    "resourceUri": "ui://amfxt/slidetune"
    }
    }
    }
  4. Tool Result with content + structuredContent ✅
    tools/call returns both text fallback and structured data per spec:
    {
    "content": [{ "type": "text", "text": "{...full JSON...}" }],
    "structuredContent": { "route_summary": {...}, "Swiss (LX)": [...], ... },
    "isError": false
    }
  5. Resource Read ✅
    resources/read returns the HTML resource with correct MIME type and CSP:
    {
    "contents": [{
    "uri": "ui://amfxt/slidetune",
    "mimeType": "text/html;profile=mcp-app",
    "text": "...2200 lines of interactive UI...",
    "_meta": {
    "ui": {
    "csp": {
    "resourceDomains": ["https://fonts.googleapis.com", "https://fonts.gstatic.com"]
    },
    "prefersBorder": false
    }
    }
    }]
    }
  6. End-to-End Rendering in basic-host ✅
    The identical server + UI combination renders correctly in the SDK's basic-host test harness (@modelcontextprotocol/ext-apps examples). The HTML:
    • Receives structuredContent via postMessage (ui/notifications/tool-result)
    • Parses flight data via loadFlightData()
    • Renders interactive flight cards with sorting, filtering, bag sliders, price breakdowns
    • Responds to host theme changes
    Screenshot of working basic-host rendering attached separately.
  7. MCP Inspector ✅
    MCP Inspector confirms the full JSON-RPC exchange is correct — tools/call returns valid content + structuredContent, resources/read returns valid HTML.

What Does Not Work
Claude Desktop & claude.ai: No iframe rendered ❌
Despite the complete and correct protocol exchange above, no iframe appears in the conversation. The user sees only the text:
[This tool call rendered an interactive widget in the chat. The user can already see the result — do not repeat it in text or with another visualization tool.]
This message indicates the backend/model layer acknowledges a widget should be rendered, but the frontend does not display it.


HTML Resource Details
The UI resource (SlideTune) is a self-contained ~2200-line HTML file implementing the MCP Apps lifecycle per SEP-1865:
• Sends ui/initialize request with protocolVersion: '2026-01-26'
• Sends ui/notifications/initialized notification after handshake
• Handles ui/notifications/tool-input and ui/notifications/tool-result
• Handles ui/notifications/host-context-changed (theme, safe area insets)
• Responds to ui/resource-teardown
• Uses postMessage for all communication (no SDK dependency — raw JSON-RPC per spec)
• Has baked-data fallback: works both as standalone HTML (direct URL) and as MCP App (iframe with postMessage data delivery)
• CSP requirements: Google Fonts only (fonts.googleapis.com, fonts.gstatic.com)


Steps to Reproduce

  1. Connect to MCP server at https://amfxt.com (or http://localhost:1000 locally)
  2. Verify Initialize [ui=true] in server logs
  3. Ask: "Fly me from MLA to ZRH on 20 June"
  4. Observe: tool call succeeds, data returned, but no UI rendered

Expected Behavior
Per SEP-1865 §"Host calls tool → Host renders resource UI → Server returns result → UI receives result":

  1. Host calls tools/call → receives result with structuredContent
  2. Host fetches resource via resources/read using the resourceUri from _meta.ui
  3. Host renders HTML in a sandboxed iframe
  4. Host sends ui/notifications/tool-result to iframe via postMessage
  5. UI renders interactive flight comparison

Actual Behavior
Steps 1-2 occur (confirmed by server logs). Steps 3-5 do not occur. No iframe is created. The model's text response references a widget that the user cannot see.


Questions for Anthropic

  1. Is MCP Apps UI rendering enabled in Claude Desktop for Windows as of May 2026?
  2. Is there a feature flag, account setting, or plan tier requirement to enable it?
  3. Does mcp-remote proxy correctly forward the io.modelcontextprotocol/ui extension capability? (Claude Desktop config uses mcp-remote for the connection)
  4. The "url" key in claude_desktop_config.json (for direct Streamable HTTP without mcp-remote) was rejected with "not a valid MCP configuration" — is this supported?
  5. Is there a list of hosts that currently render MCP Apps? The spec's clients page references host support varying.

Attachments
• Server logs showing complete protocol exchange
• Screenshot of successful rendering in basic-host test harness
Initialize [ui=true]
===>>> method: [notifications/initialized] -> result: [{}]
===>>> method: [resources/list] -> result: [{"resources":[{"uri":"ui://amfxt/slidetune","name":"SlideTune Flight Comparison","description":"Interactive fare comparison with bags, pets, equipment pricing, sorting and filtering","mimeType":"text/html;profile=mcp-app"}]}]
===>>> method: [resources/list] -> result: [{"resources":[{"uri":"ui://amfxt/slidetune","name":"SlideTune Flight Comparison","description":"Interactive fare comparison with bags, pets, equipment pricing, sorting and filtering","mimeType":"text/html;profile=mcp-app"}]}]
===>>> method: [tools/list] -> result: [{"tools":[{"name":"AMFXT","annotations":{"title":"AMFXT","readOnlyHint":true,"openWorldHint":true,"destructiveHint":false,"idempotentHint":false},"description":"Unified flight search, airline equipment policy, and pet transport policy tool. One tool, one call.\n\nACTION DISPATCH — set 'action' to choose mode:\n\n FLIGHTS:\n flight_search → Search flights with all-inclusive pricing (bags, pets, equipment). Supports multiple extras in one search (e.g. pet + bicycle). Returns fares across 6 classes, on-time stats, star ratings, CO₂, booking links. Supports wildcard discovery ('---').\n\n EQUIPMENT POLICIES (29 types × 600+ airlines):\n equipment_lookup → One airline + one equipment type → full policy detail\n equipment_airline → One airline → all 29 equipment policies\n equipment_compare → One equipment type across 2-5 airlines\n equipment_search → All airlines for one equipment type (with optional filter)\n\n PET POLICIES (12 modes × 21 species × 626+ airlines):"}],"_meta":{"ui":{"resourceUri":"ui://amfxt/slidetune"}}}]}]
Initialize [ui=true]
===>>> method: [notifications/initialized] -> result: [{}]
===>>> method: [tools/list] -> result: [{"tools":[{"name":"AMFXT","annotations":{"title":"AMFXT","readOnlyHint":true,"openWorldHint":true,"destructiveHint":false,"idempotentHint":false},"description":"Unified flight search, airline equipment policy, and pet transport policy tool. One tool, one call.\n\nACTION DISPATCH — set 'action' to choose mode:\n\n FLIGHTS:\n flight_search → Search flights with all-inclusive pricing (bags, pets, equipment). Supports multiple extras in one search (e.g. pet + bicycle). Returns fares across 6 classes, on-time stats, star ratings, CO₂, booking links. Supports wildcard discovery ('---').\n\n EQUIPMENT POLICIES (29 types × 600+ airlines):\n equipment_lookup → One airline + one equipment type → full policy detail\n equipment_airline → One airline → all 29 equipment policies\n equipment_compare → One equipment type across 2-5 airlines\n equipment_search → All airlines for one equipment type (with optional filter)\n\n PET POLICIES (12 modes × 21 species × 626+ airlines):"}],"_meta":{"ui":{"resourceUri":"ui://amfxt/slidetune"}}}]}]
===>>> method: [resources/list] -> result: [{"resources":[{"uri":"ui://amfxt/slidetune","name":"SlideTune Flight Comparison","description":"Interactive fare comparison with bags, pets, equipment pricing, sorting and filtering","mimeType":"text/html;profile=mcp-app"}]}]

Image

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the SEP-1865 host-rendering flow and the basic-host harness in the @modelcontextprotocol/ext-apps examples, then compare it with Claude Desktop's mcp-remote path and claude_desktop_config.json. Done means determining whether Claude host support or capability forwarding is responsible, with a reproducible conclusion or a clearly scoped SDK/spec issue.

Written by the indexing model from the issue text.

Assessment

Tech stack
express, node.js, typescript
Domain
api, backend-api-design, frontend
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
32/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.