MCP
Dani Reyes8 min read1052 views

MCP Apps in 2026: the wire calls it ui, and three announced hosts never landed

MCP Apps went stable as the first official MCP extension in January 2026. A pre-build field log on the identifier mismatch, the bilateral opt-in that fails silently, the deny-by-default CSP, and which hosts actually render one in August 2026.

Flat vector illustration of a small interactive panel with a bar chart, a slider and a button, nested inside a larger conversation panel, drawn in lime on deep navy
Flat vector illustration of a small interactive panel with a bar chart, a slider and a button, nested inside a larger conversation panel, drawn in lime on deep navy
On this page

Quick answer

MCP Apps is the first official extension to the Model Context Protocol, and as of August 2026 it is stable at version 2026-01-26 (MCP blog, 2026). It lets an MCP tool return an interactive HTML interface that renders inline in the conversation instead of a wall of text. Eleven clients support it today. Three of the hosts named in the launch announcement still do not, seven months later. And the thing that will actually cost you an afternoon is not the UI code, it is that the extension negotiates under the identifier io.modelcontextprotocol/ui, both sides have to opt in, and nothing tells you when one of them did not.

Model Context Protocol This is a pre-build field log. I spent an evening reading the spec, the SDK and the client matrix before committing a weekend to building an app, and I found enough friction in that evening to be worth writing down.

The name on the box is not the name on the wire

The extension is called MCP Apps. Everywhere. The blog post, the docs, the repo, the SDK package name @modelcontextprotocol/ext-apps.

The identifier it actually negotiates under is io.modelcontextprotocol/ui.

That is the string that goes in the extensions field of your capabilities. If you go looking for "apps" in a capabilities payload you will not find it, and if you are debugging why a host is not rendering your app, that is the first place you would look. The extension overview table on the client matrix is the only place I found the mapping stated plainly (client matrix, 2026).

It is a small thing. It is also the kind of small thing that eats forty minutes.

Both sides have to opt in

This is the design decision I would want to know before starting, and none of the explainers ranking for this query mention it.

Extensions are opt-in and bilateral. The matrix page states it directly: a client only uses an extension if both client and server declare support in the extensions field of their capabilities. The client advertises its side inside io.modelcontextprotocol/clientCapabilities in each request's _meta, and reads the server's side from the server/discover response.

So there are two independent ways your app quietly does not appear:

  1. The host does not support MCP Apps at all.
  2. The host supports it, your server did not declare it, and the tool call succeeds and returns text.

Case 2 is the nasty one, because it is not an error. Your tool works. The result is correct. It just comes back as text and the UI never loads, which reads exactly like a rendering bug in your HTML. If you use registerAppTool from the SDK the declaration is handled for you, but the moment you hand-roll a server, or wrap one, this is where the time goes.

What a tool declaration actually looks like

The whole mechanism is two fields. A tool points at a UI resource, and the server serves that resource as HTML with a specific MIME type.

json
{
  "name": "get-time",
  "title": "Get Time",
  "description": "Returns the current server time.",
  "inputSchema": {},
  "_meta": {
    "ui": {
      "resourceUri": "ui://get-time/mcp-app.html"
    }
  }
}

The ui:// scheme is what tells a host this is an MCP App resource. The path after it is arbitrary, organise it however you like. The resource itself is served with the MIME type text/html+mcp, which the SDK exports as the constant RESOURCE_MIME_TYPE so you are not hardcoding a string that may version later.

Two sibling keys live next to resourceUri and both matter more than they look: _meta.ui.csp controls which external origins your app may load from, and _meta.ui.permissions requests extra capabilities like microphone or camera.

The naming here has already moved once. The November 2025 proposal used a flat ui/resourceUri key on the tool _meta (MCP blog, 2025). The shipped spec nests it as _meta.ui.resourceUri. If you are following a tutorial written before January 2026, check that first.

The CSP is deny-by-default, and the official tutorial routes around it

