@mcp-b/global 1.1.0 → 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 +375 -0
- package/dist/index.d.ts +137 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.iife.js +1 -1
- package/dist/index.js +678 -34
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -214,6 +214,152 @@ initializeWebModelContext({
|
|
|
214
214
|
});
|
|
215
215
|
```
|
|
216
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
|
+
|
|
217
363
|
## 📖 API Reference
|
|
218
364
|
|
|
219
365
|
### Two-Bucket Tool Management System
|
|
@@ -688,9 +834,238 @@ if (window.__mcpBridge) {
|
|
|
688
834
|
}
|
|
689
835
|
```
|
|
690
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
|
+
|
|
691
1065
|
## 📦 What's Included
|
|
692
1066
|
|
|
693
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)
|
|
694
1069
|
- **Dynamic Tool Registration** - `registerTool()` with `unregister()` function
|
|
695
1070
|
- **MCP Bridge** - Automatic bridging to Model Context Protocol
|
|
696
1071
|
- **Tab Transport** - Communication layer for browser contexts
|
package/dist/index.d.ts
CHANGED
|
@@ -129,7 +129,7 @@ interface ModelContextInput {
|
|
|
129
129
|
* Array of tool descriptors
|
|
130
130
|
* Supports both JSON Schema and Zod schema formats
|
|
131
131
|
*/
|
|
132
|
-
tools: ToolDescriptor
|
|
132
|
+
tools: ToolDescriptor[];
|
|
133
133
|
}
|
|
134
134
|
/**
|
|
135
135
|
* Tool call event
|
|
@@ -167,6 +167,16 @@ interface ModelContext {
|
|
|
167
167
|
registerTool<TInputSchema extends ZodSchemaObject = Record<string, never>, TOutputSchema extends ZodSchemaObject = Record<string, never>>(tool: ToolDescriptor<TInputSchema, TOutputSchema>): {
|
|
168
168
|
unregister: () => void;
|
|
169
169
|
};
|
|
170
|
+
/**
|
|
171
|
+
* Unregister a tool by name
|
|
172
|
+
* Available in Chromium's native implementation
|
|
173
|
+
*/
|
|
174
|
+
unregisterTool(name: string): void;
|
|
175
|
+
/**
|
|
176
|
+
* Clear all registered tools (both buckets)
|
|
177
|
+
* Available in Chromium's native implementation
|
|
178
|
+
*/
|
|
179
|
+
clearContext(): void;
|
|
170
180
|
/**
|
|
171
181
|
* Add event listener for tool calls
|
|
172
182
|
*/
|
|
@@ -210,8 +220,93 @@ interface MCPBridge {
|
|
|
210
220
|
iframeServer?: Server;
|
|
211
221
|
tools: Map<string, ValidatedToolDescriptor>;
|
|
212
222
|
modelContext: InternalModelContext;
|
|
223
|
+
modelContextTesting?: ModelContextTesting;
|
|
213
224
|
isInitialized: boolean;
|
|
214
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Tool info returned by listTools() in testing API
|
|
228
|
+
* Note: inputSchema is a JSON string, not an object (matches Chromium implementation)
|
|
229
|
+
*/
|
|
230
|
+
interface ToolInfo {
|
|
231
|
+
name: string;
|
|
232
|
+
description: string;
|
|
233
|
+
inputSchema: string;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Testing API for Model Context
|
|
237
|
+
*
|
|
238
|
+
* **Native Support**: This API is available natively in Chromium-based browsers
|
|
239
|
+
* when the experimental "Model Context Testing" feature flag is enabled.
|
|
240
|
+
*
|
|
241
|
+
* **How to enable in Chromium**:
|
|
242
|
+
* - Navigate to `chrome://flags`
|
|
243
|
+
* - Search for "experimental web platform features" or "model context"
|
|
244
|
+
* - Enable the feature and restart the browser
|
|
245
|
+
* - Or launch with: `--enable-experimental-web-platform-features`
|
|
246
|
+
*
|
|
247
|
+
* **Polyfill**: If the native API is not available, this polyfill provides
|
|
248
|
+
* a compatible implementation for testing purposes.
|
|
249
|
+
*/
|
|
250
|
+
interface ModelContextTesting {
|
|
251
|
+
/**
|
|
252
|
+
* Execute a tool directly with JSON string input (Chromium native API)
|
|
253
|
+
* @param toolName - Name of the tool to execute
|
|
254
|
+
* @param inputArgsJson - JSON string of input arguments
|
|
255
|
+
* @returns Promise resolving to the tool's result
|
|
256
|
+
*/
|
|
257
|
+
executeTool(toolName: string, inputArgsJson: string): Promise<unknown>;
|
|
258
|
+
/**
|
|
259
|
+
* List all registered tools (Chromium native API)
|
|
260
|
+
* Returns tools with inputSchema as JSON string
|
|
261
|
+
*/
|
|
262
|
+
listTools(): ToolInfo[];
|
|
263
|
+
/**
|
|
264
|
+
* Register a callback that fires when the tools list changes (Chromium native API)
|
|
265
|
+
* Callback will fire on: registerTool, unregisterTool, provideContext, clearContext
|
|
266
|
+
*/
|
|
267
|
+
registerToolsChangedCallback(callback: () => void): void;
|
|
268
|
+
/**
|
|
269
|
+
* Get all tool calls that have been made (for testing/debugging)
|
|
270
|
+
* Polyfill-specific extension
|
|
271
|
+
*/
|
|
272
|
+
getToolCalls(): Array<{
|
|
273
|
+
toolName: string;
|
|
274
|
+
arguments: Record<string, unknown>;
|
|
275
|
+
timestamp: number;
|
|
276
|
+
}>;
|
|
277
|
+
/**
|
|
278
|
+
* Clear the history of tool calls
|
|
279
|
+
* Polyfill-specific extension
|
|
280
|
+
*/
|
|
281
|
+
clearToolCalls(): void;
|
|
282
|
+
/**
|
|
283
|
+
* Set a mock response for a specific tool (for testing)
|
|
284
|
+
* When set, the tool's execute function will be bypassed and the mock response returned
|
|
285
|
+
* Polyfill-specific extension
|
|
286
|
+
*/
|
|
287
|
+
setMockToolResponse(toolName: string, response: ToolResponse): void;
|
|
288
|
+
/**
|
|
289
|
+
* Clear mock response for a specific tool
|
|
290
|
+
* Polyfill-specific extension
|
|
291
|
+
*/
|
|
292
|
+
clearMockToolResponse(toolName: string): void;
|
|
293
|
+
/**
|
|
294
|
+
* Clear all mock tool responses
|
|
295
|
+
* Polyfill-specific extension
|
|
296
|
+
*/
|
|
297
|
+
clearAllMockToolResponses(): void;
|
|
298
|
+
/**
|
|
299
|
+
* Get the current tools registered in the system
|
|
300
|
+
* (same as modelContext.listTools but explicitly for testing)
|
|
301
|
+
* Polyfill-specific extension
|
|
302
|
+
*/
|
|
303
|
+
getRegisteredTools(): ReturnType<ModelContext['listTools']>;
|
|
304
|
+
/**
|
|
305
|
+
* Reset the entire testing state (clears tool calls and mock responses)
|
|
306
|
+
* Polyfill-specific extension
|
|
307
|
+
*/
|
|
308
|
+
reset(): void;
|
|
309
|
+
}
|
|
215
310
|
declare global {
|
|
216
311
|
interface Navigator {
|
|
217
312
|
/**
|
|
@@ -219,6 +314,18 @@ declare global {
|
|
|
219
314
|
* Provides tools and context to AI agents
|
|
220
315
|
*/
|
|
221
316
|
modelContext: ModelContext;
|
|
317
|
+
/**
|
|
318
|
+
* Model Context Testing API
|
|
319
|
+
*
|
|
320
|
+
* **IMPORTANT**: This API is only available in Chromium-based browsers
|
|
321
|
+
* with the experimental feature flag enabled:
|
|
322
|
+
* - `chrome://flags` → "Experimental Web Platform Features"
|
|
323
|
+
* - Or launch with: `--enable-experimental-web-platform-features`
|
|
324
|
+
*
|
|
325
|
+
* If not available natively, the @mcp-b/global polyfill provides
|
|
326
|
+
* a compatible implementation.
|
|
327
|
+
*/
|
|
328
|
+
modelContextTesting?: ModelContextTesting;
|
|
222
329
|
}
|
|
223
330
|
interface Window {
|
|
224
331
|
/**
|
|
@@ -236,13 +343,39 @@ declare global {
|
|
|
236
343
|
}
|
|
237
344
|
}
|
|
238
345
|
/**
|
|
239
|
-
*
|
|
346
|
+
* Initializes the Web Model Context API on window.navigator.
|
|
347
|
+
* Creates and exposes navigator.modelContext and navigator.modelContextTesting.
|
|
348
|
+
* Automatically detects and uses native Chromium implementation if available.
|
|
349
|
+
*
|
|
350
|
+
* @param {WebModelContextInitOptions} [options] - Configuration options
|
|
351
|
+
* @throws {Error} If initialization fails
|
|
352
|
+
* @example
|
|
353
|
+
* ```typescript
|
|
354
|
+
* import { initializeWebModelContext } from '@mcp-b/global';
|
|
355
|
+
*
|
|
356
|
+
* initializeWebModelContext({
|
|
357
|
+
* transport: {
|
|
358
|
+
* tabServer: {
|
|
359
|
+
* allowedOrigins: ['https://example.com']
|
|
360
|
+
* }
|
|
361
|
+
* }
|
|
362
|
+
* });
|
|
363
|
+
* ```
|
|
240
364
|
*/
|
|
241
365
|
declare function initializeWebModelContext(options?: WebModelContextInitOptions): void;
|
|
242
366
|
/**
|
|
243
|
-
*
|
|
367
|
+
* Cleans up the Web Model Context API.
|
|
368
|
+
* Closes all MCP servers and removes API from window.navigator.
|
|
369
|
+
* Useful for testing and hot module replacement.
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* ```typescript
|
|
373
|
+
* import { cleanupWebModelContext } from '@mcp-b/global';
|
|
374
|
+
*
|
|
375
|
+
* cleanupWebModelContext();
|
|
376
|
+
* ```
|
|
244
377
|
*/
|
|
245
378
|
declare function cleanupWebModelContext(): void;
|
|
246
379
|
//#endregion
|
|
247
|
-
export { type CallToolResult, InputSchema, InternalModelContext, MCPBridge, ModelContext, ModelContextInput, type ToolAnnotations, ToolCallEvent, ToolDescriptor, ToolResponse, TransportConfiguration, ValidatedToolDescriptor, WebModelContextInitOptions, ZodSchemaObject, cleanupWebModelContext, initializeWebModelContext };
|
|
380
|
+
export { type CallToolResult, InputSchema, InternalModelContext, MCPBridge, ModelContext, ModelContextInput, ModelContextTesting, type ToolAnnotations, ToolCallEvent, ToolDescriptor, ToolInfo, ToolResponse, TransportConfiguration, ValidatedToolDescriptor, WebModelContextInitOptions, ZodSchemaObject, cleanupWebModelContext, initializeWebModelContext };
|
|
248
381
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/global.ts"],"sourcesContent":[],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/global.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAYA;AAkBY,UAlBK,WAAA,CAkBU;EAgBf,IAAA,EAAA,MAAA;EAKK,UAAA,CAAA,EArCF,MAqCE,CAAA,MAAsB,EAAA;IAItB,IAAA,EAAA,MAAA;IAOK,WAAA,CAAA,EAAA,MAAA;IAAR,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,OAAA;EAOW,CAAA,CAAA;EAAR,QAAA,CAAA,EAAA,MAAA,EAAA;EAAO,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,OAAA;AAMxB;AAqBA;;;;AAE0C,KApE9B,eAAA,GAAkB,MAoEY,CAAA,MAAA,EApEG,CAAA,CAAE,UAoEL,CAAA;;;;;AA2CnC,KA/FK,YAAA,GAAe,cA+FpB;;AAQP;;AAIiB,UAtGA,sBAAA,CAsGA;EACD;;;EAC8B,MAAA,CAAA,EAAA,GAAA,GApG7B,SAoG6B;EAG1B;;;AAQpB;AAWA;EASa,SAAA,CAAA,EA5HC,OA4HD,CA5HS,yBA4HT,CAAA,GAAA,KAAA;EAKa;;;AAO1B;;EAcyB,YAAA,CAAA,EA/IR,OA+IQ,CA/IA,2BA+IA,CAAA,GAAA,KAAA;;;;;AAGc,UA5ItB,0BAAA,CA4IsB;EAA7B;;;EAuBc,SAAA,CAAA,EA/JV,sBA+JU;EAQF;;;EAOC,cAAA,CAAA,EAAA,OAAA;;;;;;AAmBvB;;;;;AAA0D,UAhLzC,cAgLyC,CAAA,qBA/KnC,eA+KmC,GA/KjB,MA+KiB,CAAA,MAAA,EAAA,KAAA,CAAA,EAAA,sBA9KlC,eA8KkC,GA9KhB,MA8KgB,CAAA,MAAA,EAAA,KAAA,CAAA,CAAA,CAAA;EAWzC;;;EAGI,IAAA,EAAA,MAAA;EAAZ;;;EAEkC,WAAA,EAAA,MAAA;EAQ1B;AAqBjB;;;;;;EA6DmC,WAAA,EArQpB,WAqQoB,GArQN,YAqQM;EAAX;;AAOvB;;;EAQiB,YAAA,CAAA,EA7QD,WA6QC,GA7Qa,aA6Qb;EAaQ;;;EAOC,WAAA,CAAA,EA5RX,eA4RW;EAAA;;;;ACrYP;;SAKW,EAAA,CAAA,IAAA,ED6GrB,YC7GqB,SD6GA,MC7G0B,CAAA,MAAA,EAAA,KAAA,CAAA,GD8GjD,MC9GiD,CAAA,MAAA,EAAA,OAAA,CAAA,GD+GjD,CAAA,CAAE,KC/G+C,CD+GzC,CAAA,CAAE,SC/GuC,CD+G7B,YC/G6B,CAAA,CAAA,EAAA,GDgHlD,OChHkD,CDgH1C,YChH0C,CAAA;;;AA0sCzD;AAyHA;;;UD3sCiB,uBAAA;;;eAGF;iBACE;gBACD;kBACE,4BAA4B,QAAQ;kBAGpC,CAAA,CAAE;oBACA,CAAA,CAAE;;;;;;UAOL,iBAAA;;;;;SAKR;;;;;UAMQ,aAAA,SAAsB;;;;;;;;aAS1B;;;;0BAKa;;;;;;UAOT,YAAA;;;;;;0BAMS;;;;;;oCAQD,kBAAkB,6CACjB,kBAAkB,6BAElC,eAAe,cAAc;;;;;;;;;;;;;;;;uDAsBjB,yBAAyB,mCACvB;;;;0DAQF,yBAAyB,mCACvB;;;;uBAMD;;;;;eAMR;;;iBAGE;mBACE;kBACD;;;;;;;UAQD,oBAAA,SAA6B;;;;;sCAKR,0BAA0B,QAAQ;;;;;UAMvD,SAAA;aACJ;iBACI;SACR,YAAY;gBACL;wBACQ;;;;;;;UAQP,QAAA;;;;;;;;;;;;;;;;;;;;UAqBA,mBAAA;;;;;;;wDAOuC;;;;;eAMzC;;;;;;;;;;kBAYG;;eAEH;;;;;;;;;;;;;kDAemC;;;;;;;;;;;;;;;;wBAmB1B,WAAW;;;;;;;;;;;;;kBAejB;;;;;;;;;;;;0BAaQ;;;;;;kBAOR;;;;;;;;+BChYa;;ADjB/B;AAkBA;AAgBA;AAKA;;;;;;;AAwBA;AAqBA;;;;;;;;;;AA0CU,iBC6lCM,yBAAA,CD7lCN,OAAA,CAAA,EC6lC0C,0BD7lC1C,CAAA,EAAA,IAAA;;;;;;;;;AAWV;;;;AAMkB,iBCqsCF,sBAAA,CAAA,CDrsCE,EAAA,IAAA"}
|
package/dist/index.iife.js
CHANGED
|
@@ -5,4 +5,4 @@ ${t}`:t}else if(e.examples!==void 0&&Array.isArray(e.examples)){let t=e.examples
|
|
|
5
5
|
${t.map(e=>` ${JSON.stringify(e)}`).join(`
|
|
6
6
|
`)}`;n=n?`${n}
|
|
7
7
|
${e}`:e}}return n&&(t=t.describe(n)),t},di=(e,t,n)=>{if(e.default!==void 0){if(e.default===null&&n?.path.some(e=>e===`anyOf`||e===`oneOf`)&&e.type&&e.type!==`null`&&!e.nullable)return t;t=t.default(e.default)}return t},fi=(e,t)=>(e.readOnly&&(t=t.readonly()),t),pi=(e,t)=>qr.a.nullable(e)?ii(e,t):qr.an.object(e)?ci(e,t):qr.an.array(e)?Jr(e,t):qr.an.anyOf(e)?Kr(e,t):qr.an.allOf(e)?Gr(e,t):qr.a.oneOf(e)?oi(e,t):qr.a.not(e)?ti(e,t):qr.an.enum(e)?Qr(e):qr.a.const(e)?Xr(e):qr.a.multipleType(e)?ei(e,t):qr.a.primitive(e,`string`)?li(e):qr.a.primitive(e,`number`)||qr.a.primitive(e,`integer`)?ai(e):qr.a.primitive(e,`boolean`)?Yr(e):qr.a.primitive(e,`null`)?ni(e):qr.a.conditional(e)?$r(e,t):Zr(e),mi=(e,t={seen:new Map,path:[]},n)=>{if(typeof e!=`object`)return e?nt():it();if(t.parserOverride){let n=t.parserOverride(e,t);if(n instanceof P)return n}let r=t.seen.get(e);if(r){if(r.r!==void 0)return r.r;if(t.depth===void 0||r.n>=t.depth)return nt();r.n+=1}else r={r:void 0,n:0},t.seen.set(e,r);let i=pi(e,t);return n||(t.withoutDescribes||(i=ui(e,i)),t.withoutDefaults||(i=di(e,i,t)),i=fi(e,i)),r.r=i,i},hi=(e,t={})=>mi(e,{path:[],seen:new Map,...t});function gi(e){if(typeof e!=`object`||!e||`type`in e&&typeof e.type==`string`)return!1;let t=Object.values(e);return t.length===0?!1:t.some(e=>e instanceof P)}function _i(e){try{return hi(e)}catch(e){return console.warn(`[Web Model Context] Failed to convert JSON Schema to Zod:`,e),Y({}).passthrough()}}function vi(e){let t={},n=[];for(let[r,i]of Object.entries(e)){let e=i.description||void 0,a=`string`,o,s;if(i instanceof pe)a=`string`;else if(i instanceof he)a=`number`;else if(i instanceof _e)a=`boolean`;else if(i instanceof Te){a=`array`;let e=i.element;s=e instanceof pe?{type:`string`}:e instanceof he?{type:`number`}:e instanceof _e?{type:`boolean`}:{type:`string`}}else if(i instanceof De)a=`object`;else if(i instanceof Ve){a=`string`;let e=i._def;e?.values&&(o=e.values)}let c={type:a};e&&(c.description=e),o&&(c.enum=o),s&&(c.items=s),t[r]=c,i.isOptional()||n.push(r)}return{type:`object`,properties:t,...n.length>0&&{required:n}}}function yi(e){if(gi(e))return{jsonSchema:vi(e),zodValidator:Y(e)};let t=e;return{jsonSchema:t,zodValidator:_i(t)}}function bi(e,t){let n=t.safeParse(e);return n.success?{success:!0,data:n.data}:{success:!1,error:`Validation failed:\n${n.error.errors.map(e=>` - ${e.path.join(`.`)||`root`}: ${e.message}`).join(`
|
|
8
|
-
`)}`}}var xi=class extends Event{name;arguments;_response=null;_responded=!1;constructor(e,t){super(`toolcall`,{cancelable:!0}),this.name=e,this.arguments=t}respondWith(e){if(this._responded)throw Error(`Response already provided for this tool call`);this._response=e,this._responded=!0}getResponse(){return this._response}hasResponse(){return this._responded}},Si=class{bridge;eventTarget;provideContextTools;dynamicTools;registrationTimestamps;unregisterFunctions;constructor(e){this.bridge=e,this.eventTarget=new EventTarget,this.provideContextTools=new Map,this.dynamicTools=new Map,this.registrationTimestamps=new Map,this.unregisterFunctions=new Map}addEventListener(e,t,n){this.eventTarget.addEventListener(e,t,n)}removeEventListener(e,t,n){this.eventTarget.removeEventListener(e,t,n)}dispatchEvent(e){return this.eventTarget.dispatchEvent(e)}provideContext(e){console.log(`[Web Model Context] Registering ${e.tools.length} tools via provideContext`),this.provideContextTools.clear();for(let t of e.tools){if(this.dynamicTools.has(t.name))throw Error(`[Web Model Context] Tool name collision: "${t.name}" is already registered via registerTool(). Please use a different name or unregister the dynamic tool first.`);let{jsonSchema:e,zodValidator:n}=yi(t.inputSchema),r=t.outputSchema?yi(t.outputSchema):null,i={name:t.name,description:t.description,inputSchema:e,...r&&{outputSchema:r.jsonSchema},...t.annotations&&{annotations:t.annotations},execute:t.execute,inputValidator:n,...r&&{outputValidator:r.zodValidator}};this.provideContextTools.set(t.name,i)}this.updateBridgeTools(),this.notifyToolsListChanged()}registerTool(e){console.log(`[Web Model Context] Registering tool dynamically: ${e.name}`);let t=Date.now(),n=this.registrationTimestamps.get(e.name);if(n&&t-n<50){console.warn(`[Web Model Context] Tool "${e.name}" registered multiple times within 50ms. This is likely due to React Strict Mode double-mounting. Ignoring duplicate registration.`);let t=this.unregisterFunctions.get(e.name);if(t)return{unregister:t}}if(this.provideContextTools.has(e.name))throw Error(`[Web Model Context] Tool name collision: "${e.name}" is already registered via provideContext(). Please use a different name or update your provideContext() call.`);if(this.dynamicTools.has(e.name))throw Error(`[Web Model Context] Tool name collision: "${e.name}" is already registered via registerTool(). Please unregister it first or use a different name.`);let{jsonSchema:r,zodValidator:i}=yi(e.inputSchema),a=e.outputSchema?yi(e.outputSchema):null,o={name:e.name,description:e.description,inputSchema:r,...a&&{outputSchema:a.jsonSchema},...e.annotations&&{annotations:e.annotations},execute:e.execute,inputValidator:i,...a&&{outputValidator:a.zodValidator}};this.dynamicTools.set(e.name,o),this.registrationTimestamps.set(e.name,t),this.updateBridgeTools(),this.notifyToolsListChanged();let s=()=>{if(console.log(`[Web Model Context] Unregistering tool: ${e.name}`),this.provideContextTools.has(e.name))throw Error(`[Web Model Context] Cannot unregister tool "${e.name}": This tool was registered via provideContext(). Use provideContext() to update the base tool set.`);if(!this.dynamicTools.has(e.name)){console.warn(`[Web Model Context] Tool "${e.name}" is not registered, ignoring unregister call`);return}this.dynamicTools.delete(e.name),this.registrationTimestamps.delete(e.name),this.unregisterFunctions.delete(e.name),this.updateBridgeTools(),this.notifyToolsListChanged()};return this.unregisterFunctions.set(e.name,s),{unregister:s}}updateBridgeTools(){this.bridge.tools.clear();for(let[e,t]of this.provideContextTools)this.bridge.tools.set(e,t);for(let[e,t]of this.dynamicTools)this.bridge.tools.set(e,t);console.log(`[Web Model Context] Updated bridge with ${this.provideContextTools.size} base tools + ${this.dynamicTools.size} dynamic tools = ${this.bridge.tools.size} total`)}notifyToolsListChanged(){this.bridge.tabServer.notification&&this.bridge.tabServer.notification({method:`notifications/tools/list_changed`,params:{}}),this.bridge.iframeServer?.notification&&this.bridge.iframeServer.notification({method:`notifications/tools/list_changed`,params:{}})}async executeTool(e,t){let n=this.bridge.tools.get(e);if(!n)throw Error(`Tool not found: ${e}`);console.log(`[Web Model Context] Validating input for tool: ${e}`);let r=bi(t,n.inputValidator);if(!r.success)return console.error(`[Web Model Context] Input validation failed for ${e}:`,r.error),{content:[{type:`text`,text:`Input validation error for tool "${e}":\n${r.error}`}],isError:!0};let i=r.data,a=new xi(e,i);if(this.dispatchEvent(a),a.defaultPrevented&&a.hasResponse()){let t=a.getResponse();if(t)return console.log(`[Web Model Context] Tool ${e} handled by event listener`),t}console.log(`[Web Model Context] Executing tool: ${e}`);try{let t=await n.execute(i);if(n.outputValidator&&t.structuredContent){let r=bi(t.structuredContent,n.outputValidator);r.success||console.warn(`[Web Model Context] Output validation failed for ${e}:`,r.error)}return t}catch(t){return console.error(`[Web Model Context] Error executing tool ${e}:`,t),{content:[{type:`text`,text:`Error: ${t instanceof Error?t.message:String(t)}`}],isError:!0}}}listTools(){return Array.from(this.bridge.tools.values()).map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema,...e.outputSchema&&{outputSchema:e.outputSchema},...e.annotations&&{annotations:e.annotations}}))}};function Ci(e){console.log(`[Web Model Context] Initializing MCP bridge`);let n=window.location.hostname||`localhost`,r=e?.transport,i=(e,t)=>{e.setRequestHandler(bn,async()=>(console.log(`[MCP Bridge] Handling list_tools request`),{tools:t.modelContext.listTools()})),e.setRequestHandler(Cn,async e=>{console.log(`[MCP Bridge] Handling call_tool request: ${e.params.name}`);let n=e.params.name,r=e.params.arguments||{};try{let e=await t.modelContext.executeTool(n,r);return{content:e.content,isError:e.isError}}catch(e){throw console.error(`[MCP Bridge] Error calling tool ${n}:`,e),e}})},a=r?.create?.();if(a){console.log(`[Web Model Context] Using custom transport`);let e=new Rr({name:n,version:`1.0.0`},{capabilities:{tools:{listChanged:!0}}}),t={tabServer:e,tools:new Map,modelContext:void 0,isInitialized:!0};return t.modelContext=new Si(t),i(e,t),e.connect(a),console.log(`[Web Model Context] MCP server connected with custom transport`),t}console.log(`[Web Model Context] Using dual-server mode`);let o=r?.tabServer!==!1,s=new Rr({name:`${n}-tab`,version:`1.0.0`},{capabilities:{tools:{listChanged:!0}}}),c={tabServer:s,tools:new Map,modelContext:void 0,isInitialized:!0};if(c.modelContext=new Si(c),i(s,c),o){let{allowedOrigins:e,...n}=typeof r?.tabServer==`object`?r.tabServer:{},i=new t.TabServerTransport({allowedOrigins:e??[`*`],...n});s.connect(i),console.log(`[Web Model Context] Tab server connected`)}let l=typeof window<`u`&&window.parent!==window,u=r?.iframeServer;if(u!==!1&&(u!==void 0||l)){console.log(`[Web Model Context] Enabling iframe server`);let e=new Rr({name:`${n}-iframe`,version:`1.0.0`},{capabilities:{tools:{listChanged:!0}}});i(e,c);let{allowedOrigins:r,...a}=typeof u==`object`?u:{},o=new t.IframeChildTransport({allowedOrigins:r??[`*`],...a});e.connect(o),c.iframeServer=e,console.log(`[Web Model Context] Iframe server connected`)}return c}function wi(e){if(typeof window>`u`){console.warn(`[Web Model Context] Not in browser environment, skipping initialization`);return}let t=e??window.__webModelContextOptions;if(window.navigator.modelContext){console.warn(`[Web Model Context] window.navigator.modelContext already exists, skipping initialization`);return}try{let e=Ci(t);Object.defineProperty(window.navigator,`modelContext`,{value:e.modelContext,writable:!1,configurable:!1}),Object.defineProperty(window,`__mcpBridge`,{value:e,writable:!1,configurable:!0}),console.log(`✅ [Web Model Context] window.navigator.modelContext initialized successfully`)}catch(e){throw console.error(`[Web Model Context] Failed to initialize:`,e),e}}function Ti(){if(!(typeof window>`u`)){if(window.__mcpBridge)try{window.__mcpBridge.tabServer.close(),window.__mcpBridge.iframeServer&&window.__mcpBridge.iframeServer.close()}catch(e){console.warn(`[Web Model Context] Error closing MCP servers:`,e)}delete window.navigator.modelContext,delete window.__mcpBridge,console.log(`[Web Model Context] Cleaned up`)}}function Ei(e,t){return e?t?{...e,...t,tabServer:{...e.tabServer??{},...t.tabServer??{}}}:e:t}function Di(e,t){return e?t?{...e,...t,transport:Ei(e.transport??{},t.transport??{})}:e:t}function Oi(e){if(!e||!e.dataset)return;let{dataset:t}=e;if(t.webmcpOptions)try{return JSON.parse(t.webmcpOptions)}catch(e){console.error(`[Web Model Context] Invalid JSON in data-webmcp-options:`,e);return}let n={},r=!1;t.webmcpAutoInitialize!==void 0&&(n.autoInitialize=t.webmcpAutoInitialize!==`false`,r=!0);let i={},a=!1;if(t.webmcpAllowedOrigins){let e=t.webmcpAllowedOrigins.split(`,`).map(e=>e.trim()).filter(e=>e.length>0);e.length>0&&(i.allowedOrigins=e,r=!0,a=!0)}return t.webmcpChannelId&&(i.channelId=t.webmcpChannelId,r=!0,a=!0),a&&(n.transport={...n.transport??{},tabServer:{...n.transport?.tabServer??{},...i}}),r?n:void 0}if(typeof window<`u`&&typeof document<`u`){let e=window.__webModelContextOptions,t=document.currentScript,n=Oi(t),r=Di(e,n)??e??n;r&&(window.__webModelContextOptions=r);let i=r?.autoInitialize!==!1;try{i&&wi(r)}catch(e){console.error(`[Web Model Context] Auto-initialization failed:`,e)}}return e.cleanupWebModelContext=Ti,e.initializeWebModelContext=wi,e})({},__mcp_b_transports);
|
|
8
|
+
`)}`}}function xi(){if(typeof window>`u`||typeof navigator>`u`)return{hasNativeContext:!1,hasNativeTesting:!1};let e=navigator.modelContext,t=navigator.modelContextTesting;return!e||!t||(t.constructor?.name||``).includes(`WebModelContext`)?{hasNativeContext:!1,hasNativeTesting:!1}:{hasNativeContext:!0,hasNativeTesting:!0}}var Si=class{nativeContext;nativeTesting;bridge;syncInProgress=!1;constructor(e,t,n){this.bridge=e,this.nativeContext=t,this.nativeTesting=n,this.nativeTesting.registerToolsChangedCallback(()=>{console.log(`[Native Adapter] Tool change detected from native API`),this.syncToolsFromNative()}),this.syncToolsFromNative()}syncToolsFromNative(){if(!this.syncInProgress){this.syncInProgress=!0;try{let e=this.nativeTesting.listTools();console.log(`[Native Adapter] Syncing ${e.length} tools from native API`),this.bridge.tools.clear();for(let t of e)try{let e=JSON.parse(t.inputSchema),n={name:t.name,description:t.description,inputSchema:e,execute:async e=>{let n=await this.nativeTesting.executeTool(t.name,JSON.stringify(e));return this.convertToToolResponse(n)},inputValidator:_i(e)};this.bridge.tools.set(t.name,n)}catch(e){console.error(`[Native Adapter] Failed to sync tool "${t.name}":`,e)}this.notifyMCPServers()}finally{this.syncInProgress=!1}}}convertToToolResponse(e){return typeof e==`string`?{content:[{type:`text`,text:e}]}:e==null?{content:[{type:`text`,text:``}]}:typeof e==`object`?{content:[{type:`text`,text:JSON.stringify(e,null,2)}],structuredContent:e}:{content:[{type:`text`,text:String(e)}]}}notifyMCPServers(){this.bridge.tabServer?.notification&&this.bridge.tabServer.notification({method:`notifications/tools/list_changed`,params:{}}),this.bridge.iframeServer?.notification&&this.bridge.iframeServer.notification({method:`notifications/tools/list_changed`,params:{}})}provideContext(e){console.log(`[Native Adapter] Delegating provideContext to native API`),this.nativeContext.provideContext(e)}registerTool(e){return console.log(`[Native Adapter] Delegating registerTool("${e.name}") to native API`),this.nativeContext.registerTool(e)}unregisterTool(e){console.log(`[Native Adapter] Delegating unregisterTool("${e}") to native API`),this.nativeContext.unregisterTool(e)}clearContext(){console.log(`[Native Adapter] Delegating clearContext to native API`),this.nativeContext.clearContext()}async executeTool(e,t){console.log(`[Native Adapter] Executing tool "${e}" via native API`);try{let n=await this.nativeTesting.executeTool(e,JSON.stringify(t));return this.convertToToolResponse(n)}catch(t){return console.error(`[Native Adapter] Error executing tool "${e}":`,t),{content:[{type:`text`,text:`Error: ${t instanceof Error?t.message:String(t)}`}],isError:!0}}}listTools(){return Array.from(this.bridge.tools.values()).map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema,...e.outputSchema&&{outputSchema:e.outputSchema},...e.annotations&&{annotations:e.annotations}}))}addEventListener(e,t,n){this.nativeContext.addEventListener(e,t,n)}removeEventListener(e,t,n){this.nativeContext.removeEventListener(e,t,n)}dispatchEvent(e){return this.nativeContext.dispatchEvent(e)}},Ci=class extends Event{name;arguments;_response=null;_responded=!1;constructor(e,t){super(`toolcall`,{cancelable:!0}),this.name=e,this.arguments=t}respondWith(e){if(this._responded)throw Error(`Response already provided for this tool call`);this._response=e,this._responded=!0}getResponse(){return this._response}hasResponse(){return this._responded}},wi=class{toolCallHistory=[];mockResponses=new Map;toolsChangedCallbacks=new Set;bridge;constructor(e){this.bridge=e}recordToolCall(e,t){this.toolCallHistory.push({toolName:e,arguments:t,timestamp:Date.now()})}hasMockResponse(e){return this.mockResponses.has(e)}getMockResponse(e){return this.mockResponses.get(e)}notifyToolsChanged(){for(let e of this.toolsChangedCallbacks)try{e()}catch(e){console.error(`[Model Context Testing] Error in tools changed callback:`,e)}}async executeTool(e,t){console.log(`[Model Context Testing] Executing tool: ${e}`);let n;try{n=JSON.parse(t)}catch(e){throw SyntaxError(`Invalid JSON input: ${e instanceof Error?e.message:String(e)}`)}if(!this.bridge.tools.get(e))throw Error(`Tool not found: ${e}`);let r=await this.bridge.modelContext.executeTool(e,n);if(!r.isError){if(r.structuredContent)return r.structuredContent;if(r.content&&r.content.length>0){let e=r.content[0];if(e&&e.type===`text`)return e.text}}}listTools(){return this.bridge.modelContext.listTools().map(e=>({name:e.name,description:e.description,inputSchema:JSON.stringify(e.inputSchema)}))}registerToolsChangedCallback(e){this.toolsChangedCallbacks.add(e),console.log(`[Model Context Testing] Tools changed callback registered`)}getToolCalls(){return[...this.toolCallHistory]}clearToolCalls(){this.toolCallHistory=[],console.log(`[Model Context Testing] Tool call history cleared`)}setMockToolResponse(e,t){this.mockResponses.set(e,t),console.log(`[Model Context Testing] Mock response set for tool: ${e}`)}clearMockToolResponse(e){this.mockResponses.delete(e),console.log(`[Model Context Testing] Mock response cleared for tool: ${e}`)}clearAllMockToolResponses(){this.mockResponses.clear(),console.log(`[Model Context Testing] All mock responses cleared`)}getRegisteredTools(){return this.bridge.modelContext.listTools()}reset(){this.clearToolCalls(),this.clearAllMockToolResponses(),console.log(`[Model Context Testing] Testing state reset`)}},Ti=class{bridge;eventTarget;provideContextTools;dynamicTools;registrationTimestamps;unregisterFunctions;testingAPI;constructor(e){this.bridge=e,this.eventTarget=new EventTarget,this.provideContextTools=new Map,this.dynamicTools=new Map,this.registrationTimestamps=new Map,this.unregisterFunctions=new Map}setTestingAPI(e){this.testingAPI=e}addEventListener(e,t,n){this.eventTarget.addEventListener(e,t,n)}removeEventListener(e,t,n){this.eventTarget.removeEventListener(e,t,n)}dispatchEvent(e){return this.eventTarget.dispatchEvent(e)}provideContext(e){console.log(`[Web Model Context] Registering ${e.tools.length} tools via provideContext`),this.provideContextTools.clear();for(let t of e.tools){if(this.dynamicTools.has(t.name))throw Error(`[Web Model Context] Tool name collision: "${t.name}" is already registered via registerTool(). Please use a different name or unregister the dynamic tool first.`);let{jsonSchema:e,zodValidator:n}=yi(t.inputSchema),r=t.outputSchema?yi(t.outputSchema):null,i={name:t.name,description:t.description,inputSchema:e,...r&&{outputSchema:r.jsonSchema},...t.annotations&&{annotations:t.annotations},execute:t.execute,inputValidator:n,...r&&{outputValidator:r.zodValidator}};this.provideContextTools.set(t.name,i)}this.updateBridgeTools(),this.notifyToolsListChanged()}registerTool(e){console.log(`[Web Model Context] Registering tool dynamically: ${e.name}`);let t=Date.now(),n=this.registrationTimestamps.get(e.name);if(n&&t-n<50){console.warn(`[Web Model Context] Tool "${e.name}" registered multiple times within 50ms. This is likely due to React Strict Mode double-mounting. Ignoring duplicate registration.`);let t=this.unregisterFunctions.get(e.name);if(t)return{unregister:t}}if(this.provideContextTools.has(e.name))throw Error(`[Web Model Context] Tool name collision: "${e.name}" is already registered via provideContext(). Please use a different name or update your provideContext() call.`);if(this.dynamicTools.has(e.name))throw Error(`[Web Model Context] Tool name collision: "${e.name}" is already registered via registerTool(). Please unregister it first or use a different name.`);let{jsonSchema:r,zodValidator:i}=yi(e.inputSchema),a=e.outputSchema?yi(e.outputSchema):null,o={name:e.name,description:e.description,inputSchema:r,...a&&{outputSchema:a.jsonSchema},...e.annotations&&{annotations:e.annotations},execute:e.execute,inputValidator:i,...a&&{outputValidator:a.zodValidator}};this.dynamicTools.set(e.name,o),this.registrationTimestamps.set(e.name,t),this.updateBridgeTools(),this.notifyToolsListChanged();let s=()=>{if(console.log(`[Web Model Context] Unregistering tool: ${e.name}`),this.provideContextTools.has(e.name))throw Error(`[Web Model Context] Cannot unregister tool "${e.name}": This tool was registered via provideContext(). Use provideContext() to update the base tool set.`);if(!this.dynamicTools.has(e.name)){console.warn(`[Web Model Context] Tool "${e.name}" is not registered, ignoring unregister call`);return}this.dynamicTools.delete(e.name),this.registrationTimestamps.delete(e.name),this.unregisterFunctions.delete(e.name),this.updateBridgeTools(),this.notifyToolsListChanged()};return this.unregisterFunctions.set(e.name,s),{unregister:s}}unregisterTool(e){console.log(`[Web Model Context] Unregistering tool: ${e}`);let t=this.provideContextTools.has(e),n=this.dynamicTools.has(e);if(!t&&!n){console.warn(`[Web Model Context] Tool "${e}" is not registered, ignoring unregister call`);return}t&&this.provideContextTools.delete(e),n&&(this.dynamicTools.delete(e),this.registrationTimestamps.delete(e),this.unregisterFunctions.delete(e)),this.updateBridgeTools(),this.notifyToolsListChanged()}clearContext(){console.log(`[Web Model Context] Clearing all tools`),this.provideContextTools.clear(),this.dynamicTools.clear(),this.registrationTimestamps.clear(),this.unregisterFunctions.clear(),this.updateBridgeTools(),this.notifyToolsListChanged()}updateBridgeTools(){this.bridge.tools.clear();for(let[e,t]of this.provideContextTools)this.bridge.tools.set(e,t);for(let[e,t]of this.dynamicTools)this.bridge.tools.set(e,t);console.log(`[Web Model Context] Updated bridge with ${this.provideContextTools.size} base tools + ${this.dynamicTools.size} dynamic tools = ${this.bridge.tools.size} total`)}notifyToolsListChanged(){this.bridge.tabServer.notification&&this.bridge.tabServer.notification({method:`notifications/tools/list_changed`,params:{}}),this.bridge.iframeServer?.notification&&this.bridge.iframeServer.notification({method:`notifications/tools/list_changed`,params:{}}),this.testingAPI&&`notifyToolsChanged`in this.testingAPI&&this.testingAPI.notifyToolsChanged()}async executeTool(e,t){let n=this.bridge.tools.get(e);if(!n)throw Error(`Tool not found: ${e}`);console.log(`[Web Model Context] Validating input for tool: ${e}`);let r=bi(t,n.inputValidator);if(!r.success)return console.error(`[Web Model Context] Input validation failed for ${e}:`,r.error),{content:[{type:`text`,text:`Input validation error for tool "${e}":\n${r.error}`}],isError:!0};let i=r.data;if(this.testingAPI&&this.testingAPI.recordToolCall(e,i),this.testingAPI?.hasMockResponse(e)){let t=this.testingAPI.getMockResponse(e);if(t)return console.log(`[Web Model Context] Returning mock response for tool: ${e}`),t}let a=new Ci(e,i);if(this.dispatchEvent(a),a.defaultPrevented&&a.hasResponse()){let t=a.getResponse();if(t)return console.log(`[Web Model Context] Tool ${e} handled by event listener`),t}console.log(`[Web Model Context] Executing tool: ${e}`);try{let t=await n.execute(i);if(n.outputValidator&&t.structuredContent){let r=bi(t.structuredContent,n.outputValidator);r.success||console.warn(`[Web Model Context] Output validation failed for ${e}:`,r.error)}return t}catch(t){return console.error(`[Web Model Context] Error executing tool ${e}:`,t),{content:[{type:`text`,text:`Error: ${t instanceof Error?t.message:String(t)}`}],isError:!0}}}listTools(){return Array.from(this.bridge.tools.values()).map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema,...e.outputSchema&&{outputSchema:e.outputSchema},...e.annotations&&{annotations:e.annotations}}))}};function Ei(e){console.log(`[Web Model Context] Initializing MCP bridge`);let n=window.location.hostname||`localhost`,r=e?.transport,i=(e,t)=>{e.setRequestHandler(bn,async()=>(console.log(`[MCP Bridge] Handling list_tools request`),{tools:t.modelContext.listTools()})),e.setRequestHandler(Cn,async e=>{console.log(`[MCP Bridge] Handling call_tool request: ${e.params.name}`);let n=e.params.name,r=e.params.arguments||{};try{let e=await t.modelContext.executeTool(n,r);return{content:e.content,isError:e.isError}}catch(e){throw console.error(`[MCP Bridge] Error calling tool ${n}:`,e),e}})},a=r?.create?.();if(a){console.log(`[Web Model Context] Using custom transport`);let e=new Rr({name:n,version:`1.0.0`},{capabilities:{tools:{listChanged:!0}}}),t={tabServer:e,tools:new Map,modelContext:void 0,isInitialized:!0};return t.modelContext=new Ti(t),i(e,t),e.connect(a),console.log(`[Web Model Context] MCP server connected with custom transport`),t}console.log(`[Web Model Context] Using dual-server mode`);let o=r?.tabServer!==!1,s=new Rr({name:`${n}-tab`,version:`1.0.0`},{capabilities:{tools:{listChanged:!0}}}),c={tabServer:s,tools:new Map,modelContext:void 0,isInitialized:!0};if(c.modelContext=new Ti(c),i(s,c),o){let{allowedOrigins:e,...n}=typeof r?.tabServer==`object`?r.tabServer:{},i=new t.TabServerTransport({allowedOrigins:e??[`*`],...n});s.connect(i),console.log(`[Web Model Context] Tab server connected`)}let l=typeof window<`u`&&window.parent!==window,u=r?.iframeServer;if(u!==!1&&(u!==void 0||l)){console.log(`[Web Model Context] Enabling iframe server`);let e=new Rr({name:`${n}-iframe`,version:`1.0.0`},{capabilities:{tools:{listChanged:!0}}});i(e,c);let{allowedOrigins:r,...a}=typeof u==`object`?u:{},o=new t.IframeChildTransport({allowedOrigins:r??[`*`],...a});e.connect(o),c.iframeServer=e,console.log(`[Web Model Context] Iframe server connected`)}return c}function Di(e){if(typeof window>`u`){console.warn(`[Web Model Context] Not in browser environment, skipping initialization`);return}let t=e??window.__webModelContextOptions,n=xi();if(n.hasNativeContext&&n.hasNativeTesting){let e=window.navigator.modelContext,n=window.navigator.modelContextTesting;if(!e||!n){console.error(`[Web Model Context] Native API detection mismatch`);return}console.log(`✅ [Web Model Context] Native Chromium API detected`),console.log(` Using native implementation with MCP bridge synchronization`),console.log(` Native API will automatically collect tools from embedded iframes`);try{let r=Ei(t);r.modelContext=new Si(r,e,n),r.modelContextTesting=n,Object.defineProperty(window,`__mcpBridge`,{value:r,writable:!1,configurable:!0}),console.log(`✅ [Web Model Context] MCP bridge synced with native API`),console.log(` MCP clients will receive automatic tool updates from native registry`)}catch(e){throw console.error(`[Web Model Context] Failed to initialize native adapter:`,e),e}return}if(n.hasNativeContext&&!n.hasNativeTesting){console.warn(`[Web Model Context] Partial native API detected`),console.warn(` navigator.modelContext exists but navigator.modelContextTesting is missing`),console.warn(` Cannot sync with native API. Please enable experimental features:`),console.warn(` - Navigate to chrome://flags`),console.warn(` - Enable "Experimental Web Platform Features"`),console.warn(` - Or launch with: --enable-experimental-web-platform-features`),console.warn(` Skipping initialization to avoid conflicts`);return}if(window.navigator.modelContext){console.warn(`[Web Model Context] window.navigator.modelContext already exists, skipping initialization`);return}console.log(`[Web Model Context] Native API not detected, installing polyfill`);try{let e=Ei(t);Object.defineProperty(window.navigator,`modelContext`,{value:e.modelContext,writable:!1,configurable:!1}),Object.defineProperty(window,`__mcpBridge`,{value:e,writable:!1,configurable:!0}),console.log(`✅ [Web Model Context] window.navigator.modelContext initialized successfully`),console.log(`[Model Context Testing] Installing polyfill`),console.log(` 💡 To use the native implementation in Chromium:`),console.log(` - Navigate to chrome://flags`),console.log(` - Enable "Experimental Web Platform Features"`),console.log(` - Or launch with: --enable-experimental-web-platform-features`);let n=new wi(e);e.modelContextTesting=n,e.modelContext.setTestingAPI(n),Object.defineProperty(window.navigator,`modelContextTesting`,{value:n,writable:!1,configurable:!0}),console.log(`✅ [Model Context Testing] Polyfill installed at window.navigator.modelContextTesting`)}catch(e){throw console.error(`[Web Model Context] Failed to initialize:`,e),e}}function Oi(){if(!(typeof window>`u`)){if(window.__mcpBridge)try{window.__mcpBridge.tabServer.close(),window.__mcpBridge.iframeServer&&window.__mcpBridge.iframeServer.close()}catch(e){console.warn(`[Web Model Context] Error closing MCP servers:`,e)}delete window.navigator.modelContext,delete window.navigator.modelContextTesting,delete window.__mcpBridge,console.log(`[Web Model Context] Cleaned up`)}}function ki(e,t){return e?t?{...e,...t,tabServer:{...e.tabServer??{},...t.tabServer??{}}}:e:t}function Ai(e,t){return e?t?{...e,...t,transport:ki(e.transport??{},t.transport??{})}:e:t}function ji(e){if(!e||!e.dataset)return;let{dataset:t}=e;if(t.webmcpOptions)try{return JSON.parse(t.webmcpOptions)}catch(e){console.error(`[Web Model Context] Invalid JSON in data-webmcp-options:`,e);return}let n={},r=!1;t.webmcpAutoInitialize!==void 0&&(n.autoInitialize=t.webmcpAutoInitialize!==`false`,r=!0);let i={},a=!1;if(t.webmcpAllowedOrigins){let e=t.webmcpAllowedOrigins.split(`,`).map(e=>e.trim()).filter(e=>e.length>0);e.length>0&&(i.allowedOrigins=e,r=!0,a=!0)}return t.webmcpChannelId&&(i.channelId=t.webmcpChannelId,r=!0,a=!0),a&&(n.transport={...n.transport??{},tabServer:{...n.transport?.tabServer??{},...i}}),r?n:void 0}if(typeof window<`u`&&typeof document<`u`){let e=window.__webModelContextOptions,t=document.currentScript,n=ji(t),r=Ai(e,n)??e??n;r&&(window.__webModelContextOptions=r);let i=r?.autoInitialize!==!1;try{i&&Di(r)}catch(e){console.error(`[Web Model Context] Auto-initialization failed:`,e)}}return e.cleanupWebModelContext=Oi,e.initializeWebModelContext=Di,e})({},__mcp_b_transports);
|