@mcp-b/webmcp-ts-sdk 2.2.0 → 2.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/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$1 = schema._zod?.def;
109
- if (def$1) {
110
- if (def$1.value !== void 0) return def$1.value;
111
- if (Array.isArray(def$1.values) && def$1.values.length > 0) return def$1.values[0];
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(JSONRPC_VERSION),
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(JSONRPC_VERSION),
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(JSONRPC_VERSION),
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$1) {
232
- ErrorCode$1[ErrorCode$1["ConnectionClosed"] = -32e3] = "ConnectionClosed";
233
- ErrorCode$1[ErrorCode$1["RequestTimeout"] = -32001] = "RequestTimeout";
234
- ErrorCode$1[ErrorCode$1["ParseError"] = -32700] = "ParseError";
235
- ErrorCode$1[ErrorCode$1["InvalidRequest"] = -32600] = "InvalidRequest";
236
- ErrorCode$1[ErrorCode$1["MethodNotFound"] = -32601] = "MethodNotFound";
237
- ErrorCode$1[ErrorCode$1["InvalidParams"] = -32602] = "InvalidParams";
238
- ErrorCode$1[ErrorCode$1["InternalError"] = -32603] = "InternalError";
239
- ErrorCode$1[ErrorCode$1["UrlElicitationRequired"] = -32042] = "UrlElicitationRequired";
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(JSONRPC_VERSION),
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,...params } = notification.params;
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$1 = await this.getTask({ taskId }, options);
1837
+ const task = await this.getTask({ taskId }, options);
1838
1838
  yield {
1839
1839
  type: "taskStatus",
1840
- task: task$1
1840
+ task
1841
1841
  };
