@mcp-b/global 1.1.1 → 1.1.3-beta.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
@@ -1,5 +1,5 @@
1
1
  import { IframeChildTransport, TabServerTransport } from "@mcp-b/transports";
2
- import { CallToolRequestSchema, ListToolsRequestSchema, Server } from "@mcp-b/webmcp-ts-sdk";
2
+ import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, Server } from "@mcp-b/webmcp-ts-sdk";
3
3
  import { jsonSchemaToZod } from "@composio/json-schema-to-zod";
4
4
  import { z } from "zod";
5
5
 
@@ -326,6 +326,75 @@ var NativeModelContextAdapter = class {
326
326
  }));
327
327
  }
328
328
  /**
329
+ * Registers a resource dynamically.
330
+ * Note: Native Chromium API does not yet support resources.
331
+ * This is a polyfill-only feature.
332
+ */
333
+ registerResource(_resource) {
334
+ console.warn("[Native Adapter] registerResource is not supported by native API");
335
+ return { unregister: () => {} };
336
+ }
337
+ /**
338
+ * Unregisters a resource by URI.
339
+ * Note: Native Chromium API does not yet support resources.
340
+ */
341
+ unregisterResource(_uri) {
342
+ console.warn("[Native Adapter] unregisterResource is not supported by native API");
343
+ }
344
+ /**
345
+ * Lists all registered resources.
346
+ * Note: Native Chromium API does not yet support resources.
347
+ */
348
+ listResources() {
349
+ return [];
350
+ }
351
+ /**
352
+ * Lists all resource templates.
353
+ * Note: Native Chromium API does not yet support resources.
354
+ */
355
+ listResourceTemplates() {
356
+ return [];
357
+ }
358
+ /**
359
+ * Reads a resource by URI.
360
+ * Note: Native Chromium API does not yet support resources.
361
+ * @internal
362
+ */
363
+ async readResource(_uri) {
364
+ throw new Error("[Native Adapter] readResource is not supported by native API");
365
+ }
366
+ /**
367
+ * Registers a prompt dynamically.
368
+ * Note: Native Chromium API does not yet support prompts.
369
+ * This is a polyfill-only feature.
370
+ */
371
+ registerPrompt(_prompt) {
372
+ console.warn("[Native Adapter] registerPrompt is not supported by native API");
373
+ return { unregister: () => {} };
374
+ }
375
+ /**
376
+ * Unregisters a prompt by name.
377
+ * Note: Native Chromium API does not yet support prompts.
378
+ */
379
+ unregisterPrompt(_name) {
380
+ console.warn("[Native Adapter] unregisterPrompt is not supported by native API");
381
+ }
382
+ /**
383
+ * Lists all registered prompts.
384
+ * Note: Native Chromium API does not yet support prompts.
385
+ */
386
+ listPrompts() {
387
+ return [];
388
+ }
389
+ /**
390
+ * Gets a prompt with arguments.
391
+ * Note: Native Chromium API does not yet support prompts.
392
+ * @internal
393
+ */
394
+ async getPrompt(_name, _args) {
395
+ throw new Error("[Native Adapter] getPrompt is not supported by native API");
396
+ }
397
+ /**
329
398
  * Adds an event listener for tool call events.
330
399
  * Delegates to the native API's addEventListener.
331
400
  *
@@ -357,6 +426,28 @@ var NativeModelContextAdapter = class {
357
426
  dispatchEvent(event) {
358
427
  return this.nativeContext.dispatchEvent(event);
359
428
  }
429
+ /**
430
+ * Request an LLM completion from the connected client.
431
+ * Note: Native Chromium API does not yet support sampling.
432
+ * This is handled by the polyfill.
433
+ */
434
+ async createMessage(params) {
435
+ console.log("[Native Adapter] Requesting sampling from client");
436
+ const underlyingServer = this.bridge.tabServer.server;
437
+ if (!underlyingServer?.createMessage) throw new Error("Sampling is not supported: no connected client with sampling capability");
438
+ return underlyingServer.createMessage(params);
439
+ }
440
+ /**
441
+ * Request user input from the connected client.
442
+ * Note: Native Chromium API does not yet support elicitation.
443
+ * This is handled by the polyfill.
444
+ */
445
+ async elicitInput(params) {
446
+ console.log("[Native Adapter] Requesting elicitation from client");
447
+ const underlyingServer = this.bridge.tabServer.server;
448
+ if (!underlyingServer?.elicitInput) throw new Error("Elicitation is not supported: no connected client with elicitation capability");
449
+ return underlyingServer.elicitInput(params);
450
+ }
360
451
  };
