@webmcp-auto-ui/sdk 2.5.27 → 2.5.28

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webmcp-auto-ui/sdk",
3
- "version": "2.5.27",
3
+ "version": "2.5.28",
4
4
  "description": "Skills CRUD, HyperSkill format, Svelte 5 stores",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "type": "module",
@@ -1,3 +1,3 @@
1
1
  // Canvas store — Vanilla (framework-agnostic), no Svelte dependency
2
2
  export { canvasVanilla } from './stores/canvas.js';
3
- export type { Widget, WidgetType, Block, BlockType, Mode, LLMId, ChatMsg, McpToolInfo, CanvasSnapshot } from './stores/canvas.js';
3
+ export type { Widget, WidgetType, Block, BlockType, Mode, LLMId, ChatMsg, McpToolInfo, DataServer, CanvasSnapshot } from './stores/canvas.js';
package/src/canvas.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  // Canvas store — Svelte 5 runes (browser-only, must be imported in Svelte components)
2
2
  export { canvas } from './stores/canvas.svelte.js';
3
- export type { Widget, WidgetType, Block, BlockType, Mode, LLMId, ChatMsg, McpToolInfo } from './stores/canvas.svelte.js';
3
+ export type { Widget, WidgetType, Block, BlockType, Mode, LLMId, ChatMsg, McpToolInfo, DataServer } from './stores/canvas.svelte.js';
@@ -32,7 +32,10 @@ async function compressGzip(bytes: Uint8Array): Promise<Uint8Array> {
32
32
  // @ts-ignore — CompressionStream is part of the DOM lib but may be missing in older TS targets
33
33
  const cs = new CompressionStream('gzip');
34
34
  const writer = cs.writable.getWriter();
35
- writer.write(bytes);
35
+ // Copy into a fresh Uint8Array to guarantee the underlying buffer is a plain
36
+ // ArrayBuffer (not SharedArrayBuffer/ArrayBufferLike) — required by
37
+ // WritableStreamDefaultWriter<BufferSource> under TS 5.x strict lib.
38
+ writer.write(new Uint8Array(bytes));
36
39
  writer.close();
37
40
  return new Uint8Array(await new Response(cs.readable).arrayBuffer());
38
41
  }
@@ -87,6 +90,7 @@ export function getHsParam(url?: string): string | null {
87
90
  return null;
88
91
  }
89
92
  }
93
+ if (typeof window === 'undefined') return null;
90
94
  return hs.getHsParam();
91
95
  }
92
96
 
package/src/index.ts CHANGED
@@ -44,9 +44,7 @@ import { encode, decode, hash, diff } from './hyperskills.js';
44
44
  export async function encodeHyperSkill(skill: HyperSkill, sourceUrl?: string): Promise<string> {
45
45
  const base = sourceUrl ?? (typeof window !== 'undefined' ? window.location.href.split('?')[0] : 'https://example.com');
46
46
  const json = JSON.stringify(skill);
47
- // Auto-compress with gzip when payload exceeds 6 KB to keep URLs under nginx limits
48
- const compress = json.length > 6144 ? 'gz' as const : undefined;
49
- return encode(base, json, compress ? { compress } : {});
47
+ return encode(base, json, { compress: 'gz' });
50
48
  }
51
49
 
52
50
  export async function decodeHyperSkill(urlOrParam: string): Promise<HyperSkill> {
@@ -94,3 +92,6 @@ export type { McpDemoServer } from './mcp-demo-servers.js';
94
92
  // Recipe runner — markdown-fence parser + JS/TS/SQL/etc executor over MCP
95
93
  export { parseBody, runCode, estimateTokens, safeStringify } from './recipes/index.js';
96
94
  export type { ParsedSegment, RunResult, RunLog, RunTab, RecipeData } from './recipes/index.js';
95
+
96
+ // Short URL — domain-dependent compact token
97
+ export { buildShortUrl, getShortToken } from './short-url.js';
@@ -0,0 +1,33 @@
1
+ // Domain-dependent short URL — compact token served from the skill's own domain.
2
+ // Not a dedicated subdomain: the skill host resolves `?n=<token>` to the full state.
3
+
4
+ import { hash } from './hyperskills.js';
5
+
6
+ /**
7
+ * Build a short URL from a source URL and the content to share.
8
+ * The short token is a prefix of the content hash, resolved server-side.
9
+ */
10
+ export async function buildShortUrl(sourceUrl: string, content: string): Promise<string> {
11
+ const h = await hash(sourceUrl, content);
12
+ const token = h.slice(0, 10);
13
+ const u = new URL(sourceUrl);
14
+ u.search = '';
15
+ u.searchParams.set('n', token);
16
+ return u.toString();
17
+ }
18
+
19
+ /**
20
+ * Read the short token from a URL or param string. Returns null if absent.
21
+ */
22
+ export function getShortToken(urlOrParam: string): string | null {
23
+ try {
24
+ if (urlOrParam.startsWith('?') || urlOrParam.includes('=')) {
25
+ const sp = new URLSearchParams(urlOrParam.replace(/^\?/, ''));
26
+ return sp.get('n');
27
+ }
28
+ const u = new URL(urlOrParam);
29
+ return u.searchParams.get('n');
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
@@ -7,10 +7,10 @@
7
7
  */
8
8
 
9
9
  import { canvasVanilla } from './canvas.js';
10
- import type { Widget, WidgetType, Mode, LLMId, ChatMsg, McpToolInfo } from './canvas.js';
10
+ import type { Widget, WidgetType, Mode, LLMId, ChatMsg, McpToolInfo, DataServer } from './canvas.js';
11
11
 
12
12
  // Re-export types (including deprecated aliases)
13
- export type { Widget, WidgetType, Mode, LLMId, ChatMsg, McpToolInfo };
13
+ export type { Widget, WidgetType, Mode, LLMId, ChatMsg, McpToolInfo, DataServer };
14
14
  export type { Block, BlockType, CanvasSnapshot } from './canvas.js';
15
15
 
16
16
  function createCanvas() {
@@ -29,6 +29,7 @@ function createCanvas() {
29
29
  let statusColor = $state(canvasVanilla.statusColor);
30
30
  let themeOverrides = $state<Record<string, string>>(canvasVanilla.themeOverrides);
31
31
  let enabledServerIds = $state<string[]>(canvasVanilla.enabledServerIds);
32
+ let dataServers = $state<DataServer[]>(canvasVanilla.dataServers);
32
33
 
33
34
  // ── Derived ─────────────────────────────────────────────────────────────
34
35
  const blockCount = $derived(blocks.length);
@@ -51,6 +52,7 @@ function createCanvas() {
51
52
  statusColor = s.statusColor;
52
53
  themeOverrides = s.themeOverrides;
53
54
  enabledServerIds = canvasVanilla.enabledServerIds;
55
+ dataServers = canvasVanilla.dataServers;
54
56
  });
55
57
 
56
58
  // ── Return public API ───────────────────────────────────────────────────
@@ -112,6 +114,16 @@ function createCanvas() {
112
114
  get enabledServerIds() { return enabledServerIds; },
113
115
  setEnabledServers: canvasVanilla.setEnabledServers.bind(canvasVanilla),
114
116
 
117
+ // Data servers (multi-MCP) — additive, coexists with mcp* primary fields
118
+ get dataServers() { return dataServers; },
119
+ set dataServers(v: DataServer[]) { canvasVanilla.dataServers = v; },
120
+ addDataServer: canvasVanilla.addDataServer.bind(canvasVanilla),
121
+ removeDataServer: canvasVanilla.removeDataServer.bind(canvasVanilla),
122
+ getDataServer: canvasVanilla.getDataServer.bind(canvasVanilla),
123
+ setDataServerMeta: canvasVanilla.setDataServerMeta.bind(canvasVanilla),
124
+ setDataServerEnabled: canvasVanilla.setDataServerEnabled.bind(canvasVanilla),
125
+ toggleDataServer: canvasVanilla.toggleDataServer.bind(canvasVanilla),
126
+
115
127
  // HyperSkill
116
128
  buildSkillJSON: canvasVanilla.buildSkillJSON.bind(canvasVanilla),
117
129
  buildHyperskillParam: canvasVanilla.buildHyperskillParam.bind(canvasVanilla),
@@ -46,6 +46,17 @@ export interface McpToolInfo {
46
46
  inputSchema?: Record<string, unknown>;
47
47
  }
48
48
 
49
+ export interface DataServer {
50
+ name: string; // unique identifier (user-chosen label)
51
+ url: string;
52
+ kind: 'data';
53
+ enabled: boolean; // user intent; bridge only connects to enabled servers
54
+ connected: boolean; // flipped by the bridge after handshake
55
+ tools?: McpToolInfo[];
56
+ recipes?: { name: string; description?: string; body?: string }[];
57
+ error?: string; // handshake error message, if any
58
+ }
59
+
49
60
  export interface CanvasSnapshot {
50
61
  blocks: Widget[];
51
62
  mode: Mode;
@@ -61,6 +72,7 @@ export interface CanvasSnapshot {
61
72
  statusColor: string;
62
73
  themeOverrides: Record<string, string>;
63
74
  enabledServerIds: string[];
75
+ dataServers: DataServer[];
64
76
  blockCount: number;
65
77
  isEmpty: boolean;
66
78
  }
@@ -96,6 +108,50 @@ function createCanvasVanilla() {
96
108
  let _statusColor = 'text-zinc-600';
97
109
  let _themeOverrides: Record<string, string> = {};
98
110
  let _enabledServerIds: string[] = ['autoui'];
111
+ let _dataServers: DataServer[] = [];
112
+
113
+ // ── Data servers (multi-MCP) ───────────────────────────────────────────
114
+ function addDataServer(desc: { name: string; url: string }): DataServer {
115
+ const existing = _dataServers.find((s) => s.name === desc.name);
116
+ if (existing) return existing;
117
+ const srv: DataServer = { name: desc.name, url: desc.url, kind: 'data', enabled: true, connected: false };
118
+ _dataServers = [..._dataServers, srv];
119
+ notify();
120
+ return srv;
121
+ }
122
+
123
+ function removeDataServer(name: string): boolean {
124
+ const before = _dataServers.length;
125
+ _dataServers = _dataServers.filter((s) => s.name !== name);
126
+ if (_dataServers.length !== before) { notify(); return true; }
127
+ return false;
128
+ }
129
+
130
+ function getDataServer(name: string): DataServer | undefined {
131
+ return _dataServers.find((s) => s.name === name);
132
+ }
133
+
134
+ function setDataServerMeta(name: string, patch: Partial<Omit<DataServer, 'name' | 'url' | 'kind'>>): void {
135
+ const idx = _dataServers.findIndex((s) => s.name === name);
136
+ if (idx < 0) return;
137
+ _dataServers = _dataServers.map((s, i) => i === idx ? { ...s, ...patch } : s);
138
+ notify();
139
+ }
140
+
141
+ function setDataServerEnabled(name: string, enabled: boolean): boolean {
142
+ const s = _dataServers.find((x) => x.name === name);
143
+ if (!s) return false;
144
+ if (s.enabled === enabled) return true;
145
+ _dataServers = _dataServers.map((x) => x.name === name ? { ...x, enabled } : x);
146
+ notify();
147
+ return true;
148
+ }
149
+
150
+ function toggleDataServer(name: string): boolean {
151
+ const s = _dataServers.find((x) => x.name === name);
152
+ if (!s) return false;
153
+ return setDataServerEnabled(name, !s.enabled);
154
+ }
99
155
 
100
156
  // ── Widget actions ─────────────────────────────────────────────────────
101
157
  function addWidget(type: WidgetType, data: Record<string, unknown> = {}): Widget {
@@ -173,12 +229,16 @@ function createCanvasVanilla() {
173
229
  tools?: McpToolInfo[]
174
230
  ) {
175
231
  _mcpConnected = connected;
176
- if (name) _mcpName = name;
177
- if (tools) _mcpTools = tools;
178
232
  if (connected) {
233
+ if (name) _mcpName = name;
234
+ if (tools) _mcpTools = tools;
179
235
  _statusText = `● ${name} · ${tools?.length ?? 0} tools`;
180
236
  _statusColor = 'text-teal-400';
181
237
  } else {
238
+ // Reset stale connection state on disconnect so the UI doesn't keep
239
+ // advertising the previous server name / tool list.
240
+ _mcpName = '';
241
+ _mcpTools = [];
182
242
  _statusText = '● no MCP connected';
183
243
  _statusColor = 'text-zinc-600';
184
244
  }
@@ -222,8 +282,7 @@ function createCanvasVanilla() {
222
282
 
223
283
  async function buildHyperskillParam(): Promise<string> {
224
284
  const json = JSON.stringify(buildSkillJSON());
225
- const compress = json.length > 6144 ? 'gz' as const : undefined;
226
- const url = await encode('https://x.local', json, compress ? { compress } : {});
285
+ const url = await encode('https://x.local', json, { compress: 'gz' });
227
286
  return new URL(url).searchParams.get('hs')!;
228
287
  }
229
288
 
@@ -267,10 +326,19 @@ function createCanvasVanilla() {
267
326
  async function loadFromUrl(url: string): Promise<boolean> {
268
327
  try {
269
328
  const { content: raw } = await decode(url);
270
- const decoded = JSON.parse(raw) as { meta?: Record<string, unknown>; content?: { blocks?: { type: WidgetType; data: Record<string, unknown> }[] } };
329
+ const decoded = JSON.parse(raw) as {
330
+ meta?: Record<string, unknown>;
331
+ content?: { blocks?: { type: WidgetType; data: Record<string, unknown> }[] };
332
+ servers?: string[];
333
+ };
271
334
  if (decoded.meta?.mcp) _mcpUrl = decoded.meta.mcp as string;
272
335
  if (decoded.meta?.llm) _llm = decoded.meta.llm as LLMId;
273
336
  if (decoded.meta?.theme) _themeOverrides = decoded.meta.theme as Record<string, string>;
337
+ // Align with loadFromParam: restore enabledServerIds when `servers` is
338
+ // present. buildSkillJSON emits it at the root, but tolerate it under
339
+ // `meta` as well for forward/back compatibility.
340
+ const servers = (decoded.servers ?? (decoded.meta?.servers as string[] | undefined));
341
+ if (Array.isArray(servers)) _enabledServerIds = servers;
274
342
  if (decoded.content?.blocks) _blocks = decoded.content.blocks.map((b) => ({ id: uuid(), type: b.type, data: b.data }));
275
343
  notify();
276
344
  return true;
@@ -296,6 +364,7 @@ function createCanvasVanilla() {
296
364
  statusColor: _statusColor,
297
365
  themeOverrides: _themeOverrides,
298
366
  enabledServerIds: _enabledServerIds,
367
+ dataServers: _dataServers,
299
368
  blockCount: _blocks.length,
300
369
  isEmpty: _blocks.length === 0,
301
370
  };
@@ -358,6 +427,16 @@ function createCanvasVanilla() {
358
427
  get enabledServerIds() { return _enabledServerIds; },
359
428
  setEnabledServers,
360
429
 
430
+ // Data servers (multi-MCP) — additive, coexists with mcp* primary fields
431
+ get dataServers() { return _dataServers; },
432
+ set dataServers(v: DataServer[]) { _dataServers = Array.isArray(v) ? v : []; notify(); },
433
+ addDataServer,
434
+ removeDataServer,
435
+ getDataServer,
436
+ setDataServerMeta,
437
+ setDataServerEnabled,
438
+ toggleDataServer,
439
+
361
440
  // HyperSkill
362
441
  buildSkillJSON, buildHyperskillParam, loadFromParam, loadFromUrl,
363
442