Skip to main content
When an MCP App doesn’t render or load correctly in Claude, the tool call usually still appears in the conversation, and the cause is in how the app connects, sizes itself, receives its data, or loads its assets. This page is for developers debugging their own MCP App. Open the developer tools in Claude Desktop or on iOS to inspect the app’s iframe, then match what you see against the common problems.

Open developer tools

Claude Desktop and the Claude iOS app both let you inspect a running MCP App with browser developer tools.

Claude Desktop

Claude Desktop’s Developer Tools can help you debug MCP Apps. To use them:
1

Enable Developer Mode

Open Help > Troubleshooting and click Enable Developer Mode. A new Developer menu appears in the menu bar.
2

Open Developer Tools

Open Developer Tools by pressing Cmd+Option+I on Mac or Ctrl+Shift+I on Windows.
3

Find your app's iframe

Inspect the tool call element and look for an iframe nested inside another iframe. Your app is loaded as the content of the inner iframe.
From the Developer menu, select Reload MCP Configuration after editing your claude_desktop_config.json to apply changes without restarting.

iOS

On iOS, the Claude app renders your MCP App inside a WKWebView. You can inspect it from a connected Mac using Safari’s Web Inspector. Follow Apple’s guide to inspecting iOS for setup. Once connected, the Claude web view appears under your device in Safari’s Develop menu, and you can use the console, network panel, and element inspector as you would on desktop.

Fix common problems

These are the problems developers hit most often when an MCP App doesn’t render or load correctly in Claude, each with its cause and fix.

Tool call appears but the app is invisible

An invisible app under a visible tool call is the most common issue when developing MCP Apps. The cause is usually a missing app.connect() call or an iframe with zero height.

Missing app.connect() call

Your app must call app.connect() in vanilla JS or useApp() in React to establish communication with Claude Desktop. Register your handlers before connecting:
Event handlers like app.ontoolinput and app.ontoolresult aren’t invoked until the app is connected.

Iframe has zero height

Your app needs a non-zero height to be visible. A zero height can occur if:
  • Your app’s container has no content yet
  • You called sendSizeChanged({ width, height: 0 })
Check that your root element has explicit dimensions or content that gives it height.

App doesn’t render when tool results are large

When a tool result exceeds approximately 150,000 characters and Claude’s code execution sandbox is active, Claude writes the result to the sandbox filesystem instead of passing it inline to the conversation. Your app receives a pointer to the stored file rather than the structured content it needs, so it never hydrates.
This ~150,000-character threshold is specific to claude.ai and Claude Desktop. Claude Code uses a separate 25,000-token default limit, configurable through MAX_MCP_OUTPUT_TOKENS.
To stay under the threshold, keep initial tool result payloads lean:
  • Paginate large results: return a summary or the first page of data, and let the user request more through follow-up interactions
  • Fetch details on demand: use app-initiated tool calls to load additional data from within your widget as the user explores, rather than returning everything upfront
  • Defer heavy content: if your data includes large blobs such as full document text, base64-encoded images, or extensive logs, return identifiers or previews in the initial result and provide a separate tool to retrieve the full content when needed

Assets or API requests fail only on iOS

If your app loads on desktop and web but fails to fetch scripts, images, or API data on iOS, check whether your server, CDN, or WAF is gating access on the Referer header. WebKit on iOS, in both Safari and the Claude iOS app, omits the Referer header on cross-origin subresource requests as part of its tracking prevention, per WebKit bugs 206521 and 179053. A server that requires a Referer to allow the request rejects iOS traffic even though the same app works elsewhere. To fix the iOS failures, allowlist on the Origin header instead of Referer, because WebKit does send Origin. Requests from your app carry an Origin of {hash}.claudemcpcontent.com, and Set ui.domain for Claude shows how to compute the hash for your server URL. Configure your infrastructure to allow requests whose Origin matches *.claudemcpcontent.com and return a corresponding Access-Control-Allow-Origin header.
The missing Referer header affects requests your app makes directly from the user’s device, such as loading bundles and images or calling your own API from client-side code. MCP tool calls are proxied through Claude’s backend and egress from Anthropic’s published IP ranges, not the user’s device.

ui.domain validation fails

Setting _meta.ui.domain on your resource opts your app into a stable sandbox origin, which you need if your app runs its own OAuth flow. Claude validates the value against your connector URL, and when validation fails it shows an Invalid ui.domain format or ui.domain mismatch error instead of rendering the app. The value must be exactly {hash}.claudemcpcontent.com, where {hash} is the first 32 hexadecimal characters of the SHA-256 digest of your full connector URL. Compute it by running this command with your own URL:
A mismatch usually has one of these causes:
  • The URL you hashed differs from the URL Claude connects to: the hash covers the full URL string including scheme, path, and any trailing slash, so https://example.com/mcp and https://example.com/mcp/ produce different values. Hash the exact URL configured in Customize > Connectors
  • The connector is a local stdio server: local connectors have no URL to hash, so ui.domain isn’t available for them. Remove the field, or deploy the server as a remote connector to use a stable origin
Set ui.domain for Claude explains how the origin is used across platforms.

Next steps