@mcp-b/webmcp-local-relay 1.7.3 → 1.7.5
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.js +1 -1
- package/dist/index.d.ts +66 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{mcpRelayServer-B8d7NAUK.js → mcpRelayServer-DAp7h9b1.js} +82 -23
- package/dist/mcpRelayServer-DAp7h9b1.js.map +1 -0
- package/package.json +1 -1
- package/dist/mcpRelayServer-B8d7NAUK.js.map +0 -1
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -224,6 +224,27 @@ declare const RelayToBrowserMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
|
|
|
224
224
|
* Relay-to-browser message payload.
|
|
225
225
|
*/
|
|
226
226
|
type RelayToBrowserMessage = z.infer<typeof RelayToBrowserMessageSchema>;
|
|
227
|
+
/**
|
|
228
|
+
* Relay-to-relay protocol (client mode <-> server mode).
|
|
229
|
+
*/
|
|
230
|
+
/**
|
|
231
|
+
* Schema for source metadata transmitted in relay-to-relay messages.
|
|
232
|
+
*/
|
|
233
|
+
declare const RelaySourceInfoSchema: z.ZodObject<{
|
|
234
|
+
sourceId: z.ZodString;
|
|
235
|
+
tabId: z.ZodString;
|
|
236
|
+
origin: z.ZodOptional<z.ZodString>;
|
|
237
|
+
url: z.ZodOptional<z.ZodString>;
|
|
238
|
+
title: z.ZodOptional<z.ZodString>;
|
|
239
|
+
iconUrl: z.ZodOptional<z.ZodString>;
|
|
240
|
+
connectedAt: z.ZodNumber;
|
|
241
|
+
lastSeenAt: z.ZodNumber;
|
|
242
|
+
toolCount: z.ZodNumber;
|
|
243
|
+
}, z.core.$strip>;
|
|
244
|
+
/**
|
|
245
|
+
* Source metadata transmitted in relay-to-relay messages.
|
|
246
|
+
*/
|
|
247
|
+
type RelaySourceInfo = z.infer<typeof RelaySourceInfoSchema>;
|
|
227
248
|
/**
|
|
228
249
|
* Union schema for all relay-client-to-server messages.
|
|
229
250
|
*/
|
|
@@ -245,7 +266,6 @@ type RelayClientToServerMessage = z.infer<typeof RelayClientToServerMessageSchem
|
|
|
245
266
|
* Union schema for all relay-server-to-client messages.
|
|
246
267
|
*/
|
|
247
268
|
declare const RelayServerToClientMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
248
|
-
type: z.ZodLiteral<"relay/tools">;
|
|
249
269
|
tools: z.ZodArray<z.ZodObject<{
|
|
250
270
|
description: z.ZodOptional<z.ZodString>;
|
|
251
271
|
inputSchema: z.ZodObject<{
|
|
@@ -285,6 +305,19 @@ declare const RelayServerToClientMessageSchema: z.ZodDiscriminatedUnion<[z.ZodOb
|
|
|
285
305
|
title: z.ZodOptional<z.ZodString>;
|
|
286
306
|
name: z.ZodString;
|
|
287
307
|
}, z.core.$strip>>;
|
|
308
|
+
sources: z.ZodArray<z.ZodObject<{
|
|
309
|
+
sourceId: z.ZodString;
|
|
310
|
+
tabId: z.ZodString;
|
|
311
|
+
origin: z.ZodOptional<z.ZodString>;
|
|
312
|
+
url: z.ZodOptional<z.ZodString>;
|
|
313
|
+
title: z.ZodOptional<z.ZodString>;
|
|
314
|
+
iconUrl: z.ZodOptional<z.ZodString>;
|
|
315
|
+
connectedAt: z.ZodNumber;
|
|
316
|
+
lastSeenAt: z.ZodNumber;
|
|
317
|
+
toolCount: z.ZodNumber;
|
|
318
|
+
}, z.core.$strip>>;
|
|
319
|
+
toolSourceMap: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>;
|
|
320
|
+
type: z.ZodLiteral<"relay/tools">;
|
|
288
321
|
}, z.core.$strip>, z.ZodObject<{
|
|
289
322
|
type: z.ZodLiteral<"relay/result">;
|
|
290
323
|
callId: z.ZodString;
|
|
@@ -385,7 +418,6 @@ declare const RelayServerToClientMessageSchema: z.ZodDiscriminatedUnion<[z.ZodOb
|
|
|
385
418
|
isError: z.ZodOptional<z.ZodBoolean>;
|
|
386
419
|
}, z.core.$loose>;
|
|
387
420
|
}, z.core.$strip>, z.ZodObject<{
|
|
388
|
-
type: z.ZodLiteral<"relay/tools-changed">;
|
|
389
421
|
tools: z.ZodArray<z.ZodObject<{
|
|
390
422
|
description: z.ZodOptional<z.ZodString>;
|
|
391
423
|
inputSchema: z.ZodObject<{
|
|
@@ -425,6 +457,19 @@ declare const RelayServerToClientMessageSchema: z.ZodDiscriminatedUnion<[z.ZodOb
|
|
|
425
457
|
title: z.ZodOptional<z.ZodString>;
|
|
426
458
|
name: z.ZodString;
|
|
427
459
|
}, z.core.$strip>>;
|
|
460
|
+
sources: z.ZodArray<z.ZodObject<{
|
|
461
|
+
sourceId: z.ZodString;
|
|
462
|
+
tabId: z.ZodString;
|
|
463
|
+
origin: z.ZodOptional<z.ZodString>;
|
|
464
|
+
url: z.ZodOptional<z.ZodString>;
|
|
465
|
+
title: z.ZodOptional<z.ZodString>;
|
|
466
|
+
iconUrl: z.ZodOptional<z.ZodString>;
|
|
467
|
+
connectedAt: z.ZodNumber;
|
|
468
|
+
lastSeenAt: z.ZodNumber;
|
|
469
|
+
toolCount: z.ZodNumber;
|
|
470
|
+
}, z.core.$strip>>;
|
|
471
|
+
toolSourceMap: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>;
|
|
472
|
+
type: z.ZodLiteral<"relay/tools-changed">;
|
|
428
473
|
}, z.core.$strip>]>;
|
|
429
474
|
/**
|
|
430
475
|
* Relay server to client message payload.
|
|
@@ -628,6 +673,8 @@ declare class RelayBridgeServer extends EventEmitter {
|
|
|
628
673
|
private clientReconnectAttempts;
|
|
629
674
|
private readonly clientPendingInvocations;
|
|
630
675
|
private clientTools;
|
|
676
|
+
private clientSources;
|
|
677
|
+
private clientToolSourceMap;
|
|
631
678
|
private stopping;
|
|
632
679
|
/**
|
|
633
680
|
* Creates a relay bridge server instance.
|
|
@@ -647,6 +694,17 @@ declare class RelayBridgeServer extends EventEmitter {
|
|
|
647
694
|
* Returns an empty array in server mode.
|
|
648
695
|
*/
|
|
649
696
|
listToolsFromRelay(): RelayTool[];
|
|
697
|
+
/**
|
|
698
|
+
* Source metadata received from the server relay (client mode only).
|
|
699
|
+
* Returns an empty array in server mode.
|
|
700
|
+
*/
|
|
701
|
+
listSourcesFromRelay(): RelaySourceInfo[];
|
|
702
|
+
/**
|
|
703
|
+
* Tool-to-source mapping received from the server relay (client mode only).
|
|
704
|
+
* Maps public tool names to arrays of source IDs.
|
|
705
|
+
* Returns an empty record in server mode.
|
|
706
|
+
*/
|
|
707
|
+
getToolSourceMapFromRelay(): Record<string, string[]>;
|
|
650
708
|
/**
|
|
651
709
|
* Starts the bridge. Attempts to bind a WebSocket server (server mode).
|
|
652
710
|
* If the port is already in use, automatically falls back to client mode
|
|
@@ -704,6 +762,10 @@ declare class RelayBridgeServer extends EventEmitter {
|
|
|
704
762
|
*/
|
|
705
763
|
private reconnectAsClient;
|
|
706
764
|
private invokeToolViaRelay;
|
|
765
|
+
/**
|
|
766
|
+
* Builds the complete relay response payload including tools and source metadata.
|
|
767
|
+
*/
|
|
768
|
+
private buildRelayToolsPayload;
|
|
707
769
|
/**
|
|
708
770
|
* Maps aggregated tools to the wire format used by the relay protocol.
|
|
709
771
|
*/
|
|
@@ -831,7 +893,7 @@ declare class LocalRelayMcpServer {
|
|
|
831
893
|
private listAggregatedTools;
|
|
832
894
|
/**
|
|
833
895
|
* Converts relay client tool descriptors to aggregated tool shape.
|
|
834
|
-
*
|
|
896
|
+
* Populates per-tool source metadata from the relay's tool-source mapping.
|
|
835
897
|
*/
|
|
836
898
|
private buildAggregatedToolsFromRelay;
|
|
837
899
|
/**
|
|
@@ -878,5 +940,5 @@ declare function buildPublicToolName(options: {
|
|
|
878
940
|
disambiguate?: boolean;
|
|
879
941
|
}): string;
|
|
880
942
|
//#endregion
|
|
881
|
-
export { type AggregatedTool, type BrowserToRelayMessage, BrowserToRelayMessageSchema, CallToolRequestParamsSchema, type CallToolResult, CallToolResultSchema, type CliOptions, DEFAULT_TOOL_INPUT_SCHEMA, HelloRequiredError, InboundToolSchema, LocalRelayMcpServer, type LocalRelayMcpServerOptions, NormalizedToolSchema, RelayBridgeServer, type RelayBridgeServerOptions, type RelayCallToolResult, type RelayClientToServerMessage, RelayClientToServerMessageSchema, type RelayInvokeArgs, RelayInvokeArgsSchema, RelayRegistry, type RelayServerToClientMessage, RelayServerToClientMessageSchema, type RelayToBrowserMessage, RelayToBrowserMessageSchema, type RelayTool, type RelayToolAnnotations, type ResolvedInvocation, type SourceInfo, type Tool, type ToolAnnotations, ToolAnnotationsSchema, ToolSchema, buildPublicToolName, extractSanitizedDomain, normalizeInboundTool, parseCliOptions, printHelp, sanitizeName };
|
|
943
|
+
export { type AggregatedTool, type BrowserToRelayMessage, BrowserToRelayMessageSchema, CallToolRequestParamsSchema, type CallToolResult, CallToolResultSchema, type CliOptions, DEFAULT_TOOL_INPUT_SCHEMA, HelloRequiredError, InboundToolSchema, LocalRelayMcpServer, type LocalRelayMcpServerOptions, NormalizedToolSchema, RelayBridgeServer, type RelayBridgeServerOptions, type RelayCallToolResult, type RelayClientToServerMessage, RelayClientToServerMessageSchema, type RelayInvokeArgs, RelayInvokeArgsSchema, RelayRegistry, type RelayServerToClientMessage, RelayServerToClientMessageSchema, type RelaySourceInfo, RelaySourceInfoSchema, type RelayToBrowserMessage, RelayToBrowserMessageSchema, type RelayTool, type RelayToolAnnotations, type ResolvedInvocation, type SourceInfo, type Tool, type ToolAnnotations, ToolAnnotationsSchema, ToolSchema, buildPublicToolName, extractSanitizedDomain, normalizeInboundTool, parseCliOptions, printHelp, sanitizeName };
|
|
882
944
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/protocol.ts","../src/schemas.ts","../src/registry.ts","../src/bridgeServer.ts","../src/cli-utils.ts","../src/mcpRelayServer.ts","../src/naming.ts"],"sourcesContent":[],"mappings":";;;;;;;;;cAca,sBAAoB,CAAA,CAAA;EAApB,WAAA,eAEX,YAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAQW,mBAAiB,CAAA,CAAA;;;;;;cAKjB,uBAAqB,CAAA,CAAA,YAAA,CAAA,CAAA,UAAA,CAAA,CAAA,WAAA,CAAA,CAAA;;;;;cAMrB,2BAA2B;;;;KAQ5B,SAAA,GAAY,CAAA,CAAE,aAAa;;;;KAK3B,oBAAA,GAAuB;;;AAxBnC;KA6BY,mBAAA,GAAsB;;;;AAxBrB,KA6BD,eAAA,GAAkB,OA7BkD,CA6B1C,CAAA,CAAE,KA7BwC,CAAA,OA6B3B,qBA7B2B,CAAA,EAAA,SAAA,CAAA;;;;;;AAMhF;AAQY,iBAuBI,oBAAA,CAvBuB,OAAb,EAuBoB,CAAA,CAAE,KAvBjB,CAAA,OAuB8B,iBAvB9B,CAAA,CAAA,EAuBmD,SAvBnD;;;;;;cC9BlB,2BAAyB,CAAA,CAAA;;EDCzB,KAAA,aAAA;;;;;;;;;cCwDA,6BAA2B,CAAA,CAAA,uBAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAAA,aAAA,CAAA,EAAA,OAAA,GAAA,SAAA;IAAA,CAAA,GAAA,SAAA;IAW5B,SAAA,CAAA,EAAA;MAKA,WAAA,CAAA,EAAmB,UAAA,GAAkB,UAAA,GAAA,WAAA,GAAf,SAAO;IA6B5B,CAAA,GAAA,SAAA;;;;;;;;;EAA2B,CAAA,EAAA,EAAA;;;;;;;IAAA,IAAA,aAAA;EAAA,CAAA,eAAA,CAAA,CAAA,gBAAA,CAAA;IAS5B,WAAA,EAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/protocol.ts","../src/schemas.ts","../src/registry.ts","../src/bridgeServer.ts","../src/cli-utils.ts","../src/mcpRelayServer.ts","../src/naming.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as printHelp, S as parseCliOptions, _ as NormalizedToolSchema, a as RelayServerToClientMessageSchema, b as ToolSchema, c as HelloRequiredError, d as extractSanitizedDomain, f as sanitizeName, g as InboundToolSchema, h as DEFAULT_TOOL_INPUT_SCHEMA, i as RelayClientToServerMessageSchema, l as RelayRegistry, m as CallToolResultSchema, n as RelayBridgeServer, o as RelaySourceInfoSchema, p as CallToolRequestParamsSchema, r as BrowserToRelayMessageSchema, s as RelayToBrowserMessageSchema, t as LocalRelayMcpServer, u as buildPublicToolName, v as RelayInvokeArgsSchema, x as normalizeInboundTool, y as ToolAnnotationsSchema } from "./mcpRelayServer-DAp7h9b1.js";
|
|
2
2
|
|
|
3
|
-
export { BrowserToRelayMessageSchema, CallToolRequestParamsSchema, CallToolResultSchema, DEFAULT_TOOL_INPUT_SCHEMA, HelloRequiredError, InboundToolSchema, LocalRelayMcpServer, NormalizedToolSchema, RelayBridgeServer, RelayClientToServerMessageSchema, RelayInvokeArgsSchema, RelayRegistry, RelayServerToClientMessageSchema, RelayToBrowserMessageSchema, ToolAnnotationsSchema, ToolSchema, buildPublicToolName, extractSanitizedDomain, normalizeInboundTool, parseCliOptions, printHelp, sanitizeName };
|
|
3
|
+
export { BrowserToRelayMessageSchema, CallToolRequestParamsSchema, CallToolResultSchema, DEFAULT_TOOL_INPUT_SCHEMA, HelloRequiredError, InboundToolSchema, LocalRelayMcpServer, NormalizedToolSchema, RelayBridgeServer, RelayClientToServerMessageSchema, RelayInvokeArgsSchema, RelayRegistry, RelayServerToClientMessageSchema, RelaySourceInfoSchema, RelayToBrowserMessageSchema, ToolAnnotationsSchema, ToolSchema, buildPublicToolName, extractSanitizedDomain, normalizeInboundTool, parseCliOptions, printHelp, sanitizeName };
|
|
@@ -475,6 +475,20 @@ const RelayToBrowserMessageSchema = z.discriminatedUnion("type", [
|
|
|
475
475
|
* Relay-to-relay protocol (client mode <-> server mode).
|
|
476
476
|
*/
|
|
477
477
|
/**
|
|
478
|
+
* Schema for source metadata transmitted in relay-to-relay messages.
|
|
479
|
+
*/
|
|
480
|
+
const RelaySourceInfoSchema = z.object({
|
|
481
|
+
sourceId: z.string(),
|
|
482
|
+
tabId: z.string(),
|
|
483
|
+
origin: z.string().optional(),
|
|
484
|
+
url: z.string().optional(),
|
|
485
|
+
title: z.string().optional(),
|
|
486
|
+
iconUrl: z.string().optional(),
|
|
487
|
+
connectedAt: z.number(),
|
|
488
|
+
lastSeenAt: z.number(),
|
|
489
|
+
toolCount: z.number()
|
|
490
|
+
});
|
|
491
|
+
/**
|
|
478
492
|
* Schema for relay client identification message.
|
|
479
493
|
*/
|
|
480
494
|
const RelayClientHelloSchema = z.object({ type: z.literal("relay/hello") });
|
|
@@ -499,12 +513,17 @@ const RelayClientToServerMessageSchema = z.discriminatedUnion("type", [
|
|
|
499
513
|
RelayClientListToolsSchema,
|
|
500
514
|
RelayClientInvokeSchema
|
|
501
515
|
]);
|
|
516
|
+
const RelayToolsPayloadFields = {
|
|
517
|
+
tools: z.array(NormalizedToolSchema),
|
|
518
|
+
sources: z.array(RelaySourceInfoSchema),
|
|
519
|
+
toolSourceMap: z.record(z.string(), z.array(z.string()))
|
|
520
|
+
};
|
|
502
521
|
/**
|
|
503
522
|
* Schema for relay server tool list response.
|
|
504
523
|
*/
|
|
505
524
|
const RelayServerToolsSchema = z.object({
|
|
506
525
|
type: z.literal("relay/tools"),
|
|
507
|
-
|
|
526
|
+
...RelayToolsPayloadFields
|
|
508
527
|
});
|
|
509
528
|
/**
|
|
510
529
|
* Schema for relay server invocation result response.
|
|
@@ -519,7 +538,7 @@ const RelayServerResultSchema = z.object({
|
|
|
519
538
|
*/
|
|
520
539
|
const RelayServerToolsChangedSchema = z.object({
|
|
521
540
|
type: z.literal("relay/tools-changed"),
|
|
522
|
-
|
|
541
|
+
...RelayToolsPayloadFields
|
|
523
542
|
});
|
|
524
543
|
/**
|
|
525
544
|
* Union schema for all relay-server-to-client messages.
|
|
@@ -571,6 +590,8 @@ var RelayBridgeServer = class extends EventEmitter {
|
|
|
571
590
|
clientReconnectAttempts = 0;
|
|
572
591
|
clientPendingInvocations = /* @__PURE__ */ new Map();
|
|
573
592
|
clientTools = [];
|
|
593
|
+
clientSources = [];
|
|
594
|
+
clientToolSourceMap = {};
|
|
574
595
|
stopping = false;
|
|
575
596
|
/**
|
|
576
597
|
* Creates a relay bridge server instance.
|
|
@@ -608,6 +629,21 @@ var RelayBridgeServer = class extends EventEmitter {
|
|
|
608
629
|
return this._mode === "client" ? [...this.clientTools] : [];
|
|
609
630
|
}
|
|
610
631
|
/**
|
|
632
|
+
* Source metadata received from the server relay (client mode only).
|
|
633
|
+
* Returns an empty array in server mode.
|
|
634
|
+
*/
|
|
635
|
+
listSourcesFromRelay() {
|
|
636
|
+
return this._mode === "client" ? [...this.clientSources] : [];
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Tool-to-source mapping received from the server relay (client mode only).
|
|
640
|
+
* Maps public tool names to arrays of source IDs.
|
|
641
|
+
* Returns an empty record in server mode.
|
|
642
|
+
*/
|
|
643
|
+
getToolSourceMapFromRelay() {
|
|
644
|
+
return this._mode === "client" ? { ...this.clientToolSourceMap } : {};
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
611
647
|
* Starts the bridge. Attempts to bind a WebSocket server (server mode).
|
|
612
648
|
* If the port is already in use, automatically falls back to client mode
|
|
613
649
|
* and proxies through the existing relay.
|
|
@@ -853,11 +889,7 @@ var RelayBridgeServer = class extends EventEmitter {
|
|
|
853
889
|
this.relayClientConnectionIds.add(connectionId);
|
|
854
890
|
break;
|
|
855
891
|
case "relay/list-tools": {
|
|
856
|
-
const
|
|
857
|
-
const response = {
|
|
858
|
-
type: "relay/tools",
|
|
859
|
-
tools: this.toWireTools(tools)
|
|
860
|
-
};
|
|
892
|
+
const response = this.buildRelayToolsPayload("relay/tools");
|
|
861
893
|
const socket = this.socketByConnectionId.get(connectionId);
|
|
862
894
|
if (socket?.readyState === WebSocket.OPEN) try {
|
|
863
895
|
socket.send(JSON.stringify(response));
|
|
@@ -912,11 +944,7 @@ var RelayBridgeServer = class extends EventEmitter {
|
|
|
912
944
|
*/
|
|
913
945
|
pushToolsToRelayClients() {
|
|
914
946
|
if (this.relayClientConnectionIds.size === 0) return;
|
|
915
|
-
const
|
|
916
|
-
const message = {
|
|
917
|
-
type: "relay/tools-changed",
|
|
918
|
-
tools: this.toWireTools(tools)
|
|
919
|
-
};
|
|
947
|
+
const message = this.buildRelayToolsPayload("relay/tools-changed");
|
|
920
948
|
const payload = JSON.stringify(message);
|
|
921
949
|
for (const connectionId of this.relayClientConnectionIds) {
|
|
922
950
|
const socket = this.socketByConnectionId.get(connectionId);
|
|
@@ -992,6 +1020,8 @@ var RelayBridgeServer = class extends EventEmitter {
|
|
|
992
1020
|
case "relay/tools":
|
|
993
1021
|
case "relay/tools-changed":
|
|
994
1022
|
this.clientTools = msg.data.tools;
|
|
1023
|
+
this.clientSources = msg.data.sources;
|
|
1024
|
+
this.clientToolSourceMap = msg.data.toolSourceMap;
|
|
995
1025
|
this.emit("stateChanged");
|
|
996
1026
|
break;
|
|
997
1027
|
case "relay/result": {
|
|
@@ -1015,6 +1045,8 @@ var RelayBridgeServer = class extends EventEmitter {
|
|
|
1015
1045
|
pending.reject(/* @__PURE__ */ new Error("Relay server connection lost during invocation"));
|
|
1016
1046
|
}
|
|
1017
1047
|
this.clientTools = [];
|
|
1048
|
+
this.clientSources = [];
|
|
1049
|
+
this.clientToolSourceMap = {};
|
|
1018
1050
|
this.emit("stateChanged");
|
|
1019
1051
|
if (!this.stopping) this.scheduleReconnect();
|
|
1020
1052
|
});
|
|
@@ -1126,6 +1158,21 @@ var RelayBridgeServer = class extends EventEmitter {
|
|
|
1126
1158
|
});
|
|
1127
1159
|
}
|
|
1128
1160
|
/**
|
|
1161
|
+
* Builds the complete relay response payload including tools and source metadata.
|
|
1162
|
+
*/
|
|
1163
|
+
buildRelayToolsPayload(type) {
|
|
1164
|
+
const tools = this.registry.listTools();
|
|
1165
|
+
const sources = this.registry.listSources();
|
|
1166
|
+
const toolSourceMap = {};
|
|
1167
|
+
for (const tool of tools) toolSourceMap[tool.name] = tool.sources.map((s) => s.sourceId);
|
|
1168
|
+
return {
|
|
1169
|
+
type,
|
|
1170
|
+
tools: this.toWireTools(tools),
|
|
1171
|
+
sources,
|
|
1172
|
+
toolSourceMap
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1129
1176
|
* Maps aggregated tools to the wire format used by the relay protocol.
|
|
1130
1177
|
*/
|
|
1131
1178
|
toWireTools(tools) {
|
|
@@ -1254,11 +1301,11 @@ var LocalRelayMcpServer = class {
|
|
|
1254
1301
|
}
|
|
1255
1302
|
}, async () => {
|
|
1256
1303
|
if (this.bridge.mode === "client") {
|
|
1304
|
+
const sources$1 = this.bridge.listSourcesFromRelay();
|
|
1257
1305
|
const info = {
|
|
1258
1306
|
mode: "client",
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
sources: []
|
|
1307
|
+
count: sources$1.length,
|
|
1308
|
+
sources: sources$1
|
|
1262
1309
|
};
|
|
1263
1310
|
return {
|
|
1264
1311
|
content: [{
|
|
@@ -1523,14 +1570,26 @@ var LocalRelayMcpServer = class {
|
|
|
1523
1570
|
}
|
|
1524
1571
|
/**
|
|
1525
1572
|
* Converts relay client tool descriptors to aggregated tool shape.
|
|
1526
|
-
*
|
|
1573
|
+
* Populates per-tool source metadata from the relay's tool-source mapping.
|
|
1527
1574
|
*/
|
|
1528
1575
|
buildAggregatedToolsFromRelay() {
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1576
|
+
const toolSourceMap = this.bridge.getToolSourceMapFromRelay();
|
|
1577
|
+
const allSources = this.bridge.listSourcesFromRelay();
|
|
1578
|
+
const sourceById = new Map(allSources.map((s) => [s.sourceId, s]));
|
|
1579
|
+
return this.bridge.listToolsFromRelay().map((tool) => {
|
|
1580
|
+
const sourceIds = toolSourceMap[tool.name] ?? [];
|
|
1581
|
+
const sources = [];
|
|
1582
|
+
for (const id of sourceIds) {
|
|
1583
|
+
const source = sourceById.get(id);
|
|
1584
|
+
if (source) sources.push(source);
|
|
1585
|
+
else process.stderr.write(`[webmcp-local-relay] warn: tool "${tool.name}" references unknown sourceId "${id}"\n`);
|
|
1586
|
+
}
|
|
1587
|
+
return {
|
|
1588
|
+
...tool,
|
|
1589
|
+
originalName: tool.name,
|
|
1590
|
+
sources
|
|
1591
|
+
};
|
|
1592
|
+
}).sort((left, right) => left.name.localeCompare(right.name));
|
|
1534
1593
|
}
|
|
1535
1594
|
/**
|
|
1536
1595
|
* Applies registry tool state to MCP dynamic registrations.
|
|
@@ -1635,5 +1694,5 @@ var LocalRelayMcpServer = class {
|
|
|
1635
1694
|
};
|
|
1636
1695
|
|
|
1637
1696
|
//#endregion
|
|
1638
|
-
export { printHelp as S,
|
|
1639
|
-
//# sourceMappingURL=mcpRelayServer-
|
|
1697
|
+
export { printHelp as C, parseCliOptions as S, NormalizedToolSchema as _, RelayServerToClientMessageSchema as a, ToolSchema as b, HelloRequiredError as c, extractSanitizedDomain as d, sanitizeName as f, InboundToolSchema as g, DEFAULT_TOOL_INPUT_SCHEMA as h, RelayClientToServerMessageSchema as i, RelayRegistry as l, CallToolResultSchema as m, RelayBridgeServer as n, RelaySourceInfoSchema as o, CallToolRequestParamsSchema as p, BrowserToRelayMessageSchema as r, RelayToBrowserMessageSchema as s, LocalRelayMcpServer as t, buildPublicToolName as u, RelayInvokeArgsSchema as v, normalizeInboundTool as x, ToolAnnotationsSchema as y };
|
|
1698
|
+
//# sourceMappingURL=mcpRelayServer-DAp7h9b1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpRelayServer-DAp7h9b1.js","names":["options: CliOptions","DEFAULT_TOOL_INPUT_SCHEMA: Tool['inputSchema']","normalizedCandidate: Record<string, unknown>","now: () => number","providers: ToolProvider[]","result: AggregatedTool[]","provider","message: RelayToBrowserMessage","resolveOptions: { toolName: string; sourceId?: string; requestTabId?: string }","parsedJson: unknown","response: RelayServerToClientMessage","parsed: unknown","message: RelayClientToServerMessage","toolSourceMap: Record<string, string[]>","mcpCloseError: unknown","sources","parsed: URL","command: string","args: string[]","sources: SourceInfo[]"],"sources":["../src/cli-utils.ts","../src/protocol.ts","../src/naming.ts","../src/registry.ts","../src/schemas.ts","../src/bridgeServer.ts","../src/mcpRelayServer.ts"],"sourcesContent":["/**\n * Parsed CLI options for relay startup.\n */\nexport interface CliOptions {\n host: string;\n port: number;\n allowedOrigins: string[];\n}\n\n/**\n * Parses supported CLI flags for relay startup.\n */\nexport function parseCliOptions(argv: string[]): CliOptions {\n const options: CliOptions = {\n host: '127.0.0.1',\n port: 9333,\n // Permissive by default for zero-config local development — any browser page can connect.\n // Use --widget-origin to restrict to trusted origins on shared machines or in production.\n allowedOrigins: ['*'],\n };\n\n const readFlagValue = (flag: string, index: number): string => {\n const next = argv[index + 1];\n if (!next || next.startsWith('-')) {\n throw new Error(`Missing value for ${flag}`);\n }\n return next;\n };\n\n for (let i = 0; i < argv.length; i++) {\n const token = argv[i];\n if (!token) {\n continue;\n }\n\n if (token === '--host' || token === '-H') {\n options.host = readFlagValue(token, i);\n i += 1;\n continue;\n }\n\n if (token === '--port' || token === '-p') {\n const raw = readFlagValue(token, i);\n i += 1;\n const value = Number.parseInt(raw, 10);\n if (!Number.isFinite(value) || value <= 0 || value > 65535) {\n throw new Error(`Invalid port \"${raw}\". Port must be a number between 1 and 65535.`);\n }\n options.port = value;\n continue;\n }\n\n if (token === '--widget-origin' || token === '--allowed-origin' || token === '--ws-origin') {\n const raw = readFlagValue(token, i);\n i += 1;\n const split = raw\n .split(',')\n .map((part) => part.trim())\n .filter(Boolean);\n\n if (split.length > 0) {\n options.allowedOrigins = split;\n }\n continue;\n }\n\n if (token === '--help' || token === '-h') {\n printHelp();\n process.exit(0);\n }\n\n if (token.startsWith('-')) {\n process.stderr.write(`[webmcp-local-relay] warn: unrecognized argument \"${token}\"\\n`);\n } else {\n process.stderr.write(\n `[webmcp-local-relay] warn: unrecognized argument \"${token}\" (positional arguments are not supported)\\n`\n );\n }\n }\n\n return options;\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 Bind port for local websocket relay (default: 9333)',\n ' --widget-origin Allowed host page origin(s), comma-separated (default: *)',\n ' --allowed-origin Alias for --widget-origin',\n ' --ws-origin Alias for --widget-origin',\n ' --help, -h Show help',\n '',\n ].join('\\n')\n );\n}\n","import {\n CallToolRequestParamsSchema,\n type CallToolResult,\n CallToolResultSchema,\n type Tool,\n type ToolAnnotations,\n ToolAnnotationsSchema,\n ToolSchema,\n} from '@modelcontextprotocol/sdk/types.js';\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 * Permissive inbound tool shape from browser/widget payloads.\n *\n * Only enforces a non-empty name at ingest. All other fields are normalized\n * against SDK schemas by {@link normalizeInboundTool}.\n */\nexport const InboundToolSchema = z.object({ name: z.string().min(1) }).passthrough();\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/**\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 * Normalizes permissive inbound tool payloads into SDK-compliant Tool objects.\n *\n * Invalid optional metadata (description, output schema, annotations, etc.)\n * is dropped. Invalid/missing inputSchema falls back to an empty object schema.\n */\nexport function normalizeInboundTool(inbound: z.infer<typeof InboundToolSchema>): RelayTool {\n const inputSchemaParsed = ToolSchema.shape.inputSchema.safeParse(inbound.inputSchema);\n const outputSchemaParsed = ToolSchema.shape.outputSchema.safeParse(inbound.outputSchema);\n const annotationsParsed = ToolAnnotationsSchema.safeParse(inbound.annotations);\n\n const normalizedCandidate: Record<string, unknown> = {\n name: inbound.name,\n inputSchema: inputSchemaParsed.success ? inputSchemaParsed.data : DEFAULT_TOOL_INPUT_SCHEMA,\n };\n\n if (typeof inbound.description === 'string') {\n normalizedCandidate.description = inbound.description;\n }\n if (typeof inbound.title === 'string') {\n normalizedCandidate.title = inbound.title;\n }\n if (outputSchemaParsed.success && outputSchemaParsed.data !== undefined) {\n normalizedCandidate.outputSchema = outputSchemaParsed.data;\n }\n if (annotationsParsed.success && annotationsParsed.data !== undefined) {\n normalizedCandidate.annotations = annotationsParsed.data;\n }\n\n const normalizedParsed = NormalizedToolSchema.safeParse(normalizedCandidate);\n if (normalizedParsed.success) {\n return normalizedParsed.data;\n }\n\n return {\n name: inbound.name,\n inputSchema: DEFAULT_TOOL_INPUT_SCHEMA,\n };\n}\n\nexport { CallToolRequestParamsSchema, CallToolResultSchema, ToolAnnotationsSchema, ToolSchema };\n","/**\n * Maximum tool name length supported across MCP clients.\n */\nconst 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 base = `${safeName}${suffix}`;\n\n if (base.length <= MAX_MCP_TOOL_NAME_LENGTH) {\n return base;\n }\n\n const available = MAX_MCP_TOOL_NAME_LENGTH - suffix.length;\n return `${safeName.slice(0, Math.max(1, available))}${suffix}`;\n}\n","import { buildPublicToolName } 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 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 this.removeConnectionTools(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.removeConnectionTools(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 if (options.sourceId) {\n const byConnection = rankedProviders.find(\n (provider) => provider.sourceId === options.sourceId\n );\n if (byConnection) {\n return {\n connectionId: byConnection.sourceId,\n tool: byConnection.tool,\n publicToolName: byConnection.publicToolName,\n };\n }\n\n const byTabId = rankedProviders.find((provider) => provider.tabId === options.sourceId);\n if (byTabId) {\n return {\n connectionId: byTabId.sourceId,\n tool: byTabId.tool,\n publicToolName: byTabId.publicToolName,\n };\n }\n\n return null;\n }\n\n if (options.requestTabId) {\n const byRequestTab = rankedProviders.find(\n (provider) => provider.tabId === options.requestTabId\n );\n if (!byRequestTab) {\n return null;\n }\n\n return {\n connectionId: byRequestTab.sourceId,\n tool: byRequestTab.tool,\n publicToolName: byRequestTab.publicToolName,\n };\n }\n\n const provider = rankedProviders[0];\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 * Removes all tool providers for a connection.\n */\n private removeConnectionTools(connectionId: string): void {\n this.toolsByConnectionId.delete(connectionId);\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\n for (const [, 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 provider.publicToolName = buildPublicToolName({\n originalToolName: provider.tool.name,\n tabId: provider.tabId,\n disambiguate,\n });\n\n const existing = this.providersByPublicToolName.get(provider.publicToolName) ?? [];\n existing.push(provider);\n this.providersByPublicToolName.set(\n provider.publicToolName,\n this.sortProvidersByRecency(existing)\n );\n }\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\n/**\n * Schema for source identity bootstrap message.\n */\nexport const 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\n/**\n * Schema for full tool-list synchronization message.\n *\n * Inbound tools are permissively parsed then normalized to SDK Tool shape.\n */\nexport const BrowserToolsListMessageSchema = z.object({\n type: z.literal('tools/list'),\n tools: z\n .array(InboundToolSchema)\n .transform((tools): RelayTool[] => tools.map(normalizeInboundTool)),\n});\n\n/**\n * Schema for tool-set replacement notification pushed after initial registration.\n *\n * Processing is identical to `tools/list` — the full tool set replaces any\n * previously registered tools — but the distinct type signals a dynamic update\n * rather than an initial handshake.\n */\nexport const BrowserToolsChangedMessageSchema = z.object({\n type: z.literal('tools/changed'),\n tools: z\n .array(InboundToolSchema)\n .transform((tools): RelayTool[] => tools.map(normalizeInboundTool)),\n});\n\n/**\n * Schema for invocation response payloads sent by browser sources.\n *\n * `result` remains permissive and is normalized later to preserve runtime\n * behavior for malformed tool responses.\n */\nexport const BrowserToolResultMessageSchema = z.object({\n type: z.literal('result'),\n callId: z.string().min(1),\n result: z.unknown(),\n});\n\n/**\n * Schema for browser heartbeat acknowledgement.\n */\nexport const 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 * Schema for relay invocation messages sent to browser sources.\n */\nexport const 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\n/**\n * Schema for relay heartbeat messages sent to browser sources.\n */\nexport const RelayPingMessageSchema = z.object({\n type: z.literal('ping'),\n});\n\n/**\n * Schema for relay reload messages sent to browser sources.\n */\nexport const 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 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 * Relay-to-relay protocol (client mode <-> server mode).\n */\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\n/**\n * Schema for relay client identification message.\n */\nexport const RelayClientHelloSchema = z.object({\n type: z.literal('relay/hello'),\n});\n\n/**\n * Schema for relay client tool list request.\n */\nexport const RelayClientListToolsSchema = z.object({\n type: z.literal('relay/list-tools'),\n});\n\n/**\n * Schema for relay client tool invocation request.\n */\nexport const 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\n/**\n * Schema for relay server tool list response.\n */\nexport const RelayServerToolsSchema = z.object({\n type: z.literal('relay/tools'),\n ...RelayToolsPayloadFields,\n});\n\n/**\n * Schema for relay server invocation result response.\n */\nexport const RelayServerResultSchema = z.object({\n type: z.literal('relay/result'),\n callId: z.string().min(1),\n result: CallToolResultSchema,\n});\n\n/**\n * Schema for relay server push notification when tools change.\n */\nexport const 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 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 CallToolResultSchema,\n type RelayCallToolResult,\n type RelayInvokeArgs,\n type RelayTool,\n} from './protocol.js';\nimport { type AggregatedTool, HelloRequiredError, RelayRegistry } from './registry.js';\nimport {\n BrowserToRelayMessageSchema,\n type RelayClientToServerMessage,\n RelayClientToServerMessageSchema,\n type RelayServerToClientMessage,\n RelayServerToClientMessageSchema,\n type RelaySourceInfo,\n type RelayToBrowserMessage,\n} from './schemas.js';\n\n/**\n * In-flight relay invocation waiting for a browser `result` message.\n */\ninterface PendingInvocation {\n callId: string;\n connectionId: string;\n timeoutId: ReturnType<typeof setTimeout>;\n resolve: (result: RelayCallToolResult) => void;\n reject: (error: Error) => void;\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 * Allowed host page origins reported by browser `hello` messages.\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 `1000000`\n */\n maxPayloadBytes?: number;\n /**\n * Timeout used for browser tool invocations.\n * @defaultValue `25000`\n */\n invokeTimeoutMs?: number;\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 desiredPort: number;\n private readonly allowedOrigins: string[];\n private readonly maxPayloadBytes: number;\n private readonly invokeTimeoutMs: number;\n\n private wss: WebSocketServer | null = null;\n private readonly socketByConnectionId = new Map<string, WebSocket>();\n private readonly pendingInvocations = new Map<string, PendingInvocation>();\n private readonly relayClientConnectionIds = new Set<string>();\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.desiredPort = options.port ?? 9333;\n this.allowedOrigins = options.allowedOrigins ?? ['*'];\n this.maxPayloadBytes = options.maxPayloadBytes ?? 1_000_000;\n this.invokeTimeoutMs = options.invokeTimeoutMs ?? 25_000;\n\n if (this.desiredPort !== 0 && (this.desiredPort < 1 || this.desiredPort > 65535)) {\n throw new Error(\n `Invalid port ${this.desiredPort}. Port must be 0 (auto-assign) or between 1 and 65535.`\n );\n }\n if (this.maxPayloadBytes <= 0) {\n throw new Error(`Invalid maxPayloadBytes ${this.maxPayloadBytes}. Must be greater than 0.`);\n }\n if (this.invokeTimeoutMs <= 0) {\n throw new Error(`Invalid invokeTimeoutMs ${this.invokeTimeoutMs}. Must be greater than 0.`);\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 the port is already in use, automatically falls back to client mode\n * and proxies through the existing relay.\n */\n async start(): Promise<void> {\n if (this.wss || this.clientSocket) {\n return;\n }\n\n this.stopping = false;\n\n try {\n await this.startAsServer();\n } catch (err) {\n const isAddrInUse =\n err instanceof Error &&\n ('code' in err\n ? (err as NodeJS.ErrnoException).code === 'EADDRINUSE'\n : err.message.includes('EADDRINUSE'));\n\n if (!isAddrInUse) {\n throw err;\n }\n\n this._mode = 'client';\n process.stderr.write(\n `[webmcp-local-relay] info: port ${this.desiredPort} in use, switching to client mode\\n`\n );\n await this.startAsClient();\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\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 this.socketByConnectionId.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(): Promise<void> {\n const wss = await new Promise<WebSocketServer>((resolve, reject) => {\n const server = new WebSocketServer({\n host: this.host,\n port: this.desiredPort,\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) => {\n const connectionId = randomUUID();\n this.socketByConnectionId.set(connectionId, socket);\n\n socket.on('message', (raw: WebSocket.RawData) => {\n this.onSocketMessage(connectionId, raw);\n });\n\n socket.on('close', () => {\n this.onSocketClose(connectionId);\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 this.onSocketClose(connectionId);\n });\n });\n\n wss.on('error', (err: Error) => {\n process.stderr.write(`[webmcp-local-relay] error: WebSocket server error: ${err.message}\\n`);\n this.emit('error', err);\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 invokeToolLocally(\n toolName: string,\n args: RelayInvokeArgs,\n options: { sourceId?: string; requestTabId?: string }\n ): Promise<RelayCallToolResult> {\n const resolveOptions: { toolName: string; sourceId?: string; requestTabId?: string } = {\n toolName,\n };\n if (options.sourceId !== undefined) {\n resolveOptions.sourceId = options.sourceId;\n }\n if (options.requestTabId !== undefined) {\n resolveOptions.requestTabId = options.requestTabId;\n }\n\n const resolved = this.registry.resolveInvocation(resolveOptions);\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 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 /**\n * Handles a raw WebSocket message from a connected source.\n *\n * Routes relay-protocol messages (`relay/*`) to the relay client handler\n * and browser-protocol messages to the existing browser handler.\n */\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\n ? (parsedJson as Record<string, unknown>).type\n : undefined;\n\n if (typeof typeField === 'string' && typeField.startsWith('relay/')) {\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 this.registry.touchConnection(connectionId);\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 const message = parsedMessage.data;\n\n switch (message.type) {\n case 'hello':\n try {\n if (!this.isHostOriginAllowed(message.origin)) {\n process.stderr.write(\n `[webmcp-local-relay] warn: rejecting source ${connectionId} with disallowed host origin: ${message.origin ?? 'missing'}\\n`\n );\n const socket = this.socketByConnectionId.get(connectionId);\n socket?.close(1008, 'Host origin not allowed');\n break;\n }\n this.registry.upsertSource(connectionId, message);\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 if (err instanceof HelloRequiredError) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} sent tools before hello, ignoring\\n`\n );\n } else {\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 const socket = this.socketByConnectionId.get(connectionId);\n socket?.close(1011, 'Failed to register tools');\n }\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\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 break;\n }\n }\n\n /**\n * Handles relay-protocol messages from relay client connections.\n */\n private onRelayClientMessage(connectionId: string, message: RelayClientToServerMessage): void {\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 (async () => {\n try {\n const result = await this.invokeToolLocally(toolName, args ?? {}, {});\n const response: RelayServerToClientMessage = {\n type: 'relay/result',\n callId,\n result,\n };\n const socket = this.socketByConnectionId.get(connectionId);\n if (socket?.readyState === WebSocket.OPEN) {\n try {\n socket.send(JSON.stringify(response));\n } catch (sendErr) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send relay result to ${connectionId}: ${sendErr instanceof Error ? sendErr.message : String(sendErr)}\\n`\n );\n }\n } else {\n process.stderr.write(\n `[webmcp-local-relay] warn: relay client ${connectionId} disconnected before result for callId ${callId} could be delivered\\n`\n );\n }\n } catch (err) {\n const response: RelayServerToClientMessage = {\n type: 'relay/result',\n callId,\n result: {\n content: [\n {\n type: 'text',\n text: `Relay invocation failed: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n },\n };\n const socket = this.socketByConnectionId.get(connectionId);\n if (socket?.readyState === WebSocket.OPEN) {\n try {\n socket.send(JSON.stringify(response));\n } catch (sendErr) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send relay error result to ${connectionId}: ${sendErr instanceof Error ? sendErr.message : String(sendErr)}\\n`\n );\n }\n } else {\n process.stderr.write(\n `[webmcp-local-relay] warn: relay client ${connectionId} disconnected before error result for callId ${callId} could be delivered\\n`\n );\n }\n }\n })();\n break;\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 private onSocketClose(connectionId: string): void {\n this.relayClientConnectionIds.delete(connectionId);\n this.registry.removeConnection(connectionId);\n this.socketByConnectionId.delete(connectionId);\n this.emit('stateChanged');\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 pending.reject(new Error(`Tool source ${connectionId} disconnected during invocation`));\n }\n }\n\n private async startAsClient(): Promise<void> {\n this.stopping = false;\n\n return new Promise<void>((resolve, reject) => {\n const wsUrl = `ws://${this.host}:${this.desiredPort}`;\n const ws = new WebSocket(wsUrl);\n\n const onOpen = () => {\n ws.off('error', onFirstError);\n this.clientSocket = ws;\n this.clientReconnectDelay = 500;\n\n try {\n ws.send(JSON.stringify({ type: 'relay/hello' }));\n ws.send(JSON.stringify({ type: 'relay/list-tools' }));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] error: failed to send handshake to relay server: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n this.clientSocket = null;\n ws.close();\n reject(new Error('Failed to send handshake to relay server'));\n return;\n }\n\n this.setupClientHandlers(ws);\n resolve();\n };\n\n const onFirstError = (err: Error) => {\n ws.off('open', onOpen);\n reject(\n new Error(\n `Failed to connect to relay server at ws://${this.host}:${this.desiredPort}: ${err.message}`\n )\n );\n };\n\n ws.once('open', onOpen);\n ws.once('error', onFirstError);\n });\n }\n\n private setupClientHandlers(ws: WebSocket): void {\n ws.on('message', (raw: WebSocket.RawData) => {\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;\n }\n\n const msg = RelayServerToClientMessageSchema.safeParse(parsed);\n if (!msg.success) {\n const typeField =\n typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>).type\n : 'unknown';\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid relay server message (type=${typeField}): ${msg.error.message}\\n`\n );\n return;\n }\n\n switch (msg.data.type) {\n case 'relay/tools':\n case 'relay/tools-changed':\n this.clientTools = msg.data.tools;\n this.clientSources = msg.data.sources;\n this.clientToolSourceMap = msg.data.toolSourceMap;\n this.emit('stateChanged');\n break;\n\n case 'relay/result': {\n const pending = this.clientPendingInvocations.get(msg.data.callId);\n if (!pending) {\n process.stderr.write(\n `[webmcp-local-relay] warn: received relay result for unknown callId ${msg.data.callId}\\n`\n );\n break;\n }\n clearTimeout(pending.timeoutId);\n this.clientPendingInvocations.delete(msg.data.callId);\n pending.resolve(msg.data.result);\n break;\n }\n }\n });\n\n ws.on('close', () => {\n this.clientSocket = null;\n\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 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().catch((err) => {\n process.stderr.write(\n `[webmcp-local-relay] error: unexpected failure during reconnection: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n );\n if (!this.stopping) {\n this.scheduleReconnect();\n }\n });\n }, delay);\n }\n\n /**\n * Attempts to promote from client to server mode when reconnecting.\n * Tries to bind the port first; if EADDRINUSE, falls back to client mode.\n */\n private async reconnectWithModePromotion(): Promise<void> {\n try {\n await this.startAsServer();\n this._mode = 'server';\n this.clientReconnectDelay = 500;\n this.clientReconnectAttempts = 0;\n process.stderr.write('[webmcp-local-relay] info: promoted from client to server mode\\n');\n this.emit('stateChanged');\n } catch (err) {\n const isAddrInUse =\n err instanceof Error &&\n ('code' in err\n ? (err as NodeJS.ErrnoException).code === 'EADDRINUSE'\n : err.message.includes('EADDRINUSE'));\n\n if (!isAddrInUse) {\n process.stderr.write(\n `[webmcp-local-relay] warn: server promotion failed: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n if (!this.stopping) {\n this.scheduleReconnect();\n }\n return;\n }\n\n this.reconnectAsClient();\n }\n }\n\n /**\n * Reconnects as a client to an existing relay server.\n */\n private reconnectAsClient(): void {\n try {\n const wsUrl = `ws://${this.host}:${this.desiredPort}`;\n const ws = new WebSocket(wsUrl);\n\n const onReconnectError = (err: Error) => {\n process.stderr.write(\n `[webmcp-local-relay] warn: relay client reconnection failed: ${err.message}\\n`\n );\n if (!this.stopping) {\n this.scheduleReconnect();\n }\n };\n\n ws.once('open', () => {\n ws.off('error', onReconnectError);\n this.clientSocket = ws;\n this.clientReconnectDelay = 500;\n this.clientReconnectAttempts = 0;\n\n try {\n ws.send(JSON.stringify({ type: 'relay/hello' }));\n ws.send(JSON.stringify({ type: 'relay/list-tools' }));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] error: failed to send handshake during reconnection: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n this.clientSocket = null;\n ws.close();\n this.scheduleReconnect();\n return;\n }\n\n this.setupClientHandlers(ws);\n });\n\n ws.once('error', onReconnectError);\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] error: failed to create reconnection socket: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n if (!this.stopping) {\n this.scheduleReconnect();\n }\n }\n }\n\n private 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 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: this.toWireTools(tools),\n sources,\n toolSourceMap,\n };\n }\n\n /**\n * Maps aggregated tools to the wire format used by the relay protocol.\n */\n private toWireTools(tools: AggregatedTool[]): RelayTool[] {\n return tools.map(({ originalName: _originalName, sources: _sources, ...tool }) => tool);\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 /**\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 (typeof raw === 'string') {\n return raw;\n }\n\n if (Buffer.isBuffer(raw)) {\n return raw.toString('utf8');\n }\n\n if (raw instanceof ArrayBuffer) {\n return Buffer.from(raw).toString('utf8');\n }\n\n if (Array.isArray(raw)) {\n return Buffer.concat(raw).toString('utf8');\n }\n\n return String(raw);\n }\n}\n","import { execFile } from 'node:child_process';\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { z } from 'zod/v4';\n\nimport { RelayBridgeServer, type RelayBridgeServerOptions } from './bridgeServer.js';\nimport type { AggregatedTool, SourceInfo } from './registry.js';\n\n/**\n * Handle returned by MCP tool registration.\n */\ninterface RegisteredToolHandle {\n remove: () => void;\n}\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 dynamicToolHandles = new Map<string, RegisteredToolHandle>();\n private readonly dynamicToolSignature = new Map<string, string>();\n\n private syncing = false;\n private syncRequested = false;\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 void this.syncDynamicTools().catch((err) => {\n this.logSyncError(err);\n });\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 await 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\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 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.dynamicToolHandles.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: {},\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n },\n },\n async () => {\n if (this.bridge.mode === 'client') {\n const sources = this.bridge.listSourcesFromRelay();\n const info = {\n 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 const sources = this.bridge.registry.listSources();\n return {\n content: [\n { type: 'text', text: JSON.stringify({ count: sources.length, sources }, null, 2) },\n ],\n structuredContent: {\n count: sources.length,\n sources,\n },\n };\n }\n );\n\n this.mcpServer.registerTool(\n 'webmcp_list_tools',\n {\n description:\n 'List WebMCP tools available from connected browser sources. ' +\n 'Returns tool definitions including name, description, input schema, and source info. ' +\n 'Use webmcp_call_tool to invoke a tool by name.',\n inputSchema: {},\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n },\n },\n async () => {\n const tools = this.listAggregatedTools();\n return {\n content: [\n { type: 'text', text: JSON.stringify({ count: tools.length, tools }, null, 2) },\n ],\n structuredContent: {\n count: tools.length,\n tools,\n },\n };\n }\n );\n\n this.mcpServer.registerTool(\n 'webmcp_call_tool',\n {\n description:\n 'Call a WebMCP tool registered by a connected browser page. ' +\n 'Use webmcp_list_tools first to see available tools and their input schemas.',\n inputSchema: {\n name: z\n .string()\n .describe('The tool name to call. Use webmcp_list_tools to see available tool names.'),\n arguments: z\n .record(z.string(), z.unknown())\n .optional()\n .describe(\n 'Arguments to pass to the tool as a JSON object. Check the tool inputSchema from webmcp_list_tools for expected fields.'\n ),\n },\n annotations: {\n readOnlyHint: false,\n },\n },\n async ({ name, arguments: args }) => {\n const tools = this.listAggregatedTools();\n const toolSummary = this.buildToolSummary(tools);\n\n const matched = tools.find((t) => t.name === name);\n if (!matched) {\n const errorLines = [`Tool \"${name}\" not found.`];\n if (toolSummary) {\n errorLines.push('', 'Available tools:', toolSummary);\n } else {\n errorLines.push(\n '',\n 'No tools are currently available. Ensure a browser page with WebMCP is connected.'\n );\n }\n return {\n content: [{ type: 'text' as const, text: errorLines.join('\\n') }],\n isError: true,\n };\n }\n\n try {\n const result = await this.bridge.invokeTool(name, args ?? {});\n\n const updatedTools =\n this.bridge.mode === 'client'\n ? this.buildAggregatedToolsFromRelay()\n : this.bridge.registry.listTools();\n const updatedSummary = this.buildToolSummary(updatedTools);\n if (updatedSummary) {\n result.content = [\n ...result.content,\n { type: 'text' as const, text: `\\n---\\nAvailable tools:\\n${updatedSummary}` },\n ];\n }\n\n return result;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const errorLines = [`Failed to call tool \"${name}\": ${message}`];\n if (toolSummary) {\n errorLines.push('', 'Available tools:', toolSummary);\n }\n return {\n content: [{ type: 'text' as const, text: errorLines.join('\\n') }],\n isError: true,\n };\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. \" +\n 'Use to launch WebMCP-enabled pages or reload stale connections.',\n inputSchema: {\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 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 const sources = this.bridge.registry.listSources();\n const matched = sources.find((s) => {\n if (!s.url) return false;\n try {\n return new URL(s.url).origin === parsed.origin;\n } catch {\n process.stderr.write(\n `[webmcp-local-relay] warn: source ${s.sourceId} has unparseable URL: ${s.url}\\n`\n );\n return false;\n }\n });\n\n if (!matched) {\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(matched.sourceId);\n return {\n content: [\n {\n type: 'text' as const,\n text: `Reload sent to source ${matched.sourceId} (${matched.url ?? matched.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 (this.bridge.mode === 'server') {\n const sources = this.bridge.registry.listSources();\n const existing = sources.find((s) => {\n if (!s.url) return false;\n try {\n return new URL(s.url).origin === parsed.origin;\n } catch {\n process.stderr.write(\n `[webmcp-local-relay] warn: source ${s.sourceId} has unparseable URL: ${s.url}\\n`\n );\n return false;\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\n return {\n content: [{ type: 'text' as const, text: `Opened ${url} in the default browser.` }],\n };\n }\n );\n }\n\n /**\n * Builds a concise plain-text list of available tools.\n */\n private buildToolSummary(tools: AggregatedTool[]): string | null {\n if (tools.length === 0) {\n return null;\n }\n\n return tools\n .map((t) => {\n const desc = t.description ? ` - ${t.description.split('\\n')[0]}` : '';\n return ` ${t.name}${desc}`;\n })\n .join('\\n');\n }\n\n /**\n * Opens a URL in the user's default browser using the platform open command.\n *\n * Uses the re-serialized `URL.href` to prevent injection of shell metacharacters.\n * On Windows, PowerShell `Start-Process` is used instead of `cmd /c start`\n * because cmd.exe interprets `&`, `|`, and other metacharacters in URLs.\n */\n private openInBrowser(url: string): Promise<void> {\n const safeUrl = new URL(url).href;\n return new Promise((resolve, reject) => {\n const platform = process.platform;\n let command: string;\n let args: string[];\n if (platform === 'darwin') {\n command = 'open';\n args = [safeUrl];\n } else if (platform === 'win32') {\n command = 'powershell.exe';\n args = ['-NoProfile', '-Command', `Start-Process \"${safeUrl}\"`];\n } else {\n command = 'xdg-open';\n args = [safeUrl];\n }\n execFile(command, args, (err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n /**\n * Coalesces concurrent sync requests into a single serialized sync loop.\n */\n private async syncDynamicTools(): Promise<void> {\n if (this.syncing) {\n this.syncRequested = true;\n return;\n }\n\n this.syncing = true;\n\n try {\n do {\n this.syncRequested = false;\n const tools = this.listAggregatedTools();\n this.applyDynamicTools(tools);\n } while (this.syncRequested);\n } finally {\n const retryRequested = this.syncRequested;\n this.syncRequested = false;\n this.syncing = false;\n if (retryRequested) {\n void this.syncDynamicTools().catch((err) => {\n this.logSyncError(err);\n });\n }\n }\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 as SourceInfo);\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 registry tool state to MCP dynamic registrations.\n */\n private applyDynamicTools(tools: AggregatedTool[]): void {\n const nextNames = new Set(tools.map((tool) => tool.name));\n let changed = false;\n\n for (const [name, handle] of this.dynamicToolHandles.entries()) {\n if (nextNames.has(name)) {\n continue;\n }\n\n handle.remove();\n this.dynamicToolHandles.delete(name);\n this.dynamicToolSignature.delete(name);\n changed = true;\n }\n\n for (const tool of tools) {\n const signature = this.toolSignature(tool);\n const currentSignature = this.dynamicToolSignature.get(tool.name);\n\n if (currentSignature === signature && this.dynamicToolHandles.has(tool.name)) {\n continue;\n }\n\n const existing = this.dynamicToolHandles.get(tool.name);\n if (existing) {\n existing.remove();\n this.dynamicToolHandles.delete(tool.name);\n }\n\n const handle = this.registerDynamicTool(tool);\n this.dynamicToolHandles.set(tool.name, handle);\n this.dynamicToolSignature.set(tool.name, signature);\n changed = true;\n }\n\n if (changed && this.connected) {\n try {\n this.mcpServer.sendToolListChanged();\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send tool list changed notification: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }\n }\n\n /**\n * Registers a single dynamic tool and returns a removal handle.\n */\n private registerDynamicTool(tool: AggregatedTool): RegisteredToolHandle {\n const inputSchema = z.object({}).passthrough();\n const config = {\n description: this.dynamicToolDescription(tool),\n inputSchema,\n };\n\n const handle = this.mcpServer.registerTool(\n tool.name,\n tool.annotations ? { ...config, annotations: tool.annotations } : config,\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 return {\n remove: () => {\n handle.remove();\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 private toolSignature(tool: AggregatedTool): string {\n return JSON.stringify({\n name: tool.name,\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 execution: tool.execution,\n _meta: tool._meta,\n icons: tool.icons,\n sources: tool.sources.map((source) => ({\n sourceId: source.sourceId,\n tabId: source.tabId,\n })),\n });\n }\n\n /**\n * Logs tool sync errors with full detail.\n */\n private logSyncError(err: unknown): void {\n const details = err instanceof Error ? (err.stack ?? err.message) : String(err);\n process.stderr.write(`[webmcp-local-relay] error: failed to sync dynamic tools: ${details}\\n`);\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcpRelayServer-B8d7NAUK.js","names":["options: CliOptions","DEFAULT_TOOL_INPUT_SCHEMA: Tool['inputSchema']","normalizedCandidate: Record<string, unknown>","now: () => number","providers: ToolProvider[]","result: AggregatedTool[]","provider","message: RelayToBrowserMessage","resolveOptions: { toolName: string; sourceId?: string; requestTabId?: string }","parsedJson: unknown","response: RelayServerToClientMessage","message: RelayServerToClientMessage","parsed: unknown","message: RelayClientToServerMessage","mcpCloseError: unknown","parsed: URL","command: string","args: string[]"],"sources":["../src/cli-utils.ts","../src/protocol.ts","../src/naming.ts","../src/registry.ts","../src/schemas.ts","../src/bridgeServer.ts","../src/mcpRelayServer.ts"],"sourcesContent":["/**\n * Parsed CLI options for relay startup.\n */\nexport interface CliOptions {\n host: string;\n port: number;\n allowedOrigins: string[];\n}\n\n/**\n * Parses supported CLI flags for relay startup.\n */\nexport function parseCliOptions(argv: string[]): CliOptions {\n const options: CliOptions = {\n host: '127.0.0.1',\n port: 9333,\n // Permissive by default for zero-config local development — any browser page can connect.\n // Use --widget-origin to restrict to trusted origins on shared machines or in production.\n allowedOrigins: ['*'],\n };\n\n const readFlagValue = (flag: string, index: number): string => {\n const next = argv[index + 1];\n if (!next || next.startsWith('-')) {\n throw new Error(`Missing value for ${flag}`);\n }\n return next;\n };\n\n for (let i = 0; i < argv.length; i++) {\n const token = argv[i];\n if (!token) {\n continue;\n }\n\n if (token === '--host' || token === '-H') {\n options.host = readFlagValue(token, i);\n i += 1;\n continue;\n }\n\n if (token === '--port' || token === '-p') {\n const raw = readFlagValue(token, i);\n i += 1;\n const value = Number.parseInt(raw, 10);\n if (!Number.isFinite(value) || value <= 0 || value > 65535) {\n throw new Error(`Invalid port \"${raw}\". Port must be a number between 1 and 65535.`);\n }\n options.port = value;\n continue;\n }\n\n if (token === '--widget-origin' || token === '--allowed-origin' || token === '--ws-origin') {\n const raw = readFlagValue(token, i);\n i += 1;\n const split = raw\n .split(',')\n .map((part) => part.trim())\n .filter(Boolean);\n\n if (split.length > 0) {\n options.allowedOrigins = split;\n }\n continue;\n }\n\n if (token === '--help' || token === '-h') {\n printHelp();\n process.exit(0);\n }\n\n if (token.startsWith('-')) {\n process.stderr.write(`[webmcp-local-relay] warn: unrecognized argument \"${token}\"\\n`);\n } else {\n process.stderr.write(\n `[webmcp-local-relay] warn: unrecognized argument \"${token}\" (positional arguments are not supported)\\n`\n );\n }\n }\n\n return options;\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 Bind port for local websocket relay (default: 9333)',\n ' --widget-origin Allowed host page origin(s), comma-separated (default: *)',\n ' --allowed-origin Alias for --widget-origin',\n ' --ws-origin Alias for --widget-origin',\n ' --help, -h Show help',\n '',\n ].join('\\n')\n );\n}\n","import {\n CallToolRequestParamsSchema,\n type CallToolResult,\n CallToolResultSchema,\n type Tool,\n type ToolAnnotations,\n ToolAnnotationsSchema,\n ToolSchema,\n} from '@modelcontextprotocol/sdk/types.js';\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 * Permissive inbound tool shape from browser/widget payloads.\n *\n * Only enforces a non-empty name at ingest. All other fields are normalized\n * against SDK schemas by {@link normalizeInboundTool}.\n */\nexport const InboundToolSchema = z.object({ name: z.string().min(1) }).passthrough();\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/**\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 * Normalizes permissive inbound tool payloads into SDK-compliant Tool objects.\n *\n * Invalid optional metadata (description, output schema, annotations, etc.)\n * is dropped. Invalid/missing inputSchema falls back to an empty object schema.\n */\nexport function normalizeInboundTool(inbound: z.infer<typeof InboundToolSchema>): RelayTool {\n const inputSchemaParsed = ToolSchema.shape.inputSchema.safeParse(inbound.inputSchema);\n const outputSchemaParsed = ToolSchema.shape.outputSchema.safeParse(inbound.outputSchema);\n const annotationsParsed = ToolAnnotationsSchema.safeParse(inbound.annotations);\n\n const normalizedCandidate: Record<string, unknown> = {\n name: inbound.name,\n inputSchema: inputSchemaParsed.success ? inputSchemaParsed.data : DEFAULT_TOOL_INPUT_SCHEMA,\n };\n\n if (typeof inbound.description === 'string') {\n normalizedCandidate.description = inbound.description;\n }\n if (typeof inbound.title === 'string') {\n normalizedCandidate.title = inbound.title;\n }\n if (outputSchemaParsed.success && outputSchemaParsed.data !== undefined) {\n normalizedCandidate.outputSchema = outputSchemaParsed.data;\n }\n if (annotationsParsed.success && annotationsParsed.data !== undefined) {\n normalizedCandidate.annotations = annotationsParsed.data;\n }\n\n const normalizedParsed = NormalizedToolSchema.safeParse(normalizedCandidate);\n if (normalizedParsed.success) {\n return normalizedParsed.data;\n }\n\n return {\n name: inbound.name,\n inputSchema: DEFAULT_TOOL_INPUT_SCHEMA,\n };\n}\n\nexport { CallToolRequestParamsSchema, CallToolResultSchema, ToolAnnotationsSchema, ToolSchema };\n","/**\n * Maximum tool name length supported across MCP clients.\n */\nconst 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 base = `${safeName}${suffix}`;\n\n if (base.length <= MAX_MCP_TOOL_NAME_LENGTH) {\n return base;\n }\n\n const available = MAX_MCP_TOOL_NAME_LENGTH - suffix.length;\n return `${safeName.slice(0, Math.max(1, available))}${suffix}`;\n}\n","import { buildPublicToolName } 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 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 this.removeConnectionTools(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.removeConnectionTools(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 if (options.sourceId) {\n const byConnection = rankedProviders.find(\n (provider) => provider.sourceId === options.sourceId\n );\n if (byConnection) {\n return {\n connectionId: byConnection.sourceId,\n tool: byConnection.tool,\n publicToolName: byConnection.publicToolName,\n };\n }\n\n const byTabId = rankedProviders.find((provider) => provider.tabId === options.sourceId);\n if (byTabId) {\n return {\n connectionId: byTabId.sourceId,\n tool: byTabId.tool,\n publicToolName: byTabId.publicToolName,\n };\n }\n\n return null;\n }\n\n if (options.requestTabId) {\n const byRequestTab = rankedProviders.find(\n (provider) => provider.tabId === options.requestTabId\n );\n if (!byRequestTab) {\n return null;\n }\n\n return {\n connectionId: byRequestTab.sourceId,\n tool: byRequestTab.tool,\n publicToolName: byRequestTab.publicToolName,\n };\n }\n\n const provider = rankedProviders[0];\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 * Removes all tool providers for a connection.\n */\n private removeConnectionTools(connectionId: string): void {\n this.toolsByConnectionId.delete(connectionId);\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\n for (const [, 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 provider.publicToolName = buildPublicToolName({\n originalToolName: provider.tool.name,\n tabId: provider.tabId,\n disambiguate,\n });\n\n const existing = this.providersByPublicToolName.get(provider.publicToolName) ?? [];\n existing.push(provider);\n this.providersByPublicToolName.set(\n provider.publicToolName,\n this.sortProvidersByRecency(existing)\n );\n }\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\n/**\n * Schema for source identity bootstrap message.\n */\nexport const 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\n/**\n * Schema for full tool-list synchronization message.\n *\n * Inbound tools are permissively parsed then normalized to SDK Tool shape.\n */\nexport const BrowserToolsListMessageSchema = z.object({\n type: z.literal('tools/list'),\n tools: z\n .array(InboundToolSchema)\n .transform((tools): RelayTool[] => tools.map(normalizeInboundTool)),\n});\n\n/**\n * Schema for tool-set replacement notification pushed after initial registration.\n *\n * Processing is identical to `tools/list` — the full tool set replaces any\n * previously registered tools — but the distinct type signals a dynamic update\n * rather than an initial handshake.\n */\nexport const BrowserToolsChangedMessageSchema = z.object({\n type: z.literal('tools/changed'),\n tools: z\n .array(InboundToolSchema)\n .transform((tools): RelayTool[] => tools.map(normalizeInboundTool)),\n});\n\n/**\n * Schema for invocation response payloads sent by browser sources.\n *\n * `result` remains permissive and is normalized later to preserve runtime\n * behavior for malformed tool responses.\n */\nexport const BrowserToolResultMessageSchema = z.object({\n type: z.literal('result'),\n callId: z.string().min(1),\n result: z.unknown(),\n});\n\n/**\n * Schema for browser heartbeat acknowledgement.\n */\nexport const 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 * Schema for relay invocation messages sent to browser sources.\n */\nexport const 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\n/**\n * Schema for relay heartbeat messages sent to browser sources.\n */\nexport const RelayPingMessageSchema = z.object({\n type: z.literal('ping'),\n});\n\n/**\n * Schema for relay reload messages sent to browser sources.\n */\nexport const 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 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 * Relay-to-relay protocol (client mode <-> server mode).\n */\n\n/**\n * Schema for relay client identification message.\n */\nexport const RelayClientHelloSchema = z.object({\n type: z.literal('relay/hello'),\n});\n\n/**\n * Schema for relay client tool list request.\n */\nexport const RelayClientListToolsSchema = z.object({\n type: z.literal('relay/list-tools'),\n});\n\n/**\n * Schema for relay client tool invocation request.\n */\nexport const 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\n/**\n * Schema for relay server tool list response.\n */\nexport const RelayServerToolsSchema = z.object({\n type: z.literal('relay/tools'),\n tools: z.array(NormalizedToolSchema),\n});\n\n/**\n * Schema for relay server invocation result response.\n */\nexport const RelayServerResultSchema = z.object({\n type: z.literal('relay/result'),\n callId: z.string().min(1),\n result: CallToolResultSchema,\n});\n\n/**\n * Schema for relay server push notification when tools change.\n */\nexport const RelayServerToolsChangedSchema = z.object({\n type: z.literal('relay/tools-changed'),\n tools: z.array(NormalizedToolSchema),\n});\n\n/**\n * Union schema for all relay-server-to-client messages.\n */\nexport const RelayServerToClientMessageSchema = z.discriminatedUnion('type', [\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 CallToolResultSchema,\n type RelayCallToolResult,\n type RelayInvokeArgs,\n type RelayTool,\n} from './protocol.js';\nimport { type AggregatedTool, HelloRequiredError, RelayRegistry } from './registry.js';\nimport {\n BrowserToRelayMessageSchema,\n type RelayClientToServerMessage,\n RelayClientToServerMessageSchema,\n type RelayServerToClientMessage,\n RelayServerToClientMessageSchema,\n type RelayToBrowserMessage,\n} from './schemas.js';\n\n/**\n * In-flight relay invocation waiting for a browser `result` message.\n */\ninterface PendingInvocation {\n callId: string;\n connectionId: string;\n timeoutId: ReturnType<typeof setTimeout>;\n resolve: (result: RelayCallToolResult) => void;\n reject: (error: Error) => void;\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 * Allowed host page origins reported by browser `hello` messages.\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 `1000000`\n */\n maxPayloadBytes?: number;\n /**\n * Timeout used for browser tool invocations.\n * @defaultValue `25000`\n */\n invokeTimeoutMs?: number;\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 desiredPort: number;\n private readonly allowedOrigins: string[];\n private readonly maxPayloadBytes: number;\n private readonly invokeTimeoutMs: number;\n\n private wss: WebSocketServer | null = null;\n private readonly socketByConnectionId = new Map<string, WebSocket>();\n private readonly pendingInvocations = new Map<string, PendingInvocation>();\n private readonly relayClientConnectionIds = new Set<string>();\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 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.desiredPort = options.port ?? 9333;\n this.allowedOrigins = options.allowedOrigins ?? ['*'];\n this.maxPayloadBytes = options.maxPayloadBytes ?? 1_000_000;\n this.invokeTimeoutMs = options.invokeTimeoutMs ?? 25_000;\n\n if (this.desiredPort !== 0 && (this.desiredPort < 1 || this.desiredPort > 65535)) {\n throw new Error(\n `Invalid port ${this.desiredPort}. Port must be 0 (auto-assign) or between 1 and 65535.`\n );\n }\n if (this.maxPayloadBytes <= 0) {\n throw new Error(`Invalid maxPayloadBytes ${this.maxPayloadBytes}. Must be greater than 0.`);\n }\n if (this.invokeTimeoutMs <= 0) {\n throw new Error(`Invalid invokeTimeoutMs ${this.invokeTimeoutMs}. Must be greater than 0.`);\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 * Starts the bridge. Attempts to bind a WebSocket server (server mode).\n * If the port is already in use, automatically falls back to client mode\n * and proxies through the existing relay.\n */\n async start(): Promise<void> {\n if (this.wss || this.clientSocket) {\n return;\n }\n\n this.stopping = false;\n\n try {\n await this.startAsServer();\n } catch (err) {\n const isAddrInUse =\n err instanceof Error &&\n ('code' in err\n ? (err as NodeJS.ErrnoException).code === 'EADDRINUSE'\n : err.message.includes('EADDRINUSE'));\n\n if (!isAddrInUse) {\n throw err;\n }\n\n this._mode = 'client';\n process.stderr.write(\n `[webmcp-local-relay] info: port ${this.desiredPort} in use, switching to client mode\\n`\n );\n await this.startAsClient();\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\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 this.socketByConnectionId.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(): Promise<void> {\n const wss = await new Promise<WebSocketServer>((resolve, reject) => {\n const server = new WebSocketServer({\n host: this.host,\n port: this.desiredPort,\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) => {\n const connectionId = randomUUID();\n this.socketByConnectionId.set(connectionId, socket);\n\n socket.on('message', (raw: WebSocket.RawData) => {\n this.onSocketMessage(connectionId, raw);\n });\n\n socket.on('close', () => {\n this.onSocketClose(connectionId);\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 this.onSocketClose(connectionId);\n });\n });\n\n wss.on('error', (err: Error) => {\n process.stderr.write(`[webmcp-local-relay] error: WebSocket server error: ${err.message}\\n`);\n this.emit('error', err);\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 invokeToolLocally(\n toolName: string,\n args: RelayInvokeArgs,\n options: { sourceId?: string; requestTabId?: string }\n ): Promise<RelayCallToolResult> {\n const resolveOptions: { toolName: string; sourceId?: string; requestTabId?: string } = {\n toolName,\n };\n if (options.sourceId !== undefined) {\n resolveOptions.sourceId = options.sourceId;\n }\n if (options.requestTabId !== undefined) {\n resolveOptions.requestTabId = options.requestTabId;\n }\n\n const resolved = this.registry.resolveInvocation(resolveOptions);\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 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 /**\n * Handles a raw WebSocket message from a connected source.\n *\n * Routes relay-protocol messages (`relay/*`) to the relay client handler\n * and browser-protocol messages to the existing browser handler.\n */\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\n ? (parsedJson as Record<string, unknown>).type\n : undefined;\n\n if (typeof typeField === 'string' && typeField.startsWith('relay/')) {\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 this.registry.touchConnection(connectionId);\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 const message = parsedMessage.data;\n\n switch (message.type) {\n case 'hello':\n try {\n if (!this.isHostOriginAllowed(message.origin)) {\n process.stderr.write(\n `[webmcp-local-relay] warn: rejecting source ${connectionId} with disallowed host origin: ${message.origin ?? 'missing'}\\n`\n );\n const socket = this.socketByConnectionId.get(connectionId);\n socket?.close(1008, 'Host origin not allowed');\n break;\n }\n this.registry.upsertSource(connectionId, message);\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 if (err instanceof HelloRequiredError) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} sent tools before hello, ignoring\\n`\n );\n } else {\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 const socket = this.socketByConnectionId.get(connectionId);\n socket?.close(1011, 'Failed to register tools');\n }\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\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 break;\n }\n }\n\n /**\n * Handles relay-protocol messages from relay client connections.\n */\n private onRelayClientMessage(connectionId: string, message: RelayClientToServerMessage): void {\n switch (message.type) {\n case 'relay/hello':\n this.relayClientConnectionIds.add(connectionId);\n break;\n\n case 'relay/list-tools': {\n const tools = this.registry.listTools();\n const response: RelayServerToClientMessage = {\n type: 'relay/tools',\n tools: this.toWireTools(tools),\n };\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 (async () => {\n try {\n const result = await this.invokeToolLocally(toolName, args ?? {}, {});\n const response: RelayServerToClientMessage = {\n type: 'relay/result',\n callId,\n result,\n };\n const socket = this.socketByConnectionId.get(connectionId);\n if (socket?.readyState === WebSocket.OPEN) {\n try {\n socket.send(JSON.stringify(response));\n } catch (sendErr) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send relay result to ${connectionId}: ${sendErr instanceof Error ? sendErr.message : String(sendErr)}\\n`\n );\n }\n } else {\n process.stderr.write(\n `[webmcp-local-relay] warn: relay client ${connectionId} disconnected before result for callId ${callId} could be delivered\\n`\n );\n }\n } catch (err) {\n const response: RelayServerToClientMessage = {\n type: 'relay/result',\n callId,\n result: {\n content: [\n {\n type: 'text',\n text: `Relay invocation failed: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n },\n };\n const socket = this.socketByConnectionId.get(connectionId);\n if (socket?.readyState === WebSocket.OPEN) {\n try {\n socket.send(JSON.stringify(response));\n } catch (sendErr) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send relay error result to ${connectionId}: ${sendErr instanceof Error ? sendErr.message : String(sendErr)}\\n`\n );\n }\n } else {\n process.stderr.write(\n `[webmcp-local-relay] warn: relay client ${connectionId} disconnected before error result for callId ${callId} could be delivered\\n`\n );\n }\n }\n })();\n break;\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 tools = this.registry.listTools();\n const message: RelayServerToClientMessage = {\n type: 'relay/tools-changed',\n tools: this.toWireTools(tools),\n };\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 private onSocketClose(connectionId: string): void {\n this.relayClientConnectionIds.delete(connectionId);\n this.registry.removeConnection(connectionId);\n this.socketByConnectionId.delete(connectionId);\n this.emit('stateChanged');\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 pending.reject(new Error(`Tool source ${connectionId} disconnected during invocation`));\n }\n }\n\n private async startAsClient(): Promise<void> {\n this.stopping = false;\n\n return new Promise<void>((resolve, reject) => {\n const wsUrl = `ws://${this.host}:${this.desiredPort}`;\n const ws = new WebSocket(wsUrl);\n\n const onOpen = () => {\n ws.off('error', onFirstError);\n this.clientSocket = ws;\n this.clientReconnectDelay = 500;\n\n try {\n ws.send(JSON.stringify({ type: 'relay/hello' }));\n ws.send(JSON.stringify({ type: 'relay/list-tools' }));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] error: failed to send handshake to relay server: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n this.clientSocket = null;\n ws.close();\n reject(new Error('Failed to send handshake to relay server'));\n return;\n }\n\n this.setupClientHandlers(ws);\n resolve();\n };\n\n const onFirstError = (err: Error) => {\n ws.off('open', onOpen);\n reject(\n new Error(\n `Failed to connect to relay server at ws://${this.host}:${this.desiredPort}: ${err.message}`\n )\n );\n };\n\n ws.once('open', onOpen);\n ws.once('error', onFirstError);\n });\n }\n\n private setupClientHandlers(ws: WebSocket): void {\n ws.on('message', (raw: WebSocket.RawData) => {\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;\n }\n\n const msg = RelayServerToClientMessageSchema.safeParse(parsed);\n if (!msg.success) {\n const typeField =\n typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>).type\n : 'unknown';\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid relay server message (type=${typeField}): ${msg.error.message}\\n`\n );\n return;\n }\n\n switch (msg.data.type) {\n case 'relay/tools':\n case 'relay/tools-changed':\n this.clientTools = msg.data.tools;\n this.emit('stateChanged');\n break;\n\n case 'relay/result': {\n const pending = this.clientPendingInvocations.get(msg.data.callId);\n if (!pending) {\n process.stderr.write(\n `[webmcp-local-relay] warn: received relay result for unknown callId ${msg.data.callId}\\n`\n );\n break;\n }\n clearTimeout(pending.timeoutId);\n this.clientPendingInvocations.delete(msg.data.callId);\n pending.resolve(msg.data.result);\n break;\n }\n }\n });\n\n ws.on('close', () => {\n this.clientSocket = null;\n\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.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 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().catch((err) => {\n process.stderr.write(\n `[webmcp-local-relay] error: unexpected failure during reconnection: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n );\n if (!this.stopping) {\n this.scheduleReconnect();\n }\n });\n }, delay);\n }\n\n /**\n * Attempts to promote from client to server mode when reconnecting.\n * Tries to bind the port first; if EADDRINUSE, falls back to client mode.\n */\n private async reconnectWithModePromotion(): Promise<void> {\n try {\n await this.startAsServer();\n this._mode = 'server';\n this.clientReconnectDelay = 500;\n this.clientReconnectAttempts = 0;\n process.stderr.write('[webmcp-local-relay] info: promoted from client to server mode\\n');\n this.emit('stateChanged');\n } catch (err) {\n const isAddrInUse =\n err instanceof Error &&\n ('code' in err\n ? (err as NodeJS.ErrnoException).code === 'EADDRINUSE'\n : err.message.includes('EADDRINUSE'));\n\n if (!isAddrInUse) {\n process.stderr.write(\n `[webmcp-local-relay] warn: server promotion failed: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n if (!this.stopping) {\n this.scheduleReconnect();\n }\n return;\n }\n\n this.reconnectAsClient();\n }\n }\n\n /**\n * Reconnects as a client to an existing relay server.\n */\n private reconnectAsClient(): void {\n try {\n const wsUrl = `ws://${this.host}:${this.desiredPort}`;\n const ws = new WebSocket(wsUrl);\n\n const onReconnectError = (err: Error) => {\n process.stderr.write(\n `[webmcp-local-relay] warn: relay client reconnection failed: ${err.message}\\n`\n );\n if (!this.stopping) {\n this.scheduleReconnect();\n }\n };\n\n ws.once('open', () => {\n ws.off('error', onReconnectError);\n this.clientSocket = ws;\n this.clientReconnectDelay = 500;\n this.clientReconnectAttempts = 0;\n\n try {\n ws.send(JSON.stringify({ type: 'relay/hello' }));\n ws.send(JSON.stringify({ type: 'relay/list-tools' }));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] error: failed to send handshake during reconnection: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n this.clientSocket = null;\n ws.close();\n this.scheduleReconnect();\n return;\n }\n\n this.setupClientHandlers(ws);\n });\n\n ws.once('error', onReconnectError);\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] error: failed to create reconnection socket: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n if (!this.stopping) {\n this.scheduleReconnect();\n }\n }\n }\n\n private 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 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 * Maps aggregated tools to the wire format used by the relay protocol.\n */\n private toWireTools(tools: AggregatedTool[]): RelayTool[] {\n return tools.map(({ originalName: _originalName, sources: _sources, ...tool }) => tool);\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 /**\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 (typeof raw === 'string') {\n return raw;\n }\n\n if (Buffer.isBuffer(raw)) {\n return raw.toString('utf8');\n }\n\n if (raw instanceof ArrayBuffer) {\n return Buffer.from(raw).toString('utf8');\n }\n\n if (Array.isArray(raw)) {\n return Buffer.concat(raw).toString('utf8');\n }\n\n return String(raw);\n }\n}\n","import { execFile } from 'node:child_process';\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { z } from 'zod/v4';\n\nimport { RelayBridgeServer, type RelayBridgeServerOptions } from './bridgeServer.js';\nimport type { AggregatedTool } from './registry.js';\n\n/**\n * Handle returned by MCP tool registration.\n */\ninterface RegisteredToolHandle {\n remove: () => void;\n}\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 dynamicToolHandles = new Map<string, RegisteredToolHandle>();\n private readonly dynamicToolSignature = new Map<string, string>();\n\n private syncing = false;\n private syncRequested = false;\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 void this.syncDynamicTools().catch((err) => {\n this.logSyncError(err);\n });\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 await 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\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 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.dynamicToolHandles.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: {},\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n },\n },\n async () => {\n if (this.bridge.mode === 'client') {\n const info = {\n mode: 'client' as const,\n message: 'Proxying through an existing relay server.',\n count: 0,\n sources: [],\n };\n return {\n content: [{ type: 'text', text: JSON.stringify(info, null, 2) }],\n structuredContent: info,\n };\n }\n const sources = this.bridge.registry.listSources();\n return {\n content: [\n { type: 'text', text: JSON.stringify({ count: sources.length, sources }, null, 2) },\n ],\n structuredContent: {\n count: sources.length,\n sources,\n },\n };\n }\n );\n\n this.mcpServer.registerTool(\n 'webmcp_list_tools',\n {\n description:\n 'List WebMCP tools available from connected browser sources. ' +\n 'Returns tool definitions including name, description, input schema, and source info. ' +\n 'Use webmcp_call_tool to invoke a tool by name.',\n inputSchema: {},\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n },\n },\n async () => {\n const tools = this.listAggregatedTools();\n return {\n content: [\n { type: 'text', text: JSON.stringify({ count: tools.length, tools }, null, 2) },\n ],\n structuredContent: {\n count: tools.length,\n tools,\n },\n };\n }\n );\n\n this.mcpServer.registerTool(\n 'webmcp_call_tool',\n {\n description:\n 'Call a WebMCP tool registered by a connected browser page. ' +\n 'Use webmcp_list_tools first to see available tools and their input schemas.',\n inputSchema: {\n name: z\n .string()\n .describe('The tool name to call. Use webmcp_list_tools to see available tool names.'),\n arguments: z\n .record(z.string(), z.unknown())\n .optional()\n .describe(\n 'Arguments to pass to the tool as a JSON object. Check the tool inputSchema from webmcp_list_tools for expected fields.'\n ),\n },\n annotations: {\n readOnlyHint: false,\n },\n },\n async ({ name, arguments: args }) => {\n const tools = this.listAggregatedTools();\n const toolSummary = this.buildToolSummary(tools);\n\n const matched = tools.find((t) => t.name === name);\n if (!matched) {\n const errorLines = [`Tool \"${name}\" not found.`];\n if (toolSummary) {\n errorLines.push('', 'Available tools:', toolSummary);\n } else {\n errorLines.push(\n '',\n 'No tools are currently available. Ensure a browser page with WebMCP is connected.'\n );\n }\n return {\n content: [{ type: 'text' as const, text: errorLines.join('\\n') }],\n isError: true,\n };\n }\n\n try {\n const result = await this.bridge.invokeTool(name, args ?? {});\n\n const updatedTools =\n this.bridge.mode === 'client'\n ? this.buildAggregatedToolsFromRelay()\n : this.bridge.registry.listTools();\n const updatedSummary = this.buildToolSummary(updatedTools);\n if (updatedSummary) {\n result.content = [\n ...result.content,\n { type: 'text' as const, text: `\\n---\\nAvailable tools:\\n${updatedSummary}` },\n ];\n }\n\n return result;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const errorLines = [`Failed to call tool \"${name}\": ${message}`];\n if (toolSummary) {\n errorLines.push('', 'Available tools:', toolSummary);\n }\n return {\n content: [{ type: 'text' as const, text: errorLines.join('\\n') }],\n isError: true,\n };\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. \" +\n 'Use to launch WebMCP-enabled pages or reload stale connections.',\n inputSchema: {\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 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 const sources = this.bridge.registry.listSources();\n const matched = sources.find((s) => {\n if (!s.url) return false;\n try {\n return new URL(s.url).origin === parsed.origin;\n } catch {\n process.stderr.write(\n `[webmcp-local-relay] warn: source ${s.sourceId} has unparseable URL: ${s.url}\\n`\n );\n return false;\n }\n });\n\n if (!matched) {\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(matched.sourceId);\n return {\n content: [\n {\n type: 'text' as const,\n text: `Reload sent to source ${matched.sourceId} (${matched.url ?? matched.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 (this.bridge.mode === 'server') {\n const sources = this.bridge.registry.listSources();\n const existing = sources.find((s) => {\n if (!s.url) return false;\n try {\n return new URL(s.url).origin === parsed.origin;\n } catch {\n process.stderr.write(\n `[webmcp-local-relay] warn: source ${s.sourceId} has unparseable URL: ${s.url}\\n`\n );\n return false;\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\n return {\n content: [{ type: 'text' as const, text: `Opened ${url} in the default browser.` }],\n };\n }\n );\n }\n\n /**\n * Builds a concise plain-text list of available tools.\n */\n private buildToolSummary(tools: AggregatedTool[]): string | null {\n if (tools.length === 0) {\n return null;\n }\n\n return tools\n .map((t) => {\n const desc = t.description ? ` - ${t.description.split('\\n')[0]}` : '';\n return ` ${t.name}${desc}`;\n })\n .join('\\n');\n }\n\n /**\n * Opens a URL in the user's default browser using the platform open command.\n *\n * Uses the re-serialized `URL.href` to prevent injection of shell metacharacters.\n * On Windows, PowerShell `Start-Process` is used instead of `cmd /c start`\n * because cmd.exe interprets `&`, `|`, and other metacharacters in URLs.\n */\n private openInBrowser(url: string): Promise<void> {\n const safeUrl = new URL(url).href;\n return new Promise((resolve, reject) => {\n const platform = process.platform;\n let command: string;\n let args: string[];\n if (platform === 'darwin') {\n command = 'open';\n args = [safeUrl];\n } else if (platform === 'win32') {\n command = 'powershell.exe';\n args = ['-NoProfile', '-Command', `Start-Process \"${safeUrl}\"`];\n } else {\n command = 'xdg-open';\n args = [safeUrl];\n }\n execFile(command, args, (err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n /**\n * Coalesces concurrent sync requests into a single serialized sync loop.\n */\n private async syncDynamicTools(): Promise<void> {\n if (this.syncing) {\n this.syncRequested = true;\n return;\n }\n\n this.syncing = true;\n\n try {\n do {\n this.syncRequested = false;\n const tools = this.listAggregatedTools();\n this.applyDynamicTools(tools);\n } while (this.syncRequested);\n } finally {\n const retryRequested = this.syncRequested;\n this.syncRequested = false;\n this.syncing = false;\n if (retryRequested) {\n void this.syncDynamicTools().catch((err) => {\n this.logSyncError(err);\n });\n }\n }\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 * In client mode, source metadata is unavailable so tools are given empty sources.\n */\n private buildAggregatedToolsFromRelay(): AggregatedTool[] {\n return this.bridge\n .listToolsFromRelay()\n .map((tool) => ({\n ...tool,\n originalName: tool.name,\n sources: [],\n }))\n .sort((left, right) => left.name.localeCompare(right.name));\n }\n\n /**\n * Applies registry tool state to MCP dynamic registrations.\n */\n private applyDynamicTools(tools: AggregatedTool[]): void {\n const nextNames = new Set(tools.map((tool) => tool.name));\n let changed = false;\n\n for (const [name, handle] of this.dynamicToolHandles.entries()) {\n if (nextNames.has(name)) {\n continue;\n }\n\n handle.remove();\n this.dynamicToolHandles.delete(name);\n this.dynamicToolSignature.delete(name);\n changed = true;\n }\n\n for (const tool of tools) {\n const signature = this.toolSignature(tool);\n const currentSignature = this.dynamicToolSignature.get(tool.name);\n\n if (currentSignature === signature && this.dynamicToolHandles.has(tool.name)) {\n continue;\n }\n\n const existing = this.dynamicToolHandles.get(tool.name);\n if (existing) {\n existing.remove();\n this.dynamicToolHandles.delete(tool.name);\n }\n\n const handle = this.registerDynamicTool(tool);\n this.dynamicToolHandles.set(tool.name, handle);\n this.dynamicToolSignature.set(tool.name, signature);\n changed = true;\n }\n\n if (changed && this.connected) {\n try {\n this.mcpServer.sendToolListChanged();\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send tool list changed notification: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }\n }\n\n /**\n * Registers a single dynamic tool and returns a removal handle.\n */\n private registerDynamicTool(tool: AggregatedTool): RegisteredToolHandle {\n const inputSchema = z.object({}).passthrough();\n const config = {\n description: this.dynamicToolDescription(tool),\n inputSchema,\n };\n\n const handle = this.mcpServer.registerTool(\n tool.name,\n tool.annotations ? { ...config, annotations: tool.annotations } : config,\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 return {\n remove: () => {\n handle.remove();\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 private toolSignature(tool: AggregatedTool): string {\n return JSON.stringify({\n name: tool.name,\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 execution: tool.execution,\n _meta: tool._meta,\n icons: tool.icons,\n sources: tool.sources.map((source) => ({\n sourceId: source.sourceId,\n tabId: source.tabId,\n })),\n });\n }\n\n /**\n * Logs tool sync errors with full detail.\n */\n private logSyncError(err: unknown): void {\n const details = err instanceof Error ? (err.stack ?? err.message) : String(err);\n process.stderr.write(`[webmcp-local-relay] error: failed to sync dynamic tools: ${details}\\n`);\n }\n}\n"],"mappings":"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"}
|