Large Language ModelsGenerate imagesGenerate videos

MCP UI vs MCP Apps: Differences, SDK and Examples

MCP UI started as a community SDK for interactive tool interfaces, and MCP Apps is the official extension that standardized the idea in January 2026. This article sets the two side by side: delivery model, sandboxing, host support, SDK packages and runnable code for servers, Views and hosts, plus advice on which one to pick for a new project.

MCP UI vs MCP Apps: Differences, SDK and Examples
Cristian Da Conceicao
Founder of Picasso IA

Ask ten developers what separates MCP UI from MCP Apps and you will hear ten different answers. The reason is simple: the two names describe a community project and the official standard that grew out of it, and plenty of posts treat them as rivals. If one of your MCP tools should render a chart, a form or a map instead of a wall of text, you need to know which package to install, where the HTML lives and which hosts will actually draw it. This comparison of MCP UI vs MCP Apps lays out the differences, the SDK pieces on every side and code you can adapt today.

💡 Short answer: MCP Apps is the official MCP extension that went live on January 26, 2026. MCP-UI is the earlier SDK project that pioneered the idea, and its packages now work with the MCP Apps standard. Treat them as ancestor and successor, not as competitors.

What Each One Actually Is

MCP UI in Plain Terms

MCP-UI is an SDK for building interactive interface components for Model Context Protocol tools. It showed up in 2025 and pioneered the idea that an MCP server could answer with a live interface instead of plain text. According to the MCP-UI documentation, the project directly influenced the MCP Apps specification.

The toolkit is split in two:

  • @mcp-ui/server exposes createUIResource, which builds a UI resource from an HTML string or an external URL.
  • @mcp-ui/client exposes AppRenderer for rendering a tool's interface and AppFrame for HTML you already fetched.

Server helpers also exist for Ruby (mcp_ui_server) and Python (mcp-ui-server), a real advantage when your MCP server is not written in TypeScript. In its early form, MCP-UI offered several content flavors, including raw HTML, external URLs and Remote DOM, and the interface traveled inside the tool response.

MCP Apps in Plain Terms

MCP Apps is the official extension that lets a tool return an interactive component, such as a dashboard, a form or a multi-step workflow, rendered inside the conversation in a sandboxed iframe. The official announcement calls it the first official MCP extension and says it is ready for production.

It rests on two ordinary MCP primitives:

  1. A tool that carries UI metadata in the _meta.ui.resourceUri field.
  2. A UI resource served through the ui:// scheme, holding bundled HTML and JavaScript.

The SDK lives in one npm package, @modelcontextprotocol/ext-apps, with subpaths for React hooks, host embedding and server registration.

Developer hands typing on a slim laptop showing a colorful bar chart interface at a wooden cafe table

How the Two Projects Merged

From Experiment to Extension

The maintainers proposed MCP Apps in November 2025, building on the work of MCP-UI and the OpenAI Apps SDK. Two months later, on January 26, 2026, the extension went live. The announcement is explicit that MCP-UI continues as a separate project and that moving to the official extension is optional.

That detail matters for anyone holding older code. Nothing forces a rewrite tomorrow, and the MCP-UI packages now point to registerAppTool and registerAppResource from the MCP Apps standard as the way to wire tools and resources.

Why a Standard Mattered

Before the merge, interface experiments were tied to individual clients. A panel designed around one host could not be counted on to run in another without client-specific code. Folding the MCP-UI and Apps SDK ideas into one extension means a server author writes one interface and any compliant host can render it.

For teams shipping MCP servers to many customers, that is the whole point: one build, many hosts, one security story.

Two colleagues sketching two connected rectangles with a marker on a whiteboard in a bright office

Differences That Actually Matter

The Comparison Table

AspectMCP-UIMCP Apps
OriginCommunity SDK project from 2025Official MCP extension, proposed November 2025, live January 26, 2026
RolePioneered interactive UI over MCP and supplies helpersStandard specification plus a reference SDK
Tool linkOriginal model returns the UI inside the tool response_meta.ui.resourceUri points at a ui:// resource
MIME typetext/html;profile=mcp-app when used with the standardtext/html;profile=mcp-app
Server helperscreateUIResource in @mcp-ui/serverregisterAppTool and registerAppResource in ext-apps/server
View and clientAppRenderer and AppFrame in @mcp-ui/clientApp class, plus app-bridge for hosts
LanguagesTypeScript, Ruby, Python server helpersTypeScript SDK with React hooks
StatusContinues as a separate project, migration optionalDescribed as ready for production

Read the table as a map of layers, not a scoreboard. MCP Apps defines the contract, and MCP-UI offers convenience on top of that contract.

Where the HTML Lives

The biggest structural difference is where the interface sits. In MCP Apps, the server stores the UI under a ui:// URI and the tool points at it through _meta.ui.resourceUri. The host fetches that resource separately and renders it on its own schedule, so the HTML is not stuffed into every tool response.