361
452
  /**
362
453
  * ToolCallEvent implementation for the Web Model Context API.
@@ -612,8 +703,16 @@ var WebModelContext = class {
612
703
  eventTarget;
613
704
  provideContextTools;
614
705
  dynamicTools;
615
- registrationTimestamps;
616
- unregisterFunctions;
706
+ provideContextResources;
707
+ dynamicResources;
708
+ provideContextPrompts;
709
+ dynamicPrompts;
710
+ toolRegistrationTimestamps;
711
+ resourceRegistrationTimestamps;
712
+ promptRegistrationTimestamps;
713
+ toolUnregisterFunctions;
714
+ resourceUnregisterFunctions;
715
+ promptUnregisterFunctions;
617
716
  testingAPI;
618
717
  /**
619
718
  * Creates a new WebModelContext instance.
@@ -625,8 +724,16 @@ var WebModelContext = class {
625
724
  this.eventTarget = new EventTarget();
626
725
  this.provideContextTools = /* @__PURE__ */ new Map();
627
726
  this.dynamicTools = /* @__PURE__ */ new Map();
628
- this.registrationTimestamps = /* @__PURE__ */ new Map();
629
- this.unregisterFunctions = /* @__PURE__ */ new Map();
727
+ this.toolRegistrationTimestamps = /* @__PURE__ */ new Map();
728
+ this.toolUnregisterFunctions = /* @__PURE__ */ new Map();
729
+ this.provideContextResources = /* @__PURE__ */ new Map();
730
+ this.dynamicResources = /* @__PURE__ */ new Map();
731
+ this.resourceRegistrationTimestamps = /* @__PURE__ */ new Map();
732
+ this.resourceUnregisterFunctions = /* @__PURE__ */ new Map();
733
+ this.provideContextPrompts = /* @__PURE__ */ new Map();
734
+ this.dynamicPrompts = /* @__PURE__ */ new Map();
735
+ this.promptRegistrationTimestamps = /* @__PURE__ */ new Map();
736
+ this.promptUnregisterFunctions = /* @__PURE__ */ new Map();
630
737
  }
631
738
  /**
632
739
  * Sets the testing API instance.
@@ -668,17 +775,22 @@ var WebModelContext = class {
668
775
  return this.eventTarget.dispatchEvent(event);
669
776
  }
670
777
  /**
671
- * Provides context (tools) to AI models by registering base tools (Bucket A).
672
- * Clears and replaces all previously registered base tools while preserving
673
- * dynamic tools registered via registerTool().
778
+ * Provides context (tools, resources, prompts) to AI models by registering base items (Bucket A).
779
+ * Clears and replaces all previously registered base items while preserving
780
+ * dynamic items registered via register* methods.
674
781
  *
675
- * @param {ModelContextInput} context - Context containing tools to register
676
- * @throws {Error} If a tool name collides with an existing dynamic tool
782
+ * @param {ModelContextInput} context - Context containing tools, resources, and prompts to register
783
+ * @throws {Error} If a name/uri collides with existing dynamic items
677
784
  */
678
785
  provideContext(context) {
679
- console.log(`[Web Model Context] Registering ${context.tools.length} tools via provideContext`);
786
+ const toolCount = context.tools?.length ?? 0;
787
+ const resourceCount = context.resources?.length ?? 0;
788
+ const promptCount = context.prompts?.length ?? 0;
789
+ console.log(`[Web Model Context] provideContext: ${toolCount} tools, ${resourceCount} resources, ${promptCount} prompts`);
680
790
  this.provideContextTools.clear();
681
- for (const tool of context.tools) {
791
+ this.provideContextResources.clear();
792
+ this.provideContextPrompts.clear();
793
+ for (const tool of context.tools ?? []) {
682
794
  if (this.dynamicTools.has(tool.name)) throw new Error(`[Web Model Context] Tool name collision: "${tool.name}" is already registered via registerTool(). Please use a different name or unregister the dynamic tool first.`);
683
795
  const { jsonSchema: inputJson, zodValidator: inputZod } = normalizeSchema(tool.inputSchema);
684
796
  const normalizedOutput = tool.outputSchema ? normalizeSchema(tool.outputSchema) : null;
@@ -694,8 +806,63 @@ var WebModelContext = class {
694
806
  };
695
807
  this.provideContextTools.set(tool.name, validatedTool);
696
808
  }
809
+ for (const resource of context.resources ?? []) {
810
+ if (this.dynamicResources.has(resource.uri)) throw new Error(`[Web Model Context] Resource URI collision: "${resource.uri}" is already registered via registerResource(). Please use a different URI or unregister the dynamic resource first.`);
811
+ const validatedResource = this.validateResource(resource);
812
+ this.provideContextResources.set(resource.uri, validatedResource);
813
+ }
814
+ for (const prompt of context.prompts ?? []) {
815
+ if (this.dynamicPrompts.has(prompt.name)) throw new Error(`[Web Model Context] Prompt name collision: "${prompt.name}" is already registered via registerPrompt(). Please use a different name or unregister the dynamic prompt first.`);
816
+ const validatedPrompt = this.validatePrompt(prompt);
817
+ this.provideContextPrompts.set(prompt.name, validatedPrompt);
818
+ }
697
819
  this.updateBridgeTools();
820
+ this.updateBridgeResources();
821
+ this.updateBridgePrompts();
698
822
  this.notifyToolsListChanged();
823
+ this.notifyResourcesListChanged();
824
+ this.notifyPromptsListChanged();
825
+ }
826
+ /**
827
+ * Validates and normalizes a resource descriptor.
828
+ * @private
829
+ */
830
+ validateResource(resource) {
831
+ const templateParamRegex = /\{([^}]+)\}/g;
832
+ const templateParams = [];
833
+ for (const match of resource.uri.matchAll(templateParamRegex)) {
834
+ const paramName = match[1];
835
+ if (paramName) templateParams.push(paramName);
836
+ }
837
+ return {
838
+ uri: resource.uri,
839
+ name: resource.name,
840
+ description: resource.description,
841
+ mimeType: resource.mimeType,
842
+ read: resource.read,
843
+ isTemplate: templateParams.length > 0,
844
+ templateParams
845
+ };
846
+ }
847
+ /**
848
+ * Validates and normalizes a prompt descriptor.
849
+ * @private
850
+ */
851
+ validatePrompt(prompt) {
852
+ let argsSchema;
853
+ let argsValidator;
854
+ if (prompt.argsSchema) {
855
+ const normalized = normalizeSchema(prompt.argsSchema);
856
+ argsSchema = normalized.jsonSchema;
857
+ argsValidator = normalized.zodValidator;
858
+ }
859
+ return {
860
+ name: prompt.name,
861
+ description: prompt.description,
862
+ argsSchema,
863
+ get: prompt.get,
864
+ argsValidator
865
+ };
699
866
  }
