@mcp-b/webmcp-local-relay 5.1.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpRelayServer-DEgebuO2.mjs","names":[],"sources":["../src/portStrategy.ts","../src/protocol.ts","../src/naming.ts","../src/registry.ts","../src/schemas.ts","../src/bridgeServer.ts","../src/cli-utils.ts","../src/mcpRelayServer.ts"],"sourcesContent":["import type { AddressInfo } from 'node:net';\nimport { z } from 'zod/v4';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\nexport function isTcpAddress(address: AddressInfo | string | null): address is AddressInfo {\n return address !== null && typeof address !== 'string';\n}\n\nexport const DEFAULT_RELAY_PORT = 9333;\nexport const DEFAULT_RELAY_PORT_RANGE_END = 9348;\nconst RELAY_PORT_CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000;\n\nconst PersistedRelayPortSchema = z.object({\n port: z.number().int().min(1).max(65535),\n host: z.string().min(1),\n updatedAt: z.string(),\n});\ntype PersistedRelayPort = z.infer<typeof PersistedRelayPortSchema>;\n\nexport interface PortStrategyOptions {\n fixedPort?: number;\n defaultPort: number;\n rangeEnd: number;\n host: string;\n persistPath?: string;\n}\n\nexport interface PortStrategyResult {\n port: number;\n wasFixed: boolean;\n fromCache: boolean;\n}\n\nexport function defaultRelayPortPersistPath(): string {\n return join(homedir(), '.webmcp', 'relay-port.json');\n}\n\nexport async function persistPort(\n port: number,\n path = defaultRelayPortPersistPath(),\n host = '127.0.0.1',\n now = Date.now()\n): Promise<void> {\n const payload: PersistedRelayPort = {\n port,\n host,\n updatedAt: new Date(now).toISOString(),\n };\n\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(payload, null, 2)}\\n`, 'utf8');\n}\n\nasync function readPersistedPort(path: string, expectedHost: string): Promise<number | null> {\n try {\n const raw = await readFile(path, 'utf8');\n const parsed = PersistedRelayPortSchema.parse(JSON.parse(raw));\n if (expectedHost && parsed.host !== expectedHost) {\n return null;\n }\n\n const updatedAtMs = Date.parse(parsed.updatedAt);\n if (!Number.isFinite(updatedAtMs) || Date.now() - updatedAtMs > RELAY_PORT_CACHE_MAX_AGE_MS) {\n return null;\n }\n\n return parsed.port;\n } catch {\n return null;\n }\n}\n\nexport async function buildPortCandidates(\n options: PortStrategyOptions\n): Promise<PortStrategyResult[]> {\n const {\n defaultPort,\n fixedPort,\n host,\n persistPath = defaultRelayPortPersistPath(),\n rangeEnd,\n } = options;\n\n if (fixedPort !== undefined) {\n return [{ port: fixedPort, wasFixed: true, fromCache: false }];\n }\n\n const cachedPort = await readPersistedPort(persistPath, host);\n const seen = new Set<number>();\n const candidates: PortStrategyResult[] = [];\n\n const pushCandidate = (port: number, fromCache: boolean): void => {\n if (port < 1 || port > 65535 || seen.has(port)) {\n return;\n }\n seen.add(port);\n candidates.push({ port, wasFixed: false, fromCache });\n };\n\n if (cachedPort !== null && cachedPort >= defaultPort && cachedPort <= rangeEnd) {\n pushCandidate(cachedPort, true);\n }\n\n pushCandidate(defaultPort, false);\n\n for (let port = defaultPort + 1; port <= rangeEnd; port += 1) {\n pushCandidate(port, false);\n }\n\n return candidates;\n}\n","import type { CallToolResult, Tool, ToolAnnotations } from '@modelcontextprotocol/server';\nimport {\n CallToolRequestParamsSchema,\n CallToolResultSchema,\n ToolAnnotationsSchema,\n ToolSchema,\n} from '@modelcontextprotocol/core';\nimport { z } from 'zod/v4';\n\n/**\n * SDK-derived canonical tool schema used internally by the relay.\n */\nexport const NormalizedToolSchema = ToolSchema.extend({\n name: ToolSchema.shape.name.min(1),\n});\n\n/**\n * SDK-derived argument schema for tool invocation payloads.\n */\nexport const RelayInvokeArgsSchema = CallToolRequestParamsSchema.shape.arguments;\n\n/**\n * Default input schema applied when inbound payload omits or provides\n * a non-object input schema.\n */\nexport const DEFAULT_TOOL_INPUT_SCHEMA: Tool['inputSchema'] = {\n type: 'object',\n properties: {},\n};\n\n/** SDK tool schema with a browser-compatible default input schema. */\nexport const InboundToolSchema = NormalizedToolSchema.extend({\n inputSchema: NormalizedToolSchema.shape.inputSchema.optional(),\n});\n\n/**\n * Canonical normalized relay tool shape.\n */\nexport type RelayTool = z.infer<typeof NormalizedToolSchema>;\n\n/**\n * Canonical relay tool annotations shape.\n */\nexport type RelayToolAnnotations = ToolAnnotations;\n\n/**\n * Canonical relay call result shape.\n */\nexport type RelayCallToolResult = CallToolResult;\n\n/**\n * Invocation argument object shape derived from MCP SDK request params.\n */\nexport type RelayInvokeArgs = Exclude<z.infer<typeof RelayInvokeArgsSchema>, undefined>;\n\n/**\n * Applies the browser default input schema and validates the complete SDK tool.\n */\nexport function normalizeInboundTool(inbound: z.infer<typeof InboundToolSchema>): RelayTool {\n return NormalizedToolSchema.parse({\n ...inbound,\n inputSchema: inbound.inputSchema ?? DEFAULT_TOOL_INPUT_SCHEMA,\n });\n}\n\nexport { CallToolRequestParamsSchema, CallToolResultSchema, ToolAnnotationsSchema, ToolSchema };\n","/**\n * Maximum tool name length supported across MCP clients.\n */\nexport const MAX_MCP_TOOL_NAME_LENGTH = 128;\n\n/**\n * Number of tab-id characters appended when disambiguation is required.\n */\nconst TAB_ID_DISAMBIGUATION_LENGTH = 4;\n\n/**\n * Converts arbitrary text into MCP-safe identifier characters.\n */\nexport function sanitizeName(value: string): string {\n return value.replace(/[^a-zA-Z0-9_]/g, '_');\n}\n\n/**\n * Extracts and sanitizes a domain label from an origin or URL.\n */\nexport function extractSanitizedDomain(originOrUrl?: string): string {\n if (!originOrUrl) {\n return 'unknown';\n }\n\n try {\n const parsed = new URL(originOrUrl);\n if (!parsed.hostname) {\n return 'unknown';\n }\n\n const isLocalhost =\n parsed.hostname === 'localhost' ||\n parsed.hostname === '127.0.0.1' ||\n parsed.hostname === '[::1]';\n\n const domain = isLocalhost ? `localhost_${parsed.port || '80'}` : parsed.hostname;\n return sanitizeName(domain);\n } catch {\n return 'unknown';\n }\n}\n\n/**\n * Builds a public tool name for MCP registration.\n *\n * Names can include a short tab-id suffix when multiple tabs publish the same\n * original tool name.\n */\nexport function buildPublicToolName(options: {\n originalToolName: string;\n tabId?: string;\n disambiguate?: boolean;\n}): string {\n const safeName = sanitizeName(options.originalToolName);\n\n if (!options.disambiguate || !options.tabId) {\n return safeName.slice(0, MAX_MCP_TOOL_NAME_LENGTH);\n }\n\n const shortTab = sanitizeName(options.tabId).slice(0, TAB_ID_DISAMBIGUATION_LENGTH);\n const suffix = `_${shortTab}`;\n const available = MAX_MCP_TOOL_NAME_LENGTH - suffix.length;\n return `${safeName.slice(0, available)}${suffix}`;\n}\n","import { buildPublicToolName, MAX_MCP_TOOL_NAME_LENGTH } from './naming.js';\nimport type { RelayTool } from './protocol.js';\nimport type { BrowserHelloMessage } from './schemas.js';\n\n/**\n * Internal metadata tracked for each active browser source.\n */\ninterface SourceMetadata {\n sourceId: string;\n tabId: string;\n // Optional keys, matching the wire shape in RelaySourceInfoSchema: a browser\n // source may omit these entirely rather than send them as undefined.\n origin?: string | undefined;\n url?: string | undefined;\n title?: string | undefined;\n iconUrl?: string | undefined;\n connectedAt: number;\n lastSeenAt: number;\n}\n\n/**\n * Public source metadata exposed by registry APIs.\n */\nexport interface SourceInfo extends SourceMetadata {\n toolCount: number;\n}\n\n/**\n * Aggregated relayed tool metadata across one or more sources.\n *\n * Extends {@link RelayTool} with relay-specific fields so new SDK tool\n * properties are automatically inherited without manual duplication.\n */\nexport interface AggregatedTool extends RelayTool {\n originalName: string;\n sources: SourceInfo[];\n}\n\n/**\n * Internal record linking a source to a concrete tool implementation.\n */\ninterface ToolProvider {\n sourceId: string;\n tabId: string;\n tool: RelayTool;\n publicToolName: string;\n}\n\n/**\n * Invocation target selected by the registry.\n */\nexport interface ResolvedInvocation {\n connectionId: string;\n tool: RelayTool;\n publicToolName: string;\n}\n\n/**\n * Error thrown when a source attempts to register tools before `hello`.\n */\nexport class HelloRequiredError extends Error {\n readonly connectionId: string;\n\n constructor(connectionId: string) {\n super(`Connection ${connectionId} must send hello before tools`);\n this.name = 'HelloRequiredError';\n this.connectionId = connectionId;\n }\n}\n\n/**\n * In-memory source and tool registry used by the relay bridge.\n */\nexport class RelayRegistry {\n private readonly sourceByConnectionId = new Map<string, SourceMetadata>();\n private readonly toolsByConnectionId = new Map<string, ToolProvider[]>();\n private readonly providersByPublicToolName = new Map<string, ToolProvider[]>();\n\n /**\n * @param now Time provider used for recency ordering.\n */\n constructor(private readonly now: () => number = () => Date.now()) {}\n\n /**\n * Upserts source metadata from a browser `hello` message.\n *\n * `sourceId` currently matches `connectionId`, but remains conceptually distinct\n * so stable source identity can be introduced in the future.\n */\n upsertSource(connectionId: string, hello: BrowserHelloMessage): void {\n const now = this.now();\n const existing = this.sourceByConnectionId.get(connectionId);\n\n this.sourceByConnectionId.set(connectionId, {\n sourceId: connectionId,\n tabId: hello.tabId,\n origin: hello.origin ?? existing?.origin,\n url: hello.url ?? existing?.url,\n title: hello.title ?? existing?.title,\n iconUrl: hello.iconUrl ?? existing?.iconUrl,\n connectedAt: existing?.connectedAt ?? now,\n lastSeenAt: now,\n });\n\n if (existing && existing.tabId !== hello.tabId) {\n for (const provider of this.toolsByConnectionId.get(connectionId) ?? []) {\n provider.tabId = hello.tabId;\n }\n this.rebuildPublicNames();\n }\n }\n\n /**\n * Replaces the full tool set for a source connection.\n */\n registerTools(connectionId: string, tools: RelayTool[]): void {\n const source = this.sourceByConnectionId.get(connectionId);\n if (!source) {\n throw new HelloRequiredError(connectionId);\n }\n\n this.touchConnection(connectionId);\n\n const providers: ToolProvider[] = tools.map((tool) => ({\n sourceId: connectionId,\n tabId: source.tabId,\n tool,\n publicToolName: '',\n }));\n\n this.toolsByConnectionId.set(connectionId, providers);\n this.rebuildPublicNames();\n }\n\n /**\n * Removes a source and all of its tool registrations.\n */\n removeConnection(connectionId: string): void {\n this.toolsByConnectionId.delete(connectionId);\n this.sourceByConnectionId.delete(connectionId);\n this.rebuildPublicNames();\n }\n\n /**\n * Marks a source as recently seen without mutating identity metadata.\n */\n touchConnection(connectionId: string): void {\n const source = this.sourceByConnectionId.get(connectionId);\n if (!source) {\n return;\n }\n\n source.lastSeenAt = this.now();\n }\n\n /**\n * Lists active sources that currently publish at least one tool.\n */\n listSources(): SourceInfo[] {\n return Array.from(this.sourceByConnectionId.values())\n .map((source) => this.toSourceInfo(source))\n .filter((source) => source.toolCount > 0)\n .sort((a, b) => this.compareRecency(b.lastSeenAt, a.lastSeenAt, b.sourceId, a.sourceId));\n }\n\n /**\n * Lists aggregated tools ordered by public tool name.\n */\n listTools(): AggregatedTool[] {\n const result: AggregatedTool[] = [];\n\n for (const [publicToolName, providers] of this.providersByPublicToolName.entries()) {\n const rankedProviders = this.sortProvidersByRecency(providers);\n const primaryProvider = rankedProviders[0];\n if (!primaryProvider) {\n continue;\n }\n\n const sources = rankedProviders\n .map((provider) => {\n const source = this.sourceByConnectionId.get(provider.sourceId);\n return source ? this.toSourceInfo(source) : undefined;\n })\n .filter((source): source is SourceInfo => Boolean(source));\n\n result.push({\n ...primaryProvider.tool,\n name: publicToolName,\n originalName: primaryProvider.tool.name,\n sources,\n });\n }\n\n return result.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n /**\n * Resolves a tool invocation to a concrete source provider.\n *\n * Resolution order:\n * 1. `sourceId`: exact source id, then fallback to tab id.\n * 2. `requestTabId`: strict tab id match with no fallback.\n * 3. Default: most recently seen provider.\n */\n resolveInvocation(options: {\n toolName: string;\n sourceId?: string;\n requestTabId?: string;\n }): ResolvedInvocation | null {\n const providers = this.providersByPublicToolName.get(options.toolName);\n if (!providers || providers.length === 0) {\n return null;\n }\n\n const rankedProviders = this.sortProvidersByRecency(providers);\n\n let provider: ToolProvider | undefined;\n if (options.sourceId) {\n provider =\n rankedProviders.find((candidate) => candidate.sourceId === options.sourceId) ??\n rankedProviders.find((candidate) => candidate.tabId === options.sourceId);\n } else if (options.requestTabId) {\n provider = rankedProviders.find((candidate) => candidate.tabId === options.requestTabId);\n } else {\n provider = rankedProviders[0];\n }\n\n if (!provider) {\n return null;\n }\n\n return {\n connectionId: provider.sourceId,\n tool: provider.tool,\n publicToolName: provider.publicToolName,\n };\n }\n\n /**\n * Converts internal source metadata to public source info.\n */\n private toSourceInfo(source: SourceMetadata): SourceInfo {\n return {\n ...source,\n toolCount: this.toolsByConnectionId.get(source.sourceId)?.length ?? 0,\n };\n }\n\n /**\n * Rebuilds public tool names after source set changes.\n *\n * When the same original tool name appears in multiple tabs, names are\n * disambiguated with a short tab suffix.\n */\n private rebuildPublicNames(): void {\n const byOriginalName = new Map<string, ToolProvider[]>();\n for (const providers of this.toolsByConnectionId.values()) {\n for (const provider of providers) {\n const key = provider.tool.name;\n const group = byOriginalName.get(key) ?? [];\n group.push(provider);\n byOriginalName.set(key, group);\n }\n }\n\n this.providersByPublicToolName.clear();\n const allocations: Array<{ candidate: string; key: string; provider: ToolProvider }> = [];\n\n for (const [originalName, providers] of byOriginalName) {\n const uniqueTabIds = new Set(providers.map((p) => p.tabId));\n const disambiguate = uniqueTabIds.size > 1;\n\n for (const provider of providers) {\n allocations.push({\n candidate: buildPublicToolName({\n originalToolName: originalName,\n tabId: provider.tabId,\n disambiguate,\n }),\n key: disambiguate ? `${originalName}\\0${provider.tabId}` : originalName,\n provider,\n });\n }\n }\n\n const nameByKey = new Map<string, string>();\n const usedNames = new Set<string>();\n for (const { candidate, key, provider } of allocations.sort((a, b) =>\n a.key < b.key ? -1 : a.key > b.key ? 1 : 0\n )) {\n let publicToolName = nameByKey.get(key);\n if (!publicToolName) {\n publicToolName = candidate;\n for (let index = 2; usedNames.has(publicToolName); index += 1) {\n const suffix = `_${String(index)}`;\n publicToolName = `${candidate.slice(0, MAX_MCP_TOOL_NAME_LENGTH - suffix.length)}${suffix}`;\n }\n nameByKey.set(key, publicToolName);\n usedNames.add(publicToolName);\n }\n\n provider.publicToolName = publicToolName;\n\n const existing = this.providersByPublicToolName.get(publicToolName) ?? [];\n existing.push(provider);\n this.providersByPublicToolName.set(publicToolName, existing);\n }\n }\n\n /**\n * Returns providers sorted by source recency.\n */\n private sortProvidersByRecency(providers: ToolProvider[]): ToolProvider[] {\n return providers.slice().sort((a, b) => {\n const aSource = this.sourceByConnectionId.get(a.sourceId);\n const bSource = this.sourceByConnectionId.get(b.sourceId);\n const aLastSeen = aSource?.lastSeenAt ?? 0;\n const bLastSeen = bSource?.lastSeenAt ?? 0;\n\n return this.compareRecency(bLastSeen, aLastSeen, b.sourceId, a.sourceId);\n });\n }\n\n /**\n * Compares two source recency tuples and stabilizes order by source id.\n */\n private compareRecency(\n leftLastSeen: number,\n rightLastSeen: number,\n leftId: string,\n rightId: string\n ): number {\n const timeDiff = leftLastSeen - rightLastSeen;\n if (timeDiff !== 0) {\n return timeDiff;\n }\n return leftId.localeCompare(rightId);\n }\n}\n","import { z } from 'zod/v4';\nimport {\n CallToolResultSchema,\n InboundToolSchema,\n NormalizedToolSchema,\n normalizeInboundTool,\n RelayInvokeArgsSchema,\n type RelayTool,\n} from './protocol.js';\n\nfunction normalizeSupportedInboundTools(tools: z.infer<typeof InboundToolSchema>[]): RelayTool[] {\n return tools\n .map(normalizeInboundTool)\n .filter((tool) => tool.execution?.taskSupport !== 'required');\n}\n\nconst BrowserHelloMessageSchema = z.object({\n type: z.literal('hello'),\n tabId: z.string().min(1),\n origin: z.string().optional(),\n url: z.string().optional(),\n title: z.string().optional(),\n iconUrl: z.string().optional(),\n});\n\nconst BrowserToolsListMessageSchema = z.object({\n type: z.literal('tools/list'),\n tools: z.array(InboundToolSchema).transform(normalizeSupportedInboundTools),\n});\n\nconst BrowserToolsChangedMessageSchema = z.object({\n type: z.literal('tools/changed'),\n tools: z.array(InboundToolSchema).transform(normalizeSupportedInboundTools),\n});\n\nconst BrowserToolResultMessageSchema = z.object({\n type: z.literal('result'),\n callId: z.string().min(1),\n result: z.unknown(),\n});\n\nconst BrowserPongMessageSchema = z.object({\n type: z.literal('pong'),\n});\n\n/**\n * Union schema for all browser-to-relay protocol messages.\n */\nexport const BrowserToRelayMessageSchema = z.discriminatedUnion('type', [\n BrowserHelloMessageSchema,\n BrowserToolsListMessageSchema,\n BrowserToolsChangedMessageSchema,\n BrowserToolResultMessageSchema,\n BrowserPongMessageSchema,\n]);\n\n/**\n * Browser-to-relay message payload.\n */\nexport type BrowserToRelayMessage = z.infer<typeof BrowserToRelayMessageSchema>;\n\n/**\n * Browser source bootstrap message.\n */\nexport type BrowserHelloMessage = z.infer<typeof BrowserHelloMessageSchema>;\n\n/**\n * Shared relay identity used for discovery and attach verification.\n */\nexport const RelayDescriptorSchema = z.object({\n host: z.string().min(1),\n instanceId: z.string().min(1),\n label: z.string().min(1).optional(),\n port: z.number().int().min(1).max(65535),\n relayId: z.string().min(1).optional(),\n workspace: z.string().min(1).optional(),\n});\n\n/**\n * Relay identity used for discovery and attach verification.\n */\nexport type RelayDescriptor = z.infer<typeof RelayDescriptorSchema>;\n\n/**\n * Schema for server hello messages sent immediately after WebSocket connect.\n */\nexport const ServerHelloMessageSchema = RelayDescriptorSchema.extend({\n type: z.literal('server-hello'),\n service: z.literal('webmcp-local-relay'),\n version: z.literal(1),\n});\n\n/**\n * Server hello payload.\n */\nexport type ServerHelloMessage = z.infer<typeof ServerHelloMessageSchema>;\n\n/**\n * Schema for acceptance of a browser source hello message.\n */\nexport const RelayHelloAcceptedMessageSchema = z.object({\n type: z.literal('hello/accepted'),\n});\n\n/**\n * Acceptance payload for a browser source hello message.\n */\nexport type RelayHelloAcceptedMessage = z.infer<typeof RelayHelloAcceptedMessageSchema>;\n\n/**\n * Schema for rejection of a browser source hello message.\n */\nexport const RelayHelloRejectedMessageSchema = z.object({\n type: z.literal('hello/rejected'),\n reason: z.string().min(1),\n message: z.string().min(1),\n});\n\n/**\n * Rejection payload for a browser source hello message.\n */\nexport type RelayHelloRejectedMessage = z.infer<typeof RelayHelloRejectedMessageSchema>;\n\nconst RelayInvokeMessageSchema = z.object({\n type: z.literal('invoke'),\n callId: z.string().min(1),\n toolName: z.string().min(1),\n args: RelayInvokeArgsSchema,\n});\n\nconst RelayPingMessageSchema = z.object({\n type: z.literal('ping'),\n});\n\nconst RelayReloadMessageSchema = z.object({\n type: z.literal('reload'),\n});\n\n/**\n * Union schema for all relay-to-browser protocol messages.\n */\nexport const RelayToBrowserMessageSchema = z.discriminatedUnion('type', [\n ServerHelloMessageSchema,\n RelayHelloAcceptedMessageSchema,\n RelayHelloRejectedMessageSchema,\n RelayInvokeMessageSchema,\n RelayPingMessageSchema,\n RelayReloadMessageSchema,\n]);\n\n/**\n * Relay-to-browser message payload.\n */\nexport type RelayToBrowserMessage = z.infer<typeof RelayToBrowserMessageSchema>;\n\n/**\n * Schema for source metadata transmitted in relay-to-relay messages.\n */\nexport const RelaySourceInfoSchema = z.object({\n sourceId: z.string(),\n tabId: z.string(),\n origin: z.string().optional(),\n url: z.string().optional(),\n title: z.string().optional(),\n iconUrl: z.string().optional(),\n connectedAt: z.number(),\n lastSeenAt: z.number(),\n toolCount: z.number(),\n});\n\n/**\n * Source metadata transmitted in relay-to-relay messages.\n */\nexport type RelaySourceInfo = z.infer<typeof RelaySourceInfoSchema>;\n\nconst RelayClientHelloSchema = z.object({\n type: z.literal('relay/hello'),\n});\n\nconst RelayClientListToolsSchema = z.object({\n type: z.literal('relay/list-tools'),\n});\n\nconst RelayClientInvokeSchema = z.object({\n type: z.literal('relay/invoke'),\n callId: z.string().min(1),\n toolName: z.string().min(1),\n args: RelayInvokeArgsSchema,\n});\n\n/**\n * Union schema for all relay-client-to-server messages.\n */\nexport const RelayClientToServerMessageSchema = z.discriminatedUnion('type', [\n RelayClientHelloSchema,\n RelayClientListToolsSchema,\n RelayClientInvokeSchema,\n]);\n\n/**\n * Relay client to server message payload.\n */\nexport type RelayClientToServerMessage = z.infer<typeof RelayClientToServerMessageSchema>;\n\nconst RelayToolsPayloadFields = {\n tools: z.array(NormalizedToolSchema),\n sources: z.array(RelaySourceInfoSchema),\n toolSourceMap: z.record(z.string(), z.array(z.string())),\n};\n\nconst RelayServerToolsSchema = z.object({\n type: z.literal('relay/tools'),\n ...RelayToolsPayloadFields,\n});\n\nconst RelayServerResultSchema = z.object({\n type: z.literal('relay/result'),\n callId: z.string().min(1),\n result: CallToolResultSchema,\n});\n\nconst RelayServerToolsChangedSchema = z.object({\n type: z.literal('relay/tools-changed'),\n ...RelayToolsPayloadFields,\n});\n\n/**\n * Union schema for all relay-server-to-client messages.\n */\nexport const RelayServerToClientMessageSchema = z.discriminatedUnion('type', [\n ServerHelloMessageSchema,\n RelayServerToolsSchema,\n RelayServerResultSchema,\n RelayServerToolsChangedSchema,\n]);\n\n/**\n * Relay server to client message payload.\n */\nexport type RelayServerToClientMessage = z.infer<typeof RelayServerToClientMessageSchema>;\n","import { z } from 'zod/v4';\nimport { randomUUID } from 'node:crypto';\nimport { EventEmitter } from 'node:events';\n\nimport WebSocket, { WebSocketServer } from 'ws';\nimport {\n buildPortCandidates,\n DEFAULT_RELAY_PORT,\n DEFAULT_RELAY_PORT_RANGE_END,\n defaultRelayPortPersistPath,\n persistPort,\n isTcpAddress,\n type PortStrategyOptions,\n} from './portStrategy.js';\nimport {\n CallToolResultSchema,\n type RelayCallToolResult,\n type RelayInvokeArgs,\n type RelayTool,\n} from './protocol.js';\nimport { RelayRegistry } from './registry.js';\nimport {\n type BrowserToRelayMessage,\n BrowserToRelayMessageSchema,\n type RelayClientToServerMessage,\n RelayClientToServerMessageSchema,\n type RelayServerToClientMessage,\n RelayServerToClientMessageSchema,\n type RelayHelloAcceptedMessage,\n type RelayHelloRejectedMessage,\n type RelaySourceInfo,\n type RelayToBrowserMessage,\n type ServerHelloMessage,\n} from './schemas.js';\n\nconst RELAY_BROWSER_PROTOCOL = 'webmcp.v1';\nconst RELAY_DISCOVERY_PROTOCOL = 'webmcp-discovery.v1';\nconst RELAY_INTERNAL_PROTOCOL = 'webmcp-relay.v1';\nconst RELAY_SERVER_MESSAGE_TIMEOUT_MS = 750;\nconst HEARTBEAT_INTERVAL_MS = 15_000;\nconst HEARTBEAT_DEAD_THRESHOLD_MS = 25_000;\nconst RawMessageTypeSchema = z.object({ type: z.unknown().optional() });\nconst RelayProtocolTypeSchema = z.string().startsWith('relay/');\nconst SUPPORTED_SUBPROTOCOLS = new Set([\n RELAY_BROWSER_PROTOCOL,\n RELAY_DISCOVERY_PROTOCOL,\n RELAY_INTERNAL_PROTOCOL,\n]);\n\n/**\n * In-flight relay invocation waiting for a browser `result` message.\n */\ninterface PendingInvocation {\n connectionId: string;\n timeoutId: ReturnType<typeof setTimeout>;\n resolve: (result: RelayCallToolResult) => void;\n reject: (error: Error) => void;\n}\n\nfunction formatPayloadLimit(bytes: number): string {\n if (bytes >= 1_000_000 && bytes % 1_000_000 === 0) {\n return `${String(bytes / 1_000_000)}MB`;\n }\n return `${String(bytes)} bytes`;\n}\n\n/**\n * Runtime options for {@link RelayBridgeServer}.\n */\nexport interface RelayBridgeServerOptions {\n /**\n * Network interface used by the local WebSocket server.\n * @defaultValue `\"127.0.0.1\"`\n */\n host?: string;\n /**\n * Preferred WebSocket port for browser widget connections.\n * @defaultValue `9333`\n */\n port?: number;\n /**\n * Whether the preferred port came from an explicit CLI/API override.\n * Explicit ports fail if occupied by a non-relay process.\n * @defaultValue `false`\n */\n portExplicitlySet?: boolean;\n /**\n * Inclusive upper bound for automatic port discovery.\n * @defaultValue `9348`\n */\n portRangeEnd?: number;\n /**\n * File path used to cache the last successful relay port.\n * @defaultValue `~/.webmcp/relay-port.json`\n */\n persistPath?: string;\n /**\n * Allowed host page origins verified from browser WebSocket requests.\n * Use `[\"*\"]` to allow all origins.\n *\n * Permissive by default for zero-config developer experience: any browser\n * page can connect and register tools without additional setup. For production\n * or shared machines, restrict to trusted host origins via the\n * `--widget-origin` CLI flag or by passing explicit origins here.\n *\n * @defaultValue `[\"*\"]`\n */\n allowedOrigins?: string[];\n /**\n * Maximum WebSocket payload size in bytes.\n * @defaultValue `10000000`\n */\n maxPayloadBytes?: number;\n /**\n * Timeout used for browser tool invocations.\n * @defaultValue `65000`\n */\n invokeTimeoutMs?: number;\n /**\n * Human-readable relay label reported in discovery handshakes.\n */\n label?: string;\n /**\n * Optional workspace name reported in discovery handshakes.\n */\n workspace?: string;\n /**\n * Stable relay identifier used to select between multiple relays.\n */\n relayId?: string;\n}\n\n/**\n * WebSocket relay between browser widget frames and MCP server calls.\n *\n * Operates in two modes:\n * - **server** (default): Runs a WebSocket server, accepts browser and relay\n * client connections.\n * - **client** (fallback on EADDRINUSE): Connects as a WebSocket client to an\n * existing server relay and proxies tool operations through it.\n */\nexport class RelayBridgeServer extends EventEmitter {\n /**\n * Registry for connected sources and aggregated tool definitions.\n * Only actively used in server mode; remains empty when operating as a client.\n */\n readonly registry: RelayRegistry;\n\n private readonly host: string;\n private readonly preferredPort: number;\n private desiredPort: number;\n private readonly portExplicitlySet: boolean;\n private readonly portRangeEnd: number;\n private readonly persistPath: string;\n private readonly allowedOrigins: string[];\n private readonly maxPayloadBytes: number;\n private readonly invokeTimeoutMs: number;\n private readonly label: string | undefined;\n private readonly workspace: string | undefined;\n private readonly relayId: string | undefined;\n private readonly instanceId: string;\n\n private wss: WebSocketServer | null = null;\n private readonly socketByConnectionId = new Map<string, WebSocket>();\n private readonly requestOriginByConnectionId = new Map<string, string>();\n private readonly pendingInvocations = new Map<string, PendingInvocation>();\n private readonly browserClientConnectionIds = new Set<string>();\n private readonly relayClientConnectionIds = new Set<string>();\n private readonly heartbeatIntervalByConnectionId = new Map<\n string,\n ReturnType<typeof setInterval>\n >();\n private readonly lastPongByConnectionId = new Map<string, number>();\n private readonly onStateChangedPushRelay = () => {\n this.pushToolsToRelayClients();\n };\n\n private _mode: 'server' | 'client' = 'server';\n private clientSocket: WebSocket | null = null;\n private clientReconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private clientReconnectDelay = 500;\n /**\n * Maximum delay for relay-to-relay reconnection backoff in client mode.\n */\n private readonly clientMaxReconnectDelay = 3_000;\n private readonly clientMaxReconnectAttempts = 100;\n private clientReconnectAttempts = 0;\n private readonly clientPendingInvocations = new Map<string, PendingInvocation>();\n private clientTools: RelayTool[] = [];\n private clientSources: RelaySourceInfo[] = [];\n private clientToolSourceMap: Record<string, string[]> = {};\n private stopping = false;\n\n /**\n * Creates a relay bridge server instance.\n */\n constructor(options: RelayBridgeServerOptions = {}, registry?: RelayRegistry) {\n super();\n this.registry = registry ?? new RelayRegistry();\n\n this.host = options.host ?? '127.0.0.1';\n this.preferredPort = options.port ?? DEFAULT_RELAY_PORT;\n this.desiredPort = this.preferredPort;\n this.portExplicitlySet = options.portExplicitlySet ?? false;\n this.portRangeEnd =\n options.portRangeEnd ?? Math.max(DEFAULT_RELAY_PORT_RANGE_END, this.preferredPort);\n this.persistPath = options.persistPath ?? defaultRelayPortPersistPath();\n this.allowedOrigins = options.allowedOrigins ?? ['*'];\n // 10MB default: large enough for typical JSON API responses while\n // still protecting the relay process from unbounded memory growth.\n this.maxPayloadBytes = options.maxPayloadBytes ?? 10_000_000;\n this.invokeTimeoutMs = options.invokeTimeoutMs ?? 65_000;\n this.label = options.label;\n this.workspace = options.workspace;\n this.relayId = options.relayId;\n this.instanceId = randomUUID();\n\n if (\n !Number.isInteger(this.preferredPort) ||\n this.preferredPort < 0 ||\n this.preferredPort > 65535\n ) {\n throw new Error(\n `Invalid port ${this.preferredPort}. Port must be 0 (auto-assign) or between 1 and 65535.`\n );\n }\n if (\n !Number.isInteger(this.portRangeEnd) ||\n this.portRangeEnd < 1 ||\n this.portRangeEnd > 65535 ||\n (this.portRangeEnd < this.preferredPort && this.preferredPort !== 0)\n ) {\n throw new Error(\n `Invalid port range ${this.preferredPort}-${this.portRangeEnd}. rangeEnd must be an integer between the preferred port and 65535.`\n );\n }\n if (!Number.isInteger(this.maxPayloadBytes) || this.maxPayloadBytes <= 0) {\n throw new Error(\n `Invalid maxPayloadBytes ${this.maxPayloadBytes}. Must be a positive integer.`\n );\n }\n if (!Number.isInteger(this.invokeTimeoutMs) || this.invokeTimeoutMs <= 0) {\n throw new Error(\n `Invalid invokeTimeoutMs ${this.invokeTimeoutMs}. Must be a positive integer.`\n );\n }\n }\n\n /**\n * Current operating mode.\n */\n get mode(): 'server' | 'client' {\n return this._mode;\n }\n\n /**\n * Resolved listening port. In client mode this is the port of the server\n * relay being proxied through.\n */\n get port(): number {\n return this.desiredPort;\n }\n\n /**\n * Tools received from the server relay (client mode only).\n * Returns an empty array in server mode.\n */\n listToolsFromRelay(): RelayTool[] {\n return this._mode === 'client' ? [...this.clientTools] : [];\n }\n\n /**\n * Source metadata received from the server relay (client mode only).\n * Returns an empty array in server mode.\n */\n listSourcesFromRelay(): RelaySourceInfo[] {\n return this._mode === 'client' ? [...this.clientSources] : [];\n }\n\n /**\n * Tool-to-source mapping received from the server relay (client mode only).\n * Maps public tool names to arrays of source IDs.\n * Returns an empty record in server mode.\n */\n getToolSourceMapFromRelay(): Record<string, string[]> {\n return this._mode === 'client' ? { ...this.clientToolSourceMap } : {};\n }\n\n /**\n * Starts the bridge. Attempts to bind a WebSocket server (server mode).\n * If a compatible relay already owns a candidate port, joins it in client mode.\n * If a non-relay process owns the port, continues searching the reserved range.\n */\n async start(): Promise<void> {\n if (this.wss || this.clientSocket) {\n return;\n }\n\n this.stopping = false;\n await this.startUsingPortStrategy();\n }\n\n private async startUsingPortStrategy(): Promise<void> {\n if (this.preferredPort === 0) {\n await this.startAsServer(0);\n return;\n }\n\n const strategyOptions: PortStrategyOptions = {\n defaultPort: this.preferredPort,\n host: this.host,\n persistPath: this.persistPath,\n rangeEnd: this.portRangeEnd,\n };\n if (this.portExplicitlySet) strategyOptions.fixedPort = this.preferredPort;\n const candidates = await buildPortCandidates(strategyOptions);\n\n for (const candidate of candidates) {\n try {\n await this.startAsServer(candidate.port);\n await this.persistSelectedPort();\n return;\n } catch (err) {\n if (!this.isAddressInUseError(err)) {\n throw err;\n }\n\n const attached = await this.tryAttachToExistingRelay(candidate.port);\n if (attached) {\n await this.persistSelectedPort();\n return;\n }\n\n if (candidate.wasFixed) {\n throw new Error(\n `Port ${candidate.port} is already in use by a non-WebMCP service and cannot be shared.`\n );\n }\n\n process.stderr.write(\n `[webmcp-local-relay] info: port ${candidate.port} is occupied by a non-relay service, trying next port\\n`\n );\n }\n }\n\n throw new Error(\n `No compatible relay port was available in the range ${this.preferredPort}-${this.portRangeEnd}.`\n );\n }\n\n private isAddressInUseError(cause: unknown): boolean {\n return (\n cause instanceof Error &&\n ('code' in cause ? cause.code === 'EADDRINUSE' : cause.message.includes('EADDRINUSE'))\n );\n }\n\n private async persistSelectedPort(): Promise<void> {\n try {\n await persistPort(this.port, this.persistPath, this.host);\n } catch (error) {\n process.stderr.write(\n `[webmcp-local-relay] warn: could not cache relay port: ${error instanceof Error ? error.message : String(error)}\\n`\n );\n }\n }\n\n private async tryAttachToExistingRelay(port: number): Promise<boolean> {\n try {\n await this.startAsClient(port);\n process.stderr.write(\n `[webmcp-local-relay] info: discovered compatible relay on port ${port}, switching to client mode\\n`\n );\n return true;\n } catch (error) {\n // The caller reports every false here as \"occupied by a non-relay service\". A slow\n // but healthy relay hits the 750ms hello timeout and lands here too, so name the\n // real reason rather than leaving the operator with a wrong hypothesis.\n process.stderr.write(\n `[webmcp-local-relay] info: port ${port} did not complete a relay handshake: ${error instanceof Error ? error.message : String(error)}\\n`\n );\n return false;\n }\n }\n\n /**\n * Stops all relay resources and rejects any pending invocations.\n */\n async stop(): Promise<void> {\n this.stopping = true;\n\n if (this.clientReconnectTimer) {\n clearTimeout(this.clientReconnectTimer);\n this.clientReconnectTimer = null;\n }\n\n if (this._mode === 'client') {\n for (const pending of this.clientPendingInvocations.values()) {\n clearTimeout(pending.timeoutId);\n pending.reject(new Error('Relay client stopped'));\n }\n this.clientPendingInvocations.clear();\n this.clientTools = [];\n this.clientSources = [];\n this.clientToolSourceMap = {};\n\n if (this.clientSocket) {\n try {\n this.clientSocket.close(1000, 'Relay client shutting down');\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: error closing client socket during shutdown: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n this.clientSocket = null;\n }\n return;\n }\n\n this.off('stateChanged', this.onStateChangedPushRelay);\n\n for (const [connectionId, socket] of this.socketByConnectionId) {\n this.registry.removeConnection(connectionId);\n try {\n socket.close(1001, 'Relay shutting down');\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: error closing socket during shutdown: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }\n\n for (const intervalId of this.heartbeatIntervalByConnectionId.values()) {\n clearInterval(intervalId);\n }\n this.heartbeatIntervalByConnectionId.clear();\n this.lastPongByConnectionId.clear();\n\n this.socketByConnectionId.clear();\n this.requestOriginByConnectionId.clear();\n this.browserClientConnectionIds.clear();\n this.relayClientConnectionIds.clear();\n\n for (const pending of this.pendingInvocations.values()) {\n clearTimeout(pending.timeoutId);\n pending.reject(new Error('Relay server stopped before tool invocation completed'));\n }\n this.pendingInvocations.clear();\n\n const wss = this.wss;\n this.wss = null;\n\n if (!wss) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n wss.close((err?: Error) => {\n if (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: WebSocket server close error: ${err.message}\\n`\n );\n }\n resolve();\n });\n });\n }\n\n /**\n * Sends a reload message to a connected browser source.\n * Only supported in server mode.\n */\n reloadSource(connectionId: string): void {\n if (this._mode !== 'server') {\n throw new Error('reloadSource is only supported in server mode');\n }\n const socket = this.socketByConnectionId.get(connectionId);\n if (!socket || socket.readyState !== WebSocket.OPEN) {\n throw new Error(`Source ${connectionId} is not connected`);\n }\n const message: RelayToBrowserMessage = { type: 'reload' };\n try {\n socket.send(JSON.stringify(message));\n } catch (err) {\n throw new Error(`Failed to send reload: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n /**\n * Invokes a tool either locally (server mode) or through the upstream relay\n * (client mode).\n */\n async invokeTool(\n toolName: string,\n args: RelayInvokeArgs,\n options: {\n sourceId?: string;\n requestTabId?: string;\n } = {}\n ): Promise<RelayCallToolResult> {\n if (this._mode === 'client') {\n return this.invokeToolViaRelay(toolName, args);\n }\n\n return this.invokeToolLocally(toolName, args, options);\n }\n\n private async startAsServer(port = this.desiredPort): Promise<void> {\n const wss = await new Promise<WebSocketServer>((resolve, reject) => {\n const server = new WebSocketServer({\n handleProtocols: (protocols) => {\n for (const protocol of protocols) {\n if (SUPPORTED_SUBPROTOCOLS.has(protocol)) {\n return protocol;\n }\n }\n return false;\n },\n host: this.host,\n port,\n maxPayload: this.maxPayloadBytes,\n });\n\n const onListening = () => {\n server.off('error', onError);\n resolve(server);\n };\n const onError = (err: Error) => {\n server.off('listening', onListening);\n reject(err);\n };\n\n server.once('listening', onListening);\n server.once('error', onError);\n });\n\n wss.on('connection', (socket: WebSocket, request) => {\n const requestOrigin = request.headers.origin;\n if (requestOrigin && socket.protocol === RELAY_INTERNAL_PROTOCOL) {\n socket.close(1008, 'Browser connections cannot use the relay protocol');\n return;\n }\n\n const connectionId = randomUUID();\n this.socketByConnectionId.set(connectionId, socket);\n if (requestOrigin) {\n this.requestOriginByConnectionId.set(connectionId, requestOrigin);\n }\n\n socket.on('message', (raw: WebSocket.RawData) => {\n this.onSocketMessage(connectionId, raw);\n });\n\n socket.on('close', (code: number) => {\n this.onSocketClose(connectionId, code);\n });\n\n socket.on('error', (err: Error) => {\n process.stderr.write(\n `[webmcp-local-relay] warn: socket error for connection ${connectionId}: ${err.message}\\n`\n );\n const code =\n 'code' in err && err.code === 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH' ? 1009 : undefined;\n this.onSocketClose(connectionId, code);\n });\n\n try {\n socket.send(JSON.stringify(this.buildServerHello()));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send server hello to connection ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n socket.close(1011, 'Failed to send server hello');\n return;\n }\n\n this.startHeartbeat(connectionId);\n });\n\n wss.on('error', (err: Error) => {\n // Logged, not re-emitted: nothing listens for 'error' on this EventEmitter, and\n // emit('error') without a listener rethrows, killing the relay via uncaughtException.\n process.stderr.write(`[webmcp-local-relay] error: WebSocket server error: ${err.message}\\n`);\n });\n\n this.wss = wss;\n this._mode = 'server';\n\n const address = wss.address();\n if (isTcpAddress(address)) {\n this.desiredPort = address.port;\n }\n\n this.on('stateChanged', this.onStateChangedPushRelay);\n }\n\n private buildServerHello(): ServerHelloMessage {\n return {\n type: 'server-hello',\n service: 'webmcp-local-relay',\n version: 1,\n host: this.host,\n instanceId: this.instanceId,\n label: this.label,\n port: this.desiredPort,\n relayId: this.relayId ?? this.instanceId,\n workspace: this.workspace,\n };\n }\n\n /**\n * `async` is load-bearing: {@link onRelayClientMessage} consumes this through\n * `.then(onResult, onError)`, so a synchronous throw would escape the socket\n * message handler as an uncaught exception and leave the relay client waiting\n * for a `relay/result` that never arrives.\n */\n private async invokeToolLocally(\n toolName: string,\n args: RelayInvokeArgs,\n options: { sourceId?: string; requestTabId?: string }\n ): Promise<RelayCallToolResult> {\n const invocation: Parameters<RelayRegistry['resolveInvocation']>[0] = { toolName };\n if (options.sourceId !== undefined) invocation.sourceId = options.sourceId;\n if (options.requestTabId !== undefined) invocation.requestTabId = options.requestTabId;\n const resolved = this.registry.resolveInvocation(invocation);\n\n if (!resolved) {\n throw new Error(`No active browser source provides tool \"${toolName}\"`);\n }\n\n const socket = this.socketByConnectionId.get(resolved.connectionId);\n if (!socket || socket.readyState !== WebSocket.OPEN) {\n throw new Error(\n `Tool source ${resolved.connectionId} disconnected before invocation of \"${toolName}\"`\n );\n }\n\n const callId = randomUUID();\n\n return new Promise<RelayCallToolResult>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n this.pendingInvocations.delete(callId);\n reject(\n new Error(`Invocation for tool \"${toolName}\" timed out after ${this.invokeTimeoutMs}ms`)\n );\n }, this.invokeTimeoutMs);\n\n this.pendingInvocations.set(callId, {\n connectionId: resolved.connectionId,\n timeoutId,\n resolve,\n reject,\n });\n\n const message: RelayToBrowserMessage = {\n type: 'invoke',\n callId,\n toolName: resolved.tool.name,\n args,\n };\n\n try {\n socket.send(JSON.stringify(message));\n } catch (err) {\n clearTimeout(timeoutId);\n this.pendingInvocations.delete(callId);\n reject(\n new Error(\n `Failed to send invocation for tool \"${toolName}\": ${err instanceof Error ? err.message : err}`\n )\n );\n }\n });\n }\n\n /** Handles a raw message according to the negotiated WebSocket protocol. */\n private onSocketMessage(connectionId: string, raw: WebSocket.RawData): void {\n const text = this.rawDataToUtf8(raw);\n\n let parsedJson: unknown;\n try {\n parsedJson = JSON.parse(text);\n } catch (err) {\n const preview = text.length > 200 ? `${text.slice(0, 200)}...` : text;\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid JSON from connection ${connectionId} (${err instanceof Error ? err.message : 'parse error'}): ${preview}\\n`\n );\n return;\n }\n\n const typeField = RawMessageTypeSchema.safeParse(parsedJson).data?.type;\n const socket = this.socketByConnectionId.get(connectionId);\n if (!socket) {\n return;\n }\n\n if (socket.protocol === RELAY_INTERNAL_PROTOCOL) {\n const relayMsg = RelayClientToServerMessageSchema.safeParse(parsedJson);\n if (relayMsg.success) {\n this.onRelayClientMessage(connectionId, relayMsg.data);\n } else {\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid relay message from ${connectionId}: ${relayMsg.error.message}\\n`\n );\n }\n return;\n }\n\n if (RelayProtocolTypeSchema.safeParse(typeField).success) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} used the relay protocol without negotiating it\\n`\n );\n socket.close(1008, 'Relay protocol was not negotiated');\n return;\n }\n\n const parsedMessage = BrowserToRelayMessageSchema.safeParse(parsedJson);\n if (!parsedMessage.success) {\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid message from connection ${connectionId} (type=${typeField}): ${parsedMessage.error.message}\\n`\n );\n return;\n }\n\n this.registry.touchConnection(connectionId);\n this.onBrowserClientMessage(connectionId, socket, parsedMessage.data);\n }\n\n private onBrowserClientMessage(\n connectionId: string,\n socket: WebSocket,\n message: BrowserToRelayMessage\n ): void {\n if (message.type !== 'hello' && !this.browserClientConnectionIds.has(connectionId)) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} sent ${message.type} before hello, ignoring\\n`\n );\n return;\n }\n\n switch (message.type) {\n case 'hello':\n try {\n const origin = this.requestOriginByConnectionId.get(connectionId) ?? message.origin;\n if (!this.isHostOriginAllowed(origin)) {\n process.stderr.write(\n `[webmcp-local-relay] warn: rejecting source ${connectionId} with disallowed host origin: ${origin ?? 'missing'}\\n`\n );\n this.sendHelloRejected(\n socket,\n {\n type: 'hello/rejected',\n reason: 'host-origin-not-allowed',\n message: 'Host page origin is not allowed by this relay.',\n },\n 1008,\n 'Host origin not allowed'\n );\n break;\n }\n this.registry.upsertSource(connectionId, { ...message, origin });\n this.browserClientConnectionIds.add(connectionId);\n this.sendHelloAccepted(socket, { type: 'hello/accepted' });\n this.emit('stateChanged');\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] error: failed to process hello from connection ${connectionId}: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n );\n }\n break;\n\n case 'tools/list':\n case 'tools/changed':\n try {\n this.registry.registerTools(connectionId, message.tools);\n this.emit('stateChanged');\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] error: failed to register tools for connection ${connectionId}: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n );\n socket.close(1008, 'Invalid tool list');\n }\n break;\n\n case 'result': {\n const pending = this.pendingInvocations.get(message.callId);\n if (!pending) {\n process.stderr.write(\n `[webmcp-local-relay] warn: received result for unknown callId ${message.callId}\\n`\n );\n break;\n }\n if (pending.connectionId !== connectionId) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} returned result for another source's callId ${message.callId}\\n`\n );\n break;\n }\n\n clearTimeout(pending.timeoutId);\n this.pendingInvocations.delete(message.callId);\n pending.resolve(this.normalizeCallToolResult(message.result));\n break;\n }\n\n case 'pong':\n this.lastPongByConnectionId.set(connectionId, Date.now());\n break;\n }\n }\n\n /**\n * Handles relay-protocol messages from relay client connections.\n */\n private onRelayClientMessage(connectionId: string, message: RelayClientToServerMessage): void {\n if (message.type !== 'relay/hello' && !this.relayClientConnectionIds.has(connectionId)) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} sent ${message.type} before relay/hello, ignoring\\n`\n );\n return;\n }\n\n switch (message.type) {\n case 'relay/hello':\n this.relayClientConnectionIds.add(connectionId);\n break;\n\n case 'relay/list-tools': {\n const response = this.buildRelayToolsPayload('relay/tools');\n const socket = this.socketByConnectionId.get(connectionId);\n if (socket?.readyState === WebSocket.OPEN) {\n try {\n socket.send(JSON.stringify(response));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send relay tools response to ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }\n break;\n }\n\n case 'relay/invoke': {\n const { callId, toolName, args } = message;\n void this.invokeToolLocally(toolName, args ?? {}, {}).then(\n (result) => this.sendRelayResult(connectionId, callId, result),\n (error) =>\n this.sendRelayResult(connectionId, callId, {\n content: [\n {\n type: 'text',\n text: `Relay invocation failed: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n })\n );\n break;\n }\n }\n }\n\n private sendRelayResult(connectionId: string, callId: string, result: RelayCallToolResult): void {\n const socket = this.socketByConnectionId.get(connectionId);\n if (socket?.readyState !== WebSocket.OPEN) {\n process.stderr.write(\n `[webmcp-local-relay] warn: relay client ${connectionId} disconnected before result for callId ${callId} could be delivered\\n`\n );\n return;\n }\n\n try {\n socket.send(JSON.stringify({ type: 'relay/result', callId, result }));\n } catch (error) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send relay result to ${connectionId}: ${error instanceof Error ? error.message : String(error)}\\n`\n );\n }\n }\n\n /**\n * Pushes current tool state to all connected relay clients.\n */\n private pushToolsToRelayClients(): void {\n if (this.relayClientConnectionIds.size === 0) {\n return;\n }\n\n const message = this.buildRelayToolsPayload('relay/tools-changed');\n const payload = JSON.stringify(message);\n\n for (const connectionId of this.relayClientConnectionIds) {\n const socket = this.socketByConnectionId.get(connectionId);\n if (socket?.readyState === WebSocket.OPEN) {\n try {\n socket.send(payload);\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to push tool update to relay client ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }\n }\n }\n\n /**\n * Handles source disconnection and rejects in-flight calls owned by that source.\n *\n * Guarded against double-call: both 'error' and 'close' events fire on\n * socket failure, but cleanup (and the stateChanged emission) runs only once.\n */\n private onSocketClose(connectionId: string, code?: number): void {\n // Guard against double invocation: ws fires both 'error' and 'close' for\n // maxPayload violations. The second call is a no-op.\n if (!this.socketByConnectionId.has(connectionId)) return;\n\n this.stopHeartbeat(connectionId);\n this.requestOriginByConnectionId.delete(connectionId);\n this.browserClientConnectionIds.delete(connectionId);\n this.relayClientConnectionIds.delete(connectionId);\n this.registry.removeConnection(connectionId);\n this.socketByConnectionId.delete(connectionId);\n this.emit('stateChanged');\n\n // WS close code 1009 = \"Message Too Big\": the browser sent a response\n // that exceeded maxPayloadBytes. Surface a clear error instead of letting\n // the invocation time out silently after invokeTimeoutMs.\n const isPayloadExceeded = code === 1009;\n\n for (const [callId, pending] of this.pendingInvocations.entries()) {\n if (pending.connectionId !== connectionId) {\n continue;\n }\n\n clearTimeout(pending.timeoutId);\n this.pendingInvocations.delete(callId);\n\n if (isPayloadExceeded) {\n pending.reject(\n new Error(\n `Tool result exceeded maximum payload size (${formatPayloadLimit(this.maxPayloadBytes)}). Use --max-payload to increase the limit.`\n )\n );\n } else {\n pending.reject(new Error(`Tool source ${connectionId} disconnected during invocation`));\n }\n }\n }\n\n private startHeartbeat(connectionId: string): void {\n this.lastPongByConnectionId.set(connectionId, Date.now());\n\n const intervalId = setInterval(() => {\n const socket = this.socketByConnectionId.get(connectionId);\n if (!socket || socket.readyState !== WebSocket.OPEN) {\n this.stopHeartbeat(connectionId);\n return;\n }\n\n // Skip heartbeat for relay client connections; they have their own reconnect logic.\n if (this.relayClientConnectionIds.has(connectionId)) {\n return;\n }\n\n const lastPong = this.lastPongByConnectionId.get(connectionId) ?? 0;\n if (Date.now() - lastPong > HEARTBEAT_DEAD_THRESHOLD_MS) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} missed heartbeat, closing\\n`\n );\n this.stopHeartbeat(connectionId);\n socket.close(1001, 'Heartbeat timeout');\n return;\n }\n\n try {\n socket.send(JSON.stringify({ type: 'ping' }));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send heartbeat ping to ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }, HEARTBEAT_INTERVAL_MS);\n\n this.heartbeatIntervalByConnectionId.set(connectionId, intervalId);\n }\n\n private stopHeartbeat(connectionId: string): void {\n const intervalId = this.heartbeatIntervalByConnectionId.get(connectionId);\n if (intervalId !== undefined) {\n clearInterval(intervalId);\n this.heartbeatIntervalByConnectionId.delete(connectionId);\n }\n this.lastPongByConnectionId.delete(connectionId);\n }\n\n private async startAsClient(port = this.desiredPort): Promise<void> {\n this.stopping = false;\n\n const previousMode = this._mode;\n const previousPort = this.desiredPort;\n this._mode = 'client';\n this.desiredPort = port;\n\n try {\n const { bufferedMessages, socket } = await this.connectToRelayServer(port);\n this.clientSocket = socket;\n this.clientReconnectDelay = 500;\n this.clientReconnectAttempts = 0;\n this.setupClientHandlers(socket, bufferedMessages);\n } catch (error) {\n this._mode = previousMode;\n this.desiredPort = previousPort;\n throw error;\n }\n }\n\n private async connectToRelayServer(\n port: number\n ): Promise<{ bufferedMessages: RelayServerToClientMessage[]; socket: WebSocket }> {\n return new Promise((resolve, reject) => {\n const wsUrl = `ws://${this.host}:${port}`;\n const ws = new WebSocket(wsUrl, RELAY_INTERNAL_PROTOCOL);\n const bufferedMessages: RelayServerToClientMessage[] = [];\n\n const cleanup = () => {\n clearTimeout(timeoutId);\n ws.off('close', onCloseBeforeReady);\n ws.off('error', onErrorBeforeReady);\n ws.off('message', onMessageBeforeReady);\n ws.off('open', onOpen);\n };\n\n const rejectWith = (error: Error) => {\n cleanup();\n ws.once('error', () => {\n // Ignore late socket errors from ports that failed relay verification.\n });\n ws.terminate();\n reject(error);\n };\n\n const finish = () => {\n cleanup();\n resolve({ bufferedMessages, socket: ws });\n };\n\n const onOpen = () => {\n try {\n this.sendRelayClientHandshake(ws);\n } catch (err) {\n rejectWith(\n new Error(\n `Failed to send handshake to relay server at ${wsUrl}: ${err instanceof Error ? err.message : String(err)}`\n )\n );\n }\n };\n\n const onErrorBeforeReady = (err: Error) => {\n rejectWith(new Error(`Failed to connect to relay server at ${wsUrl}: ${err.message}`));\n };\n\n const onCloseBeforeReady = () => {\n rejectWith(new Error(`Connection to ${wsUrl} closed before relay verification completed`));\n };\n\n const onMessageBeforeReady = (raw: WebSocket.RawData) => {\n const message = this.parseRelayServerMessage(raw);\n if (!message) {\n rejectWith(new Error(`Received a non-relay response while probing ${wsUrl}`));\n return;\n }\n\n bufferedMessages.push(message);\n\n if (message.type === 'server-hello') {\n if (message.service !== 'webmcp-local-relay') {\n rejectWith(new Error(`Unexpected relay service \"${message.service}\" at ${wsUrl}`));\n return;\n }\n finish();\n return;\n }\n\n if (message.type === 'relay/tools' || message.type === 'relay/tools-changed') {\n finish();\n }\n };\n\n const timeoutId = setTimeout(() => {\n rejectWith(new Error(`Timed out waiting for relay hello from ${wsUrl}`));\n }, RELAY_SERVER_MESSAGE_TIMEOUT_MS);\n\n ws.once('open', onOpen);\n ws.once('error', onErrorBeforeReady);\n ws.once('close', onCloseBeforeReady);\n ws.on('message', onMessageBeforeReady);\n });\n }\n\n private setupClientHandlers(\n ws: WebSocket,\n bufferedMessages: RelayServerToClientMessage[] = []\n ): void {\n for (const message of bufferedMessages) {\n this.processRelayServerMessage(message);\n }\n\n ws.on('message', (raw: WebSocket.RawData) => {\n const message = this.parseRelayServerMessage(raw);\n if (!message) {\n return;\n }\n\n this.processRelayServerMessage(message);\n });\n\n ws.on('close', () => {\n this.clientSocket = null;\n for (const [callId, pending] of this.clientPendingInvocations) {\n clearTimeout(pending.timeoutId);\n this.clientPendingInvocations.delete(callId);\n pending.reject(new Error('Relay server connection lost during invocation'));\n }\n\n this.clientTools = [];\n this.clientSources = [];\n this.clientToolSourceMap = {};\n this.emit('stateChanged');\n\n if (!this.stopping) {\n this.scheduleReconnect();\n }\n });\n\n ws.on('error', (err: Error) => {\n process.stderr.write(\n `[webmcp-local-relay] warn: relay client socket error: ${err.message}\\n`\n );\n });\n }\n\n private sendRelayClientHandshake(ws: WebSocket): void {\n ws.send(JSON.stringify({ type: 'relay/hello' }));\n ws.send(JSON.stringify({ type: 'relay/list-tools' }));\n }\n\n private parseRelayServerMessage(raw: WebSocket.RawData): RelayServerToClientMessage | null {\n const text = this.rawDataToUtf8(raw);\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (err) {\n const preview = text.length > 200 ? `${text.slice(0, 200)}...` : text;\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid JSON from relay server (${err instanceof Error ? err.message : 'parse error'}): ${preview}\\n`\n );\n return null;\n }\n\n const message = RelayServerToClientMessageSchema.safeParse(parsed);\n if (!message.success) {\n const typeField = RawMessageTypeSchema.safeParse(parsed).data?.type ?? 'unknown';\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid relay server message (type=${typeField}): ${message.error.message}\\n`\n );\n return null;\n }\n\n return message.data;\n }\n\n private processRelayServerMessage(message: RelayServerToClientMessage): void {\n switch (message.type) {\n case 'server-hello':\n break;\n\n case 'relay/tools':\n case 'relay/tools-changed':\n this.clientTools = message.tools;\n this.clientSources = message.sources;\n this.clientToolSourceMap = message.toolSourceMap;\n this.emit('stateChanged');\n break;\n\n case 'relay/result': {\n const pending = this.clientPendingInvocations.get(message.callId);\n if (!pending) {\n process.stderr.write(\n `[webmcp-local-relay] warn: received relay result for unknown callId ${message.callId}\\n`\n );\n break;\n }\n clearTimeout(pending.timeoutId);\n this.clientPendingInvocations.delete(message.callId);\n pending.resolve(message.result);\n break;\n }\n }\n }\n\n private scheduleReconnect(): void {\n if (this.clientReconnectTimer || this.stopping) {\n return;\n }\n\n this.clientReconnectAttempts++;\n if (this.clientReconnectAttempts >= this.clientMaxReconnectAttempts) {\n process.stderr.write(\n `[webmcp-local-relay] error: giving up reconnection after ${this.clientReconnectAttempts} attempts\\n`\n );\n return;\n }\n\n const delay = this.clientReconnectDelay;\n this.clientReconnectDelay = Math.min(\n this.clientReconnectDelay * 1.5,\n this.clientMaxReconnectDelay\n );\n\n this.clientReconnectTimer = setTimeout(() => {\n this.clientReconnectTimer = null;\n if (this.stopping) {\n return;\n }\n\n void this.reconnectWithModePromotion();\n }, delay);\n }\n\n /**\n * Attempts to promote from client to server mode when reconnecting.\n * Re-runs the same attach-or-bind strategy used during startup.\n */\n private async reconnectWithModePromotion(): Promise<void> {\n try {\n await this.startUsingPortStrategy();\n this.clientReconnectDelay = 500;\n this.clientReconnectAttempts = 0;\n if (this._mode === 'server') {\n process.stderr.write('[webmcp-local-relay] info: promoted from client to server mode\\n');\n this.emit('stateChanged');\n }\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: reconnection failed: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n if (!this.stopping) {\n this.scheduleReconnect();\n }\n }\n }\n\n private async invokeToolViaRelay(\n toolName: string,\n args: RelayInvokeArgs\n ): Promise<RelayCallToolResult> {\n if (!this.clientSocket || this.clientSocket.readyState !== WebSocket.OPEN) {\n throw new Error('Not connected to relay server');\n }\n\n const callId = randomUUID();\n const socket = this.clientSocket;\n\n return new Promise<RelayCallToolResult>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n this.clientPendingInvocations.delete(callId);\n reject(\n new Error(\n `Proxied invocation for tool \"${toolName}\" timed out after ${this.invokeTimeoutMs}ms`\n )\n );\n }, this.invokeTimeoutMs);\n\n this.clientPendingInvocations.set(callId, {\n connectionId: 'relay-server',\n timeoutId,\n resolve,\n reject,\n });\n\n const message: RelayClientToServerMessage = {\n type: 'relay/invoke',\n callId,\n toolName,\n args,\n };\n\n try {\n socket.send(JSON.stringify(message));\n } catch (err) {\n clearTimeout(timeoutId);\n this.clientPendingInvocations.delete(callId);\n reject(\n new Error(`Failed to send relay invocation: ${err instanceof Error ? err.message : err}`)\n );\n }\n });\n }\n\n /**\n * Builds the complete relay response payload including tools and source metadata.\n */\n private buildRelayToolsPayload(\n type: 'relay/tools' | 'relay/tools-changed'\n ): RelayServerToClientMessage {\n const tools = this.registry.listTools();\n const sources = this.registry.listSources();\n const toolSourceMap: Record<string, string[]> = {};\n\n for (const tool of tools) {\n toolSourceMap[tool.name] = tool.sources.map((s) => s.sourceId);\n }\n\n return {\n type,\n tools: tools.map(({ originalName: _originalName, sources: _sources, ...tool }) => tool),\n sources,\n toolSourceMap,\n };\n }\n\n private isHostOriginAllowed(origin: string | undefined): boolean {\n if (this.allowedOrigins.includes('*')) {\n return true;\n }\n\n if (!origin) {\n return false;\n }\n\n return this.allowedOrigins.includes(origin);\n }\n\n private sendHelloAccepted(socket: WebSocket, message: RelayHelloAcceptedMessage): void {\n try {\n socket.send(JSON.stringify(message));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send hello acceptance: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n socket.close(1011, 'Failed to send hello acceptance');\n }\n }\n\n private sendHelloRejected(\n socket: WebSocket,\n message: RelayHelloRejectedMessage,\n closeCode: number,\n closeReason: string\n ): void {\n try {\n socket.send(JSON.stringify(message), () => {\n try {\n socket.close(closeCode, closeReason);\n } catch {\n // Ignore close failures after a rejection send attempt.\n }\n });\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send hello rejection: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n socket.close(closeCode, closeReason);\n }\n }\n\n /**\n * Validates browser tool results against CallToolResultSchema.\n * Non-conforming payloads are wrapped as error results with diagnostic text.\n */\n private normalizeCallToolResult(result: unknown): RelayCallToolResult {\n const parsed = CallToolResultSchema.safeParse(result);\n if (parsed.success) {\n return parsed.data;\n }\n\n const preview = JSON.stringify(result)?.slice(0, 500) ?? 'undefined';\n process.stderr.write(\n `[webmcp-local-relay] warn: tool returned invalid CallToolResult (${parsed.error.message}), wrapping as error: ${preview}\\n`\n );\n\n return {\n content: [\n {\n type: 'text',\n text: `Tool returned an invalid result (expected {content: [...]}): ${preview}`,\n },\n ],\n isError: true,\n };\n }\n\n /**\n * Converts WebSocket raw data variants to a UTF-8 string payload.\n */\n private rawDataToUtf8(raw: WebSocket.RawData): string {\n if (Array.isArray(raw)) return Buffer.concat(raw).toString('utf8');\n if (Buffer.isBuffer(raw)) return raw.toString('utf8');\n return Buffer.from(raw).toString('utf8');\n }\n}\n","import { parseArgs } from 'node:util';\n\nimport { DEFAULT_RELAY_PORT } from './portStrategy.js';\n\n/**\n * Parsed CLI options for relay startup.\n */\nexport interface CliOptions {\n help: boolean;\n host: string;\n port: number;\n portExplicitlySet: boolean;\n allowedOrigins: string[];\n label?: string;\n workspace?: string;\n relayId?: string;\n maxPayloadBytes?: number;\n invokeTimeoutMs?: number;\n}\n\nfunction parsePositiveInteger(name: string, value: string | undefined): number | undefined {\n if (value === undefined) return undefined;\n const parsed = Number(value);\n if (!Number.isInteger(parsed) || parsed <= 0) {\n throw new Error(`Invalid ${name} \"${value}\". Must be a positive integer.`);\n }\n return parsed;\n}\n\n/**\n * Parses supported CLI flags for relay startup.\n */\nexport function parseCliOptions(argv: string[]): CliOptions {\n const { values } = parseArgs({\n args: argv,\n options: {\n help: { type: 'boolean', short: 'h' },\n host: { type: 'string', short: 'H' },\n 'allowed-origin': { type: 'string' },\n 'invoke-timeout': { type: 'string' },\n label: { type: 'string' },\n 'max-payload': { type: 'string' },\n port: { type: 'string', short: 'p' },\n 'relay-id': { type: 'string' },\n 'widget-origin': { type: 'string' },\n workspace: { type: 'string' },\n 'ws-origin': { type: 'string' },\n },\n strict: true,\n });\n\n const port = values.port === undefined ? DEFAULT_RELAY_PORT : Number(values.port);\n if (!Number.isInteger(port) || port <= 0 || port > 65535) {\n throw new Error(`Invalid port \"${values.port}\". Port must be an integer between 1 and 65535.`);\n }\n\n const invokeTimeoutMs = parsePositiveInteger('invoke-timeout', values['invoke-timeout']);\n const maxPayloadBytes = parsePositiveInteger('max-payload', values['max-payload']);\n\n const allowedOrigins = (\n values['widget-origin'] ??\n values['allowed-origin'] ??\n values['ws-origin']\n )\n ?.split(',')\n .map((origin) => origin.trim())\n .filter(Boolean) ?? ['*'];\n if (allowedOrigins.length === 0) {\n throw new Error('--widget-origin must include at least one origin.');\n }\n\n const options: CliOptions = {\n allowedOrigins,\n help: values.help ?? false,\n host: values.host ?? '127.0.0.1',\n port,\n portExplicitlySet: values.port !== undefined,\n };\n if (values.label !== undefined) options.label = values.label;\n if (invokeTimeoutMs !== undefined) options.invokeTimeoutMs = invokeTimeoutMs;\n if (maxPayloadBytes !== undefined) options.maxPayloadBytes = maxPayloadBytes;\n if (values['relay-id'] !== undefined) options.relayId = values['relay-id'];\n if (values.workspace !== undefined) options.workspace = values.workspace;\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 Preferred root port for the local relay cluster (default: 9333)',\n ' --widget-origin Allowed host page origin(s), comma-separated (default: *)',\n ' --allowed-origin Deprecated alias for --widget-origin',\n ' --ws-origin Deprecated alias for --widget-origin',\n ' --label Human-readable relay label reported during discovery',\n ' --workspace Optional workspace name reported during discovery',\n ' --relay-id Stable relay identifier reported during discovery',\n ' --invoke-timeout Browser tool invocation timeout in milliseconds (default: 65000)',\n ' --max-payload Maximum WebSocket payload size in bytes (default: 10000000)',\n ' --help, -h Show help',\n '',\n ].join('\\n')\n );\n}\n","import { execFile } from 'node:child_process';\n\nimport {\n type CallToolResult,\n fromJsonSchema,\n McpServer,\n type RegisteredTool,\n type Transport,\n} from '@modelcontextprotocol/server';\nimport { StdioServerTransport } from '@modelcontextprotocol/server/stdio';\nimport { z } from 'zod/v4';\n\nimport { RelayBridgeServer, type RelayBridgeServerOptions } from './bridgeServer.js';\nimport type { AggregatedTool, SourceInfo } from './registry.js';\nimport type { RelayInvokeArgs } from './protocol.js';\n\nconst JsonSchemaObjectSchema = z.record(z.string(), z.unknown());\n\nfunction textResult(text: string): CallToolResult {\n return { content: [{ type: 'text', text }] };\n}\n\nfunction errorResult(text: string): CallToolResult {\n return { content: [{ type: 'text', text }], isError: true };\n}\n\ninterface RelaySourcesResult {\n mode?: 'client';\n count: number;\n sources: SourceInfo[];\n}\n\n/**\n * Base options shared by all {@link LocalRelayMcpServer} configurations.\n */\ntype BrowserLauncher = (\n command: string,\n args: readonly string[],\n callback: (cause: Error | null) => void\n) => void;\n\ninterface LocalRelayMcpServerBaseOptions {\n /** Platform launcher; override to run without opening a browser. */\n launchBrowser?: BrowserLauncher;\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 launchBrowser: BrowserLauncher;\n private readonly dynamicTools = new Map<string, { handle: RegisteredTool; signature: string }>();\n\n private syncDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n private connected = false;\n\n /**\n * Creates a local relay MCP server with static and dynamic tool registration.\n */\n constructor(options: LocalRelayMcpServerOptions = {}) {\n this.bridge = options.bridge ?? new RelayBridgeServer(options.bridgeOptions);\n this.launchBrowser = options.launchBrowser ?? execFile;\n\n this.mcpServer = new McpServer({\n name: options.serverName ?? 'webmcp-local-relay',\n version: options.serverVersion ?? '0.0.0',\n });\n\n this.bridge.on('stateChanged', () => {\n this.debouncedSyncDynamicTools();\n });\n\n this.registerStaticTools();\n }\n\n /**\n * Starts the browser bridge and synchronizes dynamic MCP tools.\n */\n async start(): Promise<void> {\n await this.bridge.start();\n this.syncDynamicTools();\n }\n\n /**\n * Connects the MCP server to a transport.\n *\n * This may be called exactly once per instance lifecycle.\n */\n async connect(transport: Transport): Promise<void> {\n if (this.connected) {\n throw new Error('MCP server transport already connected');\n }\n\n await this.mcpServer.connect(transport);\n this.connected = true;\n\n this.mcpServer.server.onerror = (error) => {\n process.stderr.write(\n `[webmcp-local-relay] error: MCP protocol error: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}\\n`\n );\n };\n }\n\n /**\n * Convenience helper that connects the server over stdio transport.\n */\n async startStdio(): Promise<void> {\n await this.connect(new StdioServerTransport());\n }\n\n /**\n * Stops MCP transport and bridge resources.\n */\n async stop(): Promise<void> {\n this.connected = false;\n\n if (this.syncDebounceTimer) {\n clearTimeout(this.syncDebounceTimer);\n this.syncDebounceTimer = null;\n }\n\n let mcpCloseError: unknown;\n try {\n await this.mcpServer.close();\n } catch (err) {\n mcpCloseError = err;\n process.stderr.write(\n `[webmcp-local-relay] error: failed to close MCP server: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n );\n }\n await this.bridge.stop();\n if (mcpCloseError) {\n process.stderr.write(\n '[webmcp-local-relay] warn: shutdown completed with errors (MCP server close failed)\\n'\n );\n }\n }\n\n /**\n * Returns dynamic tool names currently registered in MCP.\n */\n listDynamicToolNames(): string[] {\n return Array.from(this.dynamicTools.keys()).sort();\n }\n\n /**\n * Registers built-in management tools exposed by the relay.\n */\n private registerStaticTools(): void {\n this.mcpServer.registerTool(\n 'webmcp_list_sources',\n {\n description: 'List connected browser tool sources and their metadata.',\n inputSchema: z.object({}),\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n },\n },\n async () => {\n const clientMode = this.bridge.mode === 'client';\n const sources = clientMode\n ? this.bridge.listSourcesFromRelay()\n : this.bridge.registry.listSources();\n const info: RelaySourcesResult = {\n count: sources.length,\n sources,\n };\n if (clientMode) info.mode = 'client';\n return {\n content: [{ type: 'text', text: JSON.stringify(info, null, 2) }],\n structuredContent: info,\n };\n }\n );\n\n this.mcpServer.registerTool(\n 'webmcp_list_tools',\n {\n description:\n 'List WebMCP tools available from connected browser sources. Returns tool definitions including name, description, input schema, and source info.',\n inputSchema: z.object({}),\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n },\n },\n async () => {\n const tools = this.listAggregatedTools();\n const info = { count: tools.length, tools };\n return {\n content: [{ type: 'text', text: JSON.stringify(info, null, 2) }],\n structuredContent: info,\n };\n }\n );\n\n this.mcpServer.registerTool(\n 'webmcp_open_page',\n {\n description:\n \"Open a URL in the user's default browser, or refresh a connected source page. Use to launch WebMCP-enabled pages or reload stale connections.\",\n inputSchema: z.object({\n url: z.string().describe('URL to open or match for refresh.'),\n refresh: z\n .boolean()\n .optional()\n .describe(\n 'If true, refresh the connected source matching this URL instead of opening a new tab.'\n ),\n }),\n annotations: { readOnlyHint: false },\n },\n async ({ url, refresh }) => {\n const parsed = URL.parse(url);\n if (!parsed) {\n return errorResult(`Invalid URL: ${url}`);\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return errorResult(`Only http: and https: URLs are allowed. Got: ${parsed.protocol}`);\n }\n\n const existing =\n this.bridge.mode === 'server'\n ? this.bridge.registry\n .listSources()\n .find((source) => source.url && URL.parse(source.url)?.origin === parsed.origin)\n : undefined;\n\n if (refresh) {\n if (this.bridge.mode === 'client') {\n return errorResult(\n 'Refresh is not supported in client mode. Only the server relay can reload sources.'\n );\n }\n if (!existing) {\n return errorResult(\n `No connected source matches origin ${parsed.origin}. The page may not be open or connected.`\n );\n }\n try {\n this.bridge.reloadSource(existing.sourceId);\n } catch (err) {\n return errorResult(\n `Failed to reload source: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n return textResult(\n `Reload sent to source ${existing.sourceId} (${existing.url ?? existing.origin}).`\n );\n }\n\n try {\n await this.openInBrowser(url);\n } catch (err) {\n return errorResult(\n `Failed to open browser: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n\n return textResult(\n existing\n ? `Opened ${url} in the default browser. Note: a source from ${existing.url ?? existing.origin} is already connected.`\n : `Opened ${url} in the default browser.`\n );\n }\n );\n }\n\n /**\n * Opens a URL in the user's default browser using the platform open command.\n *\n * Passes the re-serialized `URL.href` directly to the platform launcher.\n * No shell or command-language string is involved.\n */\n private openInBrowser(url: string): Promise<void> {\n const safeUrl = new URL(url).href;\n const command =\n process.platform === 'darwin'\n ? 'open'\n : process.platform === 'win32'\n ? 'explorer.exe'\n : 'xdg-open';\n return new Promise((resolve, reject) => {\n this.launchBrowser(command, [safeUrl], (err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n /**\n * Debounces rapid stateChanged events into a single sync pass.\n *\n * Browser reconnections emit multiple stateChanged events in quick\n * succession (hello, tools/list). Without debouncing, each triggers a\n * full sync that removes and re-registers tools — sending per-operation\n * notifications through the stdio transport and overwhelming the agent.\n */\n private debouncedSyncDynamicTools(): void {\n if (this.syncDebounceTimer) {\n return;\n }\n\n this.syncDebounceTimer = setTimeout(() => {\n this.syncDebounceTimer = null;\n try {\n this.syncDynamicTools();\n } catch (err) {\n const details = err instanceof Error ? (err.stack ?? err.message) : String(err);\n process.stderr.write(\n `[webmcp-local-relay] error: failed to sync dynamic tools: ${details}\\n`\n );\n }\n }, 16);\n }\n\n /**\n * Returns the current aggregated tool list, dispatching based on bridge mode.\n */\n private listAggregatedTools(): AggregatedTool[] {\n return this.bridge.mode === 'client'\n ? this.buildAggregatedToolsFromRelay()\n : this.bridge.registry.listTools();\n }\n\n /**\n * Converts relay client tool descriptors to aggregated tool shape.\n * Populates per-tool source metadata from the relay's tool-source mapping.\n */\n private buildAggregatedToolsFromRelay(): AggregatedTool[] {\n const toolSourceMap = this.bridge.getToolSourceMapFromRelay();\n const allSources = this.bridge.listSourcesFromRelay();\n const sourceById = new Map(allSources.map((s) => [s.sourceId, s]));\n\n return this.bridge\n .listToolsFromRelay()\n .map((tool) => {\n const sourceIds = toolSourceMap[tool.name] ?? [];\n const sources: SourceInfo[] = [];\n for (const id of sourceIds) {\n const source = sourceById.get(id);\n if (source) {\n sources.push(source);\n } else {\n process.stderr.write(\n `[webmcp-local-relay] warn: tool \"${tool.name}\" references unknown sourceId \"${id}\"\\n`\n );\n }\n }\n\n return {\n ...tool,\n originalName: tool.name,\n sources,\n };\n })\n .sort((left, right) => left.name.localeCompare(right.name));\n }\n\n /**\n * Applies current aggregated tool state to MCP dynamic registrations.\n */\n private syncDynamicTools(): void {\n const tools = this.listAggregatedTools();\n const nextNames = new Set(tools.map((tool) => tool.name));\n\n for (const [name, registration] of this.dynamicTools) {\n if (nextNames.has(name)) {\n continue;\n }\n\n registration.handle.remove();\n this.dynamicTools.delete(name);\n }\n\n for (const tool of tools) {\n const signature = this.toolSignature(tool);\n const current = this.dynamicTools.get(tool.name);\n\n if (current?.signature === signature) {\n continue;\n }\n\n if (current) {\n current.handle.remove();\n this.dynamicTools.delete(tool.name);\n }\n\n try {\n const handle = this.registerDynamicTool(tool);\n this.dynamicTools.set(tool.name, { handle, signature });\n } catch (err) {\n const details = err instanceof Error ? (err.stack ?? err.message) : String(err);\n process.stderr.write(\n `[webmcp-local-relay] warn: skipped dynamic tool \"${tool.name}\" because its schema could not be compiled: ${details}\\n`\n );\n }\n }\n }\n\n /**\n * Registers a single dynamic tool and returns a removal handle.\n */\n private registerDynamicTool(tool: AggregatedTool): RegisteredTool {\n // Validate the container before handing JSON Schema validation to the SDK compiler.\n const inputSchema = fromJsonSchema<RelayInvokeArgs>(\n JsonSchemaObjectSchema.parse(tool.inputSchema)\n );\n const outputSchema =\n tool.outputSchema !== undefined\n ? fromJsonSchema(JsonSchemaObjectSchema.parse(tool.outputSchema))\n : undefined;\n\n const registration: {\n [K in 'title' | 'annotations' | 'icons' | '_meta']?: Exclude<AggregatedTool[K], undefined>;\n } & {\n description: string;\n inputSchema: typeof inputSchema;\n outputSchema?: NonNullable<typeof outputSchema>;\n } = { description: this.dynamicToolDescription(tool), inputSchema };\n if (tool.title !== undefined) registration.title = tool.title;\n if (outputSchema) registration.outputSchema = outputSchema;\n if (tool.annotations) registration.annotations = tool.annotations;\n if (tool.icons) registration.icons = tool.icons;\n if (tool._meta) registration._meta = tool._meta;\n return this.mcpServer.registerTool(tool.name, registration, async (args: RelayInvokeArgs) => {\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 errorResult(`Failed to invoke relayed tool \"${tool.name}\": ${message}`);\n }\n });\n }\n\n /**\n * Builds a display description for relayed tools including source context.\n * In client mode, source metadata is unavailable, so tools are labeled `[WebMCP relay]`.\n */\n private dynamicToolDescription(tool: AggregatedTool): string {\n const source = tool.sources[0];\n const sourceLabel = source\n ? `[WebMCP ${source.tabId}${source.title ? ` • ${source.title}` : ''}]`\n : '[WebMCP relay]';\n\n return `${sourceLabel} ${tool.description ?? `Relayed tool ${tool.originalName}`}`;\n }\n\n /**\n * Produces a stable signature for change detection of dynamic tool metadata.\n *\n * Deliberately excludes `sourceId` (which is a per-connection UUID that\n * changes on every browser reconnect) so that a reconnecting tab with the\n * same tools does not trigger a needless remove+re-register cycle.\n */\n private toolSignature(tool: AggregatedTool): string {\n return JSON.stringify({\n originalName: tool.originalName,\n title: tool.title,\n description: tool.description,\n inputSchema: tool.inputSchema,\n outputSchema: tool.outputSchema,\n annotations: tool.annotations,\n _meta: tool._meta,\n icons: tool.icons,\n source: tool.sources[0]\n ? { tabId: tool.sources[0].tabId, title: tool.sources[0].title }\n : undefined,\n });\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-b/webmcp-local-relay",
3
- "version": "5.1.0",
3
+ "version": "6.0.0",
4
4
  "description": "Local MCP relay for browser WebMCP tools via embed iframe and localhost WebSocket",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -59,8 +59,7 @@
59
59
  "typescript": "^5.8.3",
60
60
  "vite-plus": "0.1.24",
61
61
  "vitest": "npm:@voidzero-dev/vite-plus-test@0.1.24",
62
- "@mcp-b/webmcp-polyfill": "5.1.0",
63
- "@mcp-b/webmcp-types": "5.1.0"
62
+ "@mcp-b/webmcp-ts-sdk": "6.0.0"
64
63
  },
65
64
  "engines": {
66
65
  "node": ">=22"