Your app renders in a sandboxed iframe with, in the docs' own words, a deny-by-default CSP configuration. It cannot touch the parent DOM, cannot read the host's cookies or local storage, cannot navigate the parent page.

Which is correct, and which means an ordinary Vite build with separate CSS and JS files will not load its own assets.

The official build guide's answer is to bundle everything into one HTML file with vite-plugin-singlefile. It is presented as a convenience. It is doing more than that: it sidesteps CSP configuration entirely by making sure there is nothing external left to fetch. You can configure CSP properly through _meta.ui.csp instead, and you will have to the moment you want a font or a chart library from a CDN, but the happy path in every starter template is "bundle it all and never think about origins."

Worth knowing which of those two you have chosen.

Who actually renders one, in August 2026

Here is the part I actually went looking for, because it decides whether the weekend is worth it.

The January 26, 2026 launch post listed Claude on web and desktop, Goose, and VS Code Insiders as available, ChatGPT as "starting this week", and named JetBrains, Kiro (AWS) and Antigravity (Google DeepMind) as exploring support.

The community-maintained matrix, checked today:

Scroll to see more

ClientMCP Apps
Claude (web)yes
Claude Desktopyes
VS Code GitHub Copilotyes
Microsoft 365 Copilotyes
Gooseyes
Postmanyes
MCPJamyes
ChatGPTyes
Cursoryes
Archestra.AIyes
PostHog Codeyes

Cursor Eleven clients, and the shape of the delta is the interesting bit. Six hosts that were not named at launch shipped it anyway: Microsoft 365 Copilot, Postman, MCPJam, Cursor, Archestra.AI and PostHog Code. Meanwhile all three of the hosts publicly described as exploring support in January are still absent in August. JetBrains, Kiro and Antigravity have not landed.

That is not a scandal, "exploring" is not a commitment. But if you read the launch post and assumed your JetBrains users were a quarter away, that assumption is now seven months old and wrong.

One honest caveat on the table above, which the page states about itself: the matrix is maintained by the community through pull requests, not generated from conformance tests. It is the best public list there is. It is not an audit, and a client could support the extension without anyone having opened a PR.

Testing it in Claude costs money

Claude The build guide's Claude path is: expose your local server with a tunnel such as cloudflared, then add the resulting URL as a custom connector in Claude.

Then a note, easy to scroll past: custom connectors are available on paid Claude plans (Pro, Max, or Team).

So the most obvious way to see your app render is behind a subscription. For a protocol extension trying to win server authors, that is a real adoption tax, and I suspect it is a chunk of the answer to why the r/mcp thread asking why almost nobody is building these keeps getting agreement rather than counter-examples.

The free path exists and is not advertised nearly as loudly. The ext-apps repo ships a basic-host test harness. Clone it, point it at your server with a SERVERS environment variable, and it serves a plain interface on port 8080 that fetches your UI resource and renders it in a sandboxed iframe (ext-apps repository, 2026). That is where I would develop, and I would treat Claude as the final check rather than the loop.

The repo is at 2.8k stars and 368 forks, and it is candid that there is no supported host implementation in it beyond that example.

So what happened to MCP-UI?

This is the question the search results ask and mostly do not answer, so: MCP-UI did not lose, and it did not get absorbed.

MCP-UI was the community project that, in the spec authors' own framing, proved interactive interfaces fit inside MCP's architecture. Its maintainers Ido Salomon and Liad Yosef are named co-authors of SEP-1865 alongside engineers from Anthropic and OpenAI. The proposal was explicitly a three-way collaboration.

Today the two occupy different sides of the same protocol. If you are writing a server or an app, you use @modelcontextprotocol/ext-apps. If you are building a host and need to render other people's apps, the docs point you at @mcp-ui/client, which provides the React components for it, or at the SDK's own App Bridge module.

Same standard. Different end of the wire.

What I would tell myself before starting