700
867
  /**
701
868
  * Registers a single tool dynamically (Bucket B).
@@ -708,10 +875,10 @@ var WebModelContext = class {
708
875
  registerTool(tool) {
709
876
  console.log(`[Web Model Context] Registering tool dynamically: ${tool.name}`);
710
877
  const now = Date.now();
711
- const lastRegistration = this.registrationTimestamps.get(tool.name);
878
+ const lastRegistration = this.toolRegistrationTimestamps.get(tool.name);
712
879
  if (lastRegistration && now - lastRegistration < RAPID_DUPLICATE_WINDOW_MS) {
713
880
  console.warn(`[Web Model Context] Tool "${tool.name}" registered multiple times within ${RAPID_DUPLICATE_WINDOW_MS}ms. This is likely due to React Strict Mode double-mounting. Ignoring duplicate registration.`);
714
- const existingUnregister = this.unregisterFunctions.get(tool.name);
881
+ const existingUnregister = this.toolUnregisterFunctions.get(tool.name);
715
882
  if (existingUnregister) return { unregister: existingUnregister };
716
883
  }
717
884
  if (this.provideContextTools.has(tool.name)) throw new Error(`[Web Model Context] Tool name collision: "${tool.name}" is already registered via provideContext(). Please use a different name or update your provideContext() call.`);
@@ -729,7 +896,7 @@ var WebModelContext = class {
729
896
  ...normalizedOutput && { outputValidator: normalizedOutput.zodValidator }
730
897
  };
731
898
  this.dynamicTools.set(tool.name, validatedTool);
732
- this.registrationTimestamps.set(tool.name, now);
899
+ this.toolRegistrationTimestamps.set(tool.name, now);
733
900
  this.updateBridgeTools();
734
901
  this.notifyToolsListChanged();
735
902
  const unregisterFn = () => {
@@ -740,15 +907,186 @@ var WebModelContext = class {
740
907
  return;
741
908
  }
742
909
  this.dynamicTools.delete(tool.name);
743
- this.registrationTimestamps.delete(tool.name);
744
- this.unregisterFunctions.delete(tool.name);
910
+ this.toolRegistrationTimestamps.delete(tool.name);
911
+ this.toolUnregisterFunctions.delete(tool.name);
745
912
  this.updateBridgeTools();
746
913
  this.notifyToolsListChanged();
747
914
  };
748
- this.unregisterFunctions.set(tool.name, unregisterFn);
915
+ this.toolUnregisterFunctions.set(tool.name, unregisterFn);
916
+ return { unregister: unregisterFn };
917
+ }
918
+ /**
919
+ * Registers a single resource dynamically (Bucket B).
920
+ * Dynamic resources persist across provideContext() calls and can be independently managed.
921
+ *
922
+ * @param {ResourceDescriptor} resource - The resource descriptor to register
923
+ * @returns {{unregister: () => void}} Object with unregister function
924
+ * @throws {Error} If resource URI collides with existing resources
925
+ */
926
+ registerResource(resource) {
927
+ console.log(`[Web Model Context] Registering resource dynamically: ${resource.uri}`);
928
+ const now = Date.now();
929
+ const lastRegistration = this.resourceRegistrationTimestamps.get(resource.uri);
930
+ if (lastRegistration && now - lastRegistration < RAPID_DUPLICATE_WINDOW_MS) {
931
+ console.warn(`[Web Model Context] Resource "${resource.uri}" registered multiple times within ${RAPID_DUPLICATE_WINDOW_MS}ms. This is likely due to React Strict Mode double-mounting. Ignoring duplicate registration.`);
932
+ const existingUnregister = this.resourceUnregisterFunctions.get(resource.uri);
933
+ if (existingUnregister) return { unregister: existingUnregister };
934
+ }
935
+ if (this.provideContextResources.has(resource.uri)) throw new Error(`[Web Model Context] Resource URI collision: "${resource.uri}" is already registered via provideContext(). Please use a different URI or update your provideContext() call.`);
936
+ if (this.dynamicResources.has(resource.uri)) throw new Error(`[Web Model Context] Resource URI collision: "${resource.uri}" is already registered via registerResource(). Please unregister it first or use a different URI.`);
937
+ const validatedResource = this.validateResource(resource);
938
+ this.dynamicResources.set(resource.uri, validatedResource);
939
+ this.resourceRegistrationTimestamps.set(resource.uri, now);
940
+ this.updateBridgeResources();
941
+ this.notifyResourcesListChanged();
942
+ const unregisterFn = () => {
943
+ console.log(`[Web Model Context] Unregistering resource: ${resource.uri}`);
944
+ if (this.provideContextResources.has(resource.uri)) throw new Error(`[Web Model Context] Cannot unregister resource "${resource.uri}": This resource was registered via provideContext(). Use provideContext() to update the base resource set.`);
945
+ if (!this.dynamicResources.has(resource.uri)) {
946
+ console.warn(`[Web Model Context] Resource "${resource.uri}" is not registered, ignoring unregister call`);
947
+ return;
948
+ }
949
+ this.dynamicResources.delete(resource.uri);
950
+ this.resourceRegistrationTimestamps.delete(resource.uri);
951
+ this.resourceUnregisterFunctions.delete(resource.uri);
952
+ this.updateBridgeResources();
953
+ this.notifyResourcesListChanged();
954
+ };
955
+ this.resourceUnregisterFunctions.set(resource.uri, unregisterFn);
956
+ return { unregister: unregisterFn };
957
+ }
958
+ /**
959
+ * Unregisters a resource by URI.
960
+ * Can unregister resources from either Bucket A (provideContext) or Bucket B (registerResource).
961
+ *
962
+ * @param {string} uri - URI of the resource to unregister
963
+ */
964
+ unregisterResource(uri) {
965
+ console.log(`[Web Model Context] Unregistering resource: ${uri}`);
966
+ const inProvideContext = this.provideContextResources.has(uri);
967
+ const inDynamic = this.dynamicResources.has(uri);
968
+ if (!inProvideContext && !inDynamic) {
969
+ console.warn(`[Web Model Context] Resource "${uri}" is not registered, ignoring unregister call`);
970
+ return;
971
+ }
972
+ if (inProvideContext) this.provideContextResources.delete(uri);
973
+ if (inDynamic) {
974
+ this.dynamicResources.delete(uri);
975
+ this.resourceRegistrationTimestamps.delete(uri);
976
+ this.resourceUnregisterFunctions.delete(uri);
977
+ }
978
+ this.updateBridgeResources();
979
+ this.notifyResourcesListChanged();
980
+ }
981
+ /**
982
+ * Lists all registered resources in MCP format.
983
+ * Returns static resources from both buckets (not templates).
984
+ *
985
+ * @returns {Resource[]} Array of resource descriptors
986
+ */
987
+ listResources() {
988
+ return Array.from(this.bridge.resources.values()).filter((r) => !r.isTemplate).map((resource) => ({
989
+ uri: resource.uri,
990
+ name: resource.name,
991
+ description: resource.description,
992
+ mimeType: resource.mimeType
993
+ }));
994
+ }
995
+ /**
996
+ * Lists all registered resource templates.
997
+ * Returns only resources with URI templates (dynamic resources).
998
+ *
999
+ * @returns {Array<{uriTemplate: string, name: string, description?: string, mimeType?: string}>}
1000
+ */
1001
+ listResourceTemplates() {
1002
+ return Array.from(this.bridge.resources.values()).filter((r) => r.isTemplate).map((resource) => ({
1003
+ uriTemplate: resource.uri,
1004
+ name: resource.name,
1005
+ ...resource.description !== void 0 && { description: resource.description },
1006
+ ...resource.mimeType !== void 0 && { mimeType: resource.mimeType }
1007
+ }));
1008
+ }
1009
+ /**
1010
+ * Registers a single prompt dynamically (Bucket B).
1011
+ * Dynamic prompts persist across provideContext() calls and can be independently managed.
1012
+ *
1013
+ * @param {PromptDescriptor} prompt - The prompt descriptor to register
1014
+ * @returns {{unregister: () => void}} Object with unregister function
1015
+ * @throws {Error} If prompt name collides with existing prompts
1016
+ */
1017
+ registerPrompt(prompt) {
1018
+ console.log(`[Web Model Context] Registering prompt dynamically: ${prompt.name}`);
1019
+ const now = Date.now();
1020
+ const lastRegistration = this.promptRegistrationTimestamps.get(prompt.name);
1021
+ if (lastRegistration && now - lastRegistration < RAPID_DUPLICATE_WINDOW_MS) {
1022
+ console.warn(`[Web Model Context] Prompt "${prompt.name}" registered multiple times within ${RAPID_DUPLICATE_WINDOW_MS}ms. This is likely due to React Strict Mode double-mounting. Ignoring duplicate registration.`);
1023
+ const existingUnregister = this.promptUnregisterFunctions.get(prompt.name);
1024
+ if (existingUnregister) return { unregister: existingUnregister };
1025
+ }
1026
+ if (this.provideContextPrompts.has(prompt.name)) throw new Error(`[Web Model Context] Prompt name collision: "${prompt.name}" is already registered via provideContext(). Please use a different name or update your provideContext() call.`);
1027
+ if (this.dynamicPrompts.has(prompt.name)) throw new Error(`[Web Model Context] Prompt name collision: "${prompt.name}" is already registered via registerPrompt(). Please unregister it first or use a different name.`);
1028
+ const validatedPrompt = this.validatePrompt(prompt);
1029
+ this.dynamicPrompts.set(prompt.name, validatedPrompt);
1030
+ this.promptRegistrationTimestamps.set(prompt.name, now);
1031
+ this.updateBridgePrompts();
1032
+ this.notifyPromptsListChanged();
1033
+ const unregisterFn = () => {
1034
+ console.log(`[Web Model Context] Unregistering prompt: ${prompt.name}`);
1035
+ if (this.provideContextPrompts.has(prompt.name)) throw new Error(`[Web Model Context] Cannot unregister prompt "${prompt.name}": This prompt was registered via provideContext(). Use provideContext() to update the base prompt set.`);
1036
+ if (!this.dynamicPrompts.has(prompt.name)) {
1037
+ console.warn(`[Web Model Context] Prompt "${prompt.name}" is not registered, ignoring unregister call`);
1038
+ return;
1039
+ }
1040
+ this.dynamicPrompts.delete(prompt.name);
1041
+ this.promptRegistrationTimestamps.delete(prompt.name);
1042
+ this.promptUnregisterFunctions.delete(prompt.name);
1043
+ this.updateBridgePrompts();
1044
+ this.notifyPromptsListChanged();
1045
+ };
1046
+ this.promptUnregisterFunctions.set(prompt.name, unregisterFn);
749
1047
  return { unregister: unregisterFn };
750
1048
  }
