Skip to content

🤖 feat: MCP Apps views in the Artifacts tab - #5426

Merged
ThomasK33 merged 8 commits into
artifacts-4-goal-boardfrom
artifacts-5-mcp-apps
Oct 2, 2026
Merged

ThomasK33 merged 8 commits into
artifacts-4-goal-boardfrom
artifacts-5-mcp-apps

Conversation

@ThomasK33

Copy link
Copy Markdown
Member

Summary

Implements the host side of MCP Apps (io.modelcontextprotocol/ui, spec 2026-01-26) so MCP servers can show their ui:// views in the Artifacts tab.

Implementation

  • The client announces the extension only while the Artifacts experiment is on; tool _meta.ui (and the deprecated flat key) is kept; tools not visible to the model are hidden from it.
  • resources/read for ui:// views: exact MIME type, 2 MB cap. Full CallToolResults for views are kept in a host-only side store (session dir, capped), never sent to the model.
  • One opaque-origin srcdoc frame (desktop-host model; SECURITY AUDIT comment explains why no proxy frame). CSP starts from the spec default and only grants declared https domains that are also on the CDN allowlist with the setting on.
  • JSON-RPC router (mcpAppHost.ts): initialize, tool input/result, size changes, logging, tools/call (same server, app-visible tools, consent strip for model-visible tools), ui/open-link (https, confirm), ui/message (into the composer, never auto-sent); everything else -32601.
  • "Open in Artifacts" on MCP tool cards; views appear under App views in the picker.

Validation

Router/CSP/visibility/validation unit tests and a Storybook play test with a fake view (handshake, tool result, consent). Dogfooded against a local stdio MCP server fixture with a real model turn.

Stack: part 5 of 9 of the Artifacts stack (each PR shows only its own layer). Everything is behind the Artifacts experiment (Settings → Experiments, off by default). The user-facing page lands in part 9 (docs/workspaces/artifacts.mdx).


Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high • Cost: $98.34

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-02T15:18:22.519327Z 3f026cb New commits
🔒 Security Review ✅ Completed 2026-10-02T15:17:02.113301Z 3f026cb New commits

Security findings

Advisory findings (4)

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@ThomasK33
ThomasK33 added this pull request to stack #5431 October 1, 2026 21:01
@ThomasK33
ThomasK33 force-pushed the artifacts-5-mcp-apps branch from fcf7b1e to 055c6e5 Compare October 1, 2026 21:12

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fcf7b1e322

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/node/services/mcpAppsOperations.ts Outdated
Comment thread src/node/services/mcpServerManager.ts Outdated
Comment thread src/node/services/mcpAppResultStore.ts Outdated
Comment thread src/node/services/mcpServerManager.ts Outdated
Comment thread src/common/utils/mcpApps.ts Outdated
Comment thread src/browser/features/RightSidebar/ArtifactsTab/mcpAppHost.ts
Comment thread src/browser/features/RightSidebar/ArtifactsTab/ArtifactsPanel.tsx Outdated
Comment thread src/node/services/mcpServerManager.ts
Comment thread src/browser/features/Tools/GenericToolCall.tsx
@ThomasK33
ThomasK33 force-pushed the artifacts-5-mcp-apps branch from 055c6e5 to 6dfd5cb Compare October 2, 2026 03:14
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6dfd5cb378

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/browser/features/RightSidebar/ArtifactsTab/McpAppFrame.tsx
Comment thread src/node/services/mcpServerManager.ts
Comment thread src/node/services/mcpServerIdentity.ts Outdated
Comment thread src/browser/features/RightSidebar/ArtifactsTab/ArtifactsPanel.tsx Outdated
Comment thread src/browser/features/RightSidebar/RightSidebar.tsx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ Codex Security Review · Automatically triggered

Here are some automated security review suggestions for this pull request.

Reviewed commit: 6dfd5cb378

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