1842
- if (isTerminal(task$1.status)) {
1843
- if (task$1.status === "completed") yield {
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$1.status === "failed") yield {
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$1.status === "cancelled") yield {
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$1.status === "input_required") {
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$1.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
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 ?? DEFAULT_REQUEST_TIMEOUT_MSEC;
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$1 = {
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$1,
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$1 = {
2062
+ let jsonrpcNotification = {
2063
2063
  ...notification,
2064
2064
  jsonrpc: "2.0"
2065
2065
  };
2066
- if (options?.relatedTask) jsonrpcNotification$1 = {
2067
- ...jsonrpcNotification$1,
2066
+ if (options?.relatedTask) jsonrpcNotification = {
2067
+ ...jsonrpcNotification,
2068
2068
  params: {
2069
- ...jsonrpcNotification$1.params,
2069
+ ...jsonrpcNotification.params,
2070
2070
  _meta: {
2071
- ...jsonrpcNotification$1.params?._meta || {},
2071
+ ...jsonrpcNotification.params?._meta || {},
2072
2072
  [RELATED_TASK_META_KEY]: options.relatedTask
2073
2073
  }
2074
2074
  }
2075
2075
  };
2076
- this._transport?.send(jsonrpcNotification$1, options).catch((error) => this._onerror(error));
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 ajv_default({
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$1) {
3567
- McpZodTypeKind$1["Completable"] = "McpCompletable";
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
- * This server IS navigator.modelContext — it implements the WebMCP standard API
4380
- * (provideContext, registerTool, unregisterTool, clearContext) while retaining
4381
- * full MCP protocol capabilities (resources, prompts, elicitation, sampling)
4382
- * via the inherited BaseMcpServer surface.
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, all tool operations are mirrored to it so that
4385
- * navigator.modelContextTesting (polyfill testing shim) stays in sync.
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;
@@ -4392,6 +4407,10 @@ var BrowserMcpServer = class extends McpServer {
4392
4407
  _publicMethodsBound = false;
4393
4408
  _provideContextDeprecationWarned = false;
4394
4409
  _clearContextDeprecationWarned = false;
4410
+ _unregisterToolDeprecationWarned = false;
4411
+ _nativeToolCleanups = /* @__PURE__ */ new Map();
4412
+ _producerEventTarget = new EventTarget();
4413
+ _ontoolchange = null;
4395
4414
  constructor(serverInfo, options) {
4396
4415
  const validator = new PolyfillJsonSchemaValidator();
4397
4416
  const enhancedOptions = {
@@ -4418,8 +4437,12 @@ var BrowserMcpServer = class extends McpServer {
4418
4437
  this.provideContext = this.provideContext.bind(this);
4419
4438
  this.clearContext = this.clearContext.bind(this);
4420
4439
  this.listTools = this.listTools.bind(this);
4440
+ this.getTools = this.getTools.bind(this);
4421
4441
  this.callTool = this.callTool.bind(this);
4422
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);
4423
4446
  this.registerResource = this.registerResource.bind(this);
4424
4447
  this.listResources = this.listResources.bind(this);
4425
4448
  this.readResource = this.readResource.bind(this);
@@ -4480,6 +4503,71 @@ var BrowserMcpServer = class extends McpServer {
4480
4503
  if (typeof candidate.listTools !== "function" || typeof candidate.callTool !== "function") return;
4481
4504
  return candidate;
4482
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
+ }
4483
4571
  registerToolInServer(tool) {
4484
4572
  const inputSchema = this.toTransportSchema(tool.inputSchema);
4485
4573
  super.registerTool(tool.name, {
@@ -4490,8 +4578,35 @@ var BrowserMcpServer = class extends McpServer {
4490
4578
  }, async (args) => {
4491
4579
  return normalizeToolResponse(await tool.execute(args, { requestUserInteraction: async (cb) => cb() }));
4492
4580
  });
4581
+ this.notifyProducerToolsChanged();
4493
4582
  return { unregister: () => this.unregisterTool(tool.name) };
4494
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
+ }
4495
4610
  backfillTools(tools, execute) {
4496
4611
  let synced = 0;
4497
4612
  for (const sourceTool of tools) {
@@ -4509,18 +4624,34 @@ var BrowserMcpServer = class extends McpServer {
4509
4624
  }
4510
4625
  return synced;
4511
4626
  }
4512
- registerTool(tool) {
4513
- if (this.native) this.native.registerTool(tool);
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;
4514
4635
  try {
4515
- return this.registerToolInServer(tool);
4636
+ handle = this.registerToolInServer(tool);
4516
4637
  } catch (error) {
4517
4638
  if (this.native) try {
4518
- this.native.unregisterTool(tool.name);
4639
+ this.unregisterNativeToolMirror(tool.name);
4519
4640
  } catch (rollbackError) {
4520
4641
  console.error("[BrowserMcpServer] Rollback of native tool registration failed:", rollbackError);
4521
4642
  }
4522
4643
  throw error;
4523
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;
4524
4655
  }
4525
4656
  /**
4526
4657
  * Backfill tools that were already registered on the native/polyfill context
@@ -4536,12 +4667,25 @@ var BrowserMcpServer = class extends McpServer {
4536
4667
  }));
4537
4668
  }
4538
4669
  unregisterTool(nameOrTool) {
4670
+ this.warnUnregisterToolDeprecationOnce();
4539
4671
  const name = this.resolveToolNameForUnregister(nameOrTool);
4540
- this._parentTools[name]?.remove();
4541
- if (this.native) this.native.unregisterTool(name);
4672
+ const existing = this._parentTools[name];
4673
+ existing?.remove();
4674
+ if (this.native) this.unregisterNativeToolMirror(name);
4675
+ if (existing) this.notifyProducerToolsChanged();
4542
4676
  }
4543
4677
  clearRegisteredTools() {
4544
- for (const name of Object.keys(this._parentTools)) this.unregisterTool(name);
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();
4545
4689
  }
4546
4690
  registerResource(descriptor) {
4547
4691
  const registered = super.registerResource(descriptor.name, descriptor.uri, {
@@ -4582,6 +4726,11 @@ var BrowserMcpServer = class extends McpServer {
4582
4726
  this._clearContextDeprecationWarned = true;
4583
4727
  console.warn("[BrowserMcpServer] navigator.modelContext.clearContext() is deprecated and will be removed in the next major version. Unregister individual tools instead.");
4584
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.");
4733
+ }
4585
4734
  listResources() {
4586
4735
  return Object.entries(this._parentResources).filter(([, resource]) => resource.enabled).map(([uri, resource]) => ({
4587
4736
  uri,
@@ -4630,6 +4779,21 @@ var BrowserMcpServer = class extends McpServer {
4630
4779
  return item;
4631
4780
  });
4632
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
+ }
4633
4797
  /**
4634
4798
  * Override SDK's validateToolInput to handle both Zod schemas and plain JSON Schema.
4635
4799
  * Zod schemas use the SDK's safeParseAsync; plain JSON Schema uses PolyfillJsonSchemaValidator.
@@ -4637,9 +4801,9 @@ var BrowserMcpServer = class extends McpServer {
4637
4801
  async validateToolInput(tool, args, toolName) {
4638
4802
  if (!tool.inputSchema) return void 0;
4639
4803
  if (this.isZodSchema(tool.inputSchema)) {
4640
- const result$1 = await safeParseAsync(tool.inputSchema, args ?? {});
4641
- if (!result$1.success) throw new Error(`Invalid arguments for tool ${toolName}: ${getParseErrorMessage(result$1.error)}`);
4642
- return result$1.data;
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;
4643
4807
  }
4644
4808
  const result = this._jsonValidator.getValidator(tool.inputSchema)(args ?? {});
4645
4809
  if (!result.valid) throw new Error(`Invalid arguments for tool ${toolName}: ${result.errorMessage}`);