@mcp-b/webmcp-local-relay 5.0.1 → 5.1.0
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/dist/cli.mjs +1 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +10 -11
- package/dist/index.mjs +1 -1
- package/dist/{mcpRelayServer-BxI6zEc2.mjs → mcpRelayServer-DfGkigG5.mjs} +13 -5
- package/dist/mcpRelayServer-DfGkigG5.mjs.map +1 -0
- package/package.json +10 -8
- package/dist/mcpRelayServer-BxI6zEc2.mjs.map +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as parseCliOptions, r as printHelp, t as LocalRelayMcpServer } from "./mcpRelayServer-
|
|
2
|
+
import { n as parseCliOptions, r as printHelp, t as LocalRelayMcpServer } from "./mcpRelayServer-DfGkigG5.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/cli.ts
|
|
5
5
|
let options;
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { parseCliOptions, printHelp } from './cli-utils.js';\nimport { LocalRelayMcpServer } from './mcpRelayServer.js';\n\nlet options: ReturnType<typeof parseCliOptions>;\ntry {\n options = parseCliOptions(process.argv.slice(2));\n} catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] error: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n process.exit(1);\n}\n\nif (options.help) {\n printHelp();\n process.exit(0);\n}\n\nif (options.allowedOrigins.includes('*')) {\n process.stderr.write(\n '[webmcp-local-relay] WARNING: accepting connections from ALL host page origins. Use --widget-origin to restrict.\\n'\n );\n}\n\nconst relay = new LocalRelayMcpServer({\n bridgeOptions: options,\n});\n\ntry {\n await relay.start();\n} catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes('EACCES')) {\n process.stderr.write(\n `[webmcp-local-relay] error: insufficient permissions to bind to ${options.host}:${options.port}.\\n`\n );\n } else {\n process.stderr.write(`[webmcp-local-relay] error: failed to start bridge server: ${message}\\n`);\n }\n process.exit(1);\n}\n\ntry {\n await relay.startStdio();\n} catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[webmcp-local-relay] error: failed to start stdio transport: ${message}\\n`);\n process.exit(1);\n}\n\n// --- Auto-exit when the MCP client disconnects, preventing zombie processes ---\n\nlet parentCheckInterval: NodeJS.Timeout | undefined;\nlet shuttingDown = false;\n\n/**\n * Gracefully shuts down bridge and MCP server for process termination signals.\n * Force-exits after 5 seconds if graceful shutdown does not complete, preventing hangs.\n */\nconst shutdown = async (signal: string) => {\n if (shuttingDown) {\n return;\n }\n shuttingDown = true;\n if (parentCheckInterval) {\n clearInterval(parentCheckInterval);\n parentCheckInterval = undefined;\n }\n\n // Safety net: force-exit if graceful shutdown hangs (e.g. SDK writing to a broken stdio pipe)\n const forceExitTimer = setTimeout(() => {\n process.stderr.write('[webmcp-local-relay] warn: graceful shutdown timed out, force exiting\\n');\n process.exit(1);\n }, 5_000);\n\n process.stderr.write(`[webmcp-local-relay] received ${signal}, shutting down\\n`);\n\n try {\n await relay.stop();\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] error during shutdown: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n clearTimeout(forceExitTimer);\n process.exit(1);\n }\n clearTimeout(forceExitTimer);\n process.exit(0);\n};\n\nprocess.stdin.on('end', () => {\n void shutdown('stdin-closed');\n});\nprocess.stdin.on('error', () => {\n void shutdown('stdin-error');\n});\nprocess.stdout.on('error', () => {\n void shutdown('stdout-error');\n});\n\nif (process.platform !== 'win32') {\n parentCheckInterval = setInterval(() => {\n if (process.ppid === 1) {\n void shutdown('parent-exited');\n }\n }, 30_000);\n parentCheckInterval.unref();\n}\n\nif (relay.bridge.mode === 'server') {\n process.stderr.write(\n `[webmcp-local-relay] server mode: listening on ws://${options.host}:${relay.bridge.port} (allowed origins: ${options.allowedOrigins.join(', ')})\\n`\n );\n} else {\n process.stderr.write(\n `[webmcp-local-relay] client mode: proxying through existing relay at ws://${options.host}:${relay.bridge.port}\\n`\n );\n}\n\nprocess.on('uncaughtException', (err) => {\n process.stderr.write(\n `[webmcp-local-relay] error: uncaught exception: ${err.stack ?? err.message}\\n`\n );\n void shutdown('uncaughtException');\n});\nprocess.on('unhandledRejection', (reason) => {\n const message = reason instanceof Error ? (reason.stack ?? reason.message) : String(reason);\n process.stderr.write(`[webmcp-local-relay] error: unhandled rejection: ${message}\\n`);\n void shutdown('unhandledRejection');\n});\n\nprocess.on('SIGINT', () => {\n void shutdown('SIGINT');\n});\nprocess.on('SIGTERM', () => {\n void shutdown('SIGTERM');\n});\n"],"mappings":";;;;AAKA,IAAI;AACJ,IAAI;CACF,UAAU,gBAAgB,QAAQ,KAAK,MAAM,
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { parseCliOptions, printHelp } from './cli-utils.js';\nimport { LocalRelayMcpServer } from './mcpRelayServer.js';\n\nlet options: ReturnType<typeof parseCliOptions>;\ntry {\n options = parseCliOptions(process.argv.slice(2));\n} catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] error: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n process.exit(1);\n}\n\nif (options.help) {\n printHelp();\n process.exit(0);\n}\n\nif (options.allowedOrigins.includes('*')) {\n process.stderr.write(\n '[webmcp-local-relay] WARNING: accepting connections from ALL host page origins. Use --widget-origin to restrict.\\n'\n );\n}\n\nconst relay = new LocalRelayMcpServer({\n bridgeOptions: options,\n});\n\ntry {\n await relay.start();\n} catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes('EACCES')) {\n process.stderr.write(\n `[webmcp-local-relay] error: insufficient permissions to bind to ${options.host}:${options.port}.\\n`\n );\n } else {\n process.stderr.write(`[webmcp-local-relay] error: failed to start bridge server: ${message}\\n`);\n }\n process.exit(1);\n}\n\ntry {\n await relay.startStdio();\n} catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[webmcp-local-relay] error: failed to start stdio transport: ${message}\\n`);\n process.exit(1);\n}\n\n// --- Auto-exit when the MCP client disconnects, preventing zombie processes ---\n\nlet parentCheckInterval: NodeJS.Timeout | undefined;\nlet shuttingDown = false;\n\n/**\n * Gracefully shuts down bridge and MCP server for process termination signals.\n * Force-exits after 5 seconds if graceful shutdown does not complete, preventing hangs.\n */\nconst shutdown = async (signal: string) => {\n if (shuttingDown) {\n return;\n }\n shuttingDown = true;\n if (parentCheckInterval) {\n clearInterval(parentCheckInterval);\n parentCheckInterval = undefined;\n }\n\n // Safety net: force-exit if graceful shutdown hangs (e.g. SDK writing to a broken stdio pipe)\n const forceExitTimer = setTimeout(() => {\n process.stderr.write('[webmcp-local-relay] warn: graceful shutdown timed out, force exiting\\n');\n process.exit(1);\n }, 5_000);\n\n process.stderr.write(`[webmcp-local-relay] received ${signal}, shutting down\\n`);\n\n try {\n await relay.stop();\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] error during shutdown: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n clearTimeout(forceExitTimer);\n process.exit(1);\n }\n clearTimeout(forceExitTimer);\n process.exit(0);\n};\n\nprocess.stdin.on('end', () => {\n void shutdown('stdin-closed');\n});\nprocess.stdin.on('error', () => {\n void shutdown('stdin-error');\n});\nprocess.stdout.on('error', () => {\n void shutdown('stdout-error');\n});\n\nif (process.platform !== 'win32') {\n parentCheckInterval = setInterval(() => {\n if (process.ppid === 1) {\n void shutdown('parent-exited');\n }\n }, 30_000);\n parentCheckInterval.unref();\n}\n\nif (relay.bridge.mode === 'server') {\n process.stderr.write(\n `[webmcp-local-relay] server mode: listening on ws://${options.host}:${relay.bridge.port} (allowed origins: ${options.allowedOrigins.join(', ')})\\n`\n );\n} else {\n process.stderr.write(\n `[webmcp-local-relay] client mode: proxying through existing relay at ws://${options.host}:${relay.bridge.port}\\n`\n );\n}\n\nprocess.on('uncaughtException', (err) => {\n process.stderr.write(\n `[webmcp-local-relay] error: uncaught exception: ${err.stack ?? err.message}\\n`\n );\n void shutdown('uncaughtException');\n});\nprocess.on('unhandledRejection', (reason) => {\n const message = reason instanceof Error ? (reason.stack ?? reason.message) : String(reason);\n process.stderr.write(`[webmcp-local-relay] error: unhandled rejection: ${message}\\n`);\n void shutdown('unhandledRejection');\n});\n\nprocess.on('SIGINT', () => {\n void shutdown('SIGINT');\n});\nprocess.on('SIGTERM', () => {\n void shutdown('SIGTERM');\n});\n"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
2
|
import { CallToolRequestParamsSchema, CallToolResultSchema, ToolAnnotationsSchema, ToolSchema } from "@modelcontextprotocol/core";
|
|
3
3
|
import { z } from "zod/v4";
|
|
4
|
-
import * as _$_modelcontextprotocol_server0 from "@modelcontextprotocol/server";
|
|
5
4
|
import { CallToolResult, Tool, ToolAnnotations, Transport } from "@modelcontextprotocol/server";
|
|
6
5
|
|
|
7
6
|
//#region src/protocol.d.ts
|
|
@@ -12,7 +11,7 @@ declare const NormalizedToolSchema: z.ZodObject<{
|
|
|
12
11
|
description: z.ZodOptional<z.ZodString>;
|
|
13
12
|
inputSchema: z.ZodObject<{
|
|
14
13
|
type: z.ZodLiteral<"object">;
|
|
15
|
-
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<
|
|
14
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<import("@modelcontextprotocol/server").JSONValue, import("@modelcontextprotocol/server").JSONValue, z.core.$ZodTypeInternals<import("@modelcontextprotocol/server").JSONValue, import("@modelcontextprotocol/server").JSONValue>>>>;
|
|
16
15
|
required: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
17
16
|
}, z.core.$catchall<z.ZodUnknown>>;
|
|
18
17
|
outputSchema: z.ZodOptional<z.ZodObject<{
|
|
@@ -88,7 +87,7 @@ declare const InboundToolSchema: z.ZodObject<{
|
|
|
88
87
|
name: z.ZodString;
|
|
89
88
|
inputSchema: z.ZodOptional<z.ZodObject<{
|
|
90
89
|
type: z.ZodLiteral<"object">;
|
|
91
|
-
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<
|
|
90
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<import("@modelcontextprotocol/server").JSONValue, import("@modelcontextprotocol/server").JSONValue, z.core.$ZodTypeInternals<import("@modelcontextprotocol/server").JSONValue, import("@modelcontextprotocol/server").JSONValue>>>>;
|
|
92
91
|
required: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
93
92
|
}, z.core.$catchall<z.ZodUnknown>>>;
|
|
94
93
|
}, z.core.$strip>;
|
|
@@ -167,14 +166,14 @@ declare const BrowserToRelayMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
|
|
|
167
166
|
name: z.ZodString;
|
|
168
167
|
inputSchema: z.ZodOptional<z.ZodObject<{
|
|
169
168
|
type: z.ZodLiteral<"object">;
|
|
170
|
-
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<
|
|
169
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<import("@modelcontextprotocol/server").JSONValue, import("@modelcontextprotocol/server").JSONValue, z.core.$ZodTypeInternals<import("@modelcontextprotocol/server").JSONValue, import("@modelcontextprotocol/server").JSONValue>>>>;
|
|
171
170
|
required: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
172
171
|
}, z.core.$catchall<z.ZodUnknown>>>;
|
|
173
172
|
}, z.core.$strip>>, z.ZodTransform<{
|
|
174
173
|
inputSchema: {
|
|
175
174
|
[x: string]: unknown;
|
|
176
175
|
type: "object";
|
|
177
|
-
properties?: Record<string,
|
|
176
|
+
properties?: Record<string, import("@modelcontextprotocol/server").JSONValue> | undefined;
|
|
178
177
|
required?: string[] | undefined;
|
|
179
178
|
};
|
|
180
179
|
name: string;
|
|
@@ -229,7 +228,7 @@ declare const BrowserToRelayMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
|
|
|
229
228
|
inputSchema?: {
|
|
230
229
|
[x: string]: unknown;
|
|
231
230
|
type: "object";
|
|
232
|
-
properties?: Record<string,
|
|
231
|
+
properties?: Record<string, import("@modelcontextprotocol/server").JSONValue> | undefined;
|
|
233
232
|
required?: string[] | undefined;
|
|
234
233
|
} | undefined;
|
|
235
234
|
}[]>>;
|
|
@@ -268,14 +267,14 @@ declare const BrowserToRelayMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
|
|
|
268
267
|
name: z.ZodString;
|
|
269
268
|
inputSchema: z.ZodOptional<z.ZodObject<{
|
|
270
269
|
type: z.ZodLiteral<"object">;
|
|
271
|
-
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<
|
|
270
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<import("@modelcontextprotocol/server").JSONValue, import("@modelcontextprotocol/server").JSONValue, z.core.$ZodTypeInternals<import("@modelcontextprotocol/server").JSONValue, import("@modelcontextprotocol/server").JSONValue>>>>;
|
|
272
271
|
required: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
273
272
|
}, z.core.$catchall<z.ZodUnknown>>>;
|
|
274
273
|
}, z.core.$strip>>, z.ZodTransform<{
|
|
275
274
|
inputSchema: {
|
|
276
275
|
[x: string]: unknown;
|
|
277
276
|
type: "object";
|
|
278
|
-
properties?: Record<string,
|
|
277
|
+
properties?: Record<string, import("@modelcontextprotocol/server").JSONValue> | undefined;
|
|
279
278
|
required?: string[] | undefined;
|
|
280
279
|
};
|
|
281
280
|
name: string;
|
|
@@ -330,7 +329,7 @@ declare const BrowserToRelayMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
|
|
|
330
329
|
inputSchema?: {
|
|
331
330
|
[x: string]: unknown;
|
|
332
331
|
type: "object";
|
|
333
|
-
properties?: Record<string,
|
|
332
|
+
properties?: Record<string, import("@modelcontextprotocol/server").JSONValue> | undefined;
|
|
334
333
|
required?: string[] | undefined;
|
|
335
334
|
} | undefined;
|
|
336
335
|
}[]>>;
|
|
@@ -490,7 +489,7 @@ declare const RelayServerToClientMessageSchema: z.ZodDiscriminatedUnion<[z.ZodOb
|
|
|
490
489
|
description: z.ZodOptional<z.ZodString>;
|
|
491
490
|
inputSchema: z.ZodObject<{
|
|
492
491
|
type: z.ZodLiteral<"object">;
|
|
493
|
-
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<
|
|
492
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<import("@modelcontextprotocol/server").JSONValue, import("@modelcontextprotocol/server").JSONValue, z.core.$ZodTypeInternals<import("@modelcontextprotocol/server").JSONValue, import("@modelcontextprotocol/server").JSONValue>>>>;
|
|
494
493
|
required: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
495
494
|
}, z.core.$catchall<z.ZodUnknown>>;
|
|
496
495
|
outputSchema: z.ZodOptional<z.ZodObject<{
|
|
@@ -653,7 +652,7 @@ declare const RelayServerToClientMessageSchema: z.ZodDiscriminatedUnion<[z.ZodOb
|
|
|
653
652
|
description: z.ZodOptional<z.ZodString>;
|
|
654
653
|
inputSchema: z.ZodObject<{
|
|
655
654
|
type: z.ZodLiteral<"object">;
|
|
656
|
-
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<
|
|
655
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<import("@modelcontextprotocol/server").JSONValue, import("@modelcontextprotocol/server").JSONValue, z.core.$ZodTypeInternals<import("@modelcontextprotocol/server").JSONValue, import("@modelcontextprotocol/server").JSONValue>>>>;
|
|
657
656
|
required: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
658
657
|
}, z.core.$catchall<z.ZodUnknown>>;
|
|
659
658
|
outputSchema: z.ZodOptional<z.ZodObject<{
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { C as NormalizedToolSchema, D as normalizeInboundTool, E as ToolSchema, S as InboundToolSchema, T as ToolAnnotationsSchema, _ as extractSanitizedDomain, a as BrowserToRelayMessageSchema, b as CallToolResultSchema, c as RelayHelloAcceptedMessageSchema, d as RelaySourceInfoSchema, f as RelayToBrowserMessageSchema, g as buildPublicToolName, h as RelayRegistry, i as RelayBridgeServer, l as RelayHelloRejectedMessageSchema, m as HelloRequiredError, n as parseCliOptions, o as RelayClientToServerMessageSchema, p as ServerHelloMessageSchema, r as printHelp, s as RelayDescriptorSchema, t as LocalRelayMcpServer, u as RelayServerToClientMessageSchema, v as sanitizeName, w as RelayInvokeArgsSchema, x as DEFAULT_TOOL_INPUT_SCHEMA, y as CallToolRequestParamsSchema } from "./mcpRelayServer-
|
|
1
|
+
import { C as NormalizedToolSchema, D as normalizeInboundTool, E as ToolSchema, S as InboundToolSchema, T as ToolAnnotationsSchema, _ as extractSanitizedDomain, a as BrowserToRelayMessageSchema, b as CallToolResultSchema, c as RelayHelloAcceptedMessageSchema, d as RelaySourceInfoSchema, f as RelayToBrowserMessageSchema, g as buildPublicToolName, h as RelayRegistry, i as RelayBridgeServer, l as RelayHelloRejectedMessageSchema, m as HelloRequiredError, n as parseCliOptions, o as RelayClientToServerMessageSchema, p as ServerHelloMessageSchema, r as printHelp, s as RelayDescriptorSchema, t as LocalRelayMcpServer, u as RelayServerToClientMessageSchema, v as sanitizeName, w as RelayInvokeArgsSchema, x as DEFAULT_TOOL_INPUT_SCHEMA, y as CallToolRequestParamsSchema } from "./mcpRelayServer-DfGkigG5.mjs";
|
|
2
2
|
|
|
3
3
|
export { BrowserToRelayMessageSchema, CallToolRequestParamsSchema, CallToolResultSchema, DEFAULT_TOOL_INPUT_SCHEMA, HelloRequiredError, InboundToolSchema, LocalRelayMcpServer, NormalizedToolSchema, RelayBridgeServer, RelayClientToServerMessageSchema, RelayDescriptorSchema, RelayHelloAcceptedMessageSchema, RelayHelloRejectedMessageSchema, RelayInvokeArgsSchema, RelayRegistry, RelayServerToClientMessageSchema, RelaySourceInfoSchema, RelayToBrowserMessageSchema, ServerHelloMessageSchema, ToolAnnotationsSchema, ToolSchema, buildPublicToolName, extractSanitizedDomain, normalizeInboundTool, parseCliOptions, printHelp, sanitizeName };
|
|
@@ -158,6 +158,7 @@ var HelloRequiredError = class extends Error {
|
|
|
158
158
|
* In-memory source and tool registry used by the relay bridge.
|
|
159
159
|
*/
|
|
160
160
|
var RelayRegistry = class {
|
|
161
|
+
now;
|
|
161
162
|
sourceByConnectionId = /* @__PURE__ */ new Map();
|
|
162
163
|
toolsByConnectionId = /* @__PURE__ */ new Map();
|
|
163
164
|
providersByPublicToolName = /* @__PURE__ */ new Map();
|
|
@@ -186,6 +187,10 @@ var RelayRegistry = class {
|
|
|
186
187
|
connectedAt: existing?.connectedAt ?? now,
|
|
187
188
|
lastSeenAt: now
|
|
188
189
|
});
|
|
190
|
+
if (existing && existing.tabId !== hello.tabId) {
|
|
191
|
+
for (const provider of this.toolsByConnectionId.get(connectionId) ?? []) provider.tabId = hello.tabId;
|
|
192
|
+
this.rebuildPublicNames();
|
|
193
|
+
}
|
|
189
194
|
}
|
|
190
195
|
/**
|
|
191
196
|
* Replaces the full tool set for a source connection.
|
|
@@ -710,10 +715,13 @@ var RelayBridgeServer = class extends EventEmitter {
|
|
|
710
715
|
return;
|
|
711
716
|
}
|
|
712
717
|
this.off("stateChanged", this.onStateChangedPushRelay);
|
|
713
|
-
for (const socket of this.socketByConnectionId
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
718
|
+
for (const [connectionId, socket] of this.socketByConnectionId) {
|
|
719
|
+
this.registry.removeConnection(connectionId);
|
|
720
|
+
try {
|
|
721
|
+
socket.close(1001, "Relay shutting down");
|
|
722
|
+
} catch (err) {
|
|
723
|
+
process.stderr.write(`[webmcp-local-relay] warn: error closing socket during shutdown: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
724
|
+
}
|
|
717
725
|
}
|
|
718
726
|
for (const intervalId of this.heartbeatIntervalByConnectionId.values()) clearInterval(intervalId);
|
|
719
727
|
this.heartbeatIntervalByConnectionId.clear();
|
|
@@ -1824,4 +1832,4 @@ var LocalRelayMcpServer = class {
|
|
|
1824
1832
|
|
|
1825
1833
|
//#endregion
|
|
1826
1834
|
export { NormalizedToolSchema as C, normalizeInboundTool as D, ToolSchema as E, InboundToolSchema as S, ToolAnnotationsSchema as T, extractSanitizedDomain as _, BrowserToRelayMessageSchema as a, CallToolResultSchema as b, RelayHelloAcceptedMessageSchema as c, RelaySourceInfoSchema as d, RelayToBrowserMessageSchema as f, buildPublicToolName as g, RelayRegistry as h, RelayBridgeServer as i, RelayHelloRejectedMessageSchema as l, HelloRequiredError as m, parseCliOptions as n, RelayClientToServerMessageSchema as o, ServerHelloMessageSchema as p, printHelp as r, RelayDescriptorSchema as s, LocalRelayMcpServer as t, RelayServerToClientMessageSchema as u, sanitizeName as v, RelayInvokeArgsSchema as w, DEFAULT_TOOL_INPUT_SCHEMA as x, CallToolRequestParamsSchema as y };
|
|
1827
|
-
//# sourceMappingURL=mcpRelayServer-
|
|
1835
|
+
//# sourceMappingURL=mcpRelayServer-DfGkigG5.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpRelayServer-DfGkigG5.mjs","names":[],"sources":["../src/portStrategy.ts","../src/protocol.ts","../src/naming.ts","../src/registry.ts","../src/schemas.ts","../src/bridgeServer.ts","../src/cli-utils.ts","../src/mcpRelayServer.ts"],"sourcesContent":["import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\nexport const DEFAULT_RELAY_PORT = 9333;\nexport const DEFAULT_RELAY_PORT_RANGE_END = 9348;\nconst RELAY_PORT_CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000;\n\ninterface PersistedRelayPort {\n host: string;\n port: number;\n updatedAt: string;\n}\n\nexport interface PortStrategyOptions {\n fixedPort?: number;\n defaultPort: number;\n rangeEnd: number;\n host: string;\n persistPath?: string;\n}\n\nexport interface PortStrategyResult {\n port: number;\n wasFixed: boolean;\n fromCache: boolean;\n}\n\nexport function defaultRelayPortPersistPath(): string {\n return join(homedir(), '.webmcp', 'relay-port.json');\n}\n\nexport async function persistPort(\n port: number,\n path = defaultRelayPortPersistPath(),\n host = '127.0.0.1',\n now = Date.now()\n): Promise<void> {\n const payload: PersistedRelayPort = {\n port,\n host,\n updatedAt: new Date(now).toISOString(),\n };\n\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(payload, null, 2)}\\n`, 'utf8');\n}\n\nasync function readPersistedPort(\n path = defaultRelayPortPersistPath(),\n options: {\n expectedHost?: string;\n maxAgeMs?: number;\n now?: number;\n } = {}\n): Promise<number | null> {\n try {\n const raw = await readFile(path, 'utf8');\n const parsed = JSON.parse(raw) as Partial<PersistedRelayPort>;\n if (\n typeof parsed.port !== 'number' ||\n !Number.isInteger(parsed.port) ||\n parsed.port < 1 ||\n parsed.port > 65535\n ) {\n return null;\n }\n if (typeof parsed.host !== 'string' || parsed.host.length === 0) {\n return null;\n }\n if (options.expectedHost && parsed.host !== options.expectedHost) {\n return null;\n }\n\n const maxAgeMs = options.maxAgeMs ?? RELAY_PORT_CACHE_MAX_AGE_MS;\n const now = options.now ?? Date.now();\n const updatedAtMs =\n typeof parsed.updatedAt === 'string' ? Date.parse(parsed.updatedAt) : Number.NaN;\n\n if (!Number.isFinite(updatedAtMs) || now - updatedAtMs > maxAgeMs) {\n return null;\n }\n\n return parsed.port;\n } catch {\n return null;\n }\n}\n\nexport async function buildPortCandidates(\n options: PortStrategyOptions\n): Promise<PortStrategyResult[]> {\n const {\n defaultPort,\n fixedPort,\n host,\n persistPath = defaultRelayPortPersistPath(),\n rangeEnd,\n } = options;\n\n if (fixedPort !== undefined) {\n return [{ port: fixedPort, wasFixed: true, fromCache: false }];\n }\n\n const cachedPort = await readPersistedPort(persistPath, { expectedHost: host });\n const seen = new Set<number>();\n const candidates: PortStrategyResult[] = [];\n\n const pushCandidate = (port: number, fromCache: boolean): void => {\n if (port < 1 || port > 65535 || seen.has(port)) {\n return;\n }\n seen.add(port);\n candidates.push({ port, wasFixed: false, fromCache });\n };\n\n if (cachedPort !== null && cachedPort >= defaultPort && cachedPort <= rangeEnd) {\n pushCandidate(cachedPort, true);\n }\n\n pushCandidate(defaultPort, false);\n\n for (let port = defaultPort + 1; port <= rangeEnd; port += 1) {\n pushCandidate(port, false);\n }\n\n return candidates;\n}\n","import type { CallToolResult, Tool, ToolAnnotations } from '@modelcontextprotocol/server';\nimport {\n CallToolRequestParamsSchema,\n CallToolResultSchema,\n ToolAnnotationsSchema,\n ToolSchema,\n} from '@modelcontextprotocol/core';\nimport { z } from 'zod/v4';\n\n/**\n * SDK-derived canonical tool schema used internally by the relay.\n */\nexport const NormalizedToolSchema = ToolSchema.extend({\n name: ToolSchema.shape.name.min(1),\n});\n\n/**\n * SDK-derived argument schema for tool invocation payloads.\n */\nexport const RelayInvokeArgsSchema = CallToolRequestParamsSchema.shape.arguments;\n\n/**\n * Default input schema applied when inbound payload omits or provides\n * a non-object input schema.\n */\nexport const DEFAULT_TOOL_INPUT_SCHEMA: Tool['inputSchema'] = {\n type: 'object',\n properties: {},\n};\n\n/** SDK tool schema with a browser-compatible default input schema. */\nexport const InboundToolSchema = NormalizedToolSchema.extend({\n inputSchema: NormalizedToolSchema.shape.inputSchema.optional(),\n});\n\n/**\n * Canonical normalized relay tool shape.\n */\nexport type RelayTool = z.infer<typeof NormalizedToolSchema>;\n\n/**\n * Canonical relay tool annotations shape.\n */\nexport type RelayToolAnnotations = ToolAnnotations;\n\n/**\n * Canonical relay call result shape.\n */\nexport type RelayCallToolResult = CallToolResult;\n\n/**\n * Invocation argument object shape derived from MCP SDK request params.\n */\nexport type RelayInvokeArgs = Exclude<z.infer<typeof RelayInvokeArgsSchema>, undefined>;\n\n/**\n * Applies the browser default input schema and validates the complete SDK tool.\n */\nexport function normalizeInboundTool(inbound: z.infer<typeof InboundToolSchema>): RelayTool {\n return NormalizedToolSchema.parse({\n ...inbound,\n inputSchema: inbound.inputSchema ?? DEFAULT_TOOL_INPUT_SCHEMA,\n });\n}\n\nexport { CallToolRequestParamsSchema, CallToolResultSchema, ToolAnnotationsSchema, ToolSchema };\n","/**\n * Maximum tool name length supported across MCP clients.\n */\nexport const MAX_MCP_TOOL_NAME_LENGTH = 128;\n\n/**\n * Number of tab-id characters appended when disambiguation is required.\n */\nconst TAB_ID_DISAMBIGUATION_LENGTH = 4;\n\n/**\n * Converts arbitrary text into MCP-safe identifier characters.\n */\nexport function sanitizeName(value: string): string {\n return value.replace(/[^a-zA-Z0-9_]/g, '_');\n}\n\n/**\n * Extracts and sanitizes a domain label from an origin or URL.\n */\nexport function extractSanitizedDomain(originOrUrl?: string): string {\n if (!originOrUrl) {\n return 'unknown';\n }\n\n try {\n const parsed = new URL(originOrUrl);\n if (!parsed.hostname) {\n return 'unknown';\n }\n\n const isLocalhost =\n parsed.hostname === 'localhost' ||\n parsed.hostname === '127.0.0.1' ||\n parsed.hostname === '[::1]';\n\n const domain = isLocalhost ? `localhost_${parsed.port || '80'}` : parsed.hostname;\n return sanitizeName(domain);\n } catch {\n return 'unknown';\n }\n}\n\n/**\n * Builds a public tool name for MCP registration.\n *\n * Names can include a short tab-id suffix when multiple tabs publish the same\n * original tool name.\n */\nexport function buildPublicToolName(options: {\n originalToolName: string;\n tabId?: string;\n disambiguate?: boolean;\n}): string {\n const safeName = sanitizeName(options.originalToolName);\n\n if (!options.disambiguate || !options.tabId) {\n return safeName.slice(0, MAX_MCP_TOOL_NAME_LENGTH);\n }\n\n const shortTab = sanitizeName(options.tabId).slice(0, TAB_ID_DISAMBIGUATION_LENGTH);\n const suffix = `_${shortTab}`;\n const available = MAX_MCP_TOOL_NAME_LENGTH - suffix.length;\n return `${safeName.slice(0, available)}${suffix}`;\n}\n","import { buildPublicToolName, MAX_MCP_TOOL_NAME_LENGTH } from './naming.js';\nimport type { RelayTool } from './protocol.js';\nimport type { BrowserHelloMessage } from './schemas.js';\n\n/**\n * Internal metadata tracked for each active browser source.\n */\ninterface SourceMetadata {\n sourceId: string;\n tabId: string;\n // Optional keys, matching the wire shape in RelaySourceInfoSchema: a browser\n // source may omit these entirely rather than send them as undefined.\n origin?: string | undefined;\n url?: string | undefined;\n title?: string | undefined;\n iconUrl?: string | undefined;\n connectedAt: number;\n lastSeenAt: number;\n}\n\n/**\n * Public source metadata exposed by registry APIs.\n */\nexport interface SourceInfo extends SourceMetadata {\n toolCount: number;\n}\n\n/**\n * Aggregated relayed tool metadata across one or more sources.\n *\n * Extends {@link RelayTool} with relay-specific fields so new SDK tool\n * properties are automatically inherited without manual duplication.\n */\nexport interface AggregatedTool extends RelayTool {\n originalName: string;\n sources: SourceInfo[];\n}\n\n/**\n * Internal record linking a source to a concrete tool implementation.\n */\ninterface ToolProvider {\n sourceId: string;\n tabId: string;\n tool: RelayTool;\n publicToolName: string;\n}\n\n/**\n * Invocation target selected by the registry.\n */\nexport interface ResolvedInvocation {\n connectionId: string;\n tool: RelayTool;\n publicToolName: string;\n}\n\n/**\n * Error thrown when a source attempts to register tools before `hello`.\n */\nexport class HelloRequiredError extends Error {\n readonly connectionId: string;\n\n constructor(connectionId: string) {\n super(`Connection ${connectionId} must send hello before tools`);\n this.name = 'HelloRequiredError';\n this.connectionId = connectionId;\n }\n}\n\n/**\n * In-memory source and tool registry used by the relay bridge.\n */\nexport class RelayRegistry {\n private readonly sourceByConnectionId = new Map<string, SourceMetadata>();\n private readonly toolsByConnectionId = new Map<string, ToolProvider[]>();\n private readonly providersByPublicToolName = new Map<string, ToolProvider[]>();\n\n /**\n * @param now Time provider used for recency ordering.\n */\n constructor(private readonly now: () => number = () => Date.now()) {}\n\n /**\n * Upserts source metadata from a browser `hello` message.\n *\n * `sourceId` currently matches `connectionId`, but remains conceptually distinct\n * so stable source identity can be introduced in the future.\n */\n upsertSource(connectionId: string, hello: BrowserHelloMessage): void {\n const now = this.now();\n const existing = this.sourceByConnectionId.get(connectionId);\n\n this.sourceByConnectionId.set(connectionId, {\n sourceId: connectionId,\n tabId: hello.tabId,\n origin: hello.origin ?? existing?.origin,\n url: hello.url ?? existing?.url,\n title: hello.title ?? existing?.title,\n iconUrl: hello.iconUrl ?? existing?.iconUrl,\n connectedAt: existing?.connectedAt ?? now,\n lastSeenAt: now,\n });\n\n if (existing && existing.tabId !== hello.tabId) {\n for (const provider of this.toolsByConnectionId.get(connectionId) ?? []) {\n provider.tabId = hello.tabId;\n }\n this.rebuildPublicNames();\n }\n }\n\n /**\n * Replaces the full tool set for a source connection.\n */\n registerTools(connectionId: string, tools: RelayTool[]): void {\n const source = this.sourceByConnectionId.get(connectionId);\n if (!source) {\n throw new HelloRequiredError(connectionId);\n }\n\n this.touchConnection(connectionId);\n\n const providers: ToolProvider[] = tools.map((tool) => ({\n sourceId: connectionId,\n tabId: source.tabId,\n tool,\n publicToolName: '',\n }));\n\n this.toolsByConnectionId.set(connectionId, providers);\n this.rebuildPublicNames();\n }\n\n /**\n * Removes a source and all of its tool registrations.\n */\n removeConnection(connectionId: string): void {\n this.toolsByConnectionId.delete(connectionId);\n this.sourceByConnectionId.delete(connectionId);\n this.rebuildPublicNames();\n }\n\n /**\n * Marks a source as recently seen without mutating identity metadata.\n */\n touchConnection(connectionId: string): void {\n const source = this.sourceByConnectionId.get(connectionId);\n if (!source) {\n return;\n }\n\n source.lastSeenAt = this.now();\n }\n\n /**\n * Lists active sources that currently publish at least one tool.\n */\n listSources(): SourceInfo[] {\n return Array.from(this.sourceByConnectionId.values())\n .map((source) => this.toSourceInfo(source))\n .filter((source) => source.toolCount > 0)\n .sort((a, b) => this.compareRecency(b.lastSeenAt, a.lastSeenAt, b.sourceId, a.sourceId));\n }\n\n /**\n * Lists aggregated tools ordered by public tool name.\n */\n listTools(): AggregatedTool[] {\n const result: AggregatedTool[] = [];\n\n for (const [publicToolName, providers] of this.providersByPublicToolName.entries()) {\n const rankedProviders = this.sortProvidersByRecency(providers);\n const primaryProvider = rankedProviders[0];\n if (!primaryProvider) {\n continue;\n }\n\n const sources = rankedProviders\n .map((provider) => {\n const source = this.sourceByConnectionId.get(provider.sourceId);\n return source ? this.toSourceInfo(source) : undefined;\n })\n .filter((source): source is SourceInfo => Boolean(source));\n\n result.push({\n ...primaryProvider.tool,\n name: publicToolName,\n originalName: primaryProvider.tool.name,\n sources,\n });\n }\n\n return result.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n /**\n * Resolves a tool invocation to a concrete source provider.\n *\n * Resolution order:\n * 1. `sourceId`: exact source id, then fallback to tab id.\n * 2. `requestTabId`: strict tab id match with no fallback.\n * 3. Default: most recently seen provider.\n */\n resolveInvocation(options: {\n toolName: string;\n sourceId?: string;\n requestTabId?: string;\n }): ResolvedInvocation | null {\n const providers = this.providersByPublicToolName.get(options.toolName);\n if (!providers || providers.length === 0) {\n return null;\n }\n\n const rankedProviders = this.sortProvidersByRecency(providers);\n\n let provider: ToolProvider | undefined;\n if (options.sourceId) {\n provider =\n rankedProviders.find((candidate) => candidate.sourceId === options.sourceId) ??\n rankedProviders.find((candidate) => candidate.tabId === options.sourceId);\n } else if (options.requestTabId) {\n provider = rankedProviders.find((candidate) => candidate.tabId === options.requestTabId);\n } else {\n provider = rankedProviders[0];\n }\n\n if (!provider) {\n return null;\n }\n\n return {\n connectionId: provider.sourceId,\n tool: provider.tool,\n publicToolName: provider.publicToolName,\n };\n }\n\n /**\n * Converts internal source metadata to public source info.\n */\n private toSourceInfo(source: SourceMetadata): SourceInfo {\n return {\n ...source,\n toolCount: this.toolsByConnectionId.get(source.sourceId)?.length ?? 0,\n };\n }\n\n /**\n * Rebuilds public tool names after source set changes.\n *\n * When the same original tool name appears in multiple tabs, names are\n * disambiguated with a short tab suffix.\n */\n private rebuildPublicNames(): void {\n const byOriginalName = new Map<string, ToolProvider[]>();\n for (const providers of this.toolsByConnectionId.values()) {\n for (const provider of providers) {\n const key = provider.tool.name;\n const group = byOriginalName.get(key) ?? [];\n group.push(provider);\n byOriginalName.set(key, group);\n }\n }\n\n this.providersByPublicToolName.clear();\n const allocations: Array<{ candidate: string; key: string; provider: ToolProvider }> = [];\n\n for (const [originalName, providers] of byOriginalName) {\n const uniqueTabIds = new Set(providers.map((p) => p.tabId));\n const disambiguate = uniqueTabIds.size > 1;\n\n for (const provider of providers) {\n allocations.push({\n candidate: buildPublicToolName({\n originalToolName: originalName,\n tabId: provider.tabId,\n disambiguate,\n }),\n key: disambiguate ? `${originalName}\\0${provider.tabId}` : originalName,\n provider,\n });\n }\n }\n\n const nameByKey = new Map<string, string>();\n const usedNames = new Set<string>();\n for (const { candidate, key, provider } of allocations.sort((a, b) =>\n a.key < b.key ? -1 : a.key > b.key ? 1 : 0\n )) {\n let publicToolName = nameByKey.get(key);\n if (!publicToolName) {\n publicToolName = candidate;\n for (let index = 2; usedNames.has(publicToolName); index += 1) {\n const suffix = `_${String(index)}`;\n publicToolName = `${candidate.slice(0, MAX_MCP_TOOL_NAME_LENGTH - suffix.length)}${suffix}`;\n }\n nameByKey.set(key, publicToolName);\n usedNames.add(publicToolName);\n }\n\n provider.publicToolName = publicToolName;\n\n const existing = this.providersByPublicToolName.get(publicToolName) ?? [];\n existing.push(provider);\n this.providersByPublicToolName.set(publicToolName, existing);\n }\n }\n\n /**\n * Returns providers sorted by source recency.\n */\n private sortProvidersByRecency(providers: ToolProvider[]): ToolProvider[] {\n return providers.slice().sort((a, b) => {\n const aSource = this.sourceByConnectionId.get(a.sourceId);\n const bSource = this.sourceByConnectionId.get(b.sourceId);\n const aLastSeen = aSource?.lastSeenAt ?? 0;\n const bLastSeen = bSource?.lastSeenAt ?? 0;\n\n return this.compareRecency(bLastSeen, aLastSeen, b.sourceId, a.sourceId);\n });\n }\n\n /**\n * Compares two source recency tuples and stabilizes order by source id.\n */\n private compareRecency(\n leftLastSeen: number,\n rightLastSeen: number,\n leftId: string,\n rightId: string\n ): number {\n const timeDiff = leftLastSeen - rightLastSeen;\n if (timeDiff !== 0) {\n return timeDiff;\n }\n return leftId.localeCompare(rightId);\n }\n}\n","import { z } from 'zod/v4';\nimport {\n CallToolResultSchema,\n InboundToolSchema,\n NormalizedToolSchema,\n normalizeInboundTool,\n RelayInvokeArgsSchema,\n type RelayTool,\n} from './protocol.js';\n\nfunction normalizeSupportedInboundTools(tools: z.infer<typeof InboundToolSchema>[]): RelayTool[] {\n return tools\n .map(normalizeInboundTool)\n .filter((tool) => tool.execution?.taskSupport !== 'required');\n}\n\nconst BrowserHelloMessageSchema = z.object({\n type: z.literal('hello'),\n tabId: z.string().min(1),\n origin: z.string().optional(),\n url: z.string().optional(),\n title: z.string().optional(),\n iconUrl: z.string().optional(),\n});\n\nconst BrowserToolsListMessageSchema = z.object({\n type: z.literal('tools/list'),\n tools: z.array(InboundToolSchema).transform(normalizeSupportedInboundTools),\n});\n\nconst BrowserToolsChangedMessageSchema = z.object({\n type: z.literal('tools/changed'),\n tools: z.array(InboundToolSchema).transform(normalizeSupportedInboundTools),\n});\n\nconst BrowserToolResultMessageSchema = z.object({\n type: z.literal('result'),\n callId: z.string().min(1),\n result: z.unknown(),\n});\n\nconst BrowserPongMessageSchema = z.object({\n type: z.literal('pong'),\n});\n\n/**\n * Union schema for all browser-to-relay protocol messages.\n */\nexport const BrowserToRelayMessageSchema = z.discriminatedUnion('type', [\n BrowserHelloMessageSchema,\n BrowserToolsListMessageSchema,\n BrowserToolsChangedMessageSchema,\n BrowserToolResultMessageSchema,\n BrowserPongMessageSchema,\n]);\n\n/**\n * Browser-to-relay message payload.\n */\nexport type BrowserToRelayMessage = z.infer<typeof BrowserToRelayMessageSchema>;\n\n/**\n * Browser source bootstrap message.\n */\nexport type BrowserHelloMessage = z.infer<typeof BrowserHelloMessageSchema>;\n\n/**\n * Shared relay identity used for discovery and attach verification.\n */\nexport const RelayDescriptorSchema = z.object({\n host: z.string().min(1),\n instanceId: z.string().min(1),\n label: z.string().min(1).optional(),\n port: z.number().int().min(1).max(65535),\n relayId: z.string().min(1).optional(),\n workspace: z.string().min(1).optional(),\n});\n\n/**\n * Relay identity used for discovery and attach verification.\n */\nexport type RelayDescriptor = z.infer<typeof RelayDescriptorSchema>;\n\n/**\n * Schema for server hello messages sent immediately after WebSocket connect.\n */\nexport const ServerHelloMessageSchema = z.object({\n type: z.literal('server-hello'),\n service: z.literal('webmcp-local-relay'),\n version: z.literal(1),\n ...RelayDescriptorSchema.shape,\n});\n\n/**\n * Server hello payload.\n */\nexport type ServerHelloMessage = z.infer<typeof ServerHelloMessageSchema>;\n\n/**\n * Schema for acceptance of a browser source hello message.\n */\nexport const RelayHelloAcceptedMessageSchema = z.object({\n type: z.literal('hello/accepted'),\n});\n\n/**\n * Acceptance payload for a browser source hello message.\n */\nexport type RelayHelloAcceptedMessage = z.infer<typeof RelayHelloAcceptedMessageSchema>;\n\n/**\n * Schema for rejection of a browser source hello message.\n */\nexport const RelayHelloRejectedMessageSchema = z.object({\n type: z.literal('hello/rejected'),\n reason: z.string().min(1),\n message: z.string().min(1),\n});\n\n/**\n * Rejection payload for a browser source hello message.\n */\nexport type RelayHelloRejectedMessage = z.infer<typeof RelayHelloRejectedMessageSchema>;\n\nconst RelayInvokeMessageSchema = z.object({\n type: z.literal('invoke'),\n callId: z.string().min(1),\n toolName: z.string().min(1),\n args: RelayInvokeArgsSchema,\n});\n\nconst RelayPingMessageSchema = z.object({\n type: z.literal('ping'),\n});\n\nconst RelayReloadMessageSchema = z.object({\n type: z.literal('reload'),\n});\n\n/**\n * Union schema for all relay-to-browser protocol messages.\n */\nexport const RelayToBrowserMessageSchema = z.discriminatedUnion('type', [\n ServerHelloMessageSchema,\n RelayHelloAcceptedMessageSchema,\n RelayHelloRejectedMessageSchema,\n RelayInvokeMessageSchema,\n RelayPingMessageSchema,\n RelayReloadMessageSchema,\n]);\n\n/**\n * Relay-to-browser message payload.\n */\nexport type RelayToBrowserMessage = z.infer<typeof RelayToBrowserMessageSchema>;\n\n/**\n * Schema for source metadata transmitted in relay-to-relay messages.\n */\nexport const RelaySourceInfoSchema = z.object({\n sourceId: z.string(),\n tabId: z.string(),\n origin: z.string().optional(),\n url: z.string().optional(),\n title: z.string().optional(),\n iconUrl: z.string().optional(),\n connectedAt: z.number(),\n lastSeenAt: z.number(),\n toolCount: z.number(),\n});\n\n/**\n * Source metadata transmitted in relay-to-relay messages.\n */\nexport type RelaySourceInfo = z.infer<typeof RelaySourceInfoSchema>;\n\nconst RelayClientHelloSchema = z.object({\n type: z.literal('relay/hello'),\n});\n\nconst RelayClientListToolsSchema = z.object({\n type: z.literal('relay/list-tools'),\n});\n\nconst RelayClientInvokeSchema = z.object({\n type: z.literal('relay/invoke'),\n callId: z.string().min(1),\n toolName: z.string().min(1),\n args: RelayInvokeArgsSchema,\n});\n\n/**\n * Union schema for all relay-client-to-server messages.\n */\nexport const RelayClientToServerMessageSchema = z.discriminatedUnion('type', [\n RelayClientHelloSchema,\n RelayClientListToolsSchema,\n RelayClientInvokeSchema,\n]);\n\n/**\n * Relay client to server message payload.\n */\nexport type RelayClientToServerMessage = z.infer<typeof RelayClientToServerMessageSchema>;\n\nconst RelayToolsPayloadFields = {\n tools: z.array(NormalizedToolSchema),\n sources: z.array(RelaySourceInfoSchema),\n toolSourceMap: z.record(z.string(), z.array(z.string())),\n};\n\nconst RelayServerToolsSchema = z.object({\n type: z.literal('relay/tools'),\n ...RelayToolsPayloadFields,\n});\n\nconst RelayServerResultSchema = z.object({\n type: z.literal('relay/result'),\n callId: z.string().min(1),\n result: CallToolResultSchema,\n});\n\nconst RelayServerToolsChangedSchema = z.object({\n type: z.literal('relay/tools-changed'),\n ...RelayToolsPayloadFields,\n});\n\n/**\n * Union schema for all relay-server-to-client messages.\n */\nexport const RelayServerToClientMessageSchema = z.discriminatedUnion('type', [\n ServerHelloMessageSchema,\n RelayServerToolsSchema,\n RelayServerResultSchema,\n RelayServerToolsChangedSchema,\n]);\n\n/**\n * Relay server to client message payload.\n */\nexport type RelayServerToClientMessage = z.infer<typeof RelayServerToClientMessageSchema>;\n","import { randomUUID } from 'node:crypto';\nimport { EventEmitter } from 'node:events';\n\nimport WebSocket, { WebSocketServer } from 'ws';\nimport {\n buildPortCandidates,\n DEFAULT_RELAY_PORT,\n DEFAULT_RELAY_PORT_RANGE_END,\n defaultRelayPortPersistPath,\n persistPort,\n} from './portStrategy.js';\nimport {\n CallToolResultSchema,\n type RelayCallToolResult,\n type RelayInvokeArgs,\n type RelayTool,\n} from './protocol.js';\nimport { RelayRegistry } from './registry.js';\nimport {\n type BrowserToRelayMessage,\n BrowserToRelayMessageSchema,\n type RelayClientToServerMessage,\n RelayClientToServerMessageSchema,\n type RelayServerToClientMessage,\n RelayServerToClientMessageSchema,\n type RelayHelloAcceptedMessage,\n type RelayHelloRejectedMessage,\n type RelaySourceInfo,\n type RelayToBrowserMessage,\n type ServerHelloMessage,\n} from './schemas.js';\n\nconst RELAY_BROWSER_PROTOCOL = 'webmcp.v1';\nconst RELAY_DISCOVERY_PROTOCOL = 'webmcp-discovery.v1';\nconst RELAY_INTERNAL_PROTOCOL = 'webmcp-relay.v1';\nconst RELAY_SERVER_MESSAGE_TIMEOUT_MS = 750;\nconst HEARTBEAT_INTERVAL_MS = 15_000;\nconst HEARTBEAT_DEAD_THRESHOLD_MS = 25_000;\nconst SUPPORTED_SUBPROTOCOLS = new Set([\n RELAY_BROWSER_PROTOCOL,\n RELAY_DISCOVERY_PROTOCOL,\n RELAY_INTERNAL_PROTOCOL,\n]);\n\n/**\n * In-flight relay invocation waiting for a browser `result` message.\n */\ninterface PendingInvocation {\n connectionId: string;\n timeoutId: ReturnType<typeof setTimeout>;\n resolve: (result: RelayCallToolResult) => void;\n reject: (error: Error) => void;\n}\n\nfunction formatPayloadLimit(bytes: number): string {\n if (bytes >= 1_000_000 && bytes % 1_000_000 === 0) {\n return `${String(bytes / 1_000_000)}MB`;\n }\n return `${String(bytes)} bytes`;\n}\n\n/**\n * Runtime options for {@link RelayBridgeServer}.\n */\nexport interface RelayBridgeServerOptions {\n /**\n * Network interface used by the local WebSocket server.\n * @defaultValue `\"127.0.0.1\"`\n */\n host?: string;\n /**\n * Preferred WebSocket port for browser widget connections.\n * @defaultValue `9333`\n */\n port?: number;\n /**\n * Whether the preferred port came from an explicit CLI/API override.\n * Explicit ports fail if occupied by a non-relay process.\n * @defaultValue `false`\n */\n portExplicitlySet?: boolean;\n /**\n * Inclusive upper bound for automatic port discovery.\n * @defaultValue `9348`\n */\n portRangeEnd?: number;\n /**\n * File path used to cache the last successful relay port.\n * @defaultValue `~/.webmcp/relay-port.json`\n */\n persistPath?: string;\n /**\n * Allowed host page origins verified from browser WebSocket requests.\n * Use `[\"*\"]` to allow all origins.\n *\n * Permissive by default for zero-config developer experience: any browser\n * page can connect and register tools without additional setup. For production\n * or shared machines, restrict to trusted host origins via the\n * `--widget-origin` CLI flag or by passing explicit origins here.\n *\n * @defaultValue `[\"*\"]`\n */\n allowedOrigins?: string[];\n /**\n * Maximum WebSocket payload size in bytes.\n * @defaultValue `10000000`\n */\n maxPayloadBytes?: number;\n /**\n * Timeout used for browser tool invocations.\n * @defaultValue `65000`\n */\n invokeTimeoutMs?: number;\n /**\n * Human-readable relay label reported in discovery handshakes.\n */\n label?: string;\n /**\n * Optional workspace name reported in discovery handshakes.\n */\n workspace?: string;\n /**\n * Stable relay identifier used to select between multiple relays.\n */\n relayId?: string;\n}\n\n/**\n * WebSocket relay between browser widget frames and MCP server calls.\n *\n * Operates in two modes:\n * - **server** (default): Runs a WebSocket server, accepts browser and relay\n * client connections.\n * - **client** (fallback on EADDRINUSE): Connects as a WebSocket client to an\n * existing server relay and proxies tool operations through it.\n */\nexport class RelayBridgeServer extends EventEmitter {\n /**\n * Registry for connected sources and aggregated tool definitions.\n * Only actively used in server mode; remains empty when operating as a client.\n */\n readonly registry: RelayRegistry;\n\n private readonly host: string;\n private readonly preferredPort: number;\n private desiredPort: number;\n private readonly portExplicitlySet: boolean;\n private readonly portRangeEnd: number;\n private readonly persistPath: string;\n private readonly allowedOrigins: string[];\n private readonly maxPayloadBytes: number;\n private readonly invokeTimeoutMs: number;\n private readonly label: string | undefined;\n private readonly workspace: string | undefined;\n private readonly relayId: string | undefined;\n private readonly instanceId: string;\n\n private wss: WebSocketServer | null = null;\n private readonly socketByConnectionId = new Map<string, WebSocket>();\n private readonly requestOriginByConnectionId = new Map<string, string>();\n private readonly pendingInvocations = new Map<string, PendingInvocation>();\n private readonly browserClientConnectionIds = new Set<string>();\n private readonly relayClientConnectionIds = new Set<string>();\n private readonly heartbeatIntervalByConnectionId = new Map<\n string,\n ReturnType<typeof setInterval>\n >();\n private readonly lastPongByConnectionId = new Map<string, number>();\n private readonly onStateChangedPushRelay = () => {\n this.pushToolsToRelayClients();\n };\n\n private _mode: 'server' | 'client' = 'server';\n private clientSocket: WebSocket | null = null;\n private clientReconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private clientReconnectDelay = 500;\n /**\n * Maximum delay for relay-to-relay reconnection backoff in client mode.\n */\n private readonly clientMaxReconnectDelay = 3_000;\n private readonly clientMaxReconnectAttempts = 100;\n private clientReconnectAttempts = 0;\n private readonly clientPendingInvocations = new Map<string, PendingInvocation>();\n private clientTools: RelayTool[] = [];\n private clientSources: RelaySourceInfo[] = [];\n private clientToolSourceMap: Record<string, string[]> = {};\n private stopping = false;\n\n /**\n * Creates a relay bridge server instance.\n */\n constructor(options: RelayBridgeServerOptions = {}, registry?: RelayRegistry) {\n super();\n this.registry = registry ?? new RelayRegistry();\n\n this.host = options.host ?? '127.0.0.1';\n this.preferredPort = options.port ?? DEFAULT_RELAY_PORT;\n this.desiredPort = this.preferredPort;\n this.portExplicitlySet = options.portExplicitlySet ?? false;\n this.portRangeEnd =\n options.portRangeEnd ?? Math.max(DEFAULT_RELAY_PORT_RANGE_END, this.preferredPort);\n this.persistPath = options.persistPath ?? defaultRelayPortPersistPath();\n this.allowedOrigins = options.allowedOrigins ?? ['*'];\n // 10MB default: large enough for typical JSON API responses while\n // still protecting the relay process from unbounded memory growth.\n this.maxPayloadBytes = options.maxPayloadBytes ?? 10_000_000;\n this.invokeTimeoutMs = options.invokeTimeoutMs ?? 65_000;\n this.label = options.label;\n this.workspace = options.workspace;\n this.relayId = options.relayId;\n this.instanceId = randomUUID();\n\n if (\n !Number.isInteger(this.preferredPort) ||\n this.preferredPort < 0 ||\n this.preferredPort > 65535\n ) {\n throw new Error(\n `Invalid port ${this.preferredPort}. Port must be 0 (auto-assign) or between 1 and 65535.`\n );\n }\n if (\n !Number.isInteger(this.portRangeEnd) ||\n this.portRangeEnd < 1 ||\n this.portRangeEnd > 65535 ||\n (this.portRangeEnd < this.preferredPort && this.preferredPort !== 0)\n ) {\n throw new Error(\n `Invalid port range ${this.preferredPort}-${this.portRangeEnd}. rangeEnd must be an integer between the preferred port and 65535.`\n );\n }\n if (!Number.isInteger(this.maxPayloadBytes) || this.maxPayloadBytes <= 0) {\n throw new Error(\n `Invalid maxPayloadBytes ${this.maxPayloadBytes}. Must be a positive integer.`\n );\n }\n if (!Number.isInteger(this.invokeTimeoutMs) || this.invokeTimeoutMs <= 0) {\n throw new Error(\n `Invalid invokeTimeoutMs ${this.invokeTimeoutMs}. Must be a positive integer.`\n );\n }\n }\n\n /**\n * Current operating mode.\n */\n get mode(): 'server' | 'client' {\n return this._mode;\n }\n\n /**\n * Resolved listening port. In client mode this is the port of the server\n * relay being proxied through.\n */\n get port(): number {\n return this.desiredPort;\n }\n\n /**\n * Tools received from the server relay (client mode only).\n * Returns an empty array in server mode.\n */\n listToolsFromRelay(): RelayTool[] {\n return this._mode === 'client' ? [...this.clientTools] : [];\n }\n\n /**\n * Source metadata received from the server relay (client mode only).\n * Returns an empty array in server mode.\n */\n listSourcesFromRelay(): RelaySourceInfo[] {\n return this._mode === 'client' ? [...this.clientSources] : [];\n }\n\n /**\n * Tool-to-source mapping received from the server relay (client mode only).\n * Maps public tool names to arrays of source IDs.\n * Returns an empty record in server mode.\n */\n getToolSourceMapFromRelay(): Record<string, string[]> {\n return this._mode === 'client' ? { ...this.clientToolSourceMap } : {};\n }\n\n /**\n * Starts the bridge. Attempts to bind a WebSocket server (server mode).\n * If a compatible relay already owns a candidate port, joins it in client mode.\n * If a non-relay process owns the port, continues searching the reserved range.\n */\n async start(): Promise<void> {\n if (this.wss || this.clientSocket) {\n return;\n }\n\n this.stopping = false;\n await this.startUsingPortStrategy();\n }\n\n private async startUsingPortStrategy(): Promise<void> {\n if (this.preferredPort === 0) {\n await this.startAsServer(0);\n return;\n }\n\n const candidates = await buildPortCandidates({\n defaultPort: this.preferredPort,\n ...(this.portExplicitlySet ? { fixedPort: this.preferredPort } : {}),\n host: this.host,\n persistPath: this.persistPath,\n rangeEnd: this.portRangeEnd,\n });\n\n for (const candidate of candidates) {\n try {\n await this.startAsServer(candidate.port);\n await this.persistSelectedPort();\n return;\n } catch (err) {\n if (!this.isAddressInUseError(err)) {\n throw err;\n }\n\n const attached = await this.tryAttachToExistingRelay(candidate.port);\n if (attached) {\n await this.persistSelectedPort();\n return;\n }\n\n if (candidate.wasFixed) {\n throw new Error(\n `Port ${candidate.port} is already in use by a non-WebMCP service and cannot be shared.`\n );\n }\n\n process.stderr.write(\n `[webmcp-local-relay] info: port ${candidate.port} is occupied by a non-relay service, trying next port\\n`\n );\n }\n }\n\n throw new Error(\n `No compatible relay port was available in the range ${this.preferredPort}-${this.portRangeEnd}.`\n );\n }\n\n private isAddressInUseError(error: unknown): boolean {\n return (\n error instanceof Error &&\n ('code' in error ? error.code === 'EADDRINUSE' : error.message.includes('EADDRINUSE'))\n );\n }\n\n private async persistSelectedPort(): Promise<void> {\n try {\n await persistPort(this.port, this.persistPath, this.host);\n } catch (error) {\n process.stderr.write(\n `[webmcp-local-relay] warn: could not cache relay port: ${error instanceof Error ? error.message : String(error)}\\n`\n );\n }\n }\n\n private async tryAttachToExistingRelay(port: number): Promise<boolean> {\n try {\n await this.startAsClient(port);\n process.stderr.write(\n `[webmcp-local-relay] info: discovered compatible relay on port ${port}, switching to client mode\\n`\n );\n return true;\n } catch (error) {\n // The caller reports every false here as \"occupied by a non-relay service\". A slow\n // but healthy relay hits the 750ms hello timeout and lands here too, so name the\n // real reason rather than leaving the operator with a wrong hypothesis.\n process.stderr.write(\n `[webmcp-local-relay] info: port ${port} did not complete a relay handshake: ${error instanceof Error ? error.message : String(error)}\\n`\n );\n return false;\n }\n }\n\n /**\n * Stops all relay resources and rejects any pending invocations.\n */\n async stop(): Promise<void> {\n this.stopping = true;\n\n if (this.clientReconnectTimer) {\n clearTimeout(this.clientReconnectTimer);\n this.clientReconnectTimer = null;\n }\n\n if (this._mode === 'client') {\n for (const pending of this.clientPendingInvocations.values()) {\n clearTimeout(pending.timeoutId);\n pending.reject(new Error('Relay client stopped'));\n }\n this.clientPendingInvocations.clear();\n this.clientTools = [];\n this.clientSources = [];\n this.clientToolSourceMap = {};\n\n if (this.clientSocket) {\n try {\n this.clientSocket.close(1000, 'Relay client shutting down');\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: error closing client socket during shutdown: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n this.clientSocket = null;\n }\n return;\n }\n\n this.off('stateChanged', this.onStateChangedPushRelay);\n\n for (const [connectionId, socket] of this.socketByConnectionId) {\n this.registry.removeConnection(connectionId);\n try {\n socket.close(1001, 'Relay shutting down');\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: error closing socket during shutdown: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }\n\n for (const intervalId of this.heartbeatIntervalByConnectionId.values()) {\n clearInterval(intervalId);\n }\n this.heartbeatIntervalByConnectionId.clear();\n this.lastPongByConnectionId.clear();\n\n this.socketByConnectionId.clear();\n this.requestOriginByConnectionId.clear();\n this.browserClientConnectionIds.clear();\n this.relayClientConnectionIds.clear();\n\n for (const pending of this.pendingInvocations.values()) {\n clearTimeout(pending.timeoutId);\n pending.reject(new Error('Relay server stopped before tool invocation completed'));\n }\n this.pendingInvocations.clear();\n\n const wss = this.wss;\n this.wss = null;\n\n if (!wss) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n wss.close((err?: Error) => {\n if (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: WebSocket server close error: ${err.message}\\n`\n );\n }\n resolve();\n });\n });\n }\n\n /**\n * Sends a reload message to a connected browser source.\n * Only supported in server mode.\n */\n reloadSource(connectionId: string): void {\n if (this._mode !== 'server') {\n throw new Error('reloadSource is only supported in server mode');\n }\n const socket = this.socketByConnectionId.get(connectionId);\n if (!socket || socket.readyState !== WebSocket.OPEN) {\n throw new Error(`Source ${connectionId} is not connected`);\n }\n const message: RelayToBrowserMessage = { type: 'reload' };\n try {\n socket.send(JSON.stringify(message));\n } catch (err) {\n throw new Error(`Failed to send reload: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n /**\n * Invokes a tool either locally (server mode) or through the upstream relay\n * (client mode).\n */\n async invokeTool(\n toolName: string,\n args: RelayInvokeArgs,\n options: {\n sourceId?: string;\n requestTabId?: string;\n } = {}\n ): Promise<RelayCallToolResult> {\n if (this._mode === 'client') {\n return this.invokeToolViaRelay(toolName, args);\n }\n\n return this.invokeToolLocally(toolName, args, options);\n }\n\n private async startAsServer(port = this.desiredPort): Promise<void> {\n const wss = await new Promise<WebSocketServer>((resolve, reject) => {\n const server = new WebSocketServer({\n handleProtocols: (protocols) => {\n for (const protocol of protocols) {\n if (SUPPORTED_SUBPROTOCOLS.has(protocol)) {\n return protocol;\n }\n }\n return false;\n },\n host: this.host,\n port,\n maxPayload: this.maxPayloadBytes,\n });\n\n const onListening = () => {\n server.off('error', onError);\n resolve(server);\n };\n const onError = (err: Error) => {\n server.off('listening', onListening);\n reject(err);\n };\n\n server.once('listening', onListening);\n server.once('error', onError);\n });\n\n wss.on('connection', (socket: WebSocket, request) => {\n const requestOrigin = request.headers.origin;\n if (requestOrigin && socket.protocol === RELAY_INTERNAL_PROTOCOL) {\n socket.close(1008, 'Browser connections cannot use the relay protocol');\n return;\n }\n\n const connectionId = randomUUID();\n this.socketByConnectionId.set(connectionId, socket);\n if (requestOrigin) {\n this.requestOriginByConnectionId.set(connectionId, requestOrigin);\n }\n\n socket.on('message', (raw: WebSocket.RawData) => {\n this.onSocketMessage(connectionId, raw);\n });\n\n socket.on('close', (code: number) => {\n this.onSocketClose(connectionId, code);\n });\n\n socket.on('error', (err: Error) => {\n process.stderr.write(\n `[webmcp-local-relay] warn: socket error for connection ${connectionId}: ${err.message}\\n`\n );\n const code =\n 'code' in err && err.code === 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH' ? 1009 : undefined;\n this.onSocketClose(connectionId, code);\n });\n\n try {\n socket.send(JSON.stringify(this.buildServerHello()));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send server hello to connection ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n socket.close(1011, 'Failed to send server hello');\n return;\n }\n\n this.startHeartbeat(connectionId);\n });\n\n wss.on('error', (err: Error) => {\n // Logged, not re-emitted: nothing listens for 'error' on this EventEmitter, and\n // emit('error') without a listener rethrows, killing the relay via uncaughtException.\n process.stderr.write(`[webmcp-local-relay] error: WebSocket server error: ${err.message}\\n`);\n });\n\n this.wss = wss;\n this._mode = 'server';\n\n const address = wss.address();\n if (address && typeof address !== 'string') {\n this.desiredPort = address.port;\n }\n\n this.on('stateChanged', this.onStateChangedPushRelay);\n }\n\n private buildServerHello(): ServerHelloMessage {\n return {\n type: 'server-hello',\n service: 'webmcp-local-relay',\n version: 1,\n host: this.host,\n instanceId: this.instanceId,\n label: this.label,\n port: this.desiredPort,\n relayId: this.relayId ?? this.instanceId,\n workspace: this.workspace,\n };\n }\n\n /**\n * `async` is load-bearing: {@link onRelayClientMessage} consumes this through\n * `.then(onResult, onError)`, so a synchronous throw would escape the socket\n * message handler as an uncaught exception and leave the relay client waiting\n * for a `relay/result` that never arrives.\n */\n private async invokeToolLocally(\n toolName: string,\n args: RelayInvokeArgs,\n options: { sourceId?: string; requestTabId?: string }\n ): Promise<RelayCallToolResult> {\n const resolved = this.registry.resolveInvocation({\n toolName,\n ...(options.sourceId === undefined ? {} : { sourceId: options.sourceId }),\n ...(options.requestTabId === undefined ? {} : { requestTabId: options.requestTabId }),\n });\n\n if (!resolved) {\n throw new Error(`No active browser source provides tool \"${toolName}\"`);\n }\n\n const socket = this.socketByConnectionId.get(resolved.connectionId);\n if (!socket || socket.readyState !== WebSocket.OPEN) {\n throw new Error(\n `Tool source ${resolved.connectionId} disconnected before invocation of \"${toolName}\"`\n );\n }\n\n const callId = randomUUID();\n\n return new Promise<RelayCallToolResult>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n this.pendingInvocations.delete(callId);\n reject(\n new Error(`Invocation for tool \"${toolName}\" timed out after ${this.invokeTimeoutMs}ms`)\n );\n }, this.invokeTimeoutMs);\n\n this.pendingInvocations.set(callId, {\n connectionId: resolved.connectionId,\n timeoutId,\n resolve,\n reject,\n });\n\n const message: RelayToBrowserMessage = {\n type: 'invoke',\n callId,\n toolName: resolved.tool.name,\n args,\n };\n\n try {\n socket.send(JSON.stringify(message));\n } catch (err) {\n clearTimeout(timeoutId);\n this.pendingInvocations.delete(callId);\n reject(\n new Error(\n `Failed to send invocation for tool \"${toolName}\": ${err instanceof Error ? err.message : err}`\n )\n );\n }\n });\n }\n\n /** Handles a raw message according to the negotiated WebSocket protocol. */\n private onSocketMessage(connectionId: string, raw: WebSocket.RawData): void {\n const text = this.rawDataToUtf8(raw);\n\n let parsedJson: unknown;\n try {\n parsedJson = JSON.parse(text);\n } catch (err) {\n const preview = text.length > 200 ? `${text.slice(0, 200)}...` : text;\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid JSON from connection ${connectionId} (${err instanceof Error ? err.message : 'parse error'}): ${preview}\\n`\n );\n return;\n }\n\n const typeField =\n typeof parsedJson === 'object' && parsedJson !== null && 'type' in parsedJson\n ? parsedJson.type\n : undefined;\n const socket = this.socketByConnectionId.get(connectionId);\n if (!socket) {\n return;\n }\n\n if (socket.protocol === RELAY_INTERNAL_PROTOCOL) {\n const relayMsg = RelayClientToServerMessageSchema.safeParse(parsedJson);\n if (relayMsg.success) {\n this.onRelayClientMessage(connectionId, relayMsg.data);\n } else {\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid relay message from ${connectionId}: ${relayMsg.error.message}\\n`\n );\n }\n return;\n }\n\n if (typeof typeField === 'string' && typeField.startsWith('relay/')) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} used the relay protocol without negotiating it\\n`\n );\n socket.close(1008, 'Relay protocol was not negotiated');\n return;\n }\n\n const parsedMessage = BrowserToRelayMessageSchema.safeParse(parsedJson);\n if (!parsedMessage.success) {\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid message from connection ${connectionId} (type=${typeField}): ${parsedMessage.error.message}\\n`\n );\n return;\n }\n\n this.registry.touchConnection(connectionId);\n this.onBrowserClientMessage(connectionId, parsedMessage.data);\n }\n\n private onBrowserClientMessage(connectionId: string, message: BrowserToRelayMessage): void {\n if (message.type !== 'hello' && !this.browserClientConnectionIds.has(connectionId)) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} sent ${message.type} before hello, ignoring\\n`\n );\n return;\n }\n\n switch (message.type) {\n case 'hello':\n try {\n const socket = this.socketByConnectionId.get(connectionId);\n const origin = this.requestOriginByConnectionId.get(connectionId) ?? message.origin;\n if (!this.isHostOriginAllowed(origin)) {\n process.stderr.write(\n `[webmcp-local-relay] warn: rejecting source ${connectionId} with disallowed host origin: ${origin ?? 'missing'}\\n`\n );\n if (socket) {\n this.sendHelloRejected(\n socket,\n {\n type: 'hello/rejected',\n reason: 'host-origin-not-allowed',\n message: 'Host page origin is not allowed by this relay.',\n },\n 1008,\n 'Host origin not allowed'\n );\n }\n break;\n }\n this.registry.upsertSource(connectionId, { ...message, origin });\n this.browserClientConnectionIds.add(connectionId);\n if (socket) {\n this.sendHelloAccepted(socket, { type: 'hello/accepted' });\n }\n this.emit('stateChanged');\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] error: failed to process hello from connection ${connectionId}: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n );\n }\n break;\n\n case 'tools/list':\n case 'tools/changed':\n try {\n this.registry.registerTools(connectionId, message.tools);\n this.emit('stateChanged');\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] error: failed to register tools for connection ${connectionId}: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n );\n this.socketByConnectionId.get(connectionId)?.close(1008, 'Invalid tool list');\n }\n break;\n\n case 'result': {\n const pending = this.pendingInvocations.get(message.callId);\n if (!pending) {\n process.stderr.write(\n `[webmcp-local-relay] warn: received result for unknown callId ${message.callId}\\n`\n );\n break;\n }\n if (pending.connectionId !== connectionId) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} returned result for another source's callId ${message.callId}\\n`\n );\n break;\n }\n\n clearTimeout(pending.timeoutId);\n this.pendingInvocations.delete(message.callId);\n pending.resolve(this.normalizeCallToolResult(message.result));\n break;\n }\n\n case 'pong':\n this.lastPongByConnectionId.set(connectionId, Date.now());\n break;\n }\n }\n\n /**\n * Handles relay-protocol messages from relay client connections.\n */\n private onRelayClientMessage(connectionId: string, message: RelayClientToServerMessage): void {\n if (message.type !== 'relay/hello' && !this.relayClientConnectionIds.has(connectionId)) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} sent ${message.type} before relay/hello, ignoring\\n`\n );\n return;\n }\n\n switch (message.type) {\n case 'relay/hello':\n this.relayClientConnectionIds.add(connectionId);\n break;\n\n case 'relay/list-tools': {\n const response = this.buildRelayToolsPayload('relay/tools');\n const socket = this.socketByConnectionId.get(connectionId);\n if (socket?.readyState === WebSocket.OPEN) {\n try {\n socket.send(JSON.stringify(response));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send relay tools response to ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }\n break;\n }\n\n case 'relay/invoke': {\n const { callId, toolName, args } = message;\n void this.invokeToolLocally(toolName, args ?? {}, {}).then(\n (result) => this.sendRelayResult(connectionId, callId, result),\n (error: unknown) =>\n this.sendRelayResult(connectionId, callId, {\n content: [\n {\n type: 'text',\n text: `Relay invocation failed: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n })\n );\n break;\n }\n }\n }\n\n private sendRelayResult(connectionId: string, callId: string, result: RelayCallToolResult): void {\n const socket = this.socketByConnectionId.get(connectionId);\n if (socket?.readyState !== WebSocket.OPEN) {\n process.stderr.write(\n `[webmcp-local-relay] warn: relay client ${connectionId} disconnected before result for callId ${callId} could be delivered\\n`\n );\n return;\n }\n\n try {\n socket.send(JSON.stringify({ type: 'relay/result', callId, result }));\n } catch (error) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send relay result to ${connectionId}: ${error instanceof Error ? error.message : String(error)}\\n`\n );\n }\n }\n\n /**\n * Pushes current tool state to all connected relay clients.\n */\n private pushToolsToRelayClients(): void {\n if (this.relayClientConnectionIds.size === 0) {\n return;\n }\n\n const message = this.buildRelayToolsPayload('relay/tools-changed');\n const payload = JSON.stringify(message);\n\n for (const connectionId of this.relayClientConnectionIds) {\n const socket = this.socketByConnectionId.get(connectionId);\n if (socket?.readyState === WebSocket.OPEN) {\n try {\n socket.send(payload);\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to push tool update to relay client ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }\n }\n }\n\n /**\n * Handles source disconnection and rejects in-flight calls owned by that source.\n *\n * Guarded against double-call: both 'error' and 'close' events fire on\n * socket failure, but cleanup (and the stateChanged emission) runs only once.\n */\n private onSocketClose(connectionId: string, code?: number): void {\n // Guard against double invocation: ws fires both 'error' and 'close' for\n // maxPayload violations. The second call is a no-op.\n if (!this.socketByConnectionId.has(connectionId)) return;\n\n this.stopHeartbeat(connectionId);\n this.requestOriginByConnectionId.delete(connectionId);\n this.browserClientConnectionIds.delete(connectionId);\n this.relayClientConnectionIds.delete(connectionId);\n this.registry.removeConnection(connectionId);\n this.socketByConnectionId.delete(connectionId);\n this.emit('stateChanged');\n\n // WS close code 1009 = \"Message Too Big\": the browser sent a response\n // that exceeded maxPayloadBytes. Surface a clear error instead of letting\n // the invocation time out silently after invokeTimeoutMs.\n const isPayloadExceeded = code === 1009;\n\n for (const [callId, pending] of this.pendingInvocations.entries()) {\n if (pending.connectionId !== connectionId) {\n continue;\n }\n\n clearTimeout(pending.timeoutId);\n this.pendingInvocations.delete(callId);\n\n if (isPayloadExceeded) {\n pending.reject(\n new Error(\n `Tool result exceeded maximum payload size (${formatPayloadLimit(this.maxPayloadBytes)}). Use --max-payload to increase the limit.`\n )\n );\n } else {\n pending.reject(new Error(`Tool source ${connectionId} disconnected during invocation`));\n }\n }\n }\n\n private startHeartbeat(connectionId: string): void {\n this.lastPongByConnectionId.set(connectionId, Date.now());\n\n const intervalId = setInterval(() => {\n const socket = this.socketByConnectionId.get(connectionId);\n if (!socket || socket.readyState !== WebSocket.OPEN) {\n this.stopHeartbeat(connectionId);\n return;\n }\n\n // Skip heartbeat for relay client connections; they have their own reconnect logic.\n if (this.relayClientConnectionIds.has(connectionId)) {\n return;\n }\n\n const lastPong = this.lastPongByConnectionId.get(connectionId) ?? 0;\n if (Date.now() - lastPong > HEARTBEAT_DEAD_THRESHOLD_MS) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} missed heartbeat, closing\\n`\n );\n this.stopHeartbeat(connectionId);\n socket.close(1001, 'Heartbeat timeout');\n return;\n }\n\n try {\n socket.send(JSON.stringify({ type: 'ping' }));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send heartbeat ping to ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }, HEARTBEAT_INTERVAL_MS);\n\n this.heartbeatIntervalByConnectionId.set(connectionId, intervalId);\n }\n\n private stopHeartbeat(connectionId: string): void {\n const intervalId = this.heartbeatIntervalByConnectionId.get(connectionId);\n if (intervalId !== undefined) {\n clearInterval(intervalId);\n this.heartbeatIntervalByConnectionId.delete(connectionId);\n }\n this.lastPongByConnectionId.delete(connectionId);\n }\n\n private async startAsClient(port = this.desiredPort): Promise<void> {\n this.stopping = false;\n\n const previousMode = this._mode;\n const previousPort = this.desiredPort;\n this._mode = 'client';\n this.desiredPort = port;\n\n try {\n const { bufferedMessages, socket } = await this.connectToRelayServer(port);\n this.clientSocket = socket;\n this.clientReconnectDelay = 500;\n this.clientReconnectAttempts = 0;\n this.setupClientHandlers(socket, bufferedMessages);\n } catch (error) {\n this._mode = previousMode;\n this.desiredPort = previousPort;\n throw error;\n }\n }\n\n private async connectToRelayServer(\n port: number\n ): Promise<{ bufferedMessages: RelayServerToClientMessage[]; socket: WebSocket }> {\n return new Promise((resolve, reject) => {\n const wsUrl = `ws://${this.host}:${port}`;\n const ws = new WebSocket(wsUrl, RELAY_INTERNAL_PROTOCOL);\n const bufferedMessages: RelayServerToClientMessage[] = [];\n\n const cleanup = () => {\n clearTimeout(timeoutId);\n ws.off('close', onCloseBeforeReady);\n ws.off('error', onErrorBeforeReady);\n ws.off('message', onMessageBeforeReady);\n ws.off('open', onOpen);\n };\n\n const rejectWith = (error: Error) => {\n cleanup();\n ws.once('error', () => {\n // Ignore late socket errors from ports that failed relay verification.\n });\n ws.terminate();\n reject(error);\n };\n\n const finish = () => {\n cleanup();\n resolve({ bufferedMessages, socket: ws });\n };\n\n const onOpen = () => {\n try {\n this.sendRelayClientHandshake(ws);\n } catch (err) {\n rejectWith(\n new Error(\n `Failed to send handshake to relay server at ${wsUrl}: ${err instanceof Error ? err.message : String(err)}`\n )\n );\n }\n };\n\n const onErrorBeforeReady = (err: Error) => {\n rejectWith(new Error(`Failed to connect to relay server at ${wsUrl}: ${err.message}`));\n };\n\n const onCloseBeforeReady = () => {\n rejectWith(new Error(`Connection to ${wsUrl} closed before relay verification completed`));\n };\n\n const onMessageBeforeReady = (raw: WebSocket.RawData) => {\n const message = this.parseRelayServerMessage(raw);\n if (!message) {\n rejectWith(new Error(`Received a non-relay response while probing ${wsUrl}`));\n return;\n }\n\n bufferedMessages.push(message);\n\n if (message.type === 'server-hello') {\n if (message.service !== 'webmcp-local-relay') {\n rejectWith(new Error(`Unexpected relay service \"${message.service}\" at ${wsUrl}`));\n return;\n }\n finish();\n return;\n }\n\n if (message.type === 'relay/tools' || message.type === 'relay/tools-changed') {\n finish();\n }\n };\n\n const timeoutId = setTimeout(() => {\n rejectWith(new Error(`Timed out waiting for relay hello from ${wsUrl}`));\n }, RELAY_SERVER_MESSAGE_TIMEOUT_MS);\n\n ws.once('open', onOpen);\n ws.once('error', onErrorBeforeReady);\n ws.once('close', onCloseBeforeReady);\n ws.on('message', onMessageBeforeReady);\n });\n }\n\n private setupClientHandlers(\n ws: WebSocket,\n bufferedMessages: RelayServerToClientMessage[] = []\n ): void {\n for (const message of bufferedMessages) {\n this.processRelayServerMessage(message);\n }\n\n ws.on('message', (raw: WebSocket.RawData) => {\n const message = this.parseRelayServerMessage(raw);\n if (!message) {\n return;\n }\n\n this.processRelayServerMessage(message);\n });\n\n ws.on('close', () => {\n this.clientSocket = null;\n for (const [callId, pending] of this.clientPendingInvocations) {\n clearTimeout(pending.timeoutId);\n this.clientPendingInvocations.delete(callId);\n pending.reject(new Error('Relay server connection lost during invocation'));\n }\n\n this.clientTools = [];\n this.clientSources = [];\n this.clientToolSourceMap = {};\n this.emit('stateChanged');\n\n if (!this.stopping) {\n this.scheduleReconnect();\n }\n });\n\n ws.on('error', (err: Error) => {\n process.stderr.write(\n `[webmcp-local-relay] warn: relay client socket error: ${err.message}\\n`\n );\n });\n }\n\n private sendRelayClientHandshake(ws: WebSocket): void {\n ws.send(JSON.stringify({ type: 'relay/hello' }));\n ws.send(JSON.stringify({ type: 'relay/list-tools' }));\n }\n\n private parseRelayServerMessage(raw: WebSocket.RawData): RelayServerToClientMessage | null {\n const text = this.rawDataToUtf8(raw);\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (err) {\n const preview = text.length > 200 ? `${text.slice(0, 200)}...` : text;\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid JSON from relay server (${err instanceof Error ? err.message : 'parse error'}): ${preview}\\n`\n );\n return null;\n }\n\n const message = RelayServerToClientMessageSchema.safeParse(parsed);\n if (!message.success) {\n const typeField =\n typeof parsed === 'object' && parsed !== null && 'type' in parsed ? parsed.type : 'unknown';\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid relay server message (type=${typeField}): ${message.error.message}\\n`\n );\n return null;\n }\n\n return message.data;\n }\n\n private processRelayServerMessage(message: RelayServerToClientMessage): void {\n switch (message.type) {\n case 'server-hello':\n break;\n\n case 'relay/tools':\n case 'relay/tools-changed':\n this.clientTools = message.tools;\n this.clientSources = message.sources;\n this.clientToolSourceMap = message.toolSourceMap;\n this.emit('stateChanged');\n break;\n\n case 'relay/result': {\n const pending = this.clientPendingInvocations.get(message.callId);\n if (!pending) {\n process.stderr.write(\n `[webmcp-local-relay] warn: received relay result for unknown callId ${message.callId}\\n`\n );\n break;\n }\n clearTimeout(pending.timeoutId);\n this.clientPendingInvocations.delete(message.callId);\n pending.resolve(message.result);\n break;\n }\n }\n }\n\n private scheduleReconnect(): void {\n if (this.clientReconnectTimer || this.stopping) {\n return;\n }\n\n this.clientReconnectAttempts++;\n if (this.clientReconnectAttempts >= this.clientMaxReconnectAttempts) {\n process.stderr.write(\n `[webmcp-local-relay] error: giving up reconnection after ${this.clientReconnectAttempts} attempts\\n`\n );\n return;\n }\n\n const delay = this.clientReconnectDelay;\n this.clientReconnectDelay = Math.min(\n this.clientReconnectDelay * 1.5,\n this.clientMaxReconnectDelay\n );\n\n this.clientReconnectTimer = setTimeout(() => {\n this.clientReconnectTimer = null;\n if (this.stopping) {\n return;\n }\n\n void this.reconnectWithModePromotion();\n }, delay);\n }\n\n /**\n * Attempts to promote from client to server mode when reconnecting.\n * Re-runs the same attach-or-bind strategy used during startup.\n */\n private async reconnectWithModePromotion(): Promise<void> {\n try {\n await this.startUsingPortStrategy();\n this.clientReconnectDelay = 500;\n this.clientReconnectAttempts = 0;\n if (this._mode === 'server') {\n process.stderr.write('[webmcp-local-relay] info: promoted from client to server mode\\n');\n this.emit('stateChanged');\n }\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: reconnection failed: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n if (!this.stopping) {\n this.scheduleReconnect();\n }\n }\n }\n\n private async invokeToolViaRelay(\n toolName: string,\n args: RelayInvokeArgs\n ): Promise<RelayCallToolResult> {\n if (!this.clientSocket || this.clientSocket.readyState !== WebSocket.OPEN) {\n throw new Error('Not connected to relay server');\n }\n\n const callId = randomUUID();\n const socket = this.clientSocket;\n\n return new Promise<RelayCallToolResult>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n this.clientPendingInvocations.delete(callId);\n reject(\n new Error(\n `Proxied invocation for tool \"${toolName}\" timed out after ${this.invokeTimeoutMs}ms`\n )\n );\n }, this.invokeTimeoutMs);\n\n this.clientPendingInvocations.set(callId, {\n connectionId: 'relay-server',\n timeoutId,\n resolve,\n reject,\n });\n\n const message: RelayClientToServerMessage = {\n type: 'relay/invoke',\n callId,\n toolName,\n args,\n };\n\n try {\n socket.send(JSON.stringify(message));\n } catch (err) {\n clearTimeout(timeoutId);\n this.clientPendingInvocations.delete(callId);\n reject(\n new Error(`Failed to send relay invocation: ${err instanceof Error ? err.message : err}`)\n );\n }\n });\n }\n\n /**\n * Builds the complete relay response payload including tools and source metadata.\n */\n private buildRelayToolsPayload(\n type: 'relay/tools' | 'relay/tools-changed'\n ): RelayServerToClientMessage {\n const tools = this.registry.listTools();\n const sources = this.registry.listSources();\n const toolSourceMap: Record<string, string[]> = {};\n\n for (const tool of tools) {\n toolSourceMap[tool.name] = tool.sources.map((s) => s.sourceId);\n }\n\n return {\n type,\n tools: tools.map(({ originalName: _originalName, sources: _sources, ...tool }) => tool),\n sources,\n toolSourceMap,\n };\n }\n\n private isHostOriginAllowed(origin: string | undefined): boolean {\n if (this.allowedOrigins.includes('*')) {\n return true;\n }\n\n if (!origin) {\n return false;\n }\n\n return this.allowedOrigins.includes(origin);\n }\n\n private sendHelloAccepted(socket: WebSocket, message: RelayHelloAcceptedMessage): void {\n try {\n socket.send(JSON.stringify(message));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send hello acceptance: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n socket.close(1011, 'Failed to send hello acceptance');\n }\n }\n\n private sendHelloRejected(\n socket: WebSocket,\n message: RelayHelloRejectedMessage,\n closeCode: number,\n closeReason: string\n ): void {\n try {\n socket.send(JSON.stringify(message), () => {\n try {\n socket.close(closeCode, closeReason);\n } catch {\n // Ignore close failures after a rejection send attempt.\n }\n });\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send hello rejection: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n socket.close(closeCode, closeReason);\n }\n }\n\n /**\n * Validates browser tool results against CallToolResultSchema.\n * Non-conforming payloads are wrapped as error results with diagnostic text.\n */\n private normalizeCallToolResult(result: unknown): RelayCallToolResult {\n const parsed = CallToolResultSchema.safeParse(result);\n if (parsed.success) {\n return parsed.data;\n }\n\n const preview = JSON.stringify(result)?.slice(0, 500) ?? 'undefined';\n process.stderr.write(\n `[webmcp-local-relay] warn: tool returned invalid CallToolResult (${parsed.error.message}), wrapping as error: ${preview}\\n`\n );\n\n return {\n content: [\n {\n type: 'text',\n text: `Tool returned an invalid result (expected {content: [...]}): ${preview}`,\n },\n ],\n isError: true,\n };\n }\n\n /**\n * Converts WebSocket raw data variants to a UTF-8 string payload.\n */\n private rawDataToUtf8(raw: WebSocket.RawData): string {\n if (Array.isArray(raw)) return Buffer.concat(raw).toString('utf8');\n if (Buffer.isBuffer(raw)) return raw.toString('utf8');\n return Buffer.from(raw).toString('utf8');\n }\n}\n","import { parseArgs } from 'node:util';\n\nimport { DEFAULT_RELAY_PORT } from './portStrategy.js';\n\n/**\n * Parsed CLI options for relay startup.\n */\nexport interface CliOptions {\n help: boolean;\n host: string;\n port: number;\n portExplicitlySet: boolean;\n allowedOrigins: string[];\n label?: string;\n workspace?: string;\n relayId?: string;\n maxPayloadBytes?: number;\n invokeTimeoutMs?: number;\n}\n\nfunction parsePositiveInteger(name: string, value: string | undefined): number | undefined {\n if (value === undefined) return undefined;\n const parsed = Number(value);\n if (!Number.isInteger(parsed) || parsed <= 0) {\n throw new Error(`Invalid ${name} \"${value}\". Must be a positive integer.`);\n }\n return parsed;\n}\n\n/**\n * Parses supported CLI flags for relay startup.\n */\nexport function parseCliOptions(argv: string[]): CliOptions {\n const { values } = parseArgs({\n args: argv,\n options: {\n help: { type: 'boolean', short: 'h' },\n host: { type: 'string', short: 'H' },\n 'allowed-origin': { type: 'string' },\n 'invoke-timeout': { type: 'string' },\n label: { type: 'string' },\n 'max-payload': { type: 'string' },\n port: { type: 'string', short: 'p' },\n 'relay-id': { type: 'string' },\n 'widget-origin': { type: 'string' },\n workspace: { type: 'string' },\n 'ws-origin': { type: 'string' },\n },\n strict: true,\n });\n\n const port = values.port === undefined ? DEFAULT_RELAY_PORT : Number(values.port);\n if (!Number.isInteger(port) || port <= 0 || port > 65535) {\n throw new Error(`Invalid port \"${values.port}\". Port must be an integer between 1 and 65535.`);\n }\n\n const invokeTimeoutMs = parsePositiveInteger('invoke-timeout', values['invoke-timeout']);\n const maxPayloadBytes = parsePositiveInteger('max-payload', values['max-payload']);\n\n const allowedOrigins = (\n values['widget-origin'] ??\n values['allowed-origin'] ??\n values['ws-origin']\n )\n ?.split(',')\n .map((origin) => origin.trim())\n .filter(Boolean) ?? ['*'];\n if (allowedOrigins.length === 0) {\n throw new Error('--widget-origin must include at least one origin.');\n }\n\n return {\n allowedOrigins,\n help: values.help ?? false,\n host: values.host ?? '127.0.0.1',\n port,\n portExplicitlySet: values.port !== undefined,\n ...(values.label === undefined ? {} : { label: values.label }),\n ...(invokeTimeoutMs === undefined ? {} : { invokeTimeoutMs }),\n ...(maxPayloadBytes === undefined ? {} : { maxPayloadBytes }),\n ...(values['relay-id'] === undefined ? {} : { relayId: values['relay-id'] }),\n ...(values.workspace === undefined ? {} : { workspace: values.workspace }),\n };\n}\n\n/**\n * Prints CLI usage to stderr.\n */\nexport function printHelp(): void {\n process.stderr.write(\n [\n 'webmcp-local-relay',\n '',\n 'Usage:',\n ' webmcp-local-relay [--host 127.0.0.1] [--port 9333] [--widget-origin https://myapp.example.com]',\n '',\n 'Options:',\n ' --host, -H Bind host for local websocket relay (default: 127.0.0.1)',\n ' --port, -p Preferred root port for the local relay cluster (default: 9333)',\n ' --widget-origin Allowed host page origin(s), comma-separated (default: *)',\n ' --allowed-origin Deprecated alias for --widget-origin',\n ' --ws-origin Deprecated alias for --widget-origin',\n ' --label Human-readable relay label reported during discovery',\n ' --workspace Optional workspace name reported during discovery',\n ' --relay-id Stable relay identifier reported during discovery',\n ' --invoke-timeout Browser tool invocation timeout in milliseconds (default: 65000)',\n ' --max-payload Maximum WebSocket payload size in bytes (default: 10000000)',\n ' --help, -h Show help',\n '',\n ].join('\\n')\n );\n}\n","import { execFile } from 'node:child_process';\n\nimport {\n fromJsonSchema,\n type JsonSchemaType,\n McpServer,\n type RegisteredTool,\n type Transport,\n} from '@modelcontextprotocol/server';\nimport { StdioServerTransport } from '@modelcontextprotocol/server/stdio';\nimport { z } from 'zod/v4';\n\nimport { RelayBridgeServer, type RelayBridgeServerOptions } from './bridgeServer.js';\nimport type { AggregatedTool, SourceInfo } from './registry.js';\n\n/**\n * Base options shared by all {@link LocalRelayMcpServer} configurations.\n */\ninterface LocalRelayMcpServerBaseOptions {\n /**\n * MCP server name reported during initialization.\n */\n serverName?: string;\n /**\n * MCP server version reported during initialization.\n */\n serverVersion?: string;\n}\n\n/**\n * Construction options for {@link LocalRelayMcpServer}.\n *\n * Provide either an existing `bridge` instance OR `bridgeOptions` to create\n * one internally — not both.\n */\nexport type LocalRelayMcpServerOptions = LocalRelayMcpServerBaseOptions &\n (\n | { bridge: RelayBridgeServer; bridgeOptions?: never }\n | { bridge?: never; bridgeOptions?: RelayBridgeServerOptions }\n );\n\n/**\n * MCP server facade that exposes browser-relayed tools over MCP transport.\n */\nexport class LocalRelayMcpServer {\n /**\n * Underlying WebSocket bridge used for browser communication.\n */\n readonly bridge: RelayBridgeServer;\n\n private readonly mcpServer: McpServer;\n private readonly dynamicTools = new Map<string, { handle: RegisteredTool; signature: string }>();\n\n private syncDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n private connected = false;\n\n /**\n * Creates a local relay MCP server with static and dynamic tool registration.\n */\n constructor(options: LocalRelayMcpServerOptions = {}) {\n this.bridge = options.bridge ?? new RelayBridgeServer(options.bridgeOptions);\n\n this.mcpServer = new McpServer({\n name: options.serverName ?? 'webmcp-local-relay',\n version: options.serverVersion ?? '0.0.0',\n });\n\n this.bridge.on('stateChanged', () => {\n this.debouncedSyncDynamicTools();\n });\n\n this.registerStaticTools();\n }\n\n /**\n * Starts the browser bridge and synchronizes dynamic MCP tools.\n */\n async start(): Promise<void> {\n await this.bridge.start();\n this.syncDynamicTools();\n }\n\n /**\n * Connects the MCP server to a transport.\n *\n * This may be called exactly once per instance lifecycle.\n */\n async connect(transport: Transport): Promise<void> {\n if (this.connected) {\n throw new Error('MCP server transport already connected');\n }\n\n await this.mcpServer.connect(transport);\n this.connected = true;\n\n this.mcpServer.server.onerror = (error) => {\n process.stderr.write(\n `[webmcp-local-relay] error: MCP protocol error: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}\\n`\n );\n };\n }\n\n /**\n * Convenience helper that connects the server over stdio transport.\n */\n async startStdio(): Promise<void> {\n await this.connect(new StdioServerTransport());\n }\n\n /**\n * Stops MCP transport and bridge resources.\n */\n async stop(): Promise<void> {\n this.connected = false;\n\n if (this.syncDebounceTimer) {\n clearTimeout(this.syncDebounceTimer);\n this.syncDebounceTimer = null;\n }\n\n let mcpCloseError: unknown;\n try {\n await this.mcpServer.close();\n } catch (err) {\n mcpCloseError = err;\n process.stderr.write(\n `[webmcp-local-relay] error: failed to close MCP server: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n );\n }\n await this.bridge.stop();\n if (mcpCloseError) {\n process.stderr.write(\n '[webmcp-local-relay] warn: shutdown completed with errors (MCP server close failed)\\n'\n );\n }\n }\n\n /**\n * Returns dynamic tool names currently registered in MCP.\n */\n listDynamicToolNames(): string[] {\n return Array.from(this.dynamicTools.keys()).sort();\n }\n\n /**\n * Registers built-in management tools exposed by the relay.\n */\n private registerStaticTools(): void {\n this.mcpServer.registerTool(\n 'webmcp_list_sources',\n {\n description: 'List connected browser tool sources and their metadata.',\n inputSchema: z.object({}),\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n },\n },\n async () => {\n const clientMode = this.bridge.mode === 'client';\n const sources = clientMode\n ? this.bridge.listSourcesFromRelay()\n : this.bridge.registry.listSources();\n const info = {\n ...(clientMode ? { mode: 'client' as const } : {}),\n count: sources.length,\n sources,\n };\n return {\n content: [{ type: 'text', text: JSON.stringify(info, null, 2) }],\n structuredContent: info,\n };\n }\n );\n\n this.mcpServer.registerTool(\n 'webmcp_list_tools',\n {\n description:\n 'List WebMCP tools available from connected browser sources. Returns tool definitions including name, description, input schema, and source info.',\n inputSchema: z.object({}),\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n },\n },\n async () => {\n const tools = this.listAggregatedTools();\n const info = { count: tools.length, tools };\n return {\n content: [{ type: 'text', text: JSON.stringify(info, null, 2) }],\n structuredContent: info,\n };\n }\n );\n\n this.mcpServer.registerTool(\n 'webmcp_open_page',\n {\n description:\n \"Open a URL in the user's default browser, or refresh a connected source page. Use to launch WebMCP-enabled pages or reload stale connections.\",\n inputSchema: z.object({\n url: z.string().describe('URL to open or match for refresh.'),\n refresh: z\n .boolean()\n .optional()\n .describe(\n 'If true, refresh the connected source matching this URL instead of opening a new tab.'\n ),\n }),\n annotations: { readOnlyHint: false },\n },\n async ({ url, refresh }) => {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return {\n content: [{ type: 'text' as const, text: `Invalid URL: ${url}` }],\n isError: true,\n };\n }\n\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Only http: and https: URLs are allowed. Got: ${parsed.protocol}`,\n },\n ],\n isError: true,\n };\n }\n\n const existing =\n this.bridge.mode === 'server'\n ? this.bridge.registry\n .listSources()\n .find((source) => source.url && URL.parse(source.url)?.origin === parsed.origin)\n : undefined;\n\n if (refresh) {\n if (this.bridge.mode === 'client') {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Refresh is not supported in client mode. Only the server relay can reload sources.',\n },\n ],\n isError: true,\n };\n }\n\n if (!existing) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `No connected source matches origin ${parsed.origin}. The page may not be open or connected.`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n this.bridge.reloadSource(existing.sourceId);\n return {\n content: [\n {\n type: 'text' as const,\n text: `Reload sent to source ${existing.sourceId} (${existing.url ?? existing.origin}).`,\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to reload source: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n try {\n await this.openInBrowser(url);\n } catch (err) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to open browser: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n\n if (existing) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Opened ${url} in the default browser. Note: a source from ${existing.url ?? existing.origin} is already connected.`,\n },\n ],\n };\n }\n\n return {\n content: [{ type: 'text' as const, text: `Opened ${url} in the default browser.` }],\n };\n }\n );\n }\n\n /**\n * Opens a URL in the user's default browser using the platform open command.\n *\n * Passes the re-serialized `URL.href` directly to the platform launcher.\n * No shell or command-language string is involved.\n */\n private openInBrowser(url: string): Promise<void> {\n const safeUrl = new URL(url).href;\n const command =\n process.platform === 'darwin'\n ? 'open'\n : process.platform === 'win32'\n ? 'explorer.exe'\n : 'xdg-open';\n return new Promise((resolve, reject) => {\n execFile(command, [safeUrl], (err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n /**\n * Debounces rapid stateChanged events into a single sync pass.\n *\n * Browser reconnections emit multiple stateChanged events in quick\n * succession (hello, tools/list). Without debouncing, each triggers a\n * full sync that removes and re-registers tools — sending per-operation\n * notifications through the stdio transport and overwhelming the agent.\n */\n private debouncedSyncDynamicTools(): void {\n if (this.syncDebounceTimer) {\n return;\n }\n\n this.syncDebounceTimer = setTimeout(() => {\n this.syncDebounceTimer = null;\n try {\n this.syncDynamicTools();\n } catch (err) {\n const details = err instanceof Error ? (err.stack ?? err.message) : String(err);\n process.stderr.write(\n `[webmcp-local-relay] error: failed to sync dynamic tools: ${details}\\n`\n );\n }\n }, 16);\n }\n\n /**\n * Returns the current aggregated tool list, dispatching based on bridge mode.\n */\n private listAggregatedTools(): AggregatedTool[] {\n return this.bridge.mode === 'client'\n ? this.buildAggregatedToolsFromRelay()\n : this.bridge.registry.listTools();\n }\n\n /**\n * Converts relay client tool descriptors to aggregated tool shape.\n * Populates per-tool source metadata from the relay's tool-source mapping.\n */\n private buildAggregatedToolsFromRelay(): AggregatedTool[] {\n const toolSourceMap = this.bridge.getToolSourceMapFromRelay();\n const allSources = this.bridge.listSourcesFromRelay();\n const sourceById = new Map(allSources.map((s) => [s.sourceId, s]));\n\n return this.bridge\n .listToolsFromRelay()\n .map((tool) => {\n const sourceIds = toolSourceMap[tool.name] ?? [];\n const sources: SourceInfo[] = [];\n for (const id of sourceIds) {\n const source = sourceById.get(id);\n if (source) {\n sources.push(source);\n } else {\n process.stderr.write(\n `[webmcp-local-relay] warn: tool \"${tool.name}\" references unknown sourceId \"${id}\"\\n`\n );\n }\n }\n\n return {\n ...tool,\n originalName: tool.name,\n sources,\n };\n })\n .sort((left, right) => left.name.localeCompare(right.name));\n }\n\n /**\n * Applies current aggregated tool state to MCP dynamic registrations.\n */\n private syncDynamicTools(): void {\n const tools = this.listAggregatedTools();\n const nextNames = new Set(tools.map((tool) => tool.name));\n\n for (const [name, registration] of this.dynamicTools) {\n if (nextNames.has(name)) {\n continue;\n }\n\n registration.handle.remove();\n this.dynamicTools.delete(name);\n }\n\n for (const tool of tools) {\n const signature = this.toolSignature(tool);\n const current = this.dynamicTools.get(tool.name);\n\n if (current?.signature === signature) {\n continue;\n }\n\n if (current) {\n current.handle.remove();\n this.dynamicTools.delete(tool.name);\n }\n\n try {\n const handle = this.registerDynamicTool(tool);\n this.dynamicTools.set(tool.name, { handle, signature });\n } catch (err) {\n const details = err instanceof Error ? (err.stack ?? err.message) : String(err);\n process.stderr.write(\n `[webmcp-local-relay] warn: skipped dynamic tool \"${tool.name}\" because its schema could not be compiled: ${details}\\n`\n );\n }\n }\n }\n\n /**\n * Registers a single dynamic tool and returns a removal handle.\n */\n private registerDynamicTool(tool: AggregatedTool): RegisteredTool {\n // `AggregatedTool` has already passed the SDK's ToolSchema validation.\n // JsonSchemaType's exact optional properties are structurally narrower\n // than the protocol Tool type even though both describe JSON Schema.\n const inputSchema = fromJsonSchema<Record<string, unknown>>(tool.inputSchema as JsonSchemaType);\n const outputSchema = tool.outputSchema\n ? fromJsonSchema(tool.outputSchema as JsonSchemaType)\n : undefined;\n\n return this.mcpServer.registerTool(\n tool.name,\n {\n ...(tool.title !== undefined ? { title: tool.title } : {}),\n description: this.dynamicToolDescription(tool),\n inputSchema,\n ...(outputSchema ? { outputSchema } : {}),\n ...(tool.annotations ? { annotations: tool.annotations } : {}),\n ...(tool.icons ? { icons: tool.icons } : {}),\n ...(tool._meta ? { _meta: tool._meta } : {}),\n },\n async (args: Record<string, unknown>) => {\n try {\n return await this.bridge.invokeTool(tool.name, args);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const details = err instanceof Error ? (err.stack ?? err.message) : String(err);\n process.stderr.write(\n `[webmcp-local-relay] error: dynamic tool \"${tool.name}\" invocation failed: ${details}\\n`\n );\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to invoke relayed tool \"${tool.name}\": ${message}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n }\n\n /**\n * Builds a display description for relayed tools including source context.\n * In client mode, source metadata is unavailable, so tools are labeled `[WebMCP relay]`.\n */\n private dynamicToolDescription(tool: AggregatedTool): string {\n const source = tool.sources[0];\n const sourceLabel = source\n ? `[WebMCP ${source.tabId}${source.title ? ` • ${source.title}` : ''}]`\n : '[WebMCP relay]';\n\n return `${sourceLabel} ${tool.description ?? `Relayed tool ${tool.originalName}`}`;\n }\n\n /**\n * Produces a stable signature for change detection of dynamic tool metadata.\n *\n * Deliberately excludes `sourceId` (which is a per-connection UUID that\n * changes on every browser reconnect) so that a reconnecting tab with the\n * same tools does not trigger a needless remove+re-register cycle.\n */\n private toolSignature(tool: AggregatedTool): string {\n return JSON.stringify({\n originalName: tool.originalName,\n title: tool.title,\n description: tool.description,\n inputSchema: tool.inputSchema,\n outputSchema: tool.outputSchema,\n annotations: tool.annotations,\n _meta: tool._meta,\n icons: tool.icons,\n source: tool.sources[0]\n ? { tabId: tool.sources[0].tabId, title: tool.sources[0].title }\n : undefined,\n });\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-b/webmcp-local-relay",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"description": "Local MCP relay for browser WebMCP tools via embed iframe and localhost WebSocket",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agents",
|
|
@@ -46,20 +46,21 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@modelcontextprotocol/core": "2.0.0",
|
|
48
48
|
"@modelcontextprotocol/server": "2.0.0",
|
|
49
|
-
"ws": "^8.21.
|
|
49
|
+
"ws": "^8.21.3",
|
|
50
50
|
"zod": "4.4.3"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@anthropic-ai/mcpb": "2.1.2",
|
|
54
54
|
"@modelcontextprotocol/client": "2.0.0",
|
|
55
|
-
"@types/node": "22.
|
|
55
|
+
"@types/node": "22.20.1",
|
|
56
56
|
"@types/ws": "^8.5.14",
|
|
57
|
-
"
|
|
57
|
+
"@vitest/coverage-v8": "4.1.8",
|
|
58
|
+
"playwright": "^1.62.1",
|
|
58
59
|
"typescript": "^5.8.3",
|
|
59
|
-
"vite-plus": "
|
|
60
|
-
"vitest": "npm:@voidzero-dev/vite-plus-test@
|
|
61
|
-
"@mcp-b/webmcp-polyfill": "5.0
|
|
62
|
-
"@mcp-b/webmcp-types": "5.0
|
|
60
|
+
"vite-plus": "0.1.24",
|
|
61
|
+
"vitest": "npm:@voidzero-dev/vite-plus-test@0.1.24",
|
|
62
|
+
"@mcp-b/webmcp-polyfill": "5.1.0",
|
|
63
|
+
"@mcp-b/webmcp-types": "5.1.0"
|
|
63
64
|
},
|
|
64
65
|
"engines": {
|
|
65
66
|
"node": ">=22"
|
|
@@ -72,6 +73,7 @@
|
|
|
72
73
|
"format": "vp fmt --write",
|
|
73
74
|
"lint": "vp lint --fix",
|
|
74
75
|
"test": "vp test run",
|
|
76
|
+
"test:coverage": "vp test run --coverage",
|
|
75
77
|
"test:e2e": "pnpm --filter @mcp-b/webmcp-polyfill build && pnpm --filter @mcp-b/global build && pnpm run build && vp test run --config vitest.e2e.config.ts",
|
|
76
78
|
"test:watch": "vp test",
|
|
77
79
|
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.browser.json"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcpRelayServer-BxI6zEc2.mjs","names":[],"sources":["../src/portStrategy.ts","../src/protocol.ts","../src/naming.ts","../src/registry.ts","../src/schemas.ts","../src/bridgeServer.ts","../src/cli-utils.ts","../src/mcpRelayServer.ts"],"sourcesContent":["import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\nexport const DEFAULT_RELAY_PORT = 9333;\nexport const DEFAULT_RELAY_PORT_RANGE_END = 9348;\nconst RELAY_PORT_CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000;\n\ninterface PersistedRelayPort {\n host: string;\n port: number;\n updatedAt: string;\n}\n\nexport interface PortStrategyOptions {\n fixedPort?: number;\n defaultPort: number;\n rangeEnd: number;\n host: string;\n persistPath?: string;\n}\n\nexport interface PortStrategyResult {\n port: number;\n wasFixed: boolean;\n fromCache: boolean;\n}\n\nexport function defaultRelayPortPersistPath(): string {\n return join(homedir(), '.webmcp', 'relay-port.json');\n}\n\nexport async function persistPort(\n port: number,\n path = defaultRelayPortPersistPath(),\n host = '127.0.0.1',\n now = Date.now()\n): Promise<void> {\n const payload: PersistedRelayPort = {\n port,\n host,\n updatedAt: new Date(now).toISOString(),\n };\n\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(payload, null, 2)}\\n`, 'utf8');\n}\n\nasync function readPersistedPort(\n path = defaultRelayPortPersistPath(),\n options: {\n expectedHost?: string;\n maxAgeMs?: number;\n now?: number;\n } = {}\n): Promise<number | null> {\n try {\n const raw = await readFile(path, 'utf8');\n const parsed = JSON.parse(raw) as Partial<PersistedRelayPort>;\n if (\n typeof parsed.port !== 'number' ||\n !Number.isInteger(parsed.port) ||\n parsed.port < 1 ||\n parsed.port > 65535\n ) {\n return null;\n }\n if (typeof parsed.host !== 'string' || parsed.host.length === 0) {\n return null;\n }\n if (options.expectedHost && parsed.host !== options.expectedHost) {\n return null;\n }\n\n const maxAgeMs = options.maxAgeMs ?? RELAY_PORT_CACHE_MAX_AGE_MS;\n const now = options.now ?? Date.now();\n const updatedAtMs =\n typeof parsed.updatedAt === 'string' ? Date.parse(parsed.updatedAt) : Number.NaN;\n\n if (!Number.isFinite(updatedAtMs) || now - updatedAtMs > maxAgeMs) {\n return null;\n }\n\n return parsed.port;\n } catch {\n return null;\n }\n}\n\nexport async function buildPortCandidates(\n options: PortStrategyOptions\n): Promise<PortStrategyResult[]> {\n const {\n defaultPort,\n fixedPort,\n host,\n persistPath = defaultRelayPortPersistPath(),\n rangeEnd,\n } = options;\n\n if (fixedPort !== undefined) {\n return [{ port: fixedPort, wasFixed: true, fromCache: false }];\n }\n\n const cachedPort = await readPersistedPort(persistPath, { expectedHost: host });\n const seen = new Set<number>();\n const candidates: PortStrategyResult[] = [];\n\n const pushCandidate = (port: number, fromCache: boolean): void => {\n if (port < 1 || port > 65535 || seen.has(port)) {\n return;\n }\n seen.add(port);\n candidates.push({ port, wasFixed: false, fromCache });\n };\n\n if (cachedPort !== null && cachedPort >= defaultPort && cachedPort <= rangeEnd) {\n pushCandidate(cachedPort, true);\n }\n\n pushCandidate(defaultPort, false);\n\n for (let port = defaultPort + 1; port <= rangeEnd; port += 1) {\n pushCandidate(port, false);\n }\n\n return candidates;\n}\n","import type { CallToolResult, Tool, ToolAnnotations } from '@modelcontextprotocol/server';\nimport {\n CallToolRequestParamsSchema,\n CallToolResultSchema,\n ToolAnnotationsSchema,\n ToolSchema,\n} from '@modelcontextprotocol/core';\nimport { z } from 'zod/v4';\n\n/**\n * SDK-derived canonical tool schema used internally by the relay.\n */\nexport const NormalizedToolSchema = ToolSchema.extend({\n name: ToolSchema.shape.name.min(1),\n});\n\n/**\n * SDK-derived argument schema for tool invocation payloads.\n */\nexport const RelayInvokeArgsSchema = CallToolRequestParamsSchema.shape.arguments;\n\n/**\n * Default input schema applied when inbound payload omits or provides\n * a non-object input schema.\n */\nexport const DEFAULT_TOOL_INPUT_SCHEMA: Tool['inputSchema'] = {\n type: 'object',\n properties: {},\n};\n\n/** SDK tool schema with a browser-compatible default input schema. */\nexport const InboundToolSchema = NormalizedToolSchema.extend({\n inputSchema: NormalizedToolSchema.shape.inputSchema.optional(),\n});\n\n/**\n * Canonical normalized relay tool shape.\n */\nexport type RelayTool = z.infer<typeof NormalizedToolSchema>;\n\n/**\n * Canonical relay tool annotations shape.\n */\nexport type RelayToolAnnotations = ToolAnnotations;\n\n/**\n * Canonical relay call result shape.\n */\nexport type RelayCallToolResult = CallToolResult;\n\n/**\n * Invocation argument object shape derived from MCP SDK request params.\n */\nexport type RelayInvokeArgs = Exclude<z.infer<typeof RelayInvokeArgsSchema>, undefined>;\n\n/**\n * Applies the browser default input schema and validates the complete SDK tool.\n */\nexport function normalizeInboundTool(inbound: z.infer<typeof InboundToolSchema>): RelayTool {\n return NormalizedToolSchema.parse({\n ...inbound,\n inputSchema: inbound.inputSchema ?? DEFAULT_TOOL_INPUT_SCHEMA,\n });\n}\n\nexport { CallToolRequestParamsSchema, CallToolResultSchema, ToolAnnotationsSchema, ToolSchema };\n","/**\n * Maximum tool name length supported across MCP clients.\n */\nexport const MAX_MCP_TOOL_NAME_LENGTH = 128;\n\n/**\n * Number of tab-id characters appended when disambiguation is required.\n */\nconst TAB_ID_DISAMBIGUATION_LENGTH = 4;\n\n/**\n * Converts arbitrary text into MCP-safe identifier characters.\n */\nexport function sanitizeName(value: string): string {\n return value.replace(/[^a-zA-Z0-9_]/g, '_');\n}\n\n/**\n * Extracts and sanitizes a domain label from an origin or URL.\n */\nexport function extractSanitizedDomain(originOrUrl?: string): string {\n if (!originOrUrl) {\n return 'unknown';\n }\n\n try {\n const parsed = new URL(originOrUrl);\n if (!parsed.hostname) {\n return 'unknown';\n }\n\n const isLocalhost =\n parsed.hostname === 'localhost' ||\n parsed.hostname === '127.0.0.1' ||\n parsed.hostname === '[::1]';\n\n const domain = isLocalhost ? `localhost_${parsed.port || '80'}` : parsed.hostname;\n return sanitizeName(domain);\n } catch {\n return 'unknown';\n }\n}\n\n/**\n * Builds a public tool name for MCP registration.\n *\n * Names can include a short tab-id suffix when multiple tabs publish the same\n * original tool name.\n */\nexport function buildPublicToolName(options: {\n originalToolName: string;\n tabId?: string;\n disambiguate?: boolean;\n}): string {\n const safeName = sanitizeName(options.originalToolName);\n\n if (!options.disambiguate || !options.tabId) {\n return safeName.slice(0, MAX_MCP_TOOL_NAME_LENGTH);\n }\n\n const shortTab = sanitizeName(options.tabId).slice(0, TAB_ID_DISAMBIGUATION_LENGTH);\n const suffix = `_${shortTab}`;\n const available = MAX_MCP_TOOL_NAME_LENGTH - suffix.length;\n return `${safeName.slice(0, available)}${suffix}`;\n}\n","import { buildPublicToolName, MAX_MCP_TOOL_NAME_LENGTH } from './naming.js';\nimport type { RelayTool } from './protocol.js';\nimport type { BrowserHelloMessage } from './schemas.js';\n\n/**\n * Internal metadata tracked for each active browser source.\n */\ninterface SourceMetadata {\n sourceId: string;\n tabId: string;\n // Optional keys, matching the wire shape in RelaySourceInfoSchema: a browser\n // source may omit these entirely rather than send them as undefined.\n origin?: string | undefined;\n url?: string | undefined;\n title?: string | undefined;\n iconUrl?: string | undefined;\n connectedAt: number;\n lastSeenAt: number;\n}\n\n/**\n * Public source metadata exposed by registry APIs.\n */\nexport interface SourceInfo extends SourceMetadata {\n toolCount: number;\n}\n\n/**\n * Aggregated relayed tool metadata across one or more sources.\n *\n * Extends {@link RelayTool} with relay-specific fields so new SDK tool\n * properties are automatically inherited without manual duplication.\n */\nexport interface AggregatedTool extends RelayTool {\n originalName: string;\n sources: SourceInfo[];\n}\n\n/**\n * Internal record linking a source to a concrete tool implementation.\n */\ninterface ToolProvider {\n sourceId: string;\n tabId: string;\n tool: RelayTool;\n publicToolName: string;\n}\n\n/**\n * Invocation target selected by the registry.\n */\nexport interface ResolvedInvocation {\n connectionId: string;\n tool: RelayTool;\n publicToolName: string;\n}\n\n/**\n * Error thrown when a source attempts to register tools before `hello`.\n */\nexport class HelloRequiredError extends Error {\n readonly connectionId: string;\n\n constructor(connectionId: string) {\n super(`Connection ${connectionId} must send hello before tools`);\n this.name = 'HelloRequiredError';\n this.connectionId = connectionId;\n }\n}\n\n/**\n * In-memory source and tool registry used by the relay bridge.\n */\nexport class RelayRegistry {\n private readonly sourceByConnectionId = new Map<string, SourceMetadata>();\n private readonly toolsByConnectionId = new Map<string, ToolProvider[]>();\n private readonly providersByPublicToolName = new Map<string, ToolProvider[]>();\n\n /**\n * @param now Time provider used for recency ordering.\n */\n constructor(private readonly now: () => number = () => Date.now()) {}\n\n /**\n * Upserts source metadata from a browser `hello` message.\n *\n * `sourceId` currently matches `connectionId`, but remains conceptually distinct\n * so stable source identity can be introduced in the future.\n */\n upsertSource(connectionId: string, hello: BrowserHelloMessage): void {\n const now = this.now();\n const existing = this.sourceByConnectionId.get(connectionId);\n\n this.sourceByConnectionId.set(connectionId, {\n sourceId: connectionId,\n tabId: hello.tabId,\n origin: hello.origin ?? existing?.origin,\n url: hello.url ?? existing?.url,\n title: hello.title ?? existing?.title,\n iconUrl: hello.iconUrl ?? existing?.iconUrl,\n connectedAt: existing?.connectedAt ?? now,\n lastSeenAt: now,\n });\n }\n\n /**\n * Replaces the full tool set for a source connection.\n */\n registerTools(connectionId: string, tools: RelayTool[]): void {\n const source = this.sourceByConnectionId.get(connectionId);\n if (!source) {\n throw new HelloRequiredError(connectionId);\n }\n\n this.touchConnection(connectionId);\n\n const providers: ToolProvider[] = tools.map((tool) => ({\n sourceId: connectionId,\n tabId: source.tabId,\n tool,\n publicToolName: '',\n }));\n\n this.toolsByConnectionId.set(connectionId, providers);\n this.rebuildPublicNames();\n }\n\n /**\n * Removes a source and all of its tool registrations.\n */\n removeConnection(connectionId: string): void {\n this.toolsByConnectionId.delete(connectionId);\n this.sourceByConnectionId.delete(connectionId);\n this.rebuildPublicNames();\n }\n\n /**\n * Marks a source as recently seen without mutating identity metadata.\n */\n touchConnection(connectionId: string): void {\n const source = this.sourceByConnectionId.get(connectionId);\n if (!source) {\n return;\n }\n\n source.lastSeenAt = this.now();\n }\n\n /**\n * Lists active sources that currently publish at least one tool.\n */\n listSources(): SourceInfo[] {\n return Array.from(this.sourceByConnectionId.values())\n .map((source) => this.toSourceInfo(source))\n .filter((source) => source.toolCount > 0)\n .sort((a, b) => this.compareRecency(b.lastSeenAt, a.lastSeenAt, b.sourceId, a.sourceId));\n }\n\n /**\n * Lists aggregated tools ordered by public tool name.\n */\n listTools(): AggregatedTool[] {\n const result: AggregatedTool[] = [];\n\n for (const [publicToolName, providers] of this.providersByPublicToolName.entries()) {\n const rankedProviders = this.sortProvidersByRecency(providers);\n const primaryProvider = rankedProviders[0];\n if (!primaryProvider) {\n continue;\n }\n\n const sources = rankedProviders\n .map((provider) => {\n const source = this.sourceByConnectionId.get(provider.sourceId);\n return source ? this.toSourceInfo(source) : undefined;\n })\n .filter((source): source is SourceInfo => Boolean(source));\n\n result.push({\n ...primaryProvider.tool,\n name: publicToolName,\n originalName: primaryProvider.tool.name,\n sources,\n });\n }\n\n return result.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n /**\n * Resolves a tool invocation to a concrete source provider.\n *\n * Resolution order:\n * 1. `sourceId`: exact source id, then fallback to tab id.\n * 2. `requestTabId`: strict tab id match with no fallback.\n * 3. Default: most recently seen provider.\n */\n resolveInvocation(options: {\n toolName: string;\n sourceId?: string;\n requestTabId?: string;\n }): ResolvedInvocation | null {\n const providers = this.providersByPublicToolName.get(options.toolName);\n if (!providers || providers.length === 0) {\n return null;\n }\n\n const rankedProviders = this.sortProvidersByRecency(providers);\n\n let provider: ToolProvider | undefined;\n if (options.sourceId) {\n provider =\n rankedProviders.find((candidate) => candidate.sourceId === options.sourceId) ??\n rankedProviders.find((candidate) => candidate.tabId === options.sourceId);\n } else if (options.requestTabId) {\n provider = rankedProviders.find((candidate) => candidate.tabId === options.requestTabId);\n } else {\n provider = rankedProviders[0];\n }\n\n if (!provider) {\n return null;\n }\n\n return {\n connectionId: provider.sourceId,\n tool: provider.tool,\n publicToolName: provider.publicToolName,\n };\n }\n\n /**\n * Converts internal source metadata to public source info.\n */\n private toSourceInfo(source: SourceMetadata): SourceInfo {\n return {\n ...source,\n toolCount: this.toolsByConnectionId.get(source.sourceId)?.length ?? 0,\n };\n }\n\n /**\n * Rebuilds public tool names after source set changes.\n *\n * When the same original tool name appears in multiple tabs, names are\n * disambiguated with a short tab suffix.\n */\n private rebuildPublicNames(): void {\n const byOriginalName = new Map<string, ToolProvider[]>();\n for (const providers of this.toolsByConnectionId.values()) {\n for (const provider of providers) {\n const key = provider.tool.name;\n const group = byOriginalName.get(key) ?? [];\n group.push(provider);\n byOriginalName.set(key, group);\n }\n }\n\n this.providersByPublicToolName.clear();\n const allocations: Array<{ candidate: string; key: string; provider: ToolProvider }> = [];\n\n for (const [originalName, providers] of byOriginalName) {\n const uniqueTabIds = new Set(providers.map((p) => p.tabId));\n const disambiguate = uniqueTabIds.size > 1;\n\n for (const provider of providers) {\n allocations.push({\n candidate: buildPublicToolName({\n originalToolName: originalName,\n tabId: provider.tabId,\n disambiguate,\n }),\n key: disambiguate ? `${originalName}\\0${provider.tabId}` : originalName,\n provider,\n });\n }\n }\n\n const nameByKey = new Map<string, string>();\n const usedNames = new Set<string>();\n for (const { candidate, key, provider } of allocations.sort((a, b) =>\n a.key < b.key ? -1 : a.key > b.key ? 1 : 0\n )) {\n let publicToolName = nameByKey.get(key);\n if (!publicToolName) {\n publicToolName = candidate;\n for (let index = 2; usedNames.has(publicToolName); index += 1) {\n const suffix = `_${String(index)}`;\n publicToolName = `${candidate.slice(0, MAX_MCP_TOOL_NAME_LENGTH - suffix.length)}${suffix}`;\n }\n nameByKey.set(key, publicToolName);\n usedNames.add(publicToolName);\n }\n\n provider.publicToolName = publicToolName;\n\n const existing = this.providersByPublicToolName.get(publicToolName) ?? [];\n existing.push(provider);\n this.providersByPublicToolName.set(publicToolName, existing);\n }\n }\n\n /**\n * Returns providers sorted by source recency.\n */\n private sortProvidersByRecency(providers: ToolProvider[]): ToolProvider[] {\n return providers.slice().sort((a, b) => {\n const aSource = this.sourceByConnectionId.get(a.sourceId);\n const bSource = this.sourceByConnectionId.get(b.sourceId);\n const aLastSeen = aSource?.lastSeenAt ?? 0;\n const bLastSeen = bSource?.lastSeenAt ?? 0;\n\n return this.compareRecency(bLastSeen, aLastSeen, b.sourceId, a.sourceId);\n });\n }\n\n /**\n * Compares two source recency tuples and stabilizes order by source id.\n */\n private compareRecency(\n leftLastSeen: number,\n rightLastSeen: number,\n leftId: string,\n rightId: string\n ): number {\n const timeDiff = leftLastSeen - rightLastSeen;\n if (timeDiff !== 0) {\n return timeDiff;\n }\n return leftId.localeCompare(rightId);\n }\n}\n","import { z } from 'zod/v4';\nimport {\n CallToolResultSchema,\n InboundToolSchema,\n NormalizedToolSchema,\n normalizeInboundTool,\n RelayInvokeArgsSchema,\n type RelayTool,\n} from './protocol.js';\n\nfunction normalizeSupportedInboundTools(tools: z.infer<typeof InboundToolSchema>[]): RelayTool[] {\n return tools\n .map(normalizeInboundTool)\n .filter((tool) => tool.execution?.taskSupport !== 'required');\n}\n\nconst BrowserHelloMessageSchema = z.object({\n type: z.literal('hello'),\n tabId: z.string().min(1),\n origin: z.string().optional(),\n url: z.string().optional(),\n title: z.string().optional(),\n iconUrl: z.string().optional(),\n});\n\nconst BrowserToolsListMessageSchema = z.object({\n type: z.literal('tools/list'),\n tools: z.array(InboundToolSchema).transform(normalizeSupportedInboundTools),\n});\n\nconst BrowserToolsChangedMessageSchema = z.object({\n type: z.literal('tools/changed'),\n tools: z.array(InboundToolSchema).transform(normalizeSupportedInboundTools),\n});\n\nconst BrowserToolResultMessageSchema = z.object({\n type: z.literal('result'),\n callId: z.string().min(1),\n result: z.unknown(),\n});\n\nconst BrowserPongMessageSchema = z.object({\n type: z.literal('pong'),\n});\n\n/**\n * Union schema for all browser-to-relay protocol messages.\n */\nexport const BrowserToRelayMessageSchema = z.discriminatedUnion('type', [\n BrowserHelloMessageSchema,\n BrowserToolsListMessageSchema,\n BrowserToolsChangedMessageSchema,\n BrowserToolResultMessageSchema,\n BrowserPongMessageSchema,\n]);\n\n/**\n * Browser-to-relay message payload.\n */\nexport type BrowserToRelayMessage = z.infer<typeof BrowserToRelayMessageSchema>;\n\n/**\n * Browser source bootstrap message.\n */\nexport type BrowserHelloMessage = z.infer<typeof BrowserHelloMessageSchema>;\n\n/**\n * Shared relay identity used for discovery and attach verification.\n */\nexport const RelayDescriptorSchema = z.object({\n host: z.string().min(1),\n instanceId: z.string().min(1),\n label: z.string().min(1).optional(),\n port: z.number().int().min(1).max(65535),\n relayId: z.string().min(1).optional(),\n workspace: z.string().min(1).optional(),\n});\n\n/**\n * Relay identity used for discovery and attach verification.\n */\nexport type RelayDescriptor = z.infer<typeof RelayDescriptorSchema>;\n\n/**\n * Schema for server hello messages sent immediately after WebSocket connect.\n */\nexport const ServerHelloMessageSchema = z.object({\n type: z.literal('server-hello'),\n service: z.literal('webmcp-local-relay'),\n version: z.literal(1),\n ...RelayDescriptorSchema.shape,\n});\n\n/**\n * Server hello payload.\n */\nexport type ServerHelloMessage = z.infer<typeof ServerHelloMessageSchema>;\n\n/**\n * Schema for acceptance of a browser source hello message.\n */\nexport const RelayHelloAcceptedMessageSchema = z.object({\n type: z.literal('hello/accepted'),\n});\n\n/**\n * Acceptance payload for a browser source hello message.\n */\nexport type RelayHelloAcceptedMessage = z.infer<typeof RelayHelloAcceptedMessageSchema>;\n\n/**\n * Schema for rejection of a browser source hello message.\n */\nexport const RelayHelloRejectedMessageSchema = z.object({\n type: z.literal('hello/rejected'),\n reason: z.string().min(1),\n message: z.string().min(1),\n});\n\n/**\n * Rejection payload for a browser source hello message.\n */\nexport type RelayHelloRejectedMessage = z.infer<typeof RelayHelloRejectedMessageSchema>;\n\nconst RelayInvokeMessageSchema = z.object({\n type: z.literal('invoke'),\n callId: z.string().min(1),\n toolName: z.string().min(1),\n args: RelayInvokeArgsSchema,\n});\n\nconst RelayPingMessageSchema = z.object({\n type: z.literal('ping'),\n});\n\nconst RelayReloadMessageSchema = z.object({\n type: z.literal('reload'),\n});\n\n/**\n * Union schema for all relay-to-browser protocol messages.\n */\nexport const RelayToBrowserMessageSchema = z.discriminatedUnion('type', [\n ServerHelloMessageSchema,\n RelayHelloAcceptedMessageSchema,\n RelayHelloRejectedMessageSchema,\n RelayInvokeMessageSchema,\n RelayPingMessageSchema,\n RelayReloadMessageSchema,\n]);\n\n/**\n * Relay-to-browser message payload.\n */\nexport type RelayToBrowserMessage = z.infer<typeof RelayToBrowserMessageSchema>;\n\n/**\n * Schema for source metadata transmitted in relay-to-relay messages.\n */\nexport const RelaySourceInfoSchema = z.object({\n sourceId: z.string(),\n tabId: z.string(),\n origin: z.string().optional(),\n url: z.string().optional(),\n title: z.string().optional(),\n iconUrl: z.string().optional(),\n connectedAt: z.number(),\n lastSeenAt: z.number(),\n toolCount: z.number(),\n});\n\n/**\n * Source metadata transmitted in relay-to-relay messages.\n */\nexport type RelaySourceInfo = z.infer<typeof RelaySourceInfoSchema>;\n\nconst RelayClientHelloSchema = z.object({\n type: z.literal('relay/hello'),\n});\n\nconst RelayClientListToolsSchema = z.object({\n type: z.literal('relay/list-tools'),\n});\n\nconst RelayClientInvokeSchema = z.object({\n type: z.literal('relay/invoke'),\n callId: z.string().min(1),\n toolName: z.string().min(1),\n args: RelayInvokeArgsSchema,\n});\n\n/**\n * Union schema for all relay-client-to-server messages.\n */\nexport const RelayClientToServerMessageSchema = z.discriminatedUnion('type', [\n RelayClientHelloSchema,\n RelayClientListToolsSchema,\n RelayClientInvokeSchema,\n]);\n\n/**\n * Relay client to server message payload.\n */\nexport type RelayClientToServerMessage = z.infer<typeof RelayClientToServerMessageSchema>;\n\nconst RelayToolsPayloadFields = {\n tools: z.array(NormalizedToolSchema),\n sources: z.array(RelaySourceInfoSchema),\n toolSourceMap: z.record(z.string(), z.array(z.string())),\n};\n\nconst RelayServerToolsSchema = z.object({\n type: z.literal('relay/tools'),\n ...RelayToolsPayloadFields,\n});\n\nconst RelayServerResultSchema = z.object({\n type: z.literal('relay/result'),\n callId: z.string().min(1),\n result: CallToolResultSchema,\n});\n\nconst RelayServerToolsChangedSchema = z.object({\n type: z.literal('relay/tools-changed'),\n ...RelayToolsPayloadFields,\n});\n\n/**\n * Union schema for all relay-server-to-client messages.\n */\nexport const RelayServerToClientMessageSchema = z.discriminatedUnion('type', [\n ServerHelloMessageSchema,\n RelayServerToolsSchema,\n RelayServerResultSchema,\n RelayServerToolsChangedSchema,\n]);\n\n/**\n * Relay server to client message payload.\n */\nexport type RelayServerToClientMessage = z.infer<typeof RelayServerToClientMessageSchema>;\n","import { randomUUID } from 'node:crypto';\nimport { EventEmitter } from 'node:events';\n\nimport WebSocket, { WebSocketServer } from 'ws';\nimport {\n buildPortCandidates,\n DEFAULT_RELAY_PORT,\n DEFAULT_RELAY_PORT_RANGE_END,\n defaultRelayPortPersistPath,\n persistPort,\n} from './portStrategy.js';\nimport {\n CallToolResultSchema,\n type RelayCallToolResult,\n type RelayInvokeArgs,\n type RelayTool,\n} from './protocol.js';\nimport { RelayRegistry } from './registry.js';\nimport {\n type BrowserToRelayMessage,\n BrowserToRelayMessageSchema,\n type RelayClientToServerMessage,\n RelayClientToServerMessageSchema,\n type RelayServerToClientMessage,\n RelayServerToClientMessageSchema,\n type RelayHelloAcceptedMessage,\n type RelayHelloRejectedMessage,\n type RelaySourceInfo,\n type RelayToBrowserMessage,\n type ServerHelloMessage,\n} from './schemas.js';\n\nconst RELAY_BROWSER_PROTOCOL = 'webmcp.v1';\nconst RELAY_DISCOVERY_PROTOCOL = 'webmcp-discovery.v1';\nconst RELAY_INTERNAL_PROTOCOL = 'webmcp-relay.v1';\nconst RELAY_SERVER_MESSAGE_TIMEOUT_MS = 750;\nconst HEARTBEAT_INTERVAL_MS = 15_000;\nconst HEARTBEAT_DEAD_THRESHOLD_MS = 25_000;\nconst SUPPORTED_SUBPROTOCOLS = new Set([\n RELAY_BROWSER_PROTOCOL,\n RELAY_DISCOVERY_PROTOCOL,\n RELAY_INTERNAL_PROTOCOL,\n]);\n\n/**\n * In-flight relay invocation waiting for a browser `result` message.\n */\ninterface PendingInvocation {\n connectionId: string;\n timeoutId: ReturnType<typeof setTimeout>;\n resolve: (result: RelayCallToolResult) => void;\n reject: (error: Error) => void;\n}\n\nfunction formatPayloadLimit(bytes: number): string {\n if (bytes >= 1_000_000 && bytes % 1_000_000 === 0) {\n return `${String(bytes / 1_000_000)}MB`;\n }\n return `${String(bytes)} bytes`;\n}\n\n/**\n * Runtime options for {@link RelayBridgeServer}.\n */\nexport interface RelayBridgeServerOptions {\n /**\n * Network interface used by the local WebSocket server.\n * @defaultValue `\"127.0.0.1\"`\n */\n host?: string;\n /**\n * Preferred WebSocket port for browser widget connections.\n * @defaultValue `9333`\n */\n port?: number;\n /**\n * Whether the preferred port came from an explicit CLI/API override.\n * Explicit ports fail if occupied by a non-relay process.\n * @defaultValue `false`\n */\n portExplicitlySet?: boolean;\n /**\n * Inclusive upper bound for automatic port discovery.\n * @defaultValue `9348`\n */\n portRangeEnd?: number;\n /**\n * File path used to cache the last successful relay port.\n * @defaultValue `~/.webmcp/relay-port.json`\n */\n persistPath?: string;\n /**\n * Allowed host page origins verified from browser WebSocket requests.\n * Use `[\"*\"]` to allow all origins.\n *\n * Permissive by default for zero-config developer experience: any browser\n * page can connect and register tools without additional setup. For production\n * or shared machines, restrict to trusted host origins via the\n * `--widget-origin` CLI flag or by passing explicit origins here.\n *\n * @defaultValue `[\"*\"]`\n */\n allowedOrigins?: string[];\n /**\n * Maximum WebSocket payload size in bytes.\n * @defaultValue `10000000`\n */\n maxPayloadBytes?: number;\n /**\n * Timeout used for browser tool invocations.\n * @defaultValue `65000`\n */\n invokeTimeoutMs?: number;\n /**\n * Human-readable relay label reported in discovery handshakes.\n */\n label?: string;\n /**\n * Optional workspace name reported in discovery handshakes.\n */\n workspace?: string;\n /**\n * Stable relay identifier used to select between multiple relays.\n */\n relayId?: string;\n}\n\n/**\n * WebSocket relay between browser widget frames and MCP server calls.\n *\n * Operates in two modes:\n * - **server** (default): Runs a WebSocket server, accepts browser and relay\n * client connections.\n * - **client** (fallback on EADDRINUSE): Connects as a WebSocket client to an\n * existing server relay and proxies tool operations through it.\n */\nexport class RelayBridgeServer extends EventEmitter {\n /**\n * Registry for connected sources and aggregated tool definitions.\n * Only actively used in server mode; remains empty when operating as a client.\n */\n readonly registry: RelayRegistry;\n\n private readonly host: string;\n private readonly preferredPort: number;\n private desiredPort: number;\n private readonly portExplicitlySet: boolean;\n private readonly portRangeEnd: number;\n private readonly persistPath: string;\n private readonly allowedOrigins: string[];\n private readonly maxPayloadBytes: number;\n private readonly invokeTimeoutMs: number;\n private readonly label: string | undefined;\n private readonly workspace: string | undefined;\n private readonly relayId: string | undefined;\n private readonly instanceId: string;\n\n private wss: WebSocketServer | null = null;\n private readonly socketByConnectionId = new Map<string, WebSocket>();\n private readonly requestOriginByConnectionId = new Map<string, string>();\n private readonly pendingInvocations = new Map<string, PendingInvocation>();\n private readonly browserClientConnectionIds = new Set<string>();\n private readonly relayClientConnectionIds = new Set<string>();\n private readonly heartbeatIntervalByConnectionId = new Map<\n string,\n ReturnType<typeof setInterval>\n >();\n private readonly lastPongByConnectionId = new Map<string, number>();\n private readonly onStateChangedPushRelay = () => {\n this.pushToolsToRelayClients();\n };\n\n private _mode: 'server' | 'client' = 'server';\n private clientSocket: WebSocket | null = null;\n private clientReconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private clientReconnectDelay = 500;\n /**\n * Maximum delay for relay-to-relay reconnection backoff in client mode.\n */\n private readonly clientMaxReconnectDelay = 3_000;\n private readonly clientMaxReconnectAttempts = 100;\n private clientReconnectAttempts = 0;\n private readonly clientPendingInvocations = new Map<string, PendingInvocation>();\n private clientTools: RelayTool[] = [];\n private clientSources: RelaySourceInfo[] = [];\n private clientToolSourceMap: Record<string, string[]> = {};\n private stopping = false;\n\n /**\n * Creates a relay bridge server instance.\n */\n constructor(options: RelayBridgeServerOptions = {}, registry?: RelayRegistry) {\n super();\n this.registry = registry ?? new RelayRegistry();\n\n this.host = options.host ?? '127.0.0.1';\n this.preferredPort = options.port ?? DEFAULT_RELAY_PORT;\n this.desiredPort = this.preferredPort;\n this.portExplicitlySet = options.portExplicitlySet ?? false;\n this.portRangeEnd =\n options.portRangeEnd ?? Math.max(DEFAULT_RELAY_PORT_RANGE_END, this.preferredPort);\n this.persistPath = options.persistPath ?? defaultRelayPortPersistPath();\n this.allowedOrigins = options.allowedOrigins ?? ['*'];\n // 10MB default: large enough for typical JSON API responses while\n // still protecting the relay process from unbounded memory growth.\n this.maxPayloadBytes = options.maxPayloadBytes ?? 10_000_000;\n this.invokeTimeoutMs = options.invokeTimeoutMs ?? 65_000;\n this.label = options.label;\n this.workspace = options.workspace;\n this.relayId = options.relayId;\n this.instanceId = randomUUID();\n\n if (\n !Number.isInteger(this.preferredPort) ||\n this.preferredPort < 0 ||\n this.preferredPort > 65535\n ) {\n throw new Error(\n `Invalid port ${this.preferredPort}. Port must be 0 (auto-assign) or between 1 and 65535.`\n );\n }\n if (\n !Number.isInteger(this.portRangeEnd) ||\n this.portRangeEnd < 1 ||\n this.portRangeEnd > 65535 ||\n (this.portRangeEnd < this.preferredPort && this.preferredPort !== 0)\n ) {\n throw new Error(\n `Invalid port range ${this.preferredPort}-${this.portRangeEnd}. rangeEnd must be an integer between the preferred port and 65535.`\n );\n }\n if (!Number.isInteger(this.maxPayloadBytes) || this.maxPayloadBytes <= 0) {\n throw new Error(\n `Invalid maxPayloadBytes ${this.maxPayloadBytes}. Must be a positive integer.`\n );\n }\n if (!Number.isInteger(this.invokeTimeoutMs) || this.invokeTimeoutMs <= 0) {\n throw new Error(\n `Invalid invokeTimeoutMs ${this.invokeTimeoutMs}. Must be a positive integer.`\n );\n }\n }\n\n /**\n * Current operating mode.\n */\n get mode(): 'server' | 'client' {\n return this._mode;\n }\n\n /**\n * Resolved listening port. In client mode this is the port of the server\n * relay being proxied through.\n */\n get port(): number {\n return this.desiredPort;\n }\n\n /**\n * Tools received from the server relay (client mode only).\n * Returns an empty array in server mode.\n */\n listToolsFromRelay(): RelayTool[] {\n return this._mode === 'client' ? [...this.clientTools] : [];\n }\n\n /**\n * Source metadata received from the server relay (client mode only).\n * Returns an empty array in server mode.\n */\n listSourcesFromRelay(): RelaySourceInfo[] {\n return this._mode === 'client' ? [...this.clientSources] : [];\n }\n\n /**\n * Tool-to-source mapping received from the server relay (client mode only).\n * Maps public tool names to arrays of source IDs.\n * Returns an empty record in server mode.\n */\n getToolSourceMapFromRelay(): Record<string, string[]> {\n return this._mode === 'client' ? { ...this.clientToolSourceMap } : {};\n }\n\n /**\n * Starts the bridge. Attempts to bind a WebSocket server (server mode).\n * If a compatible relay already owns a candidate port, joins it in client mode.\n * If a non-relay process owns the port, continues searching the reserved range.\n */\n async start(): Promise<void> {\n if (this.wss || this.clientSocket) {\n return;\n }\n\n this.stopping = false;\n await this.startUsingPortStrategy();\n }\n\n private async startUsingPortStrategy(): Promise<void> {\n if (this.preferredPort === 0) {\n await this.startAsServer(0);\n return;\n }\n\n const candidates = await buildPortCandidates({\n defaultPort: this.preferredPort,\n ...(this.portExplicitlySet ? { fixedPort: this.preferredPort } : {}),\n host: this.host,\n persistPath: this.persistPath,\n rangeEnd: this.portRangeEnd,\n });\n\n for (const candidate of candidates) {\n try {\n await this.startAsServer(candidate.port);\n await this.persistSelectedPort();\n return;\n } catch (err) {\n if (!this.isAddressInUseError(err)) {\n throw err;\n }\n\n const attached = await this.tryAttachToExistingRelay(candidate.port);\n if (attached) {\n await this.persistSelectedPort();\n return;\n }\n\n if (candidate.wasFixed) {\n throw new Error(\n `Port ${candidate.port} is already in use by a non-WebMCP service and cannot be shared.`\n );\n }\n\n process.stderr.write(\n `[webmcp-local-relay] info: port ${candidate.port} is occupied by a non-relay service, trying next port\\n`\n );\n }\n }\n\n throw new Error(\n `No compatible relay port was available in the range ${this.preferredPort}-${this.portRangeEnd}.`\n );\n }\n\n private isAddressInUseError(error: unknown): boolean {\n return (\n error instanceof Error &&\n ('code' in error ? error.code === 'EADDRINUSE' : error.message.includes('EADDRINUSE'))\n );\n }\n\n private async persistSelectedPort(): Promise<void> {\n try {\n await persistPort(this.port, this.persistPath, this.host);\n } catch (error) {\n process.stderr.write(\n `[webmcp-local-relay] warn: could not cache relay port: ${error instanceof Error ? error.message : String(error)}\\n`\n );\n }\n }\n\n private async tryAttachToExistingRelay(port: number): Promise<boolean> {\n try {\n await this.startAsClient(port);\n process.stderr.write(\n `[webmcp-local-relay] info: discovered compatible relay on port ${port}, switching to client mode\\n`\n );\n return true;\n } catch (error) {\n // The caller reports every false here as \"occupied by a non-relay service\". A slow\n // but healthy relay hits the 750ms hello timeout and lands here too, so name the\n // real reason rather than leaving the operator with a wrong hypothesis.\n process.stderr.write(\n `[webmcp-local-relay] info: port ${port} did not complete a relay handshake: ${error instanceof Error ? error.message : String(error)}\\n`\n );\n return false;\n }\n }\n\n /**\n * Stops all relay resources and rejects any pending invocations.\n */\n async stop(): Promise<void> {\n this.stopping = true;\n\n if (this.clientReconnectTimer) {\n clearTimeout(this.clientReconnectTimer);\n this.clientReconnectTimer = null;\n }\n\n if (this._mode === 'client') {\n for (const pending of this.clientPendingInvocations.values()) {\n clearTimeout(pending.timeoutId);\n pending.reject(new Error('Relay client stopped'));\n }\n this.clientPendingInvocations.clear();\n this.clientTools = [];\n this.clientSources = [];\n this.clientToolSourceMap = {};\n\n if (this.clientSocket) {\n try {\n this.clientSocket.close(1000, 'Relay client shutting down');\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: error closing client socket during shutdown: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n this.clientSocket = null;\n }\n return;\n }\n\n this.off('stateChanged', this.onStateChangedPushRelay);\n\n for (const socket of this.socketByConnectionId.values()) {\n try {\n socket.close(1001, 'Relay shutting down');\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: error closing socket during shutdown: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }\n\n for (const intervalId of this.heartbeatIntervalByConnectionId.values()) {\n clearInterval(intervalId);\n }\n this.heartbeatIntervalByConnectionId.clear();\n this.lastPongByConnectionId.clear();\n\n this.socketByConnectionId.clear();\n this.requestOriginByConnectionId.clear();\n this.browserClientConnectionIds.clear();\n this.relayClientConnectionIds.clear();\n\n for (const pending of this.pendingInvocations.values()) {\n clearTimeout(pending.timeoutId);\n pending.reject(new Error('Relay server stopped before tool invocation completed'));\n }\n this.pendingInvocations.clear();\n\n const wss = this.wss;\n this.wss = null;\n\n if (!wss) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n wss.close((err?: Error) => {\n if (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: WebSocket server close error: ${err.message}\\n`\n );\n }\n resolve();\n });\n });\n }\n\n /**\n * Sends a reload message to a connected browser source.\n * Only supported in server mode.\n */\n reloadSource(connectionId: string): void {\n if (this._mode !== 'server') {\n throw new Error('reloadSource is only supported in server mode');\n }\n const socket = this.socketByConnectionId.get(connectionId);\n if (!socket || socket.readyState !== WebSocket.OPEN) {\n throw new Error(`Source ${connectionId} is not connected`);\n }\n const message: RelayToBrowserMessage = { type: 'reload' };\n try {\n socket.send(JSON.stringify(message));\n } catch (err) {\n throw new Error(`Failed to send reload: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n /**\n * Invokes a tool either locally (server mode) or through the upstream relay\n * (client mode).\n */\n async invokeTool(\n toolName: string,\n args: RelayInvokeArgs,\n options: {\n sourceId?: string;\n requestTabId?: string;\n } = {}\n ): Promise<RelayCallToolResult> {\n if (this._mode === 'client') {\n return this.invokeToolViaRelay(toolName, args);\n }\n\n return this.invokeToolLocally(toolName, args, options);\n }\n\n private async startAsServer(port = this.desiredPort): Promise<void> {\n const wss = await new Promise<WebSocketServer>((resolve, reject) => {\n const server = new WebSocketServer({\n handleProtocols: (protocols) => {\n for (const protocol of protocols) {\n if (SUPPORTED_SUBPROTOCOLS.has(protocol)) {\n return protocol;\n }\n }\n return false;\n },\n host: this.host,\n port,\n maxPayload: this.maxPayloadBytes,\n });\n\n const onListening = () => {\n server.off('error', onError);\n resolve(server);\n };\n const onError = (err: Error) => {\n server.off('listening', onListening);\n reject(err);\n };\n\n server.once('listening', onListening);\n server.once('error', onError);\n });\n\n wss.on('connection', (socket: WebSocket, request) => {\n const requestOrigin = request.headers.origin;\n if (requestOrigin && socket.protocol === RELAY_INTERNAL_PROTOCOL) {\n socket.close(1008, 'Browser connections cannot use the relay protocol');\n return;\n }\n\n const connectionId = randomUUID();\n this.socketByConnectionId.set(connectionId, socket);\n if (requestOrigin) {\n this.requestOriginByConnectionId.set(connectionId, requestOrigin);\n }\n\n socket.on('message', (raw: WebSocket.RawData) => {\n this.onSocketMessage(connectionId, raw);\n });\n\n socket.on('close', (code: number) => {\n this.onSocketClose(connectionId, code);\n });\n\n socket.on('error', (err: Error) => {\n process.stderr.write(\n `[webmcp-local-relay] warn: socket error for connection ${connectionId}: ${err.message}\\n`\n );\n const code =\n 'code' in err && err.code === 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH' ? 1009 : undefined;\n this.onSocketClose(connectionId, code);\n });\n\n try {\n socket.send(JSON.stringify(this.buildServerHello()));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send server hello to connection ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n socket.close(1011, 'Failed to send server hello');\n return;\n }\n\n this.startHeartbeat(connectionId);\n });\n\n wss.on('error', (err: Error) => {\n // Logged, not re-emitted: nothing listens for 'error' on this EventEmitter, and\n // emit('error') without a listener rethrows, killing the relay via uncaughtException.\n process.stderr.write(`[webmcp-local-relay] error: WebSocket server error: ${err.message}\\n`);\n });\n\n this.wss = wss;\n this._mode = 'server';\n\n const address = wss.address();\n if (address && typeof address !== 'string') {\n this.desiredPort = address.port;\n }\n\n this.on('stateChanged', this.onStateChangedPushRelay);\n }\n\n private buildServerHello(): ServerHelloMessage {\n return {\n type: 'server-hello',\n service: 'webmcp-local-relay',\n version: 1,\n host: this.host,\n instanceId: this.instanceId,\n label: this.label,\n port: this.desiredPort,\n relayId: this.relayId ?? this.instanceId,\n workspace: this.workspace,\n };\n }\n\n /**\n * `async` is load-bearing: {@link onRelayClientMessage} consumes this through\n * `.then(onResult, onError)`, so a synchronous throw would escape the socket\n * message handler as an uncaught exception and leave the relay client waiting\n * for a `relay/result` that never arrives.\n */\n private async invokeToolLocally(\n toolName: string,\n args: RelayInvokeArgs,\n options: { sourceId?: string; requestTabId?: string }\n ): Promise<RelayCallToolResult> {\n const resolved = this.registry.resolveInvocation({\n toolName,\n ...(options.sourceId === undefined ? {} : { sourceId: options.sourceId }),\n ...(options.requestTabId === undefined ? {} : { requestTabId: options.requestTabId }),\n });\n\n if (!resolved) {\n throw new Error(`No active browser source provides tool \"${toolName}\"`);\n }\n\n const socket = this.socketByConnectionId.get(resolved.connectionId);\n if (!socket || socket.readyState !== WebSocket.OPEN) {\n throw new Error(\n `Tool source ${resolved.connectionId} disconnected before invocation of \"${toolName}\"`\n );\n }\n\n const callId = randomUUID();\n\n return new Promise<RelayCallToolResult>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n this.pendingInvocations.delete(callId);\n reject(\n new Error(`Invocation for tool \"${toolName}\" timed out after ${this.invokeTimeoutMs}ms`)\n );\n }, this.invokeTimeoutMs);\n\n this.pendingInvocations.set(callId, {\n connectionId: resolved.connectionId,\n timeoutId,\n resolve,\n reject,\n });\n\n const message: RelayToBrowserMessage = {\n type: 'invoke',\n callId,\n toolName: resolved.tool.name,\n args,\n };\n\n try {\n socket.send(JSON.stringify(message));\n } catch (err) {\n clearTimeout(timeoutId);\n this.pendingInvocations.delete(callId);\n reject(\n new Error(\n `Failed to send invocation for tool \"${toolName}\": ${err instanceof Error ? err.message : err}`\n )\n );\n }\n });\n }\n\n /** Handles a raw message according to the negotiated WebSocket protocol. */\n private onSocketMessage(connectionId: string, raw: WebSocket.RawData): void {\n const text = this.rawDataToUtf8(raw);\n\n let parsedJson: unknown;\n try {\n parsedJson = JSON.parse(text);\n } catch (err) {\n const preview = text.length > 200 ? `${text.slice(0, 200)}...` : text;\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid JSON from connection ${connectionId} (${err instanceof Error ? err.message : 'parse error'}): ${preview}\\n`\n );\n return;\n }\n\n const typeField =\n typeof parsedJson === 'object' && parsedJson !== null && 'type' in parsedJson\n ? parsedJson.type\n : undefined;\n const socket = this.socketByConnectionId.get(connectionId);\n if (!socket) {\n return;\n }\n\n if (socket.protocol === RELAY_INTERNAL_PROTOCOL) {\n const relayMsg = RelayClientToServerMessageSchema.safeParse(parsedJson);\n if (relayMsg.success) {\n this.onRelayClientMessage(connectionId, relayMsg.data);\n } else {\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid relay message from ${connectionId}: ${relayMsg.error.message}\\n`\n );\n }\n return;\n }\n\n if (typeof typeField === 'string' && typeField.startsWith('relay/')) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} used the relay protocol without negotiating it\\n`\n );\n socket.close(1008, 'Relay protocol was not negotiated');\n return;\n }\n\n const parsedMessage = BrowserToRelayMessageSchema.safeParse(parsedJson);\n if (!parsedMessage.success) {\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid message from connection ${connectionId} (type=${typeField}): ${parsedMessage.error.message}\\n`\n );\n return;\n }\n\n this.registry.touchConnection(connectionId);\n this.onBrowserClientMessage(connectionId, parsedMessage.data);\n }\n\n private onBrowserClientMessage(connectionId: string, message: BrowserToRelayMessage): void {\n if (message.type !== 'hello' && !this.browserClientConnectionIds.has(connectionId)) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} sent ${message.type} before hello, ignoring\\n`\n );\n return;\n }\n\n switch (message.type) {\n case 'hello':\n try {\n const socket = this.socketByConnectionId.get(connectionId);\n const origin = this.requestOriginByConnectionId.get(connectionId) ?? message.origin;\n if (!this.isHostOriginAllowed(origin)) {\n process.stderr.write(\n `[webmcp-local-relay] warn: rejecting source ${connectionId} with disallowed host origin: ${origin ?? 'missing'}\\n`\n );\n if (socket) {\n this.sendHelloRejected(\n socket,\n {\n type: 'hello/rejected',\n reason: 'host-origin-not-allowed',\n message: 'Host page origin is not allowed by this relay.',\n },\n 1008,\n 'Host origin not allowed'\n );\n }\n break;\n }\n this.registry.upsertSource(connectionId, { ...message, origin });\n this.browserClientConnectionIds.add(connectionId);\n if (socket) {\n this.sendHelloAccepted(socket, { type: 'hello/accepted' });\n }\n this.emit('stateChanged');\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] error: failed to process hello from connection ${connectionId}: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n );\n }\n break;\n\n case 'tools/list':\n case 'tools/changed':\n try {\n this.registry.registerTools(connectionId, message.tools);\n this.emit('stateChanged');\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] error: failed to register tools for connection ${connectionId}: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n );\n this.socketByConnectionId.get(connectionId)?.close(1008, 'Invalid tool list');\n }\n break;\n\n case 'result': {\n const pending = this.pendingInvocations.get(message.callId);\n if (!pending) {\n process.stderr.write(\n `[webmcp-local-relay] warn: received result for unknown callId ${message.callId}\\n`\n );\n break;\n }\n if (pending.connectionId !== connectionId) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} returned result for another source's callId ${message.callId}\\n`\n );\n break;\n }\n\n clearTimeout(pending.timeoutId);\n this.pendingInvocations.delete(message.callId);\n pending.resolve(this.normalizeCallToolResult(message.result));\n break;\n }\n\n case 'pong':\n this.lastPongByConnectionId.set(connectionId, Date.now());\n break;\n }\n }\n\n /**\n * Handles relay-protocol messages from relay client connections.\n */\n private onRelayClientMessage(connectionId: string, message: RelayClientToServerMessage): void {\n if (message.type !== 'relay/hello' && !this.relayClientConnectionIds.has(connectionId)) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} sent ${message.type} before relay/hello, ignoring\\n`\n );\n return;\n }\n\n switch (message.type) {\n case 'relay/hello':\n this.relayClientConnectionIds.add(connectionId);\n break;\n\n case 'relay/list-tools': {\n const response = this.buildRelayToolsPayload('relay/tools');\n const socket = this.socketByConnectionId.get(connectionId);\n if (socket?.readyState === WebSocket.OPEN) {\n try {\n socket.send(JSON.stringify(response));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send relay tools response to ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }\n break;\n }\n\n case 'relay/invoke': {\n const { callId, toolName, args } = message;\n void this.invokeToolLocally(toolName, args ?? {}, {}).then(\n (result) => this.sendRelayResult(connectionId, callId, result),\n (error: unknown) =>\n this.sendRelayResult(connectionId, callId, {\n content: [\n {\n type: 'text',\n text: `Relay invocation failed: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n })\n );\n break;\n }\n }\n }\n\n private sendRelayResult(connectionId: string, callId: string, result: RelayCallToolResult): void {\n const socket = this.socketByConnectionId.get(connectionId);\n if (socket?.readyState !== WebSocket.OPEN) {\n process.stderr.write(\n `[webmcp-local-relay] warn: relay client ${connectionId} disconnected before result for callId ${callId} could be delivered\\n`\n );\n return;\n }\n\n try {\n socket.send(JSON.stringify({ type: 'relay/result', callId, result }));\n } catch (error) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send relay result to ${connectionId}: ${error instanceof Error ? error.message : String(error)}\\n`\n );\n }\n }\n\n /**\n * Pushes current tool state to all connected relay clients.\n */\n private pushToolsToRelayClients(): void {\n if (this.relayClientConnectionIds.size === 0) {\n return;\n }\n\n const message = this.buildRelayToolsPayload('relay/tools-changed');\n const payload = JSON.stringify(message);\n\n for (const connectionId of this.relayClientConnectionIds) {\n const socket = this.socketByConnectionId.get(connectionId);\n if (socket?.readyState === WebSocket.OPEN) {\n try {\n socket.send(payload);\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to push tool update to relay client ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }\n }\n }\n\n /**\n * Handles source disconnection and rejects in-flight calls owned by that source.\n *\n * Guarded against double-call: both 'error' and 'close' events fire on\n * socket failure, but cleanup (and the stateChanged emission) runs only once.\n */\n private onSocketClose(connectionId: string, code?: number): void {\n // Guard against double invocation: ws fires both 'error' and 'close' for\n // maxPayload violations. The second call is a no-op.\n if (!this.socketByConnectionId.has(connectionId)) return;\n\n this.stopHeartbeat(connectionId);\n this.requestOriginByConnectionId.delete(connectionId);\n this.browserClientConnectionIds.delete(connectionId);\n this.relayClientConnectionIds.delete(connectionId);\n this.registry.removeConnection(connectionId);\n this.socketByConnectionId.delete(connectionId);\n this.emit('stateChanged');\n\n // WS close code 1009 = \"Message Too Big\": the browser sent a response\n // that exceeded maxPayloadBytes. Surface a clear error instead of letting\n // the invocation time out silently after invokeTimeoutMs.\n const isPayloadExceeded = code === 1009;\n\n for (const [callId, pending] of this.pendingInvocations.entries()) {\n if (pending.connectionId !== connectionId) {\n continue;\n }\n\n clearTimeout(pending.timeoutId);\n this.pendingInvocations.delete(callId);\n\n if (isPayloadExceeded) {\n pending.reject(\n new Error(\n `Tool result exceeded maximum payload size (${formatPayloadLimit(this.maxPayloadBytes)}). Use --max-payload to increase the limit.`\n )\n );\n } else {\n pending.reject(new Error(`Tool source ${connectionId} disconnected during invocation`));\n }\n }\n }\n\n private startHeartbeat(connectionId: string): void {\n this.lastPongByConnectionId.set(connectionId, Date.now());\n\n const intervalId = setInterval(() => {\n const socket = this.socketByConnectionId.get(connectionId);\n if (!socket || socket.readyState !== WebSocket.OPEN) {\n this.stopHeartbeat(connectionId);\n return;\n }\n\n // Skip heartbeat for relay client connections; they have their own reconnect logic.\n if (this.relayClientConnectionIds.has(connectionId)) {\n return;\n }\n\n const lastPong = this.lastPongByConnectionId.get(connectionId) ?? 0;\n if (Date.now() - lastPong > HEARTBEAT_DEAD_THRESHOLD_MS) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} missed heartbeat, closing\\n`\n );\n this.stopHeartbeat(connectionId);\n socket.close(1001, 'Heartbeat timeout');\n return;\n }\n\n try {\n socket.send(JSON.stringify({ type: 'ping' }));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send heartbeat ping to ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }, HEARTBEAT_INTERVAL_MS);\n\n this.heartbeatIntervalByConnectionId.set(connectionId, intervalId);\n }\n\n private stopHeartbeat(connectionId: string): void {\n const intervalId = this.heartbeatIntervalByConnectionId.get(connectionId);\n if (intervalId !== undefined) {\n clearInterval(intervalId);\n this.heartbeatIntervalByConnectionId.delete(connectionId);\n }\n this.lastPongByConnectionId.delete(connectionId);\n }\n\n private async startAsClient(port = this.desiredPort): Promise<void> {\n this.stopping = false;\n\n const previousMode = this._mode;\n const previousPort = this.desiredPort;\n this._mode = 'client';\n this.desiredPort = port;\n\n try {\n const { bufferedMessages, socket } = await this.connectToRelayServer(port);\n this.clientSocket = socket;\n this.clientReconnectDelay = 500;\n this.clientReconnectAttempts = 0;\n this.setupClientHandlers(socket, bufferedMessages);\n } catch (error) {\n this._mode = previousMode;\n this.desiredPort = previousPort;\n throw error;\n }\n }\n\n private async connectToRelayServer(\n port: number\n ): Promise<{ bufferedMessages: RelayServerToClientMessage[]; socket: WebSocket }> {\n return new Promise((resolve, reject) => {\n const wsUrl = `ws://${this.host}:${port}`;\n const ws = new WebSocket(wsUrl, RELAY_INTERNAL_PROTOCOL);\n const bufferedMessages: RelayServerToClientMessage[] = [];\n\n const cleanup = () => {\n clearTimeout(timeoutId);\n ws.off('close', onCloseBeforeReady);\n ws.off('error', onErrorBeforeReady);\n ws.off('message', onMessageBeforeReady);\n ws.off('open', onOpen);\n };\n\n const rejectWith = (error: Error) => {\n cleanup();\n ws.once('error', () => {\n // Ignore late socket errors from ports that failed relay verification.\n });\n ws.terminate();\n reject(error);\n };\n\n const finish = () => {\n cleanup();\n resolve({ bufferedMessages, socket: ws });\n };\n\n const onOpen = () => {\n try {\n this.sendRelayClientHandshake(ws);\n } catch (err) {\n rejectWith(\n new Error(\n `Failed to send handshake to relay server at ${wsUrl}: ${err instanceof Error ? err.message : String(err)}`\n )\n );\n }\n };\n\n const onErrorBeforeReady = (err: Error) => {\n rejectWith(new Error(`Failed to connect to relay server at ${wsUrl}: ${err.message}`));\n };\n\n const onCloseBeforeReady = () => {\n rejectWith(new Error(`Connection to ${wsUrl} closed before relay verification completed`));\n };\n\n const onMessageBeforeReady = (raw: WebSocket.RawData) => {\n const message = this.parseRelayServerMessage(raw);\n if (!message) {\n rejectWith(new Error(`Received a non-relay response while probing ${wsUrl}`));\n return;\n }\n\n bufferedMessages.push(message);\n\n if (message.type === 'server-hello') {\n if (message.service !== 'webmcp-local-relay') {\n rejectWith(new Error(`Unexpected relay service \"${message.service}\" at ${wsUrl}`));\n return;\n }\n finish();\n return;\n }\n\n if (message.type === 'relay/tools' || message.type === 'relay/tools-changed') {\n finish();\n }\n };\n\n const timeoutId = setTimeout(() => {\n rejectWith(new Error(`Timed out waiting for relay hello from ${wsUrl}`));\n }, RELAY_SERVER_MESSAGE_TIMEOUT_MS);\n\n ws.once('open', onOpen);\n ws.once('error', onErrorBeforeReady);\n ws.once('close', onCloseBeforeReady);\n ws.on('message', onMessageBeforeReady);\n });\n }\n\n private setupClientHandlers(\n ws: WebSocket,\n bufferedMessages: RelayServerToClientMessage[] = []\n ): void {\n for (const message of bufferedMessages) {\n this.processRelayServerMessage(message);\n }\n\n ws.on('message', (raw: WebSocket.RawData) => {\n const message = this.parseRelayServerMessage(raw);\n if (!message) {\n return;\n }\n\n this.processRelayServerMessage(message);\n });\n\n ws.on('close', () => {\n this.clientSocket = null;\n for (const [callId, pending] of this.clientPendingInvocations) {\n clearTimeout(pending.timeoutId);\n this.clientPendingInvocations.delete(callId);\n pending.reject(new Error('Relay server connection lost during invocation'));\n }\n\n this.clientTools = [];\n this.clientSources = [];\n this.clientToolSourceMap = {};\n this.emit('stateChanged');\n\n if (!this.stopping) {\n this.scheduleReconnect();\n }\n });\n\n ws.on('error', (err: Error) => {\n process.stderr.write(\n `[webmcp-local-relay] warn: relay client socket error: ${err.message}\\n`\n );\n });\n }\n\n private sendRelayClientHandshake(ws: WebSocket): void {\n ws.send(JSON.stringify({ type: 'relay/hello' }));\n ws.send(JSON.stringify({ type: 'relay/list-tools' }));\n }\n\n private parseRelayServerMessage(raw: WebSocket.RawData): RelayServerToClientMessage | null {\n const text = this.rawDataToUtf8(raw);\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (err) {\n const preview = text.length > 200 ? `${text.slice(0, 200)}...` : text;\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid JSON from relay server (${err instanceof Error ? err.message : 'parse error'}): ${preview}\\n`\n );\n return null;\n }\n\n const message = RelayServerToClientMessageSchema.safeParse(parsed);\n if (!message.success) {\n const typeField =\n typeof parsed === 'object' && parsed !== null && 'type' in parsed ? parsed.type : 'unknown';\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid relay server message (type=${typeField}): ${message.error.message}\\n`\n );\n return null;\n }\n\n return message.data;\n }\n\n private processRelayServerMessage(message: RelayServerToClientMessage): void {\n switch (message.type) {\n case 'server-hello':\n break;\n\n case 'relay/tools':\n case 'relay/tools-changed':\n this.clientTools = message.tools;\n this.clientSources = message.sources;\n this.clientToolSourceMap = message.toolSourceMap;\n this.emit('stateChanged');\n break;\n\n case 'relay/result': {\n const pending = this.clientPendingInvocations.get(message.callId);\n if (!pending) {\n process.stderr.write(\n `[webmcp-local-relay] warn: received relay result for unknown callId ${message.callId}\\n`\n );\n break;\n }\n clearTimeout(pending.timeoutId);\n this.clientPendingInvocations.delete(message.callId);\n pending.resolve(message.result);\n break;\n }\n }\n }\n\n private scheduleReconnect(): void {\n if (this.clientReconnectTimer || this.stopping) {\n return;\n }\n\n this.clientReconnectAttempts++;\n if (this.clientReconnectAttempts >= this.clientMaxReconnectAttempts) {\n process.stderr.write(\n `[webmcp-local-relay] error: giving up reconnection after ${this.clientReconnectAttempts} attempts\\n`\n );\n return;\n }\n\n const delay = this.clientReconnectDelay;\n this.clientReconnectDelay = Math.min(\n this.clientReconnectDelay * 1.5,\n this.clientMaxReconnectDelay\n );\n\n this.clientReconnectTimer = setTimeout(() => {\n this.clientReconnectTimer = null;\n if (this.stopping) {\n return;\n }\n\n void this.reconnectWithModePromotion();\n }, delay);\n }\n\n /**\n * Attempts to promote from client to server mode when reconnecting.\n * Re-runs the same attach-or-bind strategy used during startup.\n */\n private async reconnectWithModePromotion(): Promise<void> {\n try {\n await this.startUsingPortStrategy();\n this.clientReconnectDelay = 500;\n this.clientReconnectAttempts = 0;\n if (this._mode === 'server') {\n process.stderr.write('[webmcp-local-relay] info: promoted from client to server mode\\n');\n this.emit('stateChanged');\n }\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: reconnection failed: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n if (!this.stopping) {\n this.scheduleReconnect();\n }\n }\n }\n\n private async invokeToolViaRelay(\n toolName: string,\n args: RelayInvokeArgs\n ): Promise<RelayCallToolResult> {\n if (!this.clientSocket || this.clientSocket.readyState !== WebSocket.OPEN) {\n throw new Error('Not connected to relay server');\n }\n\n const callId = randomUUID();\n const socket = this.clientSocket;\n\n return new Promise<RelayCallToolResult>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n this.clientPendingInvocations.delete(callId);\n reject(\n new Error(\n `Proxied invocation for tool \"${toolName}\" timed out after ${this.invokeTimeoutMs}ms`\n )\n );\n }, this.invokeTimeoutMs);\n\n this.clientPendingInvocations.set(callId, {\n connectionId: 'relay-server',\n timeoutId,\n resolve,\n reject,\n });\n\n const message: RelayClientToServerMessage = {\n type: 'relay/invoke',\n callId,\n toolName,\n args,\n };\n\n try {\n socket.send(JSON.stringify(message));\n } catch (err) {\n clearTimeout(timeoutId);\n this.clientPendingInvocations.delete(callId);\n reject(\n new Error(`Failed to send relay invocation: ${err instanceof Error ? err.message : err}`)\n );\n }\n });\n }\n\n /**\n * Builds the complete relay response payload including tools and source metadata.\n */\n private buildRelayToolsPayload(\n type: 'relay/tools' | 'relay/tools-changed'\n ): RelayServerToClientMessage {\n const tools = this.registry.listTools();\n const sources = this.registry.listSources();\n const toolSourceMap: Record<string, string[]> = {};\n\n for (const tool of tools) {\n toolSourceMap[tool.name] = tool.sources.map((s) => s.sourceId);\n }\n\n return {\n type,\n tools: tools.map(({ originalName: _originalName, sources: _sources, ...tool }) => tool),\n sources,\n toolSourceMap,\n };\n }\n\n private isHostOriginAllowed(origin: string | undefined): boolean {\n if (this.allowedOrigins.includes('*')) {\n return true;\n }\n\n if (!origin) {\n return false;\n }\n\n return this.allowedOrigins.includes(origin);\n }\n\n private sendHelloAccepted(socket: WebSocket, message: RelayHelloAcceptedMessage): void {\n try {\n socket.send(JSON.stringify(message));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send hello acceptance: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n socket.close(1011, 'Failed to send hello acceptance');\n }\n }\n\n private sendHelloRejected(\n socket: WebSocket,\n message: RelayHelloRejectedMessage,\n closeCode: number,\n closeReason: string\n ): void {\n try {\n socket.send(JSON.stringify(message), () => {\n try {\n socket.close(closeCode, closeReason);\n } catch {\n // Ignore close failures after a rejection send attempt.\n }\n });\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send hello rejection: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n socket.close(closeCode, closeReason);\n }\n }\n\n /**\n * Validates browser tool results against CallToolResultSchema.\n * Non-conforming payloads are wrapped as error results with diagnostic text.\n */\n private normalizeCallToolResult(result: unknown): RelayCallToolResult {\n const parsed = CallToolResultSchema.safeParse(result);\n if (parsed.success) {\n return parsed.data;\n }\n\n const preview = JSON.stringify(result)?.slice(0, 500) ?? 'undefined';\n process.stderr.write(\n `[webmcp-local-relay] warn: tool returned invalid CallToolResult (${parsed.error.message}), wrapping as error: ${preview}\\n`\n );\n\n return {\n content: [\n {\n type: 'text',\n text: `Tool returned an invalid result (expected {content: [...]}): ${preview}`,\n },\n ],\n isError: true,\n };\n }\n\n /**\n * Converts WebSocket raw data variants to a UTF-8 string payload.\n */\n private rawDataToUtf8(raw: WebSocket.RawData): string {\n if (Array.isArray(raw)) return Buffer.concat(raw).toString('utf8');\n if (Buffer.isBuffer(raw)) return raw.toString('utf8');\n return Buffer.from(raw).toString('utf8');\n }\n}\n","import { parseArgs } from 'node:util';\n\nimport { DEFAULT_RELAY_PORT } from './portStrategy.js';\n\n/**\n * Parsed CLI options for relay startup.\n */\nexport interface CliOptions {\n help: boolean;\n host: string;\n port: number;\n portExplicitlySet: boolean;\n allowedOrigins: string[];\n label?: string;\n workspace?: string;\n relayId?: string;\n maxPayloadBytes?: number;\n invokeTimeoutMs?: number;\n}\n\nfunction parsePositiveInteger(name: string, value: string | undefined): number | undefined {\n if (value === undefined) return undefined;\n const parsed = Number(value);\n if (!Number.isInteger(parsed) || parsed <= 0) {\n throw new Error(`Invalid ${name} \"${value}\". Must be a positive integer.`);\n }\n return parsed;\n}\n\n/**\n * Parses supported CLI flags for relay startup.\n */\nexport function parseCliOptions(argv: string[]): CliOptions {\n const { values } = parseArgs({\n args: argv,\n options: {\n help: { type: 'boolean', short: 'h' },\n host: { type: 'string', short: 'H' },\n 'allowed-origin': { type: 'string' },\n 'invoke-timeout': { type: 'string' },\n label: { type: 'string' },\n 'max-payload': { type: 'string' },\n port: { type: 'string', short: 'p' },\n 'relay-id': { type: 'string' },\n 'widget-origin': { type: 'string' },\n workspace: { type: 'string' },\n 'ws-origin': { type: 'string' },\n },\n strict: true,\n });\n\n const port = values.port === undefined ? DEFAULT_RELAY_PORT : Number(values.port);\n if (!Number.isInteger(port) || port <= 0 || port > 65535) {\n throw new Error(`Invalid port \"${values.port}\". Port must be an integer between 1 and 65535.`);\n }\n\n const invokeTimeoutMs = parsePositiveInteger('invoke-timeout', values['invoke-timeout']);\n const maxPayloadBytes = parsePositiveInteger('max-payload', values['max-payload']);\n\n const allowedOrigins = (\n values['widget-origin'] ??\n values['allowed-origin'] ??\n values['ws-origin']\n )\n ?.split(',')\n .map((origin) => origin.trim())\n .filter(Boolean) ?? ['*'];\n if (allowedOrigins.length === 0) {\n throw new Error('--widget-origin must include at least one origin.');\n }\n\n return {\n allowedOrigins,\n help: values.help ?? false,\n host: values.host ?? '127.0.0.1',\n port,\n portExplicitlySet: values.port !== undefined,\n ...(values.label === undefined ? {} : { label: values.label }),\n ...(invokeTimeoutMs === undefined ? {} : { invokeTimeoutMs }),\n ...(maxPayloadBytes === undefined ? {} : { maxPayloadBytes }),\n ...(values['relay-id'] === undefined ? {} : { relayId: values['relay-id'] }),\n ...(values.workspace === undefined ? {} : { workspace: values.workspace }),\n };\n}\n\n/**\n * Prints CLI usage to stderr.\n */\nexport function printHelp(): void {\n process.stderr.write(\n [\n 'webmcp-local-relay',\n '',\n 'Usage:',\n ' webmcp-local-relay [--host 127.0.0.1] [--port 9333] [--widget-origin https://myapp.example.com]',\n '',\n 'Options:',\n ' --host, -H Bind host for local websocket relay (default: 127.0.0.1)',\n ' --port, -p Preferred root port for the local relay cluster (default: 9333)',\n ' --widget-origin Allowed host page origin(s), comma-separated (default: *)',\n ' --allowed-origin Deprecated alias for --widget-origin',\n ' --ws-origin Deprecated alias for --widget-origin',\n ' --label Human-readable relay label reported during discovery',\n ' --workspace Optional workspace name reported during discovery',\n ' --relay-id Stable relay identifier reported during discovery',\n ' --invoke-timeout Browser tool invocation timeout in milliseconds (default: 65000)',\n ' --max-payload Maximum WebSocket payload size in bytes (default: 10000000)',\n ' --help, -h Show help',\n '',\n ].join('\\n')\n );\n}\n","import { execFile } from 'node:child_process';\n\nimport {\n fromJsonSchema,\n type JsonSchemaType,\n McpServer,\n type RegisteredTool,\n type Transport,\n} from '@modelcontextprotocol/server';\nimport { StdioServerTransport } from '@modelcontextprotocol/server/stdio';\nimport { z } from 'zod/v4';\n\nimport { RelayBridgeServer, type RelayBridgeServerOptions } from './bridgeServer.js';\nimport type { AggregatedTool, SourceInfo } from './registry.js';\n\n/**\n * Base options shared by all {@link LocalRelayMcpServer} configurations.\n */\ninterface LocalRelayMcpServerBaseOptions {\n /**\n * MCP server name reported during initialization.\n */\n serverName?: string;\n /**\n * MCP server version reported during initialization.\n */\n serverVersion?: string;\n}\n\n/**\n * Construction options for {@link LocalRelayMcpServer}.\n *\n * Provide either an existing `bridge` instance OR `bridgeOptions` to create\n * one internally — not both.\n */\nexport type LocalRelayMcpServerOptions = LocalRelayMcpServerBaseOptions &\n (\n | { bridge: RelayBridgeServer; bridgeOptions?: never }\n | { bridge?: never; bridgeOptions?: RelayBridgeServerOptions }\n );\n\n/**\n * MCP server facade that exposes browser-relayed tools over MCP transport.\n */\nexport class LocalRelayMcpServer {\n /**\n * Underlying WebSocket bridge used for browser communication.\n */\n readonly bridge: RelayBridgeServer;\n\n private readonly mcpServer: McpServer;\n private readonly dynamicTools = new Map<string, { handle: RegisteredTool; signature: string }>();\n\n private syncDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n private connected = false;\n\n /**\n * Creates a local relay MCP server with static and dynamic tool registration.\n */\n constructor(options: LocalRelayMcpServerOptions = {}) {\n this.bridge = options.bridge ?? new RelayBridgeServer(options.bridgeOptions);\n\n this.mcpServer = new McpServer({\n name: options.serverName ?? 'webmcp-local-relay',\n version: options.serverVersion ?? '0.0.0',\n });\n\n this.bridge.on('stateChanged', () => {\n this.debouncedSyncDynamicTools();\n });\n\n this.registerStaticTools();\n }\n\n /**\n * Starts the browser bridge and synchronizes dynamic MCP tools.\n */\n async start(): Promise<void> {\n await this.bridge.start();\n this.syncDynamicTools();\n }\n\n /**\n * Connects the MCP server to a transport.\n *\n * This may be called exactly once per instance lifecycle.\n */\n async connect(transport: Transport): Promise<void> {\n if (this.connected) {\n throw new Error('MCP server transport already connected');\n }\n\n await this.mcpServer.connect(transport);\n this.connected = true;\n\n this.mcpServer.server.onerror = (error) => {\n process.stderr.write(\n `[webmcp-local-relay] error: MCP protocol error: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}\\n`\n );\n };\n }\n\n /**\n * Convenience helper that connects the server over stdio transport.\n */\n async startStdio(): Promise<void> {\n await this.connect(new StdioServerTransport());\n }\n\n /**\n * Stops MCP transport and bridge resources.\n */\n async stop(): Promise<void> {\n this.connected = false;\n\n if (this.syncDebounceTimer) {\n clearTimeout(this.syncDebounceTimer);\n this.syncDebounceTimer = null;\n }\n\n let mcpCloseError: unknown;\n try {\n await this.mcpServer.close();\n } catch (err) {\n mcpCloseError = err;\n process.stderr.write(\n `[webmcp-local-relay] error: failed to close MCP server: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n );\n }\n await this.bridge.stop();\n if (mcpCloseError) {\n process.stderr.write(\n '[webmcp-local-relay] warn: shutdown completed with errors (MCP server close failed)\\n'\n );\n }\n }\n\n /**\n * Returns dynamic tool names currently registered in MCP.\n */\n listDynamicToolNames(): string[] {\n return Array.from(this.dynamicTools.keys()).sort();\n }\n\n /**\n * Registers built-in management tools exposed by the relay.\n */\n private registerStaticTools(): void {\n this.mcpServer.registerTool(\n 'webmcp_list_sources',\n {\n description: 'List connected browser tool sources and their metadata.',\n inputSchema: z.object({}),\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n },\n },\n async () => {\n const clientMode = this.bridge.mode === 'client';\n const sources = clientMode\n ? this.bridge.listSourcesFromRelay()\n : this.bridge.registry.listSources();\n const info = {\n ...(clientMode ? { mode: 'client' as const } : {}),\n count: sources.length,\n sources,\n };\n return {\n content: [{ type: 'text', text: JSON.stringify(info, null, 2) }],\n structuredContent: info,\n };\n }\n );\n\n this.mcpServer.registerTool(\n 'webmcp_list_tools',\n {\n description:\n 'List WebMCP tools available from connected browser sources. Returns tool definitions including name, description, input schema, and source info.',\n inputSchema: z.object({}),\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n },\n },\n async () => {\n const tools = this.listAggregatedTools();\n const info = { count: tools.length, tools };\n return {\n content: [{ type: 'text', text: JSON.stringify(info, null, 2) }],\n structuredContent: info,\n };\n }\n );\n\n this.mcpServer.registerTool(\n 'webmcp_open_page',\n {\n description:\n \"Open a URL in the user's default browser, or refresh a connected source page. Use to launch WebMCP-enabled pages or reload stale connections.\",\n inputSchema: z.object({\n url: z.string().describe('URL to open or match for refresh.'),\n refresh: z\n .boolean()\n .optional()\n .describe(\n 'If true, refresh the connected source matching this URL instead of opening a new tab.'\n ),\n }),\n annotations: { readOnlyHint: false },\n },\n async ({ url, refresh }) => {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return {\n content: [{ type: 'text' as const, text: `Invalid URL: ${url}` }],\n isError: true,\n };\n }\n\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Only http: and https: URLs are allowed. Got: ${parsed.protocol}`,\n },\n ],\n isError: true,\n };\n }\n\n const existing =\n this.bridge.mode === 'server'\n ? this.bridge.registry\n .listSources()\n .find((source) => source.url && URL.parse(source.url)?.origin === parsed.origin)\n : undefined;\n\n if (refresh) {\n if (this.bridge.mode === 'client') {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Refresh is not supported in client mode. Only the server relay can reload sources.',\n },\n ],\n isError: true,\n };\n }\n\n if (!existing) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `No connected source matches origin ${parsed.origin}. The page may not be open or connected.`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n this.bridge.reloadSource(existing.sourceId);\n return {\n content: [\n {\n type: 'text' as const,\n text: `Reload sent to source ${existing.sourceId} (${existing.url ?? existing.origin}).`,\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to reload source: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n try {\n await this.openInBrowser(url);\n } catch (err) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to open browser: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n\n if (existing) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Opened ${url} in the default browser. Note: a source from ${existing.url ?? existing.origin} is already connected.`,\n },\n ],\n };\n }\n\n return {\n content: [{ type: 'text' as const, text: `Opened ${url} in the default browser.` }],\n };\n }\n );\n }\n\n /**\n * Opens a URL in the user's default browser using the platform open command.\n *\n * Passes the re-serialized `URL.href` directly to the platform launcher.\n * No shell or command-language string is involved.\n */\n private openInBrowser(url: string): Promise<void> {\n const safeUrl = new URL(url).href;\n const command =\n process.platform === 'darwin'\n ? 'open'\n : process.platform === 'win32'\n ? 'explorer.exe'\n : 'xdg-open';\n return new Promise((resolve, reject) => {\n execFile(command, [safeUrl], (err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n /**\n * Debounces rapid stateChanged events into a single sync pass.\n *\n * Browser reconnections emit multiple stateChanged events in quick\n * succession (hello, tools/list). Without debouncing, each triggers a\n * full sync that removes and re-registers tools — sending per-operation\n * notifications through the stdio transport and overwhelming the agent.\n */\n private debouncedSyncDynamicTools(): void {\n if (this.syncDebounceTimer) {\n return;\n }\n\n this.syncDebounceTimer = setTimeout(() => {\n this.syncDebounceTimer = null;\n try {\n this.syncDynamicTools();\n } catch (err) {\n const details = err instanceof Error ? (err.stack ?? err.message) : String(err);\n process.stderr.write(\n `[webmcp-local-relay] error: failed to sync dynamic tools: ${details}\\n`\n );\n }\n }, 16);\n }\n\n /**\n * Returns the current aggregated tool list, dispatching based on bridge mode.\n */\n private listAggregatedTools(): AggregatedTool[] {\n return this.bridge.mode === 'client'\n ? this.buildAggregatedToolsFromRelay()\n : this.bridge.registry.listTools();\n }\n\n /**\n * Converts relay client tool descriptors to aggregated tool shape.\n * Populates per-tool source metadata from the relay's tool-source mapping.\n */\n private buildAggregatedToolsFromRelay(): AggregatedTool[] {\n const toolSourceMap = this.bridge.getToolSourceMapFromRelay();\n const allSources = this.bridge.listSourcesFromRelay();\n const sourceById = new Map(allSources.map((s) => [s.sourceId, s]));\n\n return this.bridge\n .listToolsFromRelay()\n .map((tool) => {\n const sourceIds = toolSourceMap[tool.name] ?? [];\n const sources: SourceInfo[] = [];\n for (const id of sourceIds) {\n const source = sourceById.get(id);\n if (source) {\n sources.push(source);\n } else {\n process.stderr.write(\n `[webmcp-local-relay] warn: tool \"${tool.name}\" references unknown sourceId \"${id}\"\\n`\n );\n }\n }\n\n return {\n ...tool,\n originalName: tool.name,\n sources,\n };\n })\n .sort((left, right) => left.name.localeCompare(right.name));\n }\n\n /**\n * Applies current aggregated tool state to MCP dynamic registrations.\n */\n private syncDynamicTools(): void {\n const tools = this.listAggregatedTools();\n const nextNames = new Set(tools.map((tool) => tool.name));\n\n for (const [name, registration] of this.dynamicTools) {\n if (nextNames.has(name)) {\n continue;\n }\n\n registration.handle.remove();\n this.dynamicTools.delete(name);\n }\n\n for (const tool of tools) {\n const signature = this.toolSignature(tool);\n const current = this.dynamicTools.get(tool.name);\n\n if (current?.signature === signature) {\n continue;\n }\n\n if (current) {\n current.handle.remove();\n this.dynamicTools.delete(tool.name);\n }\n\n try {\n const handle = this.registerDynamicTool(tool);\n this.dynamicTools.set(tool.name, { handle, signature });\n } catch (err) {\n const details = err instanceof Error ? (err.stack ?? err.message) : String(err);\n process.stderr.write(\n `[webmcp-local-relay] warn: skipped dynamic tool \"${tool.name}\" because its schema could not be compiled: ${details}\\n`\n );\n }\n }\n }\n\n /**\n * Registers a single dynamic tool and returns a removal handle.\n */\n private registerDynamicTool(tool: AggregatedTool): RegisteredTool {\n // `AggregatedTool` has already passed the SDK's ToolSchema validation.\n // JsonSchemaType's exact optional properties are structurally narrower\n // than the protocol Tool type even though both describe JSON Schema.\n const inputSchema = fromJsonSchema<Record<string, unknown>>(tool.inputSchema as JsonSchemaType);\n const outputSchema = tool.outputSchema\n ? fromJsonSchema(tool.outputSchema as JsonSchemaType)\n : undefined;\n\n return this.mcpServer.registerTool(\n tool.name,\n {\n ...(tool.title !== undefined ? { title: tool.title } : {}),\n description: this.dynamicToolDescription(tool),\n inputSchema,\n ...(outputSchema ? { outputSchema } : {}),\n ...(tool.annotations ? { annotations: tool.annotations } : {}),\n ...(tool.icons ? { icons: tool.icons } : {}),\n ...(tool._meta ? { _meta: tool._meta } : {}),\n },\n async (args: Record<string, unknown>) => {\n try {\n return await this.bridge.invokeTool(tool.name, args);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const details = err instanceof Error ? (err.stack ?? err.message) : String(err);\n process.stderr.write(\n `[webmcp-local-relay] error: dynamic tool \"${tool.name}\" invocation failed: ${details}\\n`\n );\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to invoke relayed tool \"${tool.name}\": ${message}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n }\n\n /**\n * Builds a display description for relayed tools including source context.\n * In client mode, source metadata is unavailable, so tools are labeled `[WebMCP relay]`.\n */\n private dynamicToolDescription(tool: AggregatedTool): string {\n const source = tool.sources[0];\n const sourceLabel = source\n ? `[WebMCP ${source.tabId}${source.title ? ` • ${source.title}` : ''}]`\n : '[WebMCP relay]';\n\n return `${sourceLabel} ${tool.description ?? `Relayed tool ${tool.originalName}`}`;\n }\n\n /**\n * Produces a stable signature for change detection of dynamic tool metadata.\n *\n * Deliberately excludes `sourceId` (which is a per-connection UUID that\n * changes on every browser reconnect) so that a reconnecting tab with the\n * same tools does not trigger a needless remove+re-register cycle.\n */\n private toolSignature(tool: AggregatedTool): string {\n return JSON.stringify({\n originalName: tool.originalName,\n title: tool.title,\n description: tool.description,\n inputSchema: tool.inputSchema,\n outputSchema: tool.outputSchema,\n annotations: tool.annotations,\n _meta: tool._meta,\n icons: tool.icons,\n source: tool.sources[0]\n ? { tabId: tool.sources[0].tabId, title: tool.sources[0].title }\n : undefined,\n });\n }\n}\n"],"mappings":"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"}
|