The practical gains:

  • Reviewable templates. A host can read the template before it ever runs.
  • Clean separation. Tool results stay data, and the resource stays the interface.

Aerial view of an oak desk with pencil wireframes, a laptop with a map interface and a tablet with a similar layout

Security and Sandboxing

Interactive code inside a chat is a risk, so the security model is a first-class feature. The announcement lists four layers:

  • Iframe sandboxing with restricted permissions
  • Pre-declared templates that hosts can review
  • Auditable JSON-RPC messaging for every UI-to-host exchange
  • Optional user consent before a UI-initiated tool call

On the MCP-UI side, AppRenderer takes a sandbox prop that points at a separate proxy page, which the documentation example serves from its own port. Untrusted HTML never shares a page with your host app.

💡 Tip: The documented onOpenLink handler checks that a URL starts with https:// or http:// before calling window.open. Copy that habit. A View is untrusted input, so filter what it asks the host to do.

Macro close-up of a glass terrarium holding small wooden blocks and a brass padlock like a sandbox

Hosts That Render Views

At launch, the announcement named Claude (web and desktop), Goose and Visual Studio Code Insiders, with ChatGPT arriving the same week. The ext-apps repository now lists ChatGPT, Claude, VS Code, Goose, Postman, MCPJam, the mcp-use inspector and the Alpic Playground. JetBrains, AWS, Google DeepMind and Antigravity were named as parties looking at support.

Check your target host's current status before promising a launch date. That list moves quickly.

Presenter in a glass-walled meeting room beside a wall display showing a clean dashboard while colleagues watch

The SDK Pieces You Will Touch

Two package families do the work, and they fit together.

PackagePurpose
@modelcontextprotocol/ext-appsBuild interactive Views with the App class
@modelcontextprotocol/ext-apps/reactReact hooks for Views
@modelcontextprotocol/ext-apps/app-bridgeEmbed Views in a chat client
@modelcontextprotocol/ext-apps/serverRegister tools and resources on your MCP server
@mcp-ui/servercreateUIResource and helpers for Ruby and Python
@mcp-ui/clientAppRenderer and AppFrame for hosts

Developer seen from behind at a standing desk with three monitors showing code editors in muted tones

Server Side With ext-apps

For an MCP server, the install command from the repository looks like this:

npm install -S @modelcontextprotocol/ext-apps \
  @modelcontextprotocol/client@^2.0.0 \
  @modelcontextprotocol/server@^2.0.0 \
  @modelcontextprotocol/node@^2.0.0 \
  @modelcontextprotocol/express@^2.0.0 \
  zod@^4.2.0

The server package gives you two helpers, registerAppTool and registerAppResource, plus the RESOURCE_MIME_TYPE constant. One adds UI metadata to a tool, and the other serves the HTML bundle.

View Side With the App Class

The View is the page that runs inside the iframe. Its App class talks to the host through a PostMessageTransport. You create the app, set handlers such as ontoolresult, and call connect(). From there the View can call server tools with callServerTool and push notes back to the model with updateModelContext.

Host Side With AppRenderer

If you build your own chat client, AppRenderer from @mcp-ui/client is the high-level component. It needs an MCP client, a toolName, a sandbox URL, and the toolInput and toolResult of the call. Callbacks such as onOpenLink and onMessage let the host decide what a View may do. AppFrame is the lower-level option for HTML you fetched yourself.

Working Code Examples

A Tool That Returns the Time

This server code follows the official quickstart. A tool named get-time is linked to a ui:// resource, and the resource handler reads a bundled HTML file.

import { z } from "zod";
import fs from "node:fs/promises";
import path from "node:path";
import {
  registerAppResource,
  registerAppTool,
  RESOURCE_MIME_TYPE,
} from "@modelcontextprotocol/ext-apps/server";

const DIST_DIR = path.join(process.cwd(), "dist");
const resourceUri = "ui://get-time/mcp-app.html";

registerAppTool(
  server,
  "get-time",
  {
    title: "Get Time",
    description: "Returns the current server time.",
    inputSchema: z.object({}),
    _meta: { ui: { resourceUri } },
  },
  async () => {
    const time = new Date().toISOString();
    return { content: [{ type: "text", text: time }] };
  },
);

registerAppResource(
  server,
  resourceUri,
  resourceUri,
  { mimeType: RESOURCE_MIME_TYPE },
  async () => {
    const html = await fs.readFile(path.join(DIST_DIR, "mcp-app.html"), "utf-8");
    return {
      contents: [{ uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html }],
    };
  },
);

Notice how little there is: one tool, one resource and one metadata field tying them together.

The View That Calls It

The View receives the first result through ontoolresult and can ask the server for fresh data when the user clicks a button.

import { App } from "@modelcontextprotocol/ext-apps";

const app = new App({ name: "Get Time App", version: "1.0.0" });
const serverTimeEl = document.getElementById("server-time")!;

const readTime = (result: { content?: Array<{ type: string; text?: string }> }) =>
  result.content?.find((c) => c.type === "text")?.text ?? "[ERROR]";

