@mcp-b/global 0.0.0-beta-20260109203913 → 0.0.0-beta-20260116035619
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/dist/index.d.ts +46 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.iife.js +31 -18
- package/dist/index.js +169 -135
- package/dist/index.js.map +1 -1
- package/package.json +2 -4
package/dist/index.js
CHANGED
|
@@ -1,45 +1,126 @@
|
|
|
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 {
|
|
4
|
-
import { z } from "zod";
|
|
5
|
-
import { zodToJsonSchema as zodToJsonSchema$1 } from "zod-to-json-schema";
|
|
3
|
+
import { z } from "zod/v4";
|
|
6
4
|
|
|
5
|
+
//#region src/logger.ts
|
|
6
|
+
/**
|
|
7
|
+
* @license
|
|
8
|
+
* Copyright 2025 Google LLC
|
|
9
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Lightweight logging system for @mcp-b/global
|
|
13
|
+
*
|
|
14
|
+
* Design Decision: This implements a custom logger instead of using the 'debug'
|
|
15
|
+
* package to reduce bundle size and eliminate external dependencies in the
|
|
16
|
+
* browser build. The API is intentionally simpler, focusing on the specific
|
|
17
|
+
* needs of browser-based MCP implementations.
|
|
18
|
+
*
|
|
19
|
+
* Configuration via localStorage:
|
|
20
|
+
* - localStorage.setItem('WEBMCP_DEBUG', '*') - enable all debug logging
|
|
21
|
+
* - localStorage.setItem('WEBMCP_DEBUG', 'WebModelContext') - enable specific namespace
|
|
22
|
+
* - localStorage.setItem('WEBMCP_DEBUG', 'WebModelContext,NativeAdapter') - multiple namespaces
|
|
23
|
+
* - localStorage.setItem('WEBMCP_DEBUG', 'WebModelContext:') - enable namespace and sub-namespaces
|
|
24
|
+
* - localStorage.removeItem('WEBMCP_DEBUG') - disable debug logging (default)
|
|
25
|
+
*
|
|
26
|
+
* Environment Support:
|
|
27
|
+
* - Automatically detects localStorage availability
|
|
28
|
+
* - Gracefully degrades to "disabled" state when localStorage is inaccessible
|
|
29
|
+
* - Never throws errors from configuration checks (safe for private browsing mode)
|
|
30
|
+
*/
|
|
31
|
+
/** localStorage key for debug configuration */
|
|
32
|
+
const DEBUG_CONFIG_KEY = "WEBMCP_DEBUG";
|
|
33
|
+
/**
|
|
34
|
+
* Check if debug logging is enabled for a namespace
|
|
35
|
+
*
|
|
36
|
+
* Supports namespace hierarchy via colons. Setting 'WebModelContext' will match
|
|
37
|
+
* both 'WebModelContext' and 'WebModelContext:init', but NOT 'WebModelContextTesting'.
|
|
38
|
+
*/
|
|
39
|
+
function isDebugEnabled(namespace) {
|
|
40
|
+
if (typeof window === "undefined" || !window.localStorage) return false;
|
|
41
|
+
try {
|
|
42
|
+
const debugConfig = localStorage.getItem(DEBUG_CONFIG_KEY);
|
|
43
|
+
if (!debugConfig) return false;
|
|
44
|
+
if (debugConfig === "*") return true;
|
|
45
|
+
return debugConfig.split(",").map((p) => p.trim()).some((pattern) => namespace === pattern || namespace.startsWith(`${pattern}:`));
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* No-op function for disabled log levels
|
|
52
|
+
*/
|
|
53
|
+
const noop = () => {};
|
|
54
|
+
/**
|
|
55
|
+
* Create a namespaced logger
|
|
56
|
+
*
|
|
57
|
+
* Uses .bind() to prepend namespace prefixes to console methods without manual
|
|
58
|
+
* string concatenation. Debug enablement is determined at logger creation time
|
|
59
|
+
* for performance - changes to localStorage after creation won't affect existing
|
|
60
|
+
* loggers. Refresh the page to apply new WEBMCP_DEBUG settings.
|
|
61
|
+
*
|
|
62
|
+
* @param namespace - Namespace for the logger (e.g., 'WebModelContext', 'NativeAdapter')
|
|
63
|
+
* @returns Logger instance with debug, info, warn, error methods
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* const logger = createLogger('WebModelContext');
|
|
68
|
+
* logger.debug('Tool registered:', toolName); // Only shown if WEBMCP_DEBUG includes 'WebModelContext'
|
|
69
|
+
* logger.error('Execution failed:', error); // Always enabled
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
function createLogger(namespace) {
|
|
73
|
+
const prefix = `[${namespace}]`;
|
|
74
|
+
const isDebug = isDebugEnabled(namespace);
|
|
75
|
+
const boundWarn = console.warn.bind(console, prefix);
|
|
76
|
+
const boundError = console.error.bind(console, prefix);
|
|
77
|
+
const boundLog = console.log.bind(console, prefix);
|
|
78
|
+
return {
|
|
79
|
+
warn: boundWarn,
|
|
80
|
+
error: boundError,
|
|
81
|
+
debug: isDebug ? boundLog : noop,
|
|
82
|
+
info: isDebug ? boundLog : noop
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
//#endregion
|
|
7
87
|
//#region src/validation.ts
|
|
88
|
+
const logger$2 = createLogger("WebModelContext");
|
|
8
89
|
/**
|
|
9
90
|
* Detect if a schema is a Zod schema object (Record<string, ZodType>)
|
|
10
|
-
* or a JSON Schema object
|
|
91
|
+
* or a JSON Schema object.
|
|
92
|
+
*
|
|
93
|
+
* Uses duck-typing to detect Zod 4 schemas by checking for the `_zod` property.
|
|
11
94
|
*/
|
|
12
95
|
function isZodSchema(schema) {
|
|
13
96
|
if (typeof schema !== "object" || schema === null) return false;
|
|
14
97
|
if ("type" in schema && typeof schema.type === "string") return false;
|
|
15
98
|
const values = Object.values(schema);
|
|
16
99
|
if (values.length === 0) return false;
|
|
17
|
-
return values.some((val) => val
|
|
100
|
+
return values.some((val) => val != null && typeof val === "object" && "_zod" in val);
|
|
18
101
|
}
|
|
19
102
|
/**
|
|
20
103
|
* Convert JSON Schema to Zod validator
|
|
21
|
-
* Uses
|
|
104
|
+
* Uses Zod 4's native z.fromJSONSchema() for conversion
|
|
22
105
|
*/
|
|
23
|
-
function jsonSchemaToZod
|
|
106
|
+
function jsonSchemaToZod(jsonSchema) {
|
|
24
107
|
try {
|
|
25
|
-
return
|
|
108
|
+
return z.fromJSONSchema(jsonSchema);
|
|
26
109
|
} catch (error) {
|
|
27
|
-
|
|
110
|
+
logger$2.warn("Failed to convert JSON Schema to Zod:", error);
|
|
28
111
|
return z.object({}).passthrough();
|
|
29
112
|
}
|
|
30
113
|
}
|
|
31
114
|
/**
|
|
32
115
|
* Convert Zod schema object to JSON Schema
|
|
33
|
-
* Uses
|
|
116
|
+
* Uses Zod 4's native z.toJSONSchema() for conversion
|
|
34
117
|
*
|
|
35
118
|
* @param schema - Record of Zod type definitions (e.g., { name: z.string(), age: z.number() })
|
|
36
119
|
* @returns JSON Schema object compatible with MCP InputSchema
|
|
37
120
|
*/
|
|
38
121
|
function zodToJsonSchema(schema) {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
target: "jsonSchema7"
|
|
42
|
-
});
|
|
122
|
+
const zodObject = z.object(schema);
|
|
123
|
+
const { $schema: _,...rest } = z.toJSONSchema(zodObject);
|
|
43
124
|
return rest;
|
|
44
125
|
}
|
|
45
126
|
/**
|
|
@@ -54,7 +135,7 @@ function normalizeSchema(schema) {
|
|
|
54
135
|
const jsonSchema = schema;
|
|
55
136
|
return {
|
|
56
137
|
jsonSchema,
|
|
57
|
-
zodValidator: jsonSchemaToZod
|
|
138
|
+
zodValidator: jsonSchemaToZod(jsonSchema)
|
|
58
139
|
};
|
|
59
140
|
}
|
|
60
141
|
/**
|
|
@@ -64,7 +145,7 @@ function validateWithZod(data, validator) {
|
|
|
64
145
|
const result = validator.safeParse(data);
|
|
65
146
|
if (!result.success) return {
|
|
66
147
|
success: false,
|
|
67
|
-
error: `Validation failed:\n${result.error.
|
|
148
|
+
error: `Validation failed:\n${result.error.issues.map((err) => ` - ${err.path.join(".") || "root"}: ${err.message}`).join("\n")}`
|
|
68
149
|
};
|
|
69
150
|
return {
|
|
70
151
|
success: true,
|
|
@@ -74,6 +155,10 @@ function validateWithZod(data, validator) {
|
|
|
74
155
|
|
|
75
156
|
//#endregion
|
|
76
157
|
//#region src/global.ts
|
|
158
|
+
const logger$1 = createLogger("WebModelContext");
|
|
159
|
+
const nativeLogger = createLogger("NativeAdapter");
|
|
160
|
+
const bridgeLogger = createLogger("MCPBridge");
|
|
161
|
+
const testingLogger = createLogger("ModelContextTesting");
|
|
77
162
|
/**
|
|
78
163
|
* Marker property name used to identify polyfill implementations.
|
|
79
164
|
* This constant ensures single source of truth for the marker used in
|
|
@@ -142,7 +227,6 @@ var NativeModelContextAdapter = class {
|
|
|
142
227
|
this.nativeContext = nativeContext;
|
|
143
228
|
this.nativeTesting = nativeTesting;
|
|
144
229
|
this.nativeTesting.registerToolsChangedCallback(() => {
|
|
145
|
-
console.log("[Native Adapter] Tool change detected from native API");
|
|
146
230
|
this.syncToolsFromNative();
|
|
147
231
|
});
|
|
148
232
|
this.syncToolsFromNative();
|
|
@@ -159,7 +243,6 @@ var NativeModelContextAdapter = class {
|
|
|
159
243
|
this.syncInProgress = true;
|
|
160
244
|
try {
|
|
161
245
|
const nativeTools = this.nativeTesting.listTools();
|
|
162
|
-
console.log(`[Native Adapter] Syncing ${nativeTools.length} tools from native API`);
|
|
163
246
|
this.bridge.tools.clear();
|
|
164
247
|
for (const toolInfo of nativeTools) try {
|
|
165
248
|
const inputSchema = JSON.parse(toolInfo.inputSchema);
|
|
@@ -171,11 +254,11 @@ var NativeModelContextAdapter = class {
|
|
|
171
254
|
const result = await this.nativeTesting.executeTool(toolInfo.name, JSON.stringify(args));
|
|
172
255
|
return this.convertToToolResponse(result);
|
|
173
256
|
},
|
|
174
|
-
inputValidator: jsonSchemaToZod
|
|
257
|
+
inputValidator: jsonSchemaToZod(inputSchema)
|
|
175
258
|
};
|
|
176
259
|
this.bridge.tools.set(toolInfo.name, validatedTool);
|
|
177
260
|
} catch (error) {
|
|
178
|
-
|
|
261
|
+
nativeLogger.error(`Failed to sync tool "${toolInfo.name}":`, error);
|
|
179
262
|
}
|
|
180
263
|
this.notifyMCPServers();
|
|
181
264
|
} finally {
|
|
@@ -235,7 +318,6 @@ var NativeModelContextAdapter = class {
|
|
|
235
318
|
* @param {ModelContextInput} context - Context containing tools to register
|
|
236
319
|
*/
|
|
237
320
|
provideContext(context) {
|
|
238
|
-
console.log("[Native Adapter] Delegating provideContext to native API");
|
|
239
321
|
this.nativeContext.provideContext(context);
|
|
240
322
|
}
|
|
241
323
|
/**
|
|
@@ -247,7 +329,6 @@ var NativeModelContextAdapter = class {
|
|
|
247
329
|
* @returns {{unregister: () => void}} Object with unregister function
|
|
248
330
|
*/
|
|
249
331
|
registerTool(tool) {
|
|
250
|
-
console.log(`[Native Adapter] Delegating registerTool("${tool.name}") to native API`);
|
|
251
332
|
return this.nativeContext.registerTool(tool);
|
|
252
333
|
}
|
|
253
334
|
/**
|
|
@@ -257,7 +338,6 @@ var NativeModelContextAdapter = class {
|
|
|
257
338
|
* @param {string} name - Name of the tool to unregister
|
|
258
339
|
*/
|
|
259
340
|
unregisterTool(name) {
|
|
260
|
-
console.log(`[Native Adapter] Delegating unregisterTool("${name}") to native API`);
|
|
261
341
|
this.nativeContext.unregisterTool(name);
|
|
262
342
|
}
|
|
263
343
|
/**
|
|
@@ -265,7 +345,6 @@ var NativeModelContextAdapter = class {
|
|
|
265
345
|
* Delegates to navigator.modelContext.clearContext().
|
|
266
346
|
*/
|
|
267
347
|
clearContext() {
|
|
268
|
-
console.log("[Native Adapter] Delegating clearContext to native API");
|
|
269
348
|
this.nativeContext.clearContext();
|
|
270
349
|
}
|
|
271
350
|
/**
|
|
@@ -278,12 +357,11 @@ var NativeModelContextAdapter = class {
|
|
|
278
357
|
* @internal
|
|
279
358
|
*/
|
|
280
359
|
async executeTool(toolName, args) {
|
|
281
|
-
console.log(`[Native Adapter] Executing tool "${toolName}" via native API`);
|
|
282
360
|
try {
|
|
283
361
|
const result = await this.nativeTesting.executeTool(toolName, JSON.stringify(args));
|
|
284
362
|
return this.convertToToolResponse(result);
|
|
285
363
|
} catch (error) {
|
|
286
|
-
|
|
364
|
+
nativeLogger.error(`Error executing tool "${toolName}":`, error);
|
|
287
365
|
return {
|
|
288
366
|
content: [{
|
|
289
367
|
type: "text",
|
|
@@ -314,7 +392,7 @@ var NativeModelContextAdapter = class {
|
|
|
314
392
|
* This is a polyfill-only feature.
|
|
315
393
|
*/
|
|
316
394
|
registerResource(_resource) {
|
|
317
|
-
|
|
395
|
+
nativeLogger.warn("registerResource is not supported by native API");
|
|
318
396
|
return { unregister: () => {} };
|
|
319
397
|
}
|
|
320
398
|
/**
|
|
@@ -322,7 +400,7 @@ var NativeModelContextAdapter = class {
|
|
|
322
400
|
* Note: Native Chromium API does not yet support resources.
|
|
323
401
|
*/
|
|
324
402
|
unregisterResource(_uri) {
|
|
325
|
-
|
|
403
|
+
nativeLogger.warn("unregisterResource is not supported by native API");
|
|
326
404
|
}
|
|
327
405
|
/**
|
|
328
406
|
* Lists all registered resources.
|
|
@@ -352,7 +430,7 @@ var NativeModelContextAdapter = class {
|
|
|
352
430
|
* This is a polyfill-only feature.
|
|
353
431
|
*/
|
|
354
432
|
registerPrompt(_prompt) {
|
|
355
|
-
|
|
433
|
+
nativeLogger.warn("registerPrompt is not supported by native API");
|
|
356
434
|
return { unregister: () => {} };
|
|
357
435
|
}
|
|
358
436
|
/**
|
|
@@ -360,7 +438,7 @@ var NativeModelContextAdapter = class {
|
|
|
360
438
|
* Note: Native Chromium API does not yet support prompts.
|
|
361
439
|
*/
|
|
362
440
|
unregisterPrompt(_name) {
|
|
363
|
-
|
|
441
|
+
nativeLogger.warn("unregisterPrompt is not supported by native API");
|
|
364
442
|
}
|
|
365
443
|
/**
|
|
366
444
|
* Lists all registered prompts.
|
|
@@ -415,7 +493,6 @@ var NativeModelContextAdapter = class {
|
|
|
415
493
|
* This is handled by the polyfill.
|
|
416
494
|
*/
|
|
417
495
|
async createMessage(params) {
|
|
418
|
-
console.log("[Native Adapter] Requesting sampling from client");
|
|
419
496
|
const underlyingServer = this.bridge.tabServer.server;
|
|
420
497
|
if (!underlyingServer?.createMessage) throw new Error("Sampling is not supported: no connected client with sampling capability");
|
|
421
498
|
return underlyingServer.createMessage(params);
|
|
@@ -426,7 +503,6 @@ var NativeModelContextAdapter = class {
|
|
|
426
503
|
* This is handled by the polyfill.
|
|
427
504
|
*/
|
|
428
505
|
async elicitInput(params) {
|
|
429
|
-
console.log("[Native Adapter] Requesting elicitation from client");
|
|
430
506
|
const underlyingServer = this.bridge.tabServer.server;
|
|
431
507
|
if (!underlyingServer?.elicitInput) throw new Error("Elicitation is not supported: no connected client with elicitation capability");
|
|
432
508
|
return underlyingServer.elicitInput(params);
|
|
@@ -565,7 +641,7 @@ var WebModelContextTesting = class {
|
|
|
565
641
|
for (const callback of this.toolsChangedCallbacks) try {
|
|
566
642
|
callback();
|
|
567
643
|
} catch (error) {
|
|
568
|
-
|
|
644
|
+
testingLogger.error("Error in tools changed callback:", error);
|
|
569
645
|
}
|
|
570
646
|
}
|
|
571
647
|
/**
|
|
@@ -579,7 +655,6 @@ var WebModelContextTesting = class {
|
|
|
579
655
|
* @throws {Error} If the tool does not exist
|
|
580
656
|
*/
|
|
581
657
|
async executeTool(toolName, inputArgsJson) {
|
|
582
|
-
console.log(`[Model Context Testing] Executing tool: ${toolName}`);
|
|
583
658
|
let args;
|
|
584
659
|
try {
|
|
585
660
|
args = JSON.parse(inputArgsJson);
|
|
@@ -616,7 +691,6 @@ var WebModelContextTesting = class {
|
|
|
616
691
|
*/
|
|
617
692
|
registerToolsChangedCallback(callback) {
|
|
618
693
|
this.toolsChangedCallbacks.add(callback);
|
|
619
|
-
console.log("[Model Context Testing] Tools changed callback registered");
|
|
620
694
|
}
|
|
621
695
|
/**
|
|
622
696
|
* Gets all tool calls that have been recorded (polyfill extension).
|
|
@@ -631,7 +705,6 @@ var WebModelContextTesting = class {
|
|
|
631
705
|
*/
|
|
632
706
|
clearToolCalls() {
|
|
633
707
|
this.toolCallHistory = [];
|
|
634
|
-
console.log("[Model Context Testing] Tool call history cleared");
|
|
635
708
|
}
|
|
636
709
|
/**
|
|
637
710
|
* Sets a mock response for a specific tool (polyfill extension).
|
|
@@ -642,7 +715,6 @@ var WebModelContextTesting = class {
|
|
|
642
715
|
*/
|
|
643
716
|
setMockToolResponse(toolName, response) {
|
|
644
717
|
this.mockResponses.set(toolName, response);
|
|
645
|
-
console.log(`[Model Context Testing] Mock response set for tool: ${toolName}`);
|
|
646
718
|
}
|
|
647
719
|
/**
|
|
648
720
|
* Clears the mock response for a specific tool (polyfill extension).
|
|
@@ -651,14 +723,12 @@ var WebModelContextTesting = class {
|
|
|
651
723
|
*/
|
|
652
724
|
clearMockToolResponse(toolName) {
|
|
653
725
|
this.mockResponses.delete(toolName);
|
|
654
|
-
console.log(`[Model Context Testing] Mock response cleared for tool: ${toolName}`);
|
|
655
726
|
}
|
|
656
727
|
/**
|
|
657
728
|
* Clears all mock tool responses (polyfill extension).
|
|
658
729
|
*/
|
|
659
730
|
clearAllMockToolResponses() {
|
|
660
731
|
this.mockResponses.clear();
|
|
661
|
-
console.log("[Model Context Testing] All mock responses cleared");
|
|
662
732
|
}
|
|
663
733
|
/**
|
|
664
734
|
* Gets the current tools registered in the system (polyfill extension).
|
|
@@ -675,7 +745,6 @@ var WebModelContextTesting = class {
|
|
|
675
745
|
reset() {
|
|
676
746
|
this.clearToolCalls();
|
|
677
747
|
this.clearAllMockToolResponses();
|
|
678
|
-
console.log("[Model Context Testing] Testing state reset");
|
|
679
748
|
}
|
|
680
749
|
};
|
|
681
750
|
/**
|
|
@@ -781,14 +850,13 @@ var WebModelContext = class {
|
|
|
781
850
|
* @throws {Error} If a name/uri collides with existing dynamic items
|
|
782
851
|
*/
|
|
783
852
|
provideContext(context) {
|
|
784
|
-
const toolCount = context.tools?.length ?? 0;
|
|
785
|
-
const resourceCount = context.resources?.length ?? 0;
|
|
786
|
-
const promptCount = context.prompts?.length ?? 0;
|
|
787
|
-
console.log(`[Web Model Context] provideContext: ${toolCount} tools, ${resourceCount} resources, ${promptCount} prompts`);
|
|
788
853
|
this.provideContextTools.clear();
|
|
789
854
|
this.provideContextResources.clear();
|
|
790
855
|
this.provideContextPrompts.clear();
|
|
791
856
|
for (const tool of context.tools ?? []) {
|
|
857
|
+
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.`);
|
|
858
|
+
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.`);
|
|
859
|
+
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.`);
|
|
792
860
|
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.`);
|
|
793
861
|
const { jsonSchema: inputJson, zodValidator: inputZod } = normalizeSchema(tool.inputSchema);
|
|
794
862
|
const normalizedOutput = tool.outputSchema ? normalizeSchema(tool.outputSchema) : null;
|
|
@@ -871,11 +939,13 @@ var WebModelContext = class {
|
|
|
871
939
|
* @throws {Error} If tool name collides with existing tools
|
|
872
940
|
*/
|
|
873
941
|
registerTool(tool) {
|
|
874
|
-
|
|
942
|
+
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.`);
|
|
943
|
+
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.`);
|
|
944
|
+
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.`);
|
|
875
945
|
const now = Date.now();
|
|
876
946
|
const lastRegistration = this.toolRegistrationTimestamps.get(tool.name);
|
|
877
947
|
if (lastRegistration && now - lastRegistration < RAPID_DUPLICATE_WINDOW_MS) {
|
|
878
|
-
|
|
948
|
+
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.`);
|
|
879
949
|
const existingUnregister = this.toolUnregisterFunctions.get(tool.name);
|
|
880
950
|
if (existingUnregister) return { unregister: existingUnregister };
|
|
881
951
|
}
|
|
@@ -898,10 +968,9 @@ var WebModelContext = class {
|
|
|
898
968
|
this.updateBridgeTools();
|
|
899
969
|
this.scheduleListChanged("tools");
|
|
900
970
|
const unregisterFn = () => {
|
|
901
|
-
console.log(`[Web Model Context] Unregistering tool: ${tool.name}`);
|
|
902
971
|
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.`);
|
|
903
972
|
if (!this.dynamicTools.has(tool.name)) {
|
|
904
|
-
|
|
973
|
+
logger$1.warn(`Tool "${tool.name}" is not registered, ignoring unregister call`);
|
|
905
974
|
return;
|
|
906
975
|
}
|
|
907
976
|
this.dynamicTools.delete(tool.name);
|
|
@@ -922,11 +991,10 @@ var WebModelContext = class {
|
|
|
922
991
|
* @throws {Error} If resource URI collides with existing resources
|
|
923
992
|
*/
|
|
924
993
|
registerResource(resource) {
|
|
925
|
-
console.log(`[Web Model Context] Registering resource dynamically: ${resource.uri}`);
|
|
926
994
|
const now = Date.now();
|
|
927
995
|
const lastRegistration = this.resourceRegistrationTimestamps.get(resource.uri);
|
|
928
996
|
if (lastRegistration && now - lastRegistration < RAPID_DUPLICATE_WINDOW_MS) {
|
|
929
|
-
|
|
997
|
+
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.`);
|
|
930
998
|
const existingUnregister = this.resourceUnregisterFunctions.get(resource.uri);
|
|
931
999
|
if (existingUnregister) return { unregister: existingUnregister };
|
|
932
1000
|
}
|
|
@@ -938,10 +1006,9 @@ var WebModelContext = class {
|
|
|
938
1006
|
this.updateBridgeResources();
|
|
939
1007
|
this.scheduleListChanged("resources");
|
|
940
1008
|
const unregisterFn = () => {
|
|
941
|
-
console.log(`[Web Model Context] Unregistering resource: ${resource.uri}`);
|
|
942
1009
|
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.`);
|
|
943
1010
|
if (!this.dynamicResources.has(resource.uri)) {
|
|
944
|
-
|
|
1011
|
+
logger$1.warn(`Resource "${resource.uri}" is not registered, ignoring unregister call`);
|
|
945
1012
|
return;
|
|
946
1013
|
}
|
|
947
1014
|
this.dynamicResources.delete(resource.uri);
|
|
@@ -960,11 +1027,10 @@ var WebModelContext = class {
|
|
|
960
1027
|
* @param {string} uri - URI of the resource to unregister
|
|
961
1028
|
*/
|
|
962
1029
|
unregisterResource(uri) {
|
|
963
|
-
console.log(`[Web Model Context] Unregistering resource: ${uri}`);
|
|
964
1030
|
const inProvideContext = this.provideContextResources.has(uri);
|
|
965
1031
|
const inDynamic = this.dynamicResources.has(uri);
|
|
966
1032
|
if (!inProvideContext && !inDynamic) {
|
|
967
|
-
|
|
1033
|
+
logger$1.warn(`Resource "${uri}" is not registered, ignoring unregister call`);
|
|
968
1034
|
return;
|
|
969
1035
|
}
|
|
970
1036
|
if (inProvideContext) this.provideContextResources.delete(uri);
|
|
@@ -1013,11 +1079,10 @@ var WebModelContext = class {
|
|
|
1013
1079
|
* @throws {Error} If prompt name collides with existing prompts
|
|
1014
1080
|
*/
|
|
1015
1081
|
registerPrompt(prompt) {
|
|
1016
|
-
console.log(`[Web Model Context] Registering prompt dynamically: ${prompt.name}`);
|
|
1017
1082
|
const now = Date.now();
|
|
1018
1083
|
const lastRegistration = this.promptRegistrationTimestamps.get(prompt.name);
|
|
1019
1084
|
if (lastRegistration && now - lastRegistration < RAPID_DUPLICATE_WINDOW_MS) {
|
|
1020
|
-
|
|
1085
|
+
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.`);
|
|
1021
1086
|
const existingUnregister = this.promptUnregisterFunctions.get(prompt.name);
|
|
1022
1087
|
if (existingUnregister) return { unregister: existingUnregister };
|
|
1023
1088
|
}
|
|
@@ -1029,10 +1094,9 @@ var WebModelContext = class {
|
|
|
1029
1094
|
this.updateBridgePrompts();
|
|
1030
1095
|
this.scheduleListChanged("prompts");
|
|
1031
1096
|
const unregisterFn = () => {
|
|
1032
|
-
console.log(`[Web Model Context] Unregistering prompt: ${prompt.name}`);
|
|
1033
1097
|
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.`);
|
|
1034
1098
|
if (!this.dynamicPrompts.has(prompt.name)) {
|
|
1035
|
-
|
|
1099
|
+
logger$1.warn(`Prompt "${prompt.name}" is not registered, ignoring unregister call`);
|
|
1036
1100
|
return;
|
|
1037
1101
|
}
|
|
1038
1102
|
this.dynamicPrompts.delete(prompt.name);
|
|
@@ -1051,11 +1115,10 @@ var WebModelContext = class {
|
|
|
1051
1115
|
* @param {string} name - Name of the prompt to unregister
|
|
1052
1116
|
*/
|
|
1053
1117
|
unregisterPrompt(name) {
|
|
1054
|
-
console.log(`[Web Model Context] Unregistering prompt: ${name}`);
|
|
1055
1118
|
const inProvideContext = this.provideContextPrompts.has(name);
|
|
1056
1119
|
const inDynamic = this.dynamicPrompts.has(name);
|
|
1057
1120
|
if (!inProvideContext && !inDynamic) {
|
|
1058
|
-
|
|
1121
|
+
logger$1.warn(`Prompt "${name}" is not registered, ignoring unregister call`);
|
|
1059
1122
|
return;
|
|
1060
1123
|
}
|
|
1061
1124
|
if (inProvideContext) this.provideContextPrompts.delete(name);
|
|
@@ -1091,11 +1154,10 @@ var WebModelContext = class {
|
|
|
1091
1154
|
* @param {string} name - Name of the tool to unregister
|
|
1092
1155
|
*/
|
|
1093
1156
|
unregisterTool(name) {
|
|
1094
|
-
console.log(`[Web Model Context] Unregistering tool: ${name}`);
|
|
1095
1157
|
const inProvideContext = this.provideContextTools.has(name);
|
|
1096
1158
|
const inDynamic = this.dynamicTools.has(name);
|
|
1097
1159
|
if (!inProvideContext && !inDynamic) {
|
|
1098
|
-
|
|
1160
|
+
logger$1.warn(`Tool "${name}" is not registered, ignoring unregister call`);
|
|
1099
1161
|
return;
|
|
1100
1162
|
}
|
|
1101
1163
|
if (inProvideContext) this.provideContextTools.delete(name);
|
|
@@ -1112,7 +1174,6 @@ var WebModelContext = class {
|
|
|
1112
1174
|
* Removes all tools, resources, and prompts registered via provideContext() and register* methods.
|
|
1113
1175
|
*/
|
|
1114
1176
|
clearContext() {
|
|
1115
|
-
console.log("[Web Model Context] Clearing all context (tools, resources, prompts)");
|
|
1116
1177
|
this.provideContextTools.clear();
|
|
1117
1178
|
this.dynamicTools.clear();
|
|
1118
1179
|
this.toolRegistrationTimestamps.clear();
|
|
@@ -1142,7 +1203,6 @@ var WebModelContext = class {
|
|
|
1142
1203
|
this.bridge.tools.clear();
|
|
1143
1204
|
for (const [name, tool] of this.provideContextTools) this.bridge.tools.set(name, tool);
|
|
1144
1205
|
for (const [name, tool] of this.dynamicTools) this.bridge.tools.set(name, tool);
|
|
1145
|
-
console.log(`[Web Model Context] Updated bridge with ${this.provideContextTools.size} base tools + ${this.dynamicTools.size} dynamic tools = ${this.bridge.tools.size} total`);
|
|
1146
1206
|
}
|
|
1147
1207
|
/**
|
|
1148
1208
|
* Notifies all servers and testing callbacks that the tools list has changed.
|
|
@@ -1170,7 +1230,6 @@ var WebModelContext = class {
|
|
|
1170
1230
|
this.bridge.resources.clear();
|
|
1171
1231
|
for (const [uri, resource] of this.provideContextResources) this.bridge.resources.set(uri, resource);
|
|
1172
1232
|
for (const [uri, resource] of this.dynamicResources) this.bridge.resources.set(uri, resource);
|
|
1173
|
-
console.log(`[Web Model Context] Updated bridge with ${this.provideContextResources.size} base resources + ${this.dynamicResources.size} dynamic resources = ${this.bridge.resources.size} total`);
|
|
1174
1233
|
}
|
|
1175
1234
|
/**
|
|
1176
1235
|
* Notifies all servers that the resources list has changed.
|
|
@@ -1196,7 +1255,6 @@ var WebModelContext = class {
|
|
|
1196
1255
|
this.bridge.prompts.clear();
|
|
1197
1256
|
for (const [name, prompt] of this.provideContextPrompts) this.bridge.prompts.set(name, prompt);
|
|
1198
1257
|
for (const [name, prompt] of this.dynamicPrompts) this.bridge.prompts.set(name, prompt);
|
|
1199
|
-
console.log(`[Web Model Context] Updated bridge with ${this.provideContextPrompts.size} base prompts + ${this.dynamicPrompts.size} dynamic prompts = ${this.bridge.prompts.size} total`);
|
|
1200
1258
|
}
|
|
1201
1259
|
/**
|
|
1202
1260
|
* Notifies all servers that the prompts list has changed.
|
|
@@ -1239,7 +1297,7 @@ var WebModelContext = class {
|
|
|
1239
1297
|
break;
|
|
1240
1298
|
default: {
|
|
1241
1299
|
const _exhaustive = listType;
|
|
1242
|
-
|
|
1300
|
+
logger$1.error(`Unknown list type: ${_exhaustive}`);
|
|
1243
1301
|
}
|
|
1244
1302
|
}
|
|
1245
1303
|
});
|
|
@@ -1254,13 +1312,12 @@ var WebModelContext = class {
|
|
|
1254
1312
|
* @internal
|
|
1255
1313
|
*/
|
|
1256
1314
|
async readResource(uri) {
|
|
1257
|
-
console.log(`[Web Model Context] Reading resource: ${uri}`);
|
|
1258
1315
|
const staticResource = this.bridge.resources.get(uri);
|
|
1259
1316
|
if (staticResource && !staticResource.isTemplate) try {
|
|
1260
1317
|
const parsedUri = new URL(uri);
|
|
1261
1318
|
return await staticResource.read(parsedUri);
|
|
1262
1319
|
} catch (error) {
|
|
1263
|
-
|
|
1320
|
+
logger$1.error(`Error reading resource ${uri}:`, error);
|
|
1264
1321
|
throw error;
|
|
1265
1322
|
}
|
|
1266
1323
|
for (const resource of this.bridge.resources.values()) {
|
|
@@ -1270,7 +1327,7 @@ var WebModelContext = class {
|
|
|
1270
1327
|
const parsedUri = new URL(uri);
|
|
1271
1328
|
return await resource.read(parsedUri, params);
|
|
1272
1329
|
} catch (error) {
|
|
1273
|
-
|
|
1330
|
+
logger$1.error(`Error reading resource ${uri}:`, error);
|
|
1274
1331
|
throw error;
|
|
1275
1332
|
}
|
|
1276
1333
|
}
|
|
@@ -1315,20 +1372,19 @@ var WebModelContext = class {
|
|
|
1315
1372
|
* @internal
|
|
1316
1373
|
*/
|
|
1317
1374
|
async getPrompt(name, args) {
|
|
1318
|
-
console.log(`[Web Model Context] Getting prompt: ${name}`);
|
|
1319
1375
|
const prompt = this.bridge.prompts.get(name);
|
|
1320
1376
|
if (!prompt) throw new Error(`Prompt not found: ${name}`);
|
|
1321
1377
|
if (prompt.argsValidator && args) {
|
|
1322
1378
|
const validation = validateWithZod(args, prompt.argsValidator);
|
|
1323
1379
|
if (!validation.success) {
|
|
1324
|
-
|
|
1380
|
+
logger$1.error(`Argument validation failed for prompt ${name}:`, validation.error);
|
|
1325
1381
|
throw new Error(`Argument validation error for prompt "${name}":\n${validation.error}`);
|
|
1326
1382
|
}
|
|
1327
1383
|
}
|
|
1328
1384
|
try {
|
|
1329
1385
|
return await prompt.get(args ?? {});
|
|
1330
1386
|
} catch (error) {
|
|
1331
|
-
|
|
1387
|
+
logger$1.error(`Error getting prompt ${name}:`, error);
|
|
1332
1388
|
throw error;
|
|
1333
1389
|
}
|
|
1334
1390
|
}
|
|
@@ -1351,10 +1407,9 @@ var WebModelContext = class {
|
|
|
1351
1407
|
async executeTool(toolName, args) {
|
|
1352
1408
|
const tool = this.bridge.tools.get(toolName);
|
|
1353
1409
|
if (!tool) throw new Error(`Tool not found: ${toolName}`);
|
|
1354
|
-
console.log(`[Web Model Context] Validating input for tool: ${toolName}`);
|
|
1355
1410
|
const validation = validateWithZod(args, tool.inputValidator);
|
|
1356
1411
|
if (!validation.success) {
|
|
1357
|
-
|
|
1412
|
+
logger$1.error(`Input validation failed for ${toolName}:`, validation.error);
|
|
1358
1413
|
return {
|
|
1359
1414
|
content: [{
|
|
1360
1415
|
type: "text",
|
|
@@ -1367,31 +1422,24 @@ var WebModelContext = class {
|
|
|
1367
1422
|
if (this.testingAPI) this.testingAPI.recordToolCall(toolName, validatedArgs);
|
|
1368
1423
|
if (this.testingAPI?.hasMockResponse(toolName)) {
|
|
1369
1424
|
const mockResponse = this.testingAPI.getMockResponse(toolName);
|
|
1370
|
-
if (mockResponse)
|
|
1371
|
-
console.log(`[Web Model Context] Returning mock response for tool: ${toolName}`);
|
|
1372
|
-
return mockResponse;
|
|
1373
|
-
}
|
|
1425
|
+
if (mockResponse) return mockResponse;
|
|
1374
1426
|
}
|
|
1375
1427
|
const event = new WebToolCallEvent(toolName, validatedArgs);
|
|
1376
1428
|
this.dispatchEvent(event);
|
|
1377
1429
|
if (event.defaultPrevented && event.hasResponse()) {
|
|
1378
1430
|
const response = event.getResponse();
|
|
1379
|
-
if (response)
|
|
1380
|
-
console.log(`[Web Model Context] Tool ${toolName} handled by event listener`);
|
|
1381
|
-
return response;
|
|
1382
|
-
}
|
|
1431
|
+
if (response) return response;
|
|
1383
1432
|
}
|
|
1384
|
-
console.log(`[Web Model Context] Executing tool: ${toolName}`);
|
|
1385
1433
|
try {
|
|
1386
1434
|
const response = await tool.execute(validatedArgs);
|
|
1387
1435
|
if (tool.outputValidator && response.structuredContent) {
|
|
1388
1436
|
const outputValidation = validateWithZod(response.structuredContent, tool.outputValidator);
|
|
1389
|
-
if (!outputValidation.success)
|
|
1437
|
+
if (!outputValidation.success) logger$1.warn(`Output validation failed for ${toolName}:`, outputValidation.error);
|
|
1390
1438
|
}
|
|
1391
|
-
if (response.metadata && "willNavigate" in response.metadata)
|
|
1439
|
+
if (response.metadata && typeof response.metadata === "object" && "willNavigate" in response.metadata) logger$1.info(`Tool "${toolName}" will trigger navigation`, response.metadata);
|
|
1392
1440
|
return response;
|
|
1393
1441
|
} catch (error) {
|
|
1394
|
-
|
|
1442
|
+
logger$1.error(`Error executing tool ${toolName}:`, error);
|
|
1395
1443
|
return {
|
|
1396
1444
|
content: [{
|
|
1397
1445
|
type: "text",
|
|
@@ -1425,7 +1473,6 @@ var WebModelContext = class {
|
|
|
1425
1473
|
* @returns {Promise<SamplingResult>} The LLM completion result
|
|
1426
1474
|
*/
|
|
1427
1475
|
async createMessage(params) {
|
|
1428
|
-
console.log("[Web Model Context] Requesting sampling from client");
|
|
1429
1476
|
const underlyingServer = this.bridge.tabServer.server;
|
|
1430
1477
|
if (!underlyingServer?.createMessage) throw new Error("Sampling is not supported: no connected client with sampling capability");
|
|
1431
1478
|
return underlyingServer.createMessage(params);
|
|
@@ -1438,7 +1485,6 @@ var WebModelContext = class {
|
|
|
1438
1485
|
* @returns {Promise<ElicitationResult>} The user's response
|
|
1439
1486
|
*/
|
|
1440
1487
|
async elicitInput(params) {
|
|
1441
|
-
console.log("[Web Model Context] Requesting elicitation from client");
|
|
1442
1488
|
const underlyingServer = this.bridge.tabServer.server;
|
|
1443
1489
|
if (!underlyingServer?.elicitInput) throw new Error("Elicitation is not supported: no connected client with elicitation capability");
|
|
1444
1490
|
return underlyingServer.elicitInput(params);
|
|
@@ -1453,16 +1499,13 @@ var WebModelContext = class {
|
|
|
1453
1499
|
* @returns {MCPBridge} The initialized MCP bridge
|
|
1454
1500
|
*/
|
|
1455
1501
|
function initializeMCPBridge(options) {
|
|
1456
|
-
console.log("[Web Model Context] Initializing MCP bridge");
|
|
1457
1502
|
const hostname = window.location.hostname || "localhost";
|
|
1458
1503
|
const transportOptions = options?.transport;
|
|
1459
1504
|
const setupServerHandlers = (server, bridge$1) => {
|
|
1460
1505
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
1461
|
-
console.log("[MCP Bridge] Handling list_tools request");
|
|
1462
1506
|
return { tools: bridge$1.modelContext.listTools() };
|
|
1463
1507
|
});
|
|
1464
1508
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1465
|
-
console.log(`[MCP Bridge] Handling call_tool request: ${request.params.name}`);
|
|
1466
1509
|
const toolName = request.params.name;
|
|
1467
1510
|
const args = request.params.arguments || {};
|
|
1468
1511
|
try {
|
|
@@ -1473,40 +1516,35 @@ function initializeMCPBridge(options) {
|
|
|
1473
1516
|
...response.structuredContent && { structuredContent: response.structuredContent }
|
|
1474
1517
|
};
|
|
1475
1518
|
} catch (error) {
|
|
1476
|
-
|
|
1519
|
+
bridgeLogger.error(`Error calling tool ${toolName}:`, error);
|
|
1477
1520
|
throw error;
|
|
1478
1521
|
}
|
|
1479
1522
|
});
|
|
1480
1523
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
1481
|
-
console.log("[MCP Bridge] Handling list_resources request");
|
|
1482
1524
|
return { resources: bridge$1.modelContext.listResources() };
|
|
1483
1525
|
});
|
|
1484
1526
|
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
1485
|
-
console.log(`[MCP Bridge] Handling read_resource request: ${request.params.uri}`);
|
|
1486
1527
|
try {
|
|
1487
1528
|
return await bridge$1.modelContext.readResource(request.params.uri);
|
|
1488
1529
|
} catch (error) {
|
|
1489
|
-
|
|
1530
|
+
bridgeLogger.error(`Error reading resource ${request.params.uri}:`, error);
|
|
1490
1531
|
throw error;
|
|
1491
1532
|
}
|
|
1492
1533
|
});
|
|
1493
1534
|
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
1494
|
-
console.log("[MCP Bridge] Handling list_prompts request");
|
|
1495
1535
|
return { prompts: bridge$1.modelContext.listPrompts() };
|
|
1496
1536
|
});
|
|
1497
1537
|
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
1498
|
-
console.log(`[MCP Bridge] Handling get_prompt request: ${request.params.name}`);
|
|
1499
1538
|
try {
|
|
1500
1539
|
return await bridge$1.modelContext.getPrompt(request.params.name, request.params.arguments);
|
|
1501
1540
|
} catch (error) {
|
|
1502
|
-
|
|
1541
|
+
bridgeLogger.error(`Error getting prompt ${request.params.name}:`, error);
|
|
1503
1542
|
throw error;
|
|
1504
1543
|
}
|
|
1505
1544
|
});
|
|
1506
1545
|
};
|
|
1507
1546
|
const customTransport = transportOptions?.create?.();
|
|
1508
1547
|
if (customTransport) {
|
|
1509
|
-
console.log("[Web Model Context] Using custom transport");
|
|
1510
1548
|
const server = new Server({
|
|
1511
1549
|
name: hostname,
|
|
1512
1550
|
version: "1.0.0"
|
|
@@ -1526,10 +1564,8 @@ function initializeMCPBridge(options) {
|
|
|
1526
1564
|
bridge$1.modelContext = new WebModelContext(bridge$1);
|
|
1527
1565
|
setupServerHandlers(server, bridge$1);
|
|
1528
1566
|
server.connect(customTransport);
|
|
1529
|
-
console.log("[Web Model Context] MCP server connected with custom transport");
|
|
1530
1567
|
return bridge$1;
|
|
1531
1568
|
}
|
|
1532
|
-
console.log("[Web Model Context] Using dual-server mode");
|
|
1533
1569
|
const tabServerEnabled = transportOptions?.tabServer !== false;
|
|
1534
1570
|
const tabServer = new Server({
|
|
1535
1571
|
name: `${hostname}-tab`,
|
|
@@ -1556,12 +1592,10 @@ function initializeMCPBridge(options) {
|
|
|
1556
1592
|
...restTabServerOptions
|
|
1557
1593
|
});
|
|
1558
1594
|
tabServer.connect(tabTransport);
|
|
1559
|
-
console.log("[Web Model Context] Tab server connected");
|
|
1560
1595
|
}
|
|
1561
1596
|
const isInIframe = typeof window !== "undefined" && window.parent !== window;
|
|
1562
1597
|
const iframeServerConfig = transportOptions?.iframeServer;
|
|
1563
1598
|
if (iframeServerConfig !== false && (iframeServerConfig !== void 0 || isInIframe)) {
|
|
1564
|
-
console.log("[Web Model Context] Enabling iframe server");
|
|
1565
1599
|
const iframeServer = new Server({
|
|
1566
1600
|
name: `${hostname}-iframe`,
|
|
1567
1601
|
version: "1.0.0"
|
|
@@ -1578,7 +1612,6 @@ function initializeMCPBridge(options) {
|
|
|
1578
1612
|
});
|
|
1579
1613
|
iframeServer.connect(iframeTransport);
|
|
1580
1614
|
bridge.iframeServer = iframeServer;
|
|
1581
|
-
console.log("[Web Model Context] Iframe server connected");
|
|
1582
1615
|
}
|
|
1583
1616
|
return bridge;
|
|
1584
1617
|
}
|
|
@@ -1604,7 +1637,7 @@ function initializeMCPBridge(options) {
|
|
|
1604
1637
|
*/
|
|
1605
1638
|
function initializeWebModelContext(options) {
|
|
1606
1639
|
if (typeof window === "undefined") {
|
|
1607
|
-
|
|
1640
|
+
logger$1.warn("Not in browser environment, skipping initialization");
|
|
1608
1641
|
return;
|
|
1609
1642
|
}
|
|
1610
1643
|
const effectiveOptions = options ?? window.__webModelContextOptions;
|
|
@@ -1613,12 +1646,12 @@ function initializeWebModelContext(options) {
|
|
|
1613
1646
|
const nativeContext = window.navigator.modelContext;
|
|
1614
1647
|
const nativeTesting = window.navigator.modelContextTesting;
|
|
1615
1648
|
if (!nativeContext || !nativeTesting) {
|
|
1616
|
-
|
|
1649
|
+
logger$1.error("Native API detection mismatch");
|
|
1617
1650
|
return;
|
|
1618
1651
|
}
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1652
|
+
logger$1.info("✅ Native Chromium API detected");
|
|
1653
|
+
logger$1.info(" Using native implementation with MCP bridge synchronization");
|
|
1654
|
+
logger$1.info(" Native API will automatically collect tools from embedded iframes");
|
|
1622
1655
|
try {
|
|
1623
1656
|
const bridge = initializeMCPBridge(effectiveOptions);
|
|
1624
1657
|
bridge.modelContext = new NativeModelContextAdapter(bridge, nativeContext, nativeTesting);
|
|
@@ -1628,29 +1661,29 @@ function initializeWebModelContext(options) {
|
|
|
1628
1661
|
writable: false,
|
|
1629
1662
|
configurable: true
|
|
1630
1663
|
});
|
|
1631
|
-
|
|
1632
|
-
|
|
1664
|
+
logger$1.info("✅ MCP bridge synced with native API");
|
|
1665
|
+
logger$1.info(" MCP clients will receive automatic tool updates from native registry");
|
|
1633
1666
|
} catch (error) {
|
|
1634
|
-
|
|
1667
|
+
logger$1.error("Failed to initialize native adapter:", error);
|
|
1635
1668
|
throw error;
|
|
1636
1669
|
}
|
|
1637
1670
|
return;
|
|
1638
1671
|
}
|
|
1639
1672
|
if (native.hasNativeContext && !native.hasNativeTesting) {
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1673
|
+
logger$1.warn("Partial native API detected");
|
|
1674
|
+
logger$1.warn(" navigator.modelContext exists but navigator.modelContextTesting is missing");
|
|
1675
|
+
logger$1.warn(" Cannot sync with native API. Please enable experimental features:");
|
|
1676
|
+
logger$1.warn(" - Navigate to chrome://flags");
|
|
1677
|
+
logger$1.warn(" - Enable \"Experimental Web Platform Features\"");
|
|
1678
|
+
logger$1.warn(" - Or launch with: --enable-experimental-web-platform-features");
|
|
1679
|
+
logger$1.warn(" Skipping initialization to avoid conflicts");
|
|
1647
1680
|
return;
|
|
1648
1681
|
}
|
|
1649
1682
|
if (window.navigator.modelContext) {
|
|
1650
|
-
|
|
1683
|
+
logger$1.warn("window.navigator.modelContext already exists, skipping initialization");
|
|
1651
1684
|
return;
|
|
1652
1685
|
}
|
|
1653
|
-
|
|
1686
|
+
logger$1.info("Native API not detected, installing polyfill");
|
|
1654
1687
|
try {
|
|
1655
1688
|
const bridge = initializeMCPBridge(effectiveOptions);
|
|
1656
1689
|
Object.defineProperty(window.navigator, "modelContext", {
|
|
@@ -1663,12 +1696,12 @@ function initializeWebModelContext(options) {
|
|
|
1663
1696
|
writable: false,
|
|
1664
1697
|
configurable: true
|
|
1665
1698
|
});
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1699
|
+
logger$1.info("✅ window.navigator.modelContext initialized successfully");
|
|
1700
|
+
testingLogger.info("Installing polyfill");
|
|
1701
|
+
testingLogger.info(" 💡 To use the native implementation in Chromium:");
|
|
1702
|
+
testingLogger.info(" - Navigate to chrome://flags");
|
|
1703
|
+
testingLogger.info(" - Enable \"Experimental Web Platform Features\"");
|
|
1704
|
+
testingLogger.info(" - Or launch with: --enable-experimental-web-platform-features");
|
|
1672
1705
|
const testingAPI = new WebModelContextTesting(bridge);
|
|
1673
1706
|
bridge.modelContextTesting = testingAPI;
|
|
1674
1707
|
bridge.modelContext.setTestingAPI(testingAPI);
|
|
@@ -1677,9 +1710,9 @@ function initializeWebModelContext(options) {
|
|
|
1677
1710
|
writable: false,
|
|
1678
1711
|
configurable: true
|
|
1679
1712
|
});
|
|
1680
|
-
|
|
1713
|
+
testingLogger.info("✅ Polyfill installed at window.navigator.modelContextTesting");
|
|
1681
1714
|
} catch (error) {
|
|
1682
|
-
|
|
1715
|
+
logger$1.error("Failed to initialize:", error);
|
|
1683
1716
|
throw error;
|
|
1684
1717
|
}
|
|
1685
1718
|
}
|
|
@@ -1701,16 +1734,17 @@ function cleanupWebModelContext() {
|
|
|
1701
1734
|
window.__mcpBridge.tabServer.close();
|
|
1702
1735
|
if (window.__mcpBridge.iframeServer) window.__mcpBridge.iframeServer.close();
|
|
1703
1736
|
} catch (error) {
|
|
1704
|
-
|
|
1737
|
+
logger$1.warn("Error closing MCP servers:", error);
|
|
1705
1738
|
}
|
|
1706
1739
|
delete window.navigator.modelContext;
|
|
1707
1740
|
delete window.navigator.modelContextTesting;
|
|
1708
1741
|
delete window.__mcpBridge;
|
|
1709
|
-
|
|
1742
|
+
logger$1.info("Cleaned up");
|
|
1710
1743
|
}
|
|
1711
1744
|
|
|
1712
1745
|
//#endregion
|
|
1713
1746
|
//#region src/index.ts
|
|
1747
|
+
const logger = createLogger("WebModelContext");
|
|
1714
1748
|
function mergeTransportOptions(base, override) {
|
|
1715
1749
|
if (!base) return override;
|
|
1716
1750
|
if (!override) return base;
|
|
@@ -1738,7 +1772,7 @@ function parseScriptTagOptions(script) {
|
|
|
1738
1772
|
if (dataset.webmcpOptions) try {
|
|
1739
1773
|
return JSON.parse(dataset.webmcpOptions);
|
|
1740
1774
|
} catch (error) {
|
|
1741
|
-
|
|
1775
|
+
logger.error("Invalid JSON in data-webmcp-options:", error);
|
|
1742
1776
|
return;
|
|
1743
1777
|
}
|
|
1744
1778
|
const options = {};
|
|
@@ -1781,10 +1815,10 @@ if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
|
1781
1815
|
try {
|
|
1782
1816
|
if (shouldAutoInitialize) initializeWebModelContext(mergedOptions);
|
|
1783
1817
|
} catch (error) {
|
|
1784
|
-
|
|
1818
|
+
logger.error("Auto-initialization failed:", error);
|
|
1785
1819
|
}
|
|
1786
1820
|
}
|
|
1787
1821
|
|
|
1788
1822
|
//#endregion
|
|
1789
|
-
export { cleanupWebModelContext, initializeWebModelContext, zodToJsonSchema };
|
|
1823
|
+
export { cleanupWebModelContext, createLogger, initializeWebModelContext, zodToJsonSchema };
|
|
1790
1824
|
//# sourceMappingURL=index.js.map
|