How to Build a Claude App (April 2026)
How to build a Claude App!
TL;DR: Claude Apps are MCP Apps that run inside Claude. Build them with React, test locally with sunpeak’s inspector, and deploy to Claude, ChatGPT, or any MCP host from one codebase.
Claude renders interactive applications directly in the chat. Figma, Canva, Asana, Slack, Box, Elastic, and more already ship Claude Apps. If you have been building for the web with React, you can build one too.
This post covers what Claude Apps are, how they work, and how to build one from scratch using sunpeak, an open-source MCP App framework.
What Is a Claude App?
A Claude App is a web application that renders inside Claude’s UI. When Claude calls a tool, instead of returning text, it can render your app in a sandboxed iframe. Your app receives structured data from the tool call and displays it as real UI: cards, charts, forms, maps, whatever you build with React.
This works on Claude web and Claude Desktop. It is available on Free, Pro, Max, Team, and Enterprise plans.
The underlying technology is MCP Apps. MCP (Model Context Protocol) is the open standard that defines how apps communicate with AI hosts. Anthropic created MCP and donated it to the Agentic AI Foundation in April 2026, establishing vendor-neutral governance. A “Claude App” is just an MCP App running inside Claude. The same app runs on ChatGPT, Goose, VS Code (via Copilot), Postman, MCPJam, and any other host that implements the ext-apps specification.
How Claude Apps Work
A Claude App has two parts: a tool (backend) and a resource (frontend). Understanding how they connect is the key to building any MCP App. For the full concept breakdown, see MCP Concepts Explained.
The tool is an MCP server endpoint that Claude calls when a user asks for something. It runs your backend logic, fetches data, or computes a result. The resource is an HTML page (a React component when using sunpeak) that receives the tool’s output and renders it as UI.
Here is the data flow (see the MCP App lifecycle for the full sequence):
- A user asks Claude something (“Show me the Q4 dashboard”).
- Claude decides to call your tool. Your MCP server executes it and returns structured data.
- Claude passes that data to your resource via
structuredContent. - Your React component receives it through
useToolDataand renders UI in a sandboxed iframe.
The resource is pure frontend. It does not call APIs or run server logic directly. Claude handles the reasoning, your tool handles the data, and your resource handles the presentation.
import { useToolData, SafeArea } from 'sunpeak';
import type { ResourceConfig } from 'sunpeak';
export const resource: ResourceConfig = {
description: 'Display a metrics dashboard',
};
interface DashboardData {
title: string;
metrics: { label: string; value: string; change: string }[];
}
export function DashboardResource() {
const { output } = useToolData<unknown, DashboardData>(undefined, undefined);
if (!output) return null;
return (
<SafeArea className="p-4 font-sans">
<h1 className="text-xl font-bold mb-4">{output.title}</h1>
<div className="grid grid-cols-3 gap-4">
{output.metrics.map((m) => (
<div key={m.label} className="p-3 bg-gray-50 rounded-lg">
<div className="text-sm text-gray-500">{m.label}</div>
<div className="text-2xl font-bold">{m.value}</div>
<div className="text-sm text-green-600">{m.change}</div>
</div>
))}
</div>
</SafeArea>
);
}
That component works in Claude and ChatGPT without changes. useToolData, SafeArea, and ResourceConfig are all part of the MCP App standard that sunpeak implements. Nothing in this code is host-specific.
Connecting the Tool
The resource handles the UI. The tool triggers it. sunpeak auto-discovers tools from src/tools/*.ts:
// src/tools/show-dashboard.ts
import { z } from 'zod';
import type { AppToolConfig, ToolHandlerExtra } from 'sunpeak/mcp';
export const tool: AppToolConfig = {
resource: 'dashboard',
title: 'Show Dashboard',
description: 'Show a metrics dashboard for a time period',
annotations: { readOnlyHint: true },
};
export const schema = {
period: z.string().describe('Time period for metrics (e.g. "Q4 2025")'),
};
type Args = z.infer<z.ZodObject<typeof schema>>;
export default async function (args: Args, _extra: ToolHandlerExtra) {
return {
structuredContent: {
title: `${args.period} Dashboard`,
metrics: [
{ label: 'Visits', value: '4,218', change: '+12%' },
{ label: 'Conversions', value: '83', change: '+5%' },
],
},
};
}
The tool export links the tool to the dashboard resource and describes it to Claude. The schema defines input parameters with Zod. The default export is the handler that returns structuredContent matching the shape your resource expects. Multiple tools can point at the same resource with different data, which keeps your UI components reusable. For more on this pattern, see How to Build an MCP App.
Building Your First Claude App
Scaffold a project with sunpeak:
npx sunpeak new sunpeak-app
cd sunpeak-app
pnpm dev
This creates the project, installs dependencies, and starts a local inspector at localhost:3000 and the MCP server at localhost:8000. The inspector replicates both Claude and ChatGPT’s app runtime so you can build and test without any account or credits.
Your app code goes in src/resources/. Each resource is a React component paired with a config object. sunpeak auto-discovers resources by directory convention: any file at src/resources/{name}/{name}.tsx becomes a resource.
Mock data goes in tests/simulations/. These are JSON files that define what the user said, what tool the model called, and what data came back:
{
"tool": "show_dashboard",
"userMessage": "Show me Q4 metrics",
"toolInput": { "period": "Q4 2025" },
"toolResult": {
"structuredContent": {
"title": "Q4 2025 Dashboard",
"metrics": [
{ "label": "Visits", "value": "4,218", "change": "+12%" },
{ "label": "Conversions", "value": "83", "change": "+5%" }
]
}
}
}
The inspector loads these simulation files so you can develop against realistic scenarios without connecting to a live Claude session. Write one simulation per meaningful UI state (happy path, empty state, error state) and you have a solid development loop.
For a full walkthrough of building a resource from scratch, see the MCP App Tutorial or the ChatGPT App Tutorial. The steps are the same because both hosts use the same MCP App standard.
Making Your App Interactive
Read-only apps display tool data. Interactive apps let users take actions that the model can see. The useAppState hook is how you build interactivity.
useAppState works like React’s useState, but it syncs state back to Claude. When a user clicks a button or fills a form, the updated state is visible to the model in follow-up responses.
import { useAppState, useToolData } from 'sunpeak';
export default function FilterableList() {
const { output } = useToolData();
const [filter, setFilter] = useAppState('filter', 'all');
const items = output.items.filter(
item => filter === 'all' || item.category === filter
);
return (
<div>
<select value={filter} onChange={e => setFilter(e.target.value)}>
<option value="all">All</option>
<option value="active">Active</option>
<option value="done">Done</option>
</select>
<ul>{items.map(item => <li key={item.id}>{item.name}</li>)}</ul>
</div>
);
}
Claude sees that the user filtered to “active” and can use that context in its next message. This is how Claude Apps go from static dashboards to proper interactive tools. For a deeper walkthrough, see Interactive MCP Apps with useAppState.
Beyond state, you can also call tools from the UI with useCallServerTool and send messages back into the conversation with useSendMessage. These action hooks let your app trigger backend logic and communicate with the model, all through the standard MCP App protocol. See the runtime APIs reference for the full list.
Display Modes
Every MCP App host supports three display modes: inline, fullscreen, and picture-in-picture (PiP). Your app reads the current mode and can request a change.
import { useDisplayMode } from 'sunpeak';
export default function DashboardResource() {
const { displayMode, requestDisplayMode } = useDisplayMode();
if (displayMode === 'inline') {
return (
<div>
<SummaryCard />
<button onClick={() => requestDisplayMode('fullscreen')}>
Expand
</button>
</div>
);
}
return <FullDashboard />;
}
A summary card in inline mode with an “Expand” button that opens the full dashboard in fullscreen is a common pattern. This works identically on Claude, ChatGPT, and every other MCP App host. For the full display mode reference, see the display mode guide.
Portability Across Hosts
Claude and ChatGPT both implement the MCP Apps standard. So do Goose, VS Code (via GitHub Copilot), Postman, and MCPJam. When you build with sunpeak’s core API, your app works across all of them.
The MCP App protocol itself is the standard. useToolData, useAppState, useDisplayMode, SafeArea, and the rest of the core API map directly to the protocol specification. Your app talks MCP, and any host that speaks MCP can render it.
If you do want host-specific features, sunpeak provides opt-in subpath imports. sunpeak/chatgpt exports ChatGPT-specific utilities like useUploadFile and useRequestModal. The core sunpeak import stays portable. For most apps, you will never need host-specific code.
Testing
You can’t test an MCP App by opening a file in a browser because your UI depends on host-injected data and runs inside a sandboxed iframe. sunpeak’s inspector and testing framework solve this.
Simulation files define deterministic UI states that feed multiple test types:
pnpm test # unit + e2e tests
pnpm test:visual # visual regression tests
pnpm test:live # live host validation
pnpm test:eval # multi-model evals (GPT-4o, Claude, Gemini)
Because tests run against the standard runtime, not against Claude specifically, tests that pass locally validate your app for every MCP App host. You don’t need paid accounts or credits to get comprehensive coverage.
For more on testing, see the complete guide to testing MCP Apps, or the focused guides on unit testing, E2E testing, and visual regression testing.
What You Can Build
Claude Apps run in iframes with full HTML/CSS/JS, so you can build anything a web app can do:
- Data dashboards with live charts and filters
- Document review interfaces with inline annotations
- Multi-step forms and wizards
- Map-based UIs with location pins
- Media galleries with lightbox viewers
- Code diff viewers with syntax highlighting
- Design tools (Figma and Canva already ship as Claude Apps)
The first-party Claude Apps from Figma, Canva, Asana, Slack, Box, Hex, Monday.com, Clay, Amplitude, and Elastic demonstrate the range. These are full interactive tools rendered inside the chat.
sunpeak’s UI components give you a head start with common patterns (carousels, cards, safe area layouts), with full theming support. You can use any React library or CSS framework alongside them.
Get Started
npx sunpeak new
Further Reading
- How to Build an MCP App - cross-host architecture and build order
- MCP App Tutorial - step-by-step build from scratch
- ChatGPT App Tutorial - same steps, different host
- MCP Concepts Explained - Tools, Resources, and how MCP Apps use them
- Interactive MCP Apps with useAppState - two-way state management
- Complete Guide to Testing MCP Apps - unit, E2E, and visual regression testing
- Pre-Submission Testing for MCP Apps - validate before submitting to app stores
- ChatGPT App Display Mode Reference - inline, PiP, and fullscreen
- Claude Connector Framework
- MCP App Framework
- Testing Framework
- sunpeak documentation - quickstart and API reference
- MCP Apps specification (ext-apps)
- MCP Apps overview - official MCP docs
Frequently Asked Questions
What is a Claude App?
A Claude App is an MCP App that runs inside Claude. When Claude calls a tool, the app renders the result as interactive UI (cards, forms, charts, maps) inside a sandboxed iframe instead of plain text. Claude Apps work on Claude web and Claude Desktop across Free, Pro, Max, Team, and Enterprise plans. Because they follow the MCP Apps open standard (ext-apps v1.7.0), the same app also runs on ChatGPT, Goose, VS Code, Postman, and MCPJam without code changes.
Do I need a Claude account to build a Claude App?
No. You can build and test Claude Apps locally using sunpeak without any Claude account. The sunpeak inspector at localhost:3000 replicates the Claude and ChatGPT App runtimes. You can test display modes (inline, fullscreen, picture-in-picture), light and dark themes, and different screen sizes. You only need a Claude account when you are ready to deploy and connect your finished app.
Can the same app run on both Claude and ChatGPT?
Yes. Claude and ChatGPT both implement the MCP Apps standard (ext-apps), so an app built with sunpeak runs on both without code changes. The core API (useToolData, useAppState, useDisplayMode, SafeArea, and other hooks) is host-agnostic. You write your app once and it works across Claude, ChatGPT, Goose, VS Code (via Copilot), Postman, and MCPJam.
What framework should I use to build a Claude App?
sunpeak is an open-source MCP App framework designed for building Claude Apps and ChatGPT Apps from a single codebase. It includes a local inspector, a built-in testing framework with unit, E2E, and visual regression tests, and a development MCP server with HMR. Run npx sunpeak new to scaffold a project.
How do I test a Claude App locally?
Run pnpm dev to start the local inspector at localhost:3000 and the MCP server at localhost:8000. The inspector loads simulation files (JSON mock data) and renders your app exactly as Claude would. You can test display modes, themes, and screen sizes. For automated testing, sunpeak includes pnpm test for unit and E2E tests, pnpm test:visual for visual regression tests, and pnpm test:live for live host validation.
What is the difference between Claude Apps and Claude Connectors?
Claude Connectors securely connect Claude to data sources like email, calendars, and internal documents for context retrieval. Claude Apps are interactive UI applications that render inside the chat when Claude calls a tool. Connectors provide data to Claude. Apps provide UI to users. Both are built on MCP, and sunpeak supports both.
What companies have built Claude Apps?
As of April 2026, first-party Claude App integrations include Figma, Canva, Asana, Slack, Box, Hex, Monday.com, Clay, Amplitude, and Elastic. These apps render interactive UI inside Claude conversations, letting users interact with external tools without leaving the chat.
How do I make a Claude App interactive?
Use the useAppState hook from sunpeak. It works like React useState but syncs state back to Claude, so the model can reference user interactions in follow-up responses. A form submission, button click, or filter change is visible to the model because useAppState pushes the updated state through the host bridge. You can also use useCallServerTool to call MCP tools from the UI and useSendMessage to send messages into the conversation.