app.ontoolresult = (result) => {
  serverTimeEl.textContent = readTime(result);
};

document.getElementById("get-time-btn")!.addEventListener("click", async () => {
  const result = await app.callServerTool({ name: "get-time", arguments: {} });
  serverTimeEl.textContent = readTime(result);
});

app.connect();

Tablet and laptop side by side on a pale wooden table showing nearly identical dashboard layouts

Packaging UI With createUIResource

With MCP-UI, createUIResource from @mcp-ui/server builds the resource object for you:

import { createUIResource } from "@mcp-ui/server";

const widgetUI = await createUIResource({
  uri: "ui://my-server/widget",
  content: { type: "rawHtml", htmlString: "<h1>Widget</h1>" },
  encoding: "text",
});

The wire format it produces is a plain MCP resource with the standard MIME type:

{
  type: "resource",
  resource: {
    uri: "ui://my-server/widget",
    mimeType: "text/html;profile=mcp-app",
    text: "<h1>Widget</h1>",
  },
}

Sending Context Back to the Model

A View is not a dead end. It can tell the model what the user just did, so the next answer reflects the click:

await app.updateModelContext({
  content: [{ type: "text", text: "User selected option B" }],
});

This one call is what turns a static widget into part of the conversation.

Which One Should You Pick

The honest answer is that most new projects should start from the MCP Apps standard and treat MCP-UI as a toolbox that sits beside it.

Choose the MCP Apps SDK when:

  • You are starting a new TypeScript project and want the official, host-neutral path.
  • You want one interface to render in several hosts, such as Claude, VS Code, Goose and ChatGPT.
  • You want the React hooks, or app-bridge to embed Views in your own chat client.
  • Your tool runs long jobs. Image and video generation is asynchronous: a tool starts a job, then something polls until it finishes. A View with a progress bar and a result gallery beats a text log every time.

Keep the MCP-UI helpers when:

  • Your server is written in Ruby or Python and you want mcp_ui_server or mcp-ui-server.
  • You already ship createUIResource output and it renders correctly in your hosts.
  • You want AppRenderer as a ready-made renderer inside a client you control.

A short migration checklist:

  1. Add _meta.ui.resourceUri to every tool that has an interface.
  2. Serve the HTML from a ui:// resource with the MIME type text/html;profile=mcp-app.
  3. Replace embedded UI payloads in tool responses with resource pointers.
  4. Move View logic onto the App class and read results in ontoolresult.
  5. Test in at least two hosts before you release.

Aerial view of a hiking trail splitting in two through an autumn birch forest with a lone hiker at the fork

How to Use Sonnet 5 on PicassoIA

Views are mostly HTML, CSS and a thin layer of TypeScript, which is exactly the work a coding model handles well. Claude Sonnet 5 on Picasso IA is built for multi-step coding and tool-use tasks, reads screenshots and wireframes, and can draft a first version of a server, a View or a host wrapper from a plain request.

  1. Open the model page. Go to the Claude Sonnet 5 page on Picasso IA.
  2. Fill the required field. prompt is the only required parameter. Try: "Write a TypeScript MCP server tool named show-chart that registers a ui:// resource, plus a View using the App class that renders a bar chart from the tool result."
  3. Set the effort level. The default is low, which turns thinking off for the fastest answer. Raise it to high or max when the task touches several files, such as a server, a View and a build step together.
  4. Add a system prompt. Fix a role once, for example: "You write MCP Apps code with @modelcontextprotocol/ext-apps and never inline secrets in HTML." It then applies across the session.
  5. Attach an image if you have one. The optional image parameter accepts a wireframe or a screenshot, scaled by max_image_resolution to save time and money.
  6. Generate and review. Output can run up to 8,192 tokens by default. Copy the code, then test it in a host.
ParameterRequiredDefaultUse it for
promptYesNoneThe request itself
effortNolowHow much the model thinks before answering
max_tokensNo8192Capping output length
system_promptNoEmptyRole and coding style
imageNoNoneA wireframe or screenshot as context
max_image_resolutionNo0.5 megapixelsScaling the image down before it is sent

💡 Tip: Paste the snippets from this article into your prompt as reference. The model then matches the real API names instead of guessing them.

Woman at a studio desk reviewing a printed contact sheet with a loupe beside a laptop showing an image gallery grid

Create Your Own Visuals Today

Every MCP App ends up needing pictures: a hero image for a landing page, product shots for a gallery View, a short clip for a demo. Picasso IA puts the models for all of that in one place, so you can test ideas before you wire anything into a server.

  • Start with stills from Seedream 5 Pro, and give the prompt a lens, a light direction and a texture to get a photographic result.
  • Turn the best frame into motion with Seedance 2.0, which pairs video with built-in audio.
  • Keep the code side moving with Claude Sonnet 5, using the steps above.

Pick one prompt, generate one image, and see how far a single idea goes. Then open the Picasso IA model collection and try a second model on the same prompt. The comparison alone will teach you what each one does best.

Share this article