751
1049
  /**
1050
+ * Unregisters a prompt by name.
1051
+ * Can unregister prompts from either Bucket A (provideContext) or Bucket B (registerPrompt).
1052
+ *
1053
+ * @param {string} name - Name of the prompt to unregister
1054
+ */
1055
+ unregisterPrompt(name) {
1056
+ console.log(`[Web Model Context] Unregistering prompt: ${name}`);
1057
+ const inProvideContext = this.provideContextPrompts.has(name);
1058
+ const inDynamic = this.dynamicPrompts.has(name);
1059
+ if (!inProvideContext && !inDynamic) {
1060
+ console.warn(`[Web Model Context] Prompt "${name}" is not registered, ignoring unregister call`);
1061
+ return;
1062
+ }
1063
+ if (inProvideContext) this.provideContextPrompts.delete(name);
1064
+ if (inDynamic) {
1065
+ this.dynamicPrompts.delete(name);
1066
+ this.promptRegistrationTimestamps.delete(name);
1067
+ this.promptUnregisterFunctions.delete(name);
1068
+ }
1069
+ this.updateBridgePrompts();
1070
+ this.notifyPromptsListChanged();
1071
+ }
1072
+ /**
1073
+ * Lists all registered prompts in MCP format.
1074
+ * Returns prompts from both buckets.
1075
+ *
1076
+ * @returns {Prompt[]} Array of prompt descriptors
1077
+ */
1078
+ listPrompts() {
1079
+ return Array.from(this.bridge.prompts.values()).map((prompt) => ({
1080
+ name: prompt.name,
1081
+ description: prompt.description,
1082
+ arguments: prompt.argsSchema?.properties ? Object.entries(prompt.argsSchema.properties).map(([name, schema]) => ({
1083
+ name,
1084
+ description: schema.description,
1085
+ required: prompt.argsSchema?.required?.includes(name) ?? false
1086
+ })) : void 0
1087
+ }));
1088
+ }
1089
+ /**
752
1090
  * Unregisters a tool by name (Chromium native API).
753
1091
  * Can unregister tools from either Bucket A (provideContext) or Bucket B (registerTool).
754
1092
  *
@@ -765,24 +1103,36 @@ var WebModelContext = class {
765
1103
  if (inProvideContext) this.provideContextTools.delete(name);
766
1104
  if (inDynamic) {
767
1105
  this.dynamicTools.delete(name);
768
- this.registrationTimestamps.delete(name);
769
- this.unregisterFunctions.delete(name);
1106
+ this.toolRegistrationTimestamps.delete(name);
1107
+ this.toolUnregisterFunctions.delete(name);
770
1108
  }
771
1109
  this.updateBridgeTools();
772
1110
  this.notifyToolsListChanged();
773
1111
  }
774
1112
  /**
775
- * Clears all registered tools from both buckets (Chromium native API).
776
- * Removes all tools registered via provideContext() and registerTool().
1113
+ * Clears all registered context from both buckets (Chromium native API).
1114
+ * Removes all tools, resources, and prompts registered via provideContext() and register* methods.
777
1115
  */
