@seed-ship/mcp-ui-solid 2.5.0 → 2.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,67 +1,20 @@
1
1
  # @seed-ship/mcp-ui-solid
2
2
 
3
- SolidJS components for rendering MCP-generated UI resources. Part of the MCP UI ecosystem.
3
+ SolidJS components + chat toolkit for MCP-generated UI. Part of the [MCP UI ecosystem](https://github.com/theseedship/mcp-ui).
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@seed-ship/mcp-ui-solid.svg)](https://www.npmjs.com/package/@seed-ship/mcp-ui-solid)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
- ## What's New in v2.0.0
8
+ ## What's New in v2.5.0
9
9
 
10
- ### Highlights
11
-
12
- - **Configurable Iframe Whitelist** - Control iframe security with `IframePolicy`: `strict`, `extend`, or `allow-all`
13
- - **60+ Whitelisted Domains** - Expanded default whitelist for business use cases
14
- - **New Component Types** - Forms, Modals, Action Groups, Image Gallery, Video, Code, Map
15
- - **Table Virtualization** - Handle 10,000+ rows with smooth scrolling
16
- - **Map Clustering** - Auto-cluster markers for large datasets
17
- - **Native Chart.js Support** - Optional direct Chart.js rendering (no iframe)
18
-
19
- ### Configurable Iframe Security
20
-
21
- ```typescript
22
- import { validateComponent, DEFAULT_IFRAME_DOMAINS } from '@seed-ship/mcp-ui-solid'
23
-
24
- // Default: strict whitelist
25
- validateComponent(component)
26
-
27
- // Extend whitelist with custom domains
28
- validateComponent(component, {
29
- iframePolicy: 'extend',
30
- customIframeDomains: ['my-trusted-site.com']
31
- })
32
-
33
- // Disable whitelist (use with caution)
34
- validateComponent(component, { iframePolicy: 'allow-all' })
35
-
36
- // View default whitelist
37
- console.log(DEFAULT_IFRAME_DOMAINS)
38
- ```
39
-
40
- ### Whitelisted Domains (v2.0.0)
41
-
42
- | Category | Domains |
43
- |----------|---------|
44
- | **Video** | youtube.com, vimeo.com, loom.com, cloudflarestream.com, streamable.com |
45
- | **Diagrams** | mermaid.live, excalidraw.com, lucidchart.com, figma.com, miro.com |
46
- | **Code** | github.com, gitlab.com, codepen.io, codesandbox.io, stackblitz.com, replit.com |
47
- | **Google** | docs, sheets, slides, drive, maps, datastudio, lookerstudio |
48
- | **Business** | notion.so, airtable.com, calendly.com, typeform.com, cal.com |
49
- | **Analytics** | tableau.com, powerbi.com, observablehq.com |
50
- | **Design** | canva.com, figma.com |
51
- | **Maps** | maps.google.com, openstreetmap.org |
52
- | **Previews** | vercel.app, netlify.app |
53
- | **E-commerce** | amazon.com, amazon.fr, amazon.de, amazon.co.uk, etc. |
54
-
55
- ---
56
-
57
- ## Overview
58
-
59
- `@seed-ship/mcp-ui-solid` provides a complete rendering solution for MCP (Model Context Protocol) generated UIs. It enables AI/LLM systems to generate structured, interactive dashboards that are rendered with SolidJS.
60
-
61
- **Key Use Cases:**
62
- - Render AI-generated dashboards and reports
63
- - Display streaming UI components progressively
64
- - Build interactive data visualizations from MCP resources
10
+ - **Chat Bus** (`@experimental`) - Bidirectional event/command bus for agent interactions
11
+ - **ChatPrompt** (`@experimental`) - Structured interactions above chat input (choice, confirm, form)
12
+ - **19 component renderers** - chart, table, metric, text, code, map, form, modal, image-gallery, video, iframe, image, link, action, action-group, grid, carousel, artifact, footer
13
+ - **ExpandableWrapper** - Fullscreen expand for tables, charts, code (DOM reparenting)
14
+ - **Table/Chart/Code export** - CSV/TSV/JSON download, PNG export, word wrap toggle
15
+ - **Tiered iframe sandbox** - Trusted domains get `allow-same-origin`; untrusted get restrictive sandbox
16
+ - **Complete validation** - All 19 types validated, scatter/bubble/time-series chart support
17
+ - **ComponentToolbar** - Unified toolbar with copy, download, expand, wordwrap actions
65
18
 
66
19
  ## Installation
67
20
 
@@ -71,15 +24,15 @@ pnpm add @seed-ship/mcp-ui-solid
71
24
  npm install @seed-ship/mcp-ui-solid
72
25
  ```
73
26
 
74
- **Peer Dependencies:**
75
- - `solid-js` ^1.9.0
27
+ **Peer dependencies:** `solid-js` ^1.9.0
76
28
 
77
29
  ## Quick Start
78
30
 
31
+ ### Static UI Rendering
32
+
79
33
  ```tsx
80
- import { UIResourceRenderer, StreamingUIRenderer } from '@seed-ship/mcp-ui-solid'
34
+ import { UIResourceRenderer } from '@seed-ship/mcp-ui-solid'
81
35
 
82
- // Static rendering of a pre-built layout
83
36
  function Dashboard() {
84
37
  const layout = {
85
38
  id: 'dashboard-1',
@@ -88,358 +41,325 @@ function Dashboard() {
88
41
  {
89
42
  type: 'metric',
90
43
  id: 'revenue',
91
- title: 'Total Revenue',
44
+ title: 'Revenue',
92
45
  value: '$125,430',
93
- trend: { direction: 'up', value: '+12%' },
94
- position: { x: 0, y: 0, width: 4, height: 1 }
46
+ position: { colStart: 1, colSpan: 4 }
95
47
  },
96
48
  {
97
49
  type: 'chart',
98
- id: 'sales-chart',
99
- chartType: 'line',
100
- data: { /* chart data */ },
101
- position: { x: 0, y: 1, width: 8, height: 2 }
50
+ id: 'trends',
51
+ params: { type: 'line', data: { labels: ['Q1','Q2','Q3'], datasets: [{ label: 'Sales', data: [10,20,30] }] } },
52
+ position: { colStart: 5, colSpan: 8 }
102
53
  }
103
54
  ]
104
55
  }
105
56
 
106
57
  return <UIResourceRenderer content={layout} />
107
58
  }
59
+ ```
60
+
61
+ ### Streaming UI with SSE
62
+
63
+ ```tsx
64
+ import { StreamingUIRenderer } from '@seed-ship/mcp-ui-solid'
108
65
 
109
- // Streaming rendering from an MCP server
110
66
  function StreamingDashboard() {
111
67
  return (
112
68
  <StreamingUIRenderer
113
69
  query="Show me quarterly revenue trends"
114
70
  spaceIds={['analytics-space']}
115
- onComplete={(metadata) => console.log('Render complete', metadata)}
116
- onError={(error) => console.error('Render failed', error)}
71
+ onComplete={(metadata) => console.log('Complete', metadata)}
117
72
  />
118
73
  )
119
74
  }
120
75
  ```
121
76
 
122
- ## Architecture
123
-
124
- ```
125
- ┌─────────────────────────────────────────────────────────────┐
126
- │ Your SolidJS App │
127
- ├─────────────────────────────────────────────────────────────┤
128
- │ UIResourceRenderer │ StreamingUIRenderer │
129
- │ (static layouts) │ (progressive SSE streaming) │
130
- ├─────────────────────────────────────────────────────────────┤
131
- │ Component Registry │
132
- │ ┌─────────┬─────────┬────────┬──────────┬───────────────┐ │
133
- │ │ Chart │ Table │ Metric │ Text │ Image/Link... │ │
134
- │ │Renderer │Renderer │Renderer│Renderer │ Renderers │ │
135
- │ └─────────┴─────────┴────────┴──────────┴───────────────┘ │
136
- ├─────────────────────────────────────────────────────────────┤
137
- │ Validation │ Error Boundaries │ Grid Layout (12-col) │
138
- └─────────────────────────────────────────────────────────────┘
139
- ```
140
-
141
- ## Components
142
-
143
- ### Core Renderers
144
-
145
- | Component | Description | Key Props |
146
- |-----------|-------------|-----------|
147
- | `UIResourceRenderer` | Static layout rendering | `content`, `className` |
148
- | `StreamingUIRenderer` | Progressive SSE streaming | `query`, `spaceIds`, `onComplete`, `onError` |
149
- | `GenerativeUIErrorBoundary` | Error isolation with retry | `fallback`, `onError` |
150
-
151
- ### Data Display Renderers
152
-
153
- | Component | Type | Description |
154
- |-----------|------|-------------|
155
- | `ChartRenderer` | `chart` | Line, bar, pie, area charts (Chart.js) |
156
- | `TableRenderer` | `table` | Data tables with sorting, markdown links |
157
- | `MetricRenderer` | `metric` | KPI cards with trends and sparklines |
158
- | `TextRenderer` | `text` | Markdown-enabled text blocks |
159
- | `ImageRenderer` | `image` | Responsive images with lazy loading |
160
- | `LinkRenderer` | `link` | External links with security attributes |
161
- | `IframeRenderer` | `iframe` | Secure iframe embedding (sandboxed) |
77
+ ## Chat Bus — Agent Interactions (`@experimental`)
162
78
 
163
- ### Layout Renderers
79
+ Bidirectional event/command system for agent-driven chat interactions. Your app keeps full control of its chat UI — the bus adds structured interactivity on top.
164
80
 
165
- | Component | Type | Description |
166
- |-----------|------|-------------|
167
- | `GridRenderer` | `grid` | **NEW v1.2.0** - Nested CSS Grid layouts for complex dashboards |
81
+ ### Architecture
168
82
 
169
- ### Interactive Renderers
170
-
171
- | Component | Type | Description |
172
- |-----------|------|-------------|
173
- | `ActionRenderer` | `action` | Interactive buttons with MCP tool calls |
174
- | `ArtifactRenderer` | `artifact` | File download/preview |
175
- | `CarouselRenderer` | `carousel` | Image/content carousel |
176
- | `FooterRenderer` | `footer` | Metadata and attribution display (auto-injected)
177
-
178
- ## Exports
179
-
180
- ### Main Export
181
-
182
- ```typescript
183
- import {
184
- UIResourceRenderer,
185
- StreamingUIRenderer,
186
- GenerativeUIErrorBoundary,
187
- // Individual renderers
188
- ChartRenderer,
189
- TableRenderer,
190
- MetricRenderer,
191
- TextRenderer,
192
- ActionRenderer,
193
- // ...
194
- } from '@seed-ship/mcp-ui-solid'
195
83
  ```
196
-
197
- ### Hooks
198
-
199
- ```typescript
200
- import { useStreamingUI, useAction, useToolAction } from '@seed-ship/mcp-ui-solid'
201
-
202
- // Streaming hook
203
- const { components, isComplete, error, metadata } = useStreamingUI({
204
- query: 'Show revenue data',
205
- spaceIds: ['space-1']
206
- })
207
-
208
- // Action hooks (NEW v1.2.0)
209
- const { execute, isExecuting, lastError } = useAction()
210
- await execute('search.hub', { query: 'revenue Q4' })
211
-
212
- // Bound to specific tool
213
- const { execute: searchExecute } = useToolAction('search.hub')
214
- await searchExecute({ query: 'test' })
84
+ ┌──────────────────────┐
85
+ │ AGENT LAYER │
86
+ │ (your app logic) │
87
+ └──┬──────────────┬────┘
88
+ events │ │ commands
89
+ ▼ ▼
90
+ ┌──────────────────────────────────────────────────┐
91
+ │ Chat Messages (your app renders these) │
92
+ │ + UIResourceRenderer for MCP components │
93
+ ├──────────────────────────────────────────────────┤
94
+ │ ChatPrompt (MCP-UI) — choice | confirm | form │
95
+ ├──────────────────────────────────────────────────┤
96
+ │ Chat Input (your app controls this)
97
+ └──────────────────────────────────────────────────┘
215
98
  ```
216
99
 
217
- ### Context Provider (NEW v1.2.0)
100
+ ### Usage
218
101
 
219
- ```typescript
220
- import { MCPActionProvider, useMCPAction } from '@seed-ship/mcp-ui-solid'
102
+ ```tsx
103
+ import { ChatBusProvider, useChatBus, ChatPrompt, createChatBus } from '@seed-ship/mcp-ui-solid'
221
104
 
222
- // Wrap your app to enable typed action dispatch
105
+ // 1. Wrap your app
223
106
  function App() {
224
107
  return (
225
- <MCPActionProvider
226
- spaceIds={['space-123']}
227
- macroId="dashboard_template"
228
- onAction={(req, res) => auditLog(req, res)}
229
- onWebhook={(event) => triggerN8n(event)}
230
- >
231
- <UIResourceRenderer content={layout} />
232
- </MCPActionProvider>
108
+ <ChatBusProvider>
109
+ <ChatInterface />
110
+ <AgentRouter />
111
+ </ChatBusProvider>
233
112
  )
234
113
  }
235
114
 
236
- // Inside any component
237
- function ActionButton() {
238
- const { executeAction, isExecuting } = useMCPAction()
115
+ // 2. Bridge your SSE events to the bus
116
+ function ChatInterface() {
117
+ const bus = useChatBus()
118
+ const [activePrompt, setActivePrompt] = createSignal(null)
119
+
120
+ // Bridge SSE → bus events
121
+ onSSEEvent('done', (data) =>
122
+ bus.events.emit('onStreamEnd', { streamKey: 'main', metadata: data }))
123
+ onSSEEvent('ui_layout', (data) =>
124
+ bus.events.emit('onUILayout', { streamKey: 'main', layout: data }))
125
+
126
+ // Handle commands from agents
127
+ bus.commands.handle('injectPrompt', (text) => setInputValue(text))
128
+ bus.commands.handle('sendPrompt', (text) => {
129
+ setInputValue(text); handleSend(); return crypto.randomUUID()
130
+ })
131
+ bus.commands.handle('showChatPrompt', (config) => setActivePrompt(config))
239
132
 
240
133
  return (
241
- <button
242
- onClick={() => executeAction({ toolName: 'search.hub', params: { query: 'test' } })}
243
- disabled={isExecuting()}
244
- >
245
- Search
246
- </button>
134
+ <div>
135
+ <Messages />
136
+ <Show when={activePrompt()}>
137
+ <ChatPrompt
138
+ config={activePrompt()!}
139
+ onSubmit={(response) => {
140
+ bus.events.emit('onChatPromptResponse', { streamKey: 'main', response })
141
+ setActivePrompt(null)
142
+ }}
143
+ onDismiss={() => setActivePrompt(null)}
144
+ />
145
+ </Show>
146
+ <TextInput />
147
+ </div>
247
148
  )
248
149
  }
249
- ```
250
-
251
- ### Validation (SSR-Safe)
252
150
 
253
- ```typescript
254
- // Safe to import on server - no browser APIs
255
- import { validateUIResource, validateLayout } from '@seed-ship/mcp-ui-solid/validation'
151
+ // 3. Agents react to events and emit commands
152
+ function AgentRouter() {
153
+ const bus = useChatBus()
154
+
155
+ bus.events.on('onStreamEnd', (event) => {
156
+ if (event.metadata.needs_clarification) {
157
+ bus.commands.exec('showChatPrompt', {
158
+ type: 'choice',
159
+ title: 'Which period?',
160
+ config: { options: [{ value: '2024', label: '2024' }, { value: '2025', label: '2025' }] }
161
+ })
162
+ }
163
+ })
256
164
 
257
- const result = validateUIResource(resource)
258
- if (!result.valid) {
259
- console.error(result.errors)
165
+ return null
260
166
  }
261
167
  ```
262
168
 
263
- ### Types Only (SSR-Safe)
264
-
265
- ```typescript
266
- // Type-only imports - no runtime code
267
- import type { UIResource, UIComponent, GridPosition } from '@seed-ship/mcp-ui-solid/types-only'
268
- ```
269
-
270
- ## SSR Compatibility
271
-
272
- This package is fully SSR-compatible with SolidStart, Astro, and other SSR frameworks.
273
-
274
- ### For SolidStart Users
275
-
276
- Add `conditions` to your `app.config.ts` for optimal SSR behavior:
169
+ ### Event Types (15)
170
+
171
+ | Event | Payload | Description |
172
+ |-------|---------|-------------|
173
+ | `onToken` | `{ token }` | Streaming text token (use throttle) |
174
+ | `onStreamStart` | `{}` | Stream started |
175
+ | `onStreamEnd` | `{ metadata }` | Stream completed with metadata |
176
+ | `onError` | `{ error }` | Stream error |
177
+ | `onUILayout` | `{ layout }` | MCP UI component to render |
178
+ | `onCitation` | `{ citation }` | Citation reference |
179
+ | `onToolCall` | `{ tool }` | Tool execution status |
180
+ | `onSuggestions` | `{ items }` | Suggestion chips |
181
+ | `onChatPromptResponse` | `{ response }` | User responded to ChatPrompt |
182
+ | `onClarificationNeeded` | `{ clarification }` | Needs user clarification |
183
+ | `onAgentSwitch` | `{ agent }` | Active agent changed |
184
+ | `onBriefing` | `{ briefing }` | Briefing update |
185
+ | `onCapabilityChange` | `{ capabilities }` | Agent capabilities changed |
186
+ | `onCustomEvent` | `{ type, data }` | App-specific event |
187
+
188
+ All events carry `ChatEventBase` (`streamKey`, `conversationId?`, `correlationId?`) for multi-stream support.
189
+
190
+ ### Command Types (10)
191
+
192
+ | Command | Args | Returns | Description |
193
+ |---------|------|---------|-------------|
194
+ | `injectPrompt` | `text` | void | Fill input without sending |
195
+ | `sendPrompt` | `text, metadata?` | `correlationId` | Fill + send, returns correlation ID |
196
+ | `appendPrompt` | `text` | void | Append to current input |
197
+ | `showChatPrompt` | `config, signal?` | `Promise<Response>` | Show structured prompt (AbortSignal for cleanup) |
198
+ | `dismissChatPrompt` | — | void | Close active prompt |
199
+ | `showSuggestions` | `items` | void | Show suggestion chips |
200
+ | `toggleConnector` | `id, enabled` | void | Toggle a connector |
201
+ | `setMode` | `mode` | void | Change chat mode |
202
+ | `scrollToMessage` | `messageId` | void | Scroll to message |
203
+ | `notify` | `message, type?` | void | Show notification |
204
+
205
+ ### Throttle + StreamKey Filtering
277
206
 
278
207
  ```typescript
279
- // app.config.ts
280
- import { defineConfig } from '@solidjs/start/config'
208
+ // Throttle hot-path events (recommended for onToken)
209
+ bus.events.on('onToken', handler, { throttle: 100 })
281
210
 
282
- export default defineConfig({
283
- vite: {
284
- resolve: {
285
- conditions: ['solid', 'development', 'browser']
286
- }
287
- }
288
- })
211
+ // Filter by stream (multi-stream support)
212
+ bus.events.on('onStreamEnd', handler, { streamKey: 'stream-1' })
289
213
  ```
290
214
 
291
- ### Why This Matters
215
+ ## ChatPrompt Structured Interactions (`@experimental`)
292
216
 
293
- The `"solid"` condition enables Vite to use source exports, which are compiled in the same context as your app. This prevents:
294
- - Hydration mismatches between server and client
295
- - Module resolution conflicts with `solid-js/web`
296
- - SSR crashes from client-only APIs
217
+ Three subtypes for common agent interaction patterns:
297
218
 
298
- ### SSR-Safe Imports
299
-
300
- For server-side code, use the dedicated sub-exports:
301
-
302
- ```typescript
303
- // Server-side file (.server.ts)
304
- import type { UIResource } from '@seed-ship/mcp-ui-solid/types-only'
305
- import { validateUIResource } from '@seed-ship/mcp-ui-solid/validation'
306
-
307
- // These imports don't trigger browser APIs
219
+ ```tsx
220
+ // Choice — buttons with optional icons and descriptions
221
+ <ChatPrompt config={{
222
+ type: 'choice',
223
+ title: 'Export format?',
224
+ config: {
225
+ options: [
226
+ { value: 'pdf', label: 'PDF', icon: '📄' },
227
+ { value: 'csv', label: 'CSV', icon: '📊', description: 'Raw data' },
228
+ ],
229
+ layout: 'horizontal', // or 'vertical' | 'grid'
230
+ }
231
+ }} onSubmit={handleResponse} />
232
+
233
+ // Confirm — with danger variant
234
+ <ChatPrompt config={{
235
+ type: 'confirm',
236
+ title: 'Delete 47 documents?',
237
+ config: { message: 'This cannot be undone.', confirmLabel: 'Delete', variant: 'danger' }
238
+ }} onSubmit={handleResponse} />
239
+
240
+ // Form — quick fields with validation
241
+ <ChatPrompt config={{
242
+ type: 'form',
243
+ title: 'Additional info',
244
+ config: {
245
+ fields: [
246
+ { name: 'title', label: 'Title', type: 'text', required: true },
247
+ { name: 'category', label: 'Category', type: 'select',
248
+ options: [{ label: 'Report', value: 'report' }] },
249
+ ],
250
+ submitLabel: 'Send',
251
+ }
252
+ }} onSubmit={handleResponse} />
308
253
  ```
309
254
 
310
- ## Grid System
311
-
312
- Components use a 12-column responsive grid:
255
+ ## Component Renderers (19 types)
256
+
257
+ | Type | Renderer | Features |
258
+ |------|----------|----------|
259
+ | `chart` | ChartJSRenderer | Bar, line, pie, scatter, bubble, polarArea. Native Chart.js or Quickchart fallback. PNG export, configurable height. |
260
+ | `table` | TableRenderer | Sortable columns, pagination, virtualization (10K+ rows). CSV/TSV/JSON export. |
261
+ | `metric` | MetricRenderer | KPI cards with trends and sparklines |
262
+ | `text` | TextRenderer | Markdown rendering via marked.js |
263
+ | `code` | CodeBlockRenderer | Syntax highlighting (highlight.js), line numbers, word wrap toggle, filename header |
264
+ | `map` | MapRenderer | Leaflet maps with markers, clustering, auto-fit bounds |
265
+ | `form` | FormRenderer | Conditional fields, persistence, tool call submit |
266
+ | `modal` | ModalRenderer | Portal overlay, sizes sm-full, Escape/backdrop close |
267
+ | `image-gallery` | ImageGalleryRenderer | Grid layout, lightbox overlay, keyboard navigation |
268
+ | `video` | VideoRenderer | YouTube/Vimeo/direct URL, auto-detect provider |
269
+ | `iframe` | IframeRenderer | Tiered sandbox, 80+ whitelisted domains |
270
+ | `image` | ImageRenderer | Responsive with lazy loading |
271
+ | `link` | LinkRenderer | Styled link cards |
272
+ | `action` | ActionRenderer | Tool call buttons |
273
+ | `action-group` | ActionGroupRenderer | Grouped actions with layout options |
274
+ | `grid` | GridRenderer | Nested 12-column CSS Grid |
275
+ | `carousel` | CarouselRenderer | Content carousel |
276
+ | `artifact` | ArtifactRenderer | File download/preview |
277
+ | `footer` | FooterRenderer | Metadata display |
278
+
279
+ All wrapped with `ExpandableWrapper` (fullscreen expand via DOM reparenting) where applicable.
280
+
281
+ ## Iframe Security
282
+
283
+ Tiered sandbox system — trusted domains get `allow-same-origin`, untrusted get restrictive sandbox:
313
284
 
314
285
  ```typescript
315
- interface GridPosition {
316
- x: number // Column start (0-11)
317
- y: number // Row start
318
- width: number // Columns span (1-12)
319
- height: number // Rows span
320
- }
286
+ import { getIframeSandbox, DEFAULT_IFRAME_DOMAINS, TRUSTED_IFRAME_DOMAINS } from '@seed-ship/mcp-ui-solid'
321
287
 
322
- // Example: Full-width header
323
- { x: 0, y: 0, width: 12, height: 1 }
288
+ // Automatic IframeRenderer uses getIframeSandbox() internally
289
+ // Manual usage:
290
+ const sandbox = getIframeSandbox('https://docs.google.com/spreadsheets/...')
291
+ // → "allow-scripts allow-popups allow-same-origin allow-forms" (trusted)
324
292
 
325
- // Example: Two columns
326
- { x: 0, y: 1, width: 6, height: 2 } // Left half
327
- { x: 6, y: 1, width: 6, height: 2 } // Right half
293
+ const sandbox2 = getIframeSandbox('https://quickchart.io/chart?...')
294
+ // "allow-scripts allow-popups" (untrusted no same-origin)
328
295
  ```
329
296
 
330
- ## Advanced Usage
297
+ **80+ whitelisted domains** including: Google services, YouTube, Vimeo, GitHub, Figma, Notion, Stripe, Polar.sh, HubSpot, data.gouv.fr, and more.
331
298
 
332
- ### Nested Grid Layouts (NEW v1.2.0)
299
+ ## Validation
333
300
 
334
- Use `GridRenderer` for complex dashboard layouts:
301
+ All 19 component types validated, including:
302
+ - **Chart**: scatter/bubble (no labels required), time-series `{x,y}`, data type validation
303
+ - **Table**: columns + rows structure
304
+ - **Video**: URL + domain whitelist
305
+ - **Form/Carousel/Gallery/ActionGroup**: non-empty arrays
306
+ - **Code/Map/Artifact**: required content
335
307
 
336
308
  ```typescript
337
- const dashboardLayout = {
338
- id: 'dashboard',
339
- type: 'grid',
340
- params: {
341
- columns: 12,
342
- gap: '1rem',
343
- areas: [
344
- ['header', 'header', 'header'],
345
- ['sidebar', 'main', 'main'],
346
- ['footer', 'footer', 'footer']
347
- ],
348
- children: [
349
- { id: 'nav', type: 'text', params: { content: 'Navigation' }, position: { colStart: 1, colSpan: 3 } },
350
- { id: 'content', type: 'chart', params: { /* ... */ }, position: { colStart: 4, colSpan: 9 } }
351
- ]
352
- }
353
- }
354
- ```
355
-
356
- ### Auto-Footer (NEW v1.2.0)
357
-
358
- Footers are automatically injected when `layout.metadata` contains execution info:
309
+ import { validateComponent, validateLayout } from '@seed-ship/mcp-ui-solid'
359
310
 
360
- ```typescript
361
- const layout = {
362
- id: 'report',
363
- components: [/* ... */],
364
- metadata: {
365
- executionTime: 1234, // Shows "1234ms"
366
- sourceCount: 5, // Shows "5 sources"
367
- llmModel: 'gpt-4', // Shows model name
368
- // hideFooter: true // Opt-out of auto-footer
369
- }
370
- }
371
- // Footer automatically added showing "Powered by Deposium | 1234ms | gpt-4 | 5 sources"
311
+ const result = validateComponent(component)
312
+ if (!result.valid) console.error(result.errors)
372
313
  ```
373
314
 
374
- ### Custom Component Registry
315
+ ## SSR Compatibility
375
316
 
376
- ```typescript
377
- import { registerComponent } from '@seed-ship/mcp-ui-solid'
317
+ Fully SSR-compatible with SolidStart, Astro, etc. Add to `app.config.ts`:
378
318
 
379
- // Register a custom renderer
380
- registerComponent('custom-widget', (props) => {
381
- return <div class="custom-widget">{props.content}</div>
319
+ ```typescript
320
+ export default defineConfig({
321
+ vite: { resolve: { conditions: ['solid', 'development', 'browser'] } }
382
322
  })
383
323
  ```
384
324
 
385
- ### Error Handling
386
-
387
- ```tsx
388
- import { GenerativeUIErrorBoundary } from '@seed-ship/mcp-ui-solid'
325
+ ## Exports
389
326
 
390
- <GenerativeUIErrorBoundary
391
- fallback={(error, reset) => (
392
- <div>
393
- <p>Something went wrong: {error.message}</p>
394
- <button onClick={reset}>Try Again</button>
395
- </div>
396
- )}
397
- onError={(error) => {
398
- // Log to error tracking service
399
- Sentry.captureException(error)
400
- }}
401
- >
402
- <UIResourceRenderer content={layout} />
403
- </GenerativeUIErrorBoundary>
404
- ```
327
+ ```typescript
328
+ // Components
329
+ import {
330
+ UIResourceRenderer, StreamingUIRenderer, GenerativeUIErrorBoundary,
331
+ ExpandableWrapper, ComponentToolbar, ChatPrompt,
332
+ } from '@seed-ship/mcp-ui-solid'
405
333
 
406
- ### Streaming with Custom SSE Endpoint
334
+ // Chat Bus
335
+ import {
336
+ ChatBusProvider, useChatBus,
337
+ createChatBus, createEventEmitter, createCommandHandler,
338
+ } from '@seed-ship/mcp-ui-solid'
407
339
 
408
- ```tsx
409
- import { useStreamingUI } from '@seed-ship/mcp-ui-solid/hooks'
410
-
411
- function CustomStreaming() {
412
- const { components, isComplete } = useStreamingUI({
413
- endpoint: '/api/custom-mcp/stream',
414
- query: 'Generate report',
415
- headers: {
416
- 'Authorization': 'Bearer token'
417
- }
418
- })
340
+ // Validation + Security
341
+ import {
342
+ validateComponent, validateLayout, validateIframeDomain,
343
+ getIframeSandbox, DEFAULT_IFRAME_DOMAINS, TRUSTED_IFRAME_DOMAINS,
344
+ ComponentRegistry,
345
+ } from '@seed-ship/mcp-ui-solid'
419
346
 
420
- return (
421
- <div>
422
- {components().map(comp => (
423
- <UIResourceRenderer content={comp} />
424
- ))}
425
- </div>
426
- )
427
- }
347
+ // Types
348
+ import type {
349
+ ChatBus, ChatEvents, ChatCommands,
350
+ ChatPromptConfig, ChatPromptResponse,
351
+ AgentContext, BriefingEvent,
352
+ UIComponent, UILayout, ComponentType,
353
+ } from '@seed-ship/mcp-ui-solid'
428
354
  ```
429
355
 
430
356
  ## Related Packages
431
357
 
432
358
  | Package | Description |
433
359
  |---------|-------------|
434
- | [`@seed-ship/mcp-ui-spec`](../mcp-ui-spec) | JSON schemas and Zod validators |
435
- | [`@seed-ship/mcp-ui-cli`](../mcp-ui-cli) | CLI for validation and type generation |
436
-
437
- ## Versioning
438
-
439
- This package follows [Semantic Versioning](https://semver.org/). See [CHANGELOG.md](./CHANGELOG.md) for release notes.
440
-
441
- **Current Version:** 2.0.1
360
+ | [`@seed-ship/mcp-ui-spec`](https://www.npmjs.com/package/@seed-ship/mcp-ui-spec) | Zod schemas and JSON Schema definitions |
361
+ | [`@seed-ship/mcp-ui-cli`](https://www.npmjs.com/package/@seed-ship/mcp-ui-cli) | CLI: validate, generate-types, test-examples |
442
362
 
443
363
  ## License
444
364
 
445
- MIT
365
+ MIT — **Built by [The Seed Ship](https://github.com/theseedship)**