js-bridge-mcp 0.1.7 → 0.1.8

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.
@@ -4,29 +4,29 @@ function _(e) {
4
4
  n.push(s), o.set(s.name, c);
5
5
  return { manifest: n, fnByName: o };
6
6
  }
7
- const f = 1e4, g = 3600 * 1e3;
7
+ const w = 1e4, f = 3600 * 1e3;
8
8
  function $(e, n = {}) {
9
9
  let o, c = !1, s = 0, p;
10
10
  const m = () => {
11
11
  const a = n.tenant ?? (location.pathname.startsWith("/t/") ? location.pathname.slice(3).split("/")[0] : ""), y = a ? `/ws?tenant=${encodeURIComponent(a)}` : "/ws", d = `${n.serverUrl ? n.serverUrl.replace(/^http/, "ws") : `${location.protocol === "https:" ? "wss:" : "ws:"}//${location.host}`}${y}`;
12
12
  console.log(`[mcp-ws] connecting (attempt ${s + 1}): ${d}`), o = new WebSocket(d), o.onopen = () => {
13
13
  console.log(`[mcp-ws] connected${s > 0 ? ` after ${s} reconnect attempt(s)` : ""}`), s = 0, p = void 0, e.onConnect?.();
14
- }, o.onclose = (i) => {
15
- if (console.log(`[mcp-ws] disconnected: code=${i.code} reason=${i.reason || "(none)"} wasClean=${i.wasClean}`), e.onDisconnect?.(), !c) {
16
- if (i.code === 4404) {
14
+ }, o.onclose = (r) => {
15
+ if (console.log(`[mcp-ws] disconnected: code=${r.code} reason=${r.reason || "(none)"} wasClean=${r.wasClean}`), e.onDisconnect?.(), !c) {
16
+ if (r.code === 4404) {
17
17
  console.log("[mcp-ws] invalid tenant id (4404) — not retrying");
18
18
  return;
19
19
  }
20
- if (p ??= Date.now(), Date.now() - p > g) {
21
- console.log(`[mcp-ws] giving up after retrying for over ${g / 6e4} minutes`);
20
+ if (p ??= Date.now(), Date.now() - p > f) {
21
+ console.log(`[mcp-ws] giving up after retrying for over ${f / 6e4} minutes`);
22
22
  return;
23
23
  }
24
- s++, console.log(`[mcp-ws] retrying in ${f / 1e3}s (attempt ${s + 1})`), setTimeout(m, f);
24
+ s++, console.log(`[mcp-ws] retrying in ${w / 1e3}s (attempt ${s + 1})`), setTimeout(m, w);
25
25
  }
26
26
  }, o.onerror = () => {
27
27
  console.log("[mcp-ws] socket error (see close event for details)");
28
- }, o.onmessage = (i) => {
29
- const t = JSON.parse(i.data);
28
+ }, o.onmessage = (r) => {
29
+ const t = JSON.parse(r.data);
30
30
  t.type === "init" && e.onInit?.(t.schema, t.state), t.type === "reinit" && e.onReinit?.(t.schema, t.state), t.type === "update" && e.onUpdate?.(t.field, t.value), t.type === "call" && e.onCall?.(t.id, t.name, t.args), t.type === "identify" && (e.onIdentify ? e.onIdentify(t.label) : alert(`Identify: this is the "${t.label ?? "unlabeled"}" connection`));
31
31
  };
32
32
  };
@@ -39,23 +39,22 @@ function $(e, n = {}) {
39
39
  }
40
40
  };
41
41
  }
42
- const h = window.__mcpTools ?? [], { manifest: C, fnByName: b } = _(h), N = window.__mcpSummary ?? void 0;
43
- let r = window.__mcpAppName ?? document.title ?? void 0, w = !1;
44
- function v() {
45
- if (w) return r;
46
- w = !0;
47
- const e = prompt("Name this MCP connection (used to identify it to the agent):", r ?? "");
48
- return e && (r = e), r;
42
+ const h = window.__mcpTools ?? [], { manifest: b, fnByName: C } = _(h), N = window.__mcpSummary ?? void 0, v = typeof window.__mcpAppName == "string" && window.__mcpAppName;
43
+ let i = window.__mcpAppName ?? document.title ?? void 0, g = !1;
44
+ function S() {
45
+ if (g || (g = !0, v)) return i;
46
+ const e = prompt("Name this MCP connection (used to identify it to the agent):", i ?? "");
47
+ return e && (i = e), i;
49
48
  }
50
- const u = new URL(import.meta.url), S = u.searchParams.get("server") ?? void 0, R = u.searchParams.get("tenant") ?? void 0, l = $(
49
+ const u = new URL(import.meta.url), R = u.searchParams.get("server") ?? void 0, U = u.searchParams.get("tenant") ?? void 0, l = $(
51
50
  {
52
51
  onConnect() {
53
- const e = v();
54
- console.log(`[js-bridge-mcp] connected as "${e ?? "(unlabeled)"}"`), l.send({ type: "register_tools", tools: C, summary: N, appLabel: e });
52
+ const e = S();
53
+ console.log(`[js-bridge-mcp] connected as "${e ?? "(unlabeled)"}"`), l.send({ type: "register_tools", tools: b, summary: N, appLabel: e });
55
54
  },
56
55
  async onCall(e, n, o) {
57
56
  try {
58
- const c = b.get(n);
57
+ const c = C.get(n);
59
58
  if (!c) throw new Error(`no page tool named "${n}" — was it in window.__mcpTools when this script loaded?`);
60
59
  const s = await c(o);
61
60
  l.send({ type: "call_result", id: e, result: s });
@@ -67,9 +66,9 @@ const u = new URL(import.meta.url), S = u.searchParams.get("server") ?? void 0,
67
66
  console.log("[js-bridge-mcp] disconnected, retrying...");
68
67
  }
69
68
  },
70
- { serverUrl: S, tenant: R }
69
+ { serverUrl: R, tenant: U }
71
70
  );
72
71
  window.__mcpRename = (e) => {
73
- const n = e ?? prompt("Rename this MCP connection:", r ?? "");
74
- n && (r = n, l.send({ type: "rename_connection", appLabel: n }), console.log(`[js-bridge-mcp] renamed connection to "${n}"`));
72
+ const n = e ?? prompt("Rename this MCP connection:", i ?? "");
73
+ n && (i = n, l.send({ type: "rename_connection", appLabel: n }), console.log(`[js-bridge-mcp] renamed connection to "${n}"`));
75
74
  };
@@ -0,0 +1,60 @@
1
+ // Generic, provider-agnostic tool registry shared by any page embedding
2
+ // js-bridge-mcp. Deliberately NOT part of main.ts's build (see vite.config.ts's
3
+ // copy-tool-bus plugin) - this is infrastructure any embeddable provider
4
+ // (folderfoo today, others later) and any host app (bulletino today) can
5
+ // import by URL, independent of js-bridge-mcp's own bundle.
6
+ //
7
+ // Solves the load-order problem plain `window.__mcpTools` has: main.ts reads
8
+ // that array once, synchronously, at import time, so a provider script that
9
+ // finishes loading even a tick later is invisible to it. A host page that
10
+ // wants late-registering providers to work should:
11
+ // 1. import this file first,
12
+ // 2. import every provider script (each one calls registerProvider()),
13
+ // 3. merge window.__mcpToolBus.getTools() into window.__mcpTools itself,
14
+ // 4. only then import main.js.
15
+ // Usage: <script type="module" src="http://<js-bridge-mcp host>/tool-bus.js"></script>
16
+ window.__mcpToolBus ??= (() => {
17
+ const providers = new Map(); // providerName -> tool[]
18
+ const listeners = new Set();
19
+
20
+ function notify() {
21
+ for (const cb of listeners) cb();
22
+ }
23
+
24
+ function getTools() {
25
+ // Auto-prefix on name collision across providers, mirroring
26
+ // js-bridge-mcp's own slug-suffix-on-collision pattern for connections.
27
+ const claimedBy = new Map(); // tool name -> providerName that claimed it first
28
+ const out = [];
29
+ for (const [providerName, tools] of providers) {
30
+ for (const tool of tools) {
31
+ let name = tool.name;
32
+ if (claimedBy.has(name)) {
33
+ name = `${providerName}__${tool.name}`;
34
+ } else {
35
+ claimedBy.set(name, providerName);
36
+ }
37
+ out.push({ ...tool, name });
38
+ }
39
+ }
40
+ return out;
41
+ }
42
+
43
+ return {
44
+ // Re-registering under the same providerName replaces its previous tool
45
+ // set (no manual unregister needed for e.g. a folder-switch refresh).
46
+ registerProvider(providerName, tools) {
47
+ providers.set(providerName, tools);
48
+ notify();
49
+ return () => {
50
+ providers.delete(providerName);
51
+ notify();
52
+ };
53
+ },
54
+ getTools,
55
+ onChange(cb) {
56
+ listeners.add(cb);
57
+ return () => listeners.delete(cb);
58
+ },
59
+ };
60
+ })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-bridge-mcp",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "description": "Generic bridge: exposes MCP tools discovered from a connected page's own JSON tool manifest, dispatched over a cross-origin WebSocket. Ships a hello-world example page under legacy-page/.",
6
6
  "repository": {