Repository navigation
[Bug]: Jira MCP Apps render blank inline cards despite successful tool results #16992
Description
Activity
Note
Grok responding on behalf of Julius.
Thanks for the detailed write-up. I checked this against main at
3143335fc3, which includes the MCP Apps host from #16236. I haven't reproduced it in a running app, and I can't see the Jira shell, so everything about its HTML comes from your report.Confirmed in code: a card that never initializes stays blank, with no failure state and no way to see the tool result
- When a completed tool item carries an app reference, it becomes an
mcp-approw instead of the normal tool row (session-logic.ts#L733-L750). The app card is therefore the only view of that result. - The frame reserves its height up front:
MCP_APP_DEFAULT_HEIGHT = 320(mcpApp.ts#L21, McpAppFrame.tsx#L94, #L496). - The only error text covers failing to get the asset URL: "Unable to load the … app" (McpAppFrame.tsx#L542-L546). The code itself notes that "a frame cannot report a failed load" (#L126-L129).
- The host sends
tool-input/tool-resultonly afterui/notifications/initialized(host.ts#L202-L211, #L389-L396). The spec requires that ordering (apps.mdx#L485). But the host has no timer except the 2 s teardown one (host.ts#L478-L481), and it doesn't tell the frame whether the app ever initialized.
So if the app's script fails to load or throws before
ui/initialize, the result is exactly what you describe: a 320 px empty box that stays that way, with the successful tool result unreachable.What would block the Jira shell (candidates, none confirmed)
The Confluence documents have their JS inline and render. Per your report, the Jira shell is an empty root plus an external module script and stylesheet. Under the host's policy, that difference matters:
- Injected CSP. At capture time the server writes a CSP
<meta>into the document (McpAppSnapshot.ts#L90-L96). Itsscript-src/style-srcare only'unsafe-inline'plus the declaredresourceDomains, andconnect-srcdefaults to'none'(mcpApp.ts#L178-L195). Declared domains are filtered to bare origins: scheme + host (optional leading*.) + optional port (mcpApp.ts#L63-L76). A declared entry with a path or a trailing/would be silently dropped. Your note says the declared CSP permits the module's origin, but that's not the same as the injected one. Could you check the<meta http-equiv="Content-Security-Policy">at the top of the captured Jira attachment and confirm the module/stylesheet origin appears inscript-src/style-src? Please also check whether any API origin the app fetches appears inconnect-src. - Opaque origin. The frame is
sandbox="allow-scripts allow-forms"and neverallow-same-origin(McpAppFrame.tsx#L535-L536). The asset route also sendsContent-Security-Policy: sandbox allow-scripts allow-forms allow-popups(http.ts#L56-L61). Module scripts are fetched with CORS (MDN), so a request from an opaque origin needs a permissiveAccess-Control-Allow-Origin, which you saw (*). More likely to matter: in an opaque origin,localStoragethrowsSecurityError(MDN). An app that touches storage during startup would die beforeui/initialize. The spec's web-host design runs the view inside a sandbox proxy withallow-scripts+allow-same-originon a separate origin (apps.mdx#L474-L475). T3 omitsallow-same-originon purpose. - No
'unsafe-eval'inscript-src(also true of the spec's sample policy). It only matters if the bundle evals.
Which of these hits the Jira app needs the iframe console, so it's not pinned yet.
Possible fixes (for discussion)
- Add an init deadline to the host, e.g. expose an
initialized/stalledsignal. If the app hasn't sentui/notifications/initializedwithin N seconds, the frame swaps the blank box for "The {server} app didn't load" with a toggle that shows the plain tool call/result (it already has that data viauseTurnItemDetail, McpAppFrame.tsx#L150-L162). This fixes the blank space whatever the root cause. - Optionally surface CSP violations from the frame, so a blocked origin is visible instead of silent. That depends on the app document reporting them, so it needs a design pass.
- Whether to loosen the opaque-origin sandbox, e.g. a dedicated per-app origin like the spec's sandbox proxy, is a separate security/design decision. It shouldn't be bundled with the fallback.
Not verified: the actual runtime failure in the Jira iframe, the injected CSP in your captured document, and mobile/web behavior (mobile uses a separate
McpAppWebView). No open PR currently touchesMcpAppFrame.tsx,host.ts,mcpApp.tsorMcpAppSnapshot.ts. Related but separate: #16991 (Confluence dark-mode contrast).- When a completed tool item carries an app reference, it becomes an
- addedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
on Oct 7, 2026
Problem
In the 2026-10-07 desktop nightly, Jira MCP Apps embeds appear as empty rounded white rectangles in a thread. In light mode the rectangles blend into the background but still reserve space. Collapsing the reasoning trace leaves four app embeds: two working Confluence document cards and two blank Jira cards.
This is separate from the Confluence dark-mode text contrast report (#16991).
Read-only findings
Initial read-only inspection of persisted turn items and captured HTML establishes:
t3McpApp: twogetConfluencePagecalls and twogetJiraIssuecalls from the Atlassian MCP integration.isError: true; both contain structured issue data (issuesandcontext, with one issue node).preview_evaluatecall failed withPreview automation evaluate timed out after 15000ms.Its stored output is a text error and contains not3McpAppreference. One Jira capture precedes that timeout; the other follows it immediately, explaining why a blank app appears beneath the error.Access-Control-Allow-Origin: *. The declared resource CSP permits its origin. This does not prove the asset loaded or executed successfully in the affected client.The blank rows therefore belong to successfully captured Jira MCP resources, rather than an embed created by the evaluate failure. The initial resource read/capture succeeded. Subsequent synthetic browser verification isolates an uncaught cookie-access exception during Jira app rendering (details below). Temporal proximity alone does not establish a connection to browser automation.
Source inspection
Latest main at inspection:
3143335fc3a568cbbb5174764961889272135cb9.session-logic.tscreates standalone MCP app rows from completed tool items carrying a matching reference. These survive collapsing the work trace.McpAppSnapshot.tscaptures HTML and injects CSP; it does not snapshot external JS/CSS dependencies or establish that the app renders.McpAppFrame.tsxretains an app-height container (initially 320 px) and loads a sandboxed opaque-origin iframe. Its visible failure state covers asset URL acquisition; it does not cover module execution failure, missing app initialization, or an empty rendered root.host.tswaits forui/notifications/initializedbefore replaying the tool result, with no initialization-failure state exposed to the frame. An app that never initializes can remain empty indefinitely.These explain why a client-side app failure can leave reserved blank space. The synthetic reproduction below narrows the failure to Jira cookie access in an opaque-origin sandbox. The actual nightly client was subsequently inspected in Zen; see the live confirmation below.
Authorized synthetic browser verification
Used a separate T3 collaborative browser tab, without accessing the private thread or manipulating the user's browser. The test recreated the Jira HTML shell using its public module/CSS assets,
sandbox="allow-scripts allow-forms", a permitting resource CSP, a minimal MCP initialize response matching the inspected host, and entirely synthetic issue data (DEMO-1). It was an isolated app reproduction, not a full T3 client integration test.Observed sequence:
ui/initializeand completedui/notifications/initialized.ui/notifications/tool-result, an uncaughtSecurityErroroccurred:The public bundle stack passes through cookie
get,getAnonymousIdFromStorage,getAnonymousIdFromCookieAndUpdateLocalStorage, andgetAnonymousId, then analytics construction and a React effect. The first throwing location is bundle line 123, column 65004. The app root subsequently contains zero child elements and no text.Synthetic example issue,Synthetic project,DEMO-1, andOpen. The app reported a height of 76 px.An IndexedDB access-denied promise rejection also occurs, including in the control where the card renders. It is therefore not the blocking exception isolated by this test. localStorage setup warnings similarly do not prevent initialization.
This establishes a reproducible incompatibility between this Jira bundle's unguarded analytics cookie access and the host's intentional opaque-origin sandbox. It strongly explains the two blank Jira embeds, independently of the nearby preview automation timeout. The subsequent Zen inspection confirms the same cookie-access failure in the actual nightly client.
The diagnostic cookie substitution is not a proposed production fix. Broadening the iframe's origin privileges is not necessary to demonstrate the cause. Any eventual fix should preserve sandbox isolation and ensure widget telemetry/storage failures cannot erase the result; the host should also expose app runtime failures rather than leaving blank space.
Confirmed in the actual Zen nightly client
After explicit authorization, inspected the affected thread in the logged-in hosted nightly (
0.0.46-nightly.20261007.2787) using Zen's native UI and Developer Tools. No scripts or diagnostic substitutions were applied to the live thread.ui/notifications/tool-inputandui/notifications/tool-result. Result replay therefore reached the app after initialization.The source-mapped stack in both cases is:
This matches the synthetic reproduction and confirms the failure is after successful initialization/result delivery, rather than the nearby
preview_evaluatetimeout or failure to fetch the app's module. An initialization-only deadline would not catch this case. No private screenshots, URLs, identifiers, or tool data are included here. Zen was returned to the thread view with Developer Tools closed.Expected behavior
Successful Jira tool results should render their app cards. If an app cannot load, initialize, or render, the thread should expose an understandable failure and access to the tool result instead of indefinite blank space.
Scope and related issues
Observed in the desktop report and confirmed in hosted web through Zen. Mobile behavior was not verified. #7212 concerns the floating collaborative browser panel, not inline MCP app cards. #16991 concerns Confluence theme contrast. Searches for MCP blank/empty/embed/render and Jira reports found no matching issue.
No private screenshots, thread IDs, customer identifiers, private URLs, or raw tool payloads are included. Live state was accessed read-only. After explicit authorization, a separate synthetic preview tab and read-only inspection of the actual nightly thread in Zen were used. No private screenshots were taken or published, and no code changes were made.