@mcp-b/global 1.2.2-beta.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +353 -0
- package/dist/index.d.ts +10 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.iife.js +10 -8
- package/dist/index.js +107 -74
- package/dist/index.js.map +1 -1
- package/package.json +21 -6
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
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 {
|
|
3
|
+
import { jsonSchemaToZod } from "@n8n/json-schema-to-zod";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { zodToJsonSchema as zodToJsonSchema$1 } from "zod-to-json-schema";
|
|
4
6
|
|
|
5
7
|
//#region src/logger.ts
|
|
6
8
|
/**
|
|
@@ -90,71 +92,70 @@ function createLogger(namespace) {
|
|
|
90
92
|
//#endregion
|
|
91
93
|
//#region src/validation.ts
|
|
92
94
|
const logger$2 = createLogger("WebModelContext");
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
96
|
+
const stripSchemaMeta = (schema) => {
|
|
97
|
+
const { $schema: _,...rest } = schema;
|
|
98
|
+
return rest;
|
|
99
|
+
};
|
|
100
|
+
const isOptionalSchema = (schema) => {
|
|
101
|
+
const typeName = schema._def?.typeName;
|
|
102
|
+
return typeName === "ZodOptional" || typeName === "ZodDefault";
|
|
103
|
+
};
|
|
99
104
|
function isZodSchema(schema) {
|
|
100
|
-
if (
|
|
105
|
+
if (!isRecord(schema)) return false;
|
|
101
106
|
if ("type" in schema && typeof schema.type === "string") return false;
|
|
102
107
|
const values = Object.values(schema);
|
|
103
108
|
if (values.length === 0) return false;
|
|
104
|
-
return values.some((
|
|
109
|
+
return values.some((v) => isRecord(v) && "_def" in v);
|
|
105
110
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
function zodToJsonSchema(schema) {
|
|
112
|
+
const properties = {};
|
|
113
|
+
const required = [];
|
|
114
|
+
for (const [key, zodSchema] of Object.entries(schema)) {
|
|
115
|
+
properties[key] = stripSchemaMeta(zodToJsonSchema$1(zodSchema, {
|
|
116
|
+
strictUnions: true,
|
|
117
|
+
$refStrategy: "none"
|
|
118
|
+
}));
|
|
119
|
+
if (!isOptionalSchema(zodSchema)) required.push(key);
|
|
120
|
+
}
|
|
121
|
+
const result = {
|
|
122
|
+
type: "object",
|
|
123
|
+
properties
|
|
124
|
+
};
|
|
125
|
+
if (required.length > 0) result.required = required;
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
function jsonSchemaToZod$1(jsonSchema) {
|
|
111
129
|
try {
|
|
112
|
-
return
|
|
130
|
+
return jsonSchemaToZod(jsonSchema);
|
|
113
131
|
} catch (error) {
|
|
114
|
-
logger$2.warn("
|
|
132
|
+
logger$2.warn("jsonSchemaToZod failed:", error);
|
|
115
133
|
return z.object({}).passthrough();
|
|
116
134
|
}
|
|
117
135
|
}
|
|
118
|
-
/**
|
|
119
|
-
* Convert Zod schema object to JSON Schema
|
|
120
|
-
* Uses Zod 4's native z.toJSONSchema() for conversion
|
|
121
|
-
*
|
|
122
|
-
* @param schema - Record of Zod type definitions (e.g., { name: z.string(), age: z.number() })
|
|
123
|
-
* @returns JSON Schema object compatible with MCP InputSchema
|
|
124
|
-
*/
|
|
125
|
-
function zodToJsonSchema(schema) {
|
|
126
|
-
const zodObject = z.object(schema);
|
|
127
|
-
const { $schema: _,...rest } = z.toJSONSchema(zodObject);
|
|
128
|
-
return rest;
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Normalize a schema to both JSON Schema and Zod formats
|
|
132
|
-
* Detects which format is provided and converts to the other
|
|
133
|
-
*/
|
|
134
136
|
function normalizeSchema(schema) {
|
|
135
|
-
if (isZodSchema(schema))
|
|
136
|
-
jsonSchema
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
137
|
+
if (isZodSchema(schema)) {
|
|
138
|
+
const jsonSchema = zodToJsonSchema(schema);
|
|
139
|
+
return {
|
|
140
|
+
jsonSchema,
|
|
141
|
+
zodValidator: jsonSchemaToZod$1(jsonSchema)
|
|
142
|
+
};
|
|
143
|
+
}
|
|
140
144
|
return {
|
|
141
|
-
jsonSchema,
|
|
142
|
-
zodValidator: jsonSchemaToZod(
|
|
145
|
+
jsonSchema: schema,
|
|
146
|
+
zodValidator: jsonSchemaToZod$1(schema)
|
|
143
147
|
};
|
|
144
148
|
}
|
|
145
|
-
/**
|
|
146
|
-
* Validate data with Zod schema and return formatted result
|
|
147
|
-
*/
|
|
148
149
|
function validateWithZod(data, validator) {
|
|
149
150
|
const result = validator.safeParse(data);
|
|
150
|
-
if (
|
|
151
|
-
success: false,
|
|
152
|
-
error: `Validation failed:\n${result.error.issues.map((err) => ` - ${err.path.join(".") || "root"}: ${err.message}`).join("\n")}`
|
|
153
|
-
};
|
|
154
|
-
return {
|
|
151
|
+
if (result.success) return {
|
|
155
152
|
success: true,
|
|
156
153
|
data: result.data
|
|
157
154
|
};
|
|
155
|
+
return {
|
|
156
|
+
success: false,
|
|
157
|
+
error: `Validation failed:\n${result.error.issues.map((err) => ` - ${err.path.join(".") || "root"}: ${err.message}`).join("\n")}`
|
|
158
|
+
};
|
|
158
159
|
}
|
|
159
160
|
|
|
160
161
|
//#endregion
|
|
@@ -181,6 +182,7 @@ const POLYFILL_MARKER_PROPERTY = "__isWebMCPPolyfill";
|
|
|
181
182
|
* @returns Detection result with flags for native context and testing API availability
|
|
182
183
|
*/
|
|
183
184
|
function detectNativeAPI() {
|
|
185
|
+
/* c8 ignore next 2 */
|
|
184
186
|
if (typeof window === "undefined" || typeof navigator === "undefined") return {
|
|
185
187
|
hasNativeContext: false,
|
|
186
188
|
hasNativeTesting: false
|
|
@@ -188,8 +190,8 @@ function detectNativeAPI() {
|
|
|
188
190
|
const modelContext = navigator.modelContext;
|
|
189
191
|
const modelContextTesting = navigator.modelContextTesting;
|
|
190
192
|
if (!modelContext || !modelContextTesting) return {
|
|
191
|
-
hasNativeContext:
|
|
192
|
-
hasNativeTesting:
|
|
193
|
+
hasNativeContext: Boolean(modelContext),
|
|
194
|
+
hasNativeTesting: Boolean(modelContextTesting)
|
|
193
195
|
};
|
|
194
196
|
if (POLYFILL_MARKER_PROPERTY in modelContextTesting && modelContextTesting[POLYFILL_MARKER_PROPERTY] === true) return {
|
|
195
197
|
hasNativeContext: false,
|
|
@@ -258,7 +260,7 @@ var NativeModelContextAdapter = class {
|
|
|
258
260
|
const result = await this.nativeTesting.executeTool(toolInfo.name, JSON.stringify(args));
|
|
259
261
|
return this.convertToToolResponse(result);
|
|
260
262
|
},
|
|
261
|
-
inputValidator: jsonSchemaToZod(inputSchema)
|
|
263
|
+
inputValidator: jsonSchemaToZod$1(inputSchema)
|
|
262
264
|
};
|
|
263
265
|
this.bridge.tools.set(toolInfo.name, validatedTool);
|
|
264
266
|
} catch (error) {
|
|
@@ -316,24 +318,34 @@ var NativeModelContextAdapter = class {
|
|
|
316
318
|
}
|
|
317
319
|
/**
|
|
318
320
|
* Provides context (tools) to AI models via the native API.
|
|
319
|
-
*
|
|
321
|
+
* Converts Zod schemas to JSON Schema before passing to native API.
|
|
320
322
|
* Tool change callback will fire and trigger sync automatically.
|
|
321
323
|
*
|
|
322
324
|
* @param {ModelContextInput} context - Context containing tools to register
|
|
323
325
|
*/
|
|
324
326
|
provideContext(context) {
|
|
325
|
-
|
|
327
|
+
const { tools,...rest } = context;
|
|
328
|
+
const normalizedContext = { ...rest };
|
|
329
|
+
if (tools) normalizedContext.tools = tools.map((tool) => ({
|
|
330
|
+
...tool,
|
|
331
|
+
inputSchema: normalizeSchema(tool.inputSchema).jsonSchema
|
|
332
|
+
}));
|
|
333
|
+
this.nativeContext.provideContext(normalizedContext);
|
|
326
334
|
}
|
|
327
335
|
/**
|
|
328
336
|
* Registers a single tool dynamically via the native API.
|
|
329
|
-
*
|
|
337
|
+
* Converts Zod schemas to JSON Schema before passing to native API.
|
|
330
338
|
* Tool change callback will fire and trigger sync automatically.
|
|
331
339
|
*
|
|
332
340
|
* @param {ToolDescriptor} tool - The tool descriptor to register
|
|
333
341
|
* @returns {{unregister: () => void}} Object with unregister function
|
|
334
342
|
*/
|
|
335
343
|
registerTool(tool) {
|
|
336
|
-
|
|
344
|
+
const normalizedTool = {
|
|
345
|
+
...tool,
|
|
346
|
+
inputSchema: normalizeSchema(tool.inputSchema).jsonSchema
|
|
347
|
+
};
|
|
348
|
+
return this.nativeContext.registerTool(normalizedTool);
|
|
337
349
|
}
|
|
338
350
|
/**
|
|
339
351
|
* Unregisters a tool by name via the native API.
|
|
@@ -354,13 +366,15 @@ var NativeModelContextAdapter = class {
|
|
|
354
366
|
/**
|
|
355
367
|
* Executes a tool via the native API.
|
|
356
368
|
* Delegates to navigator.modelContextTesting.executeTool() with JSON string args.
|
|
369
|
+
* Note: skipValidation option is ignored - native API handles its own validation.
|
|
357
370
|
*
|
|
358
371
|
* @param {string} toolName - Name of the tool to execute
|
|
359
372
|
* @param {Record<string, unknown>} args - Arguments to pass to the tool
|
|
373
|
+
* @param {Object} [_options] - Execution options (ignored for native adapter)
|
|
360
374
|
* @returns {Promise<ToolResponse>} The tool's response in MCP format
|
|
361
375
|
* @internal
|
|
362
376
|
*/
|
|
363
|
-
async executeTool(toolName, args) {
|
|
377
|
+
async executeTool(toolName, args, _options) {
|
|
364
378
|
try {
|
|
365
379
|
const result = await this.nativeTesting.executeTool(toolName, JSON.stringify(args));
|
|
366
380
|
return this.convertToToolResponse(result);
|
|
@@ -898,11 +912,11 @@ var WebModelContext = class {
|
|
|
898
912
|
* @private
|
|
899
913
|
*/
|
|
900
914
|
validateResource(resource) {
|
|
901
|
-
const templateParamRegex = /\{([^}]
|
|
915
|
+
const templateParamRegex = /\{([^}]{1,100})\}/g;
|
|
902
916
|
const templateParams = [];
|
|
903
917
|
for (const match of resource.uri.matchAll(templateParamRegex)) {
|
|
904
918
|
const paramName = match[1];
|
|
905
|
-
|
|
919
|
+
templateParams.push(paramName);
|
|
906
920
|
}
|
|
907
921
|
return {
|
|
908
922
|
uri: resource.uri,
|
|
@@ -1318,7 +1332,13 @@ var WebModelContext = class {
|
|
|
1318
1332
|
async readResource(uri) {
|
|
1319
1333
|
const staticResource = this.bridge.resources.get(uri);
|
|
1320
1334
|
if (staticResource && !staticResource.isTemplate) try {
|
|
1321
|
-
|
|
1335
|
+
let parsedUri;
|
|
1336
|
+
try {
|
|
1337
|
+
parsedUri = new URL(uri);
|
|
1338
|
+
} catch {
|
|
1339
|
+
parsedUri = new URL(`custom-scheme:///${encodeURIComponent(uri)}`);
|
|
1340
|
+
parsedUri.originalUri = uri;
|
|
1341
|
+
}
|
|
1322
1342
|
return await staticResource.read(parsedUri);
|
|
1323
1343
|
} catch (error) {
|
|
1324
1344
|
logger$1.error(`Error reading resource ${uri}:`, error);
|
|
@@ -1328,7 +1348,13 @@ var WebModelContext = class {
|
|
|
1328
1348
|
if (!resource.isTemplate) continue;
|
|
1329
1349
|
const params = this.matchUriTemplate(resource.uri, uri);
|
|
1330
1350
|
if (params) try {
|
|
1331
|
-
|
|
1351
|
+
let parsedUri;
|
|
1352
|
+
try {
|
|
1353
|
+
parsedUri = new URL(uri);
|
|
1354
|
+
} catch {
|
|
1355
|
+
parsedUri = new URL(`custom-scheme:///${encodeURIComponent(uri)}`);
|
|
1356
|
+
parsedUri.originalUri = uri;
|
|
1357
|
+
}
|
|
1332
1358
|
return await resource.read(parsedUri, params);
|
|
1333
1359
|
} catch (error) {
|
|
1334
1360
|
logger$1.error(`Error reading resource ${uri}:`, error);
|
|
@@ -1361,8 +1387,7 @@ var WebModelContext = class {
|
|
|
1361
1387
|
const params = {};
|
|
1362
1388
|
for (let i = 0; i < paramNames.length; i++) {
|
|
1363
1389
|
const paramName = paramNames[i];
|
|
1364
|
-
|
|
1365
|
-
if (paramName !== void 0 && paramValue !== void 0) params[paramName] = paramValue;
|
|
1390
|
+
params[paramName] = match[i + 1];
|
|
1366
1391
|
}
|
|
1367
1392
|
return params;
|
|
1368
1393
|
}
|
|
@@ -1395,7 +1420,7 @@ var WebModelContext = class {
|
|
|
1395
1420
|
/**
|
|
1396
1421
|
* Executes a tool with validation and event dispatch.
|
|
1397
1422
|
* Follows this sequence:
|
|
1398
|
-
* 1. Validates input arguments against schema
|
|
1423
|
+
* 1. Validates input arguments against schema (unless skipValidation is true)
|
|
1399
1424
|
* 2. Records tool call in testing API (if available)
|
|
1400
1425
|
* 3. Checks for mock response (if testing)
|
|
1401
1426
|
* 4. Dispatches 'toolcall' event to listeners
|
|
@@ -1404,25 +1429,31 @@ var WebModelContext = class {
|
|
|
1404
1429
|
*
|
|
1405
1430
|
* @param {string} toolName - Name of the tool to execute
|
|
1406
1431
|
* @param {Record<string, unknown>} args - Arguments to pass to the tool
|
|
1432
|
+
* @param {Object} [options] - Execution options
|
|
1433
|
+
* @param {boolean} [options.skipValidation] - Skip input validation (used when MCP SDK already validated)
|
|
1407
1434
|
* @returns {Promise<ToolResponse>} The tool's response
|
|
1408
1435
|
* @throws {Error} If tool is not found
|
|
1409
1436
|
* @internal
|
|
1410
1437
|
*/
|
|
1411
|
-
async executeTool(toolName, args) {
|
|
1438
|
+
async executeTool(toolName, args, options) {
|
|
1412
1439
|
const tool = this.bridge.tools.get(toolName);
|
|
1413
1440
|
if (!tool) throw new Error(`Tool not found: ${toolName}`);
|
|
1414
|
-
|
|
1415
|
-
if (
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1441
|
+
let validatedArgs;
|
|
1442
|
+
if (options?.skipValidation) validatedArgs = args;
|
|
1443
|
+
else {
|
|
1444
|
+
const validation = validateWithZod(args, tool.inputValidator);
|
|
1445
|
+
if (!validation.success) {
|
|
1446
|
+
logger$1.error(`Input validation failed for ${toolName}:`, validation.error);
|
|
1447
|
+
return {
|
|
1448
|
+
content: [{
|
|
1449
|
+
type: "text",
|
|
1450
|
+
text: `Input validation error for tool "${toolName}":\n${validation.error}`
|
|
1451
|
+
}],
|
|
1452
|
+
isError: true
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
validatedArgs = validation.data;
|
|
1424
1456
|
}
|
|
1425
|
-
const validatedArgs = validation.data;
|
|
1426
1457
|
if (this.testingAPI) this.testingAPI.recordToolCall(toolName, validatedArgs);
|
|
1427
1458
|
if (this.testingAPI?.hasMockResponse(toolName)) {
|
|
1428
1459
|
const mockResponse = this.testingAPI.getMockResponse(toolName);
|
|
@@ -1640,6 +1671,7 @@ function initializeMCPBridge(options) {
|
|
|
1640
1671
|
* ```
|
|
1641
1672
|
*/
|
|
1642
1673
|
function initializeWebModelContext(options) {
|
|
1674
|
+
/* c8 ignore next 4 */
|
|
1643
1675
|
if (typeof window === "undefined") {
|
|
1644
1676
|
logger$1.warn("Not in browser environment, skipping initialization");
|
|
1645
1677
|
return;
|
|
@@ -1733,6 +1765,7 @@ function initializeWebModelContext(options) {
|
|
|
1733
1765
|
* ```
|
|
1734
1766
|
*/
|
|
1735
1767
|
function cleanupWebModelContext() {
|
|
1768
|
+
/* c8 ignore next */
|
|
1736
1769
|
if (typeof window === "undefined") return;
|
|
1737
1770
|
if (window.__mcpBridge) try {
|
|
1738
1771
|
window.__mcpBridge.tabServer.close();
|