@mcp-b/global 1.2.1 → 1.3.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 CHANGED
@@ -1,88 +1,188 @@
1
1
  import { IframeChildTransport, TabServerTransport } from "@mcp-b/transports";
2
2
  import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, Server } from "@mcp-b/webmcp-ts-sdk";
3
- import { jsonSchemaToZod } from "@composio/json-schema-to-zod";
3
+ import { jsonSchemaToZod } from "@n8n/json-schema-to-zod";
4
4
  import { z } from "zod";
5
5
  import { zodToJsonSchema as zodToJsonSchema$1 } from "zod-to-json-schema";
6
6
 
7
- //#region src/validation.ts
7
+ //#region src/logger.ts
8
+ /**
9
+ * @license
10
+ * Copyright 2025 Google LLC
11
+ * SPDX-License-Identifier: Apache-2.0
12
+ */
8
13
  /**
9
- * Detect if a schema is a Zod schema object (Record<string, ZodType>)
10
- * or a JSON Schema object
14
+ * Lightweight logging system for @mcp-b/global
15
+ *
16
+ * Design Decision: This implements a custom logger instead of using the 'debug'
17
+ * package to reduce bundle size and eliminate external dependencies in the
18
+ * browser build. The API is intentionally simpler, focusing on the specific
19
+ * needs of browser-based MCP implementations.
20
+ *
21
+ * Configuration via localStorage:
22
+ * - localStorage.setItem('WEBMCP_DEBUG', '*') - enable all debug logging
23
+ * - localStorage.setItem('WEBMCP_DEBUG', 'WebModelContext') - enable specific namespace
24
+ * - localStorage.setItem('WEBMCP_DEBUG', 'WebModelContext,NativeAdapter') - multiple namespaces
25
+ * - localStorage.setItem('WEBMCP_DEBUG', 'WebModelContext:') - enable namespace and sub-namespaces
26
+ * - localStorage.removeItem('WEBMCP_DEBUG') - disable debug logging (default)
27
+ *
28
+ * Environment Support:
29
+ * - Automatically detects localStorage availability
30
+ * - Gracefully degrades to "disabled" state when localStorage is inaccessible
31
+ * - Never throws errors from configuration checks (safe for private browsing mode)
32
+ */
33
+ /** localStorage key for debug configuration */
34
+ const DEBUG_CONFIG_KEY = "WEBMCP_DEBUG";
35
+ /**
36
+ * Check if debug logging is enabled for a namespace
37
+ *
38
+ * Supports namespace hierarchy via colons. Setting 'WebModelContext' will match
39
+ * both 'WebModelContext' and 'WebModelContext:init', but NOT 'WebModelContextTesting'.
40
+ */
41
+ function isDebugEnabled(namespace) {
42
+ if (typeof window === "undefined" || !window.localStorage) return false;
43
+ try {
44
+ const debugConfig = localStorage.getItem(DEBUG_CONFIG_KEY);
45
+ if (!debugConfig) return false;
46
+ if (debugConfig === "*") return true;
47
+ return debugConfig.split(",").map((p) => p.trim()).some((pattern) => namespace === pattern || namespace.startsWith(`${pattern}:`));
48
+ } catch (err) {
49
+ if (typeof console !== "undefined" && console.warn) {
50
+ const message = err instanceof Error ? err.message : String(err);
51
+ console.warn(`[WebMCP] localStorage access failed, debug logging disabled: ${message}`);
52
+ }
53
+ return false;
54
+ }
55
+ }
56
+ /**
57
+ * No-op function for disabled log levels
58
+ */
59
+ const noop = () => {};
60
+ /**
61
+ * Create a namespaced logger
62
+ *
63
+ * Uses .bind() to prepend namespace prefixes to console methods without manual
64
+ * string concatenation. Debug enablement is determined at logger creation time
65
+ * for performance - changes to localStorage after creation won't affect existing
66
+ * loggers. Refresh the page to apply new WEBMCP_DEBUG settings.
67
+ *
68
+ * @param namespace - Namespace for the logger (e.g., 'WebModelContext', 'NativeAdapter')
69
+ * @returns Logger instance with debug, info, warn, error methods
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * const logger = createLogger('WebModelContext');
74
+ * logger.debug('Tool registered:', toolName); // Only shown if WEBMCP_DEBUG includes 'WebModelContext'
75
+ * logger.error('Execution failed:', error); // Always enabled
76
+ * ```
11
77
  */
78
+ function createLogger(namespace) {
79
+ const prefix = `[${namespace}]`;
80
+ const isDebug = isDebugEnabled(namespace);
81
+ const boundWarn = console.warn.bind(console, prefix);
82
+ const boundError = console.error.bind(console, prefix);
83
+ const boundLog = console.log.bind(console, prefix);
84
+ return {
85
+ warn: boundWarn,
86
+ error: boundError,
87
+ debug: isDebug ? boundLog : noop,
88
+ info: isDebug ? boundLog : noop
89
+ };
90
+ }
91
+
92
+ //#endregion
93
+ //#region src/validation.ts
94
+ const logger$2 = createLogger("WebModelContext");
95
+ const isRecord = (value) => typeof value === "object" && value !== null;
96
+ const stripSchemaMeta = (schema) => {
97
+ const { $schema: _,...rest } = schema;
98
+ return rest;
99
+ };
100
+ const isOptionalSchema = (schema) => {
101
+ const typeName = schema._def?.typeName;
102
+ return typeName === "ZodOptional" || typeName === "ZodDefault";
103
+ };
12
104
  function isZodSchema(schema) {
13
- if (typeof schema !== "object" || schema === null) return false;
105
+ if (!isRecord(schema)) return false;
14
106
  if ("type" in schema && typeof schema.type === "string") return false;
15
107
  const values = Object.values(schema);
16
108
  if (values.length === 0) return false;
17
- return values.some((val) => val instanceof z.ZodType);
109
+ return values.some((v) => isRecord(v) && "_def" in v);
110
+ }
111
+ function zodToJsonSchema(schema) {
112
+ const properties = {};
113
+ const required = [];
114
+ for (const [key, zodSchema] of Object.entries(schema)) {
115
+ properties[key] = stripSchemaMeta(zodToJsonSchema$1(zodSchema, {
116
+ strictUnions: true,
117
+ $refStrategy: "none"
118
+ }));
119
+ if (!isOptionalSchema(zodSchema)) required.push(key);
120
+ }
121
+ const result = {
122
+ type: "object",
123
+ properties
124
+ };
125
+ if (required.length > 0) result.required = required;
126
+ return result;
18
127
  }
19
- /**
20
- * Convert JSON Schema to Zod validator
21
- * Uses @composio/json-schema-to-zod for conversion
22
- */
23
128
  function jsonSchemaToZod$1(jsonSchema) {
24
129
  try {
25
130
  return jsonSchemaToZod(jsonSchema);
26
131
  } catch (error) {
27
- console.warn("[Web Model Context] Failed to convert JSON Schema to Zod:", error);
132
+ logger$2.warn("jsonSchemaToZod failed:", error);
28
133
  return z.object({}).passthrough();
29
134
  }
30
135
  }
