@mcp-b/global 1.0.15 → 1.1.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 CHANGED
@@ -103,6 +103,263 @@ window.navigator.modelContext.provideContext({
103
103
  });
104
104
  ```
105
105
 
106
+ ## ⚙️ Configuration
107
+
108
+ The polyfill exposes `initializeWebModelContext(options?: WebModelContextInitOptions)` to let you control transport behaviour. When you import `@mcp-b/global` as a module it auto-initializes by default, but you can customise or defer initialization:
109
+
110
+ - **Disable auto init**: Set `window.__webModelContextOptions = { autoInitialize: false }` before importing, then call `initializeWebModelContext()` manually.
111
+ - **Configure via script tag**: When using the IIFE build, pass options through data attributes:
112
+ ```html
113
+ <script
114
+ src="https://unpkg.com/@mcp-b/global@latest/dist/index.iife.js"
115
+ data-webmcp-auto-initialize="false"
116
+ data-webmcp-allowed-origins="https://example.com,https://docs.example.com"
117
+ ></script>
118
+ <!-- Later in the page -->
119
+ <script>
120
+ window.navigator.modelContext.provideContext({ tools: [] });
121
+ </script>
122
+ ```
123
+ Use `data-webmcp-options='{"transport":{"tabServer":{"allowedOrigins":["https://example.com"]}}}'` for advanced JSON configuration.
124
+ - **Supported data attributes**
125
+ - `data-webmcp-auto-initialize="false"`: Skip automatic setup.
126
+ - `data-webmcp-allowed-origins="https://a.com,https://b.com"`: Override `tabServer.allowedOrigins`.
127
+ - `data-webmcp-channel-id="custom-channel"`: Set the Tab transport channel.
128
+
129
+ ### Dual-Server Mode (Tab + Iframe)
130
+
131
+ By default, the global package runs **two MCP servers** that share the same tool registry:
132
+
133
+ 1. **Tab Server** (`TabServerTransport`) - For same-window communication
134
+ 2. **Iframe Server** (`IframeChildTransport`) - Auto-enabled when running in an iframe (when `window.parent !== window`)
135
+
136
+ Both servers expose the same tools (Bucket A + Bucket B), allowing your tools to be accessed from:
137
+ - Same-window clients (e.g., browser extension content scripts)
138
+ - Parent page (when running in an iframe)
139
+
140
+ **Example: Running in an Iframe**
141
+
142
+ When your app runs in an iframe, both servers are automatically enabled:
143
+
144
+ ```ts
145
+ // In iframe: Auto-initializes with both servers
146
+ import '@mcp-b/global';
147
+
148
+ // Register tools - they're automatically available to:
149
+ // 1. Same-window clients (via TabServerTransport)
150
+ // 2. Parent page (via IframeChildTransport)
151
+ window.navigator.modelContext.provideContext({
152
+ tools: [
153
+ {
154
+ name: "iframe-action",
155
+ description: "Action from iframe",
156
+ inputSchema: { type: "object", properties: {} },
157
+ async execute() {
158
+ return {
159
+ content: [{ type: "text", text: "Hello from iframe!" }]
160
+ };
161
+ }
162
+ }
163
+ ]
164
+ });
165
+ ```
166
+
167
+ **Configure Iframe Server**
168
+
169
+ You can customize or disable the iframe server:
170
+
171
+ ```ts
172
+ import { initializeWebModelContext } from '@mcp-b/global';
173
+
174
+ // Customize iframe server
175
+ initializeWebModelContext({
176
+ transport: {
177
+ iframeServer: {
178
+ allowedOrigins: ['https://parent-app.com'], // Only allow specific parent
179
+ channelId: 'custom-iframe-channel',
180
+ },
181
+ },
182
+ });
183
+
184
+ // Disable iframe server (only Tab server runs)
185
+ initializeWebModelContext({
186
+ transport: {
187
+ iframeServer: false, // Disable iframe server
188
+ },
189
+ });
190
+
191
+ // Disable tab server (only Iframe server runs)
192
+ initializeWebModelContext({
193
+ transport: {
194
+ tabServer: false, // Disable tab server
195
+ iframeServer: {
196
+ allowedOrigins: ['https://parent-app.com'],
197
+ },
198
+ },
199
+ });
200
+ ```
201
+
202
+ **Custom Transport Factory**
203
+
204
+ Provide `transport.create` to supply any MCP `Transport` implementation instead of the built-in dual-server mode:
205
+
206
+ ```ts
207
+ import { initializeWebModelContext } from '@mcp-b/global';
208
+ import { CustomTransport } from './my-transport';
209
+
210
+ initializeWebModelContext({
211
+ transport: {
212
+ create: () => new CustomTransport(),
213
+ },
214
+ });
215
+ ```
216
+
217
+ ## 🔄 Native Chromium API Support
218
+
219
+ This package **automatically detects and integrates** with Chromium's native Web Model Context API when available. No configuration needed - it just works!
220
+
221
+ ### Automatic Detection & Integration
222
+
223
+ When you call `initializeWebModelContext()` (or when auto-initialization runs):
224
+
225
+ 1. **Native API detected** (both `navigator.modelContext` and `navigator.modelContextTesting` present):
226
+ - Uses native Chromium implementation
227
+ - Creates MCP bridge and syncs tools automatically
228
+ - Registers callback to listen for native tool changes
229
+ - MCP clients stay synchronized with native tool registry
230
+
231
+ 2. **No native API detected**:
232
+ - Installs full polyfill implementation
233
+ - Provides identical API surface
234
+
235
+ **Zero configuration required** - the package automatically adapts to your environment!
236
+
237
+ ### Native API Features
238
+
239
+ When the native Chromium API is available, you get:
240
+
241
+ - ✅ **Automatic tool synchronization** - Tools registered in native API are synced to MCP bridge via `registerToolsChangedCallback()`
242
+ - ✅ **Iframe tool collection** - Native API automatically collects tools from embedded iframes (no manual transport setup needed)
243
+ - ✅ **MCP compatibility** - Your MCP clients (extensions, apps) continue to work seamlessly
244
+ - ✅ **Tool change notifications** - MCP servers receive `tools/list_changed` notifications automatically
245
+ - ✅ **Consistent API** - Same code works with both native and polyfill implementations
246
+
247
+ ### How Tool Synchronization Works
248
+
249
+ The polyfill automatically registers a callback with the native API:
250
+
251
+ ```typescript
252
+ // Happens automatically when native API is detected
253
+ navigator.modelContextTesting.registerToolsChangedCallback(() => {
254
+ // Syncs native tools → MCP bridge
255
+ // MCP clients receive tools/list_changed notification
256
+ });
257
+ ```
258
+
259
+ This callback fires when:
260
+ - `navigator.modelContext.registerTool()` is called
261
+ - `navigator.modelContext.unregisterTool()` is called
262
+ - `navigator.modelContext.provideContext()` is called
263
+ - `navigator.modelContext.clearContext()` is called
264
+ - Tools are added from embedded iframes (native feature)
265
+
266
+ ### Enabling Native API in Chromium
267
+
268
+ ```bash
269
+ # Method 1: Launch with flag
270
+ chromium --enable-experimental-web-platform-features
271
+
272
+ # Method 2: Enable in chrome://flags
273
+ # Search for: "Experimental Web Platform Features"
274
+ # Set to: Enabled
275
+ # Restart browser
276
+ ```
277
+
278
+ ### Example: Using Native API
279
+
280
+ ```typescript
281
+ import '@mcp-b/global';
282
+
283
+ // If native API is present, this delegates to navigator.modelContext:
284
+ window.navigator.modelContext.registerTool({
285
+ name: 'myTool',
286
+ description: 'My tool',
287
+ inputSchema: { type: 'object', properties: {} },
288
+ async execute() {
289
+ return { content: [{ type: 'text', text: 'Hello!' }] };
290
+ }
291
+ });
292
+
293
+ // Behind the scenes:
294
+ // 1. Tool registered in native Chromium registry
295
+ // 2. Callback fires (registerToolsChangedCallback)
296
+ // 3. Tool synced to MCP bridge
297
+ // 4. MCP clients notified (tools/list_changed)
298
+ ```
299
+
300
+ ### Iframe Tool Collection (Native Only)
301
+
302
+ When the native API is active, tools from embedded iframes are **automatically collected**:
303
+
304
+ ```html
305
+ <!-- parent.html -->
306
+ <script type="module">
307
+ import '@mcp-b/global';
308
+
309
+ // Native API will collect tools from this page AND all iframes
310
+ navigator.modelContext.registerTool({
311
+ name: 'parent-tool',
312
+ description: 'Tool from parent page',
313
+ inputSchema: { type: 'object', properties: {} },
314
+ async execute() {
315
+ return { content: [{ type: 'text', text: 'Parent tool' }] };
316
+ }
317
+ });
318
+ </script>
319
+
320
+ <iframe src="child.html"></iframe>
321
+ ```
322
+
323
+ ```html
324
+ <!-- child.html -->
325
+ <script type="module">
326
+ import '@mcp-b/global';
327
+
328
+ // This tool is automatically visible in parent's registry (native feature)
329
+ navigator.modelContext.registerTool({
330
+ name: 'child-tool',
331
+ description: 'Tool from iframe',
332
+ inputSchema: { type: 'object', properties: {} },
333
+ async execute() {
334
+ return { content: [{ type: 'text', text: 'Child tool' }] };
335
+ }
336
+ });
337
+ </script>
338
+ ```
339
+
340
+ With native API, `navigator.modelContextTesting.listTools()` in the parent will show **both** tools! The MCP bridge stays in sync automatically.
341
+
342
+ ### Detection in Console
343
+
344
+ When you initialize the package, check the console logs:
345
+
346
+ ```
347
+ ✅ [Web Model Context] Native Chromium API detected
348
+ Using native implementation with MCP bridge synchronization
349
+ Native API will automatically collect tools from embedded iframes
350
+ ✅ [Web Model Context] MCP bridge synced with native API
351
+ MCP clients will receive automatic tool updates from native registry
352
+ ```
353
+
354
+ Or if polyfill is used:
355
+
356
+ ```
357
+ [Web Model Context] Native API not detected, installing polyfill
358
+ ✅ [Web Model Context] window.navigator.modelContext initialized successfully
359
+ [Model Context Testing] Installing polyfill
360
+ ✅ [Model Context Testing] Polyfill installed at window.navigator.modelContextTesting
361
+ ```
362
+
106
363
  ## 📖 API Reference
107
364
 
108
365
  ### Two-Bucket Tool Management System
@@ -577,9 +834,238 @@ if (window.__mcpBridge) {
577
834
  }
578
835
  ```
579
836
 
837
+ ## 🧪 Testing API (`navigator.modelContextTesting`)
838
+
839
+ This package provides a **Model Context Testing API** at `window.navigator.modelContextTesting` for debugging and testing your tools during development.
840
+
841
+ ### Native Support in Chromium
842
+
843
+ **IMPORTANT**: The `modelContextTesting` API is available natively in Chromium-based browsers when the experimental feature flag is enabled. This polyfill will detect and use the native implementation when available.
844
+
845
+ #### How to Enable Native API in Chromium:
846
+
847
+ **Option 1: Chrome Flags**
848
+ 1. Navigate to `chrome://flags`
849
+ 2. Search for "Experimental Web Platform Features"
850
+ 3. Enable the flag
851
+ 4. Restart your browser
852
+
853
+ **Option 2: Command Line**
854
+ ```bash
855
+ # Launch Chrome/Edge with experimental features
856
+ chrome --enable-experimental-web-platform-features
857
+ ```
858
+
859
+ **Detection**: When the native API is detected, you'll see this console message:
860
+ ```
861
+ ✅ [Model Context Testing] Native implementation detected (Chromium experimental feature)
862
+ Using native window.navigator.modelContextTesting from browser
863
+ ```
864
+
865
+ ### Polyfill Fallback
866
+
867
+ If the native API is not available, this package automatically provides a polyfill implementation with the same interface:
868
+
869
+ ```
870
+ [Model Context Testing] Native implementation not found, installing polyfill
871
+ 💡 To use the native implementation in Chromium:
872
+ - Navigate to chrome://flags
873
+ - Enable "Experimental Web Platform Features"
874
+ - Or launch with: --enable-experimental-web-platform-features
875
+ ✅ [Model Context Testing] Polyfill installed at window.navigator.modelContextTesting
876
+ ```
877
+
878
+ ### API Reference
879
+
880
+ #### `getToolCalls(): Array<ToolCall>`
881
+
882
+ Get a history of all tool calls made during the session.
883
+
884
+ ```javascript
885
+ // Register and call some tools
886
+ window.navigator.modelContext.provideContext({
887
+ tools: [{
888
+ name: "greet",
889
+ description: "Greet a user",
890
+ inputSchema: {
891
+ type: "object",
892
+ properties: { name: { type: "string" } },
893
+ required: ["name"]
894
+ },
895
+ async execute({ name }) {
896
+ return { content: [{ type: "text", text: `Hello, ${name}!` }] };
897
+ }
898
+ }]
899
+ });
900
+
901
+ // Simulate a tool call
902
+ // (In practice, this would come from an AI agent)
903
+
904
+ // Later, inspect the tool call history
905
+ const calls = window.navigator.modelContextTesting.getToolCalls();
906
+ console.log(calls);
907
+ // [
908
+ // {
909
+ // toolName: "greet",
910
+ // arguments: { name: "Alice" },
911
+ // timestamp: 1699123456789
912
+ // }
913
+ // ]
914
+ ```
915
+
916
+ #### `clearToolCalls(): void`
917
+
918
+ Clear the tool call history.
919
+
920
+ ```javascript
921
+ window.navigator.modelContextTesting.clearToolCalls();
922
+ console.log(window.navigator.modelContextTesting.getToolCalls()); // []
923
+ ```
924
+
925
+ #### `setMockToolResponse(toolName: string, response: ToolResponse): void`
926
+
927
+ Set a mock response for a specific tool. When set, the tool's `execute()` function will be bypassed and the mock response will be returned instead.
928
+
929
+ ```javascript
930
+ // Mock the "greet" tool to always return a specific response
931
+ window.navigator.modelContextTesting.setMockToolResponse("greet", {
932
+ content: [{
933
+ type: "text",
934
+ text: "Mocked greeting!"
935
+ }]
936
+ });
937
+
938
+ // Now when the tool is called, it returns the mock response
939
+ // (The execute function is never called)
940
+ ```
941
+
942
+ #### `clearMockToolResponse(toolName: string): void`
943
+
944
+ Remove the mock response for a specific tool.
945
+
946
+ ```javascript
947
+ window.navigator.modelContextTesting.clearMockToolResponse("greet");
948
+ // Tool will now use its actual execute function
949
+ ```
950
+
951
+ #### `clearAllMockToolResponses(): void`
952
+
953
+ Remove all mock tool responses.
954
+
955
+ ```javascript
956
+ window.navigator.modelContextTesting.clearAllMockToolResponses();
957
+ ```
958
+
959
+ #### `getRegisteredTools(): Array<ToolDescriptor>`
960
+
961
+ Get the list of all currently registered tools (same as `modelContext.listTools()`).
962
+
963
+ ```javascript
964
+ const tools = window.navigator.modelContextTesting.getRegisteredTools();
965
+ console.log(tools.map(t => t.name)); // ["greet", "add-todo", ...]
966
+ ```
967
+
968
+ #### `reset(): void`
969
+
970
+ Reset the entire testing state (clears tool call history and all mock responses).
971
+
972
+ ```javascript
973
+ window.navigator.modelContextTesting.reset();
974
+ ```
975
+
976
+ ### Testing Workflow Example
977
+
978
+ Here's a complete example of using the testing API:
979
+
980
+ ```javascript
981
+ // 1. Register your tools
982
+ window.navigator.modelContext.provideContext({
983
+ tools: [
984
+ {
985
+ name: "add-todo",
986
+ description: "Add a todo item",
987
+ inputSchema: {
988
+ type: "object",
989
+ properties: { text: { type: "string" } },
990
+ required: ["text"]
991
+ },
992
+ async execute({ text }) {
993
+ // This would normally add to your app state
994
+ return { content: [{ type: "text", text: `Added: ${text}` }] };
995
+ }
996
+ }
997
+ ]
998
+ });
999
+
1000
+ // 2. Set up mocks for testing
1001
+ window.navigator.modelContextTesting.setMockToolResponse("add-todo", {
1002
+ content: [{ type: "text", text: "Mock: Todo added successfully" }]
1003
+ });
1004
+
1005
+ // 3. Simulate tool calls (or let AI agent call them)
1006
+ // The tool will return the mock response instead of executing
1007
+
1008
+ // 4. Inspect tool call history
1009
+ const calls = window.navigator.modelContextTesting.getToolCalls();
1010
+ console.log(`${calls.length} tool calls made`);
1011
+ calls.forEach(call => {
1012
+ console.log(`- ${call.toolName}`, call.arguments);
1013
+ });
1014
+
1015
+ // 5. Clean up after testing
1016
+ window.navigator.modelContextTesting.reset();
1017
+ ```
1018
+
1019
+ ### Integration Testing Example
1020
+
1021
+ Perfect for automated testing with frameworks like Jest, Vitest, or Playwright:
1022
+
1023
+ ```javascript
1024
+ // test/model-context.test.js
1025
+ import { test, expect } from 'vitest';
1026
+
1027
+ test('todo tool creates correct response', async () => {
1028
+ // Arrange
1029
+ const mockResponse = {
1030
+ content: [{ type: "text", text: "Test todo added" }]
1031
+ };
1032
+
1033
+ window.navigator.modelContextTesting.setMockToolResponse(
1034
+ "add-todo",
1035
+ mockResponse
1036
+ );
1037
+
1038
+ // Act
1039
+ // Trigger your AI agent or directly call the tool via MCP
1040
+ // ...
1041
+
1042
+ // Assert
1043
+ const calls = window.navigator.modelContextTesting.getToolCalls();
1044
+ expect(calls).toHaveLength(1);
1045
+ expect(calls[0].toolName).toBe("add-todo");
1046
+ expect(calls[0].arguments).toEqual({ text: "Test item" });
1047
+
1048
+ // Cleanup
1049
+ window.navigator.modelContextTesting.reset();
1050
+ });
1051
+ ```
1052
+
1053
+ ### Browser Compatibility
1054
+
1055
+ | Browser | Native Support | Polyfill |
1056
+ |---------|---------------|----------|
1057
+ | Chrome/Edge (with flag) | ✅ Yes | N/A |
1058
+ | Chrome/Edge (default) | ❌ No | ✅ Yes |
1059
+ | Firefox | ❌ No | ✅ Yes |
1060
+ | Safari | ❌ No | ✅ Yes |
1061
+ | Other browsers | ❌ No | ✅ Yes |
1062
+
1063
+ The polyfill automatically detects and defers to the native implementation when available, ensuring forward compatibility as browsers adopt this standard.
1064
+
580
1065
  ## 📦 What's Included
581
1066
 
582
1067
  - **Web Model Context API** - Standard `window.navigator.modelContext` interface
1068
+ - **Model Context Testing API** - `window.navigator.modelContextTesting` for debugging and testing (with native Chromium support detection)
583
1069
  - **Dynamic Tool Registration** - `registerTool()` with `unregister()` function
584
1070
  - **MCP Bridge** - Automatic bridging to Model Context Protocol
585
1071
  - **Tab Transport** - Communication layer for browser contexts