Skip to main content
All posts

Why You Need a ChatGPT App Framework (September 2026)

Abe Wheeler
ChatGPT AppsChatGPT App FrameworkChatGPT App TestingMCP AppsMCP App FrameworkMCP App TestingFrameworkMCP
ChatGPT Apps are MCP Apps that render interactive UI inside ChatGPT conversations.

ChatGPT Apps are MCP Apps that render interactive UI inside ChatGPT conversations.

OpenAI now describes a plugin as a package that can include skills, an MCP server, or both. UI is optional. When a tool does need an interactive interface, ChatGPT loads an MCP Apps resource in an iframe and connects it to the host through a standard bridge.

That distinction changes the framework question. You do not need a UI framework for every plugin. You need one when the product has enough server, resource, bridge, host, and test behavior that keeping those parts aligned becomes harder than writing the first screen.

TL;DR: Use an MCP SDK directly for a skills-only plugin, a headless tool, or a short experiment. Use a ChatGPT App framework when you are shipping interactive UI with auth, mutations, several states, more than one host, or a team that needs CI evidence. Judge a framework by the boundaries it tests and exposes: tool contracts, UI resources, the ui/* bridge, host capabilities, security metadata, and production behavior.

What a ChatGPT App Is in 2026

A ChatGPT App is usually an MCP server plus an optional UI resource. The server exposes tools. A tool that needs UI points to a ui:// resource through _meta.ui.resourceUri. ChatGPT fetches the resource, renders it in a sandboxed iframe, and connects it to the host with JSON-RPC over postMessage.

OpenAI’s current UI guide says to keep tools useful without a component. That matters because ChatGPT or Codex may call the tool in a context that cannot render UI. It also gives the model a headless path for fetching or changing data before a render tool presents the final result.

The shared MCP Apps path covers the common contract:

  • _meta.ui.resourceUri links a tool to its UI resource.
  • ui/initialize starts the iframe-side lifecycle.
  • ui/notifications/tool-input and ui/notifications/tool-result send data to the app.
  • tools/call lets the app call a server tool.
  • ui/message sends a follow-up message through the host.
  • ui/update-model-context shares useful app context with the model.

ChatGPT also provides optional features through window.openai, including file handling, checkout, and host-controlled modals. OpenAI recommends starting with the shared standard, then feature-detecting a ChatGPT extension when the app needs something the standard does not cover.

That gives you a useful rule: shared behavior belongs in the MCP Apps layer, while host-specific behavior belongs behind a small capability check.

The Five Boundaries a Framework Should Own

Calling something a framework does not make it useful. A good one should reduce work at five concrete boundaries without hiding the underlying protocol.

1. Tool contracts

The tool definition is both an API contract and part of the model’s instructions. Names and descriptions affect tool selection. inputSchema validates arguments. outputSchema describes structuredContent. Annotations tell clients about behavior such as whether a tool is read-only or destructive.

Tool results also have separate data lanes:

  • content gives the model a short text result.
  • structuredContent gives the model and app typed data.
  • _meta can carry host or app details that should not become model context.

A framework should validate these fields together. If the handler returns a ticket list that no longer matches outputSchema, CI should fail before the iframe tries to read it.

2. UI resources

The HTML bundle is an MCP resource, not a normal web page. The server must return the right URI, MIME type, content, and resource metadata. That metadata can include content security policy and permissions. The URI also affects caching, so a changed bundle needs a versioning strategy.

This is where many local demos break in a real host. A resource may load on localhost but fail when its CSP omits an API domain. An old ui:// URI may keep an older bundle in cache. A framework should make the raw resource response easy to inspect and test.

3. The iframe bridge

The UI has its own lifecycle. It initializes with the host, receives tool input and results, reacts to host context, makes tool calls, sends messages, and tears down listeners when it unmounts.

The MCP 2026-07-28 core specification removed the core initialize handshake and protocol-level sessions. That does not remove the MCP Apps UI initialization flow. They are different layers. A framework should keep the stateless core request lifecycle separate from the stateful experience inside a rendered app.

This split also makes bugs easier to name. A failed tools/call is a server or transport problem. A missing ui/notifications/tool-result is a bridge problem. A stale React view after a valid notification is an app-state problem.

4. Host capabilities

The same app can receive a different theme, locale, viewport, safe area, display mode, or set of optional APIs. The app should react to the capability it receives instead of guessing the host by name.

That means a local development host should reproduce at least:

  • Inline, fullscreen, and picture-in-picture layouts where supported.
  • Light and dark themes.
  • Desktop and mobile widths.
  • Initial, loading, empty, success, denied, and error states.
  • Capabilities that are present, absent, or declined by the user.

A browser tab with mocked props is helpful for component work, but it does not prove that bridge events and resource metadata work together.

5. Evidence and operations

The last boundary is the route from a local edit to a production incident. A framework should give the team evidence at several levels:

CheckQuestion it answers
Protocol testDoes the server expose valid tools, resources, schemas, and metadata?
Integration testDoes a real tool call return the expected result and failure shape?
Host E2E testDoes the resource run through the bridge inside a realistic host?
Visual testDid a theme, display mode, or viewport change break the UI?
Live host testDoes the deployed server behave in ChatGPT or another target host?
Model evalDoes the model choose the right tool and supply useful arguments?

Production diagnostics matter too. When a user says “the card is blank,” you should be able to separate a tool failure, resource fetch failure, CSP block, bridge error, and render error. Framework abstractions that erase those details make the first week easy and the next year hard.

Why Direct SDK Code Gets Expensive

An MCP SDK is enough to register tools and resources. It does not automatically give you a repeatable app workflow.

Without a framework or internal platform, your team still needs to decide how to:

  • Build and version each UI resource.
  • Attach tools to resources without creating stale metadata.
  • Validate inputs, outputs, and tool annotations.
  • Wrap the ui/* bridge without leaking listeners or assuming capabilities.
  • Keep app-only actions out of model tool selection.
  • Model auth, approval, loading, empty, and error states.
  • Test themes, display modes, mobile widths, and safe areas.
  • Run protocol, browser, visual, live, and model checks in CI.
  • Record enough detail to debug the deployed app.

The cost grows with the number of state combinations, not the number of source files. Two auth states, four result states, three display modes, two themes, and two host capability sets already create 96 combinations before mobile widths or error classes enter the count. You do not need a screenshot for all 96, but you do need a deliberate way to choose and reproduce the risky ones.

A useful framework turns those choices into explicit fixtures, helpers, and tests. It should also let you drop to raw MCP when a new standard field arrives before the helper API catches up.

When You Do Not Need a ChatGPT App Framework

Skip the framework when its setup costs more than the boundary work it removes.

You probably do not need one for:

  • A skills-only plugin with no MCP server.
  • A read-only, headless MCP server with one or two simple tools.
  • A short experiment built to learn the protocol.
  • An internal tool where your platform team already owns the resource, bridge, test, and deploy layers.

You probably do need one when the app has interactive UI plus any of the following:

  • Authentication or user-specific data.
  • Mutations, approvals, payments, or destructive actions.
  • App-only tools called from the iframe.
  • Several loading, empty, partial, denied, or error states.
  • More than one display mode or host.
  • A team that needs stable local setup and CI checks.
  • A product that will still be maintained after the first demo.

The dividing line is ownership. If the framework owns a boundary you would otherwise build and maintain, it can save time. If it only wraps a function you already understand, it adds another dependency.

A Practical Framework Evaluation Checklist

Before adopting a framework, build one small vertical slice with it: list tools, call one tool, load one UI resource, make one app-initiated tool call, trigger an auth failure, and run the result in CI. Then check the following.

Standards fidelity

Does it use _meta.ui.resourceUri, the ui/* bridge, and current MCP schemas directly? Can you inspect the JSON-RPC messages and resource response? The framework should track the open standard without forcing private fields into the core app.

Escape hatches

Can you add a new tool field, result _meta value, or bridge method before the framework ships a wrapper? Typed helpers are useful, but the protocol should remain reachable.

Existing-server support

Can the framework inspect a server written in Python, TypeScript, Go, or another language? A framework that requires moving the server into its own runtime creates a much larger adoption decision.

Test depth

Does “testing” mean a component preview, or does it call the MCP server, read the resource, initialize the bridge, and run the app in a browser? Ask which exact boundary each test crosses.

Security defaults

Look for CSP and permissions support, OAuth failure fixtures, approval states, tool annotations, and a clear split between model-visible and app-only data. The framework should help you make access narrow and behavior explicit.

Host isolation

Host extensions should sit behind feature detection. Code such as if (openai?.requestModal) is more durable than branching on a product name. The standard path should still work when the extension is absent.

Production diagnostics

Check what you can log, trace, and reproduce after deployment. The 2026-07-28 MCP specification documents OpenTelemetry trace context in _meta, which gives frameworks a standard place to preserve request traces across the boundary.

Exit cost

Inspect the generated project. You should see ordinary source code, MCP tools, resources, and browser tests that your team can keep. A framework earns trust when leaving it is possible.

Where sunpeak Fits

sunpeak is an open-source, server-agnostic MCP App framework and MCP testing framework. Version 0.20.85 is current as of September 20, 2026.

For a new app, sunpeak scaffolds the server, UI, local host, and test setup:

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

For an existing MCP server, the inspector connects by URL or command:

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

That server can stay in its current language and repository. sunpeak supplies replicated host runtimes for local rendering, plus unit, integration, E2E, visual, live, and eval test layers. You can start with inspection, then add test setup when the app needs repeatable CI coverage:

npx sunpeak test init --server http://localhost:8000/mcp

The useful part is not a shorter tool handler. It is one workflow for checking the tool contract, resource, bridge, host states, and rendered result without using a live ChatGPT session as the first debugger.

Start With One Real Workflow

Pick a workflow that includes data, UI, and one user action. Keep the first version small enough to understand the raw MCP messages.

Then test it in this order:

  1. List the tool and inspect its schemas, annotations, and UI resource link.
  2. Call the tool and validate content, structuredContent, and _meta.
  3. Read the UI resource and inspect its MIME type, CSP, permissions, and versioned URI.
  4. Render it in a local host and verify initialization, tool input, tool result, and one app-initiated tool call.
  5. Exercise loading, empty, error, denied, dark, mobile, and larger display states.
  6. Run the same checks in CI, then do a smaller pass against the deployed app in ChatGPT.

Start with the sunpeak quickstart, OpenAI’s plugin architecture and UI guide, and the official MCP Apps overview. Those references make it easier to tell whether a framework follows the current contract or replaces it with one of its own.

Get Started

Documentation →
npx sunpeak new

Further Reading

Frequently Asked Questions

What is a ChatGPT App framework?

A ChatGPT App framework is development infrastructure for an MCP server and its optional interactive UI. It should help define tools and schemas, publish UI resources, handle the MCP Apps ui/* bridge, reproduce host states locally, and test the server, resource, and rendered app as one contract.

Do I need a framework to build a ChatGPT App?

Not always. A skills-only plugin, a headless read-only MCP server, or a short protocol experiment may be simpler with an MCP SDK alone. A framework usually pays off when the app has interactive UI, authentication, mutations, app-only tools, several display states, more than one host, or a team that needs reliable CI checks.

Is a ChatGPT plugin the same as a ChatGPT App?

No. OpenAI defines a plugin as a package that can contain skills, an MCP server, or both. A ChatGPT App commonly means an MCP server with an optional MCP Apps UI. Skills-only plugins do not need a UI framework, while UI-backed apps need server, resource, bridge, and test support.

Should new ChatGPT Apps use window.openai or the MCP Apps standard?

Start with the MCP Apps standard: _meta.ui.resourceUri, the ui/* JSON-RPC bridge, and tools/call. OpenAI recommends using window.openai only for ChatGPT features the shared standard does not cover, such as file handling, checkout, or host modals. Feature-detect those methods and keep a fallback when practical.

What changed in the MCP 2026-07-28 specification?

The 2026-07-28 core MCP specification removed protocol-level sessions and the core initialize handshake, added server/discover, put protocol details and capabilities on each request, and added cache guidance for list and resource results. MCP Apps still has its own UI initialization and lifecycle, so a framework must keep the core transport lifecycle separate from the iframe bridge lifecycle.

What should a ChatGPT App framework test?

It should test tool discovery, schemas, annotations, structuredContent, resource reads, MIME type, CSP, permissions, UI initialization, tool input and result notifications, app-initiated tool calls, loading and error states, themes, display modes, mobile widths, auth failures, visual changes, and model tool selection.

Can a framework work with an existing MCP server?

Yes, if it is server-agnostic. For example, sunpeak can inspect and test an existing MCP server over a URL or command, so teams can keep Python, TypeScript, or another server stack and add replicated-host rendering plus protocol, browser, visual, live, and eval tests.

How should I compare ChatGPT App frameworks?

Compare standards support, raw protocol access, existing-server support, local host fidelity, test depth, security defaults, host-extension isolation, production diagnostics, and exit cost. A useful framework should produce a portable MCP App and make its contracts easier to inspect, rather than hiding them behind private abstractions.