Four things, in the order they would have saved me time.

  • Search for io.modelcontextprotocol/ui, not "apps", when anything fails to negotiate.
  • Confirm the server declares the extension. A tool that returns correct text is the failure mode, not an exception.
  • Develop against basic-host on port 8080. Go to Claude when it works, not to debug.
  • Decide early whether you are bundling to one file or configuring _meta.ui.csp properly, because the starter templates decide it for you by default.

The spec itself is in good shape. The starter templates cover React, Vue, Svelte, Preact, Solid and vanilla JavaScript, the examples directory is genuinely broad, and the security model is coherent rather than bolted on. If you already run an MCP server and any of its output is a table, a diff or a form, this is a real upgrade and the build is smaller than it looks.

Just go in knowing the host list is shorter than the launch post implies, and that the first hour goes to negotiation, not to CSS.

If you are earlier than this and still standing a server up at all, I kept a thirty day log of running one in production that covers the unglamorous half.

D

Written by

Dani Reyes

Dani Reyes writes DevMoment field notes on AI dev workflow, tested on real work rather than demos.

Frequently asked questions

What are MCP Apps?

MCP Apps are the first official extension to the Model Context Protocol, stable at version 2026-01-26. They let an MCP tool return an interactive HTML interface, such as a chart, form or diff viewer, that renders inline inside the conversation instead of as plain text. The tool points at a ui:// resource through a _meta.ui.resourceUri field, the host fetches that resource and renders it in a sandboxed iframe, and the app talks back to the host over JSON-RPC on postMessage.

Which clients support MCP Apps in 2026?

As of August 2026 the community-maintained client matrix lists eleven: Claude on web, Claude Desktop, VS Code GitHub Copilot, Microsoft 365 Copilot, Goose, Postman, MCPJam, ChatGPT, Cursor, Archestra.AI and PostHog Code. Note that JetBrains, Kiro and Antigravity, the three hosts described as exploring support in the January 2026 launch post, are still not listed seven months later. The matrix is maintained through pull requests rather than conformance tests, so treat it as the best public list rather than an audit.

What is the difference between MCP-UI and MCP Apps?

They are not competitors. MCP-UI was the community project that demonstrated interactive interfaces fit inside MCP, and its maintainers Ido Salomon and Liad Yosef are named co-authors of SEP-1865 alongside engineers from Anthropic and OpenAI. Today they sit on opposite ends of the same protocol: if you are writing a server or an app you use @modelcontextprotocol/ext-apps, and if you are building a host that renders other people's apps the official docs point you at @mcp-ui/client or at the SDK's App Bridge module.

Why is my MCP App not rendering?

The most common cause is failed negotiation rather than broken UI code. Extensions are bilateral and opt-in, so both the client and the server must declare support in the extensions field of their capabilities, and the identifier is io.modelcontextprotocol/ui rather than anything containing the word apps. If the host supports MCP Apps but your server never declared it, the tool call still succeeds and simply returns text, which looks like a rendering failure but is not an error anywhere. The second most common cause is the deny-by-default CSP in the sandboxed iframe blocking external CSS or JS that was not bundled or allowed through _meta.ui.csp.

Do I need a paid Claude plan to test an MCP App?

To test inside Claude, yes. The official build guide routes local development through a tunnel added as a custom connector, and it notes that custom connectors are available on paid Claude plans (Pro, Max, or Team). There is a free alternative that is much less advertised: the ext-apps repository ships a basic-host test harness that runs locally on port 8080, takes a SERVERS environment variable pointing at your server, and renders your UI resource in a sandboxed iframe. That is the better development loop regardless of your plan.

MCP

Wiring an MCP server to my IDE in 30 minutes

I wire an MCP server to my IDE's agent in about thirty minutes, and suddenly it reads my real Postgres schema and project files instead of hallucinating. MCP is just a standard way for agents to call external tools. I pick one server that solves a real annoyance, drop a small JSON config with command, args, and env, restart, and let the agent fetch its own context. That's the whole win.

12 min read189