778
1116
  clearContext() {
779
- console.log("[Web Model Context] Clearing all tools");
1117
+ console.log("[Web Model Context] Clearing all context (tools, resources, prompts)");
780
1118
  this.provideContextTools.clear();
781
1119
  this.dynamicTools.clear();
782
- this.registrationTimestamps.clear();
783
- this.unregisterFunctions.clear();
1120
+ this.toolRegistrationTimestamps.clear();
1121
+ this.toolUnregisterFunctions.clear();
1122
+ this.provideContextResources.clear();
1123
+ this.dynamicResources.clear();
1124
+ this.resourceRegistrationTimestamps.clear();
1125
+ this.resourceUnregisterFunctions.clear();
1126
+ this.provideContextPrompts.clear();
1127
+ this.dynamicPrompts.clear();
1128
+ this.promptRegistrationTimestamps.clear();
1129
+ this.promptUnregisterFunctions.clear();
784
1130
  this.updateBridgeTools();
1131
+ this.updateBridgeResources();
1132
+ this.updateBridgePrompts();
785
1133
  this.notifyToolsListChanged();
1134
+ this.notifyResourcesListChanged();
1135
+ this.notifyPromptsListChanged();
786
1136
  }
787
1137
  /**
788
1138
  * Updates the bridge tools map with merged tools from both buckets.
@@ -814,6 +1164,146 @@ var WebModelContext = class {
814
1164
  if (this.testingAPI && "notifyToolsChanged" in this.testingAPI) this.testingAPI.notifyToolsChanged();
815
1165
  }
816
1166
  /**
1167
+ * Updates the bridge resources map with merged resources from both buckets.
1168
+ *
1169
+ * @private
1170
+ */
1171
+ updateBridgeResources() {
1172
+ this.bridge.resources.clear();
1173
+ for (const [uri, resource] of this.provideContextResources) this.bridge.resources.set(uri, resource);
1174
+ for (const [uri, resource] of this.dynamicResources) this.bridge.resources.set(uri, resource);
1175
+ console.log(`[Web Model Context] Updated bridge with ${this.provideContextResources.size} base resources + ${this.dynamicResources.size} dynamic resources = ${this.bridge.resources.size} total`);
1176
+ }
1177
+ /**
1178
+ * Notifies all servers that the resources list has changed.
1179
+ *
1180
+ * @private
1181
+ */
1182
+ notifyResourcesListChanged() {
1183
+ if (this.bridge.tabServer.notification) this.bridge.tabServer.notification({
1184
+ method: "notifications/resources/list_changed",
1185
+ params: {}
1186
+ });
1187
+ if (this.bridge.iframeServer?.notification) this.bridge.iframeServer.notification({
1188
+ method: "notifications/resources/list_changed",
1189
+ params: {}
1190
+ });
1191
+ }
1192
+ /**
1193
+ * Updates the bridge prompts map with merged prompts from both buckets.
1194
+ *
1195
+ * @private
1196
+ */
1197
+ updateBridgePrompts() {
1198
+ this.bridge.prompts.clear();
1199
+ for (const [name, prompt] of this.provideContextPrompts) this.bridge.prompts.set(name, prompt);
1200
+ for (const [name, prompt] of this.dynamicPrompts) this.bridge.prompts.set(name, prompt);
1201
+ console.log(`[Web Model Context] Updated bridge with ${this.provideContextPrompts.size} base prompts + ${this.dynamicPrompts.size} dynamic prompts = ${this.bridge.prompts.size} total`);
1202
+ }
1203
+ /**
1204
+ * Notifies all servers that the prompts list has changed.
1205
+ *
1206
+ * @private
1207
+ */
1208
+ notifyPromptsListChanged() {
1209
+ if (this.bridge.tabServer.notification) this.bridge.tabServer.notification({
1210
+ method: "notifications/prompts/list_changed",
1211
+ params: {}
1212
+ });
1213
+ if (this.bridge.iframeServer?.notification) this.bridge.iframeServer.notification({
1214
+ method: "notifications/prompts/list_changed",
1215
+ params: {}
1216
+ });
1217
+ }
1218
+ /**
1219
+ * Reads a resource by URI (internal use only by MCP bridge).
1220
+ * Handles both static resources and URI templates.
1221
+ *
1222
+ * @param {string} uri - The URI of the resource to read
1223
+ * @returns {Promise<{contents: ResourceContents[]}>} The resource contents
1224
+ * @throws {Error} If resource is not found
1225
+ * @internal
1226
+ */
1227
+ async readResource(uri) {
1228
+ console.log(`[Web Model Context] Reading resource: ${uri}`);
1229
+ const staticResource = this.bridge.resources.get(uri);
1230
+ if (staticResource && !staticResource.isTemplate) try {
1231
+ const parsedUri = new URL(uri);
1232
+ return await staticResource.read(parsedUri);
1233
+ } catch (error) {
1234
+ console.error(`[Web Model Context] Error reading resource ${uri}:`, error);
1235
+ throw error;
1236
+ }
1237
+ for (const resource of this.bridge.resources.values()) {
1238
+ if (!resource.isTemplate) continue;
1239
+ const params = this.matchUriTemplate(resource.uri, uri);
1240
+ if (params) try {
1241
+ const parsedUri = new URL(uri);
1242
+ return await resource.read(parsedUri, params);
1243
+ } catch (error) {
1244
+ console.error(`[Web Model Context] Error reading resource ${uri}:`, error);
1245
+ throw error;
1246
+ }
1247
+ }
1248
+ throw new Error(`Resource not found: ${uri}`);
1249
+ }
1250
+ /**
1251
+ * Matches a URI against a URI template and extracts parameters.
1252
+ *
1253
+ * @param {string} template - The URI template (e.g., "file://{path}")
1254
+ * @param {string} uri - The actual URI to match
1255
+ * @returns {Record<string, string> | null} Extracted parameters or null if no match
1256
+ * @private
1257
+ */
1258
+ matchUriTemplate(template, uri) {
1259
+ const paramNames = [];
1260
+ let regexPattern = template.replace(/[.*+?^${}()|[\]\\]/g, (char) => {
1261
+ if (char === "{" || char === "}") return char;
1262
+ return `\\${char}`;
1263
+ });
1264
+ regexPattern = regexPattern.replace(/\{([^}]+)\}/g, (_, paramName) => {
1265
+ paramNames.push(paramName);
1266
+ return "(.+)";
1267
+ });
1268
+ const regex = /* @__PURE__ */ new RegExp(`^${regexPattern}$`);
1269
+ const match = uri.match(regex);
1270
+ if (!match) return null;
1271
+ const params = {};
1272
+ for (let i = 0; i < paramNames.length; i++) {
1273
+ const paramName = paramNames[i];
1274
+ const paramValue = match[i + 1];
1275
+ if (paramName !== void 0 && paramValue !== void 0) params[paramName] = paramValue;
1276
+ }
1277
+ return params;
1278
+ }
1279
+ /**
1280
+ * Gets a prompt with arguments (internal use only by MCP bridge).
1281
+ *
1282
+ * @param {string} name - Name of the prompt
1283
+ * @param {Record<string, unknown>} args - Arguments to pass to the prompt
1284
+ * @returns {Promise<{messages: PromptMessage[]}>} The prompt messages
1285
+ * @throws {Error} If prompt is not found
1286
+ * @internal
1287
+ */
1288
+ async getPrompt(name, args) {
1289
+ console.log(`[Web Model Context] Getting prompt: ${name}`);
1290
+ const prompt = this.bridge.prompts.get(name);
1291
+ if (!prompt) throw new Error(`Prompt not found: ${name}`);
1292
+ if (prompt.argsValidator && args) {
1293
+ const validation = validateWithZod(args, prompt.argsValidator);
1294
+ if (!validation.success) {
1295
+ console.error(`[Web Model Context] Argument validation failed for prompt ${name}:`, validation.error);
1296
+ throw new Error(`Argument validation error for prompt "${name}":\n${validation.error}`);
1297
+ }
1298
+ }
1299
+ try {
1300
+ return await prompt.get(args ?? {});
1301
+ } catch (error) {
1302
+ console.error(`[Web Model Context] Error getting prompt ${name}:`, error);
1303
+ throw error;
1304
+ }
1305
+ }
1306
+ /**
817
1307
  * Executes a tool with validation and event dispatch.
818
1308
  * Follows this sequence:
819
1309
  * 1. Validates input arguments against schema
@@ -897,6 +1387,32 @@ var WebModelContext = class {
897
1387
  ...tool.annotations && { annotations: tool.annotations }
898
1388
  }));
899
1389
  }
1390
+ /**
1391
+ * Request an LLM completion from the connected client.
1392
+ * This sends a sampling request to the connected MCP client.
1393
+ *
1394
+ * @param {SamplingRequestParams} params - Parameters for the sampling request
1395
+ * @returns {Promise<SamplingResult>} The LLM completion result
1396
+ */
1397
+ async createMessage(params) {
1398
+ console.log("[Web Model Context] Requesting sampling from client");
1399
+ const underlyingServer = this.bridge.tabServer.server;
1400
+ if (!underlyingServer?.createMessage) throw new Error("Sampling is not supported: no connected client with sampling capability");
1401
+ return underlyingServer.createMessage(params);
1402
+ }
1403
+ /**
1404
+ * Request user input from the connected client.
1405
+ * This sends an elicitation request to the connected MCP client.
1406
+ *
1407
+ * @param {ElicitationParams} params - Parameters for the elicitation request
1408
+ * @returns {Promise<ElicitationResult>} The user's response
1409
+ */
1410
+ async elicitInput(params) {
1411
+ console.log("[Web Model Context] Requesting elicitation from client");
1412
+ const underlyingServer = this.bridge.tabServer.server;
1413
+ if (!underlyingServer?.elicitInput) throw new Error("Elicitation is not supported: no connected client with elicitation capability");
1414
+ return underlyingServer.elicitInput(params);
1415
+ }
900
1416
  };