Comment thread src/browser/features/RightSidebar/ArtifactsTab/McpAppFrame.tsx Outdated
@ThomasK33
ThomasK33 force-pushed the artifacts-5-mcp-apps branch from 6dfd5cb to bd187d5 Compare October 2, 2026 05:28
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: bd187d596c

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bd187d596c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/node/services/mcpAppResultStore.ts
Comment thread src/node/services/mcpServerManager.ts Outdated
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 5aa9c5eb6b

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5aa9c5eb6b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/browser/features/RightSidebar/ArtifactsTab/mcpAppHost.ts
Comment thread src/browser/features/RightSidebar/ArtifactsTab/McpAppFrame.tsx
Comment thread src/browser/features/Tools/GenericToolCall.tsx
Comment thread src/node/services/mcpAppResource.ts
Comment thread src/browser/features/RightSidebar/ArtifactsTab/mcpAppViewsStore.ts
@ThomasK33
ThomasK33 force-pushed the artifacts-5-mcp-apps branch from 5aa9c5e to 5573aac Compare October 2, 2026 08:36
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 5573aac574

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5573aac574

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/node/services/mcpAppsOperations.ts
Comment thread src/browser/components/SelectPrimitive/SelectPrimitive.tsx
Comment thread src/browser/features/Tools/GenericToolCall.tsx
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5573aac574

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/node/services/mcpAppsOperations.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ Codex Security Review · Automatically triggered

Here are some automated security review suggestions for this pull request.

Reviewed commit: 5573aac574

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

Comment thread src/browser/features/RightSidebar/ArtifactsTab/mcpAppHost.ts Outdated
@ThomasK33
ThomasK33 force-pushed the artifacts-5-mcp-apps branch from 5573aac to 1ce21dc Compare October 2, 2026 10:10
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: a8fa73ad5c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33
ThomasK33 force-pushed the artifacts-5-mcp-apps branch from a8fa73a to c451af5 Compare October 2, 2026 12:10
@ThomasK33
ThomasK33 force-pushed the artifacts-5-mcp-apps branch from c451af5 to 2774e9e Compare October 2, 2026 13:15

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2774e9e0cb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/browser/features/RightSidebar/ArtifactsTab/McpAppFrame.tsx Outdated
Comment thread src/browser/features/Tools/GenericToolCall.tsx
@ThomasK33
ThomasK33 force-pushed the artifacts-5-mcp-apps branch from 2774e9e to 4e24434 Compare October 2, 2026 14:03
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4e24434238

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/browser/features/RightSidebar/ArtifactsTab/mcpAppHost.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ Codex Security Review · Automatically triggered

Here are some automated security review suggestions for this pull request.

Reviewed commit: 4e24434238

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