31
- /**
32
- * Convert Zod schema object to JSON Schema
33
- * Uses zod-to-json-schema package for comprehensive conversion
34
- *
35
- * @param schema - Record of Zod type definitions (e.g., { name: z.string(), age: z.number() })
36
- * @returns JSON Schema object compatible with MCP InputSchema
37
- */
38
- function zodToJsonSchema(schema) {
39
- const { $schema: _,...rest } = zodToJsonSchema$1(z.object(schema), {
40
- $refStrategy: "none",
41
- target: "jsonSchema7"
42
- });
43
- return rest;
44
- }
45
- /**
46
- * Normalize a schema to both JSON Schema and Zod formats
47
- * Detects which format is provided and converts to the other
48
- */
49
136
  function normalizeSchema(schema) {
50
- if (isZodSchema(schema)) return {
51
- jsonSchema: zodToJsonSchema(schema),
52
- zodValidator: z.object(schema)
53
- };
54
- const jsonSchema = schema;
137
+ if (isZodSchema(schema)) {
138
+ const jsonSchema = zodToJsonSchema(schema);
139
+ return {
140
+ jsonSchema,
141
+ zodValidator: jsonSchemaToZod$1(jsonSchema)
142
+ };
143
+ }
55
144
  return {
56
- jsonSchema,
57
- zodValidator: jsonSchemaToZod$1(jsonSchema)
145
+ jsonSchema: schema,
146
+ zodValidator: jsonSchemaToZod$1(schema)
58
147
  };
59
148
  }