901
1417
  /**
902
1418
  * Initializes the MCP bridge with dual-server support.
@@ -930,6 +1446,32 @@ function initializeMCPBridge(options) {
930
1446
  throw error;
931
1447
  }
932
1448
  });
1449
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
1450
+ console.log("[MCP Bridge] Handling list_resources request");
1451
+ return { resources: bridge$1.modelContext.listResources() };
1452
+ });
1453
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
1454
+ console.log(`[MCP Bridge] Handling read_resource request: ${request.params.uri}`);
1455
+ try {
1456
+ return await bridge$1.modelContext.readResource(request.params.uri);
1457
+ } catch (error) {
1458
+ console.error(`[MCP Bridge] Error reading resource ${request.params.uri}:`, error);
1459
+ throw error;
1460
+ }
1461
+ });
1462
+ server.setRequestHandler(ListPromptsRequestSchema, async () => {
1463
+ console.log("[MCP Bridge] Handling list_prompts request");
1464
+ return { prompts: bridge$1.modelContext.listPrompts() };
1465
+ });
1466
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
1467
+ console.log(`[MCP Bridge] Handling get_prompt request: ${request.params.name}`);
1468
+ try {
1469
+ return await bridge$1.modelContext.getPrompt(request.params.name, request.params.arguments);
1470
+ } catch (error) {
1471
+ console.error(`[MCP Bridge] Error getting prompt ${request.params.name}:`, error);
1472
+ throw error;
1473
+ }
1474
+ });
933
1475
  };
934
1476
  const customTransport = transportOptions?.create?.();
935
1477
  if (customTransport) {
@@ -937,10 +1479,16 @@ function initializeMCPBridge(options) {
937
1479
  const server = new Server({
938
1480
  name: hostname,
939
1481
  version: "1.0.0"
940
- }, { capabilities: { tools: { listChanged: true } } });
1482
+ }, { capabilities: {
1483
+ tools: { listChanged: true },
1484
+ resources: { listChanged: true },
1485
+ prompts: { listChanged: true }
1486
+ } });
941
1487
  const bridge$1 = {
942
1488
  tabServer: server,
943
1489
  tools: /* @__PURE__ */ new Map(),
