@mcp-b/global 1.2.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/dist/index.d.ts +186 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.iife.js +31 -18
- package/dist/index.js +194 -137
- package/dist/index.js.map +1 -1
- package/package.json +3 -5
package/dist/index.js
CHANGED
|
@@ -1,45 +1,130 @@
|
|
|
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 (err) {
|
|
47
|
+
if (typeof console !== "undefined" && console.warn) {
|
|
48
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
49
|
+
console.warn(`[WebMCP] localStorage access failed, debug logging disabled: ${message}`);
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* No-op function for disabled log levels
|
|
56
|
+
*/
|
|
57
|
+
const noop = () => {};
|
|
58
|
+
/**
|
|
59
|
+
* Create a namespaced logger
|
|
60
|
+
*
|
|
61
|
+
* Uses .bind() to prepend namespace prefixes to console methods without manual
|
|
62
|
+
* string concatenation. Debug enablement is determined at logger creation time
|
|
63
|
+
* for performance - changes to localStorage after creation won't affect existing
|
|
64
|
+
* loggers. Refresh the page to apply new WEBMCP_DEBUG settings.
|
|
65
|
+
*
|
|
66
|
+
* @param namespace - Namespace for the logger (e.g., 'WebModelContext', 'NativeAdapter')
|
|
67
|
+
* @returns Logger instance with debug, info, warn, error methods
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* const logger = createLogger('WebModelContext');
|
|
72
|
+
* logger.debug('Tool registered:', toolName); // Only shown if WEBMCP_DEBUG includes 'WebModelContext'
|
|
73
|
+
* logger.error('Execution failed:', error); // Always enabled
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
function createLogger(namespace) {
|
|
77
|
+
const prefix = `[${namespace}]`;
|
|
78
|
+
const isDebug = isDebugEnabled(namespace);
|
|
79
|
+
const boundWarn = console.warn.bind(console, prefix);
|
|
80
|
+
const boundError = console.error.bind(console, prefix);
|
|
81
|
+
const boundLog = console.log.bind(console, prefix);
|
|
82
|
+
return {
|
|
83
|
+
warn: boundWarn,
|
|
84
|
+
error: boundError,
|
|
85
|
+
debug: isDebug ? boundLog : noop,
|
|
86
|
+
info: isDebug ? boundLog : noop
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//#endregion
|
|
7
91
|
//#region src/validation.ts
|
|
92
|
+
const logger$2 = createLogger("WebModelContext");
|
|
8
93
|
/**
|
|
9
94
|
* Detect if a schema is a Zod schema object (Record<string, ZodType>)
|
|
10
|
-
* or a JSON Schema object
|
|
95
|
+
* or a JSON Schema object.
|
|
96
|
+
*
|
|
97
|
+
* Uses duck-typing to detect Zod 4 schemas by checking for the `_zod` property.
|
|
11
98
|
*/
|
|
12
99
|
function isZodSchema(schema) {
|
|
13
100
|
if (typeof schema !== "object" || schema === null) return false;
|
|
14
101
|
if ("type" in schema && typeof schema.type === "string") return false;
|
|
15
102
|
const values = Object.values(schema);
|
|
16
103
|
if (values.length === 0) return false;
|
|
17
|
-
return values.some((val) => val
|
|
104
|
+
return values.some((val) => val != null && typeof val === "object" && "_zod" in val);
|
|
18
105
|
}
|
|
19
106
|
/**
|
|
20
107
|
* Convert JSON Schema to Zod validator
|
|
21
|
-
* Uses
|
|
108
|
+
* Uses Zod 4's native z.fromJSONSchema() for conversion
|
|
22
109
|
*/
|
|
23
|
-
function jsonSchemaToZod
|
|
110
|
+
function jsonSchemaToZod(jsonSchema) {
|
|
24
111
|
try {
|
|
25
|
-
return
|
|
112
|
+
return z.fromJSONSchema(jsonSchema);
|
|
26
113
|
} catch (error) {
|
|
27
|
-
|
|
114
|
+
logger$2.warn("Failed to convert JSON Schema to Zod:", error);
|
|
28
115
|
return z.object({}).passthrough();
|
|
29
116
|
}
|
|
30
117
|
}
|
|
31
118
|
/**
|
|
32
119
|
* Convert Zod schema object to JSON Schema
|
|
33
|
-
* Uses
|
|
120
|
+
* Uses Zod 4's native z.toJSONSchema() for conversion
|
|
34
121
|
*
|
|
35
122
|
* @param schema - Record of Zod type definitions (e.g., { name: z.string(), age: z.number() })
|
|
36
123
|
* @returns JSON Schema object compatible with MCP InputSchema
|
|
37
124
|
*/
|
|
38
125
|
function zodToJsonSchema(schema) {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
target: "jsonSchema7"
|
|
42
|
-
});
|
|
126
|
+
const zodObject = z.object(schema);
|
|
127
|
+
const { $schema: _,...rest } = z.toJSONSchema(zodObject);
|
|
43
128
|
return rest;
|
|
44
129
|
}
|
|
45
130
|
/**
|
|
@@ -54,7 +139,7 @@ function normalizeSchema(schema) {
|
|
|
54
139
|
const jsonSchema = schema;
|
|
55
140
|
return {
|
|
56
141
|
jsonSchema,
|
|
57
|
-
zodValidator: jsonSchemaToZod
|
|
142
|
+
zodValidator: jsonSchemaToZod(jsonSchema)
|
|
58
143
|
};
|
|
59
144
|
}
|
|
60
145
|
/**
|
|
@@ -64,7 +149,7 @@ function validateWithZod(data, validator) {
|
|
|
64
149
|
const result = validator.safeParse(data);
|
|
65
150
|
if (!result.success) return {
|
|
66
151
|
success: false,
|
|
67
|
-
error: `Validation failed:\n${result.error.
|
|
152
|
+
error: `Validation failed:\n${result.error.issues.map((err) => ` - ${err.path.join(".") || "root"}: ${err.message}`).join("\n")}`
|
|
68
153
|
};
|
|
69
154
|
return {
|
|
70
155
|
success: true,
|
|
@@ -74,11 +159,24 @@ function validateWithZod(data, validator) {
|
|
|
74
159
|
|
|
75
160
|
//#endregion
|
|
76
161
|
//#region src/global.ts
|
|
162
|
+
const logger$1 = createLogger("WebModelContext");
|
|
163
|
+
const nativeLogger = createLogger("NativeAdapter");
|
|
164
|
+
const bridgeLogger = createLogger("MCPBridge");
|
|
165
|
+
const testingLogger = createLogger("ModelContextTesting");
|
|
166
|
+
/**
|
|
167
|
+
* Marker property name used to identify polyfill implementations.
|
|
168
|
+
* This constant ensures single source of truth for the marker used in
|
|
169
|
+
* both detection (detectNativeAPI) and definition (WebModelContextTesting).
|
|
170
|
+
*/
|
|
171
|
+
const POLYFILL_MARKER_PROPERTY = "__isWebMCPPolyfill";
|
|
77
172
|
/**
|
|
78
173
|
* Detect if the native Chromium Web Model Context API is available.
|
|
79
174
|
* Checks for both navigator.modelContext and navigator.modelContextTesting,
|
|
80
|
-
* and verifies they are native implementations (not polyfills)
|
|
81
|
-
*
|
|
175
|
+
* and verifies they are native implementations (not polyfills).
|
|
176
|
+
*
|
|
177
|
+
* Detection uses a marker property (`__isWebMCPPolyfill`) on the testing API
|
|
178
|
+
* to reliably distinguish polyfills from native implementations. This approach
|
|
179
|
+
* works correctly even when class names are minified in production builds.
|
|
82
180
|
*
|
|
83
181
|
* @returns Detection result with flags for native context and testing API availability
|
|
84
182
|
*/
|
|
@@ -93,7 +191,7 @@ function detectNativeAPI() {
|
|
|
93
191
|
hasNativeContext: false,
|
|
94
192
|
hasNativeTesting: false
|
|
95
193
|
};
|
|
96
|
-
if (
|
|
194
|
+
if (POLYFILL_MARKER_PROPERTY in modelContextTesting && modelContextTesting[POLYFILL_MARKER_PROPERTY] === true) return {
|
|
97
195
|
hasNativeContext: false,
|
|
98
196
|
hasNativeTesting: false
|
|
99
197
|
};
|
|
@@ -133,7 +231,6 @@ var NativeModelContextAdapter = class {
|
|
|
133
231
|
this.nativeContext = nativeContext;
|
|
134
232
|
this.nativeTesting = nativeTesting;
|
|
135
233
|
this.nativeTesting.registerToolsChangedCallback(() => {
|
|
136
|
-
console.log("[Native Adapter] Tool change detected from native API");
|
|
137
234
|
this.syncToolsFromNative();
|
|
138
235
|
});
|
|
139
236
|
this.syncToolsFromNative();
|
|
@@ -150,7 +247,6 @@ var NativeModelContextAdapter = class {
|
|
|
150
247
|
this.syncInProgress = true;
|
|
151
248
|
try {
|
|
152
249
|
const nativeTools = this.nativeTesting.listTools();
|
|
153
|
-
console.log(`[Native Adapter] Syncing ${nativeTools.length} tools from native API`);
|
|
154
250
|
this.bridge.tools.clear();
|
|
155
251
|
for (const toolInfo of nativeTools) try {
|
|
156
252
|
const inputSchema = JSON.parse(toolInfo.inputSchema);
|
|
@@ -162,11 +258,11 @@ var NativeModelContextAdapter = class {
|
|
|
162
258
|
const result = await this.nativeTesting.executeTool(toolInfo.name, JSON.stringify(args));
|
|
163
259
|
return this.convertToToolResponse(result);
|
|
164
260
|
},
|
|
165
|
-
inputValidator: jsonSchemaToZod
|
|
261
|
+
inputValidator: jsonSchemaToZod(inputSchema)
|
|
166
262
|
};
|
|
167
263
|
this.bridge.tools.set(toolInfo.name, validatedTool);
|
|
168
264
|
} catch (error) {
|
|
169
|
-
|
|
265
|
+
nativeLogger.error(`Failed to sync tool "${toolInfo.name}":`, error);
|
|
170
266
|
}
|
|
171
267
|
this.notifyMCPServers();
|
|
172
268
|
} finally {
|
|
@@ -226,7 +322,6 @@ var NativeModelContextAdapter = class {
|
|
|
226
322
|
* @param {ModelContextInput} context - Context containing tools to register
|
|
227
323
|
*/
|
|
228
324
|
provideContext(context) {
|
|
229
|
-
console.log("[Native Adapter] Delegating provideContext to native API");
|
|
230
325
|
this.nativeContext.provideContext(context);
|
|
231
326
|
}
|
|
232
327
|
/**
|
|
@@ -238,7 +333,6 @@ var NativeModelContextAdapter = class {
|
|
|
238
333
|
* @returns {{unregister: () => void}} Object with unregister function
|
|
239
334
|
*/
|
|
240
335
|
registerTool(tool) {
|
|
241
|
-
console.log(`[Native Adapter] Delegating registerTool("${tool.name}") to native API`);
|
|
242
336
|
return this.nativeContext.registerTool(tool);
|
|
243
337
|
}
|
|
244
338
|
/**
|
|
@@ -248,7 +342,6 @@ var NativeModelContextAdapter = class {
|
|
|
248
342
|
* @param {string} name - Name of the tool to unregister
|
|
249
343
|
*/
|
|
250
344
|
unregisterTool(name) {
|
|
251
|
-
console.log(`[Native Adapter] Delegating unregisterTool("${name}") to native API`);
|
|
252
345
|
this.nativeContext.unregisterTool(name);
|
|
253
346
|
}
|
|
254
347
|
/**
|
|
@@ -256,7 +349,6 @@ var NativeModelContextAdapter = class {
|
|
|
256
349
|
* Delegates to navigator.modelContext.clearContext().
|
|
257
350
|
*/
|
|
258
351
|
clearContext() {
|
|
259
|
-
console.log("[Native Adapter] Delegating clearContext to native API");
|
|
260
352
|
this.nativeContext.clearContext();
|
|
261
353
|
}
|
|
262
354
|
/**
|
|
@@ -269,12 +361,11 @@ var NativeModelContextAdapter = class {
|
|
|
269
361
|
* @internal
|
|
270
362
|
*/
|
|
271
363
|
async executeTool(toolName, args) {
|
|
272
|
-
console.log(`[Native Adapter] Executing tool "${toolName}" via native API`);
|
|
273
364
|
try {
|
|
274
365
|
const result = await this.nativeTesting.executeTool(toolName, JSON.stringify(args));
|
|
275
366
|
return this.convertToToolResponse(result);
|
|
276
367
|
} catch (error) {
|
|
277
|
-
|
|
368
|
+
nativeLogger.error(`Error executing tool "${toolName}":`, error);
|
|
278
369
|
return {
|
|
279
370
|
content: [{
|
|
280
371
|
type: "text",
|
|
@@ -305,7 +396,7 @@ var NativeModelContextAdapter = class {
|
|
|
305
396
|
* This is a polyfill-only feature.
|
|
306
397
|
*/
|
|
307
398
|
registerResource(_resource) {
|
|
308
|
-
|
|
399
|
+
nativeLogger.warn("registerResource is not supported by native API");
|
|
309
400
|
return { unregister: () => {} };
|
|
310
401
|
}
|
|
311
402
|
/**
|
|
@@ -313,7 +404,7 @@ var NativeModelContextAdapter = class {
|
|
|
313
404
|
* Note: Native Chromium API does not yet support resources.
|
|
314
405
|
*/
|
|
315
406
|
unregisterResource(_uri) {
|
|
316
|
-
|
|
407
|
+
nativeLogger.warn("unregisterResource is not supported by native API");
|
|
317
408
|
}
|
|
318
409
|
/**
|
|
319
410
|
* Lists all registered resources.
|
|
@@ -343,7 +434,7 @@ var NativeModelContextAdapter = class {
|
|
|
343
434
|
* This is a polyfill-only feature.
|
|
344
435
|
*/
|
|
345
436
|
registerPrompt(_prompt) {
|
|
346
|
-
|
|
437
|
+
nativeLogger.warn("registerPrompt is not supported by native API");
|
|
347
438
|
return { unregister: () => {} };
|
|
348
439
|
}
|
|
349
440
|
/**
|
|
@@ -351,7 +442,7 @@ var NativeModelContextAdapter = class {
|
|
|
351
442
|
* Note: Native Chromium API does not yet support prompts.
|
|
352
443
|
*/
|
|
353
444
|
unregisterPrompt(_name) {
|
|
354
|
-
|
|
445
|
+
nativeLogger.warn("unregisterPrompt is not supported by native API");
|
|
355
446
|
}
|
|
356
447
|
/**
|
|
357
448
|
* Lists all registered prompts.
|
|
@@ -406,7 +497,6 @@ var NativeModelContextAdapter = class {
|
|
|
406
497
|
* This is handled by the polyfill.
|
|
407
498
|
*/
|
|
408
499
|
async createMessage(params) {
|
|
409
|
-
console.log("[Native Adapter] Requesting sampling from client");
|
|
410
500
|
const underlyingServer = this.bridge.tabServer.server;
|
|
411
501
|
if (!underlyingServer?.createMessage) throw new Error("Sampling is not supported: no connected client with sampling capability");
|
|
412
502
|
return underlyingServer.createMessage(params);
|
|
@@ -417,7 +507,6 @@ var NativeModelContextAdapter = class {
|
|
|
417
507
|
* This is handled by the polyfill.
|
|
418
508
|
*/
|
|
419
509
|
async elicitInput(params) {
|
|
420
|
-
console.log("[Native Adapter] Requesting elicitation from client");
|
|
421
510
|
const underlyingServer = this.bridge.tabServer.server;
|
|
422
511
|
if (!underlyingServer?.elicitInput) throw new Error("Elicitation is not supported: no connected client with elicitation capability");
|
|
423
512
|
return underlyingServer.elicitInput(params);
|
|
@@ -490,6 +579,15 @@ const RAPID_DUPLICATE_WINDOW_MS = 50;
|
|
|
490
579
|
* @implements {ModelContextTesting}
|
|
491
580
|
*/
|
|
492
581
|
var WebModelContextTesting = class {
|
|
582
|
+
/**
|
|
583
|
+
* Marker property to identify this as a polyfill implementation.
|
|
584
|
+
* Used by detectNativeAPI() to distinguish polyfill from native Chromium API.
|
|
585
|
+
* This approach works reliably even when class names are minified in production builds.
|
|
586
|
+
*
|
|
587
|
+
* @see POLYFILL_MARKER_PROPERTY - The constant defining this property name
|
|
588
|
+
* @see MayHavePolyfillMarker - The interface for type-safe detection
|
|
589
|
+
*/
|
|
590
|
+
[POLYFILL_MARKER_PROPERTY] = true;
|
|
493
591
|
toolCallHistory = [];
|
|
494
592
|
mockResponses = /* @__PURE__ */ new Map();
|
|
495
593
|
toolsChangedCallbacks = /* @__PURE__ */ new Set();
|
|
@@ -547,7 +645,7 @@ var WebModelContextTesting = class {
|
|
|
547
645
|
for (const callback of this.toolsChangedCallbacks) try {
|
|
548
646
|
callback();
|
|
549
647
|
} catch (error) {
|
|
550
|
-
|
|
648
|
+
testingLogger.error("Error in tools changed callback:", error);
|
|
551
649
|
}
|
|
552
650
|
}
|
|
553
651
|
/**
|
|
@@ -561,7 +659,6 @@ var WebModelContextTesting = class {
|
|
|
561
659
|
* @throws {Error} If the tool does not exist
|
|
562
660
|
*/
|
|
563
661
|
async executeTool(toolName, inputArgsJson) {
|
|
564
|
-
console.log(`[Model Context Testing] Executing tool: ${toolName}`);
|
|
565
662
|
let args;
|
|
566
663
|
try {
|
|
567
664
|
args = JSON.parse(inputArgsJson);
|
|
@@ -598,7 +695,6 @@ var WebModelContextTesting = class {
|
|
|
598
695
|
*/
|
|
599
696
|
registerToolsChangedCallback(callback) {
|
|
600
697
|
this.toolsChangedCallbacks.add(callback);
|
|
601
|
-
console.log("[Model Context Testing] Tools changed callback registered");
|
|
602
698
|
}
|
|
603
699
|
/**
|
|
604
700
|
* Gets all tool calls that have been recorded (polyfill extension).
|
|
@@ -613,7 +709,6 @@ var WebModelContextTesting = class {
|
|
|
613
709
|
*/
|
|
614
710
|
clearToolCalls() {
|
|
615
711
|
this.toolCallHistory = [];
|
|
616
|
-
console.log("[Model Context Testing] Tool call history cleared");
|
|
617
712
|
}
|
|
618
713
|
/**
|
|
619
714
|
* Sets a mock response for a specific tool (polyfill extension).
|
|
@@ -624,7 +719,6 @@ var WebModelContextTesting = class {
|
|
|
624
719
|
*/
|
|
625
720
|
setMockToolResponse(toolName, response) {
|
|
626
721
|
this.mockResponses.set(toolName, response);
|
|
627
|
-
console.log(`[Model Context Testing] Mock response set for tool: ${toolName}`);
|
|
628
722
|
}
|
|
629
723
|
/**
|
|
630
724
|
* Clears the mock response for a specific tool (polyfill extension).
|
|
@@ -633,14 +727,12 @@ var WebModelContextTesting = class {
|
|
|
633
727
|
*/
|
|
634
728
|
clearMockToolResponse(toolName) {
|
|
635
729
|
this.mockResponses.delete(toolName);
|
|
636
|
-
console.log(`[Model Context Testing] Mock response cleared for tool: ${toolName}`);
|
|
637
730
|
}
|
|
638
731
|
/**
|
|
639
732
|
* Clears all mock tool responses (polyfill extension).
|
|
640
733
|
*/
|
|
641
734
|
clearAllMockToolResponses() {
|
|
642
735
|
this.mockResponses.clear();
|
|
643
|
-
console.log("[Model Context Testing] All mock responses cleared");
|
|
644
736
|
}
|
|
645
737
|
/**
|
|
646
738
|
* Gets the current tools registered in the system (polyfill extension).
|
|
@@ -657,7 +749,6 @@ var WebModelContextTesting = class {
|
|
|
657
749
|
reset() {
|
|
658
750
|
this.clearToolCalls();
|
|
659
751
|
this.clearAllMockToolResponses();
|
|
660
|
-
console.log("[Model Context Testing] Testing state reset");
|
|
661
752
|
}
|
|
662
753
|
};
|
|
663
754
|
/**
|
|
@@ -763,14 +854,13 @@ var WebModelContext = class {
|
|
|
763
854
|
* @throws {Error} If a name/uri collides with existing dynamic items
|
|
764
855
|
*/
|
|
765
856
|
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
857
|
this.provideContextTools.clear();
|
|
771
858
|
this.provideContextResources.clear();
|
|
772
859
|
this.provideContextPrompts.clear();
|
|
773
860
|
for (const tool of context.tools ?? []) {
|
|
861
|
+
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.`);
|
|
862
|
+
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.`);
|
|
863
|
+
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
864
|
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
865
|
const { jsonSchema: inputJson, zodValidator: inputZod } = normalizeSchema(tool.inputSchema);
|
|
776
866
|
const normalizedOutput = tool.outputSchema ? normalizeSchema(tool.outputSchema) : null;
|
|
@@ -853,11 +943,13 @@ var WebModelContext = class {
|
|
|
853
943
|
* @throws {Error} If tool name collides with existing tools
|
|
854
944
|
*/
|
|
855
945
|
registerTool(tool) {
|
|
856
|
-
|
|
946
|
+
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.`);
|
|
947
|
+
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.`);
|
|
948
|
+
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
949
|
const now = Date.now();
|
|
858
950
|
const lastRegistration = this.toolRegistrationTimestamps.get(tool.name);
|
|
859
951
|
if (lastRegistration && now - lastRegistration < RAPID_DUPLICATE_WINDOW_MS) {
|
|
860
|
-
|
|
952
|
+
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
953
|
const existingUnregister = this.toolUnregisterFunctions.get(tool.name);
|
|
862
954
|
if (existingUnregister) return { unregister: existingUnregister };
|
|
863
955
|
}
|
|
@@ -880,10 +972,9 @@ var WebModelContext = class {
|
|
|
880
972
|
this.updateBridgeTools();
|
|
881
973
|
this.scheduleListChanged("tools");
|
|
882
974
|
const unregisterFn = () => {
|
|
883
|
-
console.log(`[Web Model Context] Unregistering tool: ${tool.name}`);
|
|
884
975
|
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
976
|
if (!this.dynamicTools.has(tool.name)) {
|
|
886
|
-
|
|
977
|
+
logger$1.warn(`Tool "${tool.name}" is not registered, ignoring unregister call`);
|
|
887
978
|
return;
|
|
888
979
|
}
|
|
889
980
|
this.dynamicTools.delete(tool.name);
|
|
@@ -904,11 +995,10 @@ var WebModelContext = class {
|
|
|
904
995
|
* @throws {Error} If resource URI collides with existing resources
|
|
905
996
|
*/
|
|
906
997
|
registerResource(resource) {
|
|
907
|
-
console.log(`[Web Model Context] Registering resource dynamically: ${resource.uri}`);
|
|
908
998
|
const now = Date.now();
|
|
909
999
|
const lastRegistration = this.resourceRegistrationTimestamps.get(resource.uri);
|
|
910
1000
|
if (lastRegistration && now - lastRegistration < RAPID_DUPLICATE_WINDOW_MS) {
|
|
911
|
-
|
|
1001
|
+
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
1002
|
const existingUnregister = this.resourceUnregisterFunctions.get(resource.uri);
|
|
913
1003
|
if (existingUnregister) return { unregister: existingUnregister };
|
|
914
1004
|
}
|
|
@@ -920,10 +1010,9 @@ var WebModelContext = class {
|
|
|
920
1010
|
this.updateBridgeResources();
|
|
921
1011
|
this.scheduleListChanged("resources");
|
|
922
1012
|
const unregisterFn = () => {
|
|
923
|
-
console.log(`[Web Model Context] Unregistering resource: ${resource.uri}`);
|
|
924
1013
|
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
1014
|
if (!this.dynamicResources.has(resource.uri)) {
|
|
926
|
-
|
|
1015
|
+
logger$1.warn(`Resource "${resource.uri}" is not registered, ignoring unregister call`);
|
|
927
1016
|
return;
|
|
928
1017
|
}
|
|
929
1018
|
this.dynamicResources.delete(resource.uri);
|
|
@@ -942,11 +1031,10 @@ var WebModelContext = class {
|
|
|
942
1031
|
* @param {string} uri - URI of the resource to unregister
|
|
943
1032
|
*/
|
|
944
1033
|
unregisterResource(uri) {
|
|
945
|
-
console.log(`[Web Model Context] Unregistering resource: ${uri}`);
|
|
946
1034
|
const inProvideContext = this.provideContextResources.has(uri);
|
|
947
1035
|
const inDynamic = this.dynamicResources.has(uri);
|
|
948
1036
|
if (!inProvideContext && !inDynamic) {
|
|
949
|
-
|
|
1037
|
+
logger$1.warn(`Resource "${uri}" is not registered, ignoring unregister call`);
|
|
950
1038
|
return;
|
|
951
1039
|
}
|
|
952
1040
|
if (inProvideContext) this.provideContextResources.delete(uri);
|
|
@@ -995,11 +1083,10 @@ var WebModelContext = class {
|
|
|
995
1083
|
* @throws {Error} If prompt name collides with existing prompts
|
|
996
1084
|
*/
|
|
997
1085
|
registerPrompt(prompt) {
|
|
998
|
-
console.log(`[Web Model Context] Registering prompt dynamically: ${prompt.name}`);
|
|
999
1086
|
const now = Date.now();
|
|
1000
1087
|
const lastRegistration = this.promptRegistrationTimestamps.get(prompt.name);
|
|
1001
1088
|
if (lastRegistration && now - lastRegistration < RAPID_DUPLICATE_WINDOW_MS) {
|
|
1002
|
-
|
|
1089
|
+
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
1090
|
const existingUnregister = this.promptUnregisterFunctions.get(prompt.name);
|
|
1004
1091
|
if (existingUnregister) return { unregister: existingUnregister };
|
|
1005
1092
|
}
|
|
@@ -1011,10 +1098,9 @@ var WebModelContext = class {
|
|
|
1011
1098
|
this.updateBridgePrompts();
|
|
1012
1099
|
this.scheduleListChanged("prompts");
|
|
1013
1100
|
const unregisterFn = () => {
|
|
1014
|
-
console.log(`[Web Model Context] Unregistering prompt: ${prompt.name}`);
|
|
1015
1101
|
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
1102
|
if (!this.dynamicPrompts.has(prompt.name)) {
|
|
1017
|
-
|
|
1103
|
+
logger$1.warn(`Prompt "${prompt.name}" is not registered, ignoring unregister call`);
|
|
1018
1104
|
return;
|
|
1019
1105
|
}
|
|
1020
1106
|
this.dynamicPrompts.delete(prompt.name);
|
|
@@ -1033,11 +1119,10 @@ var WebModelContext = class {
|
|
|
1033
1119
|
* @param {string} name - Name of the prompt to unregister
|
|
1034
1120
|
*/
|
|
1035
1121
|
unregisterPrompt(name) {
|
|
1036
|
-
console.log(`[Web Model Context] Unregistering prompt: ${name}`);
|
|
1037
1122
|
const inProvideContext = this.provideContextPrompts.has(name);
|
|
1038
1123
|
const inDynamic = this.dynamicPrompts.has(name);
|
|
1039
1124
|
if (!inProvideContext && !inDynamic) {
|
|
1040
|
-
|
|
1125
|
+
logger$1.warn(`Prompt "${name}" is not registered, ignoring unregister call`);
|
|
1041
1126
|
return;
|
|
1042
1127
|
}
|
|
1043
1128
|
if (inProvideContext) this.provideContextPrompts.delete(name);
|
|
@@ -1073,11 +1158,10 @@ var WebModelContext = class {
|
|
|
1073
1158
|
* @param {string} name - Name of the tool to unregister
|
|
1074
1159
|
*/
|
|
1075
1160
|
unregisterTool(name) {
|
|
1076
|
-
console.log(`[Web Model Context] Unregistering tool: ${name}`);
|
|
1077
1161
|
const inProvideContext = this.provideContextTools.has(name);
|
|
1078
1162
|
const inDynamic = this.dynamicTools.has(name);
|
|
1079
1163
|
if (!inProvideContext && !inDynamic) {
|
|
1080
|
-
|
|
1164
|
+
logger$1.warn(`Tool "${name}" is not registered, ignoring unregister call`);
|
|
1081
1165
|
return;
|
|
1082
1166
|
}
|
|
1083
1167
|
if (inProvideContext) this.provideContextTools.delete(name);
|
|
@@ -1094,7 +1178,6 @@ var WebModelContext = class {
|
|
|
1094
1178
|
* Removes all tools, resources, and prompts registered via provideContext() and register* methods.
|
|
1095
1179
|
*/
|
|
1096
1180
|
clearContext() {
|
|
1097
|
-
console.log("[Web Model Context] Clearing all context (tools, resources, prompts)");
|
|
1098
1181
|
this.provideContextTools.clear();
|
|
1099
1182
|
this.dynamicTools.clear();
|
|
1100
1183
|
this.toolRegistrationTimestamps.clear();
|
|
@@ -1124,7 +1207,6 @@ var WebModelContext = class {
|
|
|
1124
1207
|
this.bridge.tools.clear();
|
|
1125
1208
|
for (const [name, tool] of this.provideContextTools) this.bridge.tools.set(name, tool);
|
|
1126
1209
|
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
1210
|
}
|
|
1129
1211
|
/**
|
|
1130
1212
|
* Notifies all servers and testing callbacks that the tools list has changed.
|
|
@@ -1152,7 +1234,6 @@ var WebModelContext = class {
|
|
|
1152
1234
|
this.bridge.resources.clear();
|
|
1153
1235
|
for (const [uri, resource] of this.provideContextResources) this.bridge.resources.set(uri, resource);
|
|
1154
1236
|
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
1237
|
}
|
|
1157
1238
|
/**
|
|
1158
1239
|
* Notifies all servers that the resources list has changed.
|
|
@@ -1178,7 +1259,6 @@ var WebModelContext = class {
|
|
|
1178
1259
|
this.bridge.prompts.clear();
|
|
1179
1260
|
for (const [name, prompt] of this.provideContextPrompts) this.bridge.prompts.set(name, prompt);
|
|
1180
1261
|
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
1262
|
}
|
|
1183
1263
|
/**
|
|
1184
1264
|
* Notifies all servers that the prompts list has changed.
|
|
@@ -1221,7 +1301,7 @@ var WebModelContext = class {
|
|
|
1221
1301
|
break;
|
|
1222
1302
|
default: {
|
|
1223
1303
|
const _exhaustive = listType;
|
|
1224
|
-
|
|
1304
|
+
logger$1.error(`Unknown list type: ${_exhaustive}`);
|
|
1225
1305
|
}
|
|
1226
1306
|
}
|
|
1227
1307
|
});
|
|
@@ -1236,13 +1316,12 @@ var WebModelContext = class {
|
|
|
1236
1316
|
* @internal
|
|
1237
1317
|
*/
|
|
1238
1318
|
async readResource(uri) {
|
|
1239
|
-
console.log(`[Web Model Context] Reading resource: ${uri}`);
|
|
1240
1319
|
const staticResource = this.bridge.resources.get(uri);
|
|
1241
1320
|
if (staticResource && !staticResource.isTemplate) try {
|
|
1242
1321
|
const parsedUri = new URL(uri);
|
|
1243
1322
|
return await staticResource.read(parsedUri);
|
|
1244
1323
|
} catch (error) {
|
|
1245
|
-
|
|
1324
|
+
logger$1.error(`Error reading resource ${uri}:`, error);
|
|
1246
1325
|
throw error;
|
|
1247
1326
|
}
|
|
1248
1327
|
for (const resource of this.bridge.resources.values()) {
|
|
@@ -1252,7 +1331,7 @@ var WebModelContext = class {
|
|
|
1252
1331
|
const parsedUri = new URL(uri);
|
|
1253
1332
|
return await resource.read(parsedUri, params);
|
|
1254
1333
|
} catch (error) {
|
|
1255
|
-
|
|
1334
|
+
logger$1.error(`Error reading resource ${uri}:`, error);
|
|
1256
1335
|
throw error;
|
|
1257
1336
|
}
|
|
1258
1337
|
}
|
|
@@ -1297,20 +1376,19 @@ var WebModelContext = class {
|
|
|
1297
1376
|
* @internal
|
|
1298
1377
|
*/
|
|
1299
1378
|
async getPrompt(name, args) {
|
|
1300
|
-
console.log(`[Web Model Context] Getting prompt: ${name}`);
|
|
1301
1379
|
const prompt = this.bridge.prompts.get(name);
|
|
1302
1380
|
if (!prompt) throw new Error(`Prompt not found: ${name}`);
|
|
1303
1381
|
if (prompt.argsValidator && args) {
|
|
1304
1382
|
const validation = validateWithZod(args, prompt.argsValidator);
|
|
1305
1383
|
if (!validation.success) {
|
|
1306
|
-
|
|
1384
|
+
logger$1.error(`Argument validation failed for prompt ${name}:`, validation.error);
|
|
1307
1385
|
throw new Error(`Argument validation error for prompt "${name}":\n${validation.error}`);
|
|
1308
1386
|
}
|
|
1309
1387
|
}
|
|
1310
1388
|
try {
|
|
1311
1389
|
return await prompt.get(args ?? {});
|
|
1312
1390
|
} catch (error) {
|
|
1313
|
-
|
|
1391
|
+
logger$1.error(`Error getting prompt ${name}:`, error);
|
|
1314
1392
|
throw error;
|
|
1315
1393
|
}
|
|
1316
1394
|
}
|
|
@@ -1333,10 +1411,9 @@ var WebModelContext = class {
|
|
|
1333
1411
|
async executeTool(toolName, args) {
|
|
1334
1412
|
const tool = this.bridge.tools.get(toolName);
|
|
1335
1413
|
if (!tool) throw new Error(`Tool not found: ${toolName}`);
|
|
1336
|
-
console.log(`[Web Model Context] Validating input for tool: ${toolName}`);
|
|
1337
1414
|
const validation = validateWithZod(args, tool.inputValidator);
|
|
1338
1415
|
if (!validation.success) {
|
|
1339
|
-
|
|
1416
|
+
logger$1.error(`Input validation failed for ${toolName}:`, validation.error);
|
|
1340
1417
|
return {
|
|
1341
1418
|
content: [{
|
|
1342
1419
|
type: "text",
|
|
@@ -1349,30 +1426,24 @@ var WebModelContext = class {
|
|
|
1349
1426
|
if (this.testingAPI) this.testingAPI.recordToolCall(toolName, validatedArgs);
|
|
1350
1427
|
if (this.testingAPI?.hasMockResponse(toolName)) {
|
|
1351
1428
|
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
|
-
}
|
|
1429
|
+
if (mockResponse) return mockResponse;
|
|
1356
1430
|
}
|
|
1357
1431
|
const event = new WebToolCallEvent(toolName, validatedArgs);
|
|
1358
1432
|
this.dispatchEvent(event);
|
|
1359
1433
|
if (event.defaultPrevented && event.hasResponse()) {
|
|
1360
1434
|
const response = event.getResponse();
|
|
1361
|
-
if (response)
|
|
1362
|
-
console.log(`[Web Model Context] Tool ${toolName} handled by event listener`);
|
|
1363
|
-
return response;
|
|
1364
|
-
}
|
|
1435
|
+
if (response) return response;
|
|
1365
1436
|
}
|
|
1366
|
-
console.log(`[Web Model Context] Executing tool: ${toolName}`);
|
|
1367
1437
|
try {
|
|
1368
1438
|
const response = await tool.execute(validatedArgs);
|
|
1369
1439
|
if (tool.outputValidator && response.structuredContent) {
|
|
1370
1440
|
const outputValidation = validateWithZod(response.structuredContent, tool.outputValidator);
|
|
1371
|
-
if (!outputValidation.success)
|
|
1441
|
+
if (!outputValidation.success) logger$1.warn(`Output validation failed for ${toolName}:`, outputValidation.error);
|
|
1372
1442
|
}
|
|
1443
|
+
if (response.metadata && typeof response.metadata === "object" && "willNavigate" in response.metadata) logger$1.info(`Tool "${toolName}" will trigger navigation`, response.metadata);
|
|
1373
1444
|
return response;
|
|
1374
1445
|
} catch (error) {
|
|
1375
|
-
|
|
1446
|
+
logger$1.error(`Error executing tool ${toolName}:`, error);
|
|
1376
1447
|
return {
|
|
1377
1448
|
content: [{
|
|
1378
1449
|
type: "text",
|
|
@@ -1406,7 +1477,6 @@ var WebModelContext = class {
|
|
|
1406
1477
|
* @returns {Promise<SamplingResult>} The LLM completion result
|
|
1407
1478
|
*/
|
|
1408
1479
|
async createMessage(params) {
|
|
1409
|
-
console.log("[Web Model Context] Requesting sampling from client");
|
|
1410
1480
|
const underlyingServer = this.bridge.tabServer.server;
|
|
1411
1481
|
if (!underlyingServer?.createMessage) throw new Error("Sampling is not supported: no connected client with sampling capability");
|
|
1412
1482
|
return underlyingServer.createMessage(params);
|
|
@@ -1419,7 +1489,6 @@ var WebModelContext = class {
|
|
|
1419
1489
|
* @returns {Promise<ElicitationResult>} The user's response
|
|
1420
1490
|
*/
|
|
1421
1491
|
async elicitInput(params) {
|
|
1422
|
-
console.log("[Web Model Context] Requesting elicitation from client");
|
|
1423
1492
|
const underlyingServer = this.bridge.tabServer.server;
|
|
1424
1493
|
if (!underlyingServer?.elicitInput) throw new Error("Elicitation is not supported: no connected client with elicitation capability");
|
|
1425
1494
|
return underlyingServer.elicitInput(params);
|
|
@@ -1434,16 +1503,13 @@ var WebModelContext = class {
|
|
|
1434
1503
|
* @returns {MCPBridge} The initialized MCP bridge
|
|
1435
1504
|
*/
|
|
1436
1505
|
function initializeMCPBridge(options) {
|
|
1437
|
-
console.log("[Web Model Context] Initializing MCP bridge");
|
|
1438
1506
|
const hostname = window.location.hostname || "localhost";
|
|
1439
1507
|
const transportOptions = options?.transport;
|
|
1440
1508
|
const setupServerHandlers = (server, bridge$1) => {
|
|
1441
1509
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
1442
|
-
console.log("[MCP Bridge] Handling list_tools request");
|
|
1443
1510
|
return { tools: bridge$1.modelContext.listTools() };
|
|
1444
1511
|
});
|
|
1445
1512
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1446
|
-
console.log(`[MCP Bridge] Handling call_tool request: ${request.params.name}`);
|
|
1447
1513
|
const toolName = request.params.name;
|
|
1448
1514
|
const args = request.params.arguments || {};
|
|
1449
1515
|
try {
|
|
@@ -1454,40 +1520,35 @@ function initializeMCPBridge(options) {
|
|
|
1454
1520
|
...response.structuredContent && { structuredContent: response.structuredContent }
|
|
1455
1521
|
};
|
|
1456
1522
|
} catch (error) {
|
|
1457
|
-
|
|
1523
|
+
bridgeLogger.error(`Error calling tool ${toolName}:`, error);
|
|
1458
1524
|
throw error;
|
|
1459
1525
|
}
|
|
1460
1526
|
});
|
|
1461
1527
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
1462
|
-
console.log("[MCP Bridge] Handling list_resources request");
|
|
1463
1528
|
return { resources: bridge$1.modelContext.listResources() };
|
|
1464
1529
|
});
|
|
1465
1530
|
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
1466
|
-
console.log(`[MCP Bridge] Handling read_resource request: ${request.params.uri}`);
|
|
1467
1531
|
try {
|
|
1468
1532
|
return await bridge$1.modelContext.readResource(request.params.uri);
|
|
1469
1533
|
} catch (error) {
|
|
1470
|
-
|
|
1534
|
+
bridgeLogger.error(`Error reading resource ${request.params.uri}:`, error);
|
|
1471
1535
|
throw error;
|
|
1472
1536
|
}
|
|
1473
1537
|
});
|
|
1474
1538
|
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
1475
|
-
console.log("[MCP Bridge] Handling list_prompts request");
|
|
1476
1539
|
return { prompts: bridge$1.modelContext.listPrompts() };
|
|
1477
1540
|
});
|
|
1478
1541
|
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
1479
|
-
console.log(`[MCP Bridge] Handling get_prompt request: ${request.params.name}`);
|
|
1480
1542
|
try {
|
|
1481
1543
|
return await bridge$1.modelContext.getPrompt(request.params.name, request.params.arguments);
|
|
1482
1544
|
} catch (error) {
|
|
1483
|
-
|
|
1545
|
+
bridgeLogger.error(`Error getting prompt ${request.params.name}:`, error);
|
|
1484
1546
|
throw error;
|
|
1485
1547
|
}
|
|
1486
1548
|
});
|
|
1487
1549
|
};
|
|
1488
1550
|
const customTransport = transportOptions?.create?.();
|
|
1489
1551
|
if (customTransport) {
|
|
1490
|
-
console.log("[Web Model Context] Using custom transport");
|
|
1491
1552
|
const server = new Server({
|
|
1492
1553
|
name: hostname,
|
|
1493
1554
|
version: "1.0.0"
|
|
@@ -1507,10 +1568,8 @@ function initializeMCPBridge(options) {
|
|
|
1507
1568
|
bridge$1.modelContext = new WebModelContext(bridge$1);
|
|
1508
1569
|
setupServerHandlers(server, bridge$1);
|
|
1509
1570
|
server.connect(customTransport);
|
|
1510
|
-
console.log("[Web Model Context] MCP server connected with custom transport");
|
|
1511
1571
|
return bridge$1;
|
|
1512
1572
|
}
|
|
1513
|
-
console.log("[Web Model Context] Using dual-server mode");
|
|
1514
1573
|
const tabServerEnabled = transportOptions?.tabServer !== false;
|
|
1515
1574
|
const tabServer = new Server({
|
|
1516
1575
|
name: `${hostname}-tab`,
|
|
@@ -1537,12 +1596,10 @@ function initializeMCPBridge(options) {
|
|
|
1537
1596
|
...restTabServerOptions
|
|
1538
1597
|
});
|
|
1539
1598
|
tabServer.connect(tabTransport);
|
|
1540
|
-
console.log("[Web Model Context] Tab server connected");
|
|
1541
1599
|
}
|
|
1542
1600
|
const isInIframe = typeof window !== "undefined" && window.parent !== window;
|
|
1543
1601
|
const iframeServerConfig = transportOptions?.iframeServer;
|
|
1544
1602
|
if (iframeServerConfig !== false && (iframeServerConfig !== void 0 || isInIframe)) {
|
|
1545
|
-
console.log("[Web Model Context] Enabling iframe server");
|
|
1546
1603
|
const iframeServer = new Server({
|
|
1547
1604
|
name: `${hostname}-iframe`,
|
|
1548
1605
|
version: "1.0.0"
|
|
@@ -1559,7 +1616,6 @@ function initializeMCPBridge(options) {
|
|
|
1559
1616
|
});
|
|
1560
1617
|
iframeServer.connect(iframeTransport);
|
|
1561
1618
|
bridge.iframeServer = iframeServer;
|
|
1562
|
-
console.log("[Web Model Context] Iframe server connected");
|
|
1563
1619
|
}
|
|
1564
1620
|
return bridge;
|
|
1565
1621
|
}
|
|
@@ -1585,7 +1641,7 @@ function initializeMCPBridge(options) {
|
|
|
1585
1641
|
*/
|
|
1586
1642
|
function initializeWebModelContext(options) {
|
|
1587
1643
|
if (typeof window === "undefined") {
|
|
1588
|
-
|
|
1644
|
+
logger$1.warn("Not in browser environment, skipping initialization");
|
|
1589
1645
|
return;
|
|
1590
1646
|
}
|
|
1591
1647
|
const effectiveOptions = options ?? window.__webModelContextOptions;
|
|
@@ -1594,12 +1650,12 @@ function initializeWebModelContext(options) {
|
|
|
1594
1650
|
const nativeContext = window.navigator.modelContext;
|
|
1595
1651
|
const nativeTesting = window.navigator.modelContextTesting;
|
|
1596
1652
|
if (!nativeContext || !nativeTesting) {
|
|
1597
|
-
|
|
1653
|
+
logger$1.error("Native API detection mismatch");
|
|
1598
1654
|
return;
|
|
1599
1655
|
}
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1656
|
+
logger$1.info("✅ Native Chromium API detected");
|
|
1657
|
+
logger$1.info(" Using native implementation with MCP bridge synchronization");
|
|
1658
|
+
logger$1.info(" Native API will automatically collect tools from embedded iframes");
|
|
1603
1659
|
try {
|
|
1604
1660
|
const bridge = initializeMCPBridge(effectiveOptions);
|
|
1605
1661
|
bridge.modelContext = new NativeModelContextAdapter(bridge, nativeContext, nativeTesting);
|
|
@@ -1609,29 +1665,29 @@ function initializeWebModelContext(options) {
|
|
|
1609
1665
|
writable: false,
|
|
1610
1666
|
configurable: true
|
|
1611
1667
|
});
|
|
1612
|
-
|
|
1613
|
-
|
|
1668
|
+
logger$1.info("✅ MCP bridge synced with native API");
|
|
1669
|
+
logger$1.info(" MCP clients will receive automatic tool updates from native registry");
|
|
1614
1670
|
} catch (error) {
|
|
1615
|
-
|
|
1671
|
+
logger$1.error("Failed to initialize native adapter:", error);
|
|
1616
1672
|
throw error;
|
|
1617
1673
|
}
|
|
1618
1674
|
return;
|
|
1619
1675
|
}
|
|
1620
1676
|
if (native.hasNativeContext && !native.hasNativeTesting) {
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1677
|
+
logger$1.warn("Partial native API detected");
|
|
1678
|
+
logger$1.warn(" navigator.modelContext exists but navigator.modelContextTesting is missing");
|
|
1679
|
+
logger$1.warn(" Cannot sync with native API. Please enable experimental features:");
|
|
1680
|
+
logger$1.warn(" - Navigate to chrome://flags");
|
|
1681
|
+
logger$1.warn(" - Enable \"Experimental Web Platform Features\"");
|
|
1682
|
+
logger$1.warn(" - Or launch with: --enable-experimental-web-platform-features");
|
|
1683
|
+
logger$1.warn(" Skipping initialization to avoid conflicts");
|
|
1628
1684
|
return;
|
|
1629
1685
|
}
|
|
1630
1686
|
if (window.navigator.modelContext) {
|
|
1631
|
-
|
|
1687
|
+
logger$1.warn("window.navigator.modelContext already exists, skipping initialization");
|
|
1632
1688
|
return;
|
|
1633
1689
|
}
|
|
1634
|
-
|
|
1690
|
+
logger$1.info("Native API not detected, installing polyfill");
|
|
1635
1691
|
try {
|
|
1636
1692
|
const bridge = initializeMCPBridge(effectiveOptions);
|
|
1637
1693
|
Object.defineProperty(window.navigator, "modelContext", {
|
|
@@ -1644,12 +1700,12 @@ function initializeWebModelContext(options) {
|
|
|
1644
1700
|
writable: false,
|
|
1645
1701
|
configurable: true
|
|
1646
1702
|
});
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1703
|
+
logger$1.info("✅ window.navigator.modelContext initialized successfully");
|
|
1704
|
+
testingLogger.info("Installing polyfill");
|
|
1705
|
+
testingLogger.info(" 💡 To use the native implementation in Chromium:");
|
|
1706
|
+
testingLogger.info(" - Navigate to chrome://flags");
|
|
1707
|
+
testingLogger.info(" - Enable \"Experimental Web Platform Features\"");
|
|
1708
|
+
testingLogger.info(" - Or launch with: --enable-experimental-web-platform-features");
|
|
1653
1709
|
const testingAPI = new WebModelContextTesting(bridge);
|
|
1654
1710
|
bridge.modelContextTesting = testingAPI;
|
|
1655
1711
|
bridge.modelContext.setTestingAPI(testingAPI);
|
|
@@ -1658,9 +1714,9 @@ function initializeWebModelContext(options) {
|
|
|
1658
1714
|
writable: false,
|
|
1659
1715
|
configurable: true
|
|
1660
1716
|
});
|
|
1661
|
-
|
|
1717
|
+
testingLogger.info("✅ Polyfill installed at window.navigator.modelContextTesting");
|
|
1662
1718
|
} catch (error) {
|
|
1663
|
-
|
|
1719
|
+
logger$1.error("Failed to initialize:", error);
|
|
1664
1720
|
throw error;
|
|
1665
1721
|
}
|
|
1666
1722
|
}
|
|
@@ -1682,16 +1738,17 @@ function cleanupWebModelContext() {
|
|
|
1682
1738
|
window.__mcpBridge.tabServer.close();
|
|
1683
1739
|
if (window.__mcpBridge.iframeServer) window.__mcpBridge.iframeServer.close();
|
|
1684
1740
|
} catch (error) {
|
|
1685
|
-
|
|
1741
|
+
logger$1.warn("Error closing MCP servers:", error);
|
|
1686
1742
|
}
|
|
1687
1743
|
delete window.navigator.modelContext;
|
|
1688
1744
|
delete window.navigator.modelContextTesting;
|
|
1689
1745
|
delete window.__mcpBridge;
|
|
1690
|
-
|
|
1746
|
+
logger$1.info("Cleaned up");
|
|
1691
1747
|
}
|
|
1692
1748
|
|
|
1693
1749
|
//#endregion
|
|
1694
1750
|
//#region src/index.ts
|
|
1751
|
+
const logger = createLogger("WebModelContext");
|
|
1695
1752
|
function mergeTransportOptions(base, override) {
|
|
1696
1753
|
if (!base) return override;
|
|
1697
1754
|
if (!override) return base;
|
|
@@ -1719,7 +1776,7 @@ function parseScriptTagOptions(script) {
|
|
|
1719
1776
|
if (dataset.webmcpOptions) try {
|
|
1720
1777
|
return JSON.parse(dataset.webmcpOptions);
|
|
1721
1778
|
} catch (error) {
|
|
1722
|
-
|
|
1779
|
+
logger.error("Invalid JSON in data-webmcp-options:", error);
|
|
1723
1780
|
return;
|
|
1724
1781
|
}
|
|
1725
1782
|
const options = {};
|
|
@@ -1762,10 +1819,10 @@ if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
|
1762
1819
|
try {
|
|
1763
1820
|
if (shouldAutoInitialize) initializeWebModelContext(mergedOptions);
|
|
1764
1821
|
} catch (error) {
|
|
1765
|
-
|
|
1822
|
+
logger.error("Auto-initialization failed:", error);
|
|
1766
1823
|
}
|
|
1767
1824
|
}
|
|
1768
1825
|
|
|
1769
1826
|
//#endregion
|
|
1770
|
-
export { cleanupWebModelContext, initializeWebModelContext, zodToJsonSchema };
|
|
1827
|
+
export { cleanupWebModelContext, createLogger, initializeWebModelContext, zodToJsonSchema };
|
|
1771
1828
|
//# sourceMappingURL=index.js.map
|