All posts

sunpeak is MCP-App-First (June 2026)

Abe Wheeler
MCP AppsMCP App FrameworkMCP App TestingChatGPT AppsChatGPT App FrameworkClaude ConnectorsClaude Connector Framework
sunpeak keeps MCP App code portable while leaving room for host-specific features.

sunpeak keeps MCP App code portable while leaving room for host-specific features.

MCP Apps are now the shared UI layer for AI host integrations. The official MCP Apps docs define the pattern: an MCP tool declares a ui:// resource, the host renders that resource in a sandboxed iframe, and the app talks to the host over JSON-RPC through postMessage.

That is the right default target for a framework. ChatGPT Apps, Claude Connectors, VS Code extensions, and other host surfaces can add useful differences, but the base app contract should be the open standard.

TL;DR: sunpeak is an MCP App framework built around the MCP Apps standard first. Core APIs cover the portable bridge, resource metadata, tool data, app state, themes, display modes, and host context. ChatGPT and Claude-specific capabilities stay in explicit extension paths. Build against the standard, use host features only when they help, and test the full matrix locally with the sunpeak testing framework.

Why MCP-App-First Still Matters

When developers search for a ChatGPT App framework, they often mean one of two things:

  • “I need my app to run inside ChatGPT.”
  • “I need an interactive MCP App that can also run inside ChatGPT.”

Those are close, but they are not the same. The second path is usually better because the same resource and tool contract can run in more than one host.

OpenAI’s MCP Apps compatibility guide says ChatGPT supports the MCP Apps open standard for embedded app UIs and recommends using the MCP Apps bridge by default, then reaching for window.openai when a ChatGPT-only feature is needed. The official MCP docs describe the same cross-host model: tools point at UI resources, hosts fetch the resources, render them in sandboxed iframes, and proxy app-host communication.

sunpeak follows that split. The framework should not make your first resource depend on one host’s global object, preview mode, app store workflow, or account plan. The default should be a portable MCP App. The host-specific code should be obvious when it appears.

The Portable Core

In sunpeak, core APIs come from the top-level sunpeak import. They map to concepts that exist in the MCP Apps runtime: tool data, app state, host context, theme, display mode, auto-resize behavior, and resource metadata.

import { useToolData, useHostContext, useDisplayMode, useAppState } from 'sunpeak';
import type { ResourceConfig } from 'sunpeak';

export const resource: ResourceConfig = {
  description: 'Show analytics dashboard',
  _meta: {
    ui: {
      csp: {
        resourceDomains: ['https://cdn.example.com'],
      },
    },
  },
};

export function DashboardResource() {
  const { output } = useToolData<{ accounts: Array<{ id: string; name: string }> }>();
  const context = useHostContext();
  const displayMode = useDisplayMode();
  const [selectedAccount, setSelectedAccount] = useAppState<string | null>('selectedAccount', null);

  return (
    <main data-display-mode={displayMode} data-locale={context.locale}>
      {output.accounts.map((account) => (
        <button key={account.id} onClick={() => setSelectedAccount(account.id)}>
          {account.name}
        </button>
      ))}
      <p>Selected account: {selectedAccount ?? 'none'}</p>
    </main>
  );
}

Nothing in that component assumes a particular host. It can render in ChatGPT, Claude, or any host that implements the app bridge. It also gives the model useful state because useAppState lets the app expose interaction state back to the host instead of hiding everything inside local React state.

The server side follows the same idea. A sunpeak tool links to a resource, returns structured data, and lets the host decide how to render it:

import { z } from 'zod';
import type { AppToolConfig } from 'sunpeak/mcp';

export const tool: AppToolConfig = {
  resource: 'dashboard',
  title: 'Show account dashboard',
  description: 'Render account metrics in an interactive dashboard',
  annotations: { readOnlyHint: true },
};

export const schema = {
  accountId: z.string().describe('Account to inspect'),
};

export default async function ({ accountId }: { accountId: string }) {
  return {
    structuredContent: {
      accountId,
      accounts: [{ id: accountId, name: 'Northwind' }],
    },
  };
}

For a deeper architecture walkthrough, read How to Build an MCP App and MCP Concepts Explained.

Host Features Belong Behind Feature Checks

MCP-App-first does not mean pretending every host is identical. ChatGPT may expose file APIs, host-owned modals, or commerce flows through window.openai. Claude Connector workflows may differ around custom connector setup, directory submission, or data access policy. Some hosts support more display modes than others. Some mobile surfaces restrict iframe behavior differently than desktop.

The key is to treat those as optional capabilities, not as the foundation of your app.

import { useHostInfo } from 'sunpeak';

export function ExportControls() {
  const { hostCapabilities } = useHostInfo();
  const canOpenExternalLinks = Boolean(hostCapabilities?.openExternalLinks);

  if (!canOpenExternalLinks) {
    return <CopyReportUrlButton />;
  }

  return <OpenReportButton />;
}

That pattern ages better than a host-name check. A host can add support. A workspace policy can remove support. A mobile app can differ from a desktop app. Feature detection keeps the UI honest, and it gives you a concrete test matrix. For the full pattern, see MCP App capability detection.

Where MCP Apps Run Now

The current host picture is broader than it was when this post first ran in April.