60
- /**
61
- * Validate data with Zod schema and return formatted result
62
- */
63
149
  function validateWithZod(data, validator) {
64
150
  const result = validator.safeParse(data);
65
- if (!result.success) return {
66
- success: false,
67
- error: `Validation failed:\n${result.error.errors.map((err) => ` - ${err.path.join(".") || "root"}: ${err.message}`).join("\n")}`
68
- };
69
- return {
151
+ if (result.success) return {
70
152
  success: true,
71
153
  data: result.data
72
154
  };
155
+ return {
156
+ success: false,
157
+ error: `Validation failed:\n${result.error.issues.map((err) => ` - ${err.path.join(".") || "root"}: ${err.message}`).join("\n")}`
158
+ };
73
159
  }
74
160
 
75
161
  //#endregion
76
162
  //#region src/global.ts
163
+ const logger$1 = createLogger("WebModelContext");
164
+ const nativeLogger = createLogger("NativeAdapter");
165
+ const bridgeLogger = createLogger("MCPBridge");
166
+ const testingLogger = createLogger("ModelContextTesting");
167
+ /**
168
+ * Marker property name used to identify polyfill implementations.
169
+ * This constant ensures single source of truth for the marker used in
170
+ * both detection (detectNativeAPI) and definition (WebModelContextTesting).
171
+ */
172
+ const POLYFILL_MARKER_PROPERTY = "__isWebMCPPolyfill";
77
173
  /**
78
174
  * Detect if the native Chromium Web Model Context API is available.
79
175
  * Checks for both navigator.modelContext and navigator.modelContextTesting,
80
- * and verifies they are native implementations (not polyfills) by examining
81
- * the constructor name.
176
+ * and verifies they are native implementations (not polyfills).
177
+ *
178
+ * Detection uses a marker property (`__isWebMCPPolyfill`) on the testing API
179
+ * to reliably distinguish polyfills from native implementations. This approach
180
+ * works correctly even when class names are minified in production builds.
82
181
  *
83
182
  * @returns Detection result with flags for native context and testing API availability
84
183
  */
85
184
  function detectNativeAPI() {
185
+ /* c8 ignore next 2 */
86
186
  if (typeof window === "undefined" || typeof navigator === "undefined") return {
87
187
  hasNativeContext: false,
88
188
  hasNativeTesting: false
@@ -90,10 +190,10 @@ function detectNativeAPI() {
90
190
  const modelContext = navigator.modelContext;
91
191
  const modelContextTesting = navigator.modelContextTesting;
92
192
  if (!modelContext || !modelContextTesting) return {
93
- hasNativeContext: false,
94
- hasNativeTesting: false
193
+ hasNativeContext: Boolean(modelContext),
194
+ hasNativeTesting: Boolean(modelContextTesting)
95
195
  };
96
- if ((modelContextTesting.constructor?.name || "").includes("WebModelContext")) return {
196
+ if (POLYFILL_MARKER_PROPERTY in modelContextTesting && modelContextTesting[POLYFILL_MARKER_PROPERTY] === true) return {
97
197
  hasNativeContext: false,
98
198
  hasNativeTesting: false
99
199
  };
@@ -133,7 +233,6 @@ var NativeModelContextAdapter = class {
133
233
  this.nativeContext = nativeContext;
134
234
  this.nativeTesting = nativeTesting;
135
235
  this.nativeTesting.registerToolsChangedCallback(() => {
136
- console.log("[Native Adapter] Tool change detected from native API");
137
236
  this.syncToolsFromNative();
138
237
  });
139
238
  this.syncToolsFromNative();
@@ -150,7 +249,6 @@ var NativeModelContextAdapter = class {
150
249
  this.syncInProgress = true;
151
250
  try {
152
251
  const nativeTools = this.nativeTesting.listTools();
153
- console.log(`[Native Adapter] Syncing ${nativeTools.length} tools from native API`);
154
252
  this.bridge.tools.clear();
155
253
  for (const toolInfo of nativeTools) try {
156
254
  const inputSchema = JSON.parse(toolInfo.inputSchema);
@@ -166,7 +264,7 @@ var NativeModelContextAdapter = class {
166
264
  };
167
265
  this.bridge.tools.set(toolInfo.name, validatedTool);
168
266
  } catch (error) {
169
- console.error(`[Native Adapter] Failed to sync tool "${toolInfo.name}":`, error);
267
+ nativeLogger.error(`Failed to sync tool "${toolInfo.name}":`, error);
170
268
  }
171
269
  this.notifyMCPServers();
172
270
  } finally {
@@ -220,26 +318,34 @@ var NativeModelContextAdapter = class {
220
318
  }
221
319
  /**
222
320
  * Provides context (tools) to AI models via the native API.
223
- * Delegates to navigator.modelContext.provideContext().
321
+ * Converts Zod schemas to JSON Schema before passing to native API.
224
322
  * Tool change callback will fire and trigger sync automatically.
225
323
  *
226
324
  * @param {ModelContextInput} context - Context containing tools to register
227
325
  */
228
326
  provideContext(context) {
229
- console.log("[Native Adapter] Delegating provideContext to native API");
230
- this.nativeContext.provideContext(context);
327
+ const { tools,...rest } = context;
328
+ const normalizedContext = { ...rest };
329
+ if (tools) normalizedContext.tools = tools.map((tool) => ({
330
+ ...tool,
331
+ inputSchema: normalizeSchema(tool.inputSchema).jsonSchema
332
+ }));
333
+ this.nativeContext.provideContext(normalizedContext);
231
334
  }
232
335
  /**
233
336
  * Registers a single tool dynamically via the native API.
234
- * Delegates to navigator.modelContext.registerTool().
337
+ * Converts Zod schemas to JSON Schema before passing to native API.
235
338
  * Tool change callback will fire and trigger sync automatically.
236
339
  *
237
340
  * @param {ToolDescriptor} tool - The tool descriptor to register
238
341
  * @returns {{unregister: () => void}} Object with unregister function
239
342
  */
240
343
  registerTool(tool) {
241
- console.log(`[Native Adapter] Delegating registerTool("${tool.name}") to native API`);
242
- return this.nativeContext.registerTool(tool);
344
+ const normalizedTool = {
345
+ ...tool,
346
+ inputSchema: normalizeSchema(tool.inputSchema).jsonSchema
347
+ };
348
+ return this.nativeContext.registerTool(normalizedTool);
243
349
  }
244
350
  /**
245
351
  * Unregisters a tool by name via the native API.
@@ -248,7 +354,6 @@ var NativeModelContextAdapter = class {
248
354
  * @param {string} name - Name of the tool to unregister
249
355
  */
250
356
  unregisterTool(name) {
251
- console.log(`[Native Adapter] Delegating unregisterTool("${name}") to native API`);
252
357
  this.nativeContext.unregisterTool(name);
253
358
  }
254
359
  /**
@@ -256,25 +361,25 @@ var NativeModelContextAdapter = class {
256
361
  * Delegates to navigator.modelContext.clearContext().
257
362
  */
258
363
  clearContext() {
259
- console.log("[Native Adapter] Delegating clearContext to native API");
260
364
  this.nativeContext.clearContext();
261
365
  }
262
366
  /**
263
367
  * Executes a tool via the native API.
264
368
  * Delegates to navigator.modelContextTesting.executeTool() with JSON string args.
369
+ * Note: skipValidation option is ignored - native API handles its own validation.
265
370
  *
266
371
  * @param {string} toolName - Name of the tool to execute
267
372
  * @param {Record<string, unknown>} args - Arguments to pass to the tool
373
+ * @param {Object} [_options] - Execution options (ignored for native adapter)
268
374
  * @returns {Promise<ToolResponse>} The tool's response in MCP format
269
375
  * @internal
270
376
  */
271
- async executeTool(toolName, args) {
272
- console.log(`[Native Adapter] Executing tool "${toolName}" via native API`);
377
+ async executeTool(toolName, args, _options) {
273
378
  try {
274
379
  const result = await this.nativeTesting.executeTool(toolName, JSON.stringify(args));
275
380
  return this.convertToToolResponse(result);
276
381
  } catch (error) {
277
- console.error(`[Native Adapter] Error executing tool "${toolName}":`, error);
382
+ nativeLogger.error(`Error executing tool "${toolName}":`, error);
278
383
  return {
279
384
  content: [{
280
385
  type: "text",
@@ -305,7 +410,7 @@ var NativeModelContextAdapter = class {
305
410
  * This is a polyfill-only feature.
306
411
  */
307
412
  registerResource(_resource) {
308
- console.warn("[Native Adapter] registerResource is not supported by native API");
413
+ nativeLogger.warn("registerResource is not supported by native API");
309
414
  return { unregister: () => {} };
310
415
  }
311
416
  /**
@@ -313,7 +418,7 @@ var NativeModelContextAdapter = class {
313
418
  * Note: Native Chromium API does not yet support resources.
314
419
  */
315
420
  unregisterResource(_uri) {
316
- console.warn("[Native Adapter] unregisterResource is not supported by native API");
421
+ nativeLogger.warn("unregisterResource is not supported by native API");
317
422
  }
318
423
  /**
319
424
  * Lists all registered resources.
@@ -343,7 +448,7 @@ var NativeModelContextAdapter = class {
343
448
  * This is a polyfill-only feature.
344
449
  */
345
450
  registerPrompt(_prompt) {
346
- console.warn("[Native Adapter] registerPrompt is not supported by native API");
451
+ nativeLogger.warn("registerPrompt is not supported by native API");
347
452
  return { unregister: () => {} };
348
453
  }
349
454
  /**
@@ -351,7 +456,7 @@ var NativeModelContextAdapter = class {
351
456
  * Note: Native Chromium API does not yet support prompts.
352
457
  */
353
458
  unregisterPrompt(_name) {
354
- console.warn("[Native Adapter] unregisterPrompt is not supported by native API");
459
+ nativeLogger.warn("unregisterPrompt is not supported by native API");
355
460
  }
356
461
  /**
357
462
  * Lists all registered prompts.
@@ -406,7 +511,6 @@ var NativeModelContextAdapter = class {
406
511
  * This is handled by the polyfill.
407
512
  */
408
513
  async createMessage(params) {
409
- console.log("[Native Adapter] Requesting sampling from client");
410
514
  const underlyingServer = this.bridge.tabServer.server;
411
515
  if (!underlyingServer?.createMessage) throw new Error("Sampling is not supported: no connected client with sampling capability");
412
516
  return underlyingServer.createMessage(params);
@@ -417,7 +521,6 @@ var NativeModelContextAdapter = class {
417
521
  * This is handled by the polyfill.
418
522
  */
419
523
  async elicitInput(params) {
420
- console.log("[Native Adapter] Requesting elicitation from client");
421
524
  const underlyingServer = this.bridge.tabServer.server;
422
525
  if (!underlyingServer?.elicitInput) throw new Error("Elicitation is not supported: no connected client with elicitation capability");
423
526
  return underlyingServer.elicitInput(params);
@@ -490,6 +593,15 @@ const RAPID_DUPLICATE_WINDOW_MS = 50;
490
593
  * @implements {ModelContextTesting}
491
594
  */
492
595
  var WebModelContextTesting = class {
596
+ /**
597
+ * Marker property to identify this as a polyfill implementation.
598
+ * Used by detectNativeAPI() to distinguish polyfill from native Chromium API.
599
+ * This approach works reliably even when class names are minified in production builds.
600
+ *
601
+ * @see POLYFILL_MARKER_PROPERTY - The constant defining this property name
602
+ * @see MayHavePolyfillMarker - The interface for type-safe detection
603
+ */
604
+ [POLYFILL_MARKER_PROPERTY] = true;
493
605
  toolCallHistory = [];
494
606
  mockResponses = /* @__PURE__ */ new Map();
495
607
  toolsChangedCallbacks = /* @__PURE__ */ new Set();
@@ -547,7 +659,7 @@ var WebModelContextTesting = class {
547
659
  for (const callback of this.toolsChangedCallbacks) try {
548
660
  callback();
549
661
  } catch (error) {
550
- console.error("[Model Context Testing] Error in tools changed callback:", error);
662
+ testingLogger.error("Error in tools changed callback:", error);
551
663
  }
552
664
  }
553
665
  /**
@@ -561,7 +673,6 @@ var WebModelContextTesting = class {
561
673
  * @throws {Error} If the tool does not exist
562
674
  */
563
675
  async executeTool(toolName, inputArgsJson) {
564
- console.log(`[Model Context Testing] Executing tool: ${toolName}`);
565
676
  let args;
566
677
  try {
567
678
  args = JSON.parse(inputArgsJson);
@@ -598,7 +709,6 @@ var WebModelContextTesting = class {
598
709
  */
599
710
  registerToolsChangedCallback(callback) {
600
711
  this.toolsChangedCallbacks.add(callback);
601
- console.log("[Model Context Testing] Tools changed callback registered");
602
712
  }
603
713
  /**
604
714
  * Gets all tool calls that have been recorded (polyfill extension).
@@ -613,7 +723,6 @@ var WebModelContextTesting = class {
613
723
  */
614
724
  clearToolCalls() {
615
725
  this.toolCallHistory = [];
616
- console.log("[Model Context Testing] Tool call history cleared");
617
726
  }
618
727
  /**
619
728
  * Sets a mock response for a specific tool (polyfill extension).
@@ -624,7 +733,6 @@ var WebModelContextTesting = class {
624
733
  */
625
734
  setMockToolResponse(toolName, response) {
626
735
  this.mockResponses.set(toolName, response);
627
- console.log(`[Model Context Testing] Mock response set for tool: ${toolName}`);
628
736
  }
629
737
  /**
630
738
  * Clears the mock response for a specific tool (polyfill extension).
@@ -633,14 +741,12 @@ var WebModelContextTesting = class {
633
741
  */
634
742
  clearMockToolResponse(toolName) {
635
743
  this.mockResponses.delete(toolName);
636
- console.log(`[Model Context Testing] Mock response cleared for tool: ${toolName}`);
637
744
  }
638
745
  /**
639
746
  * Clears all mock tool responses (polyfill extension).
640
747
  */
641
748
  clearAllMockToolResponses() {
642
749
  this.mockResponses.clear();
643
- console.log("[Model Context Testing] All mock responses cleared");
644
750
  }
645
751
  /**
646
752
  * Gets the current tools registered in the system (polyfill extension).
@@ -657,7 +763,6 @@ var WebModelContextTesting = class {
657
763
  reset() {
658
764
  this.clearToolCalls();
659
765
  this.clearAllMockToolResponses();
660
- console.log("[Model Context Testing] Testing state reset");
661
766
  }
662
767
  };
663
768
  /**
@@ -763,14 +868,13 @@ var WebModelContext = class {
763
868
  * @throws {Error} If a name/uri collides with existing dynamic items
764
869
  */
765
870
  provideContext(context) {
766
- const toolCount = context.tools?.length ?? 0;
767
- const resourceCount = context.resources?.length ?? 0;
768
- const promptCount = context.prompts?.length ?? 0;
769
- console.log(`[Web Model Context] provideContext: ${toolCount} tools, ${resourceCount} resources, ${promptCount} prompts`);
770
871
  this.provideContextTools.clear();
771
872
  this.provideContextResources.clear();
772
873
  this.provideContextPrompts.clear();
773
874
  for (const tool of context.tools ?? []) {
875
+ if (tool.name.startsWith("_")) logger$1.warn(`⚠️ Warning: Tool name "${tool.name}" starts with underscore. This may cause compatibility issues with some MCP clients. Consider using a letter as the first character.`);
876
+ if (/^[0-9]/.test(tool.name)) logger$1.warn(`⚠️ Warning: Tool name "${tool.name}" starts with a number. This may cause compatibility issues. Consider using a letter as the first character.`);
877
+ if (tool.name.startsWith("-")) logger$1.warn(`⚠️ Warning: Tool name "${tool.name}" starts with hyphen. This may cause compatibility issues. Consider using a letter as the first character.`);
774
878
  if (this.dynamicTools.has(tool.name)) throw new Error(`[Web Model Context] Tool name collision: "${tool.name}" is already registered via registerTool(). Please use a different name or unregister the dynamic tool first.`);
775
879
  const { jsonSchema: inputJson, zodValidator: inputZod } = normalizeSchema(tool.inputSchema);
776
880
  const normalizedOutput = tool.outputSchema ? normalizeSchema(tool.outputSchema) : null;
@@ -808,11 +912,11 @@ var WebModelContext = class {
808
912
  * @private
809
913
  */
810
914
  validateResource(resource) {
811
- const templateParamRegex = /\{([^}]+)\}/g;
915
+ const templateParamRegex = /\{([^}]{1,100})\}/g;
812
916
  const templateParams = [];
813
917
  for (const match of resource.uri.matchAll(templateParamRegex)) {
814
918
  const paramName = match[1];
815
- if (paramName) templateParams.push(paramName);
919
+ templateParams.push(paramName);
816
920
  }
817
921
  return {
818
922
  uri: resource.uri,
@@ -853,11 +957,13 @@ var WebModelContext = class {
853
957
  * @throws {Error} If tool name collides with existing tools
854
958
  */
855
959
  registerTool(tool) {
856
- console.log(`[Web Model Context] Registering tool dynamically: ${tool.name}`);
960
+ if (tool.name.startsWith("_")) logger$1.warn(`⚠️ Warning: Tool name "${tool.name}" starts with underscore. This may cause compatibility issues with some MCP clients. Consider using a letter as the first character.`);
961
+ if (/^[0-9]/.test(tool.name)) logger$1.warn(`⚠️ Warning: Tool name "${tool.name}" starts with a number. This may cause compatibility issues. Consider using a letter as the first character.`);
962
+ if (tool.name.startsWith("-")) logger$1.warn(`⚠️ Warning: Tool name "${tool.name}" starts with hyphen. This may cause compatibility issues. Consider using a letter as the first character.`);
857
963
  const now = Date.now();
858
964
  const lastRegistration = this.toolRegistrationTimestamps.get(tool.name);
859
965
  if (lastRegistration && now - lastRegistration < RAPID_DUPLICATE_WINDOW_MS) {
860
- console.warn(`[Web Model Context] Tool "${tool.name}" registered multiple times within ${RAPID_DUPLICATE_WINDOW_MS}ms. This is likely due to React Strict Mode double-mounting. Ignoring duplicate registration.`);
966
+ logger$1.warn(`Tool "${tool.name}" registered multiple times within ${RAPID_DUPLICATE_WINDOW_MS}ms. This is likely due to React Strict Mode double-mounting. Ignoring duplicate registration.`);
861
967
  const existingUnregister = this.toolUnregisterFunctions.get(tool.name);
862
968
  if (existingUnregister) return { unregister: existingUnregister };
863
969
  }
@@ -880,10 +986,9 @@ var WebModelContext = class {
880
986
  this.updateBridgeTools();
881
987
  this.scheduleListChanged("tools");
882
988
  const unregisterFn = () => {
883
- console.log(`[Web Model Context] Unregistering tool: ${tool.name}`);
884
989
  if (this.provideContextTools.has(tool.name)) throw new Error(`[Web Model Context] Cannot unregister tool "${tool.name}": This tool was registered via provideContext(). Use provideContext() to update the base tool set.`);
885
990
  if (!this.dynamicTools.has(tool.name)) {
886
- console.warn(`[Web Model Context] Tool "${tool.name}" is not registered, ignoring unregister call`);
991
+ logger$1.warn(`Tool "${tool.name}" is not registered, ignoring unregister call`);
887
992
  return;
888
993
  }
889
994
  this.dynamicTools.delete(tool.name);
@@ -904,11 +1009,10 @@ var WebModelContext = class {
904
1009
  * @throws {Error} If resource URI collides with existing resources
905
1010
  */
906
1011
  registerResource(resource) {
907
- console.log(`[Web Model Context] Registering resource dynamically: ${resource.uri}`);
908
1012
  const now = Date.now();
909
1013
  const lastRegistration = this.resourceRegistrationTimestamps.get(resource.uri);
910
1014
  if (lastRegistration && now - lastRegistration < RAPID_DUPLICATE_WINDOW_MS) {
911
- console.warn(`[Web Model Context] Resource "${resource.uri}" registered multiple times within ${RAPID_DUPLICATE_WINDOW_MS}ms. This is likely due to React Strict Mode double-mounting. Ignoring duplicate registration.`);
1015
+ logger$1.warn(`Resource "${resource.uri}" registered multiple times within ${RAPID_DUPLICATE_WINDOW_MS}ms. This is likely due to React Strict Mode double-mounting. Ignoring duplicate registration.`);
912
1016
  const existingUnregister = this.resourceUnregisterFunctions.get(resource.uri);
913
1017
  if (existingUnregister) return { unregister: existingUnregister };
914
1018
  }
@@ -920,10 +1024,9 @@ var WebModelContext = class {
920
1024
  this.updateBridgeResources();
921
1025
  this.scheduleListChanged("resources");
922
1026
  const unregisterFn = () => {
923
- console.log(`[Web Model Context] Unregistering resource: ${resource.uri}`);
924
1027
  if (this.provideContextResources.has(resource.uri)) throw new Error(`[Web Model Context] Cannot unregister resource "${resource.uri}": This resource was registered via provideContext(). Use provideContext() to update the base resource set.`);
925
1028
  if (!this.dynamicResources.has(resource.uri)) {
926
- console.warn(`[Web Model Context] Resource "${resource.uri}" is not registered, ignoring unregister call`);
1029
+ logger$1.warn(`Resource "${resource.uri}" is not registered, ignoring unregister call`);
927
1030
  return;
928
1031
  }
929
1032
  this.dynamicResources.delete(resource.uri);
@@ -942,11 +1045,10 @@ var WebModelContext = class {
942
1045
  * @param {string} uri - URI of the resource to unregister
943
1046
  */
944
1047
  unregisterResource(uri) {
945
- console.log(`[Web Model Context] Unregistering resource: ${uri}`);
946
1048
  const inProvideContext = this.provideContextResources.has(uri);
947
1049
  const inDynamic = this.dynamicResources.has(uri);
948
1050
  if (!inProvideContext && !inDynamic) {
949
- console.warn(`[Web Model Context] Resource "${uri}" is not registered, ignoring unregister call`);
1051
+ logger$1.warn(`Resource "${uri}" is not registered, ignoring unregister call`);
950
1052
  return;
951
1053
  }
952
1054
  if (inProvideContext) this.provideContextResources.delete(uri);
@@ -995,11 +1097,10 @@ var WebModelContext = class {
995
1097
  * @throws {Error} If prompt name collides with existing prompts
996
1098
  */
997
1099
  registerPrompt(prompt) {
998
- console.log(`[Web Model Context] Registering prompt dynamically: ${prompt.name}`);
999
1100
  const now = Date.now();
1000
1101
  const lastRegistration = this.promptRegistrationTimestamps.get(prompt.name);
1001
1102
  if (lastRegistration && now - lastRegistration < RAPID_DUPLICATE_WINDOW_MS) {
1002
- console.warn(`[Web Model Context] Prompt "${prompt.name}" registered multiple times within ${RAPID_DUPLICATE_WINDOW_MS}ms. This is likely due to React Strict Mode double-mounting. Ignoring duplicate registration.`);
1103
+ logger$1.warn(`Prompt "${prompt.name}" registered multiple times within ${RAPID_DUPLICATE_WINDOW_MS}ms. This is likely due to React Strict Mode double-mounting. Ignoring duplicate registration.`);
1003
1104
  const existingUnregister = this.promptUnregisterFunctions.get(prompt.name);
1004
1105
  if (existingUnregister) return { unregister: existingUnregister };
1005
1106
  }
@@ -1011,10 +1112,9 @@ var WebModelContext = class {
1011
1112
  this.updateBridgePrompts();
1012
1113
  this.scheduleListChanged("prompts");
1013
1114
  const unregisterFn = () => {
1014
- console.log(`[Web Model Context] Unregistering prompt: ${prompt.name}`);
1015
1115
  if (this.provideContextPrompts.has(prompt.name)) throw new Error(`[Web Model Context] Cannot unregister prompt "${prompt.name}": This prompt was registered via provideContext(). Use provideContext() to update the base prompt set.`);
1016
1116
  if (!this.dynamicPrompts.has(prompt.name)) {
1017
- console.warn(`[Web Model Context] Prompt "${prompt.name}" is not registered, ignoring unregister call`);
1117
+ logger$1.warn(`Prompt "${prompt.name}" is not registered, ignoring unregister call`);
1018
1118
  return;
1019
1119
  }
1020
1120
  this.dynamicPrompts.delete(prompt.name);
@@ -1033,11 +1133,10 @@ var WebModelContext = class {
1033
1133
  * @param {string} name - Name of the prompt to unregister
1034
1134
  */
1035
1135
  unregisterPrompt(name) {
1036
- console.log(`[Web Model Context] Unregistering prompt: ${name}`);
1037
1136
  const inProvideContext = this.provideContextPrompts.has(name);
1038
1137
  const inDynamic = this.dynamicPrompts.has(name);
1039
1138
  if (!inProvideContext && !inDynamic) {
1040
- console.warn(`[Web Model Context] Prompt "${name}" is not registered, ignoring unregister call`);
1139
+ logger$1.warn(`Prompt "${name}" is not registered, ignoring unregister call`);
1041
1140
  return;
1042
1141
  }
1043
1142
  if (inProvideContext) this.provideContextPrompts.delete(name);
@@ -1073,11 +1172,10 @@ var WebModelContext = class {
1073
1172
  * @param {string} name - Name of the tool to unregister
1074
1173
  */
1075
1174
  unregisterTool(name) {
1076
- console.log(`[Web Model Context] Unregistering tool: ${name}`);
1077
1175
  const inProvideContext = this.provideContextTools.has(name);
1078
1176
  const inDynamic = this.dynamicTools.has(name);
1079
1177
  if (!inProvideContext && !inDynamic) {
1080
- console.warn(`[Web Model Context] Tool "${name}" is not registered, ignoring unregister call`);
1178
+ logger$1.warn(`Tool "${name}" is not registered, ignoring unregister call`);
1081
1179
  return;
1082
1180
  }
1083
1181
  if (inProvideContext) this.provideContextTools.delete(name);
@@ -1094,7 +1192,6 @@ var WebModelContext = class {
1094
1192
  * Removes all tools, resources, and prompts registered via provideContext() and register* methods.
1095
1193
  */
1096
1194
  clearContext() {
1097
- console.log("[Web Model Context] Clearing all context (tools, resources, prompts)");
1098
1195
  this.provideContextTools.clear();
1099
1196
  this.dynamicTools.clear();
1100
1197
  this.toolRegistrationTimestamps.clear();
@@ -1124,7 +1221,6 @@ var WebModelContext = class {
1124
1221
  this.bridge.tools.clear();
1125
1222
  for (const [name, tool] of this.provideContextTools) this.bridge.tools.set(name, tool);
1126
1223
  for (const [name, tool] of this.dynamicTools) this.bridge.tools.set(name, tool);
1127
- console.log(`[Web Model Context] Updated bridge with ${this.provideContextTools.size} base tools + ${this.dynamicTools.size} dynamic tools = ${this.bridge.tools.size} total`);
1128
1224
  }
1129
1225
  /**
1130
1226
  * Notifies all servers and testing callbacks that the tools list has changed.
@@ -1152,7 +1248,6 @@ var WebModelContext = class {
1152
1248
  this.bridge.resources.clear();
1153
1249
  for (const [uri, resource] of this.provideContextResources) this.bridge.resources.set(uri, resource);
1154
1250
  for (const [uri, resource] of this.dynamicResources) this.bridge.resources.set(uri, resource);
1155
- console.log(`[Web Model Context] Updated bridge with ${this.provideContextResources.size} base resources + ${this.dynamicResources.size} dynamic resources = ${this.bridge.resources.size} total`);
1156
1251
  }
1157
1252
  /**
1158
1253
  * Notifies all servers that the resources list has changed.
@@ -1178,7 +1273,6 @@ var WebModelContext = class {
1178
1273
  this.bridge.prompts.clear();
1179
1274
  for (const [name, prompt] of this.provideContextPrompts) this.bridge.prompts.set(name, prompt);
1180
1275
  for (const [name, prompt] of this.dynamicPrompts) this.bridge.prompts.set(name, prompt);
1181
- console.log(`[Web Model Context] Updated bridge with ${this.provideContextPrompts.size} base prompts + ${this.dynamicPrompts.size} dynamic prompts = ${this.bridge.prompts.size} total`);
1182
1276
  }
1183
1277
  /**
1184
1278
  * Notifies all servers that the prompts list has changed.
@@ -1221,7 +1315,7 @@ var WebModelContext = class {
1221
1315
  break;
1222
1316
  default: {
1223
1317
  const _exhaustive = listType;
1224
- console.error(`[Web Model Context] Unknown list type: ${_exhaustive}`);
1318
+ logger$1.error(`Unknown list type: ${_exhaustive}`);
1225
1319
  }
1226
1320
  }
1227
1321
  });
@@ -1236,23 +1330,34 @@ var WebModelContext = class {
1236
1330
  * @internal
1237
1331
  */
1238
1332
  async readResource(uri) {
1239
- console.log(`[Web Model Context] Reading resource: ${uri}`);
1240
1333
  const staticResource = this.bridge.resources.get(uri);
1241
1334
  if (staticResource && !staticResource.isTemplate) try {
1242
- const parsedUri = new URL(uri);
1335
+ let parsedUri;
1336
+ try {
1337
+ parsedUri = new URL(uri);
1338
+ } catch {
1339
+ parsedUri = new URL(`custom-scheme:///${encodeURIComponent(uri)}`);
1340
+ parsedUri.originalUri = uri;
1341
+ }
1243
1342
  return await staticResource.read(parsedUri);
1244
1343
  } catch (error) {
1245
- console.error(`[Web Model Context] Error reading resource ${uri}:`, error);
1344
+ logger$1.error(`Error reading resource ${uri}:`, error);
1246
1345
  throw error;
1247
1346
  }
1248
1347
  for (const resource of this.bridge.resources.values()) {
1249
1348
  if (!resource.isTemplate) continue;
1250
1349
  const params = this.matchUriTemplate(resource.uri, uri);
1251
1350
  if (params) try {
1252
- const parsedUri = new URL(uri);
1351
+ let parsedUri;
1352
+ try {
1353
+ parsedUri = new URL(uri);
1354
+ } catch {
1355
+ parsedUri = new URL(`custom-scheme:///${encodeURIComponent(uri)}`);
1356
+ parsedUri.originalUri = uri;
1357
+ }
1253
1358
  return await resource.read(parsedUri, params);
1254
1359
  } catch (error) {
1255
- console.error(`[Web Model Context] Error reading resource ${uri}:`, error);
1360
+ logger$1.error(`Error reading resource ${uri}:`, error);
1256
1361
  throw error;
1257
1362
  }
1258
1363
  }
@@ -1282,8 +1387,7 @@ var WebModelContext = class {
1282
1387
  const params = {};
1283
1388
  for (let i = 0; i < paramNames.length; i++) {
1284
1389
  const paramName = paramNames[i];
1285
- const paramValue = match[i + 1];
1286
- if (paramName !== void 0 && paramValue !== void 0) params[paramName] = paramValue;
1390
+ params[paramName] = match[i + 1];
1287
1391
  }
1288
1392
  return params;
1289
1393
  }
@@ -1297,27 +1401,26 @@ var WebModelContext = class {
1297
1401
  * @internal
1298
1402
  */
1299
1403
  async getPrompt(name, args) {
1300
- console.log(`[Web Model Context] Getting prompt: ${name}`);
1301
1404
  const prompt = this.bridge.prompts.get(name);
1302
1405
  if (!prompt) throw new Error(`Prompt not found: ${name}`);
1303
1406
  if (prompt.argsValidator && args) {
1304
1407
  const validation = validateWithZod(args, prompt.argsValidator);
1305
1408
  if (!validation.success) {
1306
- console.error(`[Web Model Context] Argument validation failed for prompt ${name}:`, validation.error);
1409
+ logger$1.error(`Argument validation failed for prompt ${name}:`, validation.error);
1307
1410
  throw new Error(`Argument validation error for prompt "${name}":\n${validation.error}`);
1308
1411
  }
1309
1412
  }
1310
1413
  try {
1311
1414
  return await prompt.get(args ?? {});
1312
1415
  } catch (error) {
1313
- console.error(`[Web Model Context] Error getting prompt ${name}:`, error);
1416
+ logger$1.error(`Error getting prompt ${name}:`, error);
1314
1417
  throw error;
1315
1418
  }
1316
1419
  }
1317
1420
  /**
1318
1421
  * Executes a tool with validation and event dispatch.
1319
1422
  * Follows this sequence:
1320
- * 1. Validates input arguments against schema
1423
+ * 1. Validates input arguments against schema (unless skipValidation is true)
1321
1424
  * 2. Records tool call in testing API (if available)
1322
1425
  * 3. Checks for mock response (if testing)
1323
1426
  * 4. Dispatches 'toolcall' event to listeners
@@ -1326,53 +1429,52 @@ var WebModelContext = class {
1326
1429
  *
1327
1430
  * @param {string} toolName - Name of the tool to execute
1328
1431
  * @param {Record<string, unknown>} args - Arguments to pass to the tool
1432
+ * @param {Object} [options] - Execution options
1433
+ * @param {boolean} [options.skipValidation] - Skip input validation (used when MCP SDK already validated)
1329
1434
  * @returns {Promise<ToolResponse>} The tool's response
1330
1435
  * @throws {Error} If tool is not found
1331
1436
  * @internal
1332
1437
  */
1333
- async executeTool(toolName, args) {
1438
+ async executeTool(toolName, args, options) {
1334
1439
  const tool = this.bridge.tools.get(toolName);
1335
1440
  if (!tool) throw new Error(`Tool not found: ${toolName}`);
1336
- console.log(`[Web Model Context] Validating input for tool: ${toolName}`);
1337
- const validation = validateWithZod(args, tool.inputValidator);
1338
- if (!validation.success) {
1339
- console.error(`[Web Model Context] Input validation failed for ${toolName}:`, validation.error);
1340
- return {
1341
- content: [{
1342
- type: "text",
1343
- text: `Input validation error for tool "${toolName}":\n${validation.error}`
1344
- }],
1345
- isError: true
1346
- };
1441
+ let validatedArgs;
1442
+ if (options?.skipValidation) validatedArgs = args;
1443
+ else {
1444
+ const validation = validateWithZod(args, tool.inputValidator);
1445
+ if (!validation.success) {
1446
+ logger$1.error(`Input validation failed for ${toolName}:`, validation.error);
1447
+ return {
1448
+ content: [{
1449
+ type: "text",
1450
+ text: `Input validation error for tool "${toolName}":\n${validation.error}`
1451
+ }],
1452
+ isError: true
1453
+ };
1454
+ }
1455
+ validatedArgs = validation.data;
1347
1456
  }
1348
- const validatedArgs = validation.data;
1349
1457
  if (this.testingAPI) this.testingAPI.recordToolCall(toolName, validatedArgs);
1350
1458
  if (this.testingAPI?.hasMockResponse(toolName)) {
1351
1459
  const mockResponse = this.testingAPI.getMockResponse(toolName);
1352
- if (mockResponse) {
1353
- console.log(`[Web Model Context] Returning mock response for tool: ${toolName}`);
1354
- return mockResponse;
1355
- }
1460
+ if (mockResponse) return mockResponse;
1356
1461
  }
1357
1462
  const event = new WebToolCallEvent(toolName, validatedArgs);
1358
1463
  this.dispatchEvent(event);
1359
1464
  if (event.defaultPrevented && event.hasResponse()) {
1360
1465
  const response = event.getResponse();
1361
- if (response) {
1362
- console.log(`[Web Model Context] Tool ${toolName} handled by event listener`);
1363
- return response;
1364
- }
1466
+ if (response) return response;
1365
1467
  }
1366
- console.log(`[Web Model Context] Executing tool: ${toolName}`);
1367
1468
  try {
1368
1469
  const response = await tool.execute(validatedArgs);
1369
1470
  if (tool.outputValidator && response.structuredContent) {
1370
1471
  const outputValidation = validateWithZod(response.structuredContent, tool.outputValidator);
1371
- if (!outputValidation.success) console.warn(`[Web Model Context] Output validation failed for ${toolName}:`, outputValidation.error);
1472
+ if (!outputValidation.success) logger$1.warn(`Output validation failed for ${toolName}:`, outputValidation.error);
1372
1473
  }
1474
+ if (response.metadata && typeof response.metadata === "object" && "willNavigate" in response.metadata) logger$1.info(`Tool "${toolName}" will trigger navigation`, response.metadata);
1373
1475
  return response;
1374
1476
  } catch (error) {
1375
- console.error(`[Web Model Context] Error executing tool ${toolName}:`, error);
1477
+ logger$1.error(`Error executing tool ${toolName}:`, error);
1376
1478
  return {
1377
1479
  content: [{
1378
1480
  type: "text",
@@ -1406,7 +1508,6 @@ var WebModelContext = class {
1406
1508
  * @returns {Promise<SamplingResult>} The LLM completion result
1407
1509
  */
1408
1510
  async createMessage(params) {
1409
- console.log("[Web Model Context] Requesting sampling from client");
1410
1511
  const underlyingServer = this.bridge.tabServer.server;
1411
1512
  if (!underlyingServer?.createMessage) throw new Error("Sampling is not supported: no connected client with sampling capability");
1412
1513
  return underlyingServer.createMessage(params);
@@ -1419,7 +1520,6 @@ var WebModelContext = class {
1419
1520
  * @returns {Promise<ElicitationResult>} The user's response
1420
1521
  */
1421
1522
  async elicitInput(params) {
1422
- console.log("[Web Model Context] Requesting elicitation from client");
1423
1523
  const underlyingServer = this.bridge.tabServer.server;
1424
1524
  if (!underlyingServer?.elicitInput) throw new Error("Elicitation is not supported: no connected client with elicitation capability");
1425
1525
  return underlyingServer.elicitInput(params);
@@ -1434,16 +1534,13 @@ var WebModelContext = class {
1434
1534
  * @returns {MCPBridge} The initialized MCP bridge
1435
1535
  */
1436
1536
  function initializeMCPBridge(options) {
1437
- console.log("[Web Model Context] Initializing MCP bridge");
1438
1537
  const hostname = window.location.hostname || "localhost";
1439
1538
  const transportOptions = options?.transport;
1440
1539
  const setupServerHandlers = (server, bridge$1) => {
1441
1540
  server.setRequestHandler(ListToolsRequestSchema, async () => {
1442
- console.log("[MCP Bridge] Handling list_tools request");
1443
1541
  return { tools: bridge$1.modelContext.listTools() };
1444
1542
  });
1445
1543
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
1446
- console.log(`[MCP Bridge] Handling call_tool request: ${request.params.name}`);
1447
1544
  const toolName = request.params.name;
1448
1545
  const args = request.params.arguments || {};
1449
1546
  try {
@@ -1454,40 +1551,35 @@ function initializeMCPBridge(options) {
1454
1551
  ...response.structuredContent && { structuredContent: response.structuredContent }
1455
1552
  };
1456
1553
  } catch (error) {
1457
- console.error(`[MCP Bridge] Error calling tool ${toolName}:`, error);
1554
+ bridgeLogger.error(`Error calling tool ${toolName}:`, error);
1458
1555
  throw error;
1459
1556
  }
1460
1557
  });
1461
1558
  server.setRequestHandler(ListResourcesRequestSchema, async () => {
1462
- console.log("[MCP Bridge] Handling list_resources request");
1463
1559
  return { resources: bridge$1.modelContext.listResources() };
1464
1560
  });
1465
1561
  server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
1466
- console.log(`[MCP Bridge] Handling read_resource request: ${request.params.uri}`);
1467
1562
  try {
1468
1563
  return await bridge$1.modelContext.readResource(request.params.uri);
1469
1564
  } catch (error) {
1470
- console.error(`[MCP Bridge] Error reading resource ${request.params.uri}:`, error);
1565
+ bridgeLogger.error(`Error reading resource ${request.params.uri}:`, error);
1471
1566
  throw error;
1472
1567
  }
1473
1568
  });
1474
1569
  server.setRequestHandler(ListPromptsRequestSchema, async () => {
1475
- console.log("[MCP Bridge] Handling list_prompts request");
1476
1570
  return { prompts: bridge$1.modelContext.listPrompts() };
1477
1571
  });
1478
1572
  server.setRequestHandler(GetPromptRequestSchema, async (request) => {
1479
- console.log(`[MCP Bridge] Handling get_prompt request: ${request.params.name}`);
1480
1573
  try {
1481
1574
  return await bridge$1.modelContext.getPrompt(request.params.name, request.params.arguments);
1482
1575
  } catch (error) {
1483
- console.error(`[MCP Bridge] Error getting prompt ${request.params.name}:`, error);
1576
+ bridgeLogger.error(`Error getting prompt ${request.params.name}:`, error);
1484
1577
  throw error;
1485
1578
  }
1486
1579
  });
1487
1580
  };
1488
1581
  const customTransport = transportOptions?.create?.();
1489
1582
  if (customTransport) {
1490
- console.log("[Web Model Context] Using custom transport");
1491
1583
  const server = new Server({
1492
1584
  name: hostname,
1493
1585
  version: "1.0.0"
@@ -1507,10 +1599,8 @@ function initializeMCPBridge(options) {
1507
1599
  bridge$1.modelContext = new WebModelContext(bridge$1);
1508
1600
  setupServerHandlers(server, bridge$1);
1509
1601
  server.connect(customTransport);
1510
- console.log("[Web Model Context] MCP server connected with custom transport");
1511
1602
  return bridge$1;
1512
1603
  }
1513
- console.log("[Web Model Context] Using dual-server mode");
1514
1604
  const tabServerEnabled = transportOptions?.tabServer !== false;
1515
1605
  const tabServer = new Server({
1516
1606
  name: `${hostname}-tab`,
@@ -1537,12 +1627,10 @@ function initializeMCPBridge(options) {
1537
1627
  ...restTabServerOptions
1538
1628
  });
1539
1629
  tabServer.connect(tabTransport);
1540
- console.log("[Web Model Context] Tab server connected");
1541
1630
  }
1542
1631
  const isInIframe = typeof window !== "undefined" && window.parent !== window;
1543
1632
  const iframeServerConfig = transportOptions?.iframeServer;
1544
1633
  if (iframeServerConfig !== false && (iframeServerConfig !== void 0 || isInIframe)) {
1545
- console.log("[Web Model Context] Enabling iframe server");
1546
1634
  const iframeServer = new Server({
1547
1635
  name: `${hostname}-iframe`,
1548
1636
  version: "1.0.0"
@@ -1559,7 +1647,6 @@ function initializeMCPBridge(options) {
1559
1647
  });
1560
1648
  iframeServer.connect(iframeTransport);
1561
1649
  bridge.iframeServer = iframeServer;
1562
- console.log("[Web Model Context] Iframe server connected");
1563
1650
  }
1564
1651
  return bridge;
1565
1652
  }
@@ -1584,8 +1671,9 @@ function initializeMCPBridge(options) {
1584
1671
  * ```
1585
1672
  */
1586
1673
  function initializeWebModelContext(options) {
1674
+ /* c8 ignore next 4 */
1587
1675
  if (typeof window === "undefined") {
1588
- console.warn("[Web Model Context] Not in browser environment, skipping initialization");
1676
+ logger$1.warn("Not in browser environment, skipping initialization");
1589
1677
  return;
1590
1678
  }
1591
1679
  const effectiveOptions = options ?? window.__webModelContextOptions;
@@ -1594,12 +1682,12 @@ function initializeWebModelContext(options) {
1594
1682
  const nativeContext = window.navigator.modelContext;
1595
1683
  const nativeTesting = window.navigator.modelContextTesting;
1596
1684
  if (!nativeContext || !nativeTesting) {
1597
- console.error("[Web Model Context] Native API detection mismatch");
1685
+ logger$1.error("Native API detection mismatch");
1598
1686
  return;
1599
1687
  }
1600
- console.log("✅ [Web Model Context] Native Chromium API detected");
1601
- console.log(" Using native implementation with MCP bridge synchronization");
1602
- console.log(" Native API will automatically collect tools from embedded iframes");
1688
+ logger$1.info("✅ Native Chromium API detected");
1689
+ logger$1.info(" Using native implementation with MCP bridge synchronization");
1690
+ logger$1.info(" Native API will automatically collect tools from embedded iframes");
1603
1691
  try {
1604
1692
  const bridge = initializeMCPBridge(effectiveOptions);
1605
1693
  bridge.modelContext = new NativeModelContextAdapter(bridge, nativeContext, nativeTesting);
@@ -1609,29 +1697,29 @@ function initializeWebModelContext(options) {
1609
1697
  writable: false,
1610
1698
  configurable: true
1611
1699
  });
1612
- console.log("✅ [Web Model Context] MCP bridge synced with native API");
1613
- console.log(" MCP clients will receive automatic tool updates from native registry");
1700
+ logger$1.info("✅ MCP bridge synced with native API");
1701
+ logger$1.info(" MCP clients will receive automatic tool updates from native registry");
1614
1702
  } catch (error) {
1615
- console.error("[Web Model Context] Failed to initialize native adapter:", error);
1703
+ logger$1.error("Failed to initialize native adapter:", error);
1616
1704
  throw error;
1617
1705
  }
1618
1706
  return;
1619
1707
  }
1620
1708
  if (native.hasNativeContext && !native.hasNativeTesting) {
1621
- console.warn("[Web Model Context] Partial native API detected");
1622
- console.warn(" navigator.modelContext exists but navigator.modelContextTesting is missing");
1623
- console.warn(" Cannot sync with native API. Please enable experimental features:");
1624
- console.warn(" - Navigate to chrome://flags");
1625
- console.warn(" - Enable \"Experimental Web Platform Features\"");
1626
- console.warn(" - Or launch with: --enable-experimental-web-platform-features");
1627
- console.warn(" Skipping initialization to avoid conflicts");
1709
+ logger$1.warn("Partial native API detected");
1710
+ logger$1.warn(" navigator.modelContext exists but navigator.modelContextTesting is missing");
1711
+ logger$1.warn(" Cannot sync with native API. Please enable experimental features:");
1712
+ logger$1.warn(" - Navigate to chrome://flags");
1713
+ logger$1.warn(" - Enable \"Experimental Web Platform Features\"");
1714
+ logger$1.warn(" - Or launch with: --enable-experimental-web-platform-features");
1715
+ logger$1.warn(" Skipping initialization to avoid conflicts");
1628
1716
  return;
1629
1717
  }
1630
1718
  if (window.navigator.modelContext) {
1631
- console.warn("[Web Model Context] window.navigator.modelContext already exists, skipping initialization");
1719
+ logger$1.warn("window.navigator.modelContext already exists, skipping initialization");
1632
1720
  return;
1633
1721
  }
1634
- console.log("[Web Model Context] Native API not detected, installing polyfill");
1722
+ logger$1.info("Native API not detected, installing polyfill");
1635
1723
  try {
1636
1724
  const bridge = initializeMCPBridge(effectiveOptions);
1637
1725
  Object.defineProperty(window.navigator, "modelContext", {
@@ -1644,12 +1732,12 @@ function initializeWebModelContext(options) {
1644
1732
  writable: false,
1645
1733
  configurable: true
1646
1734
  });
1647
- console.log("✅ [Web Model Context] window.navigator.modelContext initialized successfully");
1648
- console.log("[Model Context Testing] Installing polyfill");
1649
- console.log(" 💡 To use the native implementation in Chromium:");
1650
- console.log(" - Navigate to chrome://flags");
1651
- console.log(" - Enable \"Experimental Web Platform Features\"");
1652
- console.log(" - Or launch with: --enable-experimental-web-platform-features");
1735
+ logger$1.info("✅ window.navigator.modelContext initialized successfully");
1736
+ testingLogger.info("Installing polyfill");
1737
+ testingLogger.info(" 💡 To use the native implementation in Chromium:");
1738
+ testingLogger.info(" - Navigate to chrome://flags");
1739
+ testingLogger.info(" - Enable \"Experimental Web Platform Features\"");
1740
+ testingLogger.info(" - Or launch with: --enable-experimental-web-platform-features");
1653
1741
  const testingAPI = new WebModelContextTesting(bridge);
1654
1742
  bridge.modelContextTesting = testingAPI;
1655
1743
  bridge.modelContext.setTestingAPI(testingAPI);
@@ -1658,9 +1746,9 @@ function initializeWebModelContext(options) {
1658
1746
  writable: false,
1659
1747
  configurable: true
1660
1748
  });
1661
- console.log("✅ [Model Context Testing] Polyfill installed at window.navigator.modelContextTesting");
1749
+ testingLogger.info("✅ Polyfill installed at window.navigator.modelContextTesting");
1662
1750
  } catch (error) {
1663
- console.error("[Web Model Context] Failed to initialize:", error);
1751
+ logger$1.error("Failed to initialize:", error);
1664
1752
  throw error;
1665
1753
  }
1666
1754
  }
@@ -1677,21 +1765,23 @@ function initializeWebModelContext(options) {
1677
1765
  * ```
1678
1766
  */
1679
1767
  function cleanupWebModelContext() {
1768
+ /* c8 ignore next */
1680
1769
  if (typeof window === "undefined") return;
1681
1770
  if (window.__mcpBridge) try {
1682
1771
  window.__mcpBridge.tabServer.close();
1683
1772
  if (window.__mcpBridge.iframeServer) window.__mcpBridge.iframeServer.close();
1684
1773
  } catch (error) {
1685
- console.warn("[Web Model Context] Error closing MCP servers:", error);
1774
+ logger$1.warn("Error closing MCP servers:", error);
1686
1775
  }
1687
1776
  delete window.navigator.modelContext;
1688
1777
  delete window.navigator.modelContextTesting;
1689
1778
  delete window.__mcpBridge;
1690
- console.log("[Web Model Context] Cleaned up");
1779
+ logger$1.info("Cleaned up");
1691
1780
  }
1692
1781
 
1693
1782
  //#endregion
1694
1783
  //#region src/index.ts
1784
+ const logger = createLogger("WebModelContext");
1695
1785
  function mergeTransportOptions(base, override) {
1696
1786
  if (!base) return override;
1697
1787
  if (!override) return base;
@@ -1719,7 +1809,7 @@ function parseScriptTagOptions(script) {
1719
1809
  if (dataset.webmcpOptions) try {
1720
1810
  return JSON.parse(dataset.webmcpOptions);
1721
1811
  } catch (error) {
1722
- console.error("[Web Model Context] Invalid JSON in data-webmcp-options:", error);
1812
+ logger.error("Invalid JSON in data-webmcp-options:", error);
1723
1813
  return;
1724
1814
  }
1725
1815
  const options = {};
@@ -1762,10 +1852,10 @@ if (typeof window !== "undefined" && typeof document !== "undefined") {
1762
1852
  try {
1763
1853
  if (shouldAutoInitialize) initializeWebModelContext(mergedOptions);
1764
1854
  } catch (error) {
1765
- console.error("[Web Model Context] Auto-initialization failed:", error);
1855
+ logger.error("Auto-initialization failed:", error);
1766
1856
  }
1767
1857
  }
1768
1858
 
1769
1859
  //#endregion
1770
- export { cleanupWebModelContext, initializeWebModelContext, zodToJsonSchema };
1860
+ export { cleanupWebModelContext, createLogger, initializeWebModelContext, zodToJsonSchema };
1771
1861
  //# sourceMappingURL=index.js.map