@mcp-b/global 1.3.0 → 1.5.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 +33 -0
- package/dist/index.d.ts +2 -1013
- package/dist/index.d.ts.map +1 -1
- package/dist/index.iife.js +13 -9
- package/dist/index.js +246 -232
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +71 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +48 -0
- package/dist/testing.js.map +1 -0
- package/dist/types-DemXxUoc.d.ts +1036 -0
- package/dist/types-DemXxUoc.d.ts.map +1 -0
- package/package.json +10 -4
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IframeChildTransport, TabServerTransport } from "@mcp-b/transports";
|
|
2
2
|
import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, Server } from "@mcp-b/webmcp-ts-sdk";
|
|
3
|
-
import { jsonSchemaToZod } from "@
|
|
3
|
+
import { jsonSchemaToZod } from "@composio/json-schema-to-zod";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { zodToJsonSchema as zodToJsonSchema$1 } from "zod-to-json-schema";
|
|
6
6
|
|
|
@@ -89,6 +89,24 @@ function createLogger(namespace) {
|
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/tab-server-capabilities.ts
|
|
94
|
+
function getTabServerCapabilities(server) {
|
|
95
|
+
return server.server;
|
|
96
|
+
}
|
|
97
|
+
function requireCreateMessageCapability(server) {
|
|
98
|
+
const capabilities = getTabServerCapabilities(server);
|
|
99
|
+
const createMessage = capabilities?.createMessage;
|
|
100
|
+
if (!createMessage) throw new Error("Sampling is not supported: no connected client with sampling capability");
|
|
101
|
+
return createMessage.bind(capabilities);
|
|
102
|
+
}
|
|
103
|
+
function requireElicitInputCapability(server) {
|
|
104
|
+
const capabilities = getTabServerCapabilities(server);
|
|
105
|
+
const elicitInput = capabilities?.elicitInput;
|
|
106
|
+
if (!elicitInput) throw new Error("Elicitation is not supported: no connected client with elicitation capability");
|
|
107
|
+
return elicitInput.bind(capabilities);
|
|
108
|
+
}
|
|
109
|
+
|
|
92
110
|
//#endregion
|
|
93
111
|
//#region src/validation.ts
|
|
94
112
|
const logger$2 = createLogger("WebModelContext");
|
|
@@ -159,27 +177,17 @@ function validateWithZod(data, validator) {
|
|
|
159
177
|
}
|
|
160
178
|
|
|
161
179
|
//#endregion
|
|
162
|
-
//#region src/
|
|
163
|
-
const logger$1 = createLogger("WebModelContext");
|
|
180
|
+
//#region src/native-adapter.ts
|
|
164
181
|
const nativeLogger = createLogger("NativeAdapter");
|
|
165
|
-
const
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
* both detection (detectNativeAPI) and definition (WebModelContextTesting).
|
|
171
|
-
*/
|
|
172
|
-
const POLYFILL_MARKER_PROPERTY = "__isWebMCPPolyfill";
|
|
182
|
+
const testingLogger$1 = createLogger("ModelContextTesting");
|
|
183
|
+
const POLYFILL_MARKER_PROPERTY$1 = "__isWebMCPPolyfill";
|
|
184
|
+
const CONSUMER_SHIM_MARKER_PROPERTY = "__webMCPConsumerShimInstalled";
|
|
185
|
+
const CONSUMER_CALL_TOOL_SHIM_MARKER_PROPERTY = "__webMCPCallToolShimInstalled";
|
|
186
|
+
const MODEL_CONTEXT_TESTING_DEPRECATION_MESSAGE = "navigator.modelContextTesting is deprecated. Use navigator.modelContext.callTool() and addEventListener('toolschanged', ...) instead.";
|
|
173
187
|
/**
|
|
174
188
|
* Detect if the native Chromium Web Model Context API is available.
|
|
175
189
|
* Checks for both navigator.modelContext and navigator.modelContextTesting,
|
|
176
190
|
* and verifies they are native implementations (not polyfills).
|
|
177
|
-
*
|
|
178
|
-
* Detection uses a marker property (`__isWebMCPPolyfill`) on the testing API
|
|
179
|
-
* to reliably distinguish polyfills from native implementations. This approach
|
|
180
|
-
* works correctly even when class names are minified in production builds.
|
|
181
|
-
*
|
|
182
|
-
* @returns Detection result with flags for native context and testing API availability
|
|
183
191
|
*/
|
|
184
192
|
function detectNativeAPI() {
|
|
185
193
|
/* c8 ignore next 2 */
|
|
@@ -193,7 +201,7 @@ function detectNativeAPI() {
|
|
|
193
201
|
hasNativeContext: Boolean(modelContext),
|
|
194
202
|
hasNativeTesting: Boolean(modelContextTesting)
|
|
195
203
|
};
|
|
196
|
-
if (POLYFILL_MARKER_PROPERTY in modelContextTesting && modelContextTesting[POLYFILL_MARKER_PROPERTY] === true) return {
|
|
204
|
+
if (POLYFILL_MARKER_PROPERTY$1 in modelContextTesting && modelContextTesting[POLYFILL_MARKER_PROPERTY$1] === true) return {
|
|
197
205
|
hasNativeContext: false,
|
|
198
206
|
hasNativeTesting: false
|
|
199
207
|
};
|
|
@@ -203,62 +211,146 @@ function detectNativeAPI() {
|
|
|
203
211
|
};
|
|
204
212
|
}
|
|
205
213
|
/**
|
|
214
|
+
* Installs a deprecation getter for navigator.modelContextTesting.
|
|
215
|
+
* Emits a warning on first access while preserving compatibility behavior.
|
|
216
|
+
*/
|
|
217
|
+
function installDeprecatedTestingAccessor(modelContextTesting) {
|
|
218
|
+
let hasWarned = false;
|
|
219
|
+
try {
|
|
220
|
+
Object.defineProperty(window.navigator, "modelContextTesting", {
|
|
221
|
+
configurable: true,
|
|
222
|
+
enumerable: true,
|
|
223
|
+
get() {
|
|
224
|
+
if (!hasWarned) {
|
|
225
|
+
testingLogger$1.warn(MODEL_CONTEXT_TESTING_DEPRECATION_MESSAGE);
|
|
226
|
+
hasWarned = true;
|
|
227
|
+
}
|
|
228
|
+
return modelContextTesting;
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
} catch (error) {
|
|
232
|
+
testingLogger$1.warn("Failed to install modelContextTesting deprecation accessor:", error);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Dispatches a tools-changed event on a modelContext object.
|
|
237
|
+
*/
|
|
238
|
+
function dispatchToolsChangedEvent(modelContext) {
|
|
239
|
+
try {
|
|
240
|
+
modelContext.dispatchEvent(new Event("toolschanged"));
|
|
241
|
+
} catch (error) {
|
|
242
|
+
nativeLogger.warn("Failed to dispatch \"toolschanged\" event:", error);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Installs consumer methods on an existing modelContext object.
|
|
247
|
+
* This allows native producer APIs to expose consumer semantics without replacing the object.
|
|
248
|
+
*/
|
|
249
|
+
function installConsumerShim(nativeContext, callTool, options) {
|
|
250
|
+
const target = nativeContext;
|
|
251
|
+
if (target[CONSUMER_SHIM_MARKER_PROPERTY] === true) return;
|
|
252
|
+
let installedCallToolShim = false;
|
|
253
|
+
if (typeof target.callTool !== "function") try {
|
|
254
|
+
Object.defineProperty(target, "callTool", {
|
|
255
|
+
configurable: true,
|
|
256
|
+
writable: true,
|
|
257
|
+
value: callTool
|
|
258
|
+
});
|
|
259
|
+
installedCallToolShim = true;
|
|
260
|
+
} catch (error) {
|
|
261
|
+
nativeLogger.warn("Failed to install modelContext.callTool shim:", error);
|
|
262
|
+
}
|
|
263
|
+
if (!options.hasNativeTesting) {
|
|
264
|
+
const queueToolsChanged = () => {
|
|
265
|
+
if (options.onToolRegistryMutated) {
|
|
266
|
+
queueMicrotask(options.onToolRegistryMutated);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
queueMicrotask(() => dispatchToolsChangedEvent(nativeContext));
|
|
270
|
+
};
|
|
271
|
+
const wrapMethod = (methodName) => {
|
|
272
|
+
const original = target[methodName];
|
|
273
|
+
if (typeof original !== "function") return;
|
|
274
|
+
try {
|
|
275
|
+
Object.defineProperty(target, methodName, {
|
|
276
|
+
configurable: true,
|
|
277
|
+
writable: true,
|
|
278
|
+
value: (...args) => {
|
|
279
|
+
const result = original.apply(target, args);
|
|
280
|
+
if (methodName === "registerTool" && result && typeof result === "object" && "unregister" in result) {
|
|
281
|
+
const registration = result;
|
|
282
|
+
if (typeof registration.unregister === "function") {
|
|
283
|
+
const originalUnregister = registration.unregister.bind(registration);
|
|
284
|
+
registration.unregister = () => {
|
|
285
|
+
originalUnregister();
|
|
286
|
+
queueToolsChanged();
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
queueToolsChanged();
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
} catch (error) {
|
|
295
|
+
nativeLogger.warn(`Failed to wrap modelContext.${methodName} for "toolschanged":`, error);
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
wrapMethod("provideContext");
|
|
299
|
+
wrapMethod("registerTool");
|
|
300
|
+
wrapMethod("unregisterTool");
|
|
301
|
+
wrapMethod("clearContext");
|
|
302
|
+
}
|
|
303
|
+
try {
|
|
304
|
+
Object.defineProperty(target, CONSUMER_SHIM_MARKER_PROPERTY, {
|
|
305
|
+
configurable: true,
|
|
306
|
+
enumerable: false,
|
|
307
|
+
writable: false,
|
|
308
|
+
value: true
|
|
309
|
+
});
|
|
310
|
+
if (installedCallToolShim) Object.defineProperty(target, CONSUMER_CALL_TOOL_SHIM_MARKER_PROPERTY, {
|
|
311
|
+
configurable: true,
|
|
312
|
+
enumerable: false,
|
|
313
|
+
writable: false,
|
|
314
|
+
value: true
|
|
315
|
+
});
|
|
316
|
+
} catch {}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
206
319
|
* Adapter that wraps the native Chromium Web Model Context API.
|
|
207
320
|
* Synchronizes tool changes from the native API to the MCP bridge,
|
|
208
321
|
* enabling MCP clients to stay in sync with the native tool registry.
|
|
209
|
-
*
|
|
210
|
-
* Key features:
|
|
211
|
-
* - Listens to native tool changes via registerToolsChangedCallback()
|
|
212
|
-
* - Syncs native tools to MCP bridge automatically
|
|
213
|
-
* - Delegates tool execution to native API
|
|
214
|
-
* - Converts native results to MCP ToolResponse format
|
|
215
|
-
*
|
|
216
|
-
* @class NativeModelContextAdapter
|
|
217
|
-
* @implements {InternalModelContext}
|
|
218
322
|
*/
|
|
219
323
|
var NativeModelContextAdapter = class {
|
|
220
324
|
nativeContext;
|
|
221
325
|
nativeTesting;
|
|
222
326
|
bridge;
|
|
223
327
|
syncInProgress = false;
|
|
224
|
-
|
|
225
|
-
* Creates a new NativeModelContextAdapter.
|
|
226
|
-
*
|
|
227
|
-
* @param {MCPBridge} bridge - The MCP bridge instance
|
|
228
|
-
* @param {ModelContext} nativeContext - The native navigator.modelContext
|
|
229
|
-
* @param {ModelContextTesting} nativeTesting - The native navigator.modelContextTesting
|
|
230
|
-
*/
|
|
328
|
+
hasCompletedInitialToolSync = false;
|
|
231
329
|
constructor(bridge, nativeContext, nativeTesting) {
|
|
232
330
|
this.bridge = bridge;
|
|
233
331
|
this.nativeContext = nativeContext;
|
|
234
332
|
this.nativeTesting = nativeTesting;
|
|
235
|
-
this.nativeTesting.registerToolsChangedCallback(() => {
|
|
333
|
+
if (this.nativeTesting) this.nativeTesting.registerToolsChangedCallback(() => {
|
|
236
334
|
this.syncToolsFromNative();
|
|
237
335
|
});
|
|
238
336
|
this.syncToolsFromNative();
|
|
239
337
|
}
|
|
240
|
-
/**
|
|
241
|
-
* Synchronizes tools from the native API to the MCP bridge.
|
|
242
|
-
* Fetches all tools from navigator.modelContextTesting.listTools()
|
|
243
|
-
* and updates the bridge's tool registry.
|
|
244
|
-
*
|
|
245
|
-
* @private
|
|
246
|
-
*/
|
|
247
338
|
syncToolsFromNative() {
|
|
248
339
|
if (this.syncInProgress) return;
|
|
249
340
|
this.syncInProgress = true;
|
|
250
341
|
try {
|
|
251
|
-
const
|
|
342
|
+
const nativeTestingTools = this.nativeTesting?.listTools();
|
|
343
|
+
const nativeTools = this.nativeContext.listTools();
|
|
252
344
|
this.bridge.tools.clear();
|
|
253
|
-
|
|
254
|
-
|
|
345
|
+
const sourceTools = nativeTestingTools ?? nativeTools;
|
|
346
|
+
for (const toolInfo of sourceTools) try {
|
|
347
|
+
const inputSchema = nativeTestingTools && "inputSchema" in toolInfo && typeof toolInfo.inputSchema === "string" ? JSON.parse(toolInfo.inputSchema) : toolInfo.inputSchema ?? { type: "object" };
|
|
255
348
|
const validatedTool = {
|
|
256
349
|
name: toolInfo.name,
|
|
257
350
|
description: toolInfo.description,
|
|
258
351
|
inputSchema,
|
|
259
352
|
execute: async (args) => {
|
|
260
|
-
|
|
261
|
-
return this.convertToToolResponse(result);
|
|
353
|
+
return this.executeTool(toolInfo.name, args);
|
|
262
354
|
},
|
|
263
355
|
inputValidator: jsonSchemaToZod$1(inputSchema)
|
|
264
356
|
};
|
|
@@ -267,19 +359,39 @@ var NativeModelContextAdapter = class {
|
|
|
267
359
|
nativeLogger.error(`Failed to sync tool "${toolInfo.name}":`, error);
|
|
268
360
|
}
|
|
269
361
|
this.notifyMCPServers();
|
|
362
|
+
if (this.hasCompletedInitialToolSync) dispatchToolsChangedEvent(this.nativeContext);
|
|
363
|
+
else this.hasCompletedInitialToolSync = true;
|
|
270
364
|
} finally {
|
|
271
365
|
this.syncInProgress = false;
|
|
272
366
|
}
|
|
273
367
|
}
|
|
274
368
|
/**
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
* which need to be wrapped in the MCP CallToolResult format.
|
|
278
|
-
*
|
|
279
|
-
* @param {unknown} result - The result from native executeTool()
|
|
280
|
-
* @returns {ToolResponse} Formatted MCP ToolResponse
|
|
281
|
-
* @private
|
|
369
|
+
* Public refresh hook for consumer shims that detect native tool mutations
|
|
370
|
+
* without modelContextTesting callbacks.
|
|
282
371
|
*/
|
|
372
|
+
refreshToolsFromNative() {
|
|
373
|
+
this.syncToolsFromNative();
|
|
374
|
+
}
|
|
375
|
+
async executeToolViaNative(toolName, args) {
|
|
376
|
+
const nativeWithConsumer = this.nativeContext;
|
|
377
|
+
if (typeof nativeWithConsumer.callTool === "function" && nativeWithConsumer[CONSUMER_CALL_TOOL_SHIM_MARKER_PROPERTY] !== true) {
|
|
378
|
+
const result = await nativeWithConsumer.callTool({
|
|
379
|
+
name: toolName,
|
|
380
|
+
arguments: args
|
|
381
|
+
});
|
|
382
|
+
if (result && typeof result === "object" && "content" in result && Array.isArray(result.content)) return result;
|
|
383
|
+
return this.convertToToolResponse(result);
|
|
384
|
+
}
|
|
385
|
+
if (this.nativeTesting) {
|
|
386
|
+
const result = await this.nativeTesting.executeTool(toolName, JSON.stringify(args));
|
|
387
|
+
return this.convertToToolResponse(result);
|
|
388
|
+
}
|
|
389
|
+
if (typeof nativeWithConsumer.executeTool === "function") {
|
|
390
|
+
const result = await nativeWithConsumer.executeTool(toolName, args);
|
|
391
|
+
return this.convertToToolResponse(result);
|
|
392
|
+
}
|
|
393
|
+
throw new Error("[Native Adapter] Tool execution is not supported by this native implementation");
|
|
394
|
+
}
|
|
283
395
|
convertToToolResponse(result) {
|
|
284
396
|
if (typeof result === "string") return { content: [{
|
|
285
397
|
type: "text",
|
|
@@ -301,11 +413,6 @@ var NativeModelContextAdapter = class {
|
|
|
301
413
|
text: String(result)
|
|
302
414
|
}] };
|
|
303
415
|
}
|
|
304
|
-
/**
|
|
305
|
-
* Notifies all connected MCP servers that the tools list has changed.
|
|
306
|
-
*
|
|
307
|
-
* @private
|
|
308
|
-
*/
|
|
309
416
|
notifyMCPServers() {
|
|
310
417
|
if (this.bridge.tabServer?.notification) this.bridge.tabServer.notification({
|
|
311
418
|
method: "notifications/tools/list_changed",
|
|
@@ -316,13 +423,6 @@ var NativeModelContextAdapter = class {
|
|
|
316
423
|
params: {}
|
|
317
424
|
});
|
|
318
425
|
}
|
|
319
|
-
/**
|
|
320
|
-
* Provides context (tools) to AI models via the native API.
|
|
321
|
-
* Converts Zod schemas to JSON Schema before passing to native API.
|
|
322
|
-
* Tool change callback will fire and trigger sync automatically.
|
|
323
|
-
*
|
|
324
|
-
* @param {ModelContextInput} context - Context containing tools to register
|
|
325
|
-
*/
|
|
326
426
|
provideContext(context) {
|
|
327
427
|
const { tools,...rest } = context;
|
|
328
428
|
const normalizedContext = { ...rest };
|
|
@@ -332,14 +432,6 @@ var NativeModelContextAdapter = class {
|
|
|
332
432
|
}));
|
|
333
433
|
this.nativeContext.provideContext(normalizedContext);
|
|
334
434
|
}
|
|
335
|
-
/**
|
|
336
|
-
* Registers a single tool dynamically via the native API.
|
|
337
|
-
* Converts Zod schemas to JSON Schema before passing to native API.
|
|
338
|
-
* Tool change callback will fire and trigger sync automatically.
|
|
339
|
-
*
|
|
340
|
-
* @param {ToolDescriptor} tool - The tool descriptor to register
|
|
341
|
-
* @returns {{unregister: () => void}} Object with unregister function
|
|
342
|
-
*/
|
|
343
435
|
registerTool(tool) {
|
|
344
436
|
const normalizedTool = {
|
|
345
437
|
...tool,
|
|
@@ -347,37 +439,15 @@ var NativeModelContextAdapter = class {
|
|
|
347
439
|
};
|
|
348
440
|
return this.nativeContext.registerTool(normalizedTool);
|
|
349
441
|
}
|
|
350
|
-
/**
|
|
351
|
-
* Unregisters a tool by name via the native API.
|
|
352
|
-
* Delegates to navigator.modelContext.unregisterTool().
|
|
353
|
-
*
|
|
354
|
-
* @param {string} name - Name of the tool to unregister
|
|
355
|
-
*/
|
|
356
442
|
unregisterTool(name) {
|
|
357
443
|
this.nativeContext.unregisterTool(name);
|
|
358
444
|
}
|
|
359
|
-
/**
|
|
360
|
-
* Clears all registered tools via the native API.
|
|
361
|
-
* Delegates to navigator.modelContext.clearContext().
|
|
362
|
-
*/
|
|
363
445
|
clearContext() {
|
|
364
446
|
this.nativeContext.clearContext();
|
|
365
447
|
}
|
|
366
|
-
/**
|
|
367
|
-
* Executes a tool via the native API.
|
|
368
|
-
* Delegates to navigator.modelContextTesting.executeTool() with JSON string args.
|
|
369
|
-
* Note: skipValidation option is ignored - native API handles its own validation.
|
|
370
|
-
*
|
|
371
|
-
* @param {string} toolName - Name of the tool to execute
|
|
372
|
-
* @param {Record<string, unknown>} args - Arguments to pass to the tool
|
|
373
|
-
* @param {Object} [_options] - Execution options (ignored for native adapter)
|
|
374
|
-
* @returns {Promise<ToolResponse>} The tool's response in MCP format
|
|
375
|
-
* @internal
|
|
376
|
-
*/
|
|
377
448
|
async executeTool(toolName, args, _options) {
|
|
378
449
|
try {
|
|
379
|
-
|
|
380
|
-
return this.convertToToolResponse(result);
|
|
450
|
+
return await this.executeToolViaNative(toolName, args);
|
|
381
451
|
} catch (error) {
|
|
382
452
|
nativeLogger.error(`Error executing tool "${toolName}":`, error);
|
|
383
453
|
return {
|
|
@@ -389,12 +459,6 @@ var NativeModelContextAdapter = class {
|
|
|
389
459
|
};
|
|
390
460
|
}
|
|
391
461
|
}
|
|
392
|
-
/**
|
|
393
|
-
* Lists all registered tools from the MCP bridge.
|
|
394
|
-
* Returns tools synced from the native API.
|
|
395
|
-
*
|
|
396
|
-
* @returns {Array<{name: string, description: string, inputSchema: InputSchema}>} Array of tool descriptors
|
|
397
|
-
*/
|
|
398
462
|
listTools() {
|
|
399
463
|
return Array.from(this.bridge.tools.values()).map((tool) => ({
|
|
400
464
|
name: tool.name,
|
|
@@ -404,128 +468,76 @@ var NativeModelContextAdapter = class {
|
|
|
404
468
|
...tool.annotations && { annotations: tool.annotations }
|
|
405
469
|
}));
|
|
406
470
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
471
|
+
async callTool(params) {
|
|
472
|
+
if (!params?.name || typeof params.name !== "string") throw new Error("Tool name is required");
|
|
473
|
+
if (!this.bridge.tools.has(params.name)) throw new Error(`Tool not found: ${params.name}`);
|
|
474
|
+
return this.executeTool(params.name, params.arguments ?? {});
|
|
475
|
+
}
|
|
412
476
|
registerResource(_resource) {
|
|
413
477
|
nativeLogger.warn("registerResource is not supported by native API");
|
|
414
478
|
return { unregister: () => {} };
|
|
415
479
|
}
|
|
416
|
-
/**
|
|
417
|
-
* Unregisters a resource by URI.
|
|
418
|
-
* Note: Native Chromium API does not yet support resources.
|
|
419
|
-
*/
|
|
420
480
|
unregisterResource(_uri) {
|
|
421
481
|
nativeLogger.warn("unregisterResource is not supported by native API");
|
|
422
482
|
}
|
|
423
|
-
/**
|
|
424
|
-
* Lists all registered resources.
|
|
425
|
-
* Note: Native Chromium API does not yet support resources.
|
|
426
|
-
*/
|
|
427
483
|
listResources() {
|
|
428
484
|
return [];
|
|
429
485
|
}
|
|
430
|
-
/**
|
|
431
|
-
* Lists all resource templates.
|
|
432
|
-
* Note: Native Chromium API does not yet support resources.
|
|
433
|
-
*/
|
|
434
486
|
listResourceTemplates() {
|
|
435
487
|
return [];
|
|
436
488
|
}
|
|
437
|
-
/**
|
|
438
|
-
* Reads a resource by URI.
|
|
439
|
-
* Note: Native Chromium API does not yet support resources.
|
|
440
|
-
* @internal
|
|
441
|
-
*/
|
|
442
489
|
async readResource(_uri) {
|
|
443
490
|
throw new Error("[Native Adapter] readResource is not supported by native API");
|
|
444
491
|
}
|
|
445
|
-
/**
|
|
446
|
-
* Registers a prompt dynamically.
|
|
447
|
-
* Note: Native Chromium API does not yet support prompts.
|
|
448
|
-
* This is a polyfill-only feature.
|
|
449
|
-
*/
|
|
450
492
|
registerPrompt(_prompt) {
|
|
451
493
|
nativeLogger.warn("registerPrompt is not supported by native API");
|
|
452
494
|
return { unregister: () => {} };
|
|
453
495
|
}
|
|
454
|
-
/**
|
|
455
|
-
* Unregisters a prompt by name.
|
|
456
|
-
* Note: Native Chromium API does not yet support prompts.
|
|
457
|
-
*/
|
|
458
496
|
unregisterPrompt(_name) {
|
|
459
497
|
nativeLogger.warn("unregisterPrompt is not supported by native API");
|
|
460
498
|
}
|
|
461
|
-
/**
|
|
462
|
-
* Lists all registered prompts.
|
|
463
|
-
* Note: Native Chromium API does not yet support prompts.
|
|
464
|
-
*/
|
|
465
499
|
listPrompts() {
|
|
466
500
|
return [];
|
|
467
501
|
}
|
|
468
|
-
/**
|
|
469
|
-
* Gets a prompt with arguments.
|
|
470
|
-
* Note: Native Chromium API does not yet support prompts.
|
|
471
|
-
* @internal
|
|
472
|
-
*/
|
|
473
502
|
async getPrompt(_name, _args) {
|
|
474
503
|
throw new Error("[Native Adapter] getPrompt is not supported by native API");
|
|
475
504
|
}
|
|
476
|
-
/**
|
|
477
|
-
* Adds an event listener for tool call events.
|
|
478
|
-
* Delegates to the native API's addEventListener.
|
|
479
|
-
*
|
|
480
|
-
* @param {'toolcall'} type - Event type
|
|
481
|
-
* @param {(event: ToolCallEvent) => void | Promise<void>} listener - Event handler
|
|
482
|
-
* @param {boolean | AddEventListenerOptions} [options] - Event listener options
|
|
483
|
-
*/
|
|
484
505
|
addEventListener(type, listener, options) {
|
|
485
|
-
|
|
506
|
+
if (type === "toolcall") {
|
|
507
|
+
this.nativeContext.addEventListener("toolcall", listener, options);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
this.nativeContext.addEventListener("toolschanged", listener, options);
|
|
486
511
|
}
|
|
487
|
-
/**
|
|
488
|
-
* Removes an event listener for tool call events.
|
|
489
|
-
* Delegates to the native API's removeEventListener.
|
|
490
|
-
*
|
|
491
|
-
* @param {'toolcall'} type - Event type
|
|
492
|
-
* @param {(event: ToolCallEvent) => void | Promise<void>} listener - Event handler
|
|
493
|
-
* @param {boolean | EventListenerOptions} [options] - Event listener options
|
|
494
|
-
*/
|
|
495
512
|
removeEventListener(type, listener, options) {
|
|
496
|
-
|
|
513
|
+
if (type === "toolcall") {
|
|
514
|
+
this.nativeContext.removeEventListener("toolcall", listener, options);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
this.nativeContext.removeEventListener("toolschanged", listener, options);
|
|
497
518
|
}
|
|
498
|
-
/**
|
|
499
|
-
* Dispatches a tool call event.
|
|
500
|
-
* Delegates to the native API's dispatchEvent.
|
|
501
|
-
*
|
|
502
|
-
* @param {Event} event - The event to dispatch
|
|
503
|
-
* @returns {boolean} False if event was cancelled, true otherwise
|
|
504
|
-
*/
|
|
505
519
|
dispatchEvent(event) {
|
|
506
520
|
return this.nativeContext.dispatchEvent(event);
|
|
507
521
|
}
|
|
508
|
-
/**
|
|
509
|
-
* Request an LLM completion from the connected client.
|
|
510
|
-
* Note: Native Chromium API does not yet support sampling.
|
|
511
|
-
* This is handled by the polyfill.
|
|
512
|
-
*/
|
|
513
522
|
async createMessage(params) {
|
|
514
|
-
|
|
515
|
-
if (!underlyingServer?.createMessage) throw new Error("Sampling is not supported: no connected client with sampling capability");
|
|
516
|
-
return underlyingServer.createMessage(params);
|
|
523
|
+
return requireCreateMessageCapability(this.bridge.tabServer)(params);
|
|
517
524
|
}
|
|
518
|
-
/**
|
|
519
|
-
* Request user input from the connected client.
|
|
520
|
-
* Note: Native Chromium API does not yet support elicitation.
|
|
521
|
-
* This is handled by the polyfill.
|
|
522
|
-
*/
|
|
523
525
|
async elicitInput(params) {
|
|
524
|
-
|
|
525
|
-
if (!underlyingServer?.elicitInput) throw new Error("Elicitation is not supported: no connected client with elicitation capability");
|
|
526
|
-
return underlyingServer.elicitInput(params);
|
|
526
|
+
return requireElicitInputCapability(this.bridge.tabServer)(params);
|
|
527
527
|
}
|
|
528
528
|
};
|
|
529
|
+
|
|
530
|
+
//#endregion
|
|
531
|
+
//#region src/global.ts
|
|
532
|
+
const logger$1 = createLogger("WebModelContext");
|
|
533
|
+
const bridgeLogger = createLogger("MCPBridge");
|
|
534
|
+
const testingLogger = createLogger("ModelContextTesting");
|
|
535
|
+
/**
|
|
536
|
+
* Marker property name used to identify polyfill implementations.
|
|
537
|
+
* This constant ensures single source of truth for the marker used in
|
|
538
|
+
* both detection (detectNativeAPI) and definition (WebModelContextTesting).
|
|
539
|
+
*/
|
|
540
|
+
const POLYFILL_MARKER_PROPERTY = "__isWebMCPPolyfill";
|
|
529
541
|
/**
|
|
530
542
|
* ToolCallEvent implementation for the Web Model Context API.
|
|
531
543
|
* Represents an event fired when a tool is called, allowing event listeners
|
|
@@ -599,7 +611,7 @@ var WebModelContextTesting = class {
|
|
|
599
611
|
* This approach works reliably even when class names are minified in production builds.
|
|
600
612
|
*
|
|
601
613
|
* @see POLYFILL_MARKER_PROPERTY - The constant defining this property name
|
|
602
|
-
* @see
|
|
614
|
+
* @see detectNativeAPI in native-adapter.ts - native/polyfill detection logic
|
|
603
615
|
*/
|
|
604
616
|
[POLYFILL_MARKER_PROPERTY] = true;
|
|
605
617
|
toolCallHistory = [];
|
|
@@ -830,23 +842,9 @@ var WebModelContext = class {
|
|
|
830
842
|
setTestingAPI(testingAPI) {
|
|
831
843
|
this.testingAPI = testingAPI;
|
|
832
844
|
}
|
|
833
|
-
/**
|
|
834
|
-
* Adds an event listener for tool call events.
|
|
835
|
-
*
|
|
836
|
-
* @param {'toolcall'} type - Event type (only 'toolcall' is supported)
|
|
837
|
-
* @param {(event: ToolCallEvent) => void | Promise<void>} listener - Event handler function
|
|
838
|
-
* @param {boolean | AddEventListenerOptions} [options] - Event listener options
|
|
839
|
-
*/
|
|
840
845
|
addEventListener(type, listener, options) {
|
|
841
846
|
this.eventTarget.addEventListener(type, listener, options);
|
|
842
847
|
}
|
|
843
|
-
/**
|
|
844
|
-
* Removes an event listener for tool call events.
|
|
845
|
-
*
|
|
846
|
-
* @param {'toolcall'} type - Event type (only 'toolcall' is supported)
|
|
847
|
-
* @param {(event: ToolCallEvent) => void | Promise<void>} listener - Event handler function
|
|
848
|
-
* @param {boolean | EventListenerOptions} [options] - Event listener options
|
|
849
|
-
*/
|
|
850
848
|
removeEventListener(type, listener, options) {
|
|
851
849
|
this.eventTarget.removeEventListener(type, listener, options);
|
|
852
850
|
}
|
|
@@ -916,7 +914,7 @@ var WebModelContext = class {
|
|
|
916
914
|
const templateParams = [];
|
|
917
915
|
for (const match of resource.uri.matchAll(templateParamRegex)) {
|
|
918
916
|
const paramName = match[1];
|
|
919
|
-
templateParams.push(paramName);
|
|
917
|
+
if (typeof paramName === "string") templateParams.push(paramName);
|
|
920
918
|
}
|
|
921
919
|
return {
|
|
922
920
|
uri: resource.uri,
|
|
@@ -1238,6 +1236,7 @@ var WebModelContext = class {
|
|
|
1238
1236
|
params: {}
|
|
1239
1237
|
});
|
|
1240
1238
|
if (this.testingAPI && "notifyToolsChanged" in this.testingAPI) this.testingAPI.notifyToolsChanged();
|
|
1239
|
+
this.dispatchEvent(new Event("toolschanged"));
|
|
1241
1240
|
}
|
|
1242
1241
|
/**
|
|
1243
1242
|
* Updates the bridge resources map with merged resources from both buckets.
|
|
@@ -1387,7 +1386,9 @@ var WebModelContext = class {
|
|
|
1387
1386
|
const params = {};
|
|
1388
1387
|
for (let i = 0; i < paramNames.length; i++) {
|
|
1389
1388
|
const paramName = paramNames[i];
|
|
1390
|
-
|
|
1389
|
+
const paramValue = match[i + 1];
|
|
1390
|
+
if (typeof paramName !== "string" || typeof paramValue !== "string") continue;
|
|
1391
|
+
params[paramName] = paramValue;
|
|
1391
1392
|
}
|
|
1392
1393
|
return params;
|
|
1393
1394
|
}
|
|
@@ -1501,6 +1502,15 @@ var WebModelContext = class {
|
|
|
1501
1502
|
}));
|
|
1502
1503
|
}
|
|
1503
1504
|
/**
|
|
1505
|
+
* Executes a registered tool using MCP-style params.
|
|
1506
|
+
* Throws for missing tools and returns MCP-shaped errors for execution/validation failures.
|
|
1507
|
+
*/
|
|
1508
|
+
async callTool(params) {
|
|
1509
|
+
if (!params?.name || typeof params.name !== "string") throw new Error("Tool name is required");
|
|
1510
|
+
if (!this.bridge.tools.has(params.name)) throw new Error(`Tool not found: ${params.name}`);
|
|
1511
|
+
return this.executeTool(params.name, params.arguments ?? {});
|
|
1512
|
+
}
|
|
1513
|
+
/**
|
|
1504
1514
|
* Request an LLM completion from the connected client.
|
|
1505
1515
|
* This sends a sampling request to the connected MCP client.
|
|
1506
1516
|
*
|
|
@@ -1508,9 +1518,7 @@ var WebModelContext = class {
|
|
|
1508
1518
|
* @returns {Promise<SamplingResult>} The LLM completion result
|
|
1509
1519
|
*/
|
|
1510
1520
|
async createMessage(params) {
|
|
1511
|
-
|
|
1512
|
-
if (!underlyingServer?.createMessage) throw new Error("Sampling is not supported: no connected client with sampling capability");
|
|
1513
|
-
return underlyingServer.createMessage(params);
|
|
1521
|
+
return requireCreateMessageCapability(this.bridge.tabServer)(params);
|
|
1514
1522
|
}
|
|
1515
1523
|
/**
|
|
1516
1524
|
* Request user input from the connected client.
|
|
@@ -1520,9 +1528,7 @@ var WebModelContext = class {
|
|
|
1520
1528
|
* @returns {Promise<ElicitationResult>} The user's response
|
|
1521
1529
|
*/
|
|
1522
1530
|
async elicitInput(params) {
|
|
1523
|
-
|
|
1524
|
-
if (!underlyingServer?.elicitInput) throw new Error("Elicitation is not supported: no connected client with elicitation capability");
|
|
1525
|
-
return underlyingServer.elicitInput(params);
|
|
1531
|
+
return requireElicitInputCapability(this.bridge.tabServer)(params);
|
|
1526
1532
|
}
|
|
1527
1533
|
};
|
|
1528
1534
|
/**
|
|
@@ -1678,43 +1684,44 @@ function initializeWebModelContext(options) {
|
|
|
1678
1684
|
}
|
|
1679
1685
|
const effectiveOptions = options ?? window.__webModelContextOptions;
|
|
1680
1686
|
const native = detectNativeAPI();
|
|
1681
|
-
if (native.hasNativeContext
|
|
1687
|
+
if (native.hasNativeContext) {
|
|
1682
1688
|
const nativeContext = window.navigator.modelContext;
|
|
1683
|
-
const nativeTesting = window.navigator.modelContextTesting;
|
|
1684
|
-
if (!nativeContext
|
|
1689
|
+
const nativeTesting = native.hasNativeTesting ? window.navigator.modelContextTesting : void 0;
|
|
1690
|
+
if (!nativeContext) {
|
|
1685
1691
|
logger$1.error("Native API detection mismatch");
|
|
1686
1692
|
return;
|
|
1687
1693
|
}
|
|
1688
1694
|
logger$1.info("✅ Native Chromium API detected");
|
|
1689
|
-
|
|
1690
|
-
|
|
1695
|
+
if (nativeTesting) {
|
|
1696
|
+
logger$1.info(" Using native implementation with MCP bridge synchronization");
|
|
1697
|
+
logger$1.info(" Native API will automatically collect tools from embedded iframes");
|
|
1698
|
+
} else logger$1.info(" Native modelContextTesting not detected; installing consumer shim on modelContext");
|
|
1691
1699
|
try {
|
|
1692
1700
|
const bridge = initializeMCPBridge(effectiveOptions);
|
|
1693
|
-
|
|
1694
|
-
bridge.
|
|
1701
|
+
const adapter = new NativeModelContextAdapter(bridge, nativeContext, nativeTesting);
|
|
1702
|
+
bridge.modelContext = adapter;
|
|
1703
|
+
if (nativeTesting) {
|
|
1704
|
+
bridge.modelContextTesting = nativeTesting;
|
|
1705
|
+
installDeprecatedTestingAccessor(nativeTesting);
|
|
1706
|
+
}
|
|
1707
|
+
installConsumerShim(nativeContext, (params) => adapter.callTool(params), {
|
|
1708
|
+
hasNativeTesting: Boolean(nativeTesting),
|
|
1709
|
+
...!nativeTesting && { onToolRegistryMutated: () => adapter.refreshToolsFromNative() }
|
|
1710
|
+
});
|
|
1695
1711
|
Object.defineProperty(window, "__mcpBridge", {
|
|
1696
1712
|
value: bridge,
|
|
1697
1713
|
writable: false,
|
|
1698
1714
|
configurable: true
|
|
1699
1715
|
});
|
|
1700
1716
|
logger$1.info("✅ MCP bridge synced with native API");
|
|
1701
|
-
logger$1.info(" MCP clients will receive automatic tool updates from native registry");
|
|
1717
|
+
if (nativeTesting) logger$1.info(" MCP clients will receive automatic tool updates from native registry");
|
|
1718
|
+
else logger$1.warn(" Native testing callbacks are unavailable; tool list change events are best-effort.");
|
|
1702
1719
|
} catch (error) {
|
|
1703
1720
|
logger$1.error("Failed to initialize native adapter:", error);
|
|
1704
1721
|
throw error;
|
|
1705
1722
|
}
|
|
1706
1723
|
return;
|
|
1707
1724
|
}
|
|
1708
|
-
if (native.hasNativeContext && !native.hasNativeTesting) {
|
|
1709
|
-
logger$1.warn("Partial native API detected");
|
|
1710
|
-
logger$1.warn(" navigator.modelContext exists but navigator.modelContextTesting is missing");
|
|
1711
|
-
logger$1.warn(" Cannot sync with native API. Please enable experimental features:");
|
|
1712
|
-
logger$1.warn(" - Navigate to chrome://flags");
|
|
1713
|
-
logger$1.warn(" - Enable \"Experimental Web Platform Features\"");
|
|
1714
|
-
logger$1.warn(" - Or launch with: --enable-experimental-web-platform-features");
|
|
1715
|
-
logger$1.warn(" Skipping initialization to avoid conflicts");
|
|
1716
|
-
return;
|
|
1717
|
-
}
|
|
1718
1725
|
if (window.navigator.modelContext) {
|
|
1719
1726
|
logger$1.warn("window.navigator.modelContext already exists, skipping initialization");
|
|
1720
1727
|
return;
|
|
@@ -1741,11 +1748,7 @@ function initializeWebModelContext(options) {
|
|
|
1741
1748
|
const testingAPI = new WebModelContextTesting(bridge);
|
|
1742
1749
|
bridge.modelContextTesting = testingAPI;
|
|
1743
1750
|
bridge.modelContext.setTestingAPI(testingAPI);
|
|
1744
|
-
|
|
1745
|
-
value: testingAPI,
|
|
1746
|
-
writable: false,
|
|
1747
|
-
configurable: true
|
|
1748
|
-
});
|
|
1751
|
+
installDeprecatedTestingAccessor(testingAPI);
|
|
1749
1752
|
testingLogger.info("✅ Polyfill installed at window.navigator.modelContextTesting");
|
|
1750
1753
|
} catch (error) {
|
|
1751
1754
|
logger$1.error("Failed to initialize:", error);
|
|
@@ -1773,8 +1776,19 @@ function cleanupWebModelContext() {
|
|
|
1773
1776
|
} catch (error) {
|
|
1774
1777
|
logger$1.warn("Error closing MCP servers:", error);
|
|
1775
1778
|
}
|
|
1776
|
-
|
|
1777
|
-
|
|
1779
|
+
const safeDeleteNavigatorProperty = (propertyName) => {
|
|
1780
|
+
if (Object.getOwnPropertyDescriptor(window.navigator, propertyName)?.configurable === false) {
|
|
1781
|
+
logger$1.debug(`Skipping cleanup for navigator.${propertyName} because property is non-configurable`);
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1784
|
+
try {
|
|
1785
|
+
delete window.navigator[propertyName];
|
|
1786
|
+
} catch (error) {
|
|
1787
|
+
logger$1.warn(`Failed to remove navigator.${propertyName} during cleanup:`, error);
|
|
1788
|
+
}
|
|
1789
|
+
};
|
|
1790
|
+
safeDeleteNavigatorProperty("modelContext");
|
|
1791
|
+
safeDeleteNavigatorProperty("modelContextTesting");
|
|
1778
1792
|
delete window.__mcpBridge;
|
|
1779
1793
|
logger$1.info("Cleaned up");
|
|
1780
1794
|
}
|