@webmcp-auto-ui/sdk 2.5.31 → 2.5.33
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 +44 -107
- 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,68 +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 {
|
|
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
|
-
// Create a placeholder primary with a stable name derived from the URL.
|
|
238
|
-
const nm = url ? new URL(url, 'http://local').host || url : 'primary';
|
|
239
|
-
_servers = [..._servers, {
|
|
240
|
-
name: nm, url: url ?? '', kind: 'data', enabled: true, connected: false, primary: true,
|
|
241
|
-
}];
|
|
242
|
-
return _servers[_servers.length - 1]!;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function setMcpUrl(u: string): void {
|
|
246
|
-
// Update the primary server's URL (create one if none).
|
|
247
|
-
ensurePrimary(u);
|
|
248
|
-
notify();
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
function setMcpConnecting(connecting: boolean): void {
|
|
252
|
-
const p = ensurePrimary();
|
|
253
|
-
_servers = _servers.map((s) => s.name === p.name ? { ...s, connecting } : s);
|
|
254
|
-
notify();
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function setMcpConnected(connected: boolean, name?: string, tools?: McpToolInfo[]): void {
|
|
258
|
-
if (!connected) {
|
|
259
|
-
// Disconnect all — agent-level disconnect affects the primary and
|
|
260
|
-
// traditionally cleared the flat tools. Mirror that by disconnecting
|
|
261
|
-
// all primary-flagged servers.
|
|
262
|
-
const p = primaryServer();
|
|
263
|
-
if (p) {
|
|
264
|
-
_servers = _servers.map((s) => s.primary
|
|
265
|
-
? { ...s, connected: false, connecting: false, tools: [], error: undefined }
|
|
266
|
-
: s);
|
|
267
|
-
}
|
|
268
|
-
notify();
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
const p = ensurePrimary();
|
|
272
|
-
const newName = name && name.length > 0 ? name : p.name;
|
|
273
|
-
_servers = _servers.map((s) => s.name === p.name
|
|
274
|
-
? { ...s, name: newName, connected: true, connecting: false, tools: tools ?? s.tools ?? [], error: undefined }
|
|
275
|
-
: s);
|
|
276
|
-
notify();
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
function setMcpError(err: string): void {
|
|
280
|
-
const p = ensurePrimary();
|
|
281
|
-
_servers = _servers.map((s) => s.name === p.name
|
|
282
|
-
? { ...s, connected: false, connecting: false, error: err }
|
|
283
|
-
: s);
|
|
284
|
-
notify();
|
|
285
|
-
}
|
|
286
|
-
|
|
287
223
|
// ── Widget actions ─────────────────────────────────────────────────────
|
|
288
224
|
function addWidget(type: WidgetType, data: Record<string, unknown> = {}): Widget {
|
|
289
225
|
const widget: Widget = { id: uuid(), type, data };
|
|
@@ -340,12 +276,12 @@ function createCanvasVanilla() {
|
|
|
340
276
|
|
|
341
277
|
// ── HyperSkill ─────────────────────────────────────────────────────────
|
|
342
278
|
function buildSkillJSON() {
|
|
343
|
-
const
|
|
279
|
+
const first = connectedServers()[0] ?? _servers[0];
|
|
344
280
|
const skill: Record<string, unknown> = {
|
|
345
281
|
version: '1.0',
|
|
346
282
|
name: 'skill-' + Date.now(),
|
|
347
283
|
created: new Date().toISOString(),
|
|
348
|
-
mcp:
|
|
284
|
+
mcp: first?.url ?? '',
|
|
349
285
|
llm: _llm,
|
|
350
286
|
blocks: _blocks.map((b) => ({ type: b.type, data: JSON.parse(JSON.stringify(b.data)) })),
|
|
351
287
|
};
|
|
@@ -368,7 +304,7 @@ function createCanvasVanilla() {
|
|
|
368
304
|
servers?: string[];
|
|
369
305
|
blocks?: { type: WidgetType; data: Record<string, unknown> }[];
|
|
370
306
|
}) {
|
|
371
|
-
if (skill.mcp)
|
|
307
|
+
if (skill.mcp) addMcpServer(skill.mcp);
|
|
372
308
|
if (skill.llm) _llm = skill.llm;
|
|
373
309
|
if (skill.theme) _themeOverrides = skill.theme;
|
|
374
310
|
if (skill.servers) _enabledServerIds = skill.servers;
|
|
@@ -401,7 +337,7 @@ function createCanvasVanilla() {
|
|
|
401
337
|
content?: { blocks?: { type: WidgetType; data: Record<string, unknown> }[] };
|
|
402
338
|
servers?: string[];
|
|
403
339
|
};
|
|
404
|
-
if (decoded.meta?.mcp)
|
|
340
|
+
if (decoded.meta?.mcp) addMcpServer(decoded.meta.mcp as string);
|
|
405
341
|
if (decoded.meta?.llm) _llm = decoded.meta.llm as LLMId;
|
|
406
342
|
if (decoded.meta?.theme) _themeOverrides = decoded.meta.theme as Record<string, string>;
|
|
407
343
|
const servers = (decoded.servers ?? (decoded.meta?.servers as string[] | undefined));
|
|
@@ -416,15 +352,15 @@ function createCanvasVanilla() {
|
|
|
416
352
|
|
|
417
353
|
// ── Snapshot (fields kept for API stability) ───────────────────────────
|
|
418
354
|
function getSnapshot(): CanvasSnapshot {
|
|
419
|
-
const
|
|
355
|
+
const first = connectedServers()[0] ?? _servers[0];
|
|
420
356
|
return {
|
|
421
357
|
blocks: _blocks,
|
|
422
358
|
mode: _mode,
|
|
423
359
|
llm: _llm,
|
|
424
|
-
mcpUrl:
|
|
425
|
-
mcpConnected:
|
|
360
|
+
mcpUrl: first?.url ?? '',
|
|
361
|
+
mcpConnected: _servers.some((s) => s.connected),
|
|
426
362
|
mcpConnecting: anyConnecting(),
|
|
427
|
-
mcpName:
|
|
363
|
+
mcpName: first && first.connected ? aliasName(first.serverName ?? first.name) : '',
|
|
428
364
|
mcpTools: unionTools(),
|
|
429
365
|
messages: _messages,
|
|
430
366
|
generating: _generating,
|
|
@@ -450,11 +386,14 @@ function createCanvasVanilla() {
|
|
|
450
386
|
set mode(v: Mode) { _mode = v; notify(); },
|
|
451
387
|
get llm() { return _llm; },
|
|
452
388
|
set llm(v: LLMId) { _llm = v; notify(); },
|
|
453
|
-
get mcpUrl() { return
|
|
454
|
-
set mcpUrl(v: string) {
|
|
455
|
-
get mcpConnected() { return
|
|
389
|
+
get mcpUrl() { return (connectedServers()[0] ?? _servers[0])?.url ?? ''; },
|
|
390
|
+
set mcpUrl(v: string) { addMcpServer(v); },
|
|
391
|
+
get mcpConnected() { return _servers.some((s) => s.connected); },
|
|
456
392
|
get mcpConnecting() { return anyConnecting(); },
|
|
457
|
-
get mcpName() {
|
|
393
|
+
get mcpName() {
|
|
394
|
+
const s = connectedServers()[0];
|
|
395
|
+
return s ? aliasName(s.serverName ?? s.name) : '';
|
|
396
|
+
},
|
|
458
397
|
get mcpTools() { return unionTools(); },
|
|
459
398
|
get messages() { return _messages; },
|
|
460
399
|
get generating() { return _generating; },
|
|
@@ -466,15 +405,12 @@ function createCanvasVanilla() {
|
|
|
466
405
|
|
|
467
406
|
setMode(m: Mode) { _mode = m; notify(); },
|
|
468
407
|
setLlm(l: LLMId) { _llm = l; notify(); },
|
|
469
|
-
setMcpUrl,
|
|
470
408
|
setGenerating(g: boolean) { _generating = g; notify(); },
|
|
471
409
|
|
|
472
410
|
addWidget, addBlock,
|
|
473
411
|
removeBlock, updateBlock, moveBlock, clearBlocks, setBlocks,
|
|
474
412
|
addMsg, updateMsg, clearMessages,
|
|
475
413
|
|
|
476
|
-
setMcpConnecting, setMcpConnected, setMcpError,
|
|
477
|
-
|
|
478
414
|
get themeOverrides() { return _themeOverrides; },
|
|
479
415
|
setThemeOverrides,
|
|
480
416
|
|
|
@@ -486,6 +422,7 @@ function createCanvasVanilla() {
|
|
|
486
422
|
get dataServers() { return _servers; },
|
|
487
423
|
set dataServers(v: DataServer[]) { _servers = Array.isArray(v) ? v : []; notify(); },
|
|
488
424
|
addDataServer,
|
|
425
|
+
addMcpServer,
|
|
489
426
|
removeDataServer,
|
|
490
427
|
getDataServer,
|
|
491
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