The official MCP Apps overview lists support across Claude, Claude Desktop, VS Code GitHub Copilot, Microsoft 365 Copilot, Goose, Postman, MCPJam, and Archestra.AI, and it points developers to the client support matrix for extension support. ChatGPT supports MCP Apps compatibility through the OpenAI Apps SDK, with standard ui/* JSON-RPC messages over postMessage, standard MCP tool calls through tools/call, and optional ChatGPT-specific extensions when needed.

That means a framework has to optimize for three things:

  • The shared MCP Apps contract: ui:// resources, tool metadata, sandboxed iframes, JSON-RPC over postMessage, and server tool calls.
  • Host-specific capability layers: ChatGPT file APIs, host-owned modals, display mode behavior, Claude Connector workflows, and future host extensions.
  • Testable fallbacks: the app must still give the user a clear path when a host lacks an optional capability.

This is why sunpeak is not just a ChatGPT App framework or a Claude Connector framework. It is an MCP App framework with host-specific support where the standard leaves room for host differences.

Testing Is Where MCP-App-First Pays Off

Portability is easy to claim and hard to prove. You prove it by testing the same resource across host runtimes, themes, display modes, tool results, user states, and viewport sizes.

sunpeak’s Inspector is the local host runtime for that loop. In a new sunpeak project, pnpm dev starts the inspector and local MCP server:

npx sunpeak new sunpeak-app
cd sunpeak-app
pnpm dev

If you already have an MCP server, you can inspect it without adopting the full framework:

npx sunpeak inspect --server http://localhost:8000/mcp

The testing framework uses the same host replica in Playwright, so you can write tests against real iframe behavior instead of plain component mocks:

import { test, expect } from 'sunpeak/test';

test('dashboard renders in Claude dark mode', async ({ inspector }) => {
  const result = await inspector.renderTool(
    'show-account-dashboard',
    { accountId: 'acct_123' },
    { host: 'claude', theme: 'dark', displayMode: 'inline' },
  );

  await expect(result.app().getByRole('button', { name: 'Northwind' })).toBeVisible();
});

The useful part is not just the happy path. You can pin a simulation fixture where a tool returns an error, a display mode is unavailable, a host lacks server tool calls, or a viewport forces mobile layout. Those states are tedious to reproduce manually in live hosts, but they are normal test cases locally.

The Architecture Rule

Here is the practical rule I use when deciding whether code belongs in the portable core or a host extension:

  • If the behavior comes from the MCP Apps standard, use the top-level sunpeak APIs.
  • If the behavior depends on one host, isolate it in a host adapter or small feature component.
  • If the behavior may or may not exist in the current runtime, feature-detect it and ship a fallback.

That rule keeps the resource understandable. It also keeps migration paths open. A team can start with a ChatGPT App, add Claude Connector support later, and keep most app code unchanged because the core rendering, data, state, and tests already speak MCP Apps.

For more on the framework choice, read How to Choose an MCP App Framework. For a build walkthrough, read MCP App Tutorial.

Get Started

sunpeak is open-source and works locally and in CI with no paid AI host account required for the local loop. Scaffold a project, start the inspector, and build against the MCP Apps standard first.

npx sunpeak new sunpeak-app
cd sunpeak-app
pnpm dev

See the quickstart guide, the MCP App framework page, or the MCP testing framework page if you already have a server and want coverage before adding UI.

Get Started

Documentation →
npx sunpeak new

Further Reading

Frequently Asked Questions

What does MCP-App-first mean for sunpeak?

MCP-App-first means sunpeak builds its core APIs around the MCP Apps standard instead of one host runtime. Hooks such as useToolData, useAppState, useTheme, useDisplayMode, and useHostContext map to the shared app bridge. Host-specific features for ChatGPT and Claude stay in optional extension layers, so the default path stays portable.

What hosts support MCP Apps in June 2026?

The official MCP Apps documentation lists support across Claude, Claude Desktop, VS Code GitHub Copilot, Microsoft 365 Copilot, Goose, Postman, MCPJam, and Archestra.AI, with extension support tracked in the MCP client matrix. ChatGPT also supports MCP Apps compatibility through the OpenAI Apps SDK. Because support can differ by plan, device, and feature, apps should detect host capabilities at runtime.

How do I use ChatGPT-specific features without locking in my whole app?

Keep the main resource UI on the MCP Apps bridge, then call ChatGPT-specific APIs only where they add real value, such as file handling, host-owned modals, or commerce flows. OpenAI recommends building with standard MCP Apps keys and bridge behavior by default, then using window.openai for optional ChatGPT capabilities.

What is the difference between MCP Apps and ChatGPT Apps?

MCP Apps are interactive UI applications built on the MCP Apps extension that can run across compatible AI hosts. ChatGPT Apps are MCP Apps running in ChatGPT, with optional ChatGPT-specific capabilities exposed through the Apps SDK and window.openai. sunpeak supports both by keeping the shared MCP App surface separate from host-only features.

Can I test MCP Apps for ChatGPT and Claude locally?

Yes. Run pnpm dev in a sunpeak project to start the local inspector at localhost:3000, or run npx sunpeak inspect --server URL to inspect an existing MCP server. The inspector replicates ChatGPT and Claude-style MCP App runtimes, so you can test display modes, themes, tool calls, simulation fixtures, and responsive layouts before using a live host.

Do I need to rewrite my ChatGPT App to support Claude?

Usually no. If your app uses the MCP Apps bridge and sunpeak core APIs for tool data, app state, display mode, theme, and host context, most UI code can run across ChatGPT, Claude, and other compatible hosts. You still need feature detection for optional host capabilities, because a host may support the base iframe bridge but not every extension.

What is the MCP App standard?

The MCP Apps standard is an official extension to the Model Context Protocol. It lets MCP tools declare UI resources with ui:// URIs, then lets hosts render those resources in sandboxed iframes inside conversations. The app and host communicate with JSON-RPC over postMessage, including ui/* methods and tool calls through the host bridge.

How do I get started building MCP Apps with sunpeak?

Create a project with npx sunpeak new sunpeak-app, then run pnpm dev to start the inspector and local MCP server. If you already have an MCP server, use npx sunpeak test init --server URL or npx sunpeak inspect --server URL to add testing and inspection without changing your project structure.