Skip to content

[AI-stack FB4] fresh-ui: mcp-ui-widget (themed sandboxed ui:// iframe) #257

Description

@rickylabs

Add an mcp-ui-widget fresh-ui registry island that renders ui:// MCP-UI resources in a themed, sandboxed iframe and remounts cleanly on theme change.

Context

Part of epic #238 · cluster fresh-ui · wave v1 · depends-on FB0, FA3.

This slice sits at the top of the fresh-ui plane's DAG: it consumes FB0's ai collection registration and FA3's ui:// origin/server handler to ship the actual consumer-facing widget. It cannot land before either, since there is no collection to register into and no origin to point the iframe at.

Problem / motivation

eis-chat renders MCP-UI resources today via a hand-rolled iframe with no sandbox hardening, no theme propagation, and no defined remount contract, so a theme switch leaves a stale iframe showing the previous theme's injected styles (or, worse, a half-updated DOM). NetScript has no registry item at all for consuming ui:// resources; fresh-ui's generative-UI story is incomplete without a themed, sandboxed widget that the renderer can hand a resource URL to and trust it will behave consistently across theme changes.

Scope

Ships

  • packages/fresh-ui/registry/islands/McpUiWidget.tsx — Fresh island rendering an <iframe> against a caller-supplied ui://-resolved src (the FA3-owned origin), with a restrictive sandbox attribute (allow-scripts only; no allow-same-origin).
  • Keyed remount: the iframe is keyed on the active theme value so a theme change forces a full unmount/remount instead of reusing a stale iframe document.
  • registry.manifest.ts: new registry item mcp-ui-widget (kind: 'island', layer: 3, tags ['ai', 'mcp-ui', 'iframe'], registryDependencies: ['theme-seed']), appended to the ai collection's items list (the collection itself is created by FB0).

Out of scope

  • The ui:// server handler / origin that actually serves MCP-UI resources — owned by FA3.
  • Creating the ai collection and registering the 7 chat primitives — owned by FB0 (hard prerequisite for this slice).
  • The generative-UI renderer that decides when to hand a resource URL to this widget, and the curated design-system vocabulary around it — owned by the fresh-ui renderer slice(s) tracked separately in the epic: NetScript AI Stack — first-class AI runtime, chat & plugin seams (anchor #219) #238 DAG.
  • Any postMessage/RPC bridge between host app and iframe content beyond the iframe boundary itself — not specified in this slice; flag as a follow-on if the widget needs bidirectional messaging.

Public surface

  • New file packages/fresh-ui/registry/islands/McpUiWidget.tsx exporting a default island component, approximate props { src: string; theme: string; title?: string; sandbox?: string }.
  • New registry.manifest.ts entry: { name: 'mcp-ui-widget', kind: 'island', layer: 3, files: [...], registryDependencies: ['theme-seed'], tags: ['ai', 'mcp-ui', 'iframe'] }.
  • mcp-ui-widget added to the ai collection's items array (collection object owned/created by FB0; this slice only appends its own item id).
  • No new @netscript/fresh-ui subpath exports — registry items are copy-scaffolded files, not module exports.

Acceptance & fitness gates

  • F-5: McpUiWidget.tsx carries the same JSDoc/@module convention as existing registry islands (e.g. Toast.tsx); item stays within the manifest's per-item field conventions.
  • F-6: @netscript/fresh-ui still publishes via deno task publish:dry-run WITHOUT --allow-slow-types after the new file and manifest entry land.
  • gate:e2e -> deferred to follow-up test(cli-e2e): scaffold.runtime coverage for ui:add ai (McpUiWidget) — split from #257 #561 (ui:add scaffold.runtime e2e coverage); tracked separately, not a merge gate for the fresh-ui island (island proven via PR feat(fresh-ui): [FB4] mcp-ui-widget — themed sandboxed ui:// iframe island #550 IMPL-EVAL, evaluate-257.md).
  • Keyed remount verified: changing the theme prop produces a distinct iframe mount (not a reused DOM node retaining the prior theme's injected content).
  • Sandbox attribute is present and restrictive on every render path (allow-scripts without allow-same-origin), and src only ever resolves to the FA3-owned origin, never a same-origin app route.
  • deno task check / lint pass on packages/fresh-ui with the new file and manifest entry included.

Dependencies

Depends on FB0, FA3 (GitHub numbers tracked in the #238 DAG checklist). FB0 must land first to create the ai collection this slice appends into; FA3 must land first to provide the ui:// origin this widget's iframe points at.

Size

M

Part of #238

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions