@mcp-b/global 2.3.2 → 3.0.0-beta.20260702171253

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
@@ -7,14 +7,30 @@ let runtime = null;
7
7
  function isBrowserEnvironment() {
8
8
  return typeof window !== "undefined" && typeof window.navigator !== "undefined";
9
9
  }
10
- /**
11
- * Replace navigator.modelContext with the given value.
12
- * Tries an own-property on the navigator instance first. If the native browser
13
- * defines modelContext as a non-configurable property (common in Chromium), this
14
- * will throw — in that case we fall back to redefining the getter on
15
- * Navigator.prototype so that `navigator.modelContext` resolves to our value.
16
- */
17
- function replaceModelContext(value) {
10
+ function readCurrentModelContext() {
11
+ if (Object.getOwnPropertyDescriptor(navigator, "modelContext")) return navigator.modelContext;
12
+ return document.modelContext ?? navigator.modelContext;
13
+ }
14
+ function replaceDocumentModelContext(value) {
15
+ try {
16
+ Object.defineProperty(document, "modelContext", {
17
+ configurable: true,
18
+ enumerable: true,
19
+ writable: false,
20
+ value
21
+ });
22
+ } catch {
23
+ Object.defineProperty(Object.getPrototypeOf(document), "modelContext", {
24
+ configurable: true,
25
+ enumerable: true,
26
+ get() {
27
+ return value;
28
+ }
29
+ });
30
+ }
31
+ if (document.modelContext !== value) console.error("[WebModelContext] Failed to replace document.modelContext.", "Descriptor:", Object.getOwnPropertyDescriptor(document, "modelContext"));
32
+ }
33
+ function replaceNavigatorModelContext(value) {
18
34
  try {
19
35
  Object.defineProperty(navigator, "modelContext", {
20
36
  configurable: true,
@@ -33,6 +49,18 @@ function replaceModelContext(value) {
33
49
  }
34
50
  if (navigator.modelContext !== value) console.error("[WebModelContext] Failed to replace navigator.modelContext.", "Descriptor:", Object.getOwnPropertyDescriptor(navigator, "modelContext"));
35
51
  }
52
+ /**
53
+ * Replace both modelContext surfaces with the given value.
54
+ *
55
+ * The Chrome 152 baseline uses document.modelContext as canonical and keeps
56
+ * navigator.modelContext as a deprecated alias. @mcp-b/global still supports
57
+ * old navigator-first users, so the bridge exposes the BrowserMcpServer wrapper
58
+ * through both properties.
59
+ */
60
+ function replaceModelContext(value) {
61
+ replaceDocumentModelContext(value);
62
+ replaceNavigatorModelContext(value);
63
+ }
36
64
  function createTransport(config) {
37
65
  if (window.parent !== window && config?.iframeServer !== false) {
38
66
  const { allowedOrigins, ...rest } = typeof config?.iframeServer === "object" ? config.iframeServer : {};
@@ -105,22 +133,26 @@ function syncToolsFromTestingShim(server) {
105
133
  function initializeWebModelContext(options) {
106
134
  if (!isBrowserEnvironment()) return;
107
135
  if (runtime) return;
108
- if (navigator.modelContext?.[SERVER_MARKER_PROPERTY]) return;
136
+ if (readCurrentModelContext()?.[SERVER_MARKER_PROPERTY]) return;
109
137
  initializeWebMCPPolyfill({ installTestingShim: options?.installTestingShim ?? "if-missing" });
110
- const native = navigator.modelContext;
111
- if (!native) throw new Error("navigator.modelContext is not available");
138
+ const native = readCurrentModelContext();
139
+ if (!native) throw new Error("modelContext is not available");
112
140
  const server = new BrowserMcpServer({
113
141
  name: `${window.location.hostname || "localhost"}-webmcp`,
114
142
  version: "1.0.0"
115
143
  }, { native });
116
144
  server.syncNativeTools();
117
145
  syncToolsFromTestingShim(server);
146
+ const previousDocumentModelContextDescriptor = Object.getOwnPropertyDescriptor(document, "modelContext");
147
+ const previousNavigatorModelContextDescriptor = Object.getOwnPropertyDescriptor(navigator, "modelContext");
118
148
  replaceModelContext(server);
119
149
  const transport = createTransport(options?.transport);
120
150
  runtime = {
121
151
  native,
122
152
  server,
123
- transport
153
+ transport,
154
+ previousDocumentModelContextDescriptor,
155
+ previousNavigatorModelContextDescriptor
124
156
  };
125
157
  server.connect(transport).catch((error) => {
126
158
  console.error("[WebModelContext] Failed to connect MCP transport:", error);
@@ -128,11 +160,14 @@ function initializeWebModelContext(options) {
128
160
  }
129
161
  function cleanupWebModelContext() {
130
162
  if (!runtime) return;
131
- const { native, server, transport } = runtime;
163
+ const { server, transport, previousDocumentModelContextDescriptor, previousNavigatorModelContextDescriptor } = runtime;
132
164
  runtime = null;
133
165
  server.close();
134
166
  transport.close();
135
- replaceModelContext(native);
167
+ if (previousDocumentModelContextDescriptor) Object.defineProperty(document, "modelContext", previousDocumentModelContextDescriptor);
168
+ else delete document.modelContext;
169
+ if (previousNavigatorModelContextDescriptor) Object.defineProperty(navigator, "modelContext", previousNavigatorModelContextDescriptor);
170
+ else delete navigator.modelContext;
136
171
  }
137
172
 
138
173
  //#endregion
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/global.ts","../src/index.ts"],"sourcesContent":["import {\n IframeChildTransport,\n type IframeChildTransportOptions,\n TabServerTransport,\n type TabServerTransportOptions,\n} from '@mcp-b/transports';\nimport { initializeWebMCPPolyfill } from '@mcp-b/webmcp-polyfill';\nimport { BrowserMcpServer, SERVER_MARKER_PROPERTY, type Transport } from '@mcp-b/webmcp-ts-sdk';\nimport type {\n InputSchema,\n ModelContextCore,\n ModelContextTesting,\n ModelContextTestingPolyfillExtensions,\n ToolListItem,\n ToolResponse,\n} from '@mcp-b/webmcp-types';\nimport type { WebModelContextInitOptions } from './types.js';\n\ninterface RuntimeState {\n native: ModelContextCore;\n server: BrowserMcpServer;\n transport: Transport;\n}\n\nlet runtime: RuntimeState | null = null;\n\nfunction isBrowserEnvironment(): boolean {\n return typeof window !== 'undefined' && typeof window.navigator !== 'undefined';\n}\n\n/**\n * Replace navigator.modelContext with the given value.\n * Tries an own-property on the navigator instance first. If the native browser\n * defines modelContext as a non-configurable property (common in Chromium), this\n * will throw — in that case we fall back to redefining the getter on\n * Navigator.prototype so that `navigator.modelContext` resolves to our value.\n */\nfunction replaceModelContext(value: unknown): void {\n try {\n Object.defineProperty(navigator, 'modelContext', {\n configurable: true,\n enumerable: true,\n writable: false,\n value,\n });\n } catch {\n // Native browser property is non-configurable on the instance.\n // Shadow it with a getter on the prototype instead.\n Object.defineProperty(Object.getPrototypeOf(navigator), 'modelContext', {\n configurable: true,\n enumerable: true,\n get() {\n return value;\n },\n });\n }\n\n // Verify the replacement actually worked — the prototype getter cannot\n // shadow a non-configurable own property on the navigator instance.\n if (navigator.modelContext !== value) {\n console.error(\n '[WebModelContext] Failed to replace navigator.modelContext.',\n 'Descriptor:',\n Object.getOwnPropertyDescriptor(navigator, 'modelContext')\n );\n }\n}\n\nfunction createTransport(config: WebModelContextInitOptions['transport']): Transport {\n const inIframe = window.parent !== window;\n\n if (inIframe && config?.iframeServer !== false) {\n const iframeOptions =\n typeof config?.iframeServer === 'object'\n ? config.iframeServer\n : ({} as Partial<IframeChildTransportOptions>);\n\n const { allowedOrigins, ...rest } = iframeOptions;\n\n return new IframeChildTransport({\n allowedOrigins: allowedOrigins ?? ['*'],\n ...rest,\n });\n }\n\n if (config?.tabServer === false) {\n throw new Error('tabServer transport is disabled and iframe transport was not selected');\n }\n\n const tabOptions =\n typeof config?.tabServer === 'object'\n ? config.tabServer\n : ({} as Partial<TabServerTransportOptions>);\n\n const { allowedOrigins, ...rest } = tabOptions;\n\n return new TabServerTransport({\n allowedOrigins: allowedOrigins ?? ['*'],\n ...rest,\n });\n}\n\nfunction parseTestingInputSchema(inputSchema: string | undefined): InputSchema | undefined {\n if (!inputSchema) {\n return undefined;\n }\n\n try {\n const parsed = JSON.parse(inputSchema) as unknown;\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return undefined;\n }\n return parsed as InputSchema;\n } catch (error) {\n console.warn('[WebMCP] Failed to parse testing inputSchema JSON:', error);\n return undefined;\n }\n}\n\nfunction getTestingShimTools():\n | {\n testingShim: ModelContextTesting;\n tools: ToolListItem[];\n }\n | undefined {\n const testingShim = navigator.modelContextTesting as\n | (ModelContextTesting & Partial<ModelContextTestingPolyfillExtensions>)\n | undefined;\n if (!testingShim) {\n return undefined;\n }\n\n if (typeof testingShim.getRegisteredTools === 'function') {\n return {\n testingShim,\n tools: testingShim.getRegisteredTools() as ToolListItem[],\n };\n }\n\n if (typeof testingShim.listTools !== 'function') {\n return undefined;\n }\n\n const tools = testingShim.listTools().map(\n (tool): ToolListItem => ({\n name: tool.name,\n description: tool.description ?? '',\n inputSchema: parseTestingInputSchema(tool.inputSchema) ?? {\n type: 'object',\n properties: {},\n },\n })\n );\n\n return {\n testingShim,\n tools,\n };\n}\n\nfunction syncToolsFromTestingShim(server: BrowserMcpServer): number {\n const shimState = getTestingShimTools();\n if (!shimState) {\n return 0;\n }\n\n const { testingShim, tools } = shimState;\n return server.backfillTools(tools, async (name: string, args: Record<string, unknown>) => {\n const serialized = await testingShim.executeTool(name, JSON.stringify(args ?? {}));\n if (serialized === null) {\n return {\n content: [{ type: 'text', text: 'Tool execution interrupted by navigation' }],\n isError: true,\n } satisfies ToolResponse;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(serialized);\n } catch (parseError) {\n throw new Error(\n `Failed to parse serialized tool response for ${name}: ${parseError instanceof Error ? parseError.message : String(parseError)}`\n );\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(`Invalid serialized tool response for ${name}`);\n }\n return parsed as ToolResponse;\n });\n}\n\nexport function initializeWebModelContext(options?: WebModelContextInitOptions): void {\n if (!isBrowserEnvironment()) {\n return;\n }\n\n if (runtime) {\n return;\n }\n\n // Cross-bundle guard: if navigator.modelContext is already a BrowserMcpServer\n // (set by another bundle in this window), skip initialization.\n const existingContext = navigator.modelContext as unknown as Record<string, unknown> | undefined;\n if (existingContext?.[SERVER_MARKER_PROPERTY]) {\n return;\n }\n\n // 1. Install polyfill (provides modelContext + modelContextTesting)\n initializeWebMCPPolyfill({\n installTestingShim: options?.installTestingShim ?? 'if-missing',\n });\n\n // 2. Save reference to the polyfill's (or native) context\n const native = navigator.modelContext as unknown as ModelContextCore;\n if (!native) {\n throw new Error('navigator.modelContext is not available');\n }\n\n // 3. Create server with native mirroring\n const hostname = window.location.hostname || 'localhost';\n const server = new BrowserMcpServer({ name: `${hostname}-webmcp`, version: '1.0.0' }, { native });\n server.syncNativeTools();\n syncToolsFromTestingShim(server);\n\n // 4. Replace navigator.modelContext with the server.\n // Try own-property on the navigator instance first (works for polyfill and most cases).\n // Fall back to a prototype getter if the native property is non-configurable.\n replaceModelContext(server);\n\n // 5. Create transport and connect\n const transport = createTransport(options?.transport);\n runtime = { native, server, transport };\n\n void server.connect(transport).catch((error: unknown) => {\n console.error('[WebModelContext] Failed to connect MCP transport:', error);\n });\n}\n\nexport function cleanupWebModelContext(): void {\n if (!runtime) {\n return;\n }\n\n const { native, server, transport } = runtime;\n runtime = null;\n\n void server.close();\n void transport.close();\n\n // Restore the context that existed before we wrapped it with BrowserMcpServer.\n // We intentionally do NOT call cleanupWebMCPPolyfill() here — the polyfill\n // manages its own lifecycle (auto-init, testing shim) independently.\n replaceModelContext(native);\n}\n","import { cleanupWebModelContext, initializeWebModelContext } from './global.js';\n\nexport { cleanupWebModelContext, initializeWebModelContext };\n\nexport type {\n NativeModelContextBehavior,\n TransportConfiguration,\n WebModelContextInitOptions,\n} from './types.js';\n\nif (typeof window !== 'undefined' && typeof document !== 'undefined') {\n const options = window.__webModelContextOptions;\n const shouldAutoInitialize = options?.autoInitialize !== false;\n\n if (shouldAutoInitialize) {\n try {\n initializeWebModelContext(options);\n } catch (error) {\n console.error('[WebModelContext] Auto-initialization failed:', error);\n }\n }\n}\n"],"mappings":";;;;;AAwBA,IAAI,UAA+B;AAEnC,SAAS,uBAAgC;AACvC,QAAO,OAAO,WAAW,eAAe,OAAO,OAAO,cAAc;;;;;;;;;AAUtE,SAAS,oBAAoB,OAAsB;AACjD,KAAI;AACF,SAAO,eAAe,WAAW,gBAAgB;GAC/C,cAAc;GACd,YAAY;GACZ,UAAU;GACV;GACD,CAAC;SACI;AAGN,SAAO,eAAe,OAAO,eAAe,UAAU,EAAE,gBAAgB;GACtE,cAAc;GACd,YAAY;GACZ,MAAM;AACJ,WAAO;;GAEV,CAAC;;AAKJ,KAAI,UAAU,iBAAiB,MAC7B,SAAQ,MACN,+DACA,eACA,OAAO,yBAAyB,WAAW,eAAe,CAC3D;;AAIL,SAAS,gBAAgB,QAA4D;AAGnF,KAFiB,OAAO,WAAW,UAEnB,QAAQ,iBAAiB,OAAO;EAM9C,MAAM,EAAE,gBAAgB,GAAG,SAJzB,OAAO,QAAQ,iBAAiB,WAC5B,OAAO,eACN,EAAE;AAIT,SAAO,IAAI,qBAAqB;GAC9B,gBAAgB,kBAAkB,CAAC,IAAI;GACvC,GAAG;GACJ,CAAC;;AAGJ,KAAI,QAAQ,cAAc,MACxB,OAAM,IAAI,MAAM,wEAAwE;CAQ1F,MAAM,EAAE,gBAAgB,GAAG,SAJzB,OAAO,QAAQ,cAAc,WACzB,OAAO,YACN,EAAE;AAIT,QAAO,IAAI,mBAAmB;EAC5B,gBAAgB,kBAAkB,CAAC,IAAI;EACvC,GAAG;EACJ,CAAC;;AAGJ,SAAS,wBAAwB,aAA0D;AACzF,KAAI,CAAC,YACH;AAGF,KAAI;EACF,MAAM,SAAS,KAAK,MAAM,YAAY;AACtC,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,OAAO,CAChE;AAEF,SAAO;UACA,OAAO;AACd,UAAQ,KAAK,sDAAsD,MAAM;AACzE;;;AAIJ,SAAS,sBAKK;CACZ,MAAM,cAAc,UAAU;AAG9B,KAAI,CAAC,YACH;AAGF,KAAI,OAAO,YAAY,uBAAuB,WAC5C,QAAO;EACL;EACA,OAAO,YAAY,oBAAoB;EACxC;AAGH,KAAI,OAAO,YAAY,cAAc,WACnC;AAcF,QAAO;EACL;EACA,OAbY,YAAY,WAAW,CAAC,KACnC,UAAwB;GACvB,MAAM,KAAK;GACX,aAAa,KAAK,eAAe;GACjC,aAAa,wBAAwB,KAAK,YAAY,IAAI;IACxD,MAAM;IACN,YAAY,EAAE;IACf;GACF,EAKI;EACN;;AAGH,SAAS,yBAAyB,QAAkC;CAClE,MAAM,YAAY,qBAAqB;AACvC,KAAI,CAAC,UACH,QAAO;CAGT,MAAM,EAAE,aAAa,UAAU;AAC/B,QAAO,OAAO,cAAc,OAAO,OAAO,MAAc,SAAkC;EACxF,MAAM,aAAa,MAAM,YAAY,YAAY,MAAM,KAAK,UAAU,QAAQ,EAAE,CAAC,CAAC;AAClF,MAAI,eAAe,KACjB,QAAO;GACL,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM;IAA4C,CAAC;GAC7E,SAAS;GACV;EAGH,IAAI;AACJ,MAAI;AACF,YAAS,KAAK,MAAM,WAAW;WACxB,YAAY;AACnB,SAAM,IAAI,MACR,gDAAgD,KAAK,IAAI,sBAAsB,QAAQ,WAAW,UAAU,OAAO,WAAW,GAC/H;;AAEH,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,OAAO,CAChE,OAAM,IAAI,MAAM,wCAAwC,OAAO;AAEjE,SAAO;GACP;;AAGJ,SAAgB,0BAA0B,SAA4C;AACpF,KAAI,CAAC,sBAAsB,CACzB;AAGF,KAAI,QACF;AAMF,KADwB,UAAU,eACZ,wBACpB;AAIF,0BAAyB,EACvB,oBAAoB,SAAS,sBAAsB,cACpD,CAAC;CAGF,MAAM,SAAS,UAAU;AACzB,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,0CAA0C;CAK5D,MAAM,SAAS,IAAI,iBAAiB;EAAE,MAAM,GAD3B,OAAO,SAAS,YAAY,YACW;EAAU,SAAS;EAAS,EAAE,EAAE,QAAQ,CAAC;AACjG,QAAO,iBAAiB;AACxB,0BAAyB,OAAO;AAKhC,qBAAoB,OAAO;CAG3B,MAAM,YAAY,gBAAgB,SAAS,UAAU;AACrD,WAAU;EAAE;EAAQ;EAAQ;EAAW;AAEvC,CAAK,OAAO,QAAQ,UAAU,CAAC,OAAO,UAAmB;AACvD,UAAQ,MAAM,sDAAsD,MAAM;GAC1E;;AAGJ,SAAgB,yBAA+B;AAC7C,KAAI,CAAC,QACH;CAGF,MAAM,EAAE,QAAQ,QAAQ,cAAc;AACtC,WAAU;AAEV,CAAK,OAAO,OAAO;AACnB,CAAK,UAAU,OAAO;AAKtB,qBAAoB,OAAO;;;;;AClP7B,IAAI,OAAO,WAAW,eAAe,OAAO,aAAa,aAAa;CACpE,MAAM,UAAU,OAAO;AAGvB,KAF6B,SAAS,mBAAmB,MAGvD,KAAI;AACF,4BAA0B,QAAQ;UAC3B,OAAO;AACd,UAAQ,MAAM,iDAAiD,MAAM"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/global.ts","../src/index.ts"],"sourcesContent":["import {\n IframeChildTransport,\n type IframeChildTransportOptions,\n TabServerTransport,\n type TabServerTransportOptions,\n} from '@mcp-b/transports';\nimport { initializeWebMCPPolyfill } from '@mcp-b/webmcp-polyfill';\nimport { BrowserMcpServer, SERVER_MARKER_PROPERTY, type Transport } from '@mcp-b/webmcp-ts-sdk';\nimport type {\n InputSchema,\n ModelContextCore,\n ModelContextTesting,\n ModelContextTestingPolyfillExtensions,\n ToolListItem,\n ToolResponse,\n} from '@mcp-b/webmcp-types';\nimport type { WebModelContextInitOptions } from './types.js';\n\ninterface RuntimeState {\n native: ModelContextCore;\n server: BrowserMcpServer;\n transport: Transport;\n previousDocumentModelContextDescriptor: PropertyDescriptor | undefined;\n previousNavigatorModelContextDescriptor: PropertyDescriptor | undefined;\n}\n\nlet runtime: RuntimeState | null = null;\n\nfunction isBrowserEnvironment(): boolean {\n return typeof window !== 'undefined' && typeof window.navigator !== 'undefined';\n}\n\nfunction readCurrentModelContext(): unknown {\n const navigatorDescriptor = Object.getOwnPropertyDescriptor(navigator, 'modelContext');\n if (navigatorDescriptor) {\n return navigator.modelContext;\n }\n\n return document.modelContext ?? navigator.modelContext;\n}\n\nfunction replaceDocumentModelContext(value: unknown): void {\n try {\n Object.defineProperty(document, 'modelContext', {\n configurable: true,\n enumerable: true,\n writable: false,\n value,\n });\n } catch {\n Object.defineProperty(Object.getPrototypeOf(document), 'modelContext', {\n configurable: true,\n enumerable: true,\n get() {\n return value;\n },\n });\n }\n\n if (document.modelContext !== value) {\n console.error(\n '[WebModelContext] Failed to replace document.modelContext.',\n 'Descriptor:',\n Object.getOwnPropertyDescriptor(document, 'modelContext')\n );\n }\n}\n\nfunction replaceNavigatorModelContext(value: unknown): void {\n try {\n Object.defineProperty(navigator, 'modelContext', {\n configurable: true,\n enumerable: true,\n writable: false,\n value,\n });\n } catch {\n // Native browser property is non-configurable on the instance.\n // Shadow it with a getter on the prototype instead.\n Object.defineProperty(Object.getPrototypeOf(navigator), 'modelContext', {\n configurable: true,\n enumerable: true,\n get() {\n return value;\n },\n });\n }\n\n // Verify the replacement actually worked — the prototype getter cannot\n // shadow a non-configurable own property on the navigator instance.\n if (navigator.modelContext !== value) {\n console.error(\n '[WebModelContext] Failed to replace navigator.modelContext.',\n 'Descriptor:',\n Object.getOwnPropertyDescriptor(navigator, 'modelContext')\n );\n }\n}\n\n/**\n * Replace both modelContext surfaces with the given value.\n *\n * The Chrome 152 baseline uses document.modelContext as canonical and keeps\n * navigator.modelContext as a deprecated alias. @mcp-b/global still supports\n * old navigator-first users, so the bridge exposes the BrowserMcpServer wrapper\n * through both properties.\n */\nfunction replaceModelContext(value: unknown): void {\n replaceDocumentModelContext(value);\n replaceNavigatorModelContext(value);\n}\n\nfunction createTransport(config: WebModelContextInitOptions['transport']): Transport {\n const inIframe = window.parent !== window;\n\n if (inIframe && config?.iframeServer !== false) {\n const iframeOptions =\n typeof config?.iframeServer === 'object'\n ? config.iframeServer\n : ({} as Partial<IframeChildTransportOptions>);\n\n const { allowedOrigins, ...rest } = iframeOptions;\n\n return new IframeChildTransport({\n allowedOrigins: allowedOrigins ?? ['*'],\n ...rest,\n });\n }\n\n if (config?.tabServer === false) {\n throw new Error('tabServer transport is disabled and iframe transport was not selected');\n }\n\n const tabOptions =\n typeof config?.tabServer === 'object'\n ? config.tabServer\n : ({} as Partial<TabServerTransportOptions>);\n\n const { allowedOrigins, ...rest } = tabOptions;\n\n return new TabServerTransport({\n allowedOrigins: allowedOrigins ?? ['*'],\n ...rest,\n });\n}\n\nfunction parseTestingInputSchema(inputSchema: string | undefined): InputSchema | undefined {\n if (!inputSchema) {\n return undefined;\n }\n\n try {\n const parsed = JSON.parse(inputSchema) as unknown;\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return undefined;\n }\n return parsed as InputSchema;\n } catch (error) {\n console.warn('[WebMCP] Failed to parse testing inputSchema JSON:', error);\n return undefined;\n }\n}\n\nfunction getTestingShimTools():\n | {\n testingShim: ModelContextTesting;\n tools: ToolListItem[];\n }\n | undefined {\n const testingShim = navigator.modelContextTesting as\n | (ModelContextTesting & Partial<ModelContextTestingPolyfillExtensions>)\n | undefined;\n if (!testingShim) {\n return undefined;\n }\n\n if (typeof testingShim.getRegisteredTools === 'function') {\n return {\n testingShim,\n tools: testingShim.getRegisteredTools() as ToolListItem[],\n };\n }\n\n if (typeof testingShim.listTools !== 'function') {\n return undefined;\n }\n\n const tools = testingShim.listTools().map(\n (tool): ToolListItem => ({\n name: tool.name,\n description: tool.description ?? '',\n inputSchema: parseTestingInputSchema(tool.inputSchema) ?? {\n type: 'object',\n properties: {},\n },\n })\n );\n\n return {\n testingShim,\n tools,\n };\n}\n\nfunction syncToolsFromTestingShim(server: BrowserMcpServer): number {\n const shimState = getTestingShimTools();\n if (!shimState) {\n return 0;\n }\n\n const { testingShim, tools } = shimState;\n return server.backfillTools(tools, async (name: string, args: Record<string, unknown>) => {\n const serialized = await testingShim.executeTool(name, JSON.stringify(args ?? {}));\n if (serialized === null) {\n return {\n content: [{ type: 'text', text: 'Tool execution interrupted by navigation' }],\n isError: true,\n } satisfies ToolResponse;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(serialized);\n } catch (parseError) {\n throw new Error(\n `Failed to parse serialized tool response for ${name}: ${parseError instanceof Error ? parseError.message : String(parseError)}`\n );\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(`Invalid serialized tool response for ${name}`);\n }\n return parsed as ToolResponse;\n });\n}\n\nexport function initializeWebModelContext(options?: WebModelContextInitOptions): void {\n if (!isBrowserEnvironment()) {\n return;\n }\n\n if (runtime) {\n return;\n }\n\n // Cross-bundle guard: if modelContext is already a BrowserMcpServer\n // (set by another bundle in this window), skip initialization.\n const existingContext = readCurrentModelContext() as Record<string, unknown> | undefined;\n if (existingContext?.[SERVER_MARKER_PROPERTY]) {\n return;\n }\n\n // 1. Install polyfill (provides modelContext + modelContextTesting)\n initializeWebMCPPolyfill({\n installTestingShim: options?.installTestingShim ?? 'if-missing',\n });\n\n // 2. Save reference to the polyfill's (or native) context\n const native = readCurrentModelContext() as ModelContextCore | undefined;\n if (!native) {\n throw new Error('modelContext is not available');\n }\n\n // 3. Create server with native mirroring\n const hostname = window.location.hostname || 'localhost';\n const server = new BrowserMcpServer({ name: `${hostname}-webmcp`, version: '1.0.0' }, { native });\n server.syncNativeTools();\n syncToolsFromTestingShim(server);\n\n // 4. Replace navigator.modelContext with the server.\n // Try own-property on the navigator instance first (works for polyfill and most cases).\n // Fall back to a prototype getter if the native property is non-configurable.\n const previousDocumentModelContextDescriptor = Object.getOwnPropertyDescriptor(\n document,\n 'modelContext'\n );\n const previousNavigatorModelContextDescriptor = Object.getOwnPropertyDescriptor(\n navigator,\n 'modelContext'\n );\n replaceModelContext(server);\n\n // 5. Create transport and connect\n const transport = createTransport(options?.transport);\n runtime = {\n native,\n server,\n transport,\n previousDocumentModelContextDescriptor,\n previousNavigatorModelContextDescriptor,\n };\n\n void server.connect(transport).catch((error: unknown) => {\n console.error('[WebModelContext] Failed to connect MCP transport:', error);\n });\n}\n\nexport function cleanupWebModelContext(): void {\n if (!runtime) {\n return;\n }\n\n const {\n server,\n transport,\n previousDocumentModelContextDescriptor,\n previousNavigatorModelContextDescriptor,\n } = runtime;\n runtime = null;\n\n void server.close();\n void transport.close();\n\n // Restore the descriptors that existed before we wrapped with BrowserMcpServer.\n // We intentionally do NOT call cleanupWebMCPPolyfill() here — the polyfill\n // manages its own lifecycle (auto-init, testing shim) independently.\n if (previousDocumentModelContextDescriptor) {\n Object.defineProperty(document, 'modelContext', previousDocumentModelContextDescriptor);\n } else {\n delete (document as unknown as Record<string, unknown>).modelContext;\n }\n\n if (previousNavigatorModelContextDescriptor) {\n Object.defineProperty(navigator, 'modelContext', previousNavigatorModelContextDescriptor);\n } else {\n delete (navigator as unknown as Record<string, unknown>).modelContext;\n }\n}\n","import { cleanupWebModelContext, initializeWebModelContext } from './global.js';\n\nexport { cleanupWebModelContext, initializeWebModelContext };\n\nexport type {\n NativeModelContextBehavior,\n TransportConfiguration,\n WebModelContextInitOptions,\n} from './types.js';\n\nif (typeof window !== 'undefined' && typeof document !== 'undefined') {\n const options = window.__webModelContextOptions;\n const shouldAutoInitialize = options?.autoInitialize !== false;\n\n if (shouldAutoInitialize) {\n try {\n initializeWebModelContext(options);\n } catch (error) {\n console.error('[WebModelContext] Auto-initialization failed:', error);\n }\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mcp-b/global",
3
- "version": "2.3.2",
4
- "description": "W3C Web Model Context API polyfill - Let AI agents like Claude, ChatGPT, and Gemini interact with your website via navigator.modelContext",
3
+ "version": "3.0.0-beta.20260702171253",
4
+ "description": "W3C Web Model Context Protocol (WebMCP) API polyfill. Let AI agents like Claude, ChatGPT, and Gemini interact with your website via document.modelContext.",
5
5
  "keywords": [
6
6
  "ai-agents",
7
7
  "ai-assistant",
@@ -30,7 +30,7 @@
30
30
  "web-standard",
31
31
  "webmcp"
32
32
  ],
33
- "homepage": "https://docs.mcp-b.ai/packages/global",
33
+ "homepage": "https://docs.mcp-b.ai/packages/global/reference",
34
34
  "bugs": {
35
35
  "url": "https://github.com/WebMCP-org/npm-packages/issues"
36
36
  },
@@ -66,18 +66,18 @@
66
66
  "registry": "https://registry.npmjs.org/"
67
67
  },
68
68
  "dependencies": {
69
- "@mcp-b/webmcp-polyfill": "2.3.2",
70
- "@mcp-b/transports": "2.3.2",
71
- "@mcp-b/webmcp-types": "2.3.2",
72
- "@mcp-b/webmcp-ts-sdk": "2.3.2"
69
+ "@mcp-b/webmcp-polyfill": "3.0.0-beta.20260702171253",
70
+ "@mcp-b/webmcp-ts-sdk": "3.0.0-beta.20260702171253",
71
+ "@mcp-b/webmcp-types": "3.0.0-beta.20260702171253",
72
+ "@mcp-b/transports": "3.0.0-beta.20260702171253"
73
73
  },
74
74
  "devDependencies": {
75
75
  "@types/node": "22.17.2",
76
- "@vitest/coverage-v8": "^4.1.0",
76
+ "@vitest/coverage-v8": "^4.1.7",
77
77
  "fast-check": "^4.0.0",
78
- "playwright": "^1.58.0",
78
+ "playwright": "^1.60.0",
79
79
  "typescript": "^5.8.3",
80
- "vite-plus": "latest",
80
+ "vite-plus": "^0.1.22",
81
81
  "vitest": "npm:@voidzero-dev/vite-plus-test@latest"
82
82
  },
83
83
  "engines": {
@@ -94,9 +94,10 @@
94
94
  "test": "vp test run",
95
95
  "test:browser": "vp test run --browser.headless",
96
96
  "test:browser:ui": "vp test --browser",
97
- "test:conformance:matrix": "pnpm run test:conformance:polyfill && pnpm run test:conformance:native",
97
+ "test:conformance:global": "vp test run --config vitest.conformance.global.config.ts --browser.headless",
98
+ "test:conformance:matrix": "pnpm run test:conformance:polyfill && pnpm run test:conformance:global && pnpm run test:conformance:native",
98
99
  "test:conformance:native": "vp test run --config vitest.conformance.native.config.ts --browser.headless",
99
- "test:conformance:polyfill": "vp test run --config vitest.conformance.polyfill.config.ts --browser.headless",
100
+ "test:conformance:polyfill": "pnpm --filter @mcp-b/webmcp-polyfill run test:conformance",
100
101
  "test:node": "vp test run --config vitest.node.config.ts",
101
102
  "test:watch": "vp test",
102
103
  "typecheck": "tsc --noEmit && vp test run --typecheck --silent",