You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
1.**Receives props**: `resourceUri`, `serverName`, `toolName`, `toolArgs`, `toolContent`, `status` (note: `structuredContent` is not a prop — it is fetched via the proxy endpoint during the ext-apps lifecycle)
166
166
2.**Loads MCP resource**: Fetches the HTML resource from the MCP server via the OLS proxy; falls back to locally generated HTML if unavailable
167
-
3.**Renders in iframe**: Uses `srcDoc` to display the HTML securely
168
-
4.**Handles ext-apps protocol**: Responds to `ui/initialize`, sends `ui/notifications/tool-input` (tool arguments) followed by `ui/notifications/tool-result` (tool output), proxies `tools/call` requests from the iframe to OLS, and handles `ui/notifications/size-changed` for auto-sizing
169
-
5.**Supports refresh**: Re-calls the tool endpoint when refresh is requested
167
+
3.**Renders in iframe**: Uses `srcDoc` to display the HTML securely (sandbox is `allow-scripts allow-same-origin` for ext-apps mode, `allow-scripts` for fallback)
168
+
4.**Handles ext-apps protocol**: Responds to `ui/initialize`, sends `ui/notifications/host-context-changed` (theme), `ui/notifications/tool-input` (tool arguments), then `ui/notifications/tool-result` (fresh data from proxy call), proxies `tools/call` and `tools/list` requests from the iframe to OLS, and handles `ui/notifications/size-changed` for auto-sizing
169
+
5.**Supports refresh**: Card header button re-calls the tool endpoint and pushes new data into the iframe
When the iframe's ext-apps SDK sends `ui/notifications/initialized`, MCPAppFrame sends two notifications in order:
173
+
When the iframe's ext-apps SDK sends `ui/notifications/initialized`, MCPAppFrame sends three notifications in order:
174
174
175
-
**1. `ui/notifications/tool-input`** — carries the tool's input arguments:
175
+
**1. `ui/notifications/host-context-changed`** — sends the current theme:
176
+
177
+
-`theme`: `"dark"` or `"light"`
178
+
179
+
This ensures apps using the `onhostcontextchanged` callback can apply the correct theme immediately after initialization.
180
+
181
+
**2. `ui/notifications/tool-input`** — carries the tool's input arguments:
176
182
177
183
-`arguments`: the complete tool call arguments from the LLM (e.g., `{ query: "up{}", step: "1m", title: "Uptime" }`)
178
184
179
185
This matches the [`McpUiToolInputNotification`](https://modelcontextprotocol.github.io/ext-apps/api/interfaces/app.McpUiToolInputNotification.html) spec. MCP Apps can use this to display context about what was queried (e.g., chart title, PromQL query string, filter parameters) without needing to embed that information in the tool result.
180
186
181
-
**2. `ui/notifications/tool-result`** — carries the tool execution result:
187
+
**3. `ui/notifications/tool-result`** — carries a fresh tool execution result:
182
188
183
-
-`content`: `[{ type: "text", text: toolContent }]` (real tool output, not a placeholder)
189
+
The component calls the tool **again** via `POST /mcp-apps/tools/call` (not using the already-streamed content) so the iframe receives structured data in the ext-apps format:
-`structuredContent`: the structured data from the tool result (if available)
185
-
-`isError`: `true` when `status === "error"`
193
+
-`isError`: `true` when the tool returned an error
194
+
195
+
If the proxy call fails, MCPAppFrame falls back to the streamed text content: `content: [{ type: "text", text: toolContent }]`.
186
196
187
197
This matches the [`McpUiToolResultNotification`](https://modelcontextprotocol.github.io/ext-apps/api/interfaces/app.McpUiToolResultNotification.html) spec and ensures the iframe's `app.ontoolresult` callback receives real data on first load.
188
198
189
-
Both notifications are also re-sent on refresh so the app can update headers/context alongside the data.
199
+
All three notifications are also re-sent on refresh (triggered by the card header button) so the app can update headers/context alongside the data.
190
200
191
201
#### Immutable.js caveat for tool arguments
192
202
193
203
Tool arguments are stored in Redux via Immutable.js `mergeIn`, which deep-converts plain objects into Immutable Maps. When extracting `toolArgs` from the store, `.toJS()` must be called to convert back to a plain JS object — `postMessage` uses the structured clone algorithm which cannot clone Immutable class instances, resulting in empty/mangled objects in the iframe.
194
204
195
205
The conversion is wrapped in `React.useMemo` (keyed on the raw Immutable Map reference) to avoid creating a new object on every render. Without memoization, every Redux state change would produce a new `toolArgs` reference, causing `MCPAppFrame`'s `useEffect` hooks to re-fire and reload iframe content.
196
206
197
-
#### Generated HTML Features
207
+
#### Generated HTML Fallback
198
208
199
-
-**Summary cards**: Total pods, average CPU, average memory
200
-
-**Data table**: Pod names with CPU/memory bars (color-coded by severity)
201
-
-**Refresh button**: Triggers data reload via `postMessage`
202
-
-**Auto-resize**: Iframe height adjusts to content (starts small, grows/shrinks via resize messages, clamped to 60–960px)
203
-
-**Theme support**: Adapts to dark/light mode
209
+
When the MCP server's HTML resource is unavailable, MCPAppFrame falls back to calling the tool via the proxy endpoint and rendering the result generically:
210
+
211
+
-**`generateGenericDataHtml`**: Renders `structuredContent` (or the raw response) as formatted JSON in a `<pre>` block with the tool name as a heading
212
+
-**`wrapHtmlContent`**: Wraps raw HTML text content in a minimal styled page (used when no structured data is available)
213
+
-**Auto-resize**: Both generated pages include a `ResizeObserver` that sends `mcp-app-resize` messages to auto-size the iframe
214
+
-**Theme support**: Background and text colors adapt to dark/light mode via inline styles
204
215
205
216
#### Iframe Auto-Sizing
206
217
@@ -221,41 +232,48 @@ The iframe communicates with the parent via `postMessage`:
0 commit comments