[BUG] Cowork renders MCP App iframes but never delivers tool results (ontoolresult never fires)
Summary
Cowork renders MCP App iframes (axes, threshold lines, search boxes all visible) but never delivers tool results to the app via ontoolresult. The app HTML loads and initializes, but the postMessage bridge between the Cowork host and the iframe never completes, so the app displays an empty visualization with no data.
Tool output in Cowork
<img width="781" height="595" alt="Image" src="https://github.com/user-attachments/assets/aac0f54e-c9ee-4e48-9932-8f787404251c" />
The plot is rendered, but not the data.
Same tool output in Chat
<img width="813" height="614" alt="Image" src="https://github.com/user-attachments/assets/997e2798-2067-4fc9-915a-6328bc65c5b9" />
The plot is rendered with the data.
Steps to reproduce
- Register an MCP server that exposes a tool with
app=AppConfig(resourceUri="ui://...")(e.g., a volcano plot using Plotly via the MCP Apps ext-apps SDK) - Call the tool from Claude Chat (Desktop) -- the app renders with data correctly
- Call the same tool with the same parameters from Cowork -- the app iframe renders (axes, grid lines, UI controls are visible) but no data points appear
Expected behavior
The MCP App iframe should either:
- Receive the tool result via
ontoolresultand render the data (same as Claude Chat), OR - Not render the iframe at all if Cowork doesn't support the full MCP Apps protocol, so the user sees the text fallback instead of a confusing empty visualization
Actual behavior
- The app HTML is fetched via
resources/readand rendered in an iframe - The tool executes successfully and returns valid JSON (confirmed via server logs:
isError: false) - But
ontoolresultnever fires in the app JavaScript - The user sees an empty chart frame with no data and no error message
Environment
- Claude Desktop: latest (auto-updated as of April 2026)
- macOS (Apple Silicon)
- MCP server: FastMCP 3.0.1, Python 3.12
- ext-apps SDK:
@modelcontextprotocol/ext-apps@0.4.0
Analysis
From server logs, Cowork's MCP client:
- Successfully completes the
initializehandshake - Fetches
ui://resources viaresources/read - Executes tool calls and receives valid JSON results
The failure is between the Cowork host receiving the tool result and delivering it to the iframe via the postMessage bridge. This is consistent with the race condition described in modelcontextprotocol/ext-apps#542, where PostMessageTransport requires iframe.contentWindow at construction time, but srcdoc iframes may start executing before the host's transport is listening.
Related issues
- modelcontextprotocol/ext-apps#542 -- PostMessageTransport race condition with srcdoc iframes (fix PR #543 pending)
- modelcontextprotocol/ext-apps#482 -- Claude fetches ui:// resources but doesn't mount iframe / start bridge
- #48132 -- Feature request for MCP Apps rendering in Claude Code Preview tool
- #4496 --
structuredContentstripped from tool results in Claude Code - #22619 -- "The CLI has no iframe runtime, so MCP Apps simply don't render in Claude Code"
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