This episode explores the official MCP Apps extension: tools that return interactive interfaces (forms, mini dashboards, visual confirmations) rendered by the host in a sandboxed iframe, complete with the security limitations of that sandbox.

Episode 10 secured the server with OAuth 2.1 and SSRF/CRLF mitigations. Now it's the turn of the most visually interesting official extension: MCP Apps & UI Resources. The concept: a tool doesn't just return text or JSON — it can return an interactive interface that the host renders in an isolated iframe. Forms, mini dashboards, even visual confirmations become "alive" results.
Roadmap: the definition of MCP Apps and its position as an extension, the structure of the UI result returned by a tool, real use cases (forms, dashboards, confirmations), the rendering mechanism inside a sandboxed iframe, and the security limitations that come with it.
MCP Apps is an official extension outside the core protocol. Its main purpose is to give tools the ability to present user interfaces without forcing the host to change its architecture. In the 2026-07-28 specification, this extension was formalized as part of the official ecosystem — alongside other extensions like MCP Tasks, which we'll cover in episode 12.
Why does this matter? Imagine a "show sales report" tool. With a plain text response, the result is just numbers and a raw table.
With UI resources, the tool returns an HTML snippet that renders into a small dashboard — users see the data as an interactive chart right inside the host application, while the model still holds the context as structured data.
Info
MCP Apps doesn't let tools call out freely. The rendered UI runs inside a sandboxed iframe with strict limits — details in the security limitations section.
Conceptually, when a tool execution completes, the server can include a UI resource alongside the regular content text. The host reads the UI description and renders it. A response structure containing a UI looks roughly like this:
{
"jsonrpc": "2.0",
"id": 5,
"result": {
"content": [
{ "type": "text", "text": "Ringkasan penjualan bulan Juli" }
],
"uiResource": {
"kind": "html",
"title": "Dashboard Penjualan",
"source": "<div id='chart'></div><script>renderChart()</script>"
}
}
}The host will decide whether it can render the uiResource (its UI capability) or fall back to the plain content text. This is important: a tool must always return meaningful content, because not all hosts support UI rendering. The UI is an enhancement, not a replacement.
The three most common usage patterns:
A healthy design pattern: the UI captures user input, then the result is converted into a follow-up request (with the same messageId if needed). That way the model keeps seeing the context as structured data, not as an HTML render.
On the host side, a UI resource is rendered inside a sandboxed <iframe>. The sandbox attribute limits what the iframe can do:
const iframe = document.createElement("iframe");
iframe.src = "/mcp-apps/session-42";
iframe.sandbox = "allow-scripts allow-forms";
iframe.setAttribute("referrerpolicy", "no-referrer");
document.body.appendChild(iframe);Using sandbox without allow-same-origin means code inside the iframe can't access the host origin — no cookie, no localStorage, no access to the main page's DOM. allow-scripts lets the UI's JavaScript run, allow-forms allows forms; combining both without allow-same-origin actually forces the iframe into a safer isolated origin.
Communication out of the iframe happens through structured messages (e.g. postMessage) validated by the host, not through direct access. The host is the gatekeeper between the not-necessarily-trusted UI and the rest of the application. When a user presses a button in the UI, the result is sent out, and the host forwards it to the next tool call:
window.addEventListener("message", (event) => {
if (event.data.type !== "mcp-app-result") return;
const args = event.data.values;
callTool("checkout-submit", args);
});checkout-submit is still an ordinary MCP request — the values from the iframe only come in as arguments. Data from an unvalidated UI is treated as dangerous as any other user input.
In reality, a sandbox isn't an absolute wall — it's a limitation that must be managed. Things to watch out for:
allow-same-origin isolates the code, but any token leaked inside the UI can still be used by that tool itself.referrerpolicy="no-referrer" to prevent URL leaks.In summary, treat UI resources as untrusted code: isolation is the last line of defense, while input validation and authorization are the primary defenses that must be in place before the UI is rendered.
Danger
MCP Apps adds attack surface to the host. The sandbox protects the host from the UI, but it doesn't protect users from a malicious tool — tool authorization (episode 10) and input validation remain the primary lines of defense.
Episode 11 introduced the MCP Apps & UI Resources extension: tools can now return interactive interfaces rendered by the host in a sandboxed iframe — forms, mini dashboards, and visual confirmations — without sacrificing security isolation. You also understand the sandbox's limitations and the validation practices that must be maintained.
Key takeaways:
content as a fallback.allow-same-origin isolates the UI code from the host.In the next episode 12, the last of this phase, we cover the second official extension: MCP Tasks — long-running tasks with lifecycle, state, and progress that integrate with agentic workflow patterns. See you there!