@webmcp-auto-ui/sdk 2.5.32 → 2.5.34
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/package.json +1 -1
- package/src/index.ts +3 -0
- package/src/stores/canvas.svelte.ts +1 -6
- package/src/stores/canvas.ts +43 -112
- package/src/widget-sample-data.ts +47 -0
- package/tsconfig.json +1 -2
- package/src/Index.svelte +0 -5
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -96,3 +96,6 @@ export type { ParsedSegment, RunResult, RunLog, RunTab, RecipeData } from './rec
|
|
|
96
96
|
|
|
97
97
|
// Short URL — domain-dependent compact token
|
|
98
98
|
export { buildShortUrl, getShortToken } from './short-url.js';
|
|
99
|
+
|
|
100
|
+
// Widget sample-data extractor — parses the `## Example` block of a recipe
|
|
101
|
+
export { extractSampleData } from './widget-sample-data.js';
|
|
@@ -80,7 +80,6 @@ function createCanvas() {
|
|
|
80
80
|
// Setters (kept for backward compat)
|
|
81
81
|
setMode(m: Mode) { canvasVanilla.setMode(m); },
|
|
82
82
|
setLlm(l: LLMId) { canvasVanilla.setLlm(l); },
|
|
83
|
-
setMcpUrl(u: string) { canvasVanilla.setMcpUrl(u); },
|
|
84
83
|
setGenerating(g: boolean) { canvasVanilla.setGenerating(g); },
|
|
85
84
|
|
|
86
85
|
// Widget actions (primary name)
|
|
@@ -101,11 +100,6 @@ function createCanvas() {
|
|
|
101
100
|
updateMsg: canvasVanilla.updateMsg.bind(canvasVanilla),
|
|
102
101
|
clearMessages: canvasVanilla.clearMessages.bind(canvasVanilla),
|
|
103
102
|
|
|
104
|
-
// MCP
|
|
105
|
-
setMcpConnecting: canvasVanilla.setMcpConnecting.bind(canvasVanilla),
|
|
106
|
-
setMcpConnected: canvasVanilla.setMcpConnected.bind(canvasVanilla),
|
|
107
|
-
setMcpError: canvasVanilla.setMcpError.bind(canvasVanilla),
|
|
108
|
-
|
|
109
103
|
// Theme
|
|
110
104
|
get themeOverrides() { return themeOverrides; },
|
|
111
105
|
setThemeOverrides: canvasVanilla.setThemeOverrides.bind(canvasVanilla),
|
|
@@ -118,6 +112,7 @@ function createCanvas() {
|
|
|
118
112
|
get dataServers() { return dataServers; },
|
|
119
113
|
set dataServers(v: DataServer[]) { canvasVanilla.dataServers = v; },
|
|
120
114
|
addDataServer: canvasVanilla.addDataServer.bind(canvasVanilla),
|
|
115
|
+
addMcpServer: canvasVanilla.addMcpServer.bind(canvasVanilla),
|
|
121
116
|
removeDataServer: canvasVanilla.removeDataServer.bind(canvasVanilla),
|
|
122
117
|
getDataServer: canvasVanilla.getDataServer.bind(canvasVanilla),
|
|
123
118
|
setDataServerMeta: canvasVanilla.setDataServerMeta.bind(canvasVanilla),
|
package/src/stores/canvas.ts
CHANGED
|
@@ -10,17 +10,15 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Historically this store had TWO parallel surfaces for MCP servers:
|
|
12
12
|
* - `mcpUrl` / `mcpName` / `mcpConnected` / `mcpConnecting` / `mcpTools`
|
|
13
|
-
* (flat, single-server or comma-joined multi)
|
|
13
|
+
* (flat, single-server or comma-joined multi) driven by legacy
|
|
14
|
+
* `setMcpConnected`/`setMcpConnecting`/`setMcpError` setters.
|
|
14
15
|
* - `dataServers: DataServer[]` (list, managed by MultiMcpBridge)
|
|
15
16
|
*
|
|
16
|
-
* They were
|
|
17
|
-
*
|
|
18
|
-
* `
|
|
19
|
-
*
|
|
20
|
-
* server
|
|
21
|
-
*
|
|
22
|
-
* The public API shape is preserved (both `mcp*` and `dataServers` / `addDataServer`)
|
|
23
|
-
* so existing apps keep working without modification.
|
|
17
|
+
* They were the same concept. The legacy setters and the `primary` flag have
|
|
18
|
+
* been removed; writes go through `addDataServer` / `setDataServerMeta` /
|
|
19
|
+
* `setDataServerEnabled` / `addMcpServer` (url shorthand) exclusively. Flat
|
|
20
|
+
* getters (`mcpConnected`, `mcpName`, `mcpUrl`, `mcpConnecting`, `mcpTools`)
|
|
21
|
+
* remain as read-only derivations over the server list for UI back-compat.
|
|
24
22
|
*/
|
|
25
23
|
|
|
26
24
|
import { encode, decode } from '../hyperskills.js';
|
|
@@ -62,9 +60,8 @@ export interface McpToolInfo {
|
|
|
62
60
|
|
|
63
61
|
/**
|
|
64
62
|
* Single MCP server entry — the one true shape.
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* non-primary but otherwise identical.
|
|
63
|
+
* All servers are equal; there is no "primary" concept. The MultiMcpBridge
|
|
64
|
+
* singleton reconciles connection state across all entries.
|
|
68
65
|
*/
|
|
69
66
|
export interface DataServer {
|
|
70
67
|
name: string; // user-chosen label
|
|
@@ -73,10 +70,12 @@ export interface DataServer {
|
|
|
73
70
|
enabled: boolean; // user intent
|
|
74
71
|
connected: boolean; // handshake completed
|
|
75
72
|
connecting?: boolean;
|
|
76
|
-
primary?: boolean; // agent MCP when true
|
|
77
73
|
tools?: McpToolInfo[];
|
|
78
74
|
recipes?: { name: string; description?: string; body?: string }[];
|
|
79
75
|
error?: string;
|
|
76
|
+
/** Real server name from MCP handshake (initResult.serverInfo.name, aliased).
|
|
77
|
+
* Canvas `.name` is URL-host; `.serverName` is what to display. */
|
|
78
|
+
serverName?: string;
|
|
80
79
|
}
|
|
81
80
|
|
|
82
81
|
export interface CanvasSnapshot {
|
|
@@ -124,9 +123,6 @@ function createCanvasVanilla() {
|
|
|
124
123
|
let _servers: DataServer[] = [];
|
|
125
124
|
|
|
126
125
|
// ── Helpers over _servers ──────────────────────────────────────────────
|
|
127
|
-
function primaryServer(): DataServer | undefined {
|
|
128
|
-
return _servers.find((s) => s.primary);
|
|
129
|
-
}
|
|
130
126
|
function connectedServers(): DataServer[] {
|
|
131
127
|
return _servers.filter((s) => s.connected);
|
|
132
128
|
}
|
|
@@ -162,33 +158,35 @@ function createCanvasVanilla() {
|
|
|
162
158
|
}
|
|
163
159
|
|
|
164
160
|
// ── Server list actions (public, stable) ───────────────────────────────
|
|
165
|
-
function addDataServer(desc: { name: string; url: string
|
|
161
|
+
function addDataServer(desc: { name: string; url: string }): DataServer {
|
|
166
162
|
const existing = _servers.find((s) => s.name === desc.name);
|
|
167
|
-
if (existing)
|
|
168
|
-
if (desc.primary && !existing.primary) {
|
|
169
|
-
// Promote: there's only one primary. Demote others.
|
|
170
|
-
_servers = _servers.map((s) => ({ ...s, primary: s.name === desc.name }));
|
|
171
|
-
notify();
|
|
172
|
-
}
|
|
173
|
-
return existing;
|
|
174
|
-
}
|
|
163
|
+
if (existing) return existing;
|
|
175
164
|
const srv: DataServer = {
|
|
176
165
|
name: desc.name,
|
|
177
166
|
url: desc.url,
|
|
178
167
|
kind: 'data',
|
|
179
168
|
enabled: true,
|
|
180
169
|
connected: false,
|
|
181
|
-
primary: !!desc.primary,
|
|
182
170
|
};
|
|
183
|
-
if (srv.primary) {
|
|
184
|
-
// Demote any existing primary before inserting.
|
|
185
|
-
_servers = _servers.map((s) => ({ ...s, primary: false }));
|
|
186
|
-
}
|
|
187
171
|
_servers = [..._servers, srv];
|
|
188
172
|
notify();
|
|
189
173
|
return srv;
|
|
190
174
|
}
|
|
191
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Add a server by URL alone (derives name from the URL host). Returns the
|
|
178
|
+
* canvas name so callers can reference it later.
|
|
179
|
+
*/
|
|
180
|
+
function addMcpServer(url: string): string {
|
|
181
|
+
if (!url) return '';
|
|
182
|
+
let name: string;
|
|
183
|
+
try { name = new URL(url, 'http://local').host || url; }
|
|
184
|
+
catch { name = url; }
|
|
185
|
+
addDataServer({ name, url });
|
|
186
|
+
setDataServerEnabled(name, true);
|
|
187
|
+
return name;
|
|
188
|
+
}
|
|
189
|
+
|
|
192
190
|
function removeDataServer(name: string): boolean {
|
|
193
191
|
const before = _servers.length;
|
|
194
192
|
_servers = _servers.filter((s) => s.name !== name);
|
|
@@ -222,74 +220,6 @@ function createCanvasVanilla() {
|
|
|
222
220
|
return setDataServerEnabled(name, !s.enabled);
|
|
223
221
|
}
|
|
224
222
|
|
|
225
|
-
// ── Agent-MCP compatibility layer ──────────────────────────────────────
|
|
226
|
-
// All these mutate the SAME _servers list; `mcpUrl` targets the primary
|
|
227
|
-
// entry, creating one if none exists. Apps can equivalently call
|
|
228
|
-
// addDataServer({primary: true}) + setDataServerMeta(name, ...) directly.
|
|
229
|
-
function ensurePrimary(url?: string): DataServer | undefined {
|
|
230
|
-
let p = primaryServer();
|
|
231
|
-
if (p) {
|
|
232
|
-
if (url && p.url !== url) {
|
|
233
|
-
_servers = _servers.map((s) => s.name === p!.name ? { ...s, url } : s);
|
|
234
|
-
}
|
|
235
|
-
return _servers.find((s) => s.primary)!;
|
|
236
|
-
}
|
|
237
|
-
// Refuse to create a placeholder primary without a real URL — empty-URL
|
|
238
|
-
// ghosts caused MultiMcpBridge to POST to the current page origin, yielding
|
|
239
|
-
// a 405 storm (SvelteKit treats POST / as a form action).
|
|
240
|
-
if (!url) return undefined;
|
|
241
|
-
const nm = new URL(url, 'http://local').host || url;
|
|
242
|
-
_servers = [..._servers, {
|
|
243
|
-
name: nm, url, kind: 'data', enabled: true, connected: false, primary: true,
|
|
244
|
-
}];
|
|
245
|
-
return _servers[_servers.length - 1]!;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function setMcpUrl(u: string): void {
|
|
249
|
-
// Update the primary server's URL (create one if none).
|
|
250
|
-
ensurePrimary(u);
|
|
251
|
-
notify();
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function setMcpConnecting(connecting: boolean): void {
|
|
255
|
-
const p = ensurePrimary();
|
|
256
|
-
if (!p) return;
|
|
257
|
-
_servers = _servers.map((s) => s.name === p.name ? { ...s, connecting } : s);
|
|
258
|
-
notify();
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
function setMcpConnected(connected: boolean, name?: string, tools?: McpToolInfo[]): void {
|
|
262
|
-
if (!connected) {
|
|
263
|
-
// Disconnect all — agent-level disconnect affects the primary and
|
|
264
|
-
// traditionally cleared the flat tools. Mirror that by disconnecting
|
|
265
|
-
// all primary-flagged servers.
|
|
266
|
-
const p = primaryServer();
|
|
267
|
-
if (p) {
|
|
268
|
-
_servers = _servers.map((s) => s.primary
|
|
269
|
-
? { ...s, connected: false, connecting: false, tools: [], error: undefined }
|
|
270
|
-
: s);
|
|
271
|
-
}
|
|
272
|
-
notify();
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
const p = ensurePrimary();
|
|
276
|
-
if (!p) return;
|
|
277
|
-
const newName = name && name.length > 0 ? name : p.name;
|
|
278
|
-
_servers = _servers.map((s) => s.name === p.name
|
|
279
|
-
? { ...s, name: newName, connected: true, connecting: false, tools: tools ?? s.tools ?? [], error: undefined }
|
|
280
|
-
: s);
|
|
281
|
-
notify();
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function setMcpError(err: string): void {
|
|
285
|
-
const p = ensurePrimary();
|
|
286
|
-
if (!p) return;
|
|
287
|
-
_servers = _servers.map((s) => s.name === p.name
|
|
288
|
-
? { ...s, connected: false, connecting: false, error: err }
|
|
289
|
-
: s);
|
|
290
|
-
notify();
|
|
291
|
-
}
|
|
292
|
-
|
|
293
223
|
// ── Widget actions ─────────────────────────────────────────────────────
|
|
294
224
|
function addWidget(type: WidgetType, data: Record<string, unknown> = {}): Widget {
|
|
295
225
|
const widget: Widget = { id: uuid(), type, data };
|
|
@@ -346,12 +276,12 @@ function createCanvasVanilla() {
|
|
|
346
276
|
|
|
347
277
|
// ── HyperSkill ─────────────────────────────────────────────────────────
|
|
348
278
|
function buildSkillJSON() {
|
|
349
|
-
const
|
|
279
|
+
const first = connectedServers()[0] ?? _servers[0];
|
|
350
280
|
const skill: Record<string, unknown> = {
|
|
351
281
|
version: '1.0',
|
|
352
282
|
name: 'skill-' + Date.now(),
|
|
353
283
|
created: new Date().toISOString(),
|
|
354
|
-
mcp:
|
|
284
|
+
mcp: first?.url ?? '',
|
|
355
285
|
llm: _llm,
|
|
356
286
|
blocks: _blocks.map((b) => ({ type: b.type, data: JSON.parse(JSON.stringify(b.data)) })),
|
|
357
287
|
};
|
|
@@ -374,7 +304,7 @@ function createCanvasVanilla() {
|
|
|
374
304
|
servers?: string[];
|
|
375
305
|
blocks?: { type: WidgetType; data: Record<string, unknown> }[];
|
|
376
306
|
}) {
|
|
377
|
-
if (skill.mcp)
|
|
307
|
+
if (skill.mcp) addMcpServer(skill.mcp);
|
|
378
308
|
if (skill.llm) _llm = skill.llm;
|
|
379
309
|
if (skill.theme) _themeOverrides = skill.theme;
|
|
380
310
|
if (skill.servers) _enabledServerIds = skill.servers;
|
|
@@ -407,7 +337,7 @@ function createCanvasVanilla() {
|
|
|
407
337
|
content?: { blocks?: { type: WidgetType; data: Record<string, unknown> }[] };
|
|
408
338
|
servers?: string[];
|
|
409
339
|
};
|
|
410
|
-
if (decoded.meta?.mcp)
|
|
340
|
+
if (decoded.meta?.mcp) addMcpServer(decoded.meta.mcp as string);
|
|
411
341
|
if (decoded.meta?.llm) _llm = decoded.meta.llm as LLMId;
|
|
412
342
|
if (decoded.meta?.theme) _themeOverrides = decoded.meta.theme as Record<string, string>;
|
|
413
343
|
const servers = (decoded.servers ?? (decoded.meta?.servers as string[] | undefined));
|
|
@@ -422,15 +352,15 @@ function createCanvasVanilla() {
|
|
|
422
352
|
|
|
423
353
|
// ── Snapshot (fields kept for API stability) ───────────────────────────
|
|
424
354
|
function getSnapshot(): CanvasSnapshot {
|
|
425
|
-
const
|
|
355
|
+
const first = connectedServers()[0] ?? _servers[0];
|
|
426
356
|
return {
|
|
427
357
|
blocks: _blocks,
|
|
428
358
|
mode: _mode,
|
|
429
359
|
llm: _llm,
|
|
430
|
-
mcpUrl:
|
|
431
|
-
mcpConnected:
|
|
360
|
+
mcpUrl: first?.url ?? '',
|
|
361
|
+
mcpConnected: _servers.some((s) => s.connected),
|
|
432
362
|
mcpConnecting: anyConnecting(),
|
|
433
|
-
mcpName:
|
|
363
|
+
mcpName: first && first.connected ? aliasName(first.serverName ?? first.name) : '',
|
|
434
364
|
mcpTools: unionTools(),
|
|
435
365
|
messages: _messages,
|
|
436
366
|
generating: _generating,
|
|
@@ -456,11 +386,14 @@ function createCanvasVanilla() {
|
|
|
456
386
|
set mode(v: Mode) { _mode = v; notify(); },
|
|
457
387
|
get llm() { return _llm; },
|
|
458
388
|
set llm(v: LLMId) { _llm = v; notify(); },
|
|
459
|
-
get mcpUrl() { return
|
|
460
|
-
set mcpUrl(v: string) {
|
|
389
|
+
get mcpUrl() { return (connectedServers()[0] ?? _servers[0])?.url ?? ''; },
|
|
390
|
+
set mcpUrl(v: string) { addMcpServer(v); },
|
|
461
391
|
get mcpConnected() { return _servers.some((s) => s.connected); },
|
|
462
392
|
get mcpConnecting() { return anyConnecting(); },
|
|
463
|
-
get mcpName() {
|
|
393
|
+
get mcpName() {
|
|
394
|
+
const s = connectedServers()[0];
|
|
395
|
+
return s ? aliasName(s.serverName ?? s.name) : '';
|
|
396
|
+
},
|
|
464
397
|
get mcpTools() { return unionTools(); },
|
|
465
398
|
get messages() { return _messages; },
|
|
466
399
|
get generating() { return _generating; },
|
|
@@ -472,15 +405,12 @@ function createCanvasVanilla() {
|
|
|
472
405
|
|
|
473
406
|
setMode(m: Mode) { _mode = m; notify(); },
|
|
474
407
|
setLlm(l: LLMId) { _llm = l; notify(); },
|
|
475
|
-
setMcpUrl,
|
|
476
408
|
setGenerating(g: boolean) { _generating = g; notify(); },
|
|
477
409
|
|
|
478
410
|
addWidget, addBlock,
|
|
479
411
|
removeBlock, updateBlock, moveBlock, clearBlocks, setBlocks,
|
|
480
412
|
addMsg, updateMsg, clearMessages,
|
|
481
413
|
|
|
482
|
-
setMcpConnecting, setMcpConnected, setMcpError,
|
|
483
|
-
|
|
484
414
|
get themeOverrides() { return _themeOverrides; },
|
|
485
415
|
setThemeOverrides,
|
|
486
416
|
|
|
@@ -492,6 +422,7 @@ function createCanvasVanilla() {
|
|
|
492
422
|
get dataServers() { return _servers; },
|
|
493
423
|
set dataServers(v: DataServer[]) { _servers = Array.isArray(v) ? v : []; notify(); },
|
|
494
424
|
addDataServer,
|
|
425
|
+
addMcpServer,
|
|
495
426
|
removeDataServer,
|
|
496
427
|
getDataServer,
|
|
497
428
|
setDataServerMeta,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract sample `params` data from a widget recipe's `## Example` block.
|
|
3
|
+
*
|
|
4
|
+
* Recipes have shape:
|
|
5
|
+
* ## Example
|
|
6
|
+
* ```
|
|
7
|
+
* <server>_webmcp_widget_display({name: "<widget>", params: { ... }})
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* Recipes are bundled at build time from our own source — `new Function`
|
|
11
|
+
* eval is acceptable here (not user input).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
function findBalancedBraces(text: string, fromIndex: number): string | null {
|
|
15
|
+
let depth = 0;
|
|
16
|
+
let start = -1;
|
|
17
|
+
for (let i = fromIndex; i < text.length; i++) {
|
|
18
|
+
const ch = text[i];
|
|
19
|
+
if (ch === '{') {
|
|
20
|
+
if (start === -1) start = i;
|
|
21
|
+
depth++;
|
|
22
|
+
} else if (ch === '}') {
|
|
23
|
+
depth--;
|
|
24
|
+
if (depth === 0 && start !== -1) return text.slice(start, i + 1);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function extractSampleData(recipe: string | undefined | null): Record<string, unknown> | null {
|
|
31
|
+
if (!recipe) return null;
|
|
32
|
+
const exampleHeader = recipe.search(/^##\s*Example/m);
|
|
33
|
+
if (exampleHeader === -1) return null;
|
|
34
|
+
const after = recipe.slice(exampleHeader);
|
|
35
|
+
const paramsIdx = after.search(/params\s*:/);
|
|
36
|
+
if (paramsIdx === -1) return null;
|
|
37
|
+
const objStart = after.indexOf('{', paramsIdx);
|
|
38
|
+
if (objStart === -1) return null;
|
|
39
|
+
const objLiteral = findBalancedBraces(after, objStart);
|
|
40
|
+
if (!objLiteral) return null;
|
|
41
|
+
try {
|
|
42
|
+
const fn = new Function(`return (${objLiteral});`);
|
|
43
|
+
const value = fn();
|
|
44
|
+
if (value && typeof value === 'object') return value as Record<string, unknown>;
|
|
45
|
+
} catch { /* parse error — fall through */ }
|
|
46
|
+
return null;
|
|
47
|
+
}
|
package/tsconfig.json
CHANGED