[BUG] MCP Apps widgets never render in third-party (custom-3p) deployment mode — empty feature-flag payload disables the epitaxy MCP Apps gate

Status Open
Reported on v2.1.237
Maintainer reply None cached
Activity 0 comments · opened Aug 21, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

In third-party deployment mode (deploymentMode: 3p), an MCP server that returns UI resources
(MCP Apps, extension io.modelcontextprotocol/ui) connects and negotiates the UI extension
correctly, but its widgets never render on the Code (epitaxy) surface. Every tool result falls
back to raw text, and nothing is logged as an error, so the failure is silent.

No configuration key can enable it — I went through all 109 keys in the 3P schema.

Root cause, as far as I can trace it:

  1. The 3P bootstrap stub returns empty feature-flag payloads:

statsig: { user: { userID: ... }, values: {}, values_hash: "custom3p" }
feature_flags: { user: { userID: ... }, values: {}, values_hash: "custom3p" }

  1. The Code-surface MCP Apps check reads appFeatures.epitaxyMcpApps when present and otherwise

falls back to the client-side gate claudeai_epitaxy_mcp_apps. In 3P the appFeatures document
is never served, so the fallback is taken — and with an empty flag payload the gate is always
false.

  1. The 3P entitlement map additionally omits mcp_artifacts, inline_visualizations and

interactive_content (emitted as undefined, so they are filtered out of
current_user_access.features rather than reported as blocked).

  1. The growthbook feature set that IS derived from configuration is a fixed map; only

featureDiscoveryDisabled, claudeInChromeEnabled and autoModeEnabled contribute to it. There is
no passthrough by which an administrator can supply the missing gate.

What I ruled out:

  • Not egress or DNS. *.claudemcpcontent.com resolves (wildcard record) and answers both directly

and through the configured HTTPS proxy. The 3P protocol handler's runtime egress rules already
include *.claudemcpcontent.com and www.claudeusercontent.com. disableNonessentialServices is
false.

  • Not the MCP server. The server's own log shows the handshake succeeding, and the app reports the

server connected with its full tool count on both the managed and the Developer-settings paths.

  • Not the iframe sandbox. No McpAppSandboxSetupError is logged, i.e. the client never attempts to

set up the widget sandbox at all — consistent with a gate being off rather than an iframe
failing.

Why this reads as an oversight rather than a policy decision:

  • The 3P firewall allowlist ships *.claudemcpcontent.com — the MCP Apps widget iframe origin.
  • disableNonessentialServices documents itself as covering "the artifact-preview and MCP Apps

widget iframe origins", and warns "Artifacts will not render." A control for turning widgets
OFF only makes sense if widgets are otherwise expected to be on.

So the capability appears intended for 3P deployments, while the gate that enables it has no
source of truth in that mode.

What Should Happen?

MCP Apps widgets should render on the Code surface in third-party mode exactly as they do in
first-party mode, or an administrator should be able to enable them through the 3P configuration
schema.

Any one of these would resolve it:

  • include epitaxyMcpApps: { status: "supported" } in the appFeatures/bootstrap payload that 3P

mode synthesizes, or

  • add an administrator config key (for example mcpAppsEnabled) to the 3P schema, or
  • emit mcp_artifacts as available in the 3P entitlement map when the relevant surface is enabled.

Impact: any MCP server that ships a UI resource is text-only in third-party mode. For users
writing in a non-Latin script this is more than cosmetic — a reply-rendering MCP server is the
only thing that displays right-to-left text correctly, because plain chat output reorders it. In
3P that correct rendering is lost entirely.

Error Messages/Logs

There is no error. That is the point — the widget path is never entered.

MCP server side (its own log), showing the UI extension IS negotiated:
  client=claude-ai/0.1.0 mcp-apps=YES roots=NO extensions=["io.modelcontextprotocol/ui"]

Main process, showing the server connected on both paths:
  [custom3p-mcp] connected { name: "<server>", toolCount: 8, auth: "stdio" }
  [LocalMcpServerManager] Connected to <server> (8 tools)

Renderer, at the moment of the tool call — the MCP Apps runtime is alive:
  [MCP Apps] oncalltool handler replaced. Previous handler will no longer be called.

And notably absent, on every attempt:
  McpAppSandboxSetupError   (never logged in 3P)

For contrast, the same server in first-party mode does log that error when the iframe genuinely
fails to load, which is how I concluded the 3P failure happens earlier, at the gate.

Steps to Reproduce

  1. Configure Claude Desktop for third-party mode: deploymentMode "3p",

inferenceProvider "anthropic", inferenceCredentialKind "helper-script" (any working
credential source reproduces it).

  1. Register a local stdio MCP server that returns a UI resource (extension

io.modelcontextprotocol/ui) — either in managedMcpServers or in the Developer-settings
mcpServers list. Both reproduce.

  1. Restart the app and confirm the server connects (tool count appears in the log, tools appear

in the session).

  1. Open a Code session and call the tool that returns the UI resource.
  2. Observe: the tool result is rendered as raw text. The widget never appears, and no error is

logged anywhere.

  1. For comparison, the same server and the same tool call render the widget correctly in

first-party mode.

Also tried, with no effect: enabling every surface (chatTabEnabled, coworkTabEnabled,
isClaudeCodeForDesktopEnabled), disableNonessentialServices false, coworkEgressAllowedHosts ["*"],
isDesktopExtensionEnabled true, mcpPersistentAlwaysAllowEnabled true, modelDiscoveryEnabled true.

Claude Model

Not sure / Multiple models

Is this a regression?

No, this never worked

Last Working Version

_No response_

Claude Code Version

Claude Desktop 1.34493.1 (bundled Claude Code 2.1.237)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Other

Additional Information

Related, and why this is not a duplicate:

  • #69953 (closed) — "3P mode: custom MCP servers blocked — allowedMcpServers hardcoded to empty

array despite managedMcpServers". Same class of defect one layer down: a value hardcoded empty
in 3P silently disables a capability the configuration was meant to control. The empty
statsig / feature_flags payload above is that same pattern applied to feature gates.

  • #59351 (closed) — MCP-Apps host dropping _meta.ui.csp.frameDomains. Prior art that this host

path has had gaps before.

  • Not a duplicate of #34820 / #68599 (claudemcpcontent.com has no apex A record). Those describe

the widget iframe failing to load, which surfaces as McpAppSandboxSetupError. In third-party
mode no such error is logged and no sandbox setup is attempted at all, because the gate
short-circuits before the iframe is created. Different layer, different failure.

I searched for: custom-3p widget, epitaxy mcp apps, MCP Apps widget not rendering, mcp_artifacts,
McpAppSandboxSetupError, third-party inference widget, custom3p, claudemcpcontent — no existing
report of this.

View original on GitHub ↗