@seed-ship/mcp-ui-solid 2.4.0 → 2.5.1
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 +253 -333
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,67 +1,20 @@
|
|
|
1
1
|
# @seed-ship/mcp-ui-solid
|
|
2
2
|
|
|
3
|
-
SolidJS components for
|
|
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
|
[](https://www.npmjs.com/package/@seed-ship/mcp-ui-solid)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
-
## What's New in v2.
|
|
8
|
+
## What's New in v2.5.0
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
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
|
|
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
|
|
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: '
|
|
44
|
+
title: 'Revenue',
|
|
92
45
|
value: '$125,430',
|
|
93
|
-
|
|
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: '
|
|
99
|
-
|
|
100
|
-
|
|
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('
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
###
|
|
100
|
+
### Usage
|
|
218
101
|
|
|
219
|
-
```
|
|
220
|
-
import {
|
|
102
|
+
```tsx
|
|
103
|
+
import { ChatBusProvider, useChatBus, ChatPrompt, createChatBus } from '@seed-ship/mcp-ui-solid'
|
|
221
104
|
|
|
222
|
-
// Wrap your app
|
|
105
|
+
// 1. Wrap your app
|
|
223
106
|
function App() {
|
|
224
107
|
return (
|
|
225
|
-
<
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
//
|
|
237
|
-
function
|
|
238
|
-
const
|
|
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
|
-
<
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
|
|
258
|
-
if (!result.valid) {
|
|
259
|
-
console.error(result.errors)
|
|
165
|
+
return null
|
|
260
166
|
}
|
|
261
167
|
```
|
|
262
168
|
|
|
263
|
-
### Types
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
-
//
|
|
280
|
-
|
|
208
|
+
// Throttle hot-path events (recommended for onToken)
|
|
209
|
+
bus.events.on('onToken', handler, { throttle: 100 })
|
|
281
210
|
|
|
282
|
-
|
|
283
|
-
|
|
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
|
-
|
|
215
|
+
## ChatPrompt — Structured Interactions (`@experimental`)
|
|
292
216
|
|
|
293
|
-
|
|
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
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
##
|
|
311
|
-
|
|
312
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
323
|
-
|
|
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
|
-
|
|
326
|
-
|
|
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
|
-
|
|
297
|
+
**80+ whitelisted domains** including: Google services, YouTube, Vimeo, GitHub, Figma, Notion, Stripe, Polar.sh, HubSpot, data.gouv.fr, and more.
|
|
331
298
|
|
|
332
|
-
|
|
299
|
+
## Validation
|
|
333
300
|
|
|
334
|
-
|
|
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
|
-
|
|
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
|
-
|
|
361
|
-
|
|
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
|
-
|
|
315
|
+
## SSR Compatibility
|
|
375
316
|
|
|
376
|
-
|
|
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
|
-
|
|
380
|
-
|
|
381
|
-
|
|
319
|
+
```typescript
|
|
320
|
+
export default defineConfig({
|
|
321
|
+
vite: { resolve: { conditions: ['solid', 'development', 'browser'] } }
|
|
382
322
|
})
|
|
383
323
|
```
|
|
384
324
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
```tsx
|
|
388
|
-
import { GenerativeUIErrorBoundary } from '@seed-ship/mcp-ui-solid'
|
|
325
|
+
## Exports
|
|
389
326
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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
|
-
|
|
334
|
+
// Chat Bus
|
|
335
|
+
import {
|
|
336
|
+
ChatBusProvider, useChatBus,
|
|
337
|
+
createChatBus, createEventEmitter, createCommandHandler,
|
|
338
|
+
} from '@seed-ship/mcp-ui-solid'
|
|
407
339
|
|
|
408
|
-
|
|
409
|
-
import {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
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
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
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`](
|
|
435
|
-
| [`@seed-ship/mcp-ui-cli`](
|
|
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)**
|