The most common causes, in the order you should check them:
Uncaught JavaScript error. Open browser developer tools inside the iframe: right-click the App area, choose Inspect, then switch the console context dropdown (top-left of the Console tab) from top to the sandboxed frame. An uncaught error stops the App before it paints.
CSP violation. Look for Refused to connect to… or Refused to load… in the console. Any network request, including to localhost during development, must be declared in _meta.ui.csp.connectDomains or resourceDomains. See the CSP & CORS guide.
Resource URI mismatch. The _meta.ui.resourceUri on the tool must match the URI passed to registerAppResource exactly. A trailing slash or case difference prevents the host from finding the HTML.
Wrong MIME type. The resource's mimeType must be text/html;profile=mcp-app (exported as RESOURCE_MIME_TYPE). Plain text/html is not recognized as an App resource.
ontoolinput / ontoolresult never firesapp.ontoolresult before calling connect(). If the handler is attached after connect() resolves, the notification may have already been delivered and discarded. The React useApp hook handles this ordering automatically.@modelcontextprotocol/ext-apps than the host expects, the initialize handshake can fail silently. Keep the SDK version current.MCP Apps are portable only if they use the SDK exclusively. Common portability mistakes:
window.openai, window.claude, or any other host-injected object. Use the App class from this SDK, which speaks the standard protocol to any compliant host.resourceDomains._meta.ui.domain to request a stable origin rather than hardcoding one in CORS allowlists. See CSP & CORS.See Controlling App height. The most common cause is height: 100vh combined with the default autoResize: true.
CSP and CORS are separate controls with different error messages and different fixes:
Refused to connect): The browser blocked the request because the domain is not in connectDomains. Add the domain to _meta.ui.csp on the MCP server.No 'Access-Control-Allow-Origin' header): The API server rejected the request because it does not recognize the sandbox origin. Add the origin to the API server's allowlist, or use _meta.ui.domain to get a predictable origin that can be allowlisted.See the CSP & CORS guide for configuration examples.
If the App declares color-scheme: light dark (or color-scheme: dark) and the host document does not, browsers insert an opaque backdrop behind the iframe to prevent cross-scheme bleed-through. Remove the color-scheme declaration and use the [data-theme] attribute pattern from the host context guide.
npm start in this repository to serve examples/basic-host at http://localhost:8080. It logs all protocol traffic to the console.