Agent frameworks have become very good at calling tools and returning structured data.
The gap appears when an agent has to present information (charts, dashboards) or collect user input (forms, approvals). If the tool only returns JSON, every host ends up rebuilding the same UI logic for the same tools.
MCP Apps was proposed to solve this exact problem: a standardized way for MCP servers to ship interactive HTML UIs alongside tool outputs.
Today, we are excited to announce CopilotKit’s support for MCP Apps. Now, MCP servers can finally ship an interactive UI that works out of the box in real agent applications.
But showing UI is only step one. The hard part is keeping everything coordinated while the user is interacting:
- Tool lifecycle (started → streaming → finished / failed)
- User interactions (clicks, form submits, selections)
- Agent state updates (progress, partial results, next steps)
- Real-time back-and-forth between agent ↔ UI ↔ app
That’s why this integration is powered by AG‑UI: an event/state protocol that ships structured events back and forth (tool progress, UI actions, state updates) so the agent, the UI and your app stay synchronized while MCP Apps run.
The easiest way to get started is using the CopilotKit CLI:
npx copilotkit create -f mcp-apps
You can refer to the official docs or try the live demo if you want to explore it yourself. Now, let’s understand the technical details of what is happening under the hood.
1. MCP-UI: where the idea first worked
This proposed standard is inspired by MCP-UI and OpenAI’s Apps SDK.
MCP-UI was the community’s first proof that UI can be a first-class MCP resource (not just text/JSON). Tools could ship HTML rendered in sandboxed iframes and hosts could display rich, interactive content instead of rebuilding the UI for every tool.
MCP Apps take the core idea behind MCP-UI and formalize it into a shared, interoperable spec.
SEP-1865 was authored by MCP core maintainers at OpenAI and Anthropic, together with MCP‑UI creators and lead maintainers of the MCP-UI Community Working Group.
You can review the full SEP at modelcontextprotocol/ext-apps.
2. MCP Apps: standardizing UI within ecosystem
MCP Apps doesn’t just say “tools can return UI.” It standardizes how a server exposes UI in a way that hosts can reliably discover, render and interact with.
Credit: MCP Apps post
At a high level, it introduces a few core pieces:
- MCP server declares UI resources (using the
ui://...URI scheme). - Tools reference UI resources through the
_metafield so the host knows what to render. - Hosts can prefetch and review templates before running tools
- UI runs in sandboxed iframes for security
- All UI ↔ host communication happens using the standard MCP JSON-RPC over
postMessage.
A simplified example from a tool response might look like this:
{
"result": {
"data": {
"revenue": [280, 400, 560]
}
},
"_meta": {
"ui/resourceUri": "ui://charts/revenue"
}
}
On the server side, that UI is declared explicitly:
{
"ui://charts/revenue": {
"type": "html",
"entrypoint": "https://dev.to/ui/revenue-chart.html"
}
}
The point is: a server can ship a UI once and multiple hosts can render it consistently because the “resource + link + message channel” pattern is standardized.
What’s still missing?
MCP Apps defines the UI surface and a standard way for the UI to talk to the host, but it doesn’t tell you how to keep agent state, tool lifecycle and UI events synchronized in real-time.
That orchestration layer is where AG‑UI protocol fits in.
3. AG‑UI: the sync layer that makes it usable
You still need a consistent way to keep agent ↔ UI ↔ app aligned while the user interacts.
AG-UI (Agent–User Interaction Protocol) is an event-based protocol that standardizes this “agent ↔ frontend” real-time communication.
Because it’s transport-agnostic (SSE, WebSockets, etc.), the same frontend can work across different agent backends without custom wiring.
CopilotKit uses AG-UI as its synchronization layer, which makes it a natural fit for running interactive MCP Apps.
What it looks like (events on the wire)
When an MCP App UI is on screen, AG‑UI is the stream CopilotKit (or any compatible agent framework) uses to keep tool progress and shared state synced while the user interacts.
When the agent starts a tool call, you will see lifecycle events like:
{ "type": "TOOL_CALL_START", "toolCallId": "tool_1", "toolCallName": "get_revenue_data" }
As the agent/frontend state changes, you will see state snapshots like:
{ "type": "STATE_SNAPSHOT", "snapshot": { "status": "loading" } }
Followed by incremental updates (such as JSON Patch deltas) as the workflow progresses:
{
"type": "STATE_DELTA",
"delta": [{ "op": "replace", "path": "https://dev.to/status", "value": "ready" }]
}
STATE_DELTA uses JSON Patch so the UI can update without re-sending the entire state each time.
This is how it works in practice.
4. How CopilotKit wires MCP Apps ↔ AG‑UI
CopilotKit acts as the runtime that binds MCP Apps and AG-UI together inside a real application. Here is the simple flow:
- The agent calls an MCP tool and receives structured output
- The tool response includes a
ui://...reference, indicating which UI to render - CopilotKit renders that UI inside your app using a sandboxed iframe
- UI sends interactions back and CopilotKit forwards them into the agent loop
- AG-UI streams state/progress updates back to the UI while the agent runs.
Agent
│
│ calls tool
▼
MCP Tool ──────► returns data + ui:// reference
│
▼
CopilotKit (runtime)
│
├─ renders MCP App UI (iframe)
│
├─ forwards UI messages → agent
│
└─ participates in AG-UI event stream
│ (state / progress updates)
▼
MCP App UI
MCP Apps provide the UI surface, AG-UI provides the sync contract and CopilotKit runs the loop.
5. Integration flow: MCP Apps with CopilotKit
The fastest way to get started is to use the CopilotKit CLI:
npx copilotkit create -f mcp-apps
It creates a starter repo, which shows an end-to-end MCP Apps integration with CopilotKit and uses the official Three.js MCP Server.
If you are integrating into an existing Next.js app, here’s how you can set things up manually.
To run MCP Apps inside a real agent, you need a runtime layer that can fetch UI resources, sandbox them and keep UI state in sync with the agent.
That’s what the MCPAppsMiddleware provides. Install it along with the required CopilotKit packages:
npm install @copilotkit/react-ui @copilotkit/react-core @copilotkit/runtime @ag-ui/mcp-apps-middleware
Next, configure the CopilotKit API route. This is where the agent is created, the MCP Apps middleware is attached and the CopilotKit runtime is exposed.
-
BuiltInAgent(...): defines the agent’s LLM (model) and behavior (prompt) -
mcpServers: the list of MCP servers to connect to (you can add multiple) -
serverId: stable name for that server so CopilotKit can reload the same MCP App UI later, even if the server URL changes (local, staging, prod)
// app/api/copilotkit/route.ts
import {
CopilotRuntime,
ExperimentalEmptyAdapter,
copilotRuntimeNextJSAppRouterEndpoint,
} from "@copilotkit/runtime";
import { BuiltInAgent } from "@copilotkit/runtime/v2";
import { NextRequest } from "next/server";
import { MCPAppsMiddleware } from "@ag-ui/mcp-apps-middleware";
// 1. Create your agent and add the MCP Apps middleware
const agent = new BuiltInAgent({
model: "openai/gpt-4o",
prompt: "You are a helpful assistant.",
}).use(
new MCPAppsMiddleware({
mcpServers: [
{
type: "http",
url: "http://localhost:3108/mcp",
serverId: "my-server" // Recommended: stable identifier
},
],
}),
)
// 2. Create a service adapter, empty if not relevant
const serviceAdapter = new ExperimentalEmptyAdapter();
// 3. Create the runtime and add the agent
const runtime = new CopilotRuntime({
agents: {
default: agent,
},
});
// 4. Create the API route
export const POST = async (req: NextRequest) => {
const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
runtime,
serviceAdapter,
endpoint: "/api/copilotkit",
});
return handleRequest(req);
};
This will allow the agent to understand ui:// references returned by MCP tools and resolve them into rendered UI.
Create a .env.local file in your frontend directory and add your API key. The example uses OpenAI’s GPT-4o by default but you can also configure other language models.
OPENAI_API_KEY=your_openai_api_key
Wrap your application with the CopilotKit provider so the frontend can connect to the runtime (layout.tsx).
import { CopilotKit } from "@copilotkit/react-core";
import "@copilotkit/react-ui/styles.css";
// ...
export default function RootLayout({ children }: {children: React.ReactNode}) {
return (
<html lang="en">
<body>
<CopilotKit runtimeUrl="/api/copilotkit">
{children}
</CopilotKit>
</body>
</html>
);
}
Now add the CopilotSidebar chat component to your page.
// app/page.tsx
"use client";
import { CopilotSidebar } from "@copilotkit/react-ui";
export default function Page() {
return (
<main>
<h1>Your App</h1>
<CopilotSidebar />
</main>
);
}
That’s it! MCP Apps will now render automatically whenever the agent uses tools that reference UI resources.
The middleware supports both HTTP and SSE transports. You can find the full config options in the official docs.
You can try some of the open source MCP Apps servers from the official repo at github.com/modelcontextprotocol/ext-apps.
What this enables for Agent Builders
This brings interactive UIs into MCP in a way that works across hosts. You can:
- Ship interactive tool UIs without rebuilding them per host
- Keep UI + agent state in sync without custom wiring
- Go from prototype to production with fewer rewrites
You can even collect structured input and include human-in-the-loop steps inside your app.
5. Getting started
To start building:
👉 Start with CopilotKit & MCP Apps: npx copilotkit create -f mcp-apps
👉 Check out the CopilotKit / MCP Apps docs to get started
👉 Check out our example repo for a minimal integration setup
👉 Try the live CopilotKit demo and interact with an MCP App in real time
What’s Next
AG-UI is shaping up to be the bridge. Over the next few weeks, expect more examples, tighter primitives and clearer patterns for this integration.
If you want to follow along (or help shape what ships next), join the CopilotKit or AG-UI communities.
There’s a lot more coming soon.







