@mcp-b/global 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["logger","properties: NonNullable<InputSchema['properties']>","required: string[]","zodToJsonSchemaLib","result: InputSchema","jsonSchemaToZod","composioJsonSchemaToZod","testingLogger","POLYFILL_MARKER_PROPERTY","validatedTool: ValidatedToolDescriptor","jsonSchemaToZod","normalizedContext: ModelContextInput","logger","args: Record<string, unknown>","validatedTool: ValidatedToolDescriptor","templateParams: string[]","argsSchema: InputSchema | undefined","argsValidator: z.ZodType | undefined","_exhaustive: never","parsedUri: URL","paramNames: string[]","params: Record<string, string>","validatedArgs: Record<string, unknown>","bridge","customTransport: Transport | undefined","McpServer","bridge: MCPBridge","options: WebModelContextInitOptions","tabServerOptions: TabServerConfig"],"sources":["../src/logger.ts","../src/tab-server-capabilities.ts","../src/validation.ts","../src/native-adapter.ts","../src/global.ts","../src/index.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2025 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Lightweight logging system for @mcp-b/global\n *\n * Design Decision: This implements a custom logger instead of using the 'debug'\n * package to reduce bundle size and eliminate external dependencies in the\n * browser build. The API is intentionally simpler, focusing on the specific\n * needs of browser-based MCP implementations.\n *\n * Configuration via localStorage:\n * - localStorage.setItem('WEBMCP_DEBUG', '*') - enable all debug logging\n * - localStorage.setItem('WEBMCP_DEBUG', 'WebModelContext') - enable specific namespace\n * - localStorage.setItem('WEBMCP_DEBUG', 'WebModelContext,NativeAdapter') - multiple namespaces\n * - localStorage.setItem('WEBMCP_DEBUG', 'WebModelContext:') - enable namespace and sub-namespaces\n * - localStorage.removeItem('WEBMCP_DEBUG') - disable debug logging (default)\n *\n * Environment Support:\n * - Automatically detects localStorage availability\n * - Gracefully degrades to \"disabled\" state when localStorage is inaccessible\n * - Never throws errors from configuration checks (safe for private browsing mode)\n */\n\n/** localStorage key for debug configuration */\nconst DEBUG_CONFIG_KEY = 'WEBMCP_DEBUG' as const;\n\n/**\n * Check if debug logging is enabled for a namespace\n *\n * Supports namespace hierarchy via colons. Setting 'WebModelContext' will match\n * both 'WebModelContext' and 'WebModelContext:init', but NOT 'WebModelContextTesting'.\n */\nfunction isDebugEnabled(namespace: string): boolean {\n if (typeof window === 'undefined' || !window.localStorage) {\n return false;\n }\n\n try {\n const debugConfig = localStorage.getItem(DEBUG_CONFIG_KEY);\n if (!debugConfig) return false;\n\n if (debugConfig === '*') return true;\n\n const patterns = debugConfig.split(',').map((p) => p.trim());\n return patterns.some((pattern) => namespace === pattern || namespace.startsWith(`${pattern}:`));\n } catch (err) {\n // localStorage might throw in some browsers (private mode, disabled storage)\n // Log once to console so developers know debug logging is disabled\n if (typeof console !== 'undefined' && console.warn) {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(`[WebMCP] localStorage access failed, debug logging disabled: ${message}`);\n }\n return false;\n }\n}\n\n/**\n * No-op function for disabled log levels\n */\nconst noop = (): void => {};\n\n/**\n * Logger interface with standard log levels\n *\n * All methods accept the same argument patterns as their console.* equivalents,\n * supporting format strings, object inspection, and multiple arguments.\n */\nexport interface Logger {\n /** Debug-level logging (disabled by default, enable via WEBMCP_DEBUG) */\n debug(message?: unknown, ...optionalParams: unknown[]): void;\n /** Info-level logging (disabled by default, enable via WEBMCP_DEBUG) */\n info(message?: unknown, ...optionalParams: unknown[]): void;\n /** Warning-level logging (enabled by default, not gated by WEBMCP_DEBUG) */\n warn(message?: unknown, ...optionalParams: unknown[]): void;\n /** Error-level logging (enabled by default, not gated by WEBMCP_DEBUG) */\n error(message?: unknown, ...optionalParams: unknown[]): void;\n}\n\n/**\n * Create a namespaced logger\n *\n * Uses .bind() to prepend namespace prefixes to console methods without manual\n * string concatenation. Debug enablement is determined at logger creation time\n * for performance - changes to localStorage after creation won't affect existing\n * loggers. Refresh the page to apply new WEBMCP_DEBUG settings.\n *\n * @param namespace - Namespace for the logger (e.g., 'WebModelContext', 'NativeAdapter')\n * @returns Logger instance with debug, info, warn, error methods\n *\n * @example\n * ```typescript\n * const logger = createLogger('WebModelContext');\n * logger.debug('Tool registered:', toolName); // Only shown if WEBMCP_DEBUG includes 'WebModelContext'\n * logger.error('Execution failed:', error); // Always enabled\n * ```\n */\nexport function createLogger(namespace: string): Logger {\n const prefix = `[${namespace}]`;\n\n // Note: Debug enablement is checked once at creation time for performance.\n // Changes to localStorage after creation won't affect existing loggers.\n const isDebug = isDebugEnabled(namespace);\n\n // Create bound console methods that include the namespace prefix\n const boundWarn = console.warn.bind(console, prefix);\n const boundError = console.error.bind(console, prefix);\n const boundLog = console.log.bind(console, prefix);\n\n return {\n // Warnings and errors are always enabled (production-safe)\n warn: boundWarn,\n error: boundError,\n\n // Debug and info are conditional - use bound methods or no-ops\n debug: isDebug ? boundLog : noop,\n info: isDebug ? boundLog : noop,\n };\n}\n","import type { Server as McpServer } from '@mcp-b/webmcp-ts-sdk';\nimport type {\n ElicitationParams,\n ElicitationResult,\n SamplingRequestParams,\n SamplingResult,\n} from './types.js';\n\ninterface TabServerCapabilities {\n createMessage?: (params: SamplingRequestParams) => Promise<SamplingResult>;\n elicitInput?: (params: ElicitationParams) => Promise<ElicitationResult>;\n}\n\ninterface ServerWithCapabilities {\n server?: TabServerCapabilities;\n}\n\nfunction getTabServerCapabilities(server: McpServer): TabServerCapabilities | undefined {\n return (server as unknown as ServerWithCapabilities).server;\n}\n\nexport function requireCreateMessageCapability(\n server: McpServer\n): (params: SamplingRequestParams) => Promise<SamplingResult> {\n const capabilities = getTabServerCapabilities(server);\n const createMessage = capabilities?.createMessage;\n if (!createMessage) {\n throw new Error('Sampling is not supported: no connected client with sampling capability');\n }\n return createMessage.bind(capabilities);\n}\n\nexport function requireElicitInputCapability(\n server: McpServer\n): (params: ElicitationParams) => Promise<ElicitationResult> {\n const capabilities = getTabServerCapabilities(server);\n const elicitInput = capabilities?.elicitInput;\n if (!elicitInput) {\n throw new Error(\n 'Elicitation is not supported: no connected client with elicitation capability'\n );\n }\n return elicitInput.bind(capabilities);\n}\n","import { jsonSchemaToZod as composioJsonSchemaToZod } from '@composio/json-schema-to-zod';\nimport { z } from 'zod';\nimport { zodToJsonSchema as zodToJsonSchemaLib } from 'zod-to-json-schema';\nimport { createLogger } from './logger.js';\nimport type { InputSchema, ZodSchemaObject } from './types.js';\n\nconst logger = createLogger('WebModelContext');\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst stripSchemaMeta = <T extends Record<string, unknown>>(schema: T): T => {\n const { $schema: _, ...rest } = schema as T & { $schema?: string };\n return rest as T;\n};\n\nconst isOptionalSchema = (schema: z.ZodTypeAny): boolean => {\n const typeName = (schema as { _def?: { typeName?: string } })._def?.typeName;\n return typeName === 'ZodOptional' || typeName === 'ZodDefault';\n};\n\nexport function isZodSchema(schema: unknown): schema is ZodSchemaObject {\n if (!isRecord(schema)) return false;\n if ('type' in schema && typeof schema.type === 'string') return false;\n const values = Object.values(schema);\n if (values.length === 0) return false;\n return values.some((v) => isRecord(v) && '_def' in v);\n}\n\nexport function zodToJsonSchema(schema: ZodSchemaObject): InputSchema {\n const properties: NonNullable<InputSchema['properties']> = {};\n const required: string[] = [];\n\n for (const [key, zodSchema] of Object.entries(schema)) {\n const propSchema = zodToJsonSchemaLib(zodSchema as z.ZodTypeAny, {\n strictUnions: true,\n $refStrategy: 'none',\n });\n\n properties[key] = stripSchemaMeta(propSchema as InputSchema);\n\n if (!isOptionalSchema(zodSchema)) {\n required.push(key);\n }\n }\n\n const result: InputSchema = { type: 'object', properties };\n if (required.length > 0) result.required = required;\n return result;\n}\n\nexport function jsonSchemaToZod(jsonSchema: InputSchema): z.ZodType {\n try {\n // Cast to unknown first since InputSchema is compatible at runtime but\n // has a more permissive type signature than the library expects\n const zodSchema = composioJsonSchemaToZod(\n jsonSchema as unknown as Parameters<typeof composioJsonSchemaToZod>[0]\n );\n return zodSchema as unknown as z.ZodType;\n } catch (error) {\n logger.warn('jsonSchemaToZod failed:', error);\n return z.object({}).passthrough();\n }\n}\n\nexport function normalizeSchema(schema: InputSchema | ZodSchemaObject): {\n jsonSchema: InputSchema;\n zodValidator: z.ZodType;\n} {\n if (isZodSchema(schema)) {\n const jsonSchema = zodToJsonSchema(schema);\n return { jsonSchema, zodValidator: jsonSchemaToZod(jsonSchema) };\n }\n\n return { jsonSchema: schema, zodValidator: jsonSchemaToZod(schema) };\n}\n\nexport function validateWithZod(\n data: unknown,\n validator: z.ZodType\n): { success: true; data: unknown } | { success: false; error: string } {\n const result = validator.safeParse(data);\n\n if (result.success) {\n return { success: true, data: result.data };\n }\n\n const errors = result.error.issues\n .map((err) => ` - ${err.path.join('.') || 'root'}: ${err.message}`)\n .join('\\n');\n\n return { success: false, error: `Validation failed:\\n${errors}` };\n}\n","import { createLogger } from './logger.js';\nimport {\n requireCreateMessageCapability,\n requireElicitInputCapability,\n} from './tab-server-capabilities.js';\nimport type {\n ElicitationParams,\n ElicitationResult,\n InputSchema,\n InternalModelContext,\n MCPBridge,\n ModelContext,\n ModelContextInput,\n ModelContextTesting,\n Prompt,\n PromptDescriptor,\n PromptMessage,\n Resource,\n ResourceContents,\n ResourceDescriptor,\n SamplingRequestParams,\n SamplingResult,\n ToolCallEvent,\n ToolDescriptor,\n ToolResponse,\n ValidatedToolDescriptor,\n ZodSchemaObject,\n} from './types.js';\nimport { jsonSchemaToZod, normalizeSchema } from './validation.js';\n\nconst nativeLogger = createLogger('NativeAdapter');\nconst testingLogger = createLogger('ModelContextTesting');\n\nconst POLYFILL_MARKER_PROPERTY = '__isWebMCPPolyfill' as const;\nconst CONSUMER_SHIM_MARKER_PROPERTY = '__webMCPConsumerShimInstalled' as const;\nconst CONSUMER_CALL_TOOL_SHIM_MARKER_PROPERTY = '__webMCPCallToolShimInstalled' as const;\nconst MODEL_CONTEXT_TESTING_DEPRECATION_MESSAGE =\n \"navigator.modelContextTesting is deprecated. Use navigator.modelContext.callTool() and addEventListener('toolschanged', ...) instead.\";\n\ninterface MayHavePolyfillMarker {\n [POLYFILL_MARKER_PROPERTY]?: true;\n}\n\ninterface MayHaveConsumerShimMarker {\n [CONSUMER_SHIM_MARKER_PROPERTY]?: true;\n [CONSUMER_CALL_TOOL_SHIM_MARKER_PROPERTY]?: true;\n}\n\n/**\n * Detect if the native Chromium Web Model Context API is available.\n * Checks for both navigator.modelContext and navigator.modelContextTesting,\n * and verifies they are native implementations (not polyfills).\n */\nexport function detectNativeAPI(): {\n hasNativeContext: boolean;\n hasNativeTesting: boolean;\n} {\n /* c8 ignore next 2 */\n if (typeof window === 'undefined' || typeof navigator === 'undefined') {\n return { hasNativeContext: false, hasNativeTesting: false };\n }\n\n const modelContext = navigator.modelContext;\n const modelContextTesting = navigator.modelContextTesting;\n\n if (!modelContext || !modelContextTesting) {\n return {\n hasNativeContext: Boolean(modelContext),\n hasNativeTesting: Boolean(modelContextTesting),\n };\n }\n\n const isPolyfill =\n POLYFILL_MARKER_PROPERTY in modelContextTesting &&\n (modelContextTesting as MayHavePolyfillMarker)[POLYFILL_MARKER_PROPERTY] === true;\n\n if (isPolyfill) {\n return { hasNativeContext: false, hasNativeTesting: false };\n }\n\n return { hasNativeContext: true, hasNativeTesting: true };\n}\n\n/**\n * Installs a deprecation getter for navigator.modelContextTesting.\n * Emits a warning on first access while preserving compatibility behavior.\n */\nexport function installDeprecatedTestingAccessor(modelContextTesting: ModelContextTesting): void {\n let hasWarned = false;\n\n try {\n Object.defineProperty(window.navigator, 'modelContextTesting', {\n configurable: true,\n enumerable: true,\n get() {\n if (!hasWarned) {\n testingLogger.warn(MODEL_CONTEXT_TESTING_DEPRECATION_MESSAGE);\n hasWarned = true;\n }\n return modelContextTesting;\n },\n });\n } catch (error) {\n testingLogger.warn('Failed to install modelContextTesting deprecation accessor:', error);\n }\n}\n\n/**\n * Dispatches a tools-changed event on a modelContext object.\n */\nfunction dispatchToolsChangedEvent(modelContext: ModelContext): void {\n try {\n modelContext.dispatchEvent(new Event('toolschanged'));\n } catch (error) {\n nativeLogger.warn('Failed to dispatch \"toolschanged\" event:', error);\n }\n}\n\n/**\n * Installs consumer methods on an existing modelContext object.\n * This allows native producer APIs to expose consumer semantics without replacing the object.\n */\nexport function installConsumerShim(\n nativeContext: ModelContext,\n callTool: (params: {\n name: string;\n arguments?: Record<string, unknown>;\n }) => Promise<ToolResponse>,\n options: { hasNativeTesting: boolean; onToolRegistryMutated?: () => void }\n): void {\n const target = nativeContext as ModelContext &\n MayHaveConsumerShimMarker & {\n callTool?: (params: {\n name: string;\n arguments?: Record<string, unknown>;\n }) => Promise<ToolResponse>;\n registerTool?: (...args: unknown[]) => unknown;\n provideContext?: (...args: unknown[]) => unknown;\n unregisterTool?: (...args: unknown[]) => unknown;\n clearContext?: (...args: unknown[]) => unknown;\n };\n\n if (target[CONSUMER_SHIM_MARKER_PROPERTY] === true) {\n return;\n }\n\n let installedCallToolShim = false;\n if (typeof target.callTool !== 'function') {\n try {\n Object.defineProperty(target, 'callTool', {\n configurable: true,\n writable: true,\n value: callTool,\n });\n installedCallToolShim = true;\n } catch (error) {\n nativeLogger.warn('Failed to install modelContext.callTool shim:', error);\n }\n }\n\n // If native testing callbacks are unavailable, patch producer mutation methods so\n // toolschanged listeners still receive notifications for same-page registrations.\n if (!options.hasNativeTesting) {\n const queueToolsChanged = () => {\n if (options.onToolRegistryMutated) {\n queueMicrotask(options.onToolRegistryMutated);\n return;\n }\n queueMicrotask(() => dispatchToolsChangedEvent(nativeContext));\n };\n\n const wrapMethod = (\n methodName: 'provideContext' | 'registerTool' | 'unregisterTool' | 'clearContext'\n ) => {\n const original = target[methodName];\n if (typeof original !== 'function') {\n return;\n }\n\n try {\n Object.defineProperty(target, methodName, {\n configurable: true,\n writable: true,\n value: (...args: unknown[]) => {\n const result = original.apply(target, args);\n\n if (\n methodName === 'registerTool' &&\n result &&\n typeof result === 'object' &&\n 'unregister' in result\n ) {\n const registration = result as { unregister?: () => void };\n if (typeof registration.unregister === 'function') {\n const originalUnregister = registration.unregister.bind(registration);\n registration.unregister = () => {\n originalUnregister();\n queueToolsChanged();\n };\n }\n }\n\n queueToolsChanged();\n return result;\n },\n });\n } catch (error) {\n nativeLogger.warn(`Failed to wrap modelContext.${methodName} for \"toolschanged\":`, error);\n }\n };\n\n wrapMethod('provideContext');\n wrapMethod('registerTool');\n wrapMethod('unregisterTool');\n wrapMethod('clearContext');\n }\n\n try {\n Object.defineProperty(target, CONSUMER_SHIM_MARKER_PROPERTY, {\n configurable: true,\n enumerable: false,\n writable: false,\n value: true,\n });\n if (installedCallToolShim) {\n Object.defineProperty(target, CONSUMER_CALL_TOOL_SHIM_MARKER_PROPERTY, {\n configurable: true,\n enumerable: false,\n writable: false,\n value: true,\n });\n }\n } catch {\n // Best-effort marker only.\n }\n}\n\n/**\n * Adapter that wraps the native Chromium Web Model Context API.\n * Synchronizes tool changes from the native API to the MCP bridge,\n * enabling MCP clients to stay in sync with the native tool registry.\n */\nexport class NativeModelContextAdapter implements InternalModelContext {\n private nativeContext: ModelContext;\n private nativeTesting: ModelContextTesting | undefined;\n private bridge: MCPBridge;\n private syncInProgress = false;\n private hasCompletedInitialToolSync = false;\n\n constructor(bridge: MCPBridge, nativeContext: ModelContext, nativeTesting?: ModelContextTesting) {\n this.bridge = bridge;\n this.nativeContext = nativeContext;\n this.nativeTesting = nativeTesting;\n\n if (this.nativeTesting) {\n this.nativeTesting.registerToolsChangedCallback(() => {\n this.syncToolsFromNative();\n });\n }\n\n this.syncToolsFromNative();\n }\n\n private syncToolsFromNative(): void {\n if (this.syncInProgress) {\n return;\n }\n\n this.syncInProgress = true;\n\n try {\n const nativeTestingTools = this.nativeTesting?.listTools();\n const nativeTools = this.nativeContext.listTools();\n\n this.bridge.tools.clear();\n\n const sourceTools = nativeTestingTools ?? nativeTools;\n for (const toolInfo of sourceTools) {\n try {\n const inputSchema =\n nativeTestingTools &&\n 'inputSchema' in toolInfo &&\n typeof toolInfo.inputSchema === 'string'\n ? (JSON.parse(toolInfo.inputSchema) as InputSchema)\n : ((toolInfo as { inputSchema: InputSchema }).inputSchema ?? { type: 'object' });\n\n const validatedTool: ValidatedToolDescriptor = {\n name: toolInfo.name,\n description: toolInfo.description,\n inputSchema,\n execute: async (args: Record<string, unknown>) => {\n return this.executeTool(toolInfo.name, args);\n },\n inputValidator: jsonSchemaToZod(inputSchema),\n };\n\n this.bridge.tools.set(toolInfo.name, validatedTool);\n } catch (error) {\n nativeLogger.error(`Failed to sync tool \"${toolInfo.name}\":`, error);\n }\n }\n\n this.notifyMCPServers();\n\n if (this.hasCompletedInitialToolSync) {\n dispatchToolsChangedEvent(this.nativeContext);\n } else {\n this.hasCompletedInitialToolSync = true;\n }\n } finally {\n this.syncInProgress = false;\n }\n }\n\n /**\n * Public refresh hook for consumer shims that detect native tool mutations\n * without modelContextTesting callbacks.\n */\n refreshToolsFromNative(): void {\n this.syncToolsFromNative();\n }\n\n private async executeToolViaNative(\n toolName: string,\n args: Record<string, unknown>\n ): Promise<ToolResponse> {\n const nativeWithConsumer = this.nativeContext as ModelContext &\n MayHaveConsumerShimMarker & {\n callTool?: (params: {\n name: string;\n arguments?: Record<string, unknown>;\n }) => Promise<unknown>;\n executeTool?: (name: string, args: Record<string, unknown>) => Promise<unknown>;\n };\n\n if (\n typeof nativeWithConsumer.callTool === 'function' &&\n nativeWithConsumer[CONSUMER_CALL_TOOL_SHIM_MARKER_PROPERTY] !== true\n ) {\n const result = await nativeWithConsumer.callTool({ name: toolName, arguments: args });\n if (\n result &&\n typeof result === 'object' &&\n 'content' in result &&\n Array.isArray((result as { content?: unknown }).content)\n ) {\n return result as ToolResponse;\n }\n return this.convertToToolResponse(result);\n }\n\n if (this.nativeTesting) {\n const result = await this.nativeTesting.executeTool(toolName, JSON.stringify(args));\n return this.convertToToolResponse(result);\n }\n\n if (typeof nativeWithConsumer.executeTool === 'function') {\n const result = await nativeWithConsumer.executeTool(toolName, args);\n return this.convertToToolResponse(result);\n }\n\n throw new Error(\n '[Native Adapter] Tool execution is not supported by this native implementation'\n );\n }\n\n private convertToToolResponse(result: unknown): ToolResponse {\n if (typeof result === 'string') {\n return { content: [{ type: 'text', text: result }] };\n }\n\n if (result === undefined || result === null) {\n return { content: [{ type: 'text', text: '' }] };\n }\n\n if (typeof result === 'object') {\n return {\n content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],\n structuredContent: result as Record<string, unknown>,\n };\n }\n\n return {\n content: [{ type: 'text', text: String(result) }],\n };\n }\n\n private notifyMCPServers(): void {\n if (this.bridge.tabServer?.notification) {\n this.bridge.tabServer.notification({\n method: 'notifications/tools/list_changed',\n params: {},\n });\n }\n\n if (this.bridge.iframeServer?.notification) {\n this.bridge.iframeServer.notification({\n method: 'notifications/tools/list_changed',\n params: {},\n });\n }\n }\n\n provideContext(context: ModelContextInput): void {\n const { tools, ...rest } = context;\n const normalizedContext: ModelContextInput = { ...rest };\n if (tools) {\n normalizedContext.tools = tools.map((tool) => ({\n ...tool,\n inputSchema: normalizeSchema(tool.inputSchema).jsonSchema,\n }));\n }\n this.nativeContext.provideContext(normalizedContext);\n }\n\n registerTool<\n TInputSchema extends ZodSchemaObject = Record<string, never>,\n TOutputSchema extends ZodSchemaObject = Record<string, never>,\n >(tool: ToolDescriptor<TInputSchema, TOutputSchema>): { unregister: () => void } {\n const normalizedTool = {\n ...tool,\n inputSchema: normalizeSchema(tool.inputSchema).jsonSchema,\n };\n const result = this.nativeContext.registerTool(normalizedTool);\n return result;\n }\n\n unregisterTool(name: string): void {\n this.nativeContext.unregisterTool(name);\n }\n\n clearContext(): void {\n this.nativeContext.clearContext();\n }\n\n async executeTool(\n toolName: string,\n args: Record<string, unknown>,\n _options?: { skipValidation?: boolean }\n ): Promise<ToolResponse> {\n try {\n return await this.executeToolViaNative(toolName, args);\n } catch (error) {\n nativeLogger.error(`Error executing tool \"${toolName}\":`, error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n listTools() {\n return Array.from(this.bridge.tools.values()).map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n ...(tool.outputSchema && { outputSchema: tool.outputSchema }),\n ...(tool.annotations && { annotations: tool.annotations }),\n }));\n }\n\n async callTool(params: {\n name: string;\n arguments?: Record<string, unknown>;\n }): Promise<ToolResponse> {\n if (!params?.name || typeof params.name !== 'string') {\n throw new Error('Tool name is required');\n }\n\n if (!this.bridge.tools.has(params.name)) {\n throw new Error(`Tool not found: ${params.name}`);\n }\n\n return this.executeTool(params.name, params.arguments ?? {});\n }\n\n registerResource(_resource: ResourceDescriptor): { unregister: () => void } {\n nativeLogger.warn('registerResource is not supported by native API');\n return { unregister: () => {} };\n }\n\n unregisterResource(_uri: string): void {\n nativeLogger.warn('unregisterResource is not supported by native API');\n }\n\n listResources(): Resource[] {\n return [];\n }\n\n listResourceTemplates(): Array<{\n uriTemplate: string;\n name: string;\n description?: string;\n mimeType?: string;\n }> {\n return [];\n }\n\n async readResource(_uri: string): Promise<{ contents: ResourceContents[] }> {\n throw new Error('[Native Adapter] readResource is not supported by native API');\n }\n\n registerPrompt<TArgsSchema extends ZodSchemaObject = Record<string, never>>(\n _prompt: PromptDescriptor<TArgsSchema>\n ): { unregister: () => void } {\n nativeLogger.warn('registerPrompt is not supported by native API');\n return { unregister: () => {} };\n }\n\n unregisterPrompt(_name: string): void {\n nativeLogger.warn('unregisterPrompt is not supported by native API');\n }\n\n listPrompts(): Prompt[] {\n return [];\n }\n\n async getPrompt(\n _name: string,\n _args?: Record<string, unknown>\n ): Promise<{ messages: PromptMessage[] }> {\n throw new Error('[Native Adapter] getPrompt is not supported by native API');\n }\n\n addEventListener(\n type: 'toolcall' | 'toolschanged',\n listener: ((event: ToolCallEvent) => void | Promise<void>) | (() => void),\n options?: boolean | AddEventListenerOptions\n ): void {\n if (type === 'toolcall') {\n this.nativeContext.addEventListener(\n 'toolcall',\n listener as (event: ToolCallEvent) => void | Promise<void>,\n options\n );\n return;\n }\n\n this.nativeContext.addEventListener('toolschanged', listener as () => void, options);\n }\n\n removeEventListener(\n type: 'toolcall' | 'toolschanged',\n listener: ((event: ToolCallEvent) => void | Promise<void>) | (() => void),\n options?: boolean | EventListenerOptions\n ): void {\n if (type === 'toolcall') {\n this.nativeContext.removeEventListener(\n 'toolcall',\n listener as (event: ToolCallEvent) => void | Promise<void>,\n options\n );\n return;\n }\n\n this.nativeContext.removeEventListener('toolschanged', listener as () => void, options);\n }\n\n dispatchEvent(event: Event): boolean {\n return this.nativeContext.dispatchEvent(event);\n }\n\n async createMessage(params: SamplingRequestParams): Promise<SamplingResult> {\n return requireCreateMessageCapability(this.bridge.tabServer)(params);\n }\n\n async elicitInput(params: ElicitationParams): Promise<ElicitationResult> {\n return requireElicitInputCapability(this.bridge.tabServer)(params);\n }\n}\n","import {\n IframeChildTransport,\n type IframeChildTransportOptions,\n TabServerTransport,\n type TabServerTransportOptions,\n} from '@mcp-b/transports';\nimport type {\n Prompt,\n PromptMessage,\n Resource,\n ResourceContents,\n Transport,\n} from '@mcp-b/webmcp-ts-sdk';\nimport {\n CallToolRequestSchema,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n Server as McpServer,\n ReadResourceRequestSchema,\n} from '@mcp-b/webmcp-ts-sdk';\nimport type { z } from 'zod';\nimport { createLogger } from './logger.js';\nimport {\n detectNativeAPI,\n installConsumerShim,\n installDeprecatedTestingAccessor,\n NativeModelContextAdapter,\n} from './native-adapter.js';\nimport {\n requireCreateMessageCapability,\n requireElicitInputCapability,\n} from './tab-server-capabilities.js';\nimport type {\n ElicitationParams,\n ElicitationResult,\n InputSchema,\n InternalModelContext,\n MCPBridge,\n ModelContextInput,\n ModelContextTesting,\n PromptDescriptor,\n ResourceDescriptor,\n SamplingRequestParams,\n SamplingResult,\n ToolCallEvent,\n ToolDescriptor,\n ToolResponse,\n ValidatedPromptDescriptor,\n ValidatedResourceDescriptor,\n ValidatedToolDescriptor,\n WebModelContextInitOptions,\n ZodSchemaObject,\n} from './types.js';\nimport { normalizeSchema, validateWithZod } from './validation.js';\n\n// Create namespaced loggers for different components\nconst logger = createLogger('WebModelContext');\nconst bridgeLogger = createLogger('MCPBridge');\nconst testingLogger = createLogger('ModelContextTesting');\n\ndeclare global {\n interface Window {\n __webModelContextOptions?: WebModelContextInitOptions;\n }\n}\n\n/**\n * Marker property name used to identify polyfill implementations.\n * This constant ensures single source of truth for the marker used in\n * both detection (detectNativeAPI) and definition (WebModelContextTesting).\n */\nconst POLYFILL_MARKER_PROPERTY = '__isWebMCPPolyfill' as const;\n\n/**\n * ToolCallEvent implementation for the Web Model Context API.\n * Represents an event fired when a tool is called, allowing event listeners\n * to intercept and provide custom responses.\n *\n * @class WebToolCallEvent\n * @extends {Event}\n * @implements {ToolCallEvent}\n */\nclass WebToolCallEvent extends Event implements ToolCallEvent {\n public name: string;\n public arguments: Record<string, unknown>;\n private _response: ToolResponse | null = null;\n private _responded = false;\n\n /**\n * Creates a new ToolCallEvent.\n *\n * @param {string} toolName - Name of the tool being called\n * @param {Record<string, unknown>} args - Validated arguments for the tool\n */\n constructor(toolName: string, args: Record<string, unknown>) {\n super('toolcall', { cancelable: true });\n this.name = toolName;\n this.arguments = args;\n }\n\n /**\n * Provides a response for this tool call, preventing the default tool execution.\n *\n * @param {ToolResponse} response - The response to use instead of executing the tool\n * @throws {Error} If a response has already been provided\n */\n respondWith(response: ToolResponse): void {\n if (this._responded) {\n throw new Error('Response already provided for this tool call');\n }\n this._response = response;\n this._responded = true;\n }\n\n /**\n * Gets the response provided via respondWith().\n *\n * @returns {ToolResponse | null} The response, or null if none provided\n */\n getResponse(): ToolResponse | null {\n return this._response;\n }\n\n /**\n * Checks whether a response has been provided for this tool call.\n *\n * @returns {boolean} True if respondWith() was called\n */\n hasResponse(): boolean {\n return this._responded;\n }\n}\n\n/**\n * Time window in milliseconds to detect rapid duplicate tool registrations.\n * Used to filter out double-registrations caused by React Strict Mode.\n */\nconst RAPID_DUPLICATE_WINDOW_MS = 50;\n\n/**\n * Types of lists that can trigger change notifications.\n * Single source of truth for notification batching logic.\n */\ntype ListChangeType = 'tools' | 'resources' | 'prompts';\n\n/**\n * Testing API implementation for the Model Context Protocol.\n * Provides debugging, mocking, and testing capabilities for tool execution.\n * Implements both Chromium native methods and polyfill-specific extensions.\n *\n * @class WebModelContextTesting\n * @implements {ModelContextTesting}\n */\nclass WebModelContextTesting implements ModelContextTesting {\n /**\n * Marker property to identify this as a polyfill implementation.\n * Used by detectNativeAPI() to distinguish polyfill from native Chromium API.\n * This approach works reliably even when class names are minified in production builds.\n *\n * @see POLYFILL_MARKER_PROPERTY - The constant defining this property name\n * @see detectNativeAPI in native-adapter.ts - native/polyfill detection logic\n */\n readonly [POLYFILL_MARKER_PROPERTY] = true as const;\n\n private toolCallHistory: Array<{\n toolName: string;\n arguments: Record<string, unknown>;\n timestamp: number;\n }> = [];\n private mockResponses: Map<string, ToolResponse> = new Map();\n private toolsChangedCallbacks: Set<() => void> = new Set();\n private bridge: MCPBridge;\n\n /**\n * Creates a new WebModelContextTesting instance.\n *\n * @param {MCPBridge} bridge - The MCP bridge instance to test\n */\n constructor(bridge: MCPBridge) {\n this.bridge = bridge;\n }\n\n /**\n * Records a tool call in the history.\n * Called internally by WebModelContext when tools are executed.\n *\n * @param {string} toolName - Name of the tool that was called\n * @param {Record<string, unknown>} args - Arguments passed to the tool\n * @internal\n */\n recordToolCall(toolName: string, args: Record<string, unknown>): void {\n this.toolCallHistory.push({\n toolName,\n arguments: args,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Checks if a mock response is registered for a specific tool.\n *\n * @param {string} toolName - Name of the tool to check\n * @returns {boolean} True if a mock response exists\n * @internal\n */\n hasMockResponse(toolName: string): boolean {\n return this.mockResponses.has(toolName);\n }\n\n /**\n * Retrieves the mock response for a specific tool.\n *\n * @param {string} toolName - Name of the tool\n * @returns {ToolResponse | undefined} The mock response, or undefined if none exists\n * @internal\n */\n getMockResponse(toolName: string): ToolResponse | undefined {\n return this.mockResponses.get(toolName);\n }\n\n /**\n * Notifies all registered callbacks that the tools list has changed.\n * Called internally when tools are registered, unregistered, or cleared.\n *\n * @internal\n */\n notifyToolsChanged(): void {\n for (const callback of this.toolsChangedCallbacks) {\n try {\n callback();\n } catch (error) {\n testingLogger.error('Error in tools changed callback:', error);\n }\n }\n }\n\n /**\n * Executes a tool directly with JSON string input (Chromium native API).\n * Parses the JSON input, validates it, and executes the tool.\n *\n * @param {string} toolName - Name of the tool to execute\n * @param {string} inputArgsJson - JSON string of input arguments\n * @returns {Promise<unknown>} The tool's result, or undefined on error\n * @throws {SyntaxError} If the input JSON is invalid\n * @throws {Error} If the tool does not exist\n */\n async executeTool(toolName: string, inputArgsJson: string): Promise<unknown> {\n let args: Record<string, unknown>;\n try {\n args = JSON.parse(inputArgsJson);\n } catch (error) {\n throw new SyntaxError(\n `Invalid JSON input: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n const tool = this.bridge.tools.get(toolName);\n if (!tool) {\n throw new Error(`Tool not found: ${toolName}`);\n }\n\n const result = await this.bridge.modelContext.executeTool(toolName, args);\n\n if (result.isError) {\n return undefined;\n }\n\n if (result.structuredContent) {\n return result.structuredContent;\n }\n\n if (result.content && result.content.length > 0) {\n const firstContent = result.content[0];\n if (firstContent && firstContent.type === 'text') {\n return firstContent.text;\n }\n }\n\n return undefined;\n }\n\n /**\n * Lists all registered tools with inputSchema as JSON string (Chromium native API).\n * Returns an array of ToolInfo objects where inputSchema is stringified.\n *\n * @returns {Array<{name: string, description: string, inputSchema: string}>} Array of tool information\n */\n listTools(): Array<{ name: string; description: string; inputSchema: string }> {\n const tools = this.bridge.modelContext.listTools();\n return tools.map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: JSON.stringify(tool.inputSchema),\n }));\n }\n\n /**\n * Registers a callback that fires when the tools list changes (Chromium native API).\n * The callback will be invoked on registerTool, unregisterTool, provideContext, and clearContext.\n *\n * @param {() => void} callback - Function to call when tools change\n */\n registerToolsChangedCallback(callback: () => void): void {\n this.toolsChangedCallbacks.add(callback);\n }\n\n /**\n * Gets all tool calls that have been recorded (polyfill extension).\n *\n * @returns {Array<{toolName: string, arguments: Record<string, unknown>, timestamp: number}>} Tool call history\n */\n getToolCalls(): Array<{\n toolName: string;\n arguments: Record<string, unknown>;\n timestamp: number;\n }> {\n return [...this.toolCallHistory];\n }\n\n /**\n * Clears the tool call history (polyfill extension).\n */\n clearToolCalls(): void {\n this.toolCallHistory = [];\n }\n\n /**\n * Sets a mock response for a specific tool (polyfill extension).\n * When set, the tool's execute function will be bypassed.\n *\n * @param {string} toolName - Name of the tool to mock\n * @param {ToolResponse} response - The mock response to return\n */\n setMockToolResponse(toolName: string, response: ToolResponse): void {\n this.mockResponses.set(toolName, response);\n }\n\n /**\n * Clears the mock response for a specific tool (polyfill extension).\n *\n * @param {string} toolName - Name of the tool\n */\n clearMockToolResponse(toolName: string): void {\n this.mockResponses.delete(toolName);\n }\n\n /**\n * Clears all mock tool responses (polyfill extension).\n */\n clearAllMockToolResponses(): void {\n this.mockResponses.clear();\n }\n\n /**\n * Gets the current tools registered in the system (polyfill extension).\n *\n * @returns {ReturnType<InternalModelContext['listTools']>} Array of registered tools\n */\n getRegisteredTools(): ReturnType<InternalModelContext['listTools']> {\n return this.bridge.modelContext.listTools();\n }\n\n /**\n * Resets the entire testing state (polyfill extension).\n * Clears both tool call history and all mock responses.\n */\n reset(): void {\n this.clearToolCalls();\n this.clearAllMockToolResponses();\n }\n}\n\n/**\n * ModelContext implementation that bridges to the Model Context Protocol SDK.\n * Implements the W3C Web Model Context API proposal with two-bucket tool management:\n * - Bucket A (provideContextTools): Tools registered via provideContext()\n * - Bucket B (dynamicTools): Tools registered via registerTool()\n *\n * This separation ensures that component-scoped dynamic tools persist across\n * app-level provideContext() calls.\n *\n * @class WebModelContext\n * @implements {InternalModelContext}\n */\nclass WebModelContext implements InternalModelContext {\n private bridge: MCPBridge;\n private eventTarget: EventTarget;\n\n // Tool storage (Bucket A = provideContext, Bucket B = dynamic)\n private provideContextTools: Map<string, ValidatedToolDescriptor>;\n private dynamicTools: Map<string, ValidatedToolDescriptor>;\n\n // Resource storage (Bucket A = provideContext, Bucket B = dynamic)\n private provideContextResources: Map<string, ValidatedResourceDescriptor>;\n private dynamicResources: Map<string, ValidatedResourceDescriptor>;\n\n // Prompt storage (Bucket A = provideContext, Bucket B = dynamic)\n private provideContextPrompts: Map<string, ValidatedPromptDescriptor>;\n private dynamicPrompts: Map<string, ValidatedPromptDescriptor>;\n\n // Registration tracking for duplicate detection\n private toolRegistrationTimestamps: Map<string, number>;\n private resourceRegistrationTimestamps: Map<string, number>;\n private promptRegistrationTimestamps: Map<string, number>;\n\n // Unregister functions for dynamic registrations\n private toolUnregisterFunctions: Map<string, () => void>;\n private resourceUnregisterFunctions: Map<string, () => void>;\n private promptUnregisterFunctions: Map<string, () => void>;\n\n /**\n * Tracks which list change notifications are pending.\n * Uses microtask-based batching to coalesce rapid registrations\n * (e.g., React mount phase) into a single notification per list type.\n */\n private pendingNotifications = new Set<ListChangeType>();\n\n private testingAPI?: WebModelContextTesting;\n\n /**\n * Creates a new WebModelContext instance.\n *\n * @param {MCPBridge} bridge - The MCP bridge to use for communication\n */\n constructor(bridge: MCPBridge) {\n this.bridge = bridge;\n this.eventTarget = new EventTarget();\n\n // Initialize tool storage\n this.provideContextTools = new Map();\n this.dynamicTools = new Map();\n this.toolRegistrationTimestamps = new Map();\n this.toolUnregisterFunctions = new Map();\n\n // Initialize resource storage\n this.provideContextResources = new Map();\n this.dynamicResources = new Map();\n this.resourceRegistrationTimestamps = new Map();\n this.resourceUnregisterFunctions = new Map();\n\n // Initialize prompt storage\n this.provideContextPrompts = new Map();\n this.dynamicPrompts = new Map();\n this.promptRegistrationTimestamps = new Map();\n this.promptUnregisterFunctions = new Map();\n }\n\n /**\n * Sets the testing API instance.\n * Called during initialization to enable testing features.\n *\n * @param {WebModelContextTesting} testingAPI - The testing API instance\n * @internal\n */\n setTestingAPI(testingAPI: WebModelContextTesting): void {\n this.testingAPI = testingAPI;\n }\n\n addEventListener(\n type: 'toolcall' | 'toolschanged',\n listener: ((event: ToolCallEvent) => void | Promise<void>) | (() => void),\n options?: boolean | AddEventListenerOptions\n ): void {\n this.eventTarget.addEventListener(type, listener as EventListener, options);\n }\n\n removeEventListener(\n type: 'toolcall' | 'toolschanged',\n listener: ((event: ToolCallEvent) => void | Promise<void>) | (() => void),\n options?: boolean | EventListenerOptions\n ): void {\n this.eventTarget.removeEventListener(type, listener as EventListener, options);\n }\n\n /**\n * Dispatches a tool call event to all registered listeners.\n *\n * @param {Event} event - The event to dispatch\n * @returns {boolean} False if event was cancelled, true otherwise\n */\n dispatchEvent(event: Event): boolean {\n return this.eventTarget.dispatchEvent(event);\n }\n\n /**\n * Provides context (tools, resources, prompts) to AI models by registering base items (Bucket A).\n * Clears and replaces all previously registered base items while preserving\n * dynamic items registered via register* methods.\n *\n * @param {ModelContextInput} context - Context containing tools, resources, and prompts to register\n * @throws {Error} If a name/uri collides with existing dynamic items\n */\n provideContext(context: ModelContextInput): void {\n // Clear base items (Bucket A)\n this.provideContextTools.clear();\n this.provideContextResources.clear();\n this.provideContextPrompts.clear();\n\n // Register tools\n for (const tool of context.tools ?? []) {\n // Validate tool name and log warnings for potential compatibility issues\n // NOTE: Similar validation exists in @mcp-b/chrome-devtools-mcp/src/tools/WebMCPToolHub.ts\n // Keep both implementations in sync when making changes.\n if (tool.name.startsWith('_')) {\n logger.warn(\n `⚠️ Warning: Tool name \"${tool.name}\" starts with underscore. ` +\n 'This may cause compatibility issues with some MCP clients. ' +\n 'Consider using a letter as the first character.'\n );\n }\n if (/^[0-9]/.test(tool.name)) {\n logger.warn(\n `⚠️ Warning: Tool name \"${tool.name}\" starts with a number. ` +\n 'This may cause compatibility issues. ' +\n 'Consider using a letter as the first character.'\n );\n }\n if (tool.name.startsWith('-')) {\n logger.warn(\n `⚠️ Warning: Tool name \"${tool.name}\" starts with hyphen. ` +\n 'This may cause compatibility issues. ' +\n 'Consider using a letter as the first character.'\n );\n }\n\n if (this.dynamicTools.has(tool.name)) {\n throw new Error(\n `[Web Model Context] Tool name collision: \"${tool.name}\" is already registered via registerTool(). ` +\n 'Please use a different name or unregister the dynamic tool first.'\n );\n }\n\n const { jsonSchema: inputJson, zodValidator: inputZod } = normalizeSchema(tool.inputSchema);\n const normalizedOutput = tool.outputSchema ? normalizeSchema(tool.outputSchema) : null;\n\n const validatedTool: ValidatedToolDescriptor = {\n name: tool.name,\n description: tool.description,\n inputSchema: inputJson,\n ...(normalizedOutput && { outputSchema: normalizedOutput.jsonSchema }),\n ...(tool.annotations && { annotations: tool.annotations }),\n execute: tool.execute,\n inputValidator: inputZod,\n ...(normalizedOutput && { outputValidator: normalizedOutput.zodValidator }),\n };\n\n this.provideContextTools.set(tool.name, validatedTool);\n }\n\n // Register resources\n for (const resource of context.resources ?? []) {\n if (this.dynamicResources.has(resource.uri)) {\n throw new Error(\n `[Web Model Context] Resource URI collision: \"${resource.uri}\" is already registered via registerResource(). ` +\n 'Please use a different URI or unregister the dynamic resource first.'\n );\n }\n\n const validatedResource = this.validateResource(resource);\n this.provideContextResources.set(resource.uri, validatedResource);\n }\n\n // Register prompts\n for (const prompt of context.prompts ?? []) {\n if (this.dynamicPrompts.has(prompt.name)) {\n throw new Error(\n `[Web Model Context] Prompt name collision: \"${prompt.name}\" is already registered via registerPrompt(). ` +\n 'Please use a different name or unregister the dynamic prompt first.'\n );\n }\n\n const validatedPrompt = this.validatePrompt(prompt);\n this.provideContextPrompts.set(prompt.name, validatedPrompt);\n }\n\n // Update bridge and schedule notifications (batched via microtask)\n this.updateBridgeTools();\n this.updateBridgeResources();\n this.updateBridgePrompts();\n\n this.scheduleListChanged('tools');\n this.scheduleListChanged('resources');\n this.scheduleListChanged('prompts');\n }\n\n /**\n * Validates and normalizes a resource descriptor.\n * @private\n */\n private validateResource(resource: ResourceDescriptor): ValidatedResourceDescriptor {\n // Extract template parameters from URI (e.g., \"file://{path}\" -> [\"path\"])\n // Limit parameter name length to 100 chars to prevent ReDoS on malicious input\n const templateParamRegex = /\\{([^}]{1,100})\\}/g;\n const templateParams: string[] = [];\n for (const match of resource.uri.matchAll(templateParamRegex)) {\n const paramName = match[1];\n if (typeof paramName === 'string') {\n templateParams.push(paramName);\n }\n }\n\n return {\n uri: resource.uri,\n name: resource.name,\n description: resource.description,\n mimeType: resource.mimeType,\n read: resource.read,\n isTemplate: templateParams.length > 0,\n templateParams,\n };\n }\n\n /**\n * Validates and normalizes a prompt descriptor.\n * @private\n */\n private validatePrompt<TArgsSchema extends ZodSchemaObject>(\n prompt: PromptDescriptor<TArgsSchema>\n ): ValidatedPromptDescriptor {\n let argsSchema: InputSchema | undefined;\n let argsValidator: z.ZodType | undefined;\n\n if (prompt.argsSchema) {\n const normalized = normalizeSchema(prompt.argsSchema);\n argsSchema = normalized.jsonSchema;\n argsValidator = normalized.zodValidator;\n }\n\n return {\n name: prompt.name,\n description: prompt.description,\n argsSchema,\n get: prompt.get as (args: Record<string, unknown>) => Promise<{ messages: PromptMessage[] }>,\n argsValidator,\n };\n }\n\n /**\n * Registers a single tool dynamically (Bucket B).\n * Dynamic tools persist across provideContext() calls and can be independently managed.\n *\n * @param {ToolDescriptor} tool - The tool descriptor to register\n * @returns {{unregister: () => void}} Object with unregister function\n * @throws {Error} If tool name collides with existing tools\n */\n registerTool<\n TInputSchema extends ZodSchemaObject = Record<string, never>,\n TOutputSchema extends ZodSchemaObject = Record<string, never>,\n >(tool: ToolDescriptor<TInputSchema, TOutputSchema>): { unregister: () => void } {\n // Validate tool name and log warnings for potential compatibility issues\n // NOTE: Similar validation exists in @mcp-b/chrome-devtools-mcp/src/tools/WebMCPToolHub.ts\n // Keep both implementations in sync when making changes.\n if (tool.name.startsWith('_')) {\n logger.warn(\n `⚠️ Warning: Tool name \"${tool.name}\" starts with underscore. ` +\n 'This may cause compatibility issues with some MCP clients. ' +\n 'Consider using a letter as the first character.'\n );\n }\n if (/^[0-9]/.test(tool.name)) {\n logger.warn(\n `⚠️ Warning: Tool name \"${tool.name}\" starts with a number. ` +\n 'This may cause compatibility issues. ' +\n 'Consider using a letter as the first character.'\n );\n }\n if (tool.name.startsWith('-')) {\n logger.warn(\n `⚠️ Warning: Tool name \"${tool.name}\" starts with hyphen. ` +\n 'This may cause compatibility issues. ' +\n 'Consider using a letter as the first character.'\n );\n }\n\n const now = Date.now();\n const lastRegistration = this.toolRegistrationTimestamps.get(tool.name);\n\n if (lastRegistration && now - lastRegistration < RAPID_DUPLICATE_WINDOW_MS) {\n logger.warn(\n `Tool \"${tool.name}\" registered multiple times within ${RAPID_DUPLICATE_WINDOW_MS}ms. ` +\n 'This is likely due to React Strict Mode double-mounting. Ignoring duplicate registration.'\n );\n\n const existingUnregister = this.toolUnregisterFunctions.get(tool.name);\n if (existingUnregister) {\n return { unregister: existingUnregister };\n }\n }\n\n if (this.provideContextTools.has(tool.name)) {\n throw new Error(\n `[Web Model Context] Tool name collision: \"${tool.name}\" is already registered via provideContext(). ` +\n 'Please use a different name or update your provideContext() call.'\n );\n }\n\n if (this.dynamicTools.has(tool.name)) {\n throw new Error(\n `[Web Model Context] Tool name collision: \"${tool.name}\" is already registered via registerTool(). ` +\n 'Please unregister it first or use a different name.'\n );\n }\n\n const { jsonSchema: inputJson, zodValidator: inputZod } = normalizeSchema(tool.inputSchema);\n\n const normalizedOutput = tool.outputSchema ? normalizeSchema(tool.outputSchema) : null;\n\n const validatedTool: ValidatedToolDescriptor = {\n name: tool.name,\n description: tool.description,\n inputSchema: inputJson,\n ...(normalizedOutput && { outputSchema: normalizedOutput.jsonSchema }),\n ...(tool.annotations && { annotations: tool.annotations }),\n execute: tool.execute as (args: Record<string, unknown>) => Promise<ToolResponse>,\n inputValidator: inputZod,\n ...(normalizedOutput && { outputValidator: normalizedOutput.zodValidator }),\n };\n\n this.dynamicTools.set(tool.name, validatedTool);\n this.toolRegistrationTimestamps.set(tool.name, now);\n this.updateBridgeTools();\n this.scheduleListChanged('tools');\n\n const unregisterFn = () => {\n if (this.provideContextTools.has(tool.name)) {\n throw new Error(\n `[Web Model Context] Cannot unregister tool \"${tool.name}\": ` +\n 'This tool was registered via provideContext(). Use provideContext() to update the base tool set.'\n );\n }\n\n if (!this.dynamicTools.has(tool.name)) {\n logger.warn(`Tool \"${tool.name}\" is not registered, ignoring unregister call`);\n return;\n }\n\n this.dynamicTools.delete(tool.name);\n this.toolRegistrationTimestamps.delete(tool.name);\n this.toolUnregisterFunctions.delete(tool.name);\n this.updateBridgeTools();\n this.scheduleListChanged('tools');\n };\n\n this.toolUnregisterFunctions.set(tool.name, unregisterFn);\n\n return { unregister: unregisterFn };\n }\n\n // ==================== RESOURCE METHODS ====================\n\n /**\n * Registers a single resource dynamically (Bucket B).\n * Dynamic resources persist across provideContext() calls and can be independently managed.\n *\n * @param {ResourceDescriptor} resource - The resource descriptor to register\n * @returns {{unregister: () => void}} Object with unregister function\n * @throws {Error} If resource URI collides with existing resources\n */\n registerResource(resource: ResourceDescriptor): { unregister: () => void } {\n const now = Date.now();\n const lastRegistration = this.resourceRegistrationTimestamps.get(resource.uri);\n\n if (lastRegistration && now - lastRegistration < RAPID_DUPLICATE_WINDOW_MS) {\n logger.warn(\n `Resource \"${resource.uri}\" registered multiple times within ${RAPID_DUPLICATE_WINDOW_MS}ms. ` +\n 'This is likely due to React Strict Mode double-mounting. Ignoring duplicate registration.'\n );\n\n const existingUnregister = this.resourceUnregisterFunctions.get(resource.uri);\n if (existingUnregister) {\n return { unregister: existingUnregister };\n }\n }\n\n if (this.provideContextResources.has(resource.uri)) {\n throw new Error(\n `[Web Model Context] Resource URI collision: \"${resource.uri}\" is already registered via provideContext(). ` +\n 'Please use a different URI or update your provideContext() call.'\n );\n }\n\n if (this.dynamicResources.has(resource.uri)) {\n throw new Error(\n `[Web Model Context] Resource URI collision: \"${resource.uri}\" is already registered via registerResource(). ` +\n 'Please unregister it first or use a different URI.'\n );\n }\n\n const validatedResource = this.validateResource(resource);\n this.dynamicResources.set(resource.uri, validatedResource);\n this.resourceRegistrationTimestamps.set(resource.uri, now);\n this.updateBridgeResources();\n this.scheduleListChanged('resources');\n\n const unregisterFn = () => {\n if (this.provideContextResources.has(resource.uri)) {\n throw new Error(\n `[Web Model Context] Cannot unregister resource \"${resource.uri}\": ` +\n 'This resource was registered via provideContext(). Use provideContext() to update the base resource set.'\n );\n }\n\n if (!this.dynamicResources.has(resource.uri)) {\n logger.warn(`Resource \"${resource.uri}\" is not registered, ignoring unregister call`);\n return;\n }\n\n this.dynamicResources.delete(resource.uri);\n this.resourceRegistrationTimestamps.delete(resource.uri);\n this.resourceUnregisterFunctions.delete(resource.uri);\n this.updateBridgeResources();\n this.scheduleListChanged('resources');\n };\n\n this.resourceUnregisterFunctions.set(resource.uri, unregisterFn);\n\n return { unregister: unregisterFn };\n }\n\n /**\n * Unregisters a resource by URI.\n * Can unregister resources from either Bucket A (provideContext) or Bucket B (registerResource).\n *\n * @param {string} uri - URI of the resource to unregister\n */\n unregisterResource(uri: string): void {\n const inProvideContext = this.provideContextResources.has(uri);\n const inDynamic = this.dynamicResources.has(uri);\n\n if (!inProvideContext && !inDynamic) {\n logger.warn(`Resource \"${uri}\" is not registered, ignoring unregister call`);\n return;\n }\n\n if (inProvideContext) {\n this.provideContextResources.delete(uri);\n }\n\n if (inDynamic) {\n this.dynamicResources.delete(uri);\n this.resourceRegistrationTimestamps.delete(uri);\n this.resourceUnregisterFunctions.delete(uri);\n }\n\n this.updateBridgeResources();\n this.scheduleListChanged('resources');\n }\n\n /**\n * Lists all registered resources in MCP format.\n * Returns static resources from both buckets (not templates).\n *\n * @returns {Resource[]} Array of resource descriptors\n */\n listResources(): Resource[] {\n return Array.from(this.bridge.resources.values())\n .filter((r) => !r.isTemplate)\n .map((resource) => ({\n uri: resource.uri,\n name: resource.name,\n description: resource.description,\n mimeType: resource.mimeType,\n }));\n }\n\n /**\n * Lists all registered resource templates.\n * Returns only resources with URI templates (dynamic resources).\n *\n * @returns {Array<{uriTemplate: string, name: string, description?: string, mimeType?: string}>}\n */\n listResourceTemplates(): Array<{\n uriTemplate: string;\n name: string;\n description?: string;\n mimeType?: string;\n }> {\n return Array.from(this.bridge.resources.values())\n .filter((r) => r.isTemplate)\n .map((resource) => ({\n uriTemplate: resource.uri,\n name: resource.name,\n ...(resource.description !== undefined && { description: resource.description }),\n ...(resource.mimeType !== undefined && { mimeType: resource.mimeType }),\n }));\n }\n\n // ==================== PROMPT METHODS ====================\n\n /**\n * Registers a single prompt dynamically (Bucket B).\n * Dynamic prompts persist across provideContext() calls and can be independently managed.\n *\n * @param {PromptDescriptor} prompt - The prompt descriptor to register\n * @returns {{unregister: () => void}} Object with unregister function\n * @throws {Error} If prompt name collides with existing prompts\n */\n registerPrompt<TArgsSchema extends ZodSchemaObject = Record<string, never>>(\n prompt: PromptDescriptor<TArgsSchema>\n ): { unregister: () => void } {\n const now = Date.now();\n const lastRegistration = this.promptRegistrationTimestamps.get(prompt.name);\n\n if (lastRegistration && now - lastRegistration < RAPID_DUPLICATE_WINDOW_MS) {\n logger.warn(\n `Prompt \"${prompt.name}\" registered multiple times within ${RAPID_DUPLICATE_WINDOW_MS}ms. ` +\n 'This is likely due to React Strict Mode double-mounting. Ignoring duplicate registration.'\n );\n\n const existingUnregister = this.promptUnregisterFunctions.get(prompt.name);\n if (existingUnregister) {\n return { unregister: existingUnregister };\n }\n }\n\n if (this.provideContextPrompts.has(prompt.name)) {\n throw new Error(\n `[Web Model Context] Prompt name collision: \"${prompt.name}\" is already registered via provideContext(). ` +\n 'Please use a different name or update your provideContext() call.'\n );\n }\n\n if (this.dynamicPrompts.has(prompt.name)) {\n throw new Error(\n `[Web Model Context] Prompt name collision: \"${prompt.name}\" is already registered via registerPrompt(). ` +\n 'Please unregister it first or use a different name.'\n );\n }\n\n const validatedPrompt = this.validatePrompt(prompt);\n this.dynamicPrompts.set(prompt.name, validatedPrompt);\n this.promptRegistrationTimestamps.set(prompt.name, now);\n this.updateBridgePrompts();\n this.scheduleListChanged('prompts');\n\n const unregisterFn = () => {\n if (this.provideContextPrompts.has(prompt.name)) {\n throw new Error(\n `[Web Model Context] Cannot unregister prompt \"${prompt.name}\": ` +\n 'This prompt was registered via provideContext(). Use provideContext() to update the base prompt set.'\n );\n }\n\n if (!this.dynamicPrompts.has(prompt.name)) {\n logger.warn(`Prompt \"${prompt.name}\" is not registered, ignoring unregister call`);\n return;\n }\n\n this.dynamicPrompts.delete(prompt.name);\n this.promptRegistrationTimestamps.delete(prompt.name);\n this.promptUnregisterFunctions.delete(prompt.name);\n this.updateBridgePrompts();\n this.scheduleListChanged('prompts');\n };\n\n this.promptUnregisterFunctions.set(prompt.name, unregisterFn);\n\n return { unregister: unregisterFn };\n }\n\n /**\n * Unregisters a prompt by name.\n * Can unregister prompts from either Bucket A (provideContext) or Bucket B (registerPrompt).\n *\n * @param {string} name - Name of the prompt to unregister\n */\n unregisterPrompt(name: string): void {\n const inProvideContext = this.provideContextPrompts.has(name);\n const inDynamic = this.dynamicPrompts.has(name);\n\n if (!inProvideContext && !inDynamic) {\n logger.warn(`Prompt \"${name}\" is not registered, ignoring unregister call`);\n return;\n }\n\n if (inProvideContext) {\n this.provideContextPrompts.delete(name);\n }\n\n if (inDynamic) {\n this.dynamicPrompts.delete(name);\n this.promptRegistrationTimestamps.delete(name);\n this.promptUnregisterFunctions.delete(name);\n }\n\n this.updateBridgePrompts();\n this.scheduleListChanged('prompts');\n }\n\n /**\n * Lists all registered prompts in MCP format.\n * Returns prompts from both buckets.\n *\n * @returns {Prompt[]} Array of prompt descriptors\n */\n listPrompts(): Prompt[] {\n return Array.from(this.bridge.prompts.values()).map((prompt) => ({\n name: prompt.name,\n description: prompt.description,\n arguments: prompt.argsSchema?.properties\n ? Object.entries(prompt.argsSchema.properties).map(([name, schema]) => ({\n name,\n description: (schema as { description?: string }).description,\n required: prompt.argsSchema?.required?.includes(name) ?? false,\n }))\n : undefined,\n }));\n }\n\n /**\n * Unregisters a tool by name (Chromium native API).\n * Can unregister tools from either Bucket A (provideContext) or Bucket B (registerTool).\n *\n * @param {string} name - Name of the tool to unregister\n */\n unregisterTool(name: string): void {\n const inProvideContext = this.provideContextTools.has(name);\n const inDynamic = this.dynamicTools.has(name);\n\n if (!inProvideContext && !inDynamic) {\n logger.warn(`Tool \"${name}\" is not registered, ignoring unregister call`);\n return;\n }\n\n if (inProvideContext) {\n this.provideContextTools.delete(name);\n }\n\n if (inDynamic) {\n this.dynamicTools.delete(name);\n this.toolRegistrationTimestamps.delete(name);\n this.toolUnregisterFunctions.delete(name);\n }\n\n this.updateBridgeTools();\n this.scheduleListChanged('tools');\n }\n\n /**\n * Clears all registered context from both buckets (Chromium native API).\n * Removes all tools, resources, and prompts registered via provideContext() and register* methods.\n */\n clearContext(): void {\n // Clear tools\n this.provideContextTools.clear();\n this.dynamicTools.clear();\n this.toolRegistrationTimestamps.clear();\n this.toolUnregisterFunctions.clear();\n\n // Clear resources\n this.provideContextResources.clear();\n this.dynamicResources.clear();\n this.resourceRegistrationTimestamps.clear();\n this.resourceUnregisterFunctions.clear();\n\n // Clear prompts\n this.provideContextPrompts.clear();\n this.dynamicPrompts.clear();\n this.promptRegistrationTimestamps.clear();\n this.promptUnregisterFunctions.clear();\n\n // Update bridge\n this.updateBridgeTools();\n this.updateBridgeResources();\n this.updateBridgePrompts();\n\n // Schedule notifications (batched via microtask)\n this.scheduleListChanged('tools');\n this.scheduleListChanged('resources');\n this.scheduleListChanged('prompts');\n }\n\n /**\n * Updates the bridge tools map with merged tools from both buckets.\n * The final tool list is the union of Bucket A (provideContext) and Bucket B (dynamic).\n *\n * @private\n */\n private updateBridgeTools(): void {\n this.bridge.tools.clear();\n\n for (const [name, tool] of this.provideContextTools) {\n this.bridge.tools.set(name, tool);\n }\n\n for (const [name, tool] of this.dynamicTools) {\n this.bridge.tools.set(name, tool);\n }\n }\n\n /**\n * Notifies all servers and testing callbacks that the tools list has changed.\n * Sends MCP notifications to connected servers and invokes registered testing callbacks.\n *\n * @private\n */\n private notifyToolsListChanged(): void {\n if (this.bridge.tabServer.notification) {\n this.bridge.tabServer.notification({\n method: 'notifications/tools/list_changed',\n params: {},\n });\n }\n\n if (this.bridge.iframeServer?.notification) {\n this.bridge.iframeServer.notification({\n method: 'notifications/tools/list_changed',\n params: {},\n });\n }\n\n if (this.testingAPI && 'notifyToolsChanged' in this.testingAPI) {\n (this.testingAPI as WebModelContextTesting).notifyToolsChanged();\n }\n\n this.dispatchEvent(new Event('toolschanged'));\n }\n\n /**\n * Updates the bridge resources map with merged resources from both buckets.\n *\n * @private\n */\n private updateBridgeResources(): void {\n this.bridge.resources.clear();\n\n for (const [uri, resource] of this.provideContextResources) {\n this.bridge.resources.set(uri, resource);\n }\n\n for (const [uri, resource] of this.dynamicResources) {\n this.bridge.resources.set(uri, resource);\n }\n }\n\n /**\n * Notifies all servers that the resources list has changed.\n *\n * @private\n */\n private notifyResourcesListChanged(): void {\n if (this.bridge.tabServer.notification) {\n this.bridge.tabServer.notification({\n method: 'notifications/resources/list_changed',\n params: {},\n });\n }\n\n if (this.bridge.iframeServer?.notification) {\n this.bridge.iframeServer.notification({\n method: 'notifications/resources/list_changed',\n params: {},\n });\n }\n }\n\n /**\n * Updates the bridge prompts map with merged prompts from both buckets.\n *\n * @private\n */\n private updateBridgePrompts(): void {\n this.bridge.prompts.clear();\n\n for (const [name, prompt] of this.provideContextPrompts) {\n this.bridge.prompts.set(name, prompt);\n }\n\n for (const [name, prompt] of this.dynamicPrompts) {\n this.bridge.prompts.set(name, prompt);\n }\n }\n\n /**\n * Notifies all servers that the prompts list has changed.\n *\n * @private\n */\n private notifyPromptsListChanged(): void {\n if (this.bridge.tabServer.notification) {\n this.bridge.tabServer.notification({\n method: 'notifications/prompts/list_changed',\n params: {},\n });\n }\n\n if (this.bridge.iframeServer?.notification) {\n this.bridge.iframeServer.notification({\n method: 'notifications/prompts/list_changed',\n params: {},\n });\n }\n }\n\n /**\n * Schedules a list changed notification using microtask batching.\n * Multiple calls for the same list type within the same task are coalesced\n * into a single notification. This dramatically reduces notification spam\n * during React mount/unmount cycles.\n *\n * @param listType - The type of list that changed ('tools' | 'resources' | 'prompts')\n * @private\n */\n private scheduleListChanged(listType: ListChangeType): void {\n if (this.pendingNotifications.has(listType)) return;\n\n this.pendingNotifications.add(listType);\n queueMicrotask(() => {\n this.pendingNotifications.delete(listType);\n\n // Dispatch to the appropriate notification method\n // Exhaustive switch ensures compile-time safety when adding new list types\n switch (listType) {\n case 'tools':\n this.notifyToolsListChanged();\n break;\n case 'resources':\n this.notifyResourcesListChanged();\n break;\n case 'prompts':\n this.notifyPromptsListChanged();\n break;\n default: {\n // Exhaustiveness check: TypeScript will error if a case is missing\n const _exhaustive: never = listType;\n logger.error(`Unknown list type: ${_exhaustive}`);\n }\n }\n });\n }\n\n /**\n * Reads a resource by URI (internal use only by MCP bridge).\n * Handles both static resources and URI templates.\n *\n * @param {string} uri - The URI of the resource to read\n * @returns {Promise<{contents: ResourceContents[]}>} The resource contents\n * @throws {Error} If resource is not found\n * @internal\n */\n async readResource(uri: string): Promise<{ contents: ResourceContents[] }> {\n // First, try to find an exact match (static resource)\n const staticResource = this.bridge.resources.get(uri);\n if (staticResource && !staticResource.isTemplate) {\n try {\n // Try to parse as URL, but fall back to a pseudo-URL for custom schemes\n let parsedUri: URL;\n try {\n parsedUri = new URL(uri);\n } catch {\n // Custom URI scheme (e.g., \"iframe://config\", \"prefix_iframe://config\")\n // Create a pseudo-URL with the original URI as the pathname\n parsedUri = new URL(`custom-scheme:///${encodeURIComponent(uri)}`);\n // Store original URI for handlers that need it\n (parsedUri as URL & { originalUri: string }).originalUri = uri;\n }\n return await staticResource.read(parsedUri);\n } catch (error) {\n logger.error(`Error reading resource ${uri}:`, error);\n throw error;\n }\n }\n\n // Try to match against URI templates\n for (const resource of this.bridge.resources.values()) {\n if (!resource.isTemplate) continue;\n\n const params = this.matchUriTemplate(resource.uri, uri);\n if (params) {\n try {\n // Try to parse as URL, but fall back to a pseudo-URL for custom schemes\n let parsedUri: URL;\n try {\n parsedUri = new URL(uri);\n } catch {\n // Custom URI scheme - create a pseudo-URL\n parsedUri = new URL(`custom-scheme:///${encodeURIComponent(uri)}`);\n (parsedUri as URL & { originalUri: string }).originalUri = uri;\n }\n return await resource.read(parsedUri, params);\n } catch (error) {\n logger.error(`Error reading resource ${uri}:`, error);\n throw error;\n }\n }\n }\n\n throw new Error(`Resource not found: ${uri}`);\n }\n\n /**\n * Matches a URI against a URI template and extracts parameters.\n *\n * @param {string} template - The URI template (e.g., \"file://{path}\")\n * @param {string} uri - The actual URI to match\n * @returns {Record<string, string> | null} Extracted parameters or null if no match\n * @private\n */\n private matchUriTemplate(template: string, uri: string): Record<string, string> | null {\n // Convert template to regex pattern\n // e.g., \"file://{path}\" -> \"^file://(.+)$\" with capture group for \"path\"\n const paramNames: string[] = [];\n let regexPattern = template.replace(/[.*+?^${}()|[\\]\\\\]/g, (char) => {\n // Don't escape { and } - we'll handle them specially\n if (char === '{' || char === '}') return char;\n return `\\\\${char}`;\n });\n\n // Replace {param} with capture groups\n regexPattern = regexPattern.replace(/\\{([^}]+)\\}/g, (_, paramName) => {\n paramNames.push(paramName);\n return '(.+)';\n });\n\n const regex = new RegExp(`^${regexPattern}$`);\n const match = uri.match(regex);\n\n if (!match) return null;\n\n const params: Record<string, string> = {};\n for (let i = 0; i < paramNames.length; i++) {\n const paramName = paramNames[i];\n const paramValue = match[i + 1];\n if (typeof paramName !== 'string' || typeof paramValue !== 'string') {\n continue;\n }\n params[paramName] = paramValue;\n }\n\n return params;\n }\n\n /**\n * Gets a prompt with arguments (internal use only by MCP bridge).\n *\n * @param {string} name - Name of the prompt\n * @param {Record<string, unknown>} args - Arguments to pass to the prompt\n * @returns {Promise<{messages: PromptMessage[]}>} The prompt messages\n * @throws {Error} If prompt is not found\n * @internal\n */\n async getPrompt(\n name: string,\n args?: Record<string, unknown>\n ): Promise<{ messages: PromptMessage[] }> {\n const prompt = this.bridge.prompts.get(name);\n if (!prompt) {\n throw new Error(`Prompt not found: ${name}`);\n }\n\n // Validate arguments if schema is defined\n if (prompt.argsValidator && args) {\n const validation = validateWithZod(args, prompt.argsValidator);\n if (!validation.success) {\n logger.error(`Argument validation failed for prompt ${name}:`, validation.error);\n throw new Error(`Argument validation error for prompt \"${name}\":\\n${validation.error}`);\n }\n }\n\n try {\n return await prompt.get(args ?? {});\n } catch (error) {\n logger.error(`Error getting prompt ${name}:`, error);\n throw error;\n }\n }\n\n /**\n * Executes a tool with validation and event dispatch.\n * Follows this sequence:\n * 1. Validates input arguments against schema (unless skipValidation is true)\n * 2. Records tool call in testing API (if available)\n * 3. Checks for mock response (if testing)\n * 4. Dispatches 'toolcall' event to listeners\n * 5. Executes tool function if not prevented\n * 6. Validates output (permissive mode - warns only)\n *\n * @param {string} toolName - Name of the tool to execute\n * @param {Record<string, unknown>} args - Arguments to pass to the tool\n * @param {Object} [options] - Execution options\n * @param {boolean} [options.skipValidation] - Skip input validation (used when MCP SDK already validated)\n * @returns {Promise<ToolResponse>} The tool's response\n * @throws {Error} If tool is not found\n * @internal\n */\n async executeTool(\n toolName: string,\n args: Record<string, unknown>,\n options?: { skipValidation?: boolean }\n ): Promise<ToolResponse> {\n const tool = this.bridge.tools.get(toolName);\n if (!tool) {\n throw new Error(`Tool not found: ${toolName}`);\n }\n\n // Skip validation if requested (MCP SDK already validated via safeParseAsync)\n // Keep validation for polyfill/testing path where SDK validation doesn't apply\n let validatedArgs: Record<string, unknown>;\n if (options?.skipValidation) {\n validatedArgs = args;\n } else {\n const validation = validateWithZod(args, tool.inputValidator);\n if (!validation.success) {\n logger.error(`Input validation failed for ${toolName}:`, validation.error);\n return {\n content: [\n {\n type: 'text',\n text: `Input validation error for tool \"${toolName}\":\\n${validation.error}`,\n },\n ],\n isError: true,\n };\n }\n validatedArgs = validation.data as Record<string, unknown>;\n }\n\n if (this.testingAPI) {\n this.testingAPI.recordToolCall(toolName, validatedArgs);\n }\n\n if (this.testingAPI?.hasMockResponse(toolName)) {\n const mockResponse = this.testingAPI.getMockResponse(toolName);\n if (mockResponse) {\n return mockResponse;\n }\n }\n\n const event = new WebToolCallEvent(toolName, validatedArgs);\n\n this.dispatchEvent(event);\n\n if (event.defaultPrevented && event.hasResponse()) {\n const response = event.getResponse();\n if (response) {\n return response;\n }\n }\n\n try {\n const response = await tool.execute(validatedArgs);\n\n if (tool.outputValidator && response.structuredContent) {\n const outputValidation = validateWithZod(response.structuredContent, tool.outputValidator);\n if (!outputValidation.success) {\n logger.warn(`Output validation failed for ${toolName}:`, outputValidation.error);\n }\n }\n\n // Log navigation tools for debugging\n if (\n response.metadata &&\n typeof response.metadata === 'object' &&\n 'willNavigate' in response.metadata\n ) {\n logger.info(`Tool \"${toolName}\" will trigger navigation`, response.metadata);\n }\n\n return response;\n } catch (error) {\n logger.error(`Error executing tool ${toolName}:`, error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n /**\n * Lists all registered tools in MCP format.\n * Returns tools from both buckets with full MCP specification including\n * annotations and output schemas.\n *\n * @returns {Array<{name: string, description: string, inputSchema: InputSchema, outputSchema?: InputSchema, annotations?: ToolAnnotations}>} Array of tool descriptors\n */\n listTools() {\n return Array.from(this.bridge.tools.values()).map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n ...(tool.outputSchema && { outputSchema: tool.outputSchema }),\n ...(tool.annotations && { annotations: tool.annotations }),\n }));\n }\n\n /**\n * Executes a registered tool using MCP-style params.\n * Throws for missing tools and returns MCP-shaped errors for execution/validation failures.\n */\n async callTool(params: {\n name: string;\n arguments?: Record<string, unknown>;\n }): Promise<ToolResponse> {\n if (!params?.name || typeof params.name !== 'string') {\n throw new Error('Tool name is required');\n }\n\n if (!this.bridge.tools.has(params.name)) {\n throw new Error(`Tool not found: ${params.name}`);\n }\n\n return this.executeTool(params.name, params.arguments ?? {});\n }\n\n // ==================== SAMPLING METHODS ====================\n\n /**\n * Request an LLM completion from the connected client.\n * This sends a sampling request to the connected MCP client.\n *\n * @param {SamplingRequestParams} params - Parameters for the sampling request\n * @returns {Promise<SamplingResult>} The LLM completion result\n */\n async createMessage(params: SamplingRequestParams): Promise<SamplingResult> {\n return requireCreateMessageCapability(this.bridge.tabServer)(params);\n }\n\n // ==================== ELICITATION METHODS ====================\n\n /**\n * Request user input from the connected client.\n * This sends an elicitation request to the connected MCP client.\n *\n * @param {ElicitationParams} params - Parameters for the elicitation request\n * @returns {Promise<ElicitationResult>} The user's response\n */\n async elicitInput(params: ElicitationParams): Promise<ElicitationResult> {\n return requireElicitInputCapability(this.bridge.tabServer)(params);\n }\n}\n\n/**\n * Initializes the MCP bridge with dual-server support.\n * Creates TabServer for same-window communication and optionally IframeChildServer\n * for parent-child iframe communication.\n *\n * @param {WebModelContextInitOptions} [options] - Configuration options\n * @returns {MCPBridge} The initialized MCP bridge\n */\nfunction initializeMCPBridge(options?: WebModelContextInitOptions): MCPBridge {\n const hostname = window.location.hostname || 'localhost';\n const transportOptions = options?.transport;\n\n const setupServerHandlers = (server: McpServer, bridge: MCPBridge) => {\n // ==================== TOOL HANDLERS ====================\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: bridge.modelContext.listTools(),\n };\n });\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const toolName = request.params.name;\n const args = (request.params.arguments || {}) as Record<string, unknown>;\n\n try {\n const response = await bridge.modelContext.executeTool(toolName, args);\n return {\n content: response.content,\n isError: response.isError,\n ...(response.structuredContent && { structuredContent: response.structuredContent }),\n };\n } catch (error) {\n bridgeLogger.error(`Error calling tool ${toolName}:`, error);\n throw error;\n }\n });\n\n // ==================== RESOURCE HANDLERS ====================\n server.setRequestHandler(ListResourcesRequestSchema, async () => {\n return {\n resources: bridge.modelContext.listResources(),\n // Note: Resource templates are included in the resources list as the MCP SDK\n // doesn't export ListResourceTemplatesRequestSchema separately.\n // Clients can identify templates by checking for URI patterns containing {param}.\n };\n });\n\n server.setRequestHandler(ReadResourceRequestSchema, async (request) => {\n try {\n return await bridge.modelContext.readResource(request.params.uri);\n } catch (error) {\n bridgeLogger.error(`Error reading resource ${request.params.uri}:`, error);\n throw error;\n }\n });\n\n // ==================== PROMPT HANDLERS ====================\n server.setRequestHandler(ListPromptsRequestSchema, async () => {\n return {\n prompts: bridge.modelContext.listPrompts(),\n };\n });\n\n server.setRequestHandler(GetPromptRequestSchema, async (request) => {\n try {\n return await bridge.modelContext.getPrompt(\n request.params.name,\n request.params.arguments as Record<string, unknown> | undefined\n );\n } catch (error) {\n bridgeLogger.error(`Error getting prompt ${request.params.name}:`, error);\n throw error;\n }\n });\n\n // Note: Sampling and elicitation are server-to-client requests.\n // The server calls createMessage() and elicitInput() methods on the Server instance.\n // These are NOT request handlers - the client handles these requests.\n };\n\n const customTransport: Transport | undefined = transportOptions?.create?.();\n\n if (customTransport) {\n const server = new McpServer(\n {\n name: hostname,\n version: '1.0.0',\n },\n {\n capabilities: {\n tools: { listChanged: true },\n resources: { listChanged: true },\n prompts: { listChanged: true },\n },\n }\n );\n\n const bridge: MCPBridge = {\n tabServer: server,\n tools: new Map(),\n resources: new Map(),\n prompts: new Map(),\n modelContext: undefined as unknown as InternalModelContext,\n isInitialized: true,\n };\n\n const modelContext = new WebModelContext(bridge);\n bridge.modelContext = modelContext;\n\n setupServerHandlers(server, bridge);\n server.connect(customTransport);\n\n return bridge;\n }\n\n const tabServerEnabled = transportOptions?.tabServer !== false;\n const tabServer = new McpServer(\n {\n name: `${hostname}-tab`,\n version: '1.0.0',\n },\n {\n capabilities: {\n tools: { listChanged: true },\n resources: { listChanged: true },\n prompts: { listChanged: true },\n },\n }\n );\n\n const bridge: MCPBridge = {\n tabServer,\n tools: new Map(),\n resources: new Map(),\n prompts: new Map(),\n modelContext: undefined as unknown as InternalModelContext,\n isInitialized: true,\n };\n\n const modelContext = new WebModelContext(bridge);\n bridge.modelContext = modelContext;\n\n setupServerHandlers(tabServer, bridge);\n\n if (tabServerEnabled) {\n const tabServerOptions: Partial<TabServerTransportOptions> =\n typeof transportOptions?.tabServer === 'object' ? transportOptions.tabServer : {};\n const { allowedOrigins, ...restTabServerOptions } = tabServerOptions;\n\n const tabTransport = new TabServerTransport({\n allowedOrigins: allowedOrigins ?? ['*'],\n ...(restTabServerOptions as Omit<TabServerTransportOptions, 'allowedOrigins'>),\n });\n\n tabServer.connect(tabTransport);\n }\n\n const isInIframe = typeof window !== 'undefined' && window.parent !== window;\n const iframeServerConfig = transportOptions?.iframeServer;\n const iframeServerEnabled =\n iframeServerConfig !== false && (iframeServerConfig !== undefined || isInIframe);\n\n if (iframeServerEnabled) {\n const iframeServer = new McpServer(\n {\n name: `${hostname}-iframe`,\n version: '1.0.0',\n },\n {\n capabilities: {\n tools: { listChanged: true },\n resources: { listChanged: true },\n prompts: { listChanged: true },\n },\n }\n );\n\n setupServerHandlers(iframeServer, bridge);\n\n const iframeServerOptions: Partial<IframeChildTransportOptions> =\n typeof iframeServerConfig === 'object' ? iframeServerConfig : {};\n const { allowedOrigins, ...restIframeServerOptions } = iframeServerOptions;\n\n const iframeTransport = new IframeChildTransport({\n allowedOrigins: allowedOrigins ?? ['*'],\n ...(restIframeServerOptions as Omit<IframeChildTransportOptions, 'allowedOrigins'>),\n });\n\n iframeServer.connect(iframeTransport);\n bridge.iframeServer = iframeServer;\n }\n\n return bridge;\n}\n\n/**\n * Initializes the Web Model Context API on window.navigator.\n * Creates and exposes navigator.modelContext and navigator.modelContextTesting.\n * Automatically detects and uses native Chromium implementation if available.\n *\n * @param {WebModelContextInitOptions} [options] - Configuration options\n * @throws {Error} If initialization fails\n * @example\n * ```typescript\n * import { initializeWebModelContext } from '@mcp-b/global';\n *\n * initializeWebModelContext({\n * transport: {\n * tabServer: {\n * allowedOrigins: ['https://example.com']\n * }\n * }\n * });\n * ```\n */\nexport function initializeWebModelContext(options?: WebModelContextInitOptions): void {\n /* c8 ignore next 4 */\n if (typeof window === 'undefined') {\n logger.warn('Not in browser environment, skipping initialization');\n return;\n }\n\n const effectiveOptions = options ?? window.__webModelContextOptions;\n const native = detectNativeAPI();\n\n if (native.hasNativeContext) {\n const nativeContext = window.navigator.modelContext;\n const nativeTesting = native.hasNativeTesting\n ? window.navigator.modelContextTesting\n : undefined;\n\n if (!nativeContext) {\n logger.error('Native API detection mismatch');\n return;\n }\n\n logger.info('✅ Native Chromium API detected');\n if (nativeTesting) {\n logger.info(' Using native implementation with MCP bridge synchronization');\n logger.info(' Native API will automatically collect tools from embedded iframes');\n } else {\n logger.info(\n ' Native modelContextTesting not detected; installing consumer shim on modelContext'\n );\n }\n\n try {\n const bridge = initializeMCPBridge(effectiveOptions);\n\n const adapter = new NativeModelContextAdapter(bridge, nativeContext, nativeTesting);\n\n bridge.modelContext = adapter;\n if (nativeTesting) {\n bridge.modelContextTesting = nativeTesting;\n installDeprecatedTestingAccessor(nativeTesting);\n }\n\n installConsumerShim(\n nativeContext,\n (params: { name: string; arguments?: Record<string, unknown> }) => adapter.callTool(params),\n {\n hasNativeTesting: Boolean(nativeTesting),\n ...(!nativeTesting && {\n onToolRegistryMutated: () => adapter.refreshToolsFromNative(),\n }),\n }\n );\n\n Object.defineProperty(window, '__mcpBridge', {\n value: bridge,\n writable: false,\n configurable: true,\n });\n\n logger.info('✅ MCP bridge synced with native API');\n if (nativeTesting) {\n logger.info(' MCP clients will receive automatic tool updates from native registry');\n } else {\n logger.warn(\n ' Native testing callbacks are unavailable; tool list change events are best-effort.'\n );\n }\n } catch (error) {\n logger.error('Failed to initialize native adapter:', error);\n throw error;\n }\n\n return;\n }\n\n if (window.navigator.modelContext) {\n logger.warn('window.navigator.modelContext already exists, skipping initialization');\n return;\n }\n\n logger.info('Native API not detected, installing polyfill');\n\n try {\n const bridge = initializeMCPBridge(effectiveOptions);\n\n Object.defineProperty(window.navigator, 'modelContext', {\n value: bridge.modelContext,\n writable: false,\n configurable: false,\n });\n\n Object.defineProperty(window, '__mcpBridge', {\n value: bridge,\n writable: false,\n configurable: true,\n });\n\n logger.info('✅ window.navigator.modelContext initialized successfully');\n\n testingLogger.info('Installing polyfill');\n testingLogger.info(' 💡 To use the native implementation in Chromium:');\n testingLogger.info(' - Navigate to chrome://flags');\n testingLogger.info(' - Enable \"Experimental Web Platform Features\"');\n testingLogger.info(' - Or launch with: --enable-experimental-web-platform-features');\n\n const testingAPI = new WebModelContextTesting(bridge);\n bridge.modelContextTesting = testingAPI;\n\n (bridge.modelContext as WebModelContext).setTestingAPI(testingAPI);\n\n installDeprecatedTestingAccessor(testingAPI);\n\n testingLogger.info('✅ Polyfill installed at window.navigator.modelContextTesting');\n } catch (error) {\n logger.error('Failed to initialize:', error);\n throw error;\n }\n}\n\n/**\n * Cleans up the Web Model Context API.\n * Closes all MCP servers and removes API from window.navigator.\n * Useful for testing and hot module replacement.\n *\n * @example\n * ```typescript\n * import { cleanupWebModelContext } from '@mcp-b/global';\n *\n * cleanupWebModelContext();\n * ```\n */\nexport function cleanupWebModelContext(): void {\n /* c8 ignore next */\n if (typeof window === 'undefined') return;\n\n if (window.__mcpBridge) {\n try {\n window.__mcpBridge.tabServer.close();\n\n if (window.__mcpBridge.iframeServer) {\n window.__mcpBridge.iframeServer.close();\n }\n } catch (error) {\n logger.warn('Error closing MCP servers:', error);\n }\n }\n\n const safeDeleteNavigatorProperty = (propertyName: 'modelContext' | 'modelContextTesting') => {\n const descriptor = Object.getOwnPropertyDescriptor(window.navigator, propertyName);\n if (descriptor?.configurable === false) {\n logger.debug(\n `Skipping cleanup for navigator.${propertyName} because property is non-configurable`\n );\n return;\n }\n\n try {\n delete (window.navigator as unknown as Record<string, unknown>)[propertyName];\n } catch (error) {\n logger.warn(`Failed to remove navigator.${propertyName} during cleanup:`, error);\n }\n };\n\n safeDeleteNavigatorProperty('modelContext');\n safeDeleteNavigatorProperty('modelContextTesting');\n delete (window as unknown as { __mcpBridge?: unknown }).__mcpBridge;\n\n logger.info('Cleaned up');\n}\n","import { initializeWebModelContext } from './global.js';\nimport { createLogger } from './logger.js';\nimport type { TransportConfiguration, WebModelContextInitOptions } from './types.js';\n\nconst logger = createLogger('WebModelContext');\n\ntype TabServerConfig = NonNullable<TransportConfiguration['tabServer']>;\n\nfunction mergeTransportOptions(\n base: TransportConfiguration,\n override: TransportConfiguration\n): TransportConfiguration {\n if (!base) {\n return override;\n }\n if (!override) {\n return base;\n }\n\n return {\n ...base,\n ...override,\n tabServer: {\n ...(base.tabServer ?? {}),\n ...(override.tabServer ?? {}),\n },\n };\n}\n\nfunction mergeInitOptions(\n base?: WebModelContextInitOptions,\n override?: WebModelContextInitOptions\n): WebModelContextInitOptions | undefined {\n if (!base) {\n return override;\n }\n if (!override) {\n return base;\n }\n\n return {\n ...base,\n ...override,\n transport: mergeTransportOptions(base.transport ?? {}, override.transport ?? {}),\n };\n}\n\nfunction parseScriptTagOptions(\n script: HTMLScriptElement | null\n): WebModelContextInitOptions | undefined {\n if (!script || !script.dataset) {\n return undefined;\n }\n\n const { dataset } = script;\n\n if (dataset.webmcpOptions) {\n try {\n return JSON.parse(dataset.webmcpOptions) as WebModelContextInitOptions;\n } catch (error) {\n logger.error('Invalid JSON in data-webmcp-options:', error);\n return undefined;\n }\n }\n\n const options: WebModelContextInitOptions = {};\n let hasOptions = false;\n\n if (dataset.webmcpAutoInitialize !== undefined) {\n options.autoInitialize = dataset.webmcpAutoInitialize !== 'false';\n hasOptions = true;\n }\n\n const tabServerOptions: TabServerConfig = {};\n let hasTabServerOptions = false;\n\n if (dataset.webmcpAllowedOrigins) {\n const origins = dataset.webmcpAllowedOrigins\n .split(',')\n .map((origin) => origin.trim())\n .filter((origin) => origin.length > 0);\n\n if (origins.length > 0) {\n tabServerOptions.allowedOrigins = origins;\n hasOptions = true;\n hasTabServerOptions = true;\n }\n }\n\n if (dataset.webmcpChannelId) {\n tabServerOptions.channelId = dataset.webmcpChannelId;\n hasOptions = true;\n hasTabServerOptions = true;\n }\n\n if (hasTabServerOptions) {\n options.transport = {\n ...(options.transport ?? {}),\n tabServer: {\n ...(options.transport?.tabServer ?? {}),\n ...tabServerOptions,\n },\n };\n }\n\n return hasOptions ? options : undefined;\n}\n\nif (typeof window !== 'undefined' && typeof document !== 'undefined') {\n const globalOptions = window.__webModelContextOptions;\n const scriptElement = document.currentScript as HTMLScriptElement | null;\n const scriptOptions = parseScriptTagOptions(scriptElement);\n const mergedOptions =\n mergeInitOptions(globalOptions, scriptOptions) ?? globalOptions ?? scriptOptions;\n\n if (mergedOptions) {\n window.__webModelContextOptions = mergedOptions;\n }\n\n const shouldAutoInitialize = mergedOptions?.autoInitialize !== false;\n\n try {\n if (shouldAutoInitialize) {\n initializeWebModelContext(mergedOptions);\n }\n } catch (error) {\n logger.error('Auto-initialization failed:', error);\n }\n}\n\nexport { cleanupWebModelContext, initializeWebModelContext } from './global.js';\nexport { createLogger } from './logger.js';\nexport type * from './types.js';\nexport { zodToJsonSchema } from './validation.js';\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["runtime: RuntimeState | null","rest","allowedOrigins","parsed: unknown"],"sources":["../src/global.ts","../src/index.ts"],"sourcesContent":["import {\n IframeChildTransport,\n type IframeChildTransportOptions,\n TabServerTransport,\n type TabServerTransportOptions,\n} from '@mcp-b/transports';\nimport { initializeWebMCPPolyfill } from '@mcp-b/webmcp-polyfill';\nimport { BrowserMcpServer, type Transport } from '@mcp-b/webmcp-ts-sdk';\nimport type {\n InputSchema,\n ModelContextCore,\n ModelContextTesting,\n ModelContextTestingPolyfillExtensions,\n ToolListItem,\n ToolResponse,\n} from '@mcp-b/webmcp-types';\nimport type { WebModelContextInitOptions } from './types.js';\n\ninterface RuntimeState {\n native: ModelContextCore;\n server: BrowserMcpServer;\n transport: Transport;\n}\n\nlet runtime: RuntimeState | null = null;\n\nfunction isBrowserEnvironment(): boolean {\n return typeof window !== 'undefined' && typeof window.navigator !== 'undefined';\n}\n\n/**\n * Replace navigator.modelContext with the given value.\n * Tries an own-property on the navigator instance first. If the native browser\n * defines modelContext as a non-configurable property (common in Chromium), this\n * will throw — in that case we fall back to redefining the getter on\n * Navigator.prototype so that `navigator.modelContext` resolves to our value.\n */\nfunction replaceModelContext(value: unknown): void {\n try {\n Object.defineProperty(navigator, 'modelContext', {\n configurable: true,\n enumerable: true,\n writable: false,\n value,\n });\n } catch {\n // Native browser property is non-configurable on the instance.\n // Shadow it with a getter on the prototype instead.\n Object.defineProperty(Object.getPrototypeOf(navigator), 'modelContext', {\n configurable: true,\n enumerable: true,\n get() {\n return value;\n },\n });\n }\n\n // Verify the replacement actually worked — the prototype getter cannot\n // shadow a non-configurable own property on the navigator instance.\n if (navigator.modelContext !== value) {\n console.error(\n '[WebModelContext] Failed to replace navigator.modelContext.',\n 'Descriptor:',\n Object.getOwnPropertyDescriptor(navigator, 'modelContext')\n );\n }\n}\n\nfunction createTransport(config: WebModelContextInitOptions['transport']): Transport {\n const inIframe = window.parent !== window;\n\n if (inIframe && config?.iframeServer !== false) {\n const iframeOptions =\n typeof config?.iframeServer === 'object'\n ? config.iframeServer\n : ({} as Partial<IframeChildTransportOptions>);\n\n const { allowedOrigins, ...rest } = iframeOptions;\n\n return new IframeChildTransport({\n allowedOrigins: allowedOrigins ?? ['*'],\n ...rest,\n });\n }\n\n if (config?.tabServer === false) {\n throw new Error('tabServer transport is disabled and iframe transport was not selected');\n }\n\n const tabOptions =\n typeof config?.tabServer === 'object'\n ? config.tabServer\n : ({} as Partial<TabServerTransportOptions>);\n\n const { allowedOrigins, ...rest } = tabOptions;\n\n return new TabServerTransport({\n allowedOrigins: allowedOrigins ?? ['*'],\n ...rest,\n });\n}\n\nfunction parseTestingInputSchema(inputSchema: string | undefined): InputSchema | undefined {\n if (!inputSchema) {\n return undefined;\n }\n\n try {\n const parsed = JSON.parse(inputSchema) as unknown;\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return undefined;\n }\n return parsed as InputSchema;\n } catch (error) {\n console.warn('[WebMCP] Failed to parse testing inputSchema JSON:', error);\n return undefined;\n }\n}\n\nfunction getTestingShimTools():\n | {\n testingShim: ModelContextTesting;\n tools: ToolListItem[];\n }\n | undefined {\n const testingShim = navigator.modelContextTesting as\n | (ModelContextTesting & Partial<ModelContextTestingPolyfillExtensions>)\n | undefined;\n if (!testingShim) {\n return undefined;\n }\n\n if (typeof testingShim.getRegisteredTools === 'function') {\n return {\n testingShim,\n tools: testingShim.getRegisteredTools() as ToolListItem[],\n };\n }\n\n if (typeof testingShim.listTools !== 'function') {\n return undefined;\n }\n\n const tools = testingShim.listTools().map(\n (tool): ToolListItem => ({\n name: tool.name,\n description: tool.description ?? '',\n inputSchema: parseTestingInputSchema(tool.inputSchema) ?? {\n type: 'object',\n properties: {},\n },\n })\n );\n\n return {\n testingShim,\n tools,\n };\n}\n\nfunction syncToolsFromTestingShim(server: BrowserMcpServer): number {\n const shimState = getTestingShimTools();\n if (!shimState) {\n return 0;\n }\n\n const { testingShim, tools } = shimState;\n return server.backfillTools(tools, async (name: string, args: Record<string, unknown>) => {\n const serialized = await testingShim.executeTool(name, JSON.stringify(args ?? {}));\n if (serialized === null) {\n return {\n content: [{ type: 'text', text: 'Tool execution interrupted by navigation' }],\n isError: true,\n } satisfies ToolResponse;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(serialized);\n } catch (parseError) {\n throw new Error(\n `Failed to parse serialized tool response for ${name}: ${parseError instanceof Error ? parseError.message : String(parseError)}`\n );\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(`Invalid serialized tool response for ${name}`);\n }\n return parsed as ToolResponse;\n });\n}\n\nexport function initializeWebModelContext(options?: WebModelContextInitOptions): void {\n if (!isBrowserEnvironment()) {\n return;\n }\n\n if (runtime) {\n return;\n }\n\n // 1. Install polyfill (provides modelContext + modelContextTesting)\n initializeWebMCPPolyfill({\n installTestingShim: options?.installTestingShim ?? 'if-missing',\n });\n\n // 2. Save reference to the polyfill's (or native) context\n const native = navigator.modelContext as unknown as ModelContextCore;\n if (!native) {\n throw new Error('navigator.modelContext is not available');\n }\n\n // 3. Create server with native mirroring\n const hostname = window.location.hostname || 'localhost';\n const server = new BrowserMcpServer({ name: `${hostname}-webmcp`, version: '1.0.0' }, { native });\n server.syncNativeTools();\n syncToolsFromTestingShim(server);\n\n // 4. Replace navigator.modelContext with the server.\n // Try own-property on the navigator instance first (works for polyfill and most cases).\n // Fall back to a prototype getter if the native property is non-configurable.\n replaceModelContext(server);\n\n // 5. Create transport and connect\n const transport = createTransport(options?.transport);\n runtime = { native, server, transport };\n\n void server.connect(transport).catch((error: unknown) => {\n console.error('[WebModelContext] Failed to connect MCP transport:', error);\n });\n}\n\nexport function cleanupWebModelContext(): void {\n if (!runtime) {\n return;\n }\n\n const { native, server, transport } = runtime;\n runtime = null;\n\n void server.close();\n void transport.close();\n\n // Restore the context that existed before we wrapped it with BrowserMcpServer.\n // We intentionally do NOT call cleanupWebMCPPolyfill() here — the polyfill\n // manages its own lifecycle (auto-init, testing shim) independently.\n replaceModelContext(native);\n}\n","import { cleanupWebModelContext, initializeWebModelContext } from './global.js';\n\nexport { cleanupWebModelContext, initializeWebModelContext };\n\nexport type {\n NativeModelContextBehavior,\n TransportConfiguration,\n WebModelContextInitOptions,\n} from './types.js';\n\nif (typeof window !== 'undefined' && typeof document !== 'undefined') {\n const options = window.__webModelContextOptions;\n const shouldAutoInitialize = options?.autoInitialize !== false;\n\n if (shouldAutoInitialize) {\n try {\n initializeWebModelContext(options);\n } catch (error) {\n console.error('[WebModelContext] Auto-initialization failed:', error);\n }\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-b/global",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "W3C Web Model Context API polyfill - Let AI agents like Claude, ChatGPT, and Gemini interact with your website via navigator.modelContext",
5
5
  "keywords": [
6
6
  "mcp",
@@ -49,11 +49,6 @@
49
49
  "import": "./dist/index.js",
50
50
  "default": "./dist/index.js"
51
51
  },
52
- "./testing": {
53
- "types": "./dist/testing.d.ts",
54
- "import": "./dist/testing.js",
55
- "default": "./dist/testing.js"
56
- },
57
52
  "./iife": {
58
53
  "script": "./dist/index.iife.js",
59
54
  "default": "./dist/index.iife.js"
@@ -67,10 +62,10 @@
67
62
  "dist"
68
63
  ],
69
64
  "dependencies": {
70
- "@composio/json-schema-to-zod": "^0.1.19",
71
- "zod-to-json-schema": "^3.25.1",
72
- "@mcp-b/transports": "1.3.0",
73
- "@mcp-b/webmcp-ts-sdk": "1.2.0"
65
+ "@mcp-b/transports": "1.4.0",
66
+ "@mcp-b/webmcp-polyfill": "0.3.0",
67
+ "@mcp-b/webmcp-types": "0.3.0",
68
+ "@mcp-b/webmcp-ts-sdk": "1.6.0"
74
69
  },
75
70
  "devDependencies": {
76
71
  "@types/node": "22.17.2",
@@ -81,11 +76,7 @@
81
76
  "playwright": "^1.58.0",
82
77
  "tsdown": "^0.15.10",
83
78
  "typescript": "^5.8.3",
84
- "vitest": "^4.0.18",
85
- "zod": "3.25.76"
86
- },
87
- "peerDependencies": {
88
- "zod": ">=3.25.76 <4 || >=4.1 <5"
79
+ "vitest": "^4.0.18"
89
80
  },
90
81
  "engines": {
91
82
  "node": ">=18"
@@ -102,12 +93,15 @@
102
93
  "lint": "biome lint --write .",
103
94
  "publish:dry": "pnpm publish --access public --dry-run",
104
95
  "publish:npm": "pnpm publish --access public",
105
- "test": "vitest run && vitest run --config vitest.node.config.ts",
96
+ "test": "vitest run",
106
97
  "test:browser": "vitest run --browser.headless",
107
98
  "test:browser:ui": "vitest --browser",
99
+ "test:conformance:matrix": "pnpm run test:conformance:polyfill && pnpm run test:conformance:native",
100
+ "test:conformance:native": "vitest run --config vitest.conformance.native.config.ts --browser.headless",
101
+ "test:conformance:polyfill": "vitest run --config vitest.conformance.polyfill.config.ts --browser.headless",
108
102
  "test:node": "vitest run --config vitest.node.config.ts",
109
103
  "test:watch": "vitest",
110
- "typecheck": "tsc --noEmit",
104
+ "typecheck": "tsc --noEmit && vitest run --typecheck --silent",
111
105
  "version:major": "pnpm version major --no-git-tag-version",
112
106
  "version:minor": "pnpm version minor --no-git-tag-version",
113
107
  "version:patch": "pnpm version patch --no-git-tag-version"
package/dist/testing.d.ts DELETED
@@ -1,71 +0,0 @@
1
- import { M as ToolInfo, N as ToolListItem, P as ToolResponse, g as ModelContextTesting } from "./types-DemXxUoc.js";
2
-
3
- //#region src/testing.d.ts
4
-
5
- /**
6
- * Test helper API exposed via @mcp-b/global/testing.
7
- * Wraps navigator.modelContextTesting and normalizes object-arg tool execution.
8
- */
9
- interface ModelContextTestHelper {
10
- /**
11
- * Execute a tool with object arguments.
12
- * Internally delegates to modelContextTesting.executeTool(name, JSON.stringify(args)).
13
- */
14
- executeTool(toolName: string, args?: Record<string, unknown>): Promise<unknown>;
15
- /**
16
- * List tools exposed by modelContextTesting.
17
- */
18
- listTools(): ToolInfo[];
19
- /**
20
- * Register for tool list updates.
21
- */
22
- onToolsChanged(callback: () => void): void;
23
- /**
24
- * Get recorded tool calls.
25
- * Only available when using the polyfill extensions.
26
- */
27
- getToolCalls(): Array<{
28
- toolName: string;
29
- arguments: Record<string, unknown>;
30
- timestamp: number;
31
- }>;
32
- /**
33
- * Clear recorded tool calls.
34
- * Only available when using the polyfill extensions.
35
- */
36
- clearToolCalls(): void;
37
- /**
38
- * Set mock response for a tool.
39
- * Only available when using the polyfill extensions.
40
- */
41
- setMockToolResponse(toolName: string, response: ToolResponse): void;
42
- /**
43
- * Clear mock response for a tool.
44
- * Only available when using the polyfill extensions.
45
- */
46
- clearMockToolResponse(toolName: string): void;
47
- /**
48
- * Clear all mock responses.
49
- * Only available when using the polyfill extensions.
50
- */
51
- clearAllMockToolResponses(): void;
52
- /**
53
- * Get registered tools from modelContext internals.
54
- * Only available when using the polyfill extensions.
55
- */
56
- getRegisteredTools(): ToolListItem[];
57
- /**
58
- * Reset polyfill testing state.
59
- * Only available when using the polyfill extensions.
60
- */
61
- reset(): void;
62
- }
63
- /**
64
- * Create a testing helper bound to modelContextTesting.
65
- *
66
- * @param testingOverride - Optional explicit testing API instance.
67
- */
68
- declare function createTestHelper(testingOverride?: ModelContextTesting): ModelContextTestHelper;
69
- //#endregion
70
- export { ModelContextTestHelper, createTestHelper };
71
- //# sourceMappingURL=testing.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"testing.d.ts","names":[],"sources":["../src/testing.ts"],"sourcesContent":[],"mappings":";;;;;;AAMA;;AAKiE,UALhD,sBAAA,CAKgD;EAKlD;;;;EA6CS,WAAA,CAAA,QAAA,EAAA,MAAA,EAAA,IAAA,CAAA,EAlDe,MAkDf,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,EAlDyC,OAkDzC,CAAA,OAAA,CAAA;EAAY;AAuEpC;;eApHe;;;;;;;;;kBAWG;;eAEH;;;;;;;;;;;;kDAcmC;;;;;;;;;;;;;;;wBAkB1B;;;;;;;;;;;;iBAuER,gBAAA,mBAAmC,sBAAsB"}
package/dist/testing.js DELETED
@@ -1,48 +0,0 @@
1
- //#region src/testing.ts
2
- function resolveTestingAPI(override) {
3
- if (override) return override;
4
- if (typeof window !== "undefined") {
5
- const bridgeTesting = window.__mcpBridge?.modelContextTesting;
6
- if (bridgeTesting) return bridgeTesting;
7
- }
8
- if (typeof navigator === "undefined" || !navigator.modelContextTesting) throw new Error("navigator.modelContextTesting is not available. Ensure @mcp-b/global is initialized first.");
9
- return navigator.modelContextTesting;
10
- }
11
- function getPolyfillExtensions(testing) {
12
- const candidate = testing;
13
- for (const method of [
14
- "getToolCalls",
15
- "clearToolCalls",
16
- "setMockToolResponse",
17
- "clearMockToolResponse",
18
- "clearAllMockToolResponses",
19
- "getRegisteredTools",
20
- "reset"
21
- ]) if (typeof candidate[method] !== "function") throw new Error(`modelContextTesting.${String(method)} is not available in this runtime. These helpers require the @mcp-b/global polyfill testing extensions.`);
22
- return candidate;
23
- }
24
- /**
25
- * Create a testing helper bound to modelContextTesting.
26
- *
27
- * @param testingOverride - Optional explicit testing API instance.
28
- */
29
- function createTestHelper(testingOverride) {
30
- const testing = resolveTestingAPI(testingOverride);
31
- const polyfillExtensions = () => getPolyfillExtensions(testing);
32
- return {
33
- executeTool: (toolName, args = {}) => testing.executeTool(toolName, JSON.stringify(args)),
34
- listTools: () => testing.listTools(),
35
- onToolsChanged: (callback) => testing.registerToolsChangedCallback(callback),
36
- getToolCalls: () => polyfillExtensions().getToolCalls(),
37
- clearToolCalls: () => polyfillExtensions().clearToolCalls(),
38
- setMockToolResponse: (toolName, response) => polyfillExtensions().setMockToolResponse(toolName, response),
39
- clearMockToolResponse: (toolName) => polyfillExtensions().clearMockToolResponse(toolName),
40
- clearAllMockToolResponses: () => polyfillExtensions().clearAllMockToolResponses(),
41
- getRegisteredTools: () => polyfillExtensions().getRegisteredTools(),
42
- reset: () => polyfillExtensions().reset()
43
- };
44
- }
45
-
46
- //#endregion
47
- export { createTestHelper };
48
- //# sourceMappingURL=testing.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"testing.js","names":[],"sources":["../src/testing.ts"],"sourcesContent":["import type { ModelContextTesting, ToolInfo, ToolListItem, ToolResponse } from './types.js';\n\n/**\n * Test helper API exposed via @mcp-b/global/testing.\n * Wraps navigator.modelContextTesting and normalizes object-arg tool execution.\n */\nexport interface ModelContextTestHelper {\n /**\n * Execute a tool with object arguments.\n * Internally delegates to modelContextTesting.executeTool(name, JSON.stringify(args)).\n */\n executeTool(toolName: string, args?: Record<string, unknown>): Promise<unknown>;\n\n /**\n * List tools exposed by modelContextTesting.\n */\n listTools(): ToolInfo[];\n\n /**\n * Register for tool list updates.\n */\n onToolsChanged(callback: () => void): void;\n\n /**\n * Get recorded tool calls.\n * Only available when using the polyfill extensions.\n */\n getToolCalls(): Array<{\n toolName: string;\n arguments: Record<string, unknown>;\n timestamp: number;\n }>;\n\n /**\n * Clear recorded tool calls.\n * Only available when using the polyfill extensions.\n */\n clearToolCalls(): void;\n\n /**\n * Set mock response for a tool.\n * Only available when using the polyfill extensions.\n */\n setMockToolResponse(toolName: string, response: ToolResponse): void;\n\n /**\n * Clear mock response for a tool.\n * Only available when using the polyfill extensions.\n */\n clearMockToolResponse(toolName: string): void;\n\n /**\n * Clear all mock responses.\n * Only available when using the polyfill extensions.\n */\n clearAllMockToolResponses(): void;\n\n /**\n * Get registered tools from modelContext internals.\n * Only available when using the polyfill extensions.\n */\n getRegisteredTools(): ToolListItem[];\n\n /**\n * Reset polyfill testing state.\n * Only available when using the polyfill extensions.\n */\n reset(): void;\n}\n\ntype PolyfillTestingExtensions = Pick<\n ModelContextTesting,\n | 'getToolCalls'\n | 'clearToolCalls'\n | 'setMockToolResponse'\n | 'clearMockToolResponse'\n | 'clearAllMockToolResponses'\n | 'getRegisteredTools'\n | 'reset'\n>;\n\nfunction resolveTestingAPI(override?: ModelContextTesting): ModelContextTesting {\n if (override) {\n return override;\n }\n\n // Prefer bridge-bound instance to avoid touching deprecated navigator accessor.\n if (typeof window !== 'undefined') {\n const bridgeTesting = window.__mcpBridge?.modelContextTesting;\n if (bridgeTesting) {\n return bridgeTesting;\n }\n }\n\n if (typeof navigator === 'undefined' || !navigator.modelContextTesting) {\n throw new Error(\n 'navigator.modelContextTesting is not available. Ensure @mcp-b/global is initialized first.'\n );\n }\n\n return navigator.modelContextTesting;\n}\n\nfunction getPolyfillExtensions(testing: ModelContextTesting): PolyfillTestingExtensions {\n const candidate = testing as ModelContextTesting & Partial<PolyfillTestingExtensions>;\n const requiredMethods: Array<keyof PolyfillTestingExtensions> = [\n 'getToolCalls',\n 'clearToolCalls',\n 'setMockToolResponse',\n 'clearMockToolResponse',\n 'clearAllMockToolResponses',\n 'getRegisteredTools',\n 'reset',\n ];\n\n for (const method of requiredMethods) {\n if (typeof candidate[method] !== 'function') {\n throw new Error(\n `modelContextTesting.${String(method)} is not available in this runtime. ` +\n 'These helpers require the @mcp-b/global polyfill testing extensions.'\n );\n }\n }\n\n return candidate as PolyfillTestingExtensions;\n}\n\n/**\n * Create a testing helper bound to modelContextTesting.\n *\n * @param testingOverride - Optional explicit testing API instance.\n */\nexport function createTestHelper(testingOverride?: ModelContextTesting): ModelContextTestHelper {\n const testing = resolveTestingAPI(testingOverride);\n const polyfillExtensions = () => getPolyfillExtensions(testing);\n\n return {\n executeTool: (toolName: string, args: Record<string, unknown> = {}) =>\n testing.executeTool(toolName, JSON.stringify(args)),\n listTools: () => testing.listTools(),\n onToolsChanged: (callback: () => void) => testing.registerToolsChangedCallback(callback),\n getToolCalls: () => polyfillExtensions().getToolCalls(),\n clearToolCalls: () => polyfillExtensions().clearToolCalls(),\n setMockToolResponse: (toolName: string, response: ToolResponse) =>\n polyfillExtensions().setMockToolResponse(toolName, response),\n clearMockToolResponse: (toolName: string) =>\n polyfillExtensions().clearMockToolResponse(toolName),\n clearAllMockToolResponses: () => polyfillExtensions().clearAllMockToolResponses(),\n getRegisteredTools: () => polyfillExtensions().getRegisteredTools(),\n reset: () => polyfillExtensions().reset(),\n };\n}\n"],"mappings":";AAiFA,SAAS,kBAAkB,UAAqD;AAC9E,KAAI,SACF,QAAO;AAIT,KAAI,OAAO,WAAW,aAAa;EACjC,MAAM,gBAAgB,OAAO,aAAa;AAC1C,MAAI,cACF,QAAO;;AAIX,KAAI,OAAO,cAAc,eAAe,CAAC,UAAU,oBACjD,OAAM,IAAI,MACR,6FACD;AAGH,QAAO,UAAU;;AAGnB,SAAS,sBAAsB,SAAyD;CACtF,MAAM,YAAY;AAWlB,MAAK,MAAM,UAVqD;EAC9D;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAGC,KAAI,OAAO,UAAU,YAAY,WAC/B,OAAM,IAAI,MACR,uBAAuB,OAAO,OAAO,CAAC,yGAEvC;AAIL,QAAO;;;;;;;AAQT,SAAgB,iBAAiB,iBAA+D;CAC9F,MAAM,UAAU,kBAAkB,gBAAgB;CAClD,MAAM,2BAA2B,sBAAsB,QAAQ;AAE/D,QAAO;EACL,cAAc,UAAkB,OAAgC,EAAE,KAChE,QAAQ,YAAY,UAAU,KAAK,UAAU,KAAK,CAAC;EACrD,iBAAiB,QAAQ,WAAW;EACpC,iBAAiB,aAAyB,QAAQ,6BAA6B,SAAS;EACxF,oBAAoB,oBAAoB,CAAC,cAAc;EACvD,sBAAsB,oBAAoB,CAAC,gBAAgB;EAC3D,sBAAsB,UAAkB,aACtC,oBAAoB,CAAC,oBAAoB,UAAU,SAAS;EAC9D,wBAAwB,aACtB,oBAAoB,CAAC,sBAAsB,SAAS;EACtD,iCAAiC,oBAAoB,CAAC,2BAA2B;EACjF,0BAA0B,oBAAoB,CAAC,oBAAoB;EACnE,aAAa,oBAAoB,CAAC,OAAO;EAC1C"}