@mcp-b/webmcp-local-relay 2.2.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -18
- package/dist/browser/embed.js +1 -1
- package/dist/browser/widget.html +1 -1
- package/dist/browser/widget.js +1 -1
- package/dist/{cli.js → cli.mjs} +11 -2
- package/dist/cli.mjs.map +1 -0
- package/dist/{index.d.ts → index.d.mts} +49 -7
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +3 -0
- package/dist/{mcpRelayServer-rbay_DSr.js → mcpRelayServer-BL8kNwIK.mjs} +221 -109
- package/dist/mcpRelayServer-BL8kNwIK.mjs.map +1 -0
- package/package.json +34 -34
- package/skills/webmcp-local-relay/SKILL.md +2 -2
- package/dist/cli.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -3
- package/dist/mcpRelayServer-rbay_DSr.js.map +0 -1
- /package/dist/{cli.d.ts → cli.d.mts} +0 -0
|
@@ -1,105 +1,16 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import { CallToolRequestParamsSchema, CallToolResultSchema, ListToolsRequestSchema, ToolAnnotationsSchema, ToolSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
-
import { z } from "zod/v4";
|
|
6
1
|
import { randomUUID } from "node:crypto";
|
|
7
2
|
import { EventEmitter } from "node:events";
|
|
8
3
|
import WebSocket, { WebSocketServer } from "ws";
|
|
9
4
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
10
5
|
import { homedir } from "node:os";
|
|
11
6
|
import { dirname, join } from "node:path";
|
|
7
|
+
import { CallToolRequestParamsSchema, CallToolResultSchema, ListToolsRequestSchema, ToolAnnotationsSchema, ToolSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
8
|
+
import { z } from "zod/v4";
|
|
9
|
+
import { execFile } from "node:child_process";
|
|
10
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
11
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
12
|
import { toJsonSchemaCompat } from "@modelcontextprotocol/sdk/server/zod-json-schema-compat.js";
|
|
13
13
|
|
|
14
|
-
//#region src/cli-utils.ts
|
|
15
|
-
/**
|
|
16
|
-
* Parses supported CLI flags for relay startup.
|
|
17
|
-
*/
|
|
18
|
-
function parseCliOptions(argv) {
|
|
19
|
-
const options = {
|
|
20
|
-
host: "127.0.0.1",
|
|
21
|
-
port: 9333,
|
|
22
|
-
portExplicitlySet: false,
|
|
23
|
-
allowedOrigins: ["*"]
|
|
24
|
-
};
|
|
25
|
-
const readFlagValue = (flag, index) => {
|
|
26
|
-
const next = argv[index + 1];
|
|
27
|
-
if (!next || next.startsWith("-")) throw new Error(`Missing value for ${flag}`);
|
|
28
|
-
return next;
|
|
29
|
-
};
|
|
30
|
-
for (let i = 0; i < argv.length; i++) {
|
|
31
|
-
const token = argv[i];
|
|
32
|
-
if (!token) continue;
|
|
33
|
-
if (token === "--host" || token === "-H") {
|
|
34
|
-
options.host = readFlagValue(token, i);
|
|
35
|
-
i += 1;
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
if (token === "--port" || token === "-p") {
|
|
39
|
-
const raw = readFlagValue(token, i);
|
|
40
|
-
i += 1;
|
|
41
|
-
const value = Number.parseInt(raw, 10);
|
|
42
|
-
if (!Number.isFinite(value) || value <= 0 || value > 65535) throw new Error(`Invalid port "${raw}". Port must be a number between 1 and 65535.`);
|
|
43
|
-
options.port = value;
|
|
44
|
-
options.portExplicitlySet = true;
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
if (token === "--widget-origin" || token === "--allowed-origin" || token === "--ws-origin") {
|
|
48
|
-
const raw = readFlagValue(token, i);
|
|
49
|
-
i += 1;
|
|
50
|
-
const split = raw.split(",").map((part) => part.trim()).filter(Boolean);
|
|
51
|
-
if (split.length > 0) options.allowedOrigins = split;
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
if (token === "--label") {
|
|
55
|
-
options.label = readFlagValue(token, i);
|
|
56
|
-
i += 1;
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (token === "--workspace") {
|
|
60
|
-
options.workspace = readFlagValue(token, i);
|
|
61
|
-
i += 1;
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
if (token === "--relay-id") {
|
|
65
|
-
options.relayId = readFlagValue(token, i);
|
|
66
|
-
i += 1;
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
if (token === "--help" || token === "-h") {
|
|
70
|
-
printHelp();
|
|
71
|
-
process.exit(0);
|
|
72
|
-
}
|
|
73
|
-
if (token.startsWith("-")) process.stderr.write(`[webmcp-local-relay] warn: unrecognized argument "${token}"\n`);
|
|
74
|
-
else process.stderr.write(`[webmcp-local-relay] warn: unrecognized argument "${token}" (positional arguments are not supported)\n`);
|
|
75
|
-
}
|
|
76
|
-
return options;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Prints CLI usage to stderr.
|
|
80
|
-
*/
|
|
81
|
-
function printHelp() {
|
|
82
|
-
process.stderr.write([
|
|
83
|
-
"webmcp-local-relay",
|
|
84
|
-
"",
|
|
85
|
-
"Usage:",
|
|
86
|
-
" webmcp-local-relay [--host 127.0.0.1] [--port 9333] [--widget-origin https://myapp.example.com]",
|
|
87
|
-
"",
|
|
88
|
-
"Options:",
|
|
89
|
-
" --host, -H Bind host for local websocket relay (default: 127.0.0.1)",
|
|
90
|
-
" --port, -p Preferred root port for the local relay cluster (default: 9333)",
|
|
91
|
-
" --widget-origin Allowed host page origin(s), comma-separated (default: *)",
|
|
92
|
-
" --allowed-origin Alias for --widget-origin",
|
|
93
|
-
" --ws-origin Alias for --widget-origin",
|
|
94
|
-
" --label Human-readable relay label reported during discovery",
|
|
95
|
-
" --workspace Optional workspace name reported during discovery",
|
|
96
|
-
" --relay-id Stable relay identifier reported during discovery",
|
|
97
|
-
" --help, -h Show help",
|
|
98
|
-
""
|
|
99
|
-
].join("\n"));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
//#endregion
|
|
103
14
|
//#region src/portStrategy.ts
|
|
104
15
|
const DEFAULT_RELAY_PORT = 9333;
|
|
105
16
|
const DEFAULT_RELAY_PORT_RANGE_END = 9348;
|
|
@@ -123,7 +34,7 @@ async function readPersistedPort(path = defaultRelayPortPersistPath(), options =
|
|
|
123
34
|
if (typeof parsed.port !== "number" || !Number.isInteger(parsed.port) || parsed.port < 1 || parsed.port > 65535) return null;
|
|
124
35
|
if (typeof parsed.host !== "string" || parsed.host.length === 0) return null;
|
|
125
36
|
if (options.expectedHost && parsed.host !== options.expectedHost) return null;
|
|
126
|
-
const maxAgeMs = options.maxAgeMs ??
|
|
37
|
+
const maxAgeMs = options.maxAgeMs ?? 864e5;
|
|
127
38
|
const now = options.now ?? Date.now();
|
|
128
39
|
const updatedAtMs = typeof parsed.updatedAt === "string" ? Date.parse(parsed.updatedAt) : NaN;
|
|
129
40
|
if (!Number.isFinite(updatedAtMs) || now - updatedAtMs > maxAgeMs) return null;
|
|
@@ -373,13 +284,13 @@ var RelayRegistry = class {
|
|
|
373
284
|
if (!providers || providers.length === 0) return null;
|
|
374
285
|
const rankedProviders = this.sortProvidersByRecency(providers);
|
|
375
286
|
if (options.sourceId) {
|
|
376
|
-
const byConnection = rankedProviders.find((provider
|
|
287
|
+
const byConnection = rankedProviders.find((provider) => provider.sourceId === options.sourceId);
|
|
377
288
|
if (byConnection) return {
|
|
378
289
|
connectionId: byConnection.sourceId,
|
|
379
290
|
tool: byConnection.tool,
|
|
380
291
|
publicToolName: byConnection.publicToolName
|
|
381
292
|
};
|
|
382
|
-
const byTabId = rankedProviders.find((provider
|
|
293
|
+
const byTabId = rankedProviders.find((provider) => provider.tabId === options.sourceId);
|
|
383
294
|
if (byTabId) return {
|
|
384
295
|
connectionId: byTabId.sourceId,
|
|
385
296
|
tool: byTabId.tool,
|
|
@@ -388,7 +299,7 @@ var RelayRegistry = class {
|
|
|
388
299
|
return null;
|
|
389
300
|
}
|
|
390
301
|
if (options.requestTabId) {
|
|
391
|
-
const byRequestTab = rankedProviders.find((provider
|
|
302
|
+
const byRequestTab = rankedProviders.find((provider) => provider.tabId === options.requestTabId);
|
|
392
303
|
if (!byRequestTab) return null;
|
|
393
304
|
return {
|
|
394
305
|
connectionId: byRequestTab.sourceId,
|
|
@@ -519,6 +430,20 @@ const BrowserToolResultMessageSchema = z.object({
|
|
|
519
430
|
*/
|
|
520
431
|
const BrowserPongMessageSchema = z.object({ type: z.literal("pong") });
|
|
521
432
|
/**
|
|
433
|
+
* Schema for elicitation request forwarded from a browser tool handler.
|
|
434
|
+
*
|
|
435
|
+
* When a WebMCP tool handler calls `elicitInput()`, the embed script
|
|
436
|
+
* intercepts the call and forwards the parameters to the relay via this
|
|
437
|
+
* message type. The relay then sends `elicitation/create` to the MCP
|
|
438
|
+
* client (e.g. Claude Code) and returns the result via an
|
|
439
|
+
* `elicitation-response` message.
|
|
440
|
+
*/
|
|
441
|
+
const BrowserElicitationRequestSchema = z.object({
|
|
442
|
+
type: z.literal("elicitation-request"),
|
|
443
|
+
callId: z.string().min(1),
|
|
444
|
+
params: z.record(z.string(), z.unknown())
|
|
445
|
+
});
|
|
446
|
+
/**
|
|
522
447
|
* Union schema for all browser-to-relay protocol messages.
|
|
523
448
|
*/
|
|
524
449
|
const BrowserToRelayMessageSchema = z.discriminatedUnion("type", [
|
|
@@ -526,7 +451,8 @@ const BrowserToRelayMessageSchema = z.discriminatedUnion("type", [
|
|
|
526
451
|
BrowserToolsListMessageSchema,
|
|
527
452
|
BrowserToolsChangedMessageSchema,
|
|
528
453
|
BrowserToolResultMessageSchema,
|
|
529
|
-
BrowserPongMessageSchema
|
|
454
|
+
BrowserPongMessageSchema,
|
|
455
|
+
BrowserElicitationRequestSchema
|
|
530
456
|
]);
|
|
531
457
|
/**
|
|
532
458
|
* Shared relay identity used for discovery and attach verification.
|
|
@@ -549,6 +475,18 @@ const ServerHelloMessageSchema = z.object({
|
|
|
549
475
|
...RelayDescriptorSchema.shape
|
|
550
476
|
});
|
|
551
477
|
/**
|
|
478
|
+
* Schema for acceptance of a browser source hello message.
|
|
479
|
+
*/
|
|
480
|
+
const RelayHelloAcceptedMessageSchema = z.object({ type: z.literal("hello/accepted") });
|
|
481
|
+
/**
|
|
482
|
+
* Schema for rejection of a browser source hello message.
|
|
483
|
+
*/
|
|
484
|
+
const RelayHelloRejectedMessageSchema = z.object({
|
|
485
|
+
type: z.literal("hello/rejected"),
|
|
486
|
+
reason: z.string().min(1),
|
|
487
|
+
message: z.string().min(1)
|
|
488
|
+
});
|
|
489
|
+
/**
|
|
552
490
|
* Schema for relay invocation messages sent to browser sources.
|
|
553
491
|
*/
|
|
554
492
|
const RelayInvokeMessageSchema = z.object({
|
|
@@ -566,13 +504,26 @@ const RelayPingMessageSchema = z.object({ type: z.literal("ping") });
|
|
|
566
504
|
*/
|
|
567
505
|
const RelayReloadMessageSchema = z.object({ type: z.literal("reload") });
|
|
568
506
|
/**
|
|
507
|
+
* Schema for elicitation response sent from relay to browser.
|
|
508
|
+
*
|
|
509
|
+
* Contains the MCP client's response to an `elicitation/create` request.
|
|
510
|
+
*/
|
|
511
|
+
const RelayElicitationResponseSchema = z.object({
|
|
512
|
+
type: z.literal("elicitation-response"),
|
|
513
|
+
callId: z.string().min(1),
|
|
514
|
+
result: z.record(z.string(), z.unknown())
|
|
515
|
+
});
|
|
516
|
+
/**
|
|
569
517
|
* Union schema for all relay-to-browser protocol messages.
|
|
570
518
|
*/
|
|
571
519
|
const RelayToBrowserMessageSchema = z.discriminatedUnion("type", [
|
|
572
520
|
ServerHelloMessageSchema,
|
|
521
|
+
RelayHelloAcceptedMessageSchema,
|
|
522
|
+
RelayHelloRejectedMessageSchema,
|
|
573
523
|
RelayInvokeMessageSchema,
|
|
574
524
|
RelayPingMessageSchema,
|
|
575
|
-
RelayReloadMessageSchema
|
|
525
|
+
RelayReloadMessageSchema,
|
|
526
|
+
RelayElicitationResponseSchema
|
|
576
527
|
]);
|
|
577
528
|
/**
|
|
578
529
|
* Relay-to-relay protocol (client mode <-> server mode).
|
|
@@ -725,10 +676,10 @@ var RelayBridgeServer = class extends EventEmitter {
|
|
|
725
676
|
super();
|
|
726
677
|
this.registry = registry ?? new RelayRegistry();
|
|
727
678
|
this.host = options.host ?? "127.0.0.1";
|
|
728
|
-
this.preferredPort = options.port ??
|
|
679
|
+
this.preferredPort = options.port ?? 9333;
|
|
729
680
|
this.desiredPort = this.preferredPort;
|
|
730
681
|
this.portExplicitlySet = options.portExplicitlySet ?? false;
|
|
731
|
-
this.portRangeEnd = options.portRangeEnd ?? Math.max(
|
|
682
|
+
this.portRangeEnd = options.portRangeEnd ?? Math.max(9348, this.preferredPort);
|
|
732
683
|
this.persistPath = options.persistPath ?? defaultRelayPortPersistPath();
|
|
733
684
|
this.allowedOrigins = options.allowedOrigins ?? ["*"];
|
|
734
685
|
this.maxPayloadBytes = options.maxPayloadBytes ?? 1e6;
|
|
@@ -1039,12 +990,18 @@ var RelayBridgeServer = class extends EventEmitter {
|
|
|
1039
990
|
switch (message.type) {
|
|
1040
991
|
case "hello":
|
|
1041
992
|
try {
|
|
993
|
+
const socket = this.socketByConnectionId.get(connectionId);
|
|
1042
994
|
if (!this.isHostOriginAllowed(message.origin)) {
|
|
1043
995
|
process.stderr.write(`[webmcp-local-relay] warn: rejecting source ${connectionId} with disallowed host origin: ${message.origin ?? "missing"}\n`);
|
|
1044
|
-
this.
|
|
996
|
+
if (socket) this.sendHelloRejected(socket, {
|
|
997
|
+
type: "hello/rejected",
|
|
998
|
+
reason: "host-origin-not-allowed",
|
|
999
|
+
message: "Host page origin is not allowed by this relay."
|
|
1000
|
+
}, 1008, "Host origin not allowed");
|
|
1045
1001
|
break;
|
|
1046
1002
|
}
|
|
1047
1003
|
this.registry.upsertSource(connectionId, message);
|
|
1004
|
+
if (socket) this.sendHelloAccepted(socket, { type: "hello/accepted" });
|
|
1048
1005
|
this.emit("stateChanged");
|
|
1049
1006
|
} catch (err) {
|
|
1050
1007
|
process.stderr.write(`[webmcp-local-relay] error: failed to process hello from connection ${connectionId}: ${err instanceof Error ? err.stack ?? err.message : String(err)}\n`);
|
|
@@ -1077,6 +1034,33 @@ var RelayBridgeServer = class extends EventEmitter {
|
|
|
1077
1034
|
case "pong":
|
|
1078
1035
|
this.lastPongByConnectionId.set(connectionId, Date.now());
|
|
1079
1036
|
break;
|
|
1037
|
+
case "elicitation-request":
|
|
1038
|
+
this.emit("elicitationRequest", {
|
|
1039
|
+
callId: message.callId,
|
|
1040
|
+
connectionId,
|
|
1041
|
+
params: message.params
|
|
1042
|
+
});
|
|
1043
|
+
break;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Sends an elicitation response back to the browser source that requested it.
|
|
1048
|
+
*/
|
|
1049
|
+
sendElicitationResponse(connectionId, callId, result) {
|
|
1050
|
+
const socket = this.socketByConnectionId.get(connectionId);
|
|
1051
|
+
if (!socket || socket.readyState !== WebSocket.OPEN) {
|
|
1052
|
+
process.stderr.write(`[webmcp-local-relay] warn: cannot send elicitation response — source ${connectionId} disconnected\n`);
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
const message = {
|
|
1056
|
+
type: "elicitation-response",
|
|
1057
|
+
callId,
|
|
1058
|
+
result
|
|
1059
|
+
};
|
|
1060
|
+
try {
|
|
1061
|
+
socket.send(JSON.stringify(message));
|
|
1062
|
+
} catch (err) {
|
|
1063
|
+
process.stderr.write(`[webmcp-local-relay] warn: failed to send elicitation response: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
1080
1064
|
}
|
|
1081
1065
|
}
|
|
1082
1066
|
/**
|
|
@@ -1439,13 +1423,33 @@ var RelayBridgeServer = class extends EventEmitter {
|
|
|
1439
1423
|
* Maps aggregated tools to the wire format used by the relay protocol.
|
|
1440
1424
|
*/
|
|
1441
1425
|
toWireTools(tools) {
|
|
1442
|
-
return tools.map(({ originalName: _originalName, sources: _sources
|
|
1426
|
+
return tools.map(({ originalName: _originalName, sources: _sources, ...tool }) => tool);
|
|
1443
1427
|
}
|
|
1444
1428
|
isHostOriginAllowed(origin) {
|
|
1445
1429
|
if (this.allowedOrigins.includes("*")) return true;
|
|
1446
1430
|
if (!origin) return false;
|
|
1447
1431
|
return this.allowedOrigins.includes(origin);
|
|
1448
1432
|
}
|
|
1433
|
+
sendHelloAccepted(socket, message) {
|
|
1434
|
+
try {
|
|
1435
|
+
socket.send(JSON.stringify(message));
|
|
1436
|
+
} catch (err) {
|
|
1437
|
+
process.stderr.write(`[webmcp-local-relay] warn: failed to send hello acceptance: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
1438
|
+
socket.close(1011, "Failed to send hello acceptance");
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
sendHelloRejected(socket, message, closeCode, closeReason) {
|
|
1442
|
+
try {
|
|
1443
|
+
socket.send(JSON.stringify(message), () => {
|
|
1444
|
+
try {
|
|
1445
|
+
socket.close(closeCode, closeReason);
|
|
1446
|
+
} catch {}
|
|
1447
|
+
});
|
|
1448
|
+
} catch (err) {
|
|
1449
|
+
process.stderr.write(`[webmcp-local-relay] warn: failed to send hello rejection: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
1450
|
+
socket.close(closeCode, closeReason);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1449
1453
|
/**
|
|
1450
1454
|
* Validates browser tool results against CallToolResultSchema.
|
|
1451
1455
|
* Non-conforming payloads are wrapped as error results with diagnostic text.
|
|
@@ -1475,6 +1479,95 @@ var RelayBridgeServer = class extends EventEmitter {
|
|
|
1475
1479
|
}
|
|
1476
1480
|
};
|
|
1477
1481
|
|
|
1482
|
+
//#endregion
|
|
1483
|
+
//#region src/cli-utils.ts
|
|
1484
|
+
/**
|
|
1485
|
+
* Parses supported CLI flags for relay startup.
|
|
1486
|
+
*/
|
|
1487
|
+
function parseCliOptions(argv) {
|
|
1488
|
+
const options = {
|
|
1489
|
+
host: "127.0.0.1",
|
|
1490
|
+
port: 9333,
|
|
1491
|
+
portExplicitlySet: false,
|
|
1492
|
+
allowedOrigins: ["*"]
|
|
1493
|
+
};
|
|
1494
|
+
const readFlagValue = (flag, index) => {
|
|
1495
|
+
const next = argv[index + 1];
|
|
1496
|
+
if (!next || next.startsWith("-")) throw new Error(`Missing value for ${flag}`);
|
|
1497
|
+
return next;
|
|
1498
|
+
};
|
|
1499
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1500
|
+
const token = argv[i];
|
|
1501
|
+
if (!token) continue;
|
|
1502
|
+
if (token === "--host" || token === "-H") {
|
|
1503
|
+
options.host = readFlagValue(token, i);
|
|
1504
|
+
i += 1;
|
|
1505
|
+
continue;
|
|
1506
|
+
}
|
|
1507
|
+
if (token === "--port" || token === "-p") {
|
|
1508
|
+
const raw = readFlagValue(token, i);
|
|
1509
|
+
i += 1;
|
|
1510
|
+
const value = Number.parseInt(raw, 10);
|
|
1511
|
+
if (!Number.isFinite(value) || value <= 0 || value > 65535) throw new Error(`Invalid port "${raw}". Port must be a number between 1 and 65535.`);
|
|
1512
|
+
options.port = value;
|
|
1513
|
+
options.portExplicitlySet = true;
|
|
1514
|
+
continue;
|
|
1515
|
+
}
|
|
1516
|
+
if (token === "--widget-origin" || token === "--allowed-origin" || token === "--ws-origin") {
|
|
1517
|
+
const raw = readFlagValue(token, i);
|
|
1518
|
+
i += 1;
|
|
1519
|
+
const split = raw.split(",").map((part) => part.trim()).filter(Boolean);
|
|
1520
|
+
if (split.length > 0) options.allowedOrigins = split;
|
|
1521
|
+
continue;
|
|
1522
|
+
}
|
|
1523
|
+
if (token === "--label") {
|
|
1524
|
+
options.label = readFlagValue(token, i);
|
|
1525
|
+
i += 1;
|
|
1526
|
+
continue;
|
|
1527
|
+
}
|
|
1528
|
+
if (token === "--workspace") {
|
|
1529
|
+
options.workspace = readFlagValue(token, i);
|
|
1530
|
+
i += 1;
|
|
1531
|
+
continue;
|
|
1532
|
+
}
|
|
1533
|
+
if (token === "--relay-id") {
|
|
1534
|
+
options.relayId = readFlagValue(token, i);
|
|
1535
|
+
i += 1;
|
|
1536
|
+
continue;
|
|
1537
|
+
}
|
|
1538
|
+
if (token === "--help" || token === "-h") {
|
|
1539
|
+
printHelp();
|
|
1540
|
+
process.exit(0);
|
|
1541
|
+
}
|
|
1542
|
+
if (token.startsWith("-")) process.stderr.write(`[webmcp-local-relay] warn: unrecognized argument "${token}"\n`);
|
|
1543
|
+
else process.stderr.write(`[webmcp-local-relay] warn: unrecognized argument "${token}" (positional arguments are not supported)\n`);
|
|
1544
|
+
}
|
|
1545
|
+
return options;
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Prints CLI usage to stderr.
|
|
1549
|
+
*/
|
|
1550
|
+
function printHelp() {
|
|
1551
|
+
process.stderr.write([
|
|
1552
|
+
"webmcp-local-relay",
|
|
1553
|
+
"",
|
|
1554
|
+
"Usage:",
|
|
1555
|
+
" webmcp-local-relay [--host 127.0.0.1] [--port 9333] [--widget-origin https://myapp.example.com]",
|
|
1556
|
+
"",
|
|
1557
|
+
"Options:",
|
|
1558
|
+
" --host, -H Bind host for local websocket relay (default: 127.0.0.1)",
|
|
1559
|
+
" --port, -p Preferred root port for the local relay cluster (default: 9333)",
|
|
1560
|
+
" --widget-origin Allowed host page origin(s), comma-separated (default: *)",
|
|
1561
|
+
" --allowed-origin Alias for --widget-origin",
|
|
1562
|
+
" --ws-origin Alias for --widget-origin",
|
|
1563
|
+
" --label Human-readable relay label reported during discovery",
|
|
1564
|
+
" --workspace Optional workspace name reported during discovery",
|
|
1565
|
+
" --relay-id Stable relay identifier reported during discovery",
|
|
1566
|
+
" --help, -h Show help",
|
|
1567
|
+
""
|
|
1568
|
+
].join("\n"));
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1478
1571
|
//#endregion
|
|
1479
1572
|
//#region src/staticToolSchemas.ts
|
|
1480
1573
|
const EMPTY_STATIC_TOOL_INPUT_SHAPE = {};
|
|
@@ -1538,6 +1631,22 @@ var LocalRelayMcpServer = class {
|
|
|
1538
1631
|
this.logSyncError(err);
|
|
1539
1632
|
});
|
|
1540
1633
|
});
|
|
1634
|
+
this.bridge.on("elicitationRequest", (request) => {
|
|
1635
|
+
(async () => {
|
|
1636
|
+
try {
|
|
1637
|
+
const result = await this.mcpServer.server.elicitInput(request.params);
|
|
1638
|
+
this.bridge.sendElicitationResponse(request.connectionId, request.callId, result);
|
|
1639
|
+
} catch (err) {
|
|
1640
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1641
|
+
process.stderr.write(`[webmcp-local-relay] warn: elicitation forwarding failed: ${message}\n`);
|
|
1642
|
+
this.bridge.sendElicitationResponse(request.connectionId, request.callId, {
|
|
1643
|
+
action: "decline",
|
|
1644
|
+
content: null,
|
|
1645
|
+
error: message
|
|
1646
|
+
});
|
|
1647
|
+
}
|
|
1648
|
+
})();
|
|
1649
|
+
});
|
|
1541
1650
|
this.registerStaticTools();
|
|
1542
1651
|
this.overrideListToolsHandler();
|
|
1543
1652
|
}
|
|
@@ -1557,6 +1666,9 @@ var LocalRelayMcpServer = class {
|
|
|
1557
1666
|
if (this.connected) throw new Error("MCP server transport already connected");
|
|
1558
1667
|
await this.mcpServer.connect(transport);
|
|
1559
1668
|
this.connected = true;
|
|
1669
|
+
this.mcpServer.server.onerror = (error) => {
|
|
1670
|
+
process.stderr.write(`[webmcp-local-relay] error: MCP protocol error: ${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
|
|
1671
|
+
};
|
|
1560
1672
|
}
|
|
1561
1673
|
/**
|
|
1562
1674
|
* Convenience helper that connects the server over stdio transport.
|
|
@@ -1633,11 +1745,11 @@ var LocalRelayMcpServer = class {
|
|
|
1633
1745
|
},
|
|
1634
1746
|
handler: async () => {
|
|
1635
1747
|
if (this.bridge.mode === "client") {
|
|
1636
|
-
const sources
|
|
1748
|
+
const sources = this.bridge.listSourcesFromRelay();
|
|
1637
1749
|
const info = {
|
|
1638
1750
|
mode: "client",
|
|
1639
|
-
count: sources
|
|
1640
|
-
sources
|
|
1751
|
+
count: sources.length,
|
|
1752
|
+
sources
|
|
1641
1753
|
};
|
|
1642
1754
|
return {
|
|
1643
1755
|
content: [{
|
|
@@ -2029,5 +2141,5 @@ var LocalRelayMcpServer = class {
|
|
|
2029
2141
|
};
|
|
2030
2142
|
|
|
2031
2143
|
//#endregion
|
|
2032
|
-
export {
|
|
2033
|
-
//# sourceMappingURL=mcpRelayServer-
|
|
2144
|
+
export { NormalizedToolSchema as C, normalizeInboundTool as D, ToolSchema as E, InboundToolSchema as S, ToolAnnotationsSchema as T, extractSanitizedDomain as _, BrowserToRelayMessageSchema as a, CallToolResultSchema as b, RelayHelloAcceptedMessageSchema as c, RelaySourceInfoSchema as d, RelayToBrowserMessageSchema as f, buildPublicToolName as g, RelayRegistry as h, RelayBridgeServer as i, RelayHelloRejectedMessageSchema as l, HelloRequiredError as m, parseCliOptions as n, RelayClientToServerMessageSchema as o, ServerHelloMessageSchema as p, printHelp as r, RelayDescriptorSchema as s, LocalRelayMcpServer as t, RelayServerToClientMessageSchema as u, sanitizeName as v, RelayInvokeArgsSchema as w, DEFAULT_TOOL_INPUT_SCHEMA as x, CallToolRequestParamsSchema as y };
|
|
2145
|
+
//# sourceMappingURL=mcpRelayServer-BL8kNwIK.mjs.map
|