Comment thread src/browser/features/RightSidebar/ArtifactsTab/McpAppFrame.tsx Outdated
- MCP Apps host backend: advertise the ui extension, fetch `ui://` view resources, keep tool results in a bounded side store
- MCP Apps views in the Artifacts tab (App views picker group) with a sandboxed frame, CSP and the host bridge
- "Open in Artifacts" on MCP tool calls that carry a view
- Storybook play test for an MCP Apps view
- Codex review round 1 fixes: the stored result record is the authoritative view binding (getView returns invocation), failure snapshots keep resourceUri, records publish only after the atomic write, view tool calls honor the effective tool allowlist, optional resourceUri with independent visibility, consent shows an argument preview, Reload remounts the view, connections made under the other MCP Apps setting restart when idle, frame navigation guard on McpAppFrame
- Codex review round 2 fixes: MCP App views fail closed outside the desktop app (no fetch, frame or host), failed or interrupted app calls record their invocation, oversize display snapshots drop only the app link, the picker stays above listing errors while views are open, the narrow layout opens views only in the dialog, the consent strip shows the full arguments and declines calls over 64 KiB
- Codex review round 3 fixes: callMcpAppTool dispatches the view's tools/call at most once when the stable-plugin-epoch postflight re-runs the operation (authorization still runs on every attempt before the first dispatch)

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high` • Cost: `$123.74`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high costs=123.74 -->
…rom its display form (Codex r4 QiQG)

Tool cards carry the sanitized display key (whitespace-collapsed, capped at 80 chars),
while the result record and the server manager use the raw configured key, so getView
refused the binding and Open failed for such servers. The record check now accepts the
record's raw key or its display form, and the resource is read from the record's key.

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->
…x r5 RxWr, cross-layer)

MCP App views use their own document preamble (CSP only, no artifact bridge), so the L3
WebRTC removal did not reach them. CSP cannot stop STUN/TURN and Chromium ignores
webrtc 'block', so a view could send data out regardless of its granted connectDomains.
The view document now starts with a script that deletes every RTC global; frame-src
'none' keeps child frames from handing back a fresh constructor.

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->
…restart (Codex r6 SJup)

Server operations start servers only from request options recorded by a send or prompt
discovery. Right after a backend restart there are none, so opening a persisted MCP App
view failed with "not connected" until the next send. getView now runs the same startup
as prompt discovery first when the workspace has no recorded options.

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->
… composer (Codex r6 SLg-)

ui/message appended up to 32,000 characters to the draft without any confirmation. Padded
text could sit below the visible part of the draft and go out with the user's next Send.
The view's message now shows in the consent strip in full, and it reaches the composer
only after the user clicks Add; a declined message fails the request.

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->
… r7 UKEM)

A newer consent request replaced the shown strip in place, so a view could swap the
payload between the user's pointerdown and click and the same Allow/Open/Add button
approved the new request. While a strip is shown, newer requests are now declined, and
the buttons settle only the request that is displayed (moved down from L9; the confirm
arming delay stays in L9).

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->
…prompts (Codex r10 WmkN)

Since the view is bound to the record's raw server key (Codex r4 QiQG), the Allow and Add
prompts rendered that raw key, so a repo-defined key with bidi or control characters could
reorder or disguise the question. The prompts now show the card's sanitized display key;
tool calls still go to the raw key.

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->
…n MCP App consent (Codex r11 Xhga, Xjg8)

ui/open-link accepted https URLs with credentials, so https://trusted.example@evil.example/
was shown as-is in the only gate before the OS opened evil.example. Such links now fail,
and the strip names the parsed host ahead of the full URL. The consent previews (tool
arguments, message text, link) also rendered bidi controls live, so a view could make the
text it asks the user to approve display reordered; they now show as visible \uXXXX
escapes, while the request itself is unchanged.

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->
@ThomasK33
ThomasK33 force-pushed the artifacts-5-mcp-apps branch from 4e24434 to 3f026cb Compare October 2, 2026 15:06

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3f026cb498

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/browser/features/RightSidebar/ArtifactsTab/McpAppFrame.tsx
Comment thread src/browser/features/RightSidebar/ArtifactsTab/mcpAppCsp.ts
@ThomasK33
ThomasK33 added this pull request to the merge queue Oct 2, 2026
Merged via the queue into main with commit efda661 Oct 2, 2026
59 of 78 checks passed
@ThomasK33
ThomasK33 deleted the artifacts-5-mcp-apps branch October 2, 2026 15:53
yermakoffivan pushed a commit to yermakoffivan/mux that referenced this pull request Oct 3, 2026
…r#5559)

## Summary

HTML and SVG artifacts now render as live previews in browser/server
mode (the phone client included), not only in the Electron desktop app.
Outside desktop the preview has no host bridge: `window.xum.send`,
`setState`, frame annotations and key forwarding do nothing there. MCP
App views stay desktop-only.

![HTML artifact preview at 390 px in browser
mode](https://github.com/user-attachments/assets/6837c2ae-ca26-4ba1-92a2-8dfe5cc7e7f4)

## Background

coder#5424 and coder#5426 mounted these sandboxed `srcdoc` frames only when the
desktop preload bridge existed (`executableFrames.ts`). The reason: CSP
does not cover navigation, so a frame can still run `location.href =
"https://host/?d=..."` or follow a tapped link. Electron refuses that
navigation before the request leaves (`will-frame-navigate`,
`src/desktop/subframeNavigation.ts`). A browser has no API that cancels
a frame navigation in advance. That rule showed only escaped source on
phones, which made the Artifacts tab useless there.

## Implementation

- `ArtifactViewer` mounts the HTML/SVG frame in every mode.
- `executableFrames.ts` now gates the bridge, not the frame:
`canBridgeExecutableFrames()` (desktop only). Outside desktop,
`SandboxedArtifactFrame` attaches no `message` listener, and the panel
hides the frame Annotate button.
- Why no bridge in browser mode: a page the frame navigates to keeps the
same `contentWindow`, and its scripts run before the iframe's second
`load` event, which is when `frameNavigationGuard.tsx` notices. So the
host cannot tell the destination's messages from the artifact's (Codex
review finding).
- `McpAppFrame` keeps its desktop-only gate (renamed import only),
because MCP App views need the bridge.

## Risks

Outside Electron, an artifact can send **one** navigation request to any
URL with what the page already holds: its content and the saved state
baked into the `srcdoc`. This risk was accepted on purpose. The frame
still cannot read Xum's DOM, storage, cookies, or API (opaque-origin
sandbox, `allow-scripts` only), and with no listener a navigated page
cannot drive Xum.

Interactive artifacts that rely on `window.xum.send` or `setState`
render on phones but their sends and saves are ignored there.

## Validation

- New regression test: in browser mode, `send` and `setState` posted
from the frame window before any second `load` reach nothing, and no
`message` listener is attached. It fails with the gate line removed.
- Annotate is offered for a frame in desktop mode and hidden in browser
mode (one test covers both).
- Storybook at 390 px with no preload bridge: the frame mounts with
`sandbox="allow-scripts"` and its script runs ("Host theme: dark" in the
screenshot comes from the baked-in initial theme).

## Follow-ups

- coder#5560: tell phone users when `window.xum` interactions are
unavailable; optional `event.origin` hardening for the OAuth listeners.

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking:
`high` • Cost: `$2.65`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high
costs=2.65 -->
yermakoffivan pushed a commit to yermakoffivan/mux that referenced this pull request Oct 6, 2026
…facts (coder#5710)

## Summary

Expanding an MCP Apps tool card now shows the app's view inline, and a
**Show input/output** toggle under the view shows the raw arguments and
result. The Artifacts tab's **App views** picker now lists every
finished app call from the chat, not only views opened with **Open in
Artifacts**. A bug bash on these changes found four defects, and this PR
fixes them. It also adds the dice-board MCP Apps server, seed and
agent-driven e2e suite used to find and verify them.

## Background

MCP Apps views (artifacts experiment, coder#5426) opened only in the
Artifacts tab. Expanding the card showed JSON, and the picker held only
views opened during the session, so after a reload or without a click
the picker had no app views.

## Implementation

1. **Inline view** (`GenericToolCall.tsx`, `McpAppFrame.tsx`): a
finished call with a `ui://` view renders `McpAppFrame` with a new
`inline` variant (no Close header, height from the view's size reports,
720 px cap). The JSON renders below the view, not instead of it, so
toggling never reloads the view. Running calls, VS Code and
experiment-off rows render as before.
2. **Picker from the transcript** (`mcpAppViewsStore.ts`):
`useMcpAppViews` derives views from finished transcript tool parts with
`mcpServer.app`, through a cached snapshot that keeps its identity until
the set of views changes (no re-render per stream delta). Views opened
from cards outside the loaded window are added after them. **Close**
returns to the files and keeps transcript views listed.
3. **Review and bug-bash fixes:**
- Picker entries of one tool were identical: each now shows the call's
arguments and `failed`/`interrupted`.
- A failed call hid its error behind the toggle: failed calls open with
input/output shown, and the view gets `tool-cancelled` with reason
`failed` instead of `interrupted`.
- The inline consent strip could sit above the visible chat: it is now
`sticky top-0`. `scrollIntoView` does not work here, because the chat
pinned to the bottom scrolls back (`useAutoScroll`).
- The card's two buttons had no keyboard focus style: both get a focus
ring.
- Narrow cards show Open in Artifacts as an icon and truncate long
names. J/K walk the App views. Repeats of one call are numbered, and
model-written argument text shows control characters as escapes. App
calls nested in `code_execution` are listed too.
4. **Test harness** (`tests/bugbash`): `startApp.ts --mcp-apps` seeds a
dice-board MCP Apps server and a chat with four calls (with result,
without stored result, failed, and a tool without a view), because mock
AI cannot call MCP tools. `run.ts --config` selects
`e2e.mcpapps.config.ts`. `make mcp-apps-e2e` runs
`mcpapps/mcp-apps.e2e.ts`: seven tests that drive each flow with
`agent.act` and pin the outcome with exact checks. `docs/AGENTS.md`
explains how to run and write them. No CI job runs this suite: it makes
model calls.

## Validation

- Bug bash: 5 charters (inline view, picker state, keyboard,
cross-checking, phone) with Opus 5.5 and Sonnet 5.5, 35 findings,
triaged against the source. Four were confirmed with failing repro tests
and are fixed here.
- E2E suite: on the build before the fixes, 3 of 7 passed and the 4 bug
tests failed with `ASSERTION_FAILED` on their own checks. On this head,
7 of 7 pass (`make mcp-apps-e2e`).
- Unit tests for the inline body, the toggle (the frame stays the same
element), the transcript-derived picker, Close, failed calls and the
cancel reason. Each new test fails with its fix removed.
- Storybook: `App/Chat/Tools/Generic` → `McpAppInline` and
`McpAppInlinePhone`. The phone story pins a 358px width and asserts that
the header action stays inside the card. Both are excluded from Pixel
snapshots (budget).

Failed call, expanded: the error shows at once, and the view says `tool
cancelled: failed`.

![Failed MCP app call expanded with its error
shown](https://github.com/user-attachments/assets/f65f7ce3-e244-4624-b699-ad068581263c)

App views in the picker, told apart by arguments and outcome:

![Artifacts picker listing three dice-board app views with their
arguments](https://github.com/user-attachments/assets/737c71b9-fe27-4f8b-a431-e545353eeec3)

The consent prompt stays on screen when the chat is scrolled to the
view's buttons (from the e2e run):

![Allow/Deny consent strip pinned at the top of the
chat](https://github.com/user-attachments/assets/2ebd6ef3-55a7-48ea-80ba-7e4fc81958c9)

Inline view with input/output open (Storybook):

![Inline dice view with the raw JSON
below](https://github.com/user-attachments/assets/2e75ea5b-55ce-41f7-b687-7d6dedffdd23)

## Risks

Low. Every change is behind the artifacts experiment (off by default)
and only affects MCP tools that declare a `ui://` view. The sandbox, CSP
and consent rules of the view frame are unchanged. The transcript
subscription reads the workspace's existing message list and returns a
cached result unless the set of views changes.

## Decisions and follow-ups

- **Per-card activation.** A call with an app view expands, and so
mounts its view, only on its own card's toggle. The remembered per-tool
expansion does not apply to it, so a reload does not start a view for
every earlier call. A restored Artifacts selection still reopens its one
view after a reload. Consent rules are unchanged.
- **Redacted calls get no view.** Their output was excluded on purpose,
and a view would show it again from the host-only result record.
- **No keybind for Show input/output.** The toggle is a native button in
the card's tab order, like the card's other per-call controls. A global
shortcut needs a focused-card model, which tool cards do not have.
- **Follow-up (unverified):** in one run of the e2e suite with 4
parallel browsers on one server, a view's `tools/call` (Re-roll) never
answered. The same click works alone, and 3 other parallel runs passed.
The suite now runs serially (`workers: 1`). Whether concurrent clients
can stall a view's tool call in `mcpServerManager` is not yet known.

## Pains

The default bug-bash seed has no MCP data and mock AI cannot call MCP
tools, so the seed writes `chat.jsonl` rows and the host-only result
record directly. Tutorial popovers appear late and swallow the first
click in automated runs.

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking:
`high` • Cost: `$20.96`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high
costs=20.96 -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant