Skip to content

[FEATURE][UI]: Render MCP Apps (SEP-1865) interactive content in Playground #4928

Description

@a-effort

🧭 Type of Feature

  • New feature or capability
  • Enhancement to existing functionality

Relationship to other issues

🧭 Epic

Title: Interactive MCP Apps rendered inside the Playground chat
Goal: When a tool result or resource includes MCP Apps UI content (per SEP-1865), the React Playground renders it as a sandboxed iframe inline in the conversation, with bidirectional JSON-RPC to the gateway. This turns Playground from a text/markdown chat into a host capable of displaying dashboards, charts, configuration wizards, and other interactive UIs returned by MCP servers.
Why now: SEP-1865 reached stable on 2026-01-26 and is already supported by Claude (web + desktop), VS Code Insiders, Goose, and Postman. Adding host support in Playground gives ContextForge users a first-class way to consume rich tool output — including the obvious near-term win of metrics/observability tools returning real charts instead of text tables.


🧑🏻‍💻 User Story 1 — Inline rendering of an MCP App

As a: Playground user calling a tool that returns an MCP App (e.g. a metrics dashboard)
I want: the app to render inline in the assistant's turn, sandboxed and themed
So that: I can see and interact with rich tool output without leaving the chat

✅ Acceptance Criteria

Scenario: Tool returns MCP App content
  Given a tool result contains UI content tagged with the io.modelcontextprotocol/ui extension
  When the assistant turn renders
  Then the content is mounted in a sandboxed iframe inline in the message
  And the iframe inherits the Playground theme (light/dark) via the documented theming hook
  And CSP enforces the SEP-1865 sandbox restrictions

Scenario: App reads model context
  Given a rendered MCP App calls ui/get-model-context
  Then it receives the current conversation context per spec

🧑🏻‍💻 User Story 2 — Bidirectional communication

As a: MCP App author
I want: my app to call tools, post chat messages, and update model context from inside the iframe
So that: my dashboard can fetch fresh data, drive the conversation, and behave like a first-class participant

✅ Acceptance Criteria

Scenario: App invokes a tool
  Given a rendered MCP App calls tools/call via the host bridge
  Then the Playground forwards the call through the gateway with the session's auth
  And the response is returned to the app

Scenario: App posts a chat message
  Given a rendered MCP App calls ui/message
  Then the message appears in the conversation as if the user had typed it

Scenario: App updates model context
  Given a rendered MCP App calls ui/update-model-context
  Then subsequent assistant turns see the updated context

🧑🏻‍💻 User Story 3 — Safety and fallback

As a: gateway operator
I want: untrusted MCP App content to be confined and gracefully degraded when unsupported
So that: a third-party MCP server can't pivot through Playground into the rest of the UI

✅ Acceptance Criteria

Scenario: CSP and sandbox enforcement
  Given an MCP App attempts to break out of its iframe sandbox or violate CSP
  Then the attempt is blocked and logged

Scenario: Unsupported display mode
  Given a tool result includes UI content the Playground cannot render
  Then a fallback card is shown with the resource link and MIME type

Scenario: Feature flag off
  Given MCP Apps host support is disabled by config
  Then UI content is shown as a non-interactive fallback only

📐 Design Sketch

flowchart LR
    Tool[MCP Tool result<br/>w/ io.modelcontextprotocol/ui content] --> GW[ContextForge Gateway]
    GW --> Chat[/llmchat session/]
    Chat --> PG[React Playground]
    PG --> IF[Sandboxed iframe<br/>MCP App]
    IF -- ui/message, ui/update-model-context,<br/>tools/call via host bridge --> PG
    PG -- forwarded JSON-RPC --> GW
Loading

Scope

In scope:

  • Detect SEP-1865 UI content in tool results / resources surfaced by /llmchat/chat.
  • Mount the content in a sandboxed iframe with the spec-compliant CSP and theming.
  • Implement the host side of the JSON-RPC bridge: ui/get-model-context, ui/update-model-context, ui/message, and forwarded tools/call.
  • Display-mode support per spec (inline / expanded), with a documented fallback for unsupported modes.
  • Feature flag to disable host support globally.

Out of scope (covered by #2527):

  • Server-side ingestion, federation, and proxying of MCP Apps from upstream MCP servers.
  • Tool-UI association metadata in the registry.
  • Non-Playground hosts (e.g. surfacing the same content elsewhere in the admin UI).

🔗 MCP Standards Check

  • Implements SEP-1865 (io.modelcontextprotocol/ui) on the host/client side.
  • No breaking changes to existing MCP-compliant integrations — content without the extension renders as today.

🔄 Alternatives Considered

  • Ad-hoc inline rendering of HTML/SVG from tool results. Rejected: no security model, no bidirectional comms, no theming contract, no standards alignment.
  • Chart-only convention (e.g. Vega-Lite JSON via a known MIME). Useful as a narrow path for the metrics use case, but not a substitute for MCP Apps. Could ship alongside as a lower-cost interim.
  • Wait for the full [EPIC][SECURITY][MCP-EXTENSIONS]: Governed Extension Framework for MCP Apps and Future Extensions #2527 epic before any client work. Rejected: client-side host support is independently valuable and unblocks a near-term metrics/observability win.

📓 Additional Context

Metadata

Metadata

Assignees

No one assigned

    Labels

    SHOULDP2: Important but not vital; high-value items that are not crucial for the immediate releaseenhancementNew feature or requestfrontendFrontend development (HTML, CSS, JavaScript)mcp-appsSEP-1865: MCP Apps: Interactive User Interfaces for MCPmcp-protocolAlignment with MCP protocol or specificationuiUser Interface

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions