@mcp-b/webmcp-ts-sdk 2.1.0 → 2.2.1
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 +60 -42
- package/dist/index.d.ts +9042 -27
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +244 -59
- package/dist/index.js.map +1 -1
- package/package.json +39 -39
package/dist/index.js
CHANGED
|
@@ -105,10 +105,10 @@ function isSchemaOptional(schema) {
|
|
|
105
105
|
*/
|
|
106
106
|
function getLiteralValue(schema) {
|
|
107
107
|
if (isZ4Schema(schema)) {
|
|
108
|
-
const def
|
|
109
|
-
if (def
|
|
110
|
-
if (def
|
|
111
|
-
if (Array.isArray(def
|
|
108
|
+
const def = schema._zod?.def;
|
|
109
|
+
if (def) {
|
|
110
|
+
if (def.value !== void 0) return def.value;
|
|
111
|
+
if (Array.isArray(def.values) && def.values.length > 0) return def.values[0];
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
const def = schema._def;
|
|
@@ -196,7 +196,7 @@ const RequestIdSchema = z.union([z.string(), z.number().int()]);
|
|
|
196
196
|
* A request that expects a response.
|
|
197
197
|
*/
|
|
198
198
|
const JSONRPCRequestSchema = z.object({
|
|
199
|
-
jsonrpc: z.literal(
|
|
199
|
+
jsonrpc: z.literal("2.0"),
|
|
200
200
|
id: RequestIdSchema,
|
|
201
201
|
...RequestSchema.shape
|
|
202
202
|
}).strict();
|
|
@@ -205,7 +205,7 @@ const isJSONRPCRequest = (value) => JSONRPCRequestSchema.safeParse(value).succes
|
|
|
205
205
|
* A notification which does not expect a response.
|
|
206
206
|
*/
|
|
207
207
|
const JSONRPCNotificationSchema = z.object({
|
|
208
|
-
jsonrpc: z.literal(
|
|
208
|
+
jsonrpc: z.literal("2.0"),
|
|
209
209
|
...NotificationSchema.shape
|
|
210
210
|
}).strict();
|
|
211
211
|
const isJSONRPCNotification = (value) => JSONRPCNotificationSchema.safeParse(value).success;
|
|
@@ -213,7 +213,7 @@ const isJSONRPCNotification = (value) => JSONRPCNotificationSchema.safeParse(val
|
|
|
213
213
|
* A successful (non-error) response to a request.
|
|
214
214
|
*/
|
|
215
215
|
const JSONRPCResultResponseSchema = z.object({
|
|
216
|
-
jsonrpc: z.literal(
|
|
216
|
+
jsonrpc: z.literal("2.0"),
|
|
217
217
|
id: RequestIdSchema,
|
|
218
218
|
result: ResultSchema
|
|
219
219
|
}).strict();
|
|
@@ -228,21 +228,21 @@ const isJSONRPCResultResponse = (value) => JSONRPCResultResponseSchema.safeParse
|
|
|
228
228
|
* Error codes defined by the JSON-RPC specification.
|
|
229
229
|
*/
|
|
230
230
|
var ErrorCode;
|
|
231
|
-
(function(ErrorCode
|
|
232
|
-
ErrorCode
|
|
233
|
-
ErrorCode
|
|
234
|
-
ErrorCode
|
|
235
|
-
ErrorCode
|
|
236
|
-
ErrorCode
|
|
237
|
-
ErrorCode
|
|
238
|
-
ErrorCode
|
|
239
|
-
ErrorCode
|
|
231
|
+
(function(ErrorCode) {
|
|
232
|
+
ErrorCode[ErrorCode["ConnectionClosed"] = -32e3] = "ConnectionClosed";
|
|
233
|
+
ErrorCode[ErrorCode["RequestTimeout"] = -32001] = "RequestTimeout";
|
|
234
|
+
ErrorCode[ErrorCode["ParseError"] = -32700] = "ParseError";
|
|
235
|
+
ErrorCode[ErrorCode["InvalidRequest"] = -32600] = "InvalidRequest";
|
|
236
|
+
ErrorCode[ErrorCode["MethodNotFound"] = -32601] = "MethodNotFound";
|
|
237
|
+
ErrorCode[ErrorCode["InvalidParams"] = -32602] = "InvalidParams";
|
|
238
|
+
ErrorCode[ErrorCode["InternalError"] = -32603] = "InternalError";
|
|
239
|
+
ErrorCode[ErrorCode["UrlElicitationRequired"] = -32042] = "UrlElicitationRequired";
|
|
240
240
|
})(ErrorCode || (ErrorCode = {}));
|
|
241
241
|
/**
|
|
242
242
|
* A response to a request that indicates an error occurred.
|
|
243
243
|
*/
|
|
244
244
|
const JSONRPCErrorResponseSchema = z.object({
|
|
245
|
-
jsonrpc: z.literal(
|
|
245
|
+
jsonrpc: z.literal("2.0"),
|
|
246
246
|
id: RequestIdSchema.optional(),
|
|
247
247
|
error: z.object({
|
|
248
248
|
code: z.number().int(),
|
|
@@ -1720,7 +1720,7 @@ var Protocol = class {
|
|
|
1720
1720
|
});
|
|
1721
1721
|
}
|
|
1722
1722
|
_onprogress(notification) {
|
|
1723
|
-
const { progressToken
|
|
1723
|
+
const { progressToken, ...params } = notification.params;
|
|
1724
1724
|
const messageId = Number(progressToken);
|
|
1725
1725
|
const handler = this._progressHandlers.get(messageId);
|
|
1726
1726
|
if (!handler) {
|
|
@@ -1834,34 +1834,34 @@ var Protocol = class {
|
|
|
1834
1834
|
};
|
|
1835
1835
|
} else throw new McpError(ErrorCode.InternalError, "Task creation did not return a task");
|
|
1836
1836
|
while (true) {
|
|
1837
|
-
const task
|
|
1837
|
+
const task = await this.getTask({ taskId }, options);
|
|
1838
1838
|
yield {
|
|
1839
1839
|
type: "taskStatus",
|
|
1840
|
-
task
|
|
1840
|
+
task
|
|
1841
1841
|
};
|
|
1842
|
-
if (isTerminal(task
|
|
1843
|
-
if (task
|
|
1842
|
+
if (isTerminal(task.status)) {
|
|
1843
|
+
if (task.status === "completed") yield {
|
|
1844
1844
|
type: "result",
|
|
1845
1845
|
result: await this.getTaskResult({ taskId }, resultSchema, options)
|
|
1846
1846
|
};
|
|
1847
|
-
else if (task
|
|
1847
|
+
else if (task.status === "failed") yield {
|
|
1848
1848
|
type: "error",
|
|
1849
1849
|
error: new McpError(ErrorCode.InternalError, `Task ${taskId} failed`)
|
|
1850
1850
|
};
|
|
1851
|
-
else if (task
|
|
1851
|
+
else if (task.status === "cancelled") yield {
|
|
1852
1852
|
type: "error",
|
|
1853
1853
|
error: new McpError(ErrorCode.InternalError, `Task ${taskId} was cancelled`)
|
|
1854
1854
|
};
|
|
1855
1855
|
return;
|
|
1856
1856
|
}
|
|
1857
|
-
if (task
|
|
1857
|
+
if (task.status === "input_required") {
|
|
1858
1858
|
yield {
|
|
1859
1859
|
type: "result",
|
|
1860
1860
|
result: await this.getTaskResult({ taskId }, resultSchema, options)
|
|
1861
1861
|
};
|
|
1862
1862
|
return;
|
|
1863
1863
|
}
|
|
1864
|
-
const pollInterval = task
|
|
1864
|
+
const pollInterval = task.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
1865
1865
|
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
1866
1866
|
options?.signal?.throwIfAborted();
|
|
1867
1867
|
}
|
|
@@ -1954,7 +1954,7 @@ var Protocol = class {
|
|
|
1954
1954
|
options?.signal?.addEventListener("abort", () => {
|
|
1955
1955
|
cancel(options?.signal?.reason);
|
|
1956
1956
|
});
|
|
1957
|
-
const timeout = options?.timeout ??
|
|
1957
|
+
const timeout = options?.timeout ?? 6e4;
|
|
1958
1958
|
const timeoutHandler = () => cancel(McpError.fromError(ErrorCode.RequestTimeout, "Request timed out", { timeout }));
|
|
1959
1959
|
this._setupTimeout(messageId, timeout, options?.maxTotalTimeout, timeoutHandler, options?.resetTimeoutOnProgress ?? false);
|
|
1960
1960
|
const relatedTaskId = relatedTask?.taskId;
|
|
@@ -2035,7 +2035,7 @@ var Protocol = class {
|
|
|
2035
2035
|
this.assertNotificationCapability(notification.method);
|
|
2036
2036
|
const relatedTaskId = options?.relatedTask?.taskId;
|
|
2037
2037
|
if (relatedTaskId) {
|
|
2038
|
-
const jsonrpcNotification
|
|
2038
|
+
const jsonrpcNotification = {
|
|
2039
2039
|
...notification,
|
|
2040
2040
|
jsonrpc: "2.0",
|
|
2041
2041
|
params: {
|
|
@@ -2048,7 +2048,7 @@ var Protocol = class {
|
|
|
2048
2048
|
};
|
|
2049
2049
|
await this._enqueueTaskMessage(relatedTaskId, {
|
|
2050
2050
|
type: "notification",
|
|
2051
|
-
message: jsonrpcNotification
|
|
2051
|
+
message: jsonrpcNotification,
|
|
2052
2052
|
timestamp: Date.now()
|
|
2053
2053
|
});
|
|
2054
2054
|
return;
|
|
@@ -2059,21 +2059,21 @@ var Protocol = class {
|
|
|
2059
2059
|
Promise.resolve().then(() => {
|
|
2060
2060
|
this._pendingDebouncedNotifications.delete(notification.method);
|
|
2061
2061
|
if (!this._transport) return;
|
|
2062
|
-
let jsonrpcNotification
|
|
2062
|
+
let jsonrpcNotification = {
|
|
2063
2063
|
...notification,
|
|
2064
2064
|
jsonrpc: "2.0"
|
|
2065
2065
|
};
|
|
2066
|
-
if (options?.relatedTask) jsonrpcNotification
|
|
2067
|
-
...jsonrpcNotification
|
|
2066
|
+
if (options?.relatedTask) jsonrpcNotification = {
|
|
2067
|
+
...jsonrpcNotification,
|
|
2068
2068
|
params: {
|
|
2069
|
-
...jsonrpcNotification
|
|
2069
|
+
...jsonrpcNotification.params,
|
|
2070
2070
|
_meta: {
|
|
2071
|
-
...jsonrpcNotification
|
|
2071
|
+
...jsonrpcNotification.params?._meta || {},
|
|
2072
2072
|
[RELATED_TASK_META_KEY]: options.relatedTask
|
|
2073
2073
|
}
|
|
2074
2074
|
}
|
|
2075
2075
|
};
|
|
2076
|
-
this._transport?.send(jsonrpcNotification
|
|
2076
|
+
this._transport?.send(jsonrpcNotification, options).catch((error) => this._onerror(error));
|
|
2077
2077
|
});
|
|
2078
2078
|
return;
|
|
2079
2079
|
}
|
|
@@ -2300,7 +2300,6 @@ var Ajv = class {
|
|
|
2300
2300
|
return "";
|
|
2301
2301
|
}
|
|
2302
2302
|
};
|
|
2303
|
-
var ajv_default = Ajv;
|
|
2304
2303
|
|
|
2305
2304
|
//#endregion
|
|
2306
2305
|
//#region src/stubs/ajv-formats.ts
|
|
@@ -2312,8 +2311,11 @@ function addFormats(_ajv) {}
|
|
|
2312
2311
|
|
|
2313
2312
|
//#endregion
|
|
2314
2313
|
//#region ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.26.0_@cfworker+json-schema@4.1.1_zod@4.3.5/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
|
|
2314
|
+
/**
|
|
2315
|
+
* AJV-based JSON Schema validator provider
|
|
2316
|
+
*/
|
|
2315
2317
|
function createDefaultAjvInstance() {
|
|
2316
|
-
const ajv = new
|
|
2318
|
+
const ajv = new Ajv({
|
|
2317
2319
|
strict: false,
|
|
2318
2320
|
validateFormats: true,
|
|
2319
2321
|
validateSchema: false,
|
|
@@ -2388,6 +2390,12 @@ var AjvJsonSchemaValidator = class {
|
|
|
2388
2390
|
//#endregion
|
|
2389
2391
|
//#region ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.26.0_@cfworker+json-schema@4.1.1_zod@4.3.5/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/client.js
|
|
2390
2392
|
/**
|
|
2393
|
+
* Experimental client task features for MCP SDK.
|
|
2394
|
+
* WARNING: These APIs are experimental and may change without notice.
|
|
2395
|
+
*
|
|
2396
|
+
* @experimental
|
|
2397
|
+
*/
|
|
2398
|
+
/**
|
|
2391
2399
|
* Experimental task features for MCP clients.
|
|
2392
2400
|
*
|
|
2393
2401
|
* Access via `client.experimental.tasks`:
|
|
@@ -3563,8 +3571,8 @@ function getCompleter(schema) {
|
|
|
3563
3571
|
return schema[COMPLETABLE_SYMBOL]?.complete;
|
|
3564
3572
|
}
|
|
3565
3573
|
var McpZodTypeKind;
|
|
3566
|
-
(function(McpZodTypeKind
|
|
3567
|
-
McpZodTypeKind
|
|
3574
|
+
(function(McpZodTypeKind) {
|
|
3575
|
+
McpZodTypeKind["Completable"] = "McpCompletable";
|
|
3568
3576
|
})(McpZodTypeKind || (McpZodTypeKind = {}));
|
|
3569
3577
|
|
|
3570
3578
|
//#endregion
|
|
@@ -4336,6 +4344,11 @@ function isCallToolResult(value) {
|
|
|
4336
4344
|
function isJsonPrimitive(value) {
|
|
4337
4345
|
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
4338
4346
|
}
|
|
4347
|
+
function isPermissionsPolicySecurityError(error) {
|
|
4348
|
+
if (!error || typeof error !== "object") return false;
|
|
4349
|
+
const { name, message } = error;
|
|
4350
|
+
return name === "SecurityError" && typeof message === "string" && /permissions policy|feature "tools" is disallowed/i.test(message);
|
|
4351
|
+
}
|
|
4339
4352
|
function isJsonValue(value) {
|
|
4340
4353
|
if (isJsonPrimitive(value)) return Number.isFinite(value) || typeof value !== "number";
|
|
4341
4354
|
if (Array.isArray(value)) return value.every((entry) => isJsonValue(entry));
|
|
@@ -4376,13 +4389,15 @@ function withDefaultTimeout(options) {
|
|
|
4376
4389
|
/**
|
|
4377
4390
|
* Browser-optimized MCP Server that speaks WebMCP natively.
|
|
4378
4391
|
*
|
|
4379
|
-
*
|
|
4380
|
-
* (
|
|
4381
|
-
*
|
|
4382
|
-
*
|
|
4392
|
+
* Implements `registerTool(tool, options?)` while retaining MCP capabilities
|
|
4393
|
+
* (resources, prompts, elicitation, sampling) via BaseMcpServer.
|
|
4394
|
+
*
|
|
4395
|
+
* Deprecated compatibility (kept for Chrome Beta 147 and existing wrappers,
|
|
4396
|
+
* removed in the next major): `unregisterTool(name)`, `provideContext()`,
|
|
4397
|
+
* `clearContext()`, and the `{ unregister }` handle returned from `registerTool`.
|
|
4383
4398
|
*
|
|
4384
|
-
* When `native` is provided,
|
|
4385
|
-
* navigator.modelContextTesting
|
|
4399
|
+
* When `native` is provided, tool operations are mirrored so that
|
|
4400
|
+
* navigator.modelContextTesting stays in sync.
|
|
4386
4401
|
*/
|
|
4387
4402
|
var BrowserMcpServer = class extends McpServer {
|
|
4388
4403
|
[SERVER_MARKER_PROPERTY] = true;
|
|
@@ -4390,6 +4405,12 @@ var BrowserMcpServer = class extends McpServer {
|
|
|
4390
4405
|
_promptSchemas = /* @__PURE__ */ new Map();
|
|
4391
4406
|
_jsonValidator;
|
|
4392
4407
|
_publicMethodsBound = false;
|
|
4408
|
+
_provideContextDeprecationWarned = false;
|
|
4409
|
+
_clearContextDeprecationWarned = false;
|
|
4410
|
+
_unregisterToolDeprecationWarned = false;
|
|
4411
|
+
_nativeToolCleanups = /* @__PURE__ */ new Map();
|
|
4412
|
+
_producerEventTarget = new EventTarget();
|
|
4413
|
+
_ontoolchange = null;
|
|
4393
4414
|
constructor(serverInfo, options) {
|
|
4394
4415
|
const validator = new PolyfillJsonSchemaValidator();
|
|
4395
4416
|
const enhancedOptions = {
|
|
@@ -4416,8 +4437,12 @@ var BrowserMcpServer = class extends McpServer {
|
|
|
4416
4437
|
this.provideContext = this.provideContext.bind(this);
|
|
4417
4438
|
this.clearContext = this.clearContext.bind(this);
|
|
4418
4439
|
this.listTools = this.listTools.bind(this);
|
|
4440
|
+
this.getTools = this.getTools.bind(this);
|
|
4419
4441
|
this.callTool = this.callTool.bind(this);
|
|
4420
4442
|
this.executeTool = this.executeTool.bind(this);
|
|
4443
|
+
this.addEventListener = this.addEventListener.bind(this);
|
|
4444
|
+
this.removeEventListener = this.removeEventListener.bind(this);
|
|
4445
|
+
this.dispatchEvent = this.dispatchEvent.bind(this);
|
|
4421
4446
|
this.registerResource = this.registerResource.bind(this);
|
|
4422
4447
|
this.listResources = this.listResources.bind(this);
|
|
4423
4448
|
this.readResource = this.readResource.bind(this);
|
|
@@ -4478,6 +4503,71 @@ var BrowserMcpServer = class extends McpServer {
|
|
|
4478
4503
|
if (typeof candidate.listTools !== "function" || typeof candidate.callTool !== "function") return;
|
|
4479
4504
|
return candidate;
|
|
4480
4505
|
}
|
|
4506
|
+
getNativeUnregisterTool() {
|
|
4507
|
+
if (!this.native) return;
|
|
4508
|
+
const unregisterTool = this.native.unregisterTool;
|
|
4509
|
+
if (typeof unregisterTool !== "function") return;
|
|
4510
|
+
return (nameOrTool) => unregisterTool.call(this.native, nameOrTool);
|
|
4511
|
+
}
|
|
4512
|
+
createNativeToolCleanup(signal) {
|
|
4513
|
+
const controller = new AbortController();
|
|
4514
|
+
let removeSignalListener;
|
|
4515
|
+
if (signal) {
|
|
4516
|
+
const abortNativeSignal = () => {
|
|
4517
|
+
controller.abort();
|
|
4518
|
+
};
|
|
4519
|
+
if (signal.aborted) abortNativeSignal();
|
|
4520
|
+
else {
|
|
4521
|
+
signal.addEventListener("abort", abortNativeSignal, { once: true });
|
|
4522
|
+
removeSignalListener = () => signal.removeEventListener("abort", abortNativeSignal);
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
return {
|
|
4526
|
+
options: { signal: controller.signal },
|
|
4527
|
+
abort: () => {
|
|
4528
|
+
removeSignalListener?.();
|
|
4529
|
+
if (!controller.signal.aborted) controller.abort();
|
|
4530
|
+
}
|
|
4531
|
+
};
|
|
4532
|
+
}
|
|
4533
|
+
registerNativeToolMirror(tool, signal) {
|
|
4534
|
+
if (!this.native) return;
|
|
4535
|
+
const nativeRegister = this.native.registerTool;
|
|
4536
|
+
const nativeUnregisterTool = this.getNativeUnregisterTool();
|
|
4537
|
+
const cleanup = Boolean(signal) || !nativeUnregisterTool ? this.createNativeToolCleanup(signal) : void 0;
|
|
4538
|
+
try {
|
|
4539
|
+
if (cleanup) nativeRegister.call(this.native, tool, cleanup.options);
|
|
4540
|
+
else nativeRegister.call(this.native, tool);
|
|
4541
|
+
} catch (error) {
|
|
4542
|
+
cleanup?.abort();
|
|
4543
|
+
if (isPermissionsPolicySecurityError(error)) {
|
|
4544
|
+
console.warn("[BrowserMcpServer] Native WebMCP tool mirror is blocked by permissions policy; continuing with WebMCP transport registration only.");
|
|
4545
|
+
return;
|
|
4546
|
+
}
|
|
4547
|
+
throw error;
|
|
4548
|
+
}
|
|
4549
|
+
if (!cleanup) return;
|
|
4550
|
+
const nativeToolCleanup = {
|
|
4551
|
+
abort: cleanup.abort,
|
|
4552
|
+
nativeSignalAccepted: nativeRegister.length >= 2 || !nativeUnregisterTool
|
|
4553
|
+
};
|
|
4554
|
+
this._nativeToolCleanups.set(tool.name, nativeToolCleanup);
|
|
4555
|
+
return nativeToolCleanup;
|
|
4556
|
+
}
|
|
4557
|
+
unregisterNativeToolMirror(name, options) {
|
|
4558
|
+
const cleanup = this._nativeToolCleanups.get(name);
|
|
4559
|
+
this._nativeToolCleanups.delete(name);
|
|
4560
|
+
const nativeUnregisterTool = this.getNativeUnregisterTool();
|
|
4561
|
+
if (options?.preferAbortSignal && cleanup?.nativeSignalAccepted === true || !nativeUnregisterTool) {
|
|
4562
|
+
cleanup?.abort();
|
|
4563
|
+
return;
|
|
4564
|
+
}
|
|
4565
|
+
try {
|
|
4566
|
+
nativeUnregisterTool(name);
|
|
4567
|
+
} finally {
|
|
4568
|
+
cleanup?.abort();
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4481
4571
|
registerToolInServer(tool) {
|
|
4482
4572
|
const inputSchema = this.toTransportSchema(tool.inputSchema);
|
|
4483
4573
|
super.registerTool(tool.name, {
|
|
@@ -4488,8 +4578,35 @@ var BrowserMcpServer = class extends McpServer {
|
|
|
4488
4578
|
}, async (args) => {
|
|
4489
4579
|
return normalizeToolResponse(await tool.execute(args, { requestUserInteraction: async (cb) => cb() }));
|
|
4490
4580
|
});
|
|
4581
|
+
this.notifyProducerToolsChanged();
|
|
4491
4582
|
return { unregister: () => this.unregisterTool(tool.name) };
|
|
4492
4583
|
}
|
|
4584
|
+
get ontoolchange() {
|
|
4585
|
+
return this._ontoolchange;
|
|
4586
|
+
}
|
|
4587
|
+
set ontoolchange(handler) {
|
|
4588
|
+
this._ontoolchange = handler;
|
|
4589
|
+
}
|
|
4590
|
+
addEventListener(type, listener, options) {
|
|
4591
|
+
this._producerEventTarget.addEventListener(type, listener, options);
|
|
4592
|
+
}
|
|
4593
|
+
removeEventListener(type, listener, options) {
|
|
4594
|
+
this._producerEventTarget.removeEventListener(type, listener, options);
|
|
4595
|
+
}
|
|
4596
|
+
dispatchEvent(event) {
|
|
4597
|
+
return this._producerEventTarget.dispatchEvent(event);
|
|
4598
|
+
}
|
|
4599
|
+
notifyProducerToolsChanged() {
|
|
4600
|
+
queueMicrotask(() => {
|
|
4601
|
+
const event = new Event("toolchange");
|
|
4602
|
+
try {
|
|
4603
|
+
this._ontoolchange?.call(this, event);
|
|
4604
|
+
} catch (error) {
|
|
4605
|
+
console.warn("[BrowserMcpServer] navigator.modelContext.ontoolchange handler threw:", error);
|
|
4606
|
+
}
|
|
4607
|
+
this.dispatchEvent(event);
|
|
4608
|
+
});
|
|
4609
|
+
}
|
|
4493
4610
|
backfillTools(tools, execute) {
|
|
4494
4611
|
let synced = 0;
|
|
4495
4612
|
for (const sourceTool of tools) {
|
|
@@ -4507,18 +4624,34 @@ var BrowserMcpServer = class extends McpServer {
|
|
|
4507
4624
|
}
|
|
4508
4625
|
return synced;
|
|
4509
4626
|
}
|
|
4510
|
-
registerTool(tool) {
|
|
4511
|
-
|
|
4627
|
+
registerTool(tool, options) {
|
|
4628
|
+
const signal = options?.signal;
|
|
4629
|
+
if (signal?.aborted) {
|
|
4630
|
+
console.warn(`[BrowserMcpServer] registerTool("${tool?.name ?? "<unknown>"}") skipped: options.signal was already aborted.`);
|
|
4631
|
+
return { unregister: () => {} };
|
|
4632
|
+
}
|
|
4633
|
+
this.registerNativeToolMirror(tool, signal);
|
|
4634
|
+
let handle;
|
|
4512
4635
|
try {
|
|
4513
|
-
|
|
4636
|
+
handle = this.registerToolInServer(tool);
|
|
4514
4637
|
} catch (error) {
|
|
4515
4638
|
if (this.native) try {
|
|
4516
|
-
this.
|
|
4639
|
+
this.unregisterNativeToolMirror(tool.name);
|
|
4517
4640
|
} catch (rollbackError) {
|
|
4518
4641
|
console.error("[BrowserMcpServer] Rollback of native tool registration failed:", rollbackError);
|
|
4519
4642
|
}
|
|
4520
4643
|
throw error;
|
|
4521
4644
|
}
|
|
4645
|
+
if (signal) signal.addEventListener("abort", () => {
|
|
4646
|
+
this._parentTools[tool.name]?.remove();
|
|
4647
|
+
try {
|
|
4648
|
+
this.unregisterNativeToolMirror(tool.name, { preferAbortSignal: true });
|
|
4649
|
+
} catch (error) {
|
|
4650
|
+
console.warn("[BrowserMcpServer] Native unregister via abort fallback failed:", error);
|
|
4651
|
+
}
|
|
4652
|
+
this.notifyProducerToolsChanged();
|
|
4653
|
+
}, { once: true });
|
|
4654
|
+
return handle;
|
|
4522
4655
|
}
|
|
4523
4656
|
/**
|
|
4524
4657
|
* Backfill tools that were already registered on the native/polyfill context
|
|
@@ -4533,9 +4666,26 @@ var BrowserMcpServer = class extends McpServer {
|
|
|
4533
4666
|
arguments: args
|
|
4534
4667
|
}));
|
|
4535
4668
|
}
|
|
4536
|
-
unregisterTool(
|
|
4537
|
-
this.
|
|
4538
|
-
|
|
4669
|
+
unregisterTool(nameOrTool) {
|
|
4670
|
+
this.warnUnregisterToolDeprecationOnce();
|
|
4671
|
+
const name = this.resolveToolNameForUnregister(nameOrTool);
|
|
4672
|
+
const existing = this._parentTools[name];
|
|
4673
|
+
existing?.remove();
|
|
4674
|
+
if (this.native) this.unregisterNativeToolMirror(name);
|
|
4675
|
+
if (existing) this.notifyProducerToolsChanged();
|
|
4676
|
+
}
|
|
4677
|
+
clearRegisteredTools() {
|
|
4678
|
+
let changed = false;
|
|
4679
|
+
for (const name of Object.keys(this._parentTools)) {
|
|
4680
|
+
this._parentTools[name]?.remove();
|
|
4681
|
+
changed = true;
|
|
4682
|
+
if (this.native) try {
|
|
4683
|
+
this.unregisterNativeToolMirror(name);
|
|
4684
|
+
} catch (error) {
|
|
4685
|
+
console.warn("[BrowserMcpServer] Native unregister during clear failed:", error);
|
|
4686
|
+
}
|
|
4687
|
+
}
|
|
4688
|
+
if (changed) this.notifyProducerToolsChanged();
|
|
4539
4689
|
}
|
|
4540
4690
|
registerResource(descriptor) {
|
|
4541
4691
|
const registered = super.registerResource(descriptor.name, descriptor.uri, {
|
|
@@ -4553,13 +4703,33 @@ var BrowserMcpServer = class extends McpServer {
|
|
|
4553
4703
|
} };
|
|
4554
4704
|
}
|
|
4555
4705
|
provideContext(options) {
|
|
4556
|
-
|
|
4557
|
-
|
|
4706
|
+
this.warnProvideContextDeprecationOnce();
|
|
4707
|
+
this.clearRegisteredTools();
|
|
4558
4708
|
for (const tool of options?.tools ?? []) this.registerTool(tool);
|
|
4559
4709
|
}
|
|
4560
4710
|
clearContext() {
|
|
4561
|
-
|
|
4562
|
-
|
|
4711
|
+
this.warnClearContextDeprecationOnce();
|
|
4712
|
+
this.clearRegisteredTools();
|
|
4713
|
+
}
|
|
4714
|
+
resolveToolNameForUnregister(nameOrTool) {
|
|
4715
|
+
if (typeof nameOrTool === "string") return nameOrTool;
|
|
4716
|
+
if (isPlainObject$1(nameOrTool) && typeof nameOrTool.name === "string") return nameOrTool.name;
|
|
4717
|
+
throw new TypeError("Failed to execute 'unregisterTool' on 'ModelContext': parameter 1 must be a string or an object with a string name.");
|
|
4718
|
+
}
|
|
4719
|
+
warnProvideContextDeprecationOnce() {
|
|
4720
|
+
if (this._provideContextDeprecationWarned) return;
|
|
4721
|
+
this._provideContextDeprecationWarned = true;
|
|
4722
|
+
console.warn("[BrowserMcpServer] navigator.modelContext.provideContext() is deprecated and will be removed in the next major version. Register tools individually with registerTool() instead.");
|
|
4723
|
+
}
|
|
4724
|
+
warnClearContextDeprecationOnce() {
|
|
4725
|
+
if (this._clearContextDeprecationWarned) return;
|
|
4726
|
+
this._clearContextDeprecationWarned = true;
|
|
4727
|
+
console.warn("[BrowserMcpServer] navigator.modelContext.clearContext() is deprecated and will be removed in the next major version. Unregister individual tools instead.");
|
|
4728
|
+
}
|
|
4729
|
+
warnUnregisterToolDeprecationOnce() {
|
|
4730
|
+
if (this._unregisterToolDeprecationWarned) return;
|
|
4731
|
+
this._unregisterToolDeprecationWarned = true;
|
|
4732
|
+
console.warn("[BrowserMcpServer] navigator.modelContext.unregisterTool() is deprecated. The April 23, 2026 WebMCP draft removed it in favor of registerTool(tool, { signal }) — pass an AbortSignal and abort it to unregister.");
|
|
4563
4733
|
}
|
|
4564
4734
|
listResources() {
|
|
4565
4735
|
return Object.entries(this._parentResources).filter(([, resource]) => resource.enabled).map(([uri, resource]) => ({
|
|
@@ -4609,6 +4779,21 @@ var BrowserMcpServer = class extends McpServer {
|
|
|
4609
4779
|
return item;
|
|
4610
4780
|
});
|
|
4611
4781
|
}
|
|
4782
|
+
getTools() {
|
|
4783
|
+
return this.listTools().map((tool) => {
|
|
4784
|
+
let inputSchema;
|
|
4785
|
+
try {
|
|
4786
|
+
inputSchema = JSON.stringify(tool.inputSchema ?? DEFAULT_INPUT_SCHEMA);
|
|
4787
|
+
} catch {
|
|
4788
|
+
inputSchema = JSON.stringify(DEFAULT_INPUT_SCHEMA);
|
|
4789
|
+
}
|
|
4790
|
+
return {
|
|
4791
|
+
name: tool.name,
|
|
4792
|
+
description: tool.description ?? "",
|
|
4793
|
+
inputSchema
|
|
4794
|
+
};
|
|
4795
|
+
});
|
|
4796
|
+
}
|
|
4612
4797
|
/**
|
|
4613
4798
|
* Override SDK's validateToolInput to handle both Zod schemas and plain JSON Schema.
|
|
4614
4799
|
* Zod schemas use the SDK's safeParseAsync; plain JSON Schema uses PolyfillJsonSchemaValidator.
|
|
@@ -4616,9 +4801,9 @@ var BrowserMcpServer = class extends McpServer {
|
|
|
4616
4801
|
async validateToolInput(tool, args, toolName) {
|
|
4617
4802
|
if (!tool.inputSchema) return void 0;
|
|
4618
4803
|
if (this.isZodSchema(tool.inputSchema)) {
|
|
4619
|
-
const result
|
|
4620
|
-
if (!result
|
|
4621
|
-
return result
|
|
4804
|
+
const result = await safeParseAsync(tool.inputSchema, args ?? {});
|
|
4805
|
+
if (!result.success) throw new Error(`Invalid arguments for tool ${toolName}: ${getParseErrorMessage(result.error)}`);
|
|
4806
|
+
return result.data;
|
|
4622
4807
|
}
|
|
4623
4808
|
const result = this._jsonValidator.getValidator(tool.inputSchema)(args ?? {});
|
|
4624
4809
|
if (!result.valid) throw new Error(`Invalid arguments for tool ${toolName}: ${result.errorMessage}`);
|