1490
+ resources: /* @__PURE__ */ new Map(),
1491
+ prompts: /* @__PURE__ */ new Map(),
944
1492
  modelContext: void 0,
945
1493
  isInitialized: true
946
1494
  };
@@ -955,10 +1503,16 @@ function initializeMCPBridge(options) {
955
1503
  const tabServer = new Server({
956
1504
  name: `${hostname}-tab`,
957
1505
  version: "1.0.0"
958
- }, { capabilities: { tools: { listChanged: true } } });
1506
+ }, { capabilities: {
1507
+ tools: { listChanged: true },
1508
+ resources: { listChanged: true },
1509
+ prompts: { listChanged: true }
1510
+ } });
959
1511
  const bridge = {
960
1512
  tabServer,
961
1513
  tools: /* @__PURE__ */ new Map(),
1514
+ resources: /* @__PURE__ */ new Map(),
1515
+ prompts: /* @__PURE__ */ new Map(),
962
1516
  modelContext: void 0,
963
1517
  isInitialized: true
964
1518
  };
@@ -980,7 +1534,11 @@ function initializeMCPBridge(options) {
980
1534
  const iframeServer = new Server({
981
1535
  name: `${hostname}-iframe`,
982
1536
  version: "1.0.0"
983
- }, { capabilities: { tools: { listChanged: true } } });
1537
+ }, { capabilities: {
1538
+ tools: { listChanged: true },
1539
+ resources: { listChanged: true },
1540
+ prompts: { listChanged: true }
1541
+ } });
984
1542
  setupServerHandlers(iframeServer, bridge);
985
1543
  const { allowedOrigins,...restIframeServerOptions } = typeof iframeServerConfig === "object" ? iframeServerConfig : {};
986
1544
  const iframeTransport = new IframeChildTransport({