{"version":3,"file":"mcpRelayServer-BL8kNwIK.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/staticToolSchemas.ts","../src/mcpRelayServer.ts"],"sourcesContent":["import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\nexport const DEFAULT_RELAY_PORT = 9333;\nexport const DEFAULT_RELAY_PORT_RANGE_END = 9348;\nexport const RELAY_PORT_CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000;\n\ninterface PersistedRelayPort {\n  host: string;\n  port: number;\n  updatedAt: string;\n}\n\nexport interface PortStrategyOptions {\n  fixedPort?: number;\n  defaultPort: number;\n  rangeEnd: number;\n  host: string;\n  persistPath?: string;\n}\n\nexport interface PortStrategyResult {\n  port: number;\n  wasFixed: boolean;\n  fromCache: boolean;\n}\n\nexport function defaultRelayPortPersistPath(): string {\n  return join(homedir(), '.webmcp', 'relay-port.json');\n}\n\nexport async function persistPort(\n  port: number,\n  path = defaultRelayPortPersistPath(),\n  host = '127.0.0.1',\n  now = Date.now()\n): Promise<void> {\n  const payload: PersistedRelayPort = {\n    port,\n    host,\n    updatedAt: new Date(now).toISOString(),\n  };\n\n  await mkdir(dirname(path), { recursive: true });\n  await writeFile(path, `${JSON.stringify(payload, null, 2)}\\n`, 'utf8');\n}\n\nexport async function readPersistedPort(\n  path = defaultRelayPortPersistPath(),\n  options: {\n    expectedHost?: string;\n    maxAgeMs?: number;\n    now?: number;\n  } = {}\n): Promise<number | null> {\n  try {\n    const raw = await readFile(path, 'utf8');\n    const parsed = JSON.parse(raw) as Partial<PersistedRelayPort>;\n    if (\n      typeof parsed.port !== 'number' ||\n      !Number.isInteger(parsed.port) ||\n      parsed.port < 1 ||\n      parsed.port > 65535\n    ) {\n      return null;\n    }\n    if (typeof parsed.host !== 'string' || parsed.host.length === 0) {\n      return null;\n    }\n    if (options.expectedHost && parsed.host !== options.expectedHost) {\n      return null;\n    }\n\n    const maxAgeMs = options.maxAgeMs ?? RELAY_PORT_CACHE_MAX_AGE_MS;\n    const now = options.now ?? Date.now();\n    const updatedAtMs =\n      typeof parsed.updatedAt === 'string' ? Date.parse(parsed.updatedAt) : Number.NaN;\n\n    if (!Number.isFinite(updatedAtMs) || now - updatedAtMs > maxAgeMs) {\n      return null;\n    }\n\n    return parsed.port;\n  } catch {\n    return null;\n  }\n}\n\nexport async function buildPortCandidates(\n  options: PortStrategyOptions\n): Promise<PortStrategyResult[]> {\n  const {\n    defaultPort,\n    fixedPort,\n    host,\n    persistPath = defaultRelayPortPersistPath(),\n    rangeEnd,\n  } = options;\n\n  if (fixedPort !== undefined) {\n    return [{ port: fixedPort, wasFixed: true, fromCache: false }];\n  }\n\n  const cachedPort = await readPersistedPort(persistPath, { expectedHost: host });\n  const seen = new Set<number>();\n  const candidates: PortStrategyResult[] = [];\n\n  const pushCandidate = (port: number, fromCache: boolean): void => {\n    if (port < 1 || port > 65535 || seen.has(port)) {\n      return;\n    }\n    seen.add(port);\n    candidates.push({ port, wasFixed: false, fromCache });\n  };\n\n  if (cachedPort !== null && cachedPort >= defaultPort && cachedPort <= rangeEnd) {\n    pushCandidate(cachedPort, true);\n  }\n\n  pushCandidate(defaultPort, false);\n\n  for (let port = defaultPort + 1; port <= rangeEnd; port += 1) {\n    pushCandidate(port, false);\n  }\n\n  return candidates;\n}\n","import {\n  CallToolRequestParamsSchema,\n  type CallToolResult,\n  CallToolResultSchema,\n  type Tool,\n  type ToolAnnotations,\n  ToolAnnotationsSchema,\n  ToolSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod/v4';\n\n/**\n * SDK-derived canonical tool schema used internally by the relay.\n */\nexport const NormalizedToolSchema = ToolSchema.extend({\n  name: ToolSchema.shape.name.min(1),\n});\n\n/**\n * Permissive inbound tool shape from browser/widget payloads.\n *\n * Only enforces a non-empty name at ingest. All other fields are normalized\n * against SDK schemas by {@link normalizeInboundTool}.\n */\nexport const InboundToolSchema = z.object({ name: z.string().min(1) }).passthrough();\n\n/**\n * SDK-derived argument schema for tool invocation payloads.\n */\nexport const RelayInvokeArgsSchema = CallToolRequestParamsSchema.shape.arguments;\n\n/**\n * Default input schema applied when inbound payload omits or provides\n * a non-object input schema.\n */\nexport const DEFAULT_TOOL_INPUT_SCHEMA: Tool['inputSchema'] = {\n  type: 'object',\n  properties: {},\n};\n\n/**\n * Canonical normalized relay tool shape.\n */\nexport type RelayTool = z.infer<typeof NormalizedToolSchema>;\n\n/**\n * Canonical relay tool annotations shape.\n */\nexport type RelayToolAnnotations = ToolAnnotations;\n\n/**\n * Canonical relay call result shape.\n */\nexport type RelayCallToolResult = CallToolResult;\n\n/**\n * Invocation argument object shape derived from MCP SDK request params.\n */\nexport type RelayInvokeArgs = Exclude<z.infer<typeof RelayInvokeArgsSchema>, undefined>;\n\n/**\n * Normalizes permissive inbound tool payloads into SDK-compliant Tool objects.\n *\n * Invalid optional metadata (description, output schema, annotations, etc.)\n * is dropped. Invalid/missing inputSchema falls back to an empty object schema.\n */\nexport function normalizeInboundTool(inbound: z.infer<typeof InboundToolSchema>): RelayTool {\n  const inputSchemaParsed = ToolSchema.shape.inputSchema.safeParse(inbound.inputSchema);\n  const outputSchemaParsed = ToolSchema.shape.outputSchema.safeParse(inbound.outputSchema);\n  const annotationsParsed = ToolAnnotationsSchema.safeParse(inbound.annotations);\n\n  const normalizedCandidate: Record<string, unknown> = {\n    name: inbound.name,\n    inputSchema: inputSchemaParsed.success ? inputSchemaParsed.data : DEFAULT_TOOL_INPUT_SCHEMA,\n  };\n\n  if (typeof inbound.description === 'string') {\n    normalizedCandidate.description = inbound.description;\n  }\n  if (typeof inbound.title === 'string') {\n    normalizedCandidate.title = inbound.title;\n  }\n  if (outputSchemaParsed.success && outputSchemaParsed.data !== undefined) {\n    normalizedCandidate.outputSchema = outputSchemaParsed.data;\n  }\n  if (annotationsParsed.success && annotationsParsed.data !== undefined) {\n    normalizedCandidate.annotations = annotationsParsed.data;\n  }\n\n  const normalizedParsed = NormalizedToolSchema.safeParse(normalizedCandidate);\n  if (normalizedParsed.success) {\n    return normalizedParsed.data;\n  }\n\n  return {\n    name: inbound.name,\n    inputSchema: DEFAULT_TOOL_INPUT_SCHEMA,\n  };\n}\n\nexport { CallToolRequestParamsSchema, CallToolResultSchema, ToolAnnotationsSchema, ToolSchema };\n","/**\n * Maximum tool name length supported across MCP clients.\n */\nconst MAX_MCP_TOOL_NAME_LENGTH = 128;\n\n/**\n * Number of tab-id characters appended when disambiguation is required.\n */\nconst TAB_ID_DISAMBIGUATION_LENGTH = 4;\n\n/**\n * Converts arbitrary text into MCP-safe identifier characters.\n */\nexport function sanitizeName(value: string): string {\n  return value.replace(/[^a-zA-Z0-9_]/g, '_');\n}\n\n/**\n * Extracts and sanitizes a domain label from an origin or URL.\n */\nexport function extractSanitizedDomain(originOrUrl?: string): string {\n  if (!originOrUrl) {\n    return 'unknown';\n  }\n\n  try {\n    const parsed = new URL(originOrUrl);\n    if (!parsed.hostname) {\n      return 'unknown';\n    }\n\n    const isLocalhost =\n      parsed.hostname === 'localhost' ||\n      parsed.hostname === '127.0.0.1' ||\n      parsed.hostname === '[::1]';\n\n    const domain = isLocalhost ? `localhost_${parsed.port || '80'}` : parsed.hostname;\n    return sanitizeName(domain);\n  } catch {\n    return 'unknown';\n  }\n}\n\n/**\n * Builds a public tool name for MCP registration.\n *\n * Names can include a short tab-id suffix when multiple tabs publish the same\n * original tool name.\n */\nexport function buildPublicToolName(options: {\n  originalToolName: string;\n  tabId?: string;\n  disambiguate?: boolean;\n}): string {\n  const safeName = sanitizeName(options.originalToolName);\n\n  if (!options.disambiguate || !options.tabId) {\n    return safeName.slice(0, MAX_MCP_TOOL_NAME_LENGTH);\n  }\n\n  const shortTab = sanitizeName(options.tabId).slice(0, TAB_ID_DISAMBIGUATION_LENGTH);\n  const suffix = `_${shortTab}`;\n  const base = `${safeName}${suffix}`;\n\n  if (base.length <= MAX_MCP_TOOL_NAME_LENGTH) {\n    return base;\n  }\n\n  const available = MAX_MCP_TOOL_NAME_LENGTH - suffix.length;\n  return `${safeName.slice(0, Math.max(1, available))}${suffix}`;\n}\n","import { buildPublicToolName } from './naming.js';\nimport type { RelayTool } from './protocol.js';\nimport type { BrowserHelloMessage } from './schemas.js';\n\n/**\n * Internal metadata tracked for each active browser source.\n */\ninterface SourceMetadata {\n  sourceId: string;\n  tabId: string;\n  origin: string | undefined;\n  url: string | undefined;\n  title: string | undefined;\n  iconUrl: string | undefined;\n  connectedAt: number;\n  lastSeenAt: number;\n}\n\n/**\n * Public source metadata exposed by registry APIs.\n */\nexport interface SourceInfo extends SourceMetadata {\n  toolCount: number;\n}\n\n/**\n * Aggregated relayed tool metadata across one or more sources.\n *\n * Extends {@link RelayTool} with relay-specific fields so new SDK tool\n * properties are automatically inherited without manual duplication.\n */\nexport interface AggregatedTool extends RelayTool {\n  originalName: string;\n  sources: SourceInfo[];\n}\n\n/**\n * Internal record linking a source to a concrete tool implementation.\n */\ninterface ToolProvider {\n  sourceId: string;\n  tabId: string;\n  tool: RelayTool;\n  publicToolName: string;\n}\n\n/**\n * Invocation target selected by the registry.\n */\nexport interface ResolvedInvocation {\n  connectionId: string;\n  tool: RelayTool;\n  publicToolName: string;\n}\n\n/**\n * Error thrown when a source attempts to register tools before `hello`.\n */\nexport class HelloRequiredError extends Error {\n  readonly connectionId: string;\n\n  constructor(connectionId: string) {\n    super(`Connection ${connectionId} must send hello before tools`);\n    this.name = 'HelloRequiredError';\n    this.connectionId = connectionId;\n  }\n}\n\n/**\n * In-memory source and tool registry used by the relay bridge.\n */\nexport class RelayRegistry {\n  private readonly sourceByConnectionId = new Map<string, SourceMetadata>();\n  private readonly toolsByConnectionId = new Map<string, ToolProvider[]>();\n  private readonly providersByPublicToolName = new Map<string, ToolProvider[]>();\n\n  /**\n   * @param now Time provider used for recency ordering.\n   */\n  constructor(private readonly now: () => number = () => Date.now()) {}\n\n  /**\n   * Upserts source metadata from a browser `hello` message.\n   *\n   * `sourceId` currently matches `connectionId`, but remains conceptually distinct\n   * so stable source identity can be introduced in the future.\n   */\n  upsertSource(connectionId: string, hello: BrowserHelloMessage): void {\n    const now = this.now();\n    const existing = this.sourceByConnectionId.get(connectionId);\n\n    this.sourceByConnectionId.set(connectionId, {\n      sourceId: connectionId,\n      tabId: hello.tabId,\n      origin: hello.origin ?? existing?.origin,\n      url: hello.url ?? existing?.url,\n      title: hello.title ?? existing?.title,\n      iconUrl: hello.iconUrl ?? existing?.iconUrl,\n      connectedAt: existing?.connectedAt ?? now,\n      lastSeenAt: now,\n    });\n  }\n\n  /**\n   * Replaces the full tool set for a source connection.\n   */\n  registerTools(connectionId: string, tools: RelayTool[]): void {\n    const source = this.sourceByConnectionId.get(connectionId);\n    if (!source) {\n      throw new HelloRequiredError(connectionId);\n    }\n\n    this.touchConnection(connectionId);\n    this.removeConnectionTools(connectionId);\n\n    const providers: ToolProvider[] = tools.map((tool) => ({\n      sourceId: connectionId,\n      tabId: source.tabId,\n      tool,\n      publicToolName: '',\n    }));\n\n    this.toolsByConnectionId.set(connectionId, providers);\n    this.rebuildPublicNames();\n  }\n\n  /**\n   * Removes a source and all of its tool registrations.\n   */\n  removeConnection(connectionId: string): void {\n    this.removeConnectionTools(connectionId);\n    this.sourceByConnectionId.delete(connectionId);\n    this.rebuildPublicNames();\n  }\n\n  /**\n   * Marks a source as recently seen without mutating identity metadata.\n   */\n  touchConnection(connectionId: string): void {\n    const source = this.sourceByConnectionId.get(connectionId);\n    if (!source) {\n      return;\n    }\n\n    source.lastSeenAt = this.now();\n  }\n\n  /**\n   * Lists active sources that currently publish at least one tool.\n   */\n  listSources(): SourceInfo[] {\n    return Array.from(this.sourceByConnectionId.values())\n      .map((source) => this.toSourceInfo(source))\n      .filter((source) => source.toolCount > 0)\n      .sort((a, b) => this.compareRecency(b.lastSeenAt, a.lastSeenAt, b.sourceId, a.sourceId));\n  }\n\n  /**\n   * Lists aggregated tools ordered by public tool name.\n   */\n  listTools(): AggregatedTool[] {\n    const result: AggregatedTool[] = [];\n\n    for (const [publicToolName, providers] of this.providersByPublicToolName.entries()) {\n      const rankedProviders = this.sortProvidersByRecency(providers);\n      const primaryProvider = rankedProviders[0];\n      if (!primaryProvider) {\n        continue;\n      }\n\n      const sources = rankedProviders\n        .map((provider) => {\n          const source = this.sourceByConnectionId.get(provider.sourceId);\n          return source ? this.toSourceInfo(source) : undefined;\n        })\n        .filter((source): source is SourceInfo => Boolean(source));\n\n      result.push({\n        ...primaryProvider.tool,\n        name: publicToolName,\n        originalName: primaryProvider.tool.name,\n        sources,\n      });\n    }\n\n    return result.sort((a, b) => a.name.localeCompare(b.name));\n  }\n\n  /**\n   * Resolves a tool invocation to a concrete source provider.\n   *\n   * Resolution order:\n   * 1. `sourceId`: exact source id, then fallback to tab id.\n   * 2. `requestTabId`: strict tab id match with no fallback.\n   * 3. Default: most recently seen provider.\n   */\n  resolveInvocation(options: {\n    toolName: string;\n    sourceId?: string;\n    requestTabId?: string;\n  }): ResolvedInvocation | null {\n    const providers = this.providersByPublicToolName.get(options.toolName);\n    if (!providers || providers.length === 0) {\n      return null;\n    }\n\n    const rankedProviders = this.sortProvidersByRecency(providers);\n\n    if (options.sourceId) {\n      const byConnection = rankedProviders.find(\n        (provider) => provider.sourceId === options.sourceId\n      );\n      if (byConnection) {\n        return {\n          connectionId: byConnection.sourceId,\n          tool: byConnection.tool,\n          publicToolName: byConnection.publicToolName,\n        };\n      }\n\n      const byTabId = rankedProviders.find((provider) => provider.tabId === options.sourceId);\n      if (byTabId) {\n        return {\n          connectionId: byTabId.sourceId,\n          tool: byTabId.tool,\n          publicToolName: byTabId.publicToolName,\n        };\n      }\n\n      return null;\n    }\n\n    if (options.requestTabId) {\n      const byRequestTab = rankedProviders.find(\n        (provider) => provider.tabId === options.requestTabId\n      );\n      if (!byRequestTab) {\n        return null;\n      }\n\n      return {\n        connectionId: byRequestTab.sourceId,\n        tool: byRequestTab.tool,\n        publicToolName: byRequestTab.publicToolName,\n      };\n    }\n\n    const provider = rankedProviders[0];\n    if (!provider) {\n      return null;\n    }\n\n    return {\n      connectionId: provider.sourceId,\n      tool: provider.tool,\n      publicToolName: provider.publicToolName,\n    };\n  }\n\n  /**\n   * Converts internal source metadata to public source info.\n   */\n  private toSourceInfo(source: SourceMetadata): SourceInfo {\n    return {\n      ...source,\n      toolCount: this.toolsByConnectionId.get(source.sourceId)?.length ?? 0,\n    };\n  }\n\n  /**\n   * Removes all tool providers for a connection.\n   */\n  private removeConnectionTools(connectionId: string): void {\n    this.toolsByConnectionId.delete(connectionId);\n  }\n\n  /**\n   * Rebuilds public tool names after source set changes.\n   *\n   * When the same original tool name appears in multiple tabs, names are\n   * disambiguated with a short tab suffix.\n   */\n  private rebuildPublicNames(): void {\n    const byOriginalName = new Map<string, ToolProvider[]>();\n    for (const providers of this.toolsByConnectionId.values()) {\n      for (const provider of providers) {\n        const key = provider.tool.name;\n        const group = byOriginalName.get(key) ?? [];\n        group.push(provider);\n        byOriginalName.set(key, group);\n      }\n    }\n\n    this.providersByPublicToolName.clear();\n\n    for (const [, providers] of byOriginalName) {\n      const uniqueTabIds = new Set(providers.map((p) => p.tabId));\n      const disambiguate = uniqueTabIds.size > 1;\n\n      for (const provider of providers) {\n        provider.publicToolName = buildPublicToolName({\n          originalToolName: provider.tool.name,\n          tabId: provider.tabId,\n          disambiguate,\n        });\n\n        const existing = this.providersByPublicToolName.get(provider.publicToolName) ?? [];\n        existing.push(provider);\n        this.providersByPublicToolName.set(\n          provider.publicToolName,\n          this.sortProvidersByRecency(existing)\n        );\n      }\n    }\n  }\n\n  /**\n   * Returns providers sorted by source recency.\n   */\n  private sortProvidersByRecency(providers: ToolProvider[]): ToolProvider[] {\n    return providers.slice().sort((a, b) => {\n      const aSource = this.sourceByConnectionId.get(a.sourceId);\n      const bSource = this.sourceByConnectionId.get(b.sourceId);\n      const aLastSeen = aSource?.lastSeenAt ?? 0;\n      const bLastSeen = bSource?.lastSeenAt ?? 0;\n\n      return this.compareRecency(bLastSeen, aLastSeen, b.sourceId, a.sourceId);\n    });\n  }\n\n  /**\n   * Compares two source recency tuples and stabilizes order by source id.\n   */\n  private compareRecency(\n    leftLastSeen: number,\n    rightLastSeen: number,\n    leftId: string,\n    rightId: string\n  ): number {\n    const timeDiff = leftLastSeen - rightLastSeen;\n    if (timeDiff !== 0) {\n      return timeDiff;\n    }\n    return leftId.localeCompare(rightId);\n  }\n}\n","import { z } from 'zod/v4';\nimport {\n  CallToolResultSchema,\n  InboundToolSchema,\n  NormalizedToolSchema,\n  normalizeInboundTool,\n  RelayInvokeArgsSchema,\n  type RelayTool,\n} from './protocol.js';\n\n/**\n * Schema for source identity bootstrap message.\n */\nexport const BrowserHelloMessageSchema = z.object({\n  type: z.literal('hello'),\n  tabId: z.string().min(1),\n  origin: z.string().optional(),\n  url: z.string().optional(),\n  title: z.string().optional(),\n  iconUrl: z.string().optional(),\n});\n\n/**\n * Schema for full tool-list synchronization message.\n *\n * Inbound tools are permissively parsed then normalized to SDK Tool shape.\n */\nexport const BrowserToolsListMessageSchema = z.object({\n  type: z.literal('tools/list'),\n  tools: z\n    .array(InboundToolSchema)\n    .transform((tools): RelayTool[] => tools.map(normalizeInboundTool)),\n});\n\n/**\n * Schema for tool-set replacement notification pushed after initial registration.\n *\n * Processing is identical to `tools/list` — the full tool set replaces any\n * previously registered tools — but the distinct type signals a dynamic update\n * rather than an initial handshake.\n */\nexport const BrowserToolsChangedMessageSchema = z.object({\n  type: z.literal('tools/changed'),\n  tools: z\n    .array(InboundToolSchema)\n    .transform((tools): RelayTool[] => tools.map(normalizeInboundTool)),\n});\n\n/**\n * Schema for invocation response payloads sent by browser sources.\n *\n * `result` remains permissive and is normalized later to preserve runtime\n * behavior for malformed tool responses.\n */\nexport const BrowserToolResultMessageSchema = z.object({\n  type: z.literal('result'),\n  callId: z.string().min(1),\n  result: z.unknown(),\n});\n\n/**\n * Schema for browser heartbeat acknowledgement.\n */\nexport const BrowserPongMessageSchema = z.object({\n  type: z.literal('pong'),\n});\n\n/**\n * Schema for elicitation request forwarded from a browser tool handler.\n *\n * When a WebMCP tool handler calls `elicitInput()`, the embed script\n * intercepts the call and forwards the parameters to the relay via this\n * message type. The relay then sends `elicitation/create` to the MCP\n * client (e.g. Claude Code) and returns the result via an\n * `elicitation-response` message.\n */\nexport const BrowserElicitationRequestSchema = z.object({\n  type: z.literal('elicitation-request'),\n  callId: z.string().min(1),\n  params: z.record(z.string(), z.unknown()),\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  BrowserElicitationRequestSchema,\n]);\n\n/**\n * Browser-to-relay message payload.\n */\nexport type BrowserToRelayMessage = z.infer<typeof BrowserToRelayMessageSchema>;\n\n/**\n * Browser source bootstrap message.\n */\nexport type BrowserHelloMessage = z.infer<typeof BrowserHelloMessageSchema>;\n\n/**\n * Shared relay identity used for discovery and attach verification.\n */\nexport const RelayDescriptorSchema = z.object({\n  host: z.string().min(1),\n  instanceId: z.string().min(1),\n  label: z.string().min(1).optional(),\n  port: z.number().int().min(1).max(65535),\n  relayId: z.string().min(1).optional(),\n  workspace: z.string().min(1).optional(),\n});\n\n/**\n * Relay identity used for discovery and attach verification.\n */\nexport type RelayDescriptor = z.infer<typeof RelayDescriptorSchema>;\n\n/**\n * Schema for server hello messages sent immediately after WebSocket connect.\n */\nexport const ServerHelloMessageSchema = z.object({\n  type: z.literal('server-hello'),\n  service: z.literal('webmcp-local-relay'),\n  version: z.literal(1),\n  ...RelayDescriptorSchema.shape,\n});\n\n/**\n * Server hello payload.\n */\nexport type ServerHelloMessage = z.infer<typeof ServerHelloMessageSchema>;\n\n/**\n * Schema for acceptance of a browser source hello message.\n */\nexport const RelayHelloAcceptedMessageSchema = z.object({\n  type: z.literal('hello/accepted'),\n});\n\n/**\n * Acceptance payload for a browser source hello message.\n */\nexport type RelayHelloAcceptedMessage = z.infer<typeof RelayHelloAcceptedMessageSchema>;\n\n/**\n * Schema for rejection of a browser source hello message.\n */\nexport const RelayHelloRejectedMessageSchema = z.object({\n  type: z.literal('hello/rejected'),\n  reason: z.string().min(1),\n  message: z.string().min(1),\n});\n\n/**\n * Rejection payload for a browser source hello message.\n */\nexport type RelayHelloRejectedMessage = z.infer<typeof RelayHelloRejectedMessageSchema>;\n\n/**\n * Schema for relay invocation messages sent to browser sources.\n */\nexport const RelayInvokeMessageSchema = z.object({\n  type: z.literal('invoke'),\n  callId: z.string().min(1),\n  toolName: z.string().min(1),\n  args: RelayInvokeArgsSchema,\n});\n\n/**\n * Schema for relay heartbeat messages sent to browser sources.\n */\nexport const RelayPingMessageSchema = z.object({\n  type: z.literal('ping'),\n});\n\n/**\n * Schema for relay reload messages sent to browser sources.\n */\nexport const RelayReloadMessageSchema = z.object({\n  type: z.literal('reload'),\n});\n\n/**\n * Schema for elicitation response sent from relay to browser.\n *\n * Contains the MCP client's response to an `elicitation/create` request.\n */\nexport const RelayElicitationResponseSchema = z.object({\n  type: z.literal('elicitation-response'),\n  callId: z.string().min(1),\n  result: z.record(z.string(), z.unknown()),\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  RelayElicitationResponseSchema,\n]);\n\n/**\n * Relay-to-browser message payload.\n */\nexport type RelayToBrowserMessage = z.infer<typeof RelayToBrowserMessageSchema>;\n\n/**\n * Relay-to-relay protocol (client mode <-> server mode).\n */\n\n/**\n * Schema for source metadata transmitted in relay-to-relay messages.\n */\nexport const RelaySourceInfoSchema = z.object({\n  sourceId: z.string(),\n  tabId: z.string(),\n  origin: z.string().optional(),\n  url: z.string().optional(),\n  title: z.string().optional(),\n  iconUrl: z.string().optional(),\n  connectedAt: z.number(),\n  lastSeenAt: z.number(),\n  toolCount: z.number(),\n});\n\n/**\n * Source metadata transmitted in relay-to-relay messages.\n */\nexport type RelaySourceInfo = z.infer<typeof RelaySourceInfoSchema>;\n\n/**\n * Schema for relay client identification message.\n */\nexport const RelayClientHelloSchema = z.object({\n  type: z.literal('relay/hello'),\n});\n\n/**\n * Schema for relay client tool list request.\n */\nexport const RelayClientListToolsSchema = z.object({\n  type: z.literal('relay/list-tools'),\n});\n\n/**\n * Schema for relay client tool invocation request.\n */\nexport const RelayClientInvokeSchema = z.object({\n  type: z.literal('relay/invoke'),\n  callId: z.string().min(1),\n  toolName: z.string().min(1),\n  args: RelayInvokeArgsSchema,\n});\n\n/**\n * Union schema for all relay-client-to-server messages.\n */\nexport const RelayClientToServerMessageSchema = z.discriminatedUnion('type', [\n  RelayClientHelloSchema,\n  RelayClientListToolsSchema,\n  RelayClientInvokeSchema,\n]);\n\n/**\n * Relay client to server message payload.\n */\nexport type RelayClientToServerMessage = z.infer<typeof RelayClientToServerMessageSchema>;\n\nconst RelayToolsPayloadFields = {\n  tools: z.array(NormalizedToolSchema),\n  sources: z.array(RelaySourceInfoSchema).optional().default([]),\n  toolSourceMap: z.record(z.string(), z.array(z.string())).optional().default({}),\n};\n\n/**\n * Schema for relay server tool list response.\n */\nexport const RelayServerToolsSchema = z.object({\n  type: z.literal('relay/tools'),\n  ...RelayToolsPayloadFields,\n});\n\n/**\n * Schema for relay server invocation result response.\n */\nexport const RelayServerResultSchema = z.object({\n  type: z.literal('relay/result'),\n  callId: z.string().min(1),\n  result: CallToolResultSchema,\n});\n\n/**\n * Schema for relay server push notification when tools change.\n */\nexport const RelayServerToolsChangedSchema = z.object({\n  type: z.literal('relay/tools-changed'),\n  ...RelayToolsPayloadFields,\n});\n\n/**\n * Union schema for all relay-server-to-client messages.\n */\nexport const RelayServerToClientMessageSchema = z.discriminatedUnion('type', [\n  ServerHelloMessageSchema,\n  RelayServerToolsSchema,\n  RelayServerResultSchema,\n  RelayServerToolsChangedSchema,\n]);\n\n/**\n * Relay server to client message payload.\n */\nexport type RelayServerToClientMessage = z.infer<typeof RelayServerToClientMessageSchema>;\n","import { randomUUID } from 'node:crypto';\nimport { EventEmitter } from 'node:events';\n\nimport WebSocket, { WebSocketServer } from 'ws';\nimport {\n  buildPortCandidates,\n  DEFAULT_RELAY_PORT,\n  DEFAULT_RELAY_PORT_RANGE_END,\n  defaultRelayPortPersistPath,\n  persistPort,\n} from './portStrategy.js';\nimport {\n  CallToolResultSchema,\n  type RelayCallToolResult,\n  type RelayInvokeArgs,\n  type RelayTool,\n} from './protocol.js';\nimport { type AggregatedTool, HelloRequiredError, RelayRegistry } from './registry.js';\nimport {\n  BrowserToRelayMessageSchema,\n  type RelayClientToServerMessage,\n  RelayClientToServerMessageSchema,\n  type RelayServerToClientMessage,\n  RelayServerToClientMessageSchema,\n  type RelayHelloAcceptedMessage,\n  type RelayHelloRejectedMessage,\n  type RelaySourceInfo,\n  type RelayToBrowserMessage,\n  type ServerHelloMessage,\n} from './schemas.js';\n\nconst RELAY_BROWSER_PROTOCOL = 'webmcp.v1';\nconst RELAY_DISCOVERY_PROTOCOL = 'webmcp-discovery.v1';\nconst RELAY_INTERNAL_PROTOCOL = 'webmcp-relay.v1';\nconst RELAY_SERVER_MESSAGE_TIMEOUT_MS = 750;\nconst HEARTBEAT_INTERVAL_MS = 15_000;\nconst HEARTBEAT_DEAD_THRESHOLD_MS = 25_000;\nconst SUPPORTED_SUBPROTOCOLS = new Set([\n  RELAY_BROWSER_PROTOCOL,\n  RELAY_DISCOVERY_PROTOCOL,\n  RELAY_INTERNAL_PROTOCOL,\n]);\n\n/**\n * In-flight relay invocation waiting for a browser `result` message.\n */\ninterface PendingInvocation {\n  callId: string;\n  connectionId: string;\n  timeoutId: ReturnType<typeof setTimeout>;\n  resolve: (result: RelayCallToolResult) => void;\n  reject: (error: Error) => void;\n}\n\n/**\n * Runtime options for {@link RelayBridgeServer}.\n */\nexport interface RelayBridgeServerOptions {\n  /**\n   * Network interface used by the local WebSocket server.\n   * @defaultValue `\"127.0.0.1\"`\n   */\n  host?: string;\n  /**\n   * Preferred WebSocket port for browser widget connections.\n   * @defaultValue `9333`\n   */\n  port?: number;\n  /**\n   * 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 reported by browser `hello` messages.\n   * Use `[\"*\"]` to allow all origins.\n   *\n   * Permissive by default for zero-config developer experience — any browser\n   * page can connect and register tools without additional setup. For production\n   * or shared machines, restrict to trusted host origins via the\n   * `--widget-origin` CLI flag or by passing explicit origins here.\n   *\n   * @defaultValue `[\"*\"]`\n   */\n  allowedOrigins?: string[];\n  /**\n   * Maximum WebSocket payload size in bytes.\n   * @defaultValue `1000000`\n   */\n  maxPayloadBytes?: number;\n  /**\n   * Timeout used for browser tool invocations.\n   * @defaultValue `25000`\n   */\n  invokeTimeoutMs?: number;\n  /**\n   * 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 pendingInvocations = new Map<string, PendingInvocation>();\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    this.maxPayloadBytes = options.maxPayloadBytes ?? 1_000_000;\n    this.invokeTimeoutMs = options.invokeTimeoutMs ?? 25_000;\n    this.label = options.label;\n    this.workspace = options.workspace;\n    this.relayId = options.relayId;\n    this.instanceId = randomUUID();\n\n    if (this.preferredPort !== 0 && (this.preferredPort < 1 || this.preferredPort > 65535)) {\n      throw new Error(\n        `Invalid port ${this.preferredPort}. Port must be 0 (auto-assign) or between 1 and 65535.`\n      );\n    }\n    if (this.portRangeEnd < this.preferredPort && this.preferredPort !== 0) {\n      throw new Error(\n        `Invalid port range ${this.preferredPort}-${this.portRangeEnd}. rangeEnd must be greater than or equal to the preferred port.`\n      );\n    }\n    if (this.maxPayloadBytes <= 0) {\n      throw new Error(`Invalid maxPayloadBytes ${this.maxPayloadBytes}. Must be greater than 0.`);\n    }\n    if (this.invokeTimeoutMs <= 0) {\n      throw new Error(`Invalid invokeTimeoutMs ${this.invokeTimeoutMs}. Must be greater than 0.`);\n    }\n  }\n\n  /**\n   * Current operating mode.\n   */\n  get mode(): 'server' | 'client' {\n    return this._mode;\n  }\n\n  /**\n   * Resolved listening port. In client mode this is the port of the server\n   * relay being proxied through.\n   */\n  get port(): number {\n    return this.desiredPort;\n  }\n\n  /**\n   * Tools received from the server relay (client mode only).\n   * Returns an empty array in server mode.\n   */\n  listToolsFromRelay(): RelayTool[] {\n    return this._mode === 'client' ? [...this.clientTools] : [];\n  }\n\n  /**\n   * Source metadata received from the server relay (client mode only).\n   * Returns an empty array in server mode.\n   */\n  listSourcesFromRelay(): RelaySourceInfo[] {\n    return this._mode === 'client' ? [...this.clientSources] : [];\n  }\n\n  /**\n   * Tool-to-source mapping received from the server relay (client mode only).\n   * Maps public tool names to arrays of source IDs.\n   * Returns an empty record in server mode.\n   */\n  getToolSourceMapFromRelay(): Record<string, string[]> {\n    return this._mode === 'client' ? { ...this.clientToolSourceMap } : {};\n  }\n\n  /**\n   * Starts the bridge. Attempts to bind a WebSocket server (server mode).\n   * If a compatible relay already owns a candidate port, joins it in client mode.\n   * If a non-relay process owns the port, continues searching the reserved range.\n   */\n  async start(): Promise<void> {\n    if (this.wss || this.clientSocket) {\n      return;\n    }\n\n    this.stopping = false;\n    await this.startUsingPortStrategy();\n  }\n\n  private async startUsingPortStrategy(): Promise<void> {\n    if (this.preferredPort === 0) {\n      await this.startAsServer(0);\n      return;\n    }\n\n    const candidates = await buildPortCandidates({\n      defaultPort: this.preferredPort,\n      ...(this.portExplicitlySet ? { fixedPort: this.preferredPort } : {}),\n      host: this.host,\n      persistPath: this.persistPath,\n      rangeEnd: this.portRangeEnd,\n    });\n\n    for (const candidate of candidates) {\n      try {\n        await this.startAsServer(candidate.port);\n        await persistPort(this.port, this.persistPath, this.host);\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 persistPort(this.port, this.persistPath, this.host);\n          return;\n        }\n\n        if (candidate.wasFixed) {\n          throw new Error(\n            `Port ${candidate.port} is already in use by a non-WebMCP service and cannot be shared.`\n          );\n        }\n\n        process.stderr.write(\n          `[webmcp-local-relay] info: port ${candidate.port} is occupied by a non-relay service, trying next port\\n`\n        );\n      }\n    }\n\n    throw new Error(\n      `No compatible relay port was available in the range ${this.preferredPort}-${this.portRangeEnd}.`\n    );\n  }\n\n  private isAddressInUseError(error: unknown): boolean {\n    return (\n      error instanceof Error &&\n      ('code' in error\n        ? (error as NodeJS.ErrnoException).code === 'EADDRINUSE'\n        : error.message.includes('EADDRINUSE'))\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 {\n      return false;\n    }\n  }\n\n  /**\n   * Stops all relay resources and rejects any pending invocations.\n   */\n  async stop(): Promise<void> {\n    this.stopping = true;\n\n    if (this.clientReconnectTimer) {\n      clearTimeout(this.clientReconnectTimer);\n      this.clientReconnectTimer = null;\n    }\n\n    if (this._mode === 'client') {\n      for (const pending of this.clientPendingInvocations.values()) {\n        clearTimeout(pending.timeoutId);\n        pending.reject(new Error('Relay client stopped'));\n      }\n      this.clientPendingInvocations.clear();\n      this.clientTools = [];\n      this.clientSources = [];\n      this.clientToolSourceMap = {};\n\n      if (this.clientSocket) {\n        try {\n          this.clientSocket.close(1000, 'Relay client shutting down');\n        } catch (err) {\n          process.stderr.write(\n            `[webmcp-local-relay] warn: error closing client socket during shutdown: ${err instanceof Error ? err.message : String(err)}\\n`\n          );\n        }\n        this.clientSocket = null;\n      }\n      return;\n    }\n\n    this.off('stateChanged', this.onStateChangedPushRelay);\n\n    for (const socket of this.socketByConnectionId.values()) {\n      try {\n        socket.close(1001, 'Relay shutting down');\n      } catch (err) {\n        process.stderr.write(\n          `[webmcp-local-relay] warn: error closing socket during shutdown: ${err instanceof Error ? err.message : String(err)}\\n`\n        );\n      }\n    }\n\n    for (const intervalId of this.heartbeatIntervalByConnectionId.values()) {\n      clearInterval(intervalId);\n    }\n    this.heartbeatIntervalByConnectionId.clear();\n    this.lastPongByConnectionId.clear();\n\n    this.socketByConnectionId.clear();\n    this.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) => {\n      const connectionId = randomUUID();\n      this.socketByConnectionId.set(connectionId, socket);\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      socket.on('message', (raw: WebSocket.RawData) => {\n        this.onSocketMessage(connectionId, raw);\n      });\n\n      socket.on('close', () => {\n        this.onSocketClose(connectionId);\n      });\n\n      socket.on('error', (err: Error) => {\n        process.stderr.write(\n          `[webmcp-local-relay] warn: socket error for connection ${connectionId}: ${err.message}\\n`\n        );\n        this.onSocketClose(connectionId);\n      });\n\n      this.startHeartbeat(connectionId);\n    });\n\n    wss.on('error', (err: Error) => {\n      process.stderr.write(`[webmcp-local-relay] error: WebSocket server error: ${err.message}\\n`);\n      this.emit('error', err);\n    });\n\n    this.wss = wss;\n    this._mode = 'server';\n\n    const address = wss.address();\n    if (address && typeof address !== 'string') {\n      this.desiredPort = address.port;\n    }\n\n    this.on('stateChanged', this.onStateChangedPushRelay);\n  }\n\n  private 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  private invokeToolLocally(\n    toolName: string,\n    args: RelayInvokeArgs,\n    options: { sourceId?: string; requestTabId?: string }\n  ): Promise<RelayCallToolResult> {\n    const resolveOptions: { toolName: string; sourceId?: string; requestTabId?: string } = {\n      toolName,\n    };\n    if (options.sourceId !== undefined) {\n      resolveOptions.sourceId = options.sourceId;\n    }\n    if (options.requestTabId !== undefined) {\n      resolveOptions.requestTabId = options.requestTabId;\n    }\n\n    const resolved = this.registry.resolveInvocation(resolveOptions);\n\n    if (!resolved) {\n      throw new Error(`No active browser source provides tool \"${toolName}\"`);\n    }\n\n    const socket = this.socketByConnectionId.get(resolved.connectionId);\n    if (!socket || socket.readyState !== WebSocket.OPEN) {\n      throw new Error(\n        `Tool source ${resolved.connectionId} disconnected before invocation of \"${toolName}\"`\n      );\n    }\n\n    const callId = randomUUID();\n\n    return new Promise<RelayCallToolResult>((resolve, reject) => {\n      const timeoutId = setTimeout(() => {\n        this.pendingInvocations.delete(callId);\n        reject(\n          new Error(`Invocation for tool \"${toolName}\" timed out after ${this.invokeTimeoutMs}ms`)\n        );\n      }, this.invokeTimeoutMs);\n\n      this.pendingInvocations.set(callId, {\n        callId,\n        connectionId: resolved.connectionId,\n        timeoutId,\n        resolve,\n        reject,\n      });\n\n      const message: RelayToBrowserMessage = {\n        type: 'invoke',\n        callId,\n        toolName: resolved.tool.name,\n        args,\n      };\n\n      try {\n        socket.send(JSON.stringify(message));\n      } catch (err) {\n        clearTimeout(timeoutId);\n        this.pendingInvocations.delete(callId);\n        reject(\n          new Error(\n            `Failed to send invocation for tool \"${toolName}\": ${err instanceof Error ? err.message : err}`\n          )\n        );\n      }\n    });\n  }\n\n  /**\n   * Handles a raw WebSocket message from a connected source.\n   *\n   * Routes relay-protocol messages (`relay/*`) to the relay client handler\n   * and browser-protocol messages to the existing browser handler.\n   */\n  private onSocketMessage(connectionId: string, raw: WebSocket.RawData): void {\n    const text = this.rawDataToUtf8(raw);\n\n    let parsedJson: unknown;\n    try {\n      parsedJson = JSON.parse(text);\n    } catch (err) {\n      const preview = text.length > 200 ? `${text.slice(0, 200)}...` : text;\n      process.stderr.write(\n        `[webmcp-local-relay] warn: invalid JSON from connection ${connectionId} (${err instanceof Error ? err.message : 'parse error'}): ${preview}\\n`\n      );\n      return;\n    }\n\n    const typeField =\n      typeof parsedJson === 'object' && parsedJson !== null\n        ? (parsedJson as Record<string, unknown>).type\n        : undefined;\n\n    if (typeof typeField === 'string' && typeField.startsWith('relay/')) {\n      const relayMsg = RelayClientToServerMessageSchema.safeParse(parsedJson);\n      if (relayMsg.success) {\n        this.onRelayClientMessage(connectionId, relayMsg.data);\n      } else {\n        process.stderr.write(\n          `[webmcp-local-relay] warn: invalid relay message from ${connectionId}: ${relayMsg.error.message}\\n`\n        );\n      }\n      return;\n    }\n\n    this.registry.touchConnection(connectionId);\n\n    const parsedMessage = BrowserToRelayMessageSchema.safeParse(parsedJson);\n    if (!parsedMessage.success) {\n      process.stderr.write(\n        `[webmcp-local-relay] warn: invalid message from connection ${connectionId} (type=${typeField}): ${parsedMessage.error.message}\\n`\n      );\n      return;\n    }\n\n    const message = parsedMessage.data;\n\n    switch (message.type) {\n      case 'hello':\n        try {\n          const socket = this.socketByConnectionId.get(connectionId);\n          if (!this.isHostOriginAllowed(message.origin)) {\n            process.stderr.write(\n              `[webmcp-local-relay] warn: rejecting source ${connectionId} with disallowed host origin: ${message.origin ?? 'missing'}\\n`\n            );\n            if (socket) {\n              this.sendHelloRejected(\n                socket,\n                {\n                  type: 'hello/rejected',\n                  reason: 'host-origin-not-allowed',\n                  message: 'Host page origin is not allowed by this relay.',\n                },\n                1008,\n                'Host origin not allowed'\n              );\n            }\n            break;\n          }\n          this.registry.upsertSource(connectionId, message);\n          if (socket) {\n            this.sendHelloAccepted(socket, { type: 'hello/accepted' });\n          }\n          this.emit('stateChanged');\n        } catch (err) {\n          process.stderr.write(\n            `[webmcp-local-relay] error: failed to process hello from connection ${connectionId}: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n          );\n        }\n        break;\n\n      case 'tools/list':\n      case 'tools/changed':\n        try {\n          this.registry.registerTools(connectionId, message.tools);\n          this.emit('stateChanged');\n        } catch (err) {\n          if (err instanceof HelloRequiredError) {\n            process.stderr.write(\n              `[webmcp-local-relay] warn: connection ${connectionId} sent tools before hello, ignoring\\n`\n            );\n          } else {\n            process.stderr.write(\n              `[webmcp-local-relay] error: failed to register tools for connection ${connectionId}: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n            );\n            const socket = this.socketByConnectionId.get(connectionId);\n            socket?.close(1011, 'Failed to register tools');\n          }\n        }\n        break;\n\n      case 'result': {\n        const pending = this.pendingInvocations.get(message.callId);\n        if (!pending) {\n          process.stderr.write(\n            `[webmcp-local-relay] warn: received result for unknown callId ${message.callId}\\n`\n          );\n          break;\n        }\n\n        clearTimeout(pending.timeoutId);\n        this.pendingInvocations.delete(message.callId);\n        pending.resolve(this.normalizeCallToolResult(message.result));\n        break;\n      }\n\n      case 'pong':\n        this.lastPongByConnectionId.set(connectionId, Date.now());\n        break;\n\n      case 'elicitation-request':\n        this.emit('elicitationRequest', {\n          callId: message.callId,\n          connectionId,\n          params: message.params,\n        });\n        break;\n    }\n  }\n\n  /**\n   * Sends an elicitation response back to the browser source that requested it.\n   */\n  sendElicitationResponse(\n    connectionId: string,\n    callId: string,\n    result: Record<string, unknown>\n  ): void {\n    const socket = this.socketByConnectionId.get(connectionId);\n    if (!socket || socket.readyState !== WebSocket.OPEN) {\n      process.stderr.write(\n        `[webmcp-local-relay] warn: cannot send elicitation response — source ${connectionId} disconnected\\n`\n      );\n      return;\n    }\n    const message: RelayToBrowserMessage = {\n      type: 'elicitation-response',\n      callId,\n      result,\n    };\n    try {\n      socket.send(JSON.stringify(message));\n    } catch (err) {\n      process.stderr.write(\n        `[webmcp-local-relay] warn: failed to send elicitation response: ${err instanceof Error ? err.message : String(err)}\\n`\n      );\n    }\n  }\n\n  /**\n   * Handles relay-protocol messages from relay client connections.\n   */\n  private onRelayClientMessage(connectionId: string, message: RelayClientToServerMessage): void {\n    switch (message.type) {\n      case 'relay/hello':\n        this.relayClientConnectionIds.add(connectionId);\n        break;\n\n      case 'relay/list-tools': {\n        const response = this.buildRelayToolsPayload('relay/tools');\n        const socket = this.socketByConnectionId.get(connectionId);\n        if (socket?.readyState === WebSocket.OPEN) {\n          try {\n            socket.send(JSON.stringify(response));\n          } catch (err) {\n            process.stderr.write(\n              `[webmcp-local-relay] warn: failed to send relay tools response to ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n            );\n          }\n        }\n        break;\n      }\n\n      case 'relay/invoke': {\n        const { callId, toolName, args } = message;\n        void (async () => {\n          try {\n            const result = await this.invokeToolLocally(toolName, args ?? {}, {});\n            const response: RelayServerToClientMessage = {\n              type: 'relay/result',\n              callId,\n              result,\n            };\n            const socket = this.socketByConnectionId.get(connectionId);\n            if (socket?.readyState === WebSocket.OPEN) {\n              try {\n                socket.send(JSON.stringify(response));\n              } catch (sendErr) {\n                process.stderr.write(\n                  `[webmcp-local-relay] warn: failed to send relay result to ${connectionId}: ${sendErr instanceof Error ? sendErr.message : String(sendErr)}\\n`\n                );\n              }\n            } else {\n              process.stderr.write(\n                `[webmcp-local-relay] warn: relay client ${connectionId} disconnected before result for callId ${callId} could be delivered\\n`\n              );\n            }\n          } catch (err) {\n            const response: RelayServerToClientMessage = {\n              type: 'relay/result',\n              callId,\n              result: {\n                content: [\n                  {\n                    type: 'text',\n                    text: `Relay invocation failed: ${err instanceof Error ? err.message : String(err)}`,\n                  },\n                ],\n                isError: true,\n              },\n            };\n            const socket = this.socketByConnectionId.get(connectionId);\n            if (socket?.readyState === WebSocket.OPEN) {\n              try {\n                socket.send(JSON.stringify(response));\n              } catch (sendErr) {\n                process.stderr.write(\n                  `[webmcp-local-relay] warn: failed to send relay error result to ${connectionId}: ${sendErr instanceof Error ? sendErr.message : String(sendErr)}\\n`\n                );\n              }\n            } else {\n              process.stderr.write(\n                `[webmcp-local-relay] warn: relay client ${connectionId} disconnected before error result for callId ${callId} could be delivered\\n`\n              );\n            }\n          }\n        })();\n        break;\n      }\n    }\n  }\n\n  /**\n   * Pushes current tool state to all connected relay clients.\n   */\n  private pushToolsToRelayClients(): void {\n    if (this.relayClientConnectionIds.size === 0) {\n      return;\n    }\n\n    const message = this.buildRelayToolsPayload('relay/tools-changed');\n    const payload = JSON.stringify(message);\n\n    for (const connectionId of this.relayClientConnectionIds) {\n      const socket = this.socketByConnectionId.get(connectionId);\n      if (socket?.readyState === WebSocket.OPEN) {\n        try {\n          socket.send(payload);\n        } catch (err) {\n          process.stderr.write(\n            `[webmcp-local-relay] warn: failed to push tool update to relay client ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n          );\n        }\n      }\n    }\n  }\n\n  /**\n   * Handles source disconnection and rejects in-flight calls owned by that source.\n   */\n  private onSocketClose(connectionId: string): void {\n    this.stopHeartbeat(connectionId);\n    this.relayClientConnectionIds.delete(connectionId);\n    this.registry.removeConnection(connectionId);\n    this.socketByConnectionId.delete(connectionId);\n    this.emit('stateChanged');\n\n    for (const [callId, pending] of this.pendingInvocations.entries()) {\n      if (pending.connectionId !== connectionId) {\n        continue;\n      }\n\n      clearTimeout(pending.timeoutId);\n      this.pendingInvocations.delete(callId);\n      pending.reject(new Error(`Tool source ${connectionId} disconnected during invocation`));\n    }\n  }\n\n  private 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    this.stopping = false;\n\n    return new Promise((resolve, reject) => {\n      const wsUrl = `ws://${this.host}:${port}`;\n      const ws = new WebSocket(wsUrl, [RELAY_INTERNAL_PROTOCOL, RELAY_BROWSER_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        ws.once('error', () => {\n          // Ignore late socket errors from ports that failed relay verification.\n        });\n        cleanup();\n        reject(error);\n      };\n\n      const finish = () => {\n        cleanup();\n        resolve({ bufferedMessages, socket: ws });\n      };\n\n      const onOpen = () => {\n        try {\n          this.sendRelayClientHandshake(ws);\n        } catch (err) {\n          rejectWith(\n            new Error(\n              `Failed to send handshake to relay server at ${wsUrl}: ${err instanceof Error ? err.message : String(err)}`\n            )\n          );\n        }\n      };\n\n      const onErrorBeforeReady = (err: Error) => {\n        rejectWith(new Error(`Failed to connect to relay server at ${wsUrl}: ${err.message}`));\n      };\n\n      const onCloseBeforeReady = () => {\n        rejectWith(new Error(`Connection to ${wsUrl} closed before relay verification completed`));\n      };\n\n      const onMessageBeforeReady = (raw: WebSocket.RawData) => {\n        const message = this.parseRelayServerMessage(raw);\n        if (!message) {\n          rejectWith(new Error(`Received a non-relay response while probing ${wsUrl}`));\n          return;\n        }\n\n        bufferedMessages.push(message);\n\n        if (message.type === 'server-hello') {\n          if (message.service !== 'webmcp-local-relay') {\n            rejectWith(new Error(`Unexpected relay service \"${message.service}\" at ${wsUrl}`));\n            return;\n          }\n          finish();\n          return;\n        }\n\n        if (message.type === 'relay/tools' || message.type === 'relay/tools-changed') {\n          finish();\n        }\n      };\n\n      const timeoutId = setTimeout(() => {\n        rejectWith(new Error(`Timed out waiting for relay hello from ${wsUrl}`));\n      }, RELAY_SERVER_MESSAGE_TIMEOUT_MS);\n\n      ws.once('open', onOpen);\n      ws.once('error', onErrorBeforeReady);\n      ws.once('close', onCloseBeforeReady);\n      ws.on('message', onMessageBeforeReady);\n    });\n  }\n\n  private setupClientHandlers(\n    ws: WebSocket,\n    bufferedMessages: RelayServerToClientMessage[] = []\n  ): void {\n    for (const message of bufferedMessages) {\n      this.processRelayServerMessage(message);\n    }\n\n    ws.on('message', (raw: WebSocket.RawData) => {\n      const message = this.parseRelayServerMessage(raw);\n      if (!message) {\n        return;\n      }\n\n      this.processRelayServerMessage(message);\n    });\n\n    ws.on('close', () => {\n      this.clientSocket = null;\n      for (const [callId, pending] of this.clientPendingInvocations) {\n        clearTimeout(pending.timeoutId);\n        this.clientPendingInvocations.delete(callId);\n        pending.reject(new Error('Relay server connection lost during invocation'));\n      }\n\n      this.clientTools = [];\n      this.clientSources = [];\n      this.clientToolSourceMap = {};\n      this.emit('stateChanged');\n\n      if (!this.stopping) {\n        this.scheduleReconnect();\n      }\n    });\n\n    ws.on('error', (err: Error) => {\n      process.stderr.write(\n        `[webmcp-local-relay] warn: relay client socket error: ${err.message}\\n`\n      );\n    });\n  }\n\n  private sendRelayClientHandshake(ws: WebSocket): void {\n    ws.send(JSON.stringify({ type: 'relay/hello' }));\n    ws.send(JSON.stringify({ type: 'relay/list-tools' }));\n  }\n\n  private parseRelayServerMessage(raw: WebSocket.RawData): RelayServerToClientMessage | null {\n    const text = this.rawDataToUtf8(raw);\n    let parsed: unknown;\n    try {\n      parsed = JSON.parse(text);\n    } catch (err) {\n      const preview = text.length > 200 ? `${text.slice(0, 200)}...` : text;\n      process.stderr.write(\n        `[webmcp-local-relay] warn: invalid JSON from relay server (${err instanceof Error ? err.message : 'parse error'}): ${preview}\\n`\n      );\n      return null;\n    }\n\n    const message = RelayServerToClientMessageSchema.safeParse(parsed);\n    if (!message.success) {\n      const typeField =\n        typeof parsed === 'object' && parsed !== null\n          ? (parsed as Record<string, unknown>).type\n          : '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().catch((err) => {\n        process.stderr.write(\n          `[webmcp-local-relay] error: unexpected failure during reconnection: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n        );\n        if (!this.stopping) {\n          this.scheduleReconnect();\n        }\n      });\n    }, delay);\n  }\n\n  /**\n   * Attempts to promote from client to server mode when reconnecting.\n   * 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 invokeToolViaRelay(\n    toolName: string,\n    args: RelayInvokeArgs\n  ): Promise<RelayCallToolResult> {\n    if (!this.clientSocket || this.clientSocket.readyState !== WebSocket.OPEN) {\n      throw new Error('Not connected to relay server');\n    }\n\n    const callId = randomUUID();\n    const socket = this.clientSocket;\n\n    return new Promise<RelayCallToolResult>((resolve, reject) => {\n      const timeoutId = setTimeout(() => {\n        this.clientPendingInvocations.delete(callId);\n        reject(\n          new Error(\n            `Proxied invocation for tool \"${toolName}\" timed out after ${this.invokeTimeoutMs}ms`\n          )\n        );\n      }, this.invokeTimeoutMs);\n\n      this.clientPendingInvocations.set(callId, {\n        callId,\n        connectionId: 'relay-server',\n        timeoutId,\n        resolve,\n        reject,\n      });\n\n      const message: RelayClientToServerMessage = {\n        type: 'relay/invoke',\n        callId,\n        toolName,\n        args,\n      };\n\n      try {\n        socket.send(JSON.stringify(message));\n      } catch (err) {\n        clearTimeout(timeoutId);\n        this.clientPendingInvocations.delete(callId);\n        reject(\n          new Error(`Failed to send relay invocation: ${err instanceof Error ? err.message : err}`)\n        );\n      }\n    });\n  }\n\n  /**\n   * Builds the complete relay response payload including tools and source metadata.\n   */\n  private buildRelayToolsPayload(\n    type: 'relay/tools' | 'relay/tools-changed'\n  ): RelayServerToClientMessage {\n    const tools = this.registry.listTools();\n    const sources = this.registry.listSources();\n    const toolSourceMap: Record<string, string[]> = {};\n\n    for (const tool of tools) {\n      toolSourceMap[tool.name] = tool.sources.map((s) => s.sourceId);\n    }\n\n    return {\n      type,\n      tools: this.toWireTools(tools),\n      sources,\n      toolSourceMap,\n    };\n  }\n\n  /**\n   * Maps aggregated tools to the wire format used by the relay protocol.\n   */\n  private toWireTools(tools: AggregatedTool[]): RelayTool[] {\n    return tools.map(({ originalName: _originalName, sources: _sources, ...tool }) => tool);\n  }\n\n  private isHostOriginAllowed(origin: string | undefined): boolean {\n    if (this.allowedOrigins.includes('*')) {\n      return true;\n    }\n\n    if (!origin) {\n      return false;\n    }\n\n    return this.allowedOrigins.includes(origin);\n  }\n\n  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 (typeof raw === 'string') {\n      return raw;\n    }\n\n    if (Buffer.isBuffer(raw)) {\n      return raw.toString('utf8');\n    }\n\n    if (raw instanceof ArrayBuffer) {\n      return Buffer.from(raw).toString('utf8');\n    }\n\n    if (Array.isArray(raw)) {\n      return Buffer.concat(raw).toString('utf8');\n    }\n\n    return String(raw);\n  }\n}\n","/**\n * Parsed CLI options for relay startup.\n */\nexport interface CliOptions {\n  host: string;\n  port: number;\n  portExplicitlySet: boolean;\n  allowedOrigins: string[];\n  label?: string;\n  workspace?: string;\n  relayId?: string;\n}\n\n/**\n * Parses supported CLI flags for relay startup.\n */\nexport function parseCliOptions(argv: string[]): CliOptions {\n  const options: CliOptions = {\n    host: '127.0.0.1',\n    port: 9333,\n    portExplicitlySet: false,\n    // Permissive by default for zero-config local development — any browser page can connect.\n    // Use --widget-origin to restrict to trusted origins on shared machines or in production.\n    allowedOrigins: ['*'],\n  };\n\n  const readFlagValue = (flag: string, index: number): string => {\n    const next = argv[index + 1];\n    if (!next || next.startsWith('-')) {\n      throw new Error(`Missing value for ${flag}`);\n    }\n    return next;\n  };\n\n  for (let i = 0; i < argv.length; i++) {\n    const token = argv[i];\n    if (!token) {\n      continue;\n    }\n\n    if (token === '--host' || token === '-H') {\n      options.host = readFlagValue(token, i);\n      i += 1;\n      continue;\n    }\n\n    if (token === '--port' || token === '-p') {\n      const raw = readFlagValue(token, i);\n      i += 1;\n      const value = Number.parseInt(raw, 10);\n      if (!Number.isFinite(value) || value <= 0 || value > 65535) {\n        throw new Error(`Invalid port \"${raw}\". Port must be a number between 1 and 65535.`);\n      }\n      options.port = value;\n      options.portExplicitlySet = true;\n      continue;\n    }\n\n    if (token === '--widget-origin' || token === '--allowed-origin' || token === '--ws-origin') {\n      const raw = readFlagValue(token, i);\n      i += 1;\n      const split = raw\n        .split(',')\n        .map((part) => part.trim())\n        .filter(Boolean);\n\n      if (split.length > 0) {\n        options.allowedOrigins = split;\n      }\n      continue;\n    }\n\n    if (token === '--label') {\n      options.label = readFlagValue(token, i);\n      i += 1;\n      continue;\n    }\n\n    if (token === '--workspace') {\n      options.workspace = readFlagValue(token, i);\n      i += 1;\n      continue;\n    }\n\n    if (token === '--relay-id') {\n      options.relayId = readFlagValue(token, i);\n      i += 1;\n      continue;\n    }\n\n    if (token === '--help' || token === '-h') {\n      printHelp();\n      process.exit(0);\n    }\n\n    if (token.startsWith('-')) {\n      process.stderr.write(`[webmcp-local-relay] warn: unrecognized argument \"${token}\"\\n`);\n    } else {\n      process.stderr.write(\n        `[webmcp-local-relay] warn: unrecognized argument \"${token}\" (positional arguments are not supported)\\n`\n      );\n    }\n  }\n\n  return options;\n}\n\n/**\n * Prints CLI usage to stderr.\n */\nexport function printHelp(): void {\n  process.stderr.write(\n    [\n      'webmcp-local-relay',\n      '',\n      'Usage:',\n      '  webmcp-local-relay [--host 127.0.0.1] [--port 9333] [--widget-origin https://myapp.example.com]',\n      '',\n      'Options:',\n      '  --host, -H               Bind host for local websocket relay (default: 127.0.0.1)',\n      '  --port, -p               Preferred root port for the local relay cluster (default: 9333)',\n      '  --widget-origin          Allowed host page origin(s), comma-separated (default: *)',\n      '  --allowed-origin         Alias for --widget-origin',\n      '  --ws-origin              Alias for --widget-origin',\n      '  --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      '  --help, -h               Show help',\n      '',\n    ].join('\\n')\n  );\n}\n","import { toJsonSchemaCompat } from '@modelcontextprotocol/sdk/server/zod-json-schema-compat.js';\nimport { z } from 'zod/v4';\n\nexport type StaticToolInputShape = Record<string, z.ZodTypeAny>;\n\nexport const EMPTY_STATIC_TOOL_INPUT_SHAPE = {} satisfies StaticToolInputShape;\n\nexport const WEBMCP_CALL_TOOL_INPUT_SHAPE = {\n  name: z\n    .string()\n    .describe('The tool name to call. Use webmcp_list_tools to see available tool names.'),\n  arguments: z\n    .record(z.string(), z.unknown())\n    .optional()\n    .describe(\n      'Arguments to pass to the tool as a JSON object. Check the tool inputSchema from webmcp_list_tools for expected fields.'\n    ),\n} satisfies StaticToolInputShape;\n\nexport const WEBMCP_OPEN_PAGE_INPUT_SHAPE = {\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} satisfies StaticToolInputShape;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n  return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction stripJsonSchemaMetadata(value: unknown): unknown {\n  if (Array.isArray(value)) {\n    return value.map((item) => stripJsonSchemaMetadata(item));\n  }\n\n  if (!isRecord(value)) {\n    return value;\n  }\n\n  const next: Record<string, unknown> = {};\n  for (const [key, nestedValue] of Object.entries(value)) {\n    if (key === '$schema') {\n      continue;\n    }\n    next[key] = stripJsonSchemaMetadata(nestedValue);\n  }\n  return next;\n}\n\nexport function publicInputSchemaFromZodShape(\n  inputShape: StaticToolInputShape\n): Record<string, unknown> {\n  return stripJsonSchemaMetadata(\n    toJsonSchemaCompat(z.object(inputShape), {\n      strictUnions: true,\n      pipeStrategy: 'input',\n    }) as Record<string, unknown>\n  ) as Record<string, unknown>;\n}\n","import { execFile } from 'node:child_process';\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod/v4';\n\nimport { RelayBridgeServer, type RelayBridgeServerOptions } from './bridgeServer.js';\nimport type { AggregatedTool, SourceInfo } from './registry.js';\nimport {\n  EMPTY_STATIC_TOOL_INPUT_SHAPE,\n  publicInputSchemaFromZodShape,\n  type StaticToolInputShape,\n  WEBMCP_CALL_TOOL_INPUT_SHAPE,\n  WEBMCP_OPEN_PAGE_INPUT_SHAPE,\n} from './staticToolSchemas.js';\n\n/**\n * Handle returned by MCP tool registration.\n */\ninterface RegisteredToolHandle {\n  remove: () => void;\n}\n\ninterface StaticToolRegistration<T extends StaticToolInputShape = StaticToolInputShape> {\n  name: string;\n  description: string;\n  inputShape: T;\n  annotations?: unknown;\n  handler: (args: z.output<z.ZodObject<T>>) => Promise<Record<string, unknown>>;\n}\n\ninterface PublicToolMetadata {\n  name: string;\n  title?: string;\n  description?: string;\n  inputSchema: Record<string, unknown>;\n  outputSchema?: Record<string, unknown>;\n  annotations?: unknown;\n}\n\n/**\n * Base options shared by all {@link LocalRelayMcpServer} configurations.\n */\ninterface LocalRelayMcpServerBaseOptions {\n  /**\n   * MCP server name reported during initialization.\n   */\n  serverName?: string;\n  /**\n   * MCP server version reported during initialization.\n   */\n  serverVersion?: string;\n}\n\n/**\n * Construction options for {@link LocalRelayMcpServer}.\n *\n * Provide either an existing `bridge` instance OR `bridgeOptions` to create\n * one internally — not both.\n */\nexport type LocalRelayMcpServerOptions = LocalRelayMcpServerBaseOptions &\n  (\n    | { bridge: RelayBridgeServer; bridgeOptions?: never }\n    | { bridge?: never; bridgeOptions?: RelayBridgeServerOptions }\n  );\n\n/**\n * MCP server facade that exposes browser-relayed tools over MCP transport.\n */\nexport class LocalRelayMcpServer {\n  /**\n   * Underlying WebSocket bridge used for browser communication.\n   */\n  readonly bridge: RelayBridgeServer;\n\n  private readonly mcpServer: McpServer;\n  private readonly staticToolData = new Map<string, PublicToolMetadata>();\n  private readonly dynamicToolHandles = new Map<string, RegisteredToolHandle>();\n  private readonly dynamicToolData = new Map<string, AggregatedTool>();\n  private readonly dynamicToolSignature = new Map<string, string>();\n\n  private syncing = false;\n  private syncRequested = false;\n  private connected = false;\n\n  /**\n   * Creates a local relay MCP server with static and dynamic tool registration.\n   */\n  constructor(options: LocalRelayMcpServerOptions = {}) {\n    this.bridge = options.bridge ?? new RelayBridgeServer(options.bridgeOptions);\n\n    this.mcpServer = new McpServer({\n      name: options.serverName ?? 'webmcp-local-relay',\n      version: options.serverVersion ?? '0.0.0',\n    });\n\n    this.bridge.on('stateChanged', () => {\n      void this.syncDynamicTools().catch((err) => {\n        this.logSyncError(err);\n      });\n    });\n\n    // Forward elicitation requests from browser tool handlers to the MCP client.\n    this.bridge.on(\n      'elicitationRequest',\n      (request: { callId: string; connectionId: string; params: Record<string, unknown> }) => {\n        void (async () => {\n          try {\n            const result = await this.mcpServer.server.elicitInput(\n              request.params as Parameters<typeof this.mcpServer.server.elicitInput>[0]\n            );\n            this.bridge.sendElicitationResponse(\n              request.connectionId,\n              request.callId,\n              result as Record<string, unknown>\n            );\n          } catch (err) {\n            const message = err instanceof Error ? err.message : String(err);\n            process.stderr.write(\n              `[webmcp-local-relay] warn: elicitation forwarding failed: ${message}\\n`\n            );\n            this.bridge.sendElicitationResponse(request.connectionId, request.callId, {\n              action: 'decline',\n              content: null,\n              error: message,\n            });\n          }\n        })();\n      }\n    );\n\n    this.registerStaticTools();\n    this.overrideListToolsHandler();\n  }\n\n  /**\n   * Starts the browser bridge and synchronizes dynamic MCP tools.\n   */\n  async start(): Promise<void> {\n    await this.bridge.start();\n    await this.syncDynamicTools();\n  }\n\n  /**\n   * Connects the MCP server to a transport.\n   *\n   * This may be called exactly once per instance lifecycle.\n   */\n  async connect(transport: Transport): Promise<void> {\n    if (this.connected) {\n      throw new Error('MCP server transport already connected');\n    }\n\n    await this.mcpServer.connect(transport);\n    this.connected = true;\n\n    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    let mcpCloseError: unknown;\n    try {\n      await this.mcpServer.close();\n    } catch (err) {\n      mcpCloseError = err;\n      process.stderr.write(\n        `[webmcp-local-relay] error: failed to close MCP server: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n      );\n    }\n    await this.bridge.stop();\n    if (mcpCloseError) {\n      process.stderr.write(\n        '[webmcp-local-relay] warn: shutdown completed with errors (MCP server close failed)\\n'\n      );\n    }\n  }\n\n  /**\n   * Returns dynamic tool names currently registered in MCP.\n   */\n  listDynamicToolNames(): string[] {\n    return Array.from(this.dynamicToolHandles.keys()).sort();\n  }\n\n  /**\n   * Overrides the SDK's ListTools handler to return real JSON Schema\n   * for dynamic tools instead of the empty `z.object({}).passthrough()`\n   * schema that the SDK would otherwise convert to `{ type: 'object' }`.\n   *\n   * The MCP SDK's McpServer internally runs `toJsonSchemaCompat()` on\n   * registered inputSchema values, which strips plain JSON Schema objects\n   * (non-Zod) to empty schemas. This override bypasses that conversion\n   * for dynamic (relayed) tools while preserving static tools as-is.\n   */\n  private overrideListToolsHandler(): void {\n    this.mcpServer.server.setRequestHandler(ListToolsRequestSchema, () => {\n      const tools: Array<Record<string, unknown>> = [\n        ...Array.from(this.staticToolData.values()).map((tool) => ({ ...tool })),\n        ...Array.from(this.dynamicToolData.entries()).map(([name, tool]) => ({\n          name,\n          description: this.dynamicToolDescription(tool),\n          inputSchema: tool.inputSchema,\n          ...(tool.outputSchema ? { outputSchema: tool.outputSchema } : {}),\n          ...(tool.annotations ? { annotations: tool.annotations } : {}),\n        })),\n      ].sort((left, right) => String(left.name).localeCompare(String(right.name)));\n\n      return { tools };\n    });\n  }\n\n  private registerStaticTool<T extends StaticToolInputShape>({\n    name,\n    description,\n    inputShape,\n    annotations,\n    handler,\n  }: StaticToolRegistration<T>): void {\n    this.mcpServer.registerTool(\n      name,\n      {\n        description,\n        inputSchema: inputShape,\n        ...(annotations ? { annotations } : {}),\n      } as Parameters<McpServer['registerTool']>[1],\n      handler as Parameters<McpServer['registerTool']>[2]\n    );\n    this.staticToolData.set(name, {\n      name,\n      description,\n      inputSchema: publicInputSchemaFromZodShape(inputShape),\n      ...(annotations ? { annotations } : {}),\n    });\n  }\n\n  /**\n   * Registers built-in management tools exposed by the relay.\n   */\n  private registerStaticTools(): void {\n    this.registerStaticTool({\n      name: 'webmcp_list_sources',\n      description: 'List connected browser tool sources and their metadata.',\n      inputShape: EMPTY_STATIC_TOOL_INPUT_SHAPE,\n      annotations: {\n        readOnlyHint: true,\n        idempotentHint: true,\n      },\n      handler: async () => {\n        if (this.bridge.mode === 'client') {\n          const sources = this.bridge.listSourcesFromRelay();\n          const info = {\n            mode: 'client' as const,\n            count: sources.length,\n            sources,\n          };\n          return {\n            content: [{ type: 'text', text: JSON.stringify(info, null, 2) }],\n            structuredContent: info,\n          };\n        }\n        const sources = this.bridge.registry.listSources();\n        return {\n          content: [\n            { type: 'text', text: JSON.stringify({ count: sources.length, sources }, null, 2) },\n          ],\n          structuredContent: {\n            count: sources.length,\n            sources,\n          },\n        };\n      },\n    });\n\n    this.registerStaticTool({\n      name: 'webmcp_list_tools',\n      description:\n        'List WebMCP tools available from connected browser sources. Returns tool definitions including name, description, input schema, and source info. Use webmcp_call_tool to invoke a tool by name.',\n      inputShape: EMPTY_STATIC_TOOL_INPUT_SHAPE,\n      annotations: {\n        readOnlyHint: true,\n        idempotentHint: true,\n      },\n      handler: async () => {\n        const tools = this.listAggregatedTools();\n        return {\n          content: [\n            { type: 'text', text: JSON.stringify({ count: tools.length, tools }, null, 2) },\n          ],\n          structuredContent: {\n            count: tools.length,\n            tools,\n          },\n        };\n      },\n    });\n\n    this.registerStaticTool({\n      name: 'webmcp_call_tool',\n      description:\n        'Call a WebMCP tool registered by a connected browser page. Use webmcp_list_tools first to see available tools and their input schemas.',\n      inputShape: WEBMCP_CALL_TOOL_INPUT_SHAPE,\n      annotations: {\n        readOnlyHint: false,\n      },\n      handler: async ({ name, arguments: args }) => {\n        const tools = this.listAggregatedTools();\n        const toolSummary = this.buildToolSummary(tools);\n\n        const matched = tools.find((t) => t.name === name);\n        if (!matched) {\n          const errorLines = [`Tool \"${name}\" not found.`];\n          if (toolSummary) {\n            errorLines.push('', 'Available tools:', toolSummary);\n          } else {\n            errorLines.push(\n              '',\n              'No tools are currently available. Ensure a browser page with WebMCP is connected.'\n            );\n          }\n          return {\n            content: [{ type: 'text' as const, text: errorLines.join('\\n') }],\n            isError: true,\n          };\n        }\n\n        try {\n          const result = await this.bridge.invokeTool(name, args ?? {});\n\n          const updatedTools =\n            this.bridge.mode === 'client'\n              ? this.buildAggregatedToolsFromRelay()\n              : this.bridge.registry.listTools();\n          const updatedSummary = this.buildToolSummary(updatedTools);\n          if (updatedSummary) {\n            result.content = [\n              ...result.content,\n              { type: 'text' as const, text: `\\n---\\nAvailable tools:\\n${updatedSummary}` },\n            ];\n          }\n\n          return result;\n        } catch (err) {\n          const message = err instanceof Error ? err.message : String(err);\n          const errorLines = [`Failed to call tool \"${name}\": ${message}`];\n          if (toolSummary) {\n            errorLines.push('', 'Available tools:', toolSummary);\n          }\n          return {\n            content: [{ type: 'text' as const, text: errorLines.join('\\n') }],\n            isError: true,\n          };\n        }\n      },\n    });\n\n    this.registerStaticTool({\n      name: 'webmcp_open_page',\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      inputShape: WEBMCP_OPEN_PAGE_INPUT_SHAPE,\n      annotations: { readOnlyHint: false },\n      handler: async ({ url, refresh }) => {\n        let parsed: URL;\n        try {\n          parsed = new URL(url);\n        } catch {\n          return {\n            content: [{ type: 'text' as const, text: `Invalid URL: ${url}` }],\n            isError: true,\n          };\n        }\n\n        if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n          return {\n            content: [\n              {\n                type: 'text' as const,\n                text: `Only http: and https: URLs are allowed. Got: ${parsed.protocol}`,\n              },\n            ],\n            isError: true,\n          };\n        }\n\n        if (refresh) {\n          if (this.bridge.mode === 'client') {\n            return {\n              content: [\n                {\n                  type: 'text' as const,\n                  text: 'Refresh is not supported in client mode. Only the server relay can reload sources.',\n                },\n              ],\n              isError: true,\n            };\n          }\n\n          const sources = this.bridge.registry.listSources();\n          const matched = sources.find((s) => {\n            if (!s.url) return false;\n            try {\n              return new URL(s.url).origin === parsed.origin;\n            } catch {\n              process.stderr.write(\n                `[webmcp-local-relay] warn: source ${s.sourceId} has unparseable URL: ${s.url}\\n`\n              );\n              return false;\n            }\n          });\n\n          if (!matched) {\n            return {\n              content: [\n                {\n                  type: 'text' as const,\n                  text: `No connected source matches origin ${parsed.origin}. The page may not be open or connected.`,\n                },\n              ],\n              isError: true,\n            };\n          }\n\n          try {\n            this.bridge.reloadSource(matched.sourceId);\n            return {\n              content: [\n                {\n                  type: 'text' as const,\n                  text: `Reload sent to source ${matched.sourceId} (${matched.url ?? matched.origin}).`,\n                },\n              ],\n            };\n          } catch (err) {\n            return {\n              content: [\n                {\n                  type: 'text' as const,\n                  text: `Failed to reload source: ${err instanceof Error ? err.message : String(err)}`,\n                },\n              ],\n              isError: true,\n            };\n          }\n        }\n\n        try {\n          await this.openInBrowser(url);\n        } catch (err) {\n          return {\n            content: [\n              {\n                type: 'text' as const,\n                text: `Failed to open browser: ${err instanceof Error ? err.message : String(err)}`,\n              },\n            ],\n            isError: true,\n          };\n        }\n\n        if (this.bridge.mode === 'server') {\n          const sources = this.bridge.registry.listSources();\n          const existing = sources.find((s) => {\n            if (!s.url) return false;\n            try {\n              return new URL(s.url).origin === parsed.origin;\n            } catch {\n              process.stderr.write(\n                `[webmcp-local-relay] warn: source ${s.sourceId} has unparseable URL: ${s.url}\\n`\n              );\n              return false;\n            }\n          });\n\n          if (existing) {\n            return {\n              content: [\n                {\n                  type: 'text' as const,\n                  text: `Opened ${url} in the default browser. Note: a source from ${existing.url ?? existing.origin} is already connected.`,\n                },\n              ],\n            };\n          }\n        }\n\n        return {\n          content: [{ type: 'text' as const, text: `Opened ${url} in the default browser.` }],\n        };\n      },\n    });\n  }\n\n  /**\n   * Builds a concise plain-text list of available tools.\n   */\n  private buildToolSummary(tools: AggregatedTool[]): string | null {\n    if (tools.length === 0) {\n      return null;\n    }\n\n    return tools\n      .map((t) => {\n        const desc = t.description ? ` - ${t.description.split('\\n')[0]}` : '';\n        return `  ${t.name}${desc}`;\n      })\n      .join('\\n');\n  }\n\n  /**\n   * Opens a URL in the user's default browser using the platform open command.\n   *\n   * Uses the re-serialized `URL.href` to prevent injection of shell metacharacters.\n   * On Windows, PowerShell `Start-Process` is used instead of `cmd /c start`\n   * because cmd.exe interprets `&`, `|`, and other metacharacters in URLs.\n   */\n  private openInBrowser(url: string): Promise<void> {\n    const safeUrl = new URL(url).href;\n    return new Promise((resolve, reject) => {\n      const platform = process.platform;\n      let command: string;\n      let args: string[];\n      if (platform === 'darwin') {\n        command = 'open';\n        args = [safeUrl];\n      } else if (platform === 'win32') {\n        command = 'powershell.exe';\n        args = ['-NoProfile', '-Command', `Start-Process \"${safeUrl}\"`];\n      } else {\n        command = 'xdg-open';\n        args = [safeUrl];\n      }\n      execFile(command, args, (err) => {\n        if (err) reject(err);\n        else resolve();\n      });\n    });\n  }\n\n  /**\n   * Coalesces concurrent sync requests into a single serialized sync loop.\n   */\n  private async syncDynamicTools(): Promise<void> {\n    if (this.syncing) {\n      this.syncRequested = true;\n      return;\n    }\n\n    this.syncing = true;\n\n    try {\n      do {\n        this.syncRequested = false;\n        const tools = this.listAggregatedTools();\n        this.applyDynamicTools(tools);\n      } while (this.syncRequested);\n    } finally {\n      const retryRequested = this.syncRequested;\n      this.syncRequested = false;\n      this.syncing = false;\n      if (retryRequested) {\n        void this.syncDynamicTools().catch((err) => {\n          this.logSyncError(err);\n        });\n      }\n    }\n  }\n\n  /**\n   * Returns the current aggregated tool list, dispatching based on bridge mode.\n   */\n  private listAggregatedTools(): AggregatedTool[] {\n    return this.bridge.mode === 'client'\n      ? this.buildAggregatedToolsFromRelay()\n      : this.bridge.registry.listTools();\n  }\n\n  /**\n   * Converts relay client tool descriptors to aggregated tool shape.\n   * Populates per-tool source metadata from the relay's tool-source mapping.\n   */\n  private buildAggregatedToolsFromRelay(): AggregatedTool[] {\n    const toolSourceMap = this.bridge.getToolSourceMapFromRelay();\n    const allSources = this.bridge.listSourcesFromRelay();\n    const sourceById = new Map(allSources.map((s) => [s.sourceId, s]));\n\n    return this.bridge\n      .listToolsFromRelay()\n      .map((tool) => {\n        const sourceIds = toolSourceMap[tool.name] ?? [];\n        const sources: SourceInfo[] = [];\n        for (const id of sourceIds) {\n          const source = sourceById.get(id);\n          if (source) {\n            sources.push(source as SourceInfo);\n          } else {\n            process.stderr.write(\n              `[webmcp-local-relay] warn: tool \"${tool.name}\" references unknown sourceId \"${id}\"\\n`\n            );\n          }\n        }\n\n        return {\n          ...tool,\n          originalName: tool.name,\n          sources,\n        };\n      })\n      .sort((left, right) => left.name.localeCompare(right.name));\n  }\n\n  /**\n   * Applies registry tool state to MCP dynamic registrations.\n   */\n  private applyDynamicTools(tools: AggregatedTool[]): void {\n    const nextNames = new Set(tools.map((tool) => tool.name));\n    let changed = false;\n\n    for (const [name, handle] of this.dynamicToolHandles.entries()) {\n      if (nextNames.has(name)) {\n        continue;\n      }\n\n      handle.remove();\n      this.dynamicToolHandles.delete(name);\n      this.dynamicToolData.delete(name);\n      this.dynamicToolSignature.delete(name);\n      changed = true;\n    }\n\n    for (const tool of tools) {\n      const signature = this.toolSignature(tool);\n      const currentSignature = this.dynamicToolSignature.get(tool.name);\n\n      if (currentSignature === signature && this.dynamicToolHandles.has(tool.name)) {\n        continue;\n      }\n\n      const existing = this.dynamicToolHandles.get(tool.name);\n      if (existing) {\n        existing.remove();\n        this.dynamicToolHandles.delete(tool.name);\n      }\n\n      const handle = this.registerDynamicTool(tool);\n      this.dynamicToolHandles.set(tool.name, handle);\n      this.dynamicToolData.set(tool.name, tool);\n      this.dynamicToolSignature.set(tool.name, signature);\n      changed = true;\n    }\n\n    if (changed && this.connected) {\n      try {\n        this.mcpServer.sendToolListChanged();\n      } catch (err) {\n        process.stderr.write(\n          `[webmcp-local-relay] warn: failed to send tool list changed notification: ${err instanceof Error ? err.message : String(err)}\\n`\n        );\n      }\n    }\n  }\n\n  /**\n   * Registers a single dynamic tool and returns a removal handle.\n   */\n  private registerDynamicTool(tool: AggregatedTool): RegisteredToolHandle {\n    const inputSchema = z.object({}).passthrough();\n    const config = {\n      description: this.dynamicToolDescription(tool),\n      inputSchema,\n    };\n\n    const handle = this.mcpServer.registerTool(\n      tool.name,\n      tool.annotations ? { ...config, annotations: tool.annotations } : config,\n      async (args: Record<string, unknown>) => {\n        try {\n          return await this.bridge.invokeTool(tool.name, args);\n        } catch (err) {\n          const message = err instanceof Error ? err.message : String(err);\n          const details = err instanceof Error ? (err.stack ?? err.message) : String(err);\n          process.stderr.write(\n            `[webmcp-local-relay] error: dynamic tool \"${tool.name}\" invocation failed: ${details}\\n`\n          );\n          return {\n            content: [\n              {\n                type: 'text' as const,\n                text: `Failed to invoke relayed tool \"${tool.name}\": ${message}`,\n              },\n            ],\n            isError: true,\n          };\n        }\n      }\n    );\n\n    return {\n      remove: () => {\n        handle.remove();\n      },\n    };\n  }\n\n  /**\n   * Builds a display description for relayed tools including source context.\n   * In client mode, source metadata is unavailable, so tools are labeled `[WebMCP relay]`.\n   */\n  private dynamicToolDescription(tool: AggregatedTool): string {\n    const source = tool.sources[0];\n    const sourceLabel = source\n      ? `[WebMCP ${source.tabId}${source.title ? ` • ${source.title}` : ''}]`\n      : '[WebMCP relay]';\n\n    return `${sourceLabel} ${tool.description ?? `Relayed tool ${tool.originalName}`}`;\n  }\n\n  /**\n   * Produces a stable signature for change detection of dynamic tool metadata.\n   */\n  private toolSignature(tool: AggregatedTool): string {\n    return JSON.stringify({\n      name: tool.name,\n      originalName: tool.originalName,\n      title: tool.title,\n      description: tool.description,\n      inputSchema: tool.inputSchema,\n      outputSchema: tool.outputSchema,\n      annotations: tool.annotations,\n      execution: tool.execution,\n      _meta: tool._meta,\n      icons: tool.icons,\n      sources: tool.sources.map((source) => ({\n        sourceId: source.sourceId,\n        tabId: source.tabId,\n      })),\n    });\n  }\n\n  /**\n   * Logs tool sync errors with full detail.\n   */\n  private logSyncError(err: unknown): void {\n    const details = err instanceof Error ? (err.stack ?? err.message) : String(err);\n    process.stderr.write(`[webmcp-local-relay] error: failed to sync dynamic tools: ${details}\\n`);\n  }\n}\n"],"mappings":"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"}