
An MCP server used to be able to return text, images, structured data and file-like resources. The host would display them as part of the conversation and that was the end of its influence over your screen. An extension to the protocol, MCP Apps, changes that. In the documentation's words, MCP Apps "let servers return interactive HTML interfaces (data visualizations, forms, dashboards) that render directly in the chat".
That is a bigger change than it sounds, and it comes with a restriction that is easy to miss and expensive to discover late. Client support is only half of what you need.
Everything below is read off two pages of the Model Context Protocol documentation, the MCP Apps overview and the extension support matrix, as they stood on 13 September 2026. Extension support moves, and one of those two pages says outright that it is maintained by the community, so treat the client names here as a snapshot and check the matrix itself before you plan around any of them.
If you are new to the protocol underneath this, what MCP actually does, as opposed to function calling and agent loops is the piece to read first. This one assumes it.
What the extension actually adds#
The mechanism is deliberately small. It reuses two primitives the protocol already had, rather than inventing a UI channel.
A tool declares a reference to a UI resource in its description, through a _meta.ui.resourceUri field pointing at a ui:// resource. The host can fetch that resource before the tool is ever called, which is what makes it possible to stream a tool's inputs into the interface while the model is still assembling them. The resource itself is an HTML page, usually bundled with its own JavaScript and CSS.
So the flow is: the model decides to call a tool, the host notices the tool points at a UI resource, the host fetches that resource, and the host renders it in place of a block of text.
Once it is on screen, the interface is not a screenshot. It talks back. The app and the host exchange JSON-RPC messages over postMessage, in what the documentation calls "its own dialect of MCP". Some of those messages are the ordinary protocol, tools/call among them. Others are new and carry a ui/ prefix. The practical effect is that the interface can call tools on the server that served it, and the host can push fresh results back into the interface without another turn of conversation.
The extension has an identifier, io.modelcontextprotocol/ui, and that identifier is the thing both ends negotiate on. Hold that thought.
What the sandbox stops#
This is the part worth reading closely if you are the one deciding whether to turn it on, because a server you do not control is now putting executable markup on a user's screen.
The documentation is specific. MCP Apps run in a sandboxed iframe, and the sandbox "prevents your app from accessing the parent window's DOM, reading the host's cookies or local storage, navigating the parent page, or executing scripts in the parent context". All communication between the app and the host goes through postMessage. There is no other channel.
On top of that, capability is granted rather than assumed. "The host controls which capabilities your app can access." A host may restrict which tools an app is allowed to call, and may disable individual abilities such as opening links. An app that wants a microphone or a camera has to request it through a permissions entry on the resource's _meta.ui object, and an app that wants to load anything from an external origin has to name that origin in a csp entry.
That design is what makes the feature shippable at all. As the overview puts it, hosts can render third-party apps "without trusting the server author completely".
It is worth being clear about what this does and does not protect you from. The sandbox is an answer to a malicious or careless app reaching out of its box. It is not an answer to a malicious instruction reaching the model. Those are different problems with different fixes, and prompt injection remains the one that the iframe boundary does nothing about. A server that can draw a convincing interface is a server that can draw a convincing lie, and the sandbox is indifferent to the content of what it renders.
Who renders it#
As of the documentation on 13 September 2026, the overview page names eight: Claude, Claude Desktop, VS Code GitHub Copilot, Microsoft 365 Copilot, Goose, Postman, MCPJam and Archestra.AI.
The support matrix is a different page and a different question. It tracks eleven clients against three official extensions, MCP Apps alongside OAuth Client Credentials and Enterprise-Managed Authorization, and it is the page the project points you at for the current answer. Those two figures are not in conflict and one does not correct the other. The prose names the clients that support one extension; the table tracks a wider set of clients across three. Read the table rather than the sentence if the answer matters to you, and read it in a browser: its cells are drawn on the page after it loads, so what you get from a plain fetch of that URL is the client names and empty columns.
The half that is easy to miss#
Here is the sentence that changes how you should read every client list above. From the matrix page: "Extensions are always opt-in: a client only uses an extension if both client and server declare support in the extensions field of their capabilities."
Both. A client that supports MCP Apps will not render your interface because it can. It renders it because your server said, in its capabilities, that it speaks this extension, and the client said the same, and the two matched. Miss that and your carefully built UI resource is an unused file on a server, and the failure is silent, because a host that never negotiated the extension has no reason to report that it did not use one.
The negotiation itself is unglamorous and worth knowing. A client declares its extensions in the extensions field of the io.modelcontextprotocol/clientCapabilities it sends in the _meta of each request, and reads the server's extensions from the server's server/discover response.
The reason this matters beyond a debugging session is that it makes "supported" a two-sided word in a market that keeps publishing one-sided tables. Every extension support list you will read, this one included, describes one end of a handshake.
When it earns its complexity#
The documentation is unusually direct about when not to use this. "If your use case doesn't benefit from these properties, a regular web app might be simpler."
The properties it means are the ones a link cannot give you. The interface lives in the conversation, so nobody switches tabs or loses which thread held the dashboard. Data flows both ways over machinery the protocol already has, instead of the app needing its own API, its own auth and its own state. And the app can hand work back to the host, asking for an outcome such as scheduling a meeting and letting the host route it through whatever the user has already connected, rather than every app building and maintaining its own integrations.
The cases it suggests are the ones where text genuinely runs out: exploring data you want to click into rather than read, configuring something with a dozen interdependent options that would otherwise be a dozen conversational turns, and viewing media that a description cannot substitute for.
If you are building agents or picking up an assistant that speaks MCP, the thing to take from this is narrow and practical. The interface is now a first-class thing a server can return. Whether a user ever sees it is decided by a capability match, at connection time, at both ends.
Sources
- MCP Apps, Model Context Protocol documentationmodelcontextprotocol.io
- Extension Support Matrix, Model Context Protocol documentationmodelcontextprotocol.io



Discussion