Skip to content

Inline visual replies: sandboxed HTML pages and MCP app UIs in the thread #1525

Closed

Description

@currentbits

Replies can render Mermaid (src/components/Markdown.tsx:180), but raw HTML is dropped. MCP servers that return UI resources (ui://, the MCP Apps extension) show nothing. Agents can't show a chart, a prototype or an interactive table inline.

Proposal (M):

  1. An html fence (or an agent tool that publishes a page) renders in an <iframe sandbox="allow-scripts" srcdoc=…> with no same-origin access, no network by default (strict CSP), a size cap and an "open in browser" escape hatch.
  2. MCP tool results that carry a ui:// resource render through the same frame, with the MCP Apps postMessage bridge so the UI can call back into its server via the host.
  3. Lazy-load it and keep it off the streaming hot path. Run npx vite build before shipping.

Security review is required: the frame must not reach the preload bridge or the local web server token.

Activity

  1. added and removed on Oct 9, 2026
  2. currentbits commented on Oct 11, 2026

    @currentbits
    OwnerAuthor

    Implementing on this thread.

    Plan

    1. Sandboxed html fence (same lazy-chunk pattern as mermaid): iframe sandbox="allow-scripts" + srcdoc, strict CSP (connect-src 'none', no allow-same-origin), 200KB cap, Open-in-browser as a download of the CSP-wrapped page (never a same-origin blob window — that would inherit the preload bridge).
    2. Extract embedded ui:// / text/html resources from tool results before the 4000-char output truncate (same harvest path as tool images), store on ToolCallInfo.html, render through the same frame.
    3. MCP Apps postMessage host: accept only event.source === iframe.contentWindow; answer ui/initialize without serverTools/serverResources (host has no MCP client yet); reject tools/call / resources/read with JSON-RPC error.
    4. Keep it off the streaming tail (!live) and lazy-load the chunk. npx vite build before shipping.

    Follow-up (out of scope): a host-side MCP client so the frame can proxy tools/call back to the server.

  3. currentbits commented on Oct 11, 2026

    @currentbits
    OwnerAuthor

    Implemented on this branch (not merged).

    Shipped

    • ```html fences render in <iframe sandbox="allow-scripts" srcdoc> with strict CSP (connect-src 'none', no allow-same-origin), 200KB cap, Open-in-browser as a download of the CSP-wrapped page (not a same-origin blob window).
    • Lazy chunk HtmlPage (~1.2kB); off the streaming tail (!live). npx vite build succeeded.
    • MCP ui:// / text/html resources harvested before the 4000-char output truncate onto ToolCallInfo.html; same frame + ui/initialize handshake. tools/call / resources/read are refused (no host MCP client).
    • markdownChunks now ignores < inside fences so a closed html fence can settle while the reply still streams.

    Security notes for review

    • Frame cannot see window.coder (opaque origin).
    • CSP forbids network, so it cannot fetch the local web-server token host.
    • Do not later add allow-same-origin or connect-src 'self'.

    Follow-up suggested: host MCP client so Apps can call tools back.

  4. currentbits commented on Oct 11, 2026

    @currentbits
    OwnerAuthor

    PR: #1597

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions