js-bridge-mcp 0.1.0 → 0.1.6
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 +12 -0
- package/dist/src/tools/hello-tools.js +20 -20
- package/dist/src/tools/register.js +25 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -30,6 +30,18 @@ to write one for a new page.
|
|
|
30
30
|
|
|
31
31
|
## Run it
|
|
32
32
|
|
|
33
|
+
### Via npx
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx js-bridge-mcp
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Starts the MCP + WS + `main.js` server on port 8766. Point an MCP client at
|
|
40
|
+
`http://localhost:8766/mcp`, then serve `legacy-page/hello-world.html` (or
|
|
41
|
+
your own page) with any static file server.
|
|
42
|
+
|
|
43
|
+
### From this repo
|
|
44
|
+
|
|
33
45
|
```bash
|
|
34
46
|
npm run build # bundle src/client/main.ts -> dist/client/main.js
|
|
35
47
|
npm run start:mcp # MCP + WS + main.js server, port 8766
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
const getEmbedSnippet = {
|
|
3
3
|
name: 'get_embed_snippet',
|
|
4
|
-
description: 'Returns a single line of executable JavaScript that connects the current page to this ' +
|
|
5
|
-
'
|
|
6
|
-
'(
|
|
7
|
-
'
|
|
8
|
-
'
|
|
9
|
-
'
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'
|
|
4
|
+
description: 'Returns a single line of executable JavaScript that connects the current page to this MCP session\'s ' +
|
|
5
|
+
'channel. Paste it directly into the browser DevTools console on the target page and press enter — no ' +
|
|
6
|
+
'source edits needed (or wrap in <script type="module">...</script> before </body> to bake it into the ' +
|
|
7
|
+
'page). Once run, the page pushes its tool manifest (window.__mcpTools, optional window.__mcpSummary — see ' +
|
|
8
|
+
'describe_tools) to this session\'s channel, and its tools become callable from THIS conversation. Share ' +
|
|
9
|
+
'the snippet and tell the user to paste it in DevTools\' Console tab and press enter; do not construct the ' +
|
|
10
|
+
'URL by hand. ' +
|
|
11
|
+
'\n\n' +
|
|
12
|
+
'DEFAULT TO calling join_channel with a topic-scoped name (e.g. "bulletino", from the page/app or task) ' +
|
|
13
|
+
'BEFORE this — the normal path, not something reserved for when it happens to matter. Skip only for a ' +
|
|
14
|
+
'genuinely one-off bridge, or when nothing suggests a name worth giving it: staying on "default" means ' +
|
|
15
|
+
'sharing state with every other unnamed session on this server, and blocks another agent from reconnecting ' +
|
|
16
|
+
'to the SAME bridged page later by name. The snippet is generated fresh from whatever channel this session ' +
|
|
17
|
+
'is on, so join first, then call this. Reusing an already-generated snippet in a second tab connects that ' +
|
|
18
|
+
'tab to the SAME channel, not a new one — join a different name and call this again for a distinct one. ' +
|
|
19
|
+
'\n\n' +
|
|
20
|
+
'Running the snippet opens a browser prompt() asking the user to name this connection (pre-filled with the ' +
|
|
21
|
+
'page title) — warn them before they paste it; dismissing is safe (falls back to the page title). That ' +
|
|
22
|
+
'name becomes this connection\'s tool-name prefix once a second connection joins the same channel (see ' +
|
|
23
|
+
'describe_tools).',
|
|
24
24
|
schema: {},
|
|
25
25
|
handler: async (_args, tenant, port) => {
|
|
26
26
|
const tenantId = tenant().id;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { createManifestToolRegistry } from 'mcp-tenant-lib';
|
|
1
|
+
import { createManifestToolRegistry, registerChannelTools } from 'mcp-tenant-lib';
|
|
2
|
+
import { initialHelloState } from '../types.js';
|
|
2
3
|
import { helloTools } from './hello-tools.js';
|
|
3
|
-
export function registerHelloTools(mcp, tenant, port) {
|
|
4
|
+
export function registerHelloTools(mcp, tenant, port, setChannel) {
|
|
4
5
|
for (const tool of helloTools) {
|
|
5
6
|
mcp.tool(tool.name, tool.description, tool.schema, (args) => tool.handler(args, tenant, port));
|
|
6
7
|
}
|
|
@@ -11,6 +12,26 @@ export function registerHelloTools(mcp, tenant, port) {
|
|
|
11
12
|
// multiple concurrent MCP sessions' McpServer/registry pairs can be
|
|
12
13
|
// bound to the same tenant at once and all need to stay in sync, not
|
|
13
14
|
// just whichever one connected last.
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
//
|
|
16
|
+
// Track which Tenant object the registry is currently subscribed to so a
|
|
17
|
+
// later join_channel (see the wrapped setChannel below) can move the
|
|
18
|
+
// subscription — tenant() is a live accessor that starts pointing at a
|
|
19
|
+
// different Tenant once the session retargets to a named channel, but the
|
|
20
|
+
// subscription itself does NOT follow automatically: addManifestToolRegistry
|
|
21
|
+
// only runs once here, against whichever tenant was current at connect
|
|
22
|
+
// time. Without migrating it, a page that bridges into a channel AFTER
|
|
23
|
+
// join_channel was called would push its manifest to a tenant nothing is
|
|
24
|
+
// subscribed to — describe_tools would still see it (it reads
|
|
25
|
+
// tenant().toolManifest fresh every call), but no real MCP tool would ever
|
|
26
|
+
// get registered for it.
|
|
27
|
+
let subscribedTenant = tenant();
|
|
28
|
+
subscribedTenant.addManifestToolRegistry(registry);
|
|
29
|
+
mcp.server.onclose = () => subscribedTenant.removeManifestToolRegistry(registry);
|
|
30
|
+
const setChannelAndMigrateRegistry = (id) => {
|
|
31
|
+
setChannel(id);
|
|
32
|
+
subscribedTenant.removeManifestToolRegistry(registry);
|
|
33
|
+
subscribedTenant = tenant();
|
|
34
|
+
subscribedTenant.addManifestToolRegistry(registry);
|
|
35
|
+
};
|
|
36
|
+
registerChannelTools(mcp, tenant, port, setChannelAndMigrateRegistry, undefined, { ...initialHelloState });
|
|
16
37
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "js-bridge-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
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": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"test": "node --import tsx --test test/**/*.test.ts"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"mcp-tenant-lib": "^0.
|
|
38
|
+
"mcp-tenant-lib": "^0.3.3",
|
|
39
39
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
40
40
|
"ws": "^8.18.0",
|
|
41
41
|
"zod": "^3.23.8"
|