@mcp-b/global 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +334 -1553
- package/dist/index.d.ts +30 -81
- package/dist/index.d.ts.map +1 -1
- package/dist/index.iife.js +6 -17
- package/dist/index.js +110 -1835
- package/dist/index.js.map +1 -1
- package/package.json +11 -17
- package/dist/testing.d.ts +0 -71
- package/dist/testing.d.ts.map +0 -1
- package/dist/testing.js +0 -48
- package/dist/testing.js.map +0 -1
- package/dist/types-DemXxUoc.d.ts +0 -1036
- package/dist/types-DemXxUoc.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
[](https://bundlephobia.com/package/@mcp-b/global)
|
|
9
9
|
[](https://github.com/nicolo-ribaudo/model-context-protocol-api)
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
**[Full Documentation](https://docs.mcp-b.ai/packages/global)** | **[Quick Start](https://docs.mcp-b.ai/quickstart)** | **[Tool Registration](https://docs.mcp-b.ai/concepts/tool-registration)**
|
|
12
12
|
|
|
13
13
|
**@mcp-b/global** implements the [W3C Web Model Context API](https://github.com/nicolo-ribaudo/model-context-protocol-api) (`navigator.modelContext`) specification, allowing AI agents like Claude, ChatGPT, Gemini, Cursor, and Copilot to discover and call functions on your website.
|
|
14
14
|
|
|
@@ -20,53 +20,38 @@
|
|
|
20
20
|
| **Drop-in IIFE** | Add AI capabilities with a single `<script>` tag - no build step |
|
|
21
21
|
| **Native Chromium Support** | Auto-detects and uses native browser implementation when available |
|
|
22
22
|
| **Dual Transport** | Works with both same-window clients AND parent pages (iframe support) |
|
|
23
|
-
| **
|
|
23
|
+
| **Strict Core Semantics** | `provideContext()` replaces tool context and `registerTool()` is name-based |
|
|
24
24
|
| **Works with Any AI** | Claude, ChatGPT, Gemini, Cursor, Copilot, and any MCP client |
|
|
25
25
|
|
|
26
|
-
##
|
|
26
|
+
## Package Selection
|
|
27
27
|
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
- **Content Management**: Let AI edit, publish, and organize content
|
|
32
|
-
- **Embedded Widgets**: AI tools accessible from parent pages via iframes
|
|
28
|
+
- Use `@mcp-b/webmcp-types` when you only need strict WebMCP type definitions.
|
|
29
|
+
- Use `@mcp-b/webmcp-polyfill` when you only need strict WebMCP runtime polyfill behavior.
|
|
30
|
+
- Use `@mcp-b/global` when you want MCPB integration features (bridge transport, prompts/resources, testing helpers, extension APIs).
|
|
33
31
|
|
|
34
|
-
##
|
|
32
|
+
## Quick Start
|
|
35
33
|
|
|
36
|
-
### Via IIFE Script Tag (
|
|
37
|
-
|
|
38
|
-
The **IIFE (Immediately Invoked Function Expression)** version bundles everything into a single file and auto-initializes when loaded. Perfect for simple HTML pages or prototyping.
|
|
39
|
-
|
|
40
|
-
Add the script to your HTML `<head>`:
|
|
34
|
+
### Via IIFE Script Tag (No Build Required)
|
|
41
35
|
|
|
42
36
|
```html
|
|
43
37
|
<!DOCTYPE html>
|
|
44
38
|
<html>
|
|
45
39
|
<head>
|
|
46
|
-
<!-- IIFE version - bundles all dependencies, auto-initializes -->
|
|
47
40
|
<script src="https://unpkg.com/@mcp-b/global@latest/dist/index.iife.js"></script>
|
|
48
41
|
</head>
|
|
49
42
|
<body>
|
|
50
43
|
<h1>My AI-Powered App</h1>
|
|
51
44
|
|
|
52
45
|
<script>
|
|
53
|
-
|
|
54
|
-
// Register tools with AI agents
|
|
55
|
-
window.navigator.modelContext.provideContext({
|
|
46
|
+
navigator.modelContext.provideContext({
|
|
56
47
|
tools: [
|
|
57
48
|
{
|
|
58
49
|
name: "get-page-title",
|
|
59
50
|
description: "Get the current page title",
|
|
60
|
-
inputSchema: {
|
|
61
|
-
type: "object",
|
|
62
|
-
properties: {}
|
|
63
|
-
},
|
|
51
|
+
inputSchema: { type: "object", properties: {} },
|
|
64
52
|
async execute() {
|
|
65
53
|
return {
|
|
66
|
-
content: [{
|
|
67
|
-
type: "text",
|
|
68
|
-
text: document.title
|
|
69
|
-
}]
|
|
54
|
+
content: [{ type: "text", text: document.title }]
|
|
70
55
|
};
|
|
71
56
|
}
|
|
72
57
|
}
|
|
@@ -77,43 +62,23 @@ Add the script to your HTML `<head>`:
|
|
|
77
62
|
</html>
|
|
78
63
|
```
|
|
79
64
|
|
|
80
|
-
**
|
|
81
|
-
-
|
|
82
|
-
-
|
|
83
|
-
- ✅ **No build step** - Just drop it in your HTML
|
|
84
|
-
- ✅ **Works everywhere** - Compatible with all modern browsers
|
|
85
|
-
- ✅ **Global access** - Also exposes `window.WebMCP` for advanced usage
|
|
65
|
+
- **Self-contained** - All dependencies bundled (285KB minified)
|
|
66
|
+
- **Auto-initializes** - `navigator.modelContext` ready immediately
|
|
67
|
+
- **No build step** - Just drop it in your HTML
|
|
86
68
|
|
|
87
|
-
### Via ES Module
|
|
88
|
-
|
|
89
|
-
If you prefer ES modules and have a build system, use the ESM version:
|
|
69
|
+
### Via ES Module
|
|
90
70
|
|
|
91
71
|
```html
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
<script type="module">
|
|
97
|
-
import '@mcp-b/global';
|
|
98
|
-
|
|
99
|
-
// window.navigator.modelContext is now available
|
|
100
|
-
window.navigator.modelContext.provideContext({
|
|
101
|
-
tools: [/* your tools */]
|
|
102
|
-
});
|
|
103
|
-
</script>
|
|
104
|
-
</head>
|
|
105
|
-
<body>
|
|
106
|
-
<h1>My AI-Powered App</h1>
|
|
107
|
-
</body>
|
|
108
|
-
</html>
|
|
72
|
+
<script type="module">
|
|
73
|
+
import '@mcp-b/global';
|
|
74
|
+
navigator.modelContext.provideContext({ tools: [/* your tools */] });
|
|
75
|
+
</script>
|
|
109
76
|
```
|
|
110
77
|
|
|
111
|
-
|
|
78
|
+
The ESM version is smaller (~16KB) but doesn't bundle dependencies.
|
|
112
79
|
|
|
113
80
|
### Via NPM
|
|
114
81
|
|
|
115
|
-
For applications using a bundler (Vite, Webpack, etc.):
|
|
116
|
-
|
|
117
82
|
```bash
|
|
118
83
|
npm install @mcp-b/global
|
|
119
84
|
```
|
|
@@ -121,1654 +86,470 @@ npm install @mcp-b/global
|
|
|
121
86
|
```javascript
|
|
122
87
|
import '@mcp-b/global';
|
|
123
88
|
|
|
124
|
-
|
|
125
|
-
window.navigator.modelContext.provideContext({
|
|
89
|
+
navigator.modelContext.provideContext({
|
|
126
90
|
tools: [/* your tools */]
|
|
127
91
|
});
|
|
128
92
|
```
|
|
129
93
|
|
|
130
|
-
##
|
|
131
|
-
|
|
132
|
-
The Web Model Context API follows the same patterns as other browser APIs. Here's how to use it as a traditional web standard:
|
|
133
|
-
|
|
134
|
-
### Basic Pattern (Vanilla JavaScript)
|
|
135
|
-
|
|
136
|
-
```html
|
|
137
|
-
<!DOCTYPE html>
|
|
138
|
-
<html>
|
|
139
|
-
<head>
|
|
140
|
-
<title>Web Model Context API Example</title>
|
|
141
|
-
<script src="https://unpkg.com/@mcp-b/global@latest/dist/index.iife.js"></script>
|
|
142
|
-
</head>
|
|
143
|
-
<body>
|
|
144
|
-
<h1>Counter App</h1>
|
|
145
|
-
<p>Count: <span id="count">0</span></p>
|
|
146
|
-
<button id="increment">+</button>
|
|
147
|
-
<button id="decrement">-</button>
|
|
148
|
-
|
|
149
|
-
<script>
|
|
150
|
-
// State
|
|
151
|
-
let count = 0;
|
|
152
|
-
|
|
153
|
-
// DOM elements
|
|
154
|
-
const countEl = document.getElementById('count');
|
|
155
|
-
const incrementBtn = document.getElementById('increment');
|
|
156
|
-
const decrementBtn = document.getElementById('decrement');
|
|
157
|
-
|
|
158
|
-
// Update UI
|
|
159
|
-
function updateUI() {
|
|
160
|
-
countEl.textContent = count;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Button handlers
|
|
164
|
-
incrementBtn.addEventListener('click', () => { count++; updateUI(); });
|
|
165
|
-
decrementBtn.addEventListener('click', () => { count--; updateUI(); });
|
|
166
|
-
|
|
167
|
-
// Feature detection (like navigator.geolocation)
|
|
168
|
-
if ('modelContext' in navigator) {
|
|
169
|
-
// Register tools with the Web Model Context API
|
|
170
|
-
navigator.modelContext.provideContext({
|
|
171
|
-
tools: [
|
|
172
|
-
{
|
|
173
|
-
name: 'counter_get',
|
|
174
|
-
description: 'Get the current counter value',
|
|
175
|
-
inputSchema: { type: 'object', properties: {} },
|
|
176
|
-
execute: async () => ({
|
|
177
|
-
content: [{ type: 'text', text: String(count) }]
|
|
178
|
-
})
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
name: 'counter_set',
|
|
182
|
-
description: 'Set the counter to a specific value',
|
|
183
|
-
inputSchema: {
|
|
184
|
-
type: 'object',
|
|
185
|
-
properties: {
|
|
186
|
-
value: { type: 'number', description: 'The new counter value' }
|
|
187
|
-
},
|
|
188
|
-
required: ['value']
|
|
189
|
-
},
|
|
190
|
-
execute: async ({ value }) => {
|
|
191
|
-
count = value;
|
|
192
|
-
updateUI();
|
|
193
|
-
return {
|
|
194
|
-
content: [{ type: 'text', text: `Counter set to ${count}` }]
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
},
|
|
198
|
-
{
|
|
199
|
-
name: 'counter_increment',
|
|
200
|
-
description: 'Increment the counter by a specified amount',
|
|
201
|
-
inputSchema: {
|
|
202
|
-
type: 'object',
|
|
203
|
-
properties: {
|
|
204
|
-
amount: { type: 'number', description: 'Amount to increment by', default: 1 }
|
|
205
|
-
}
|
|
206
|
-
},
|
|
207
|
-
execute: async ({ amount = 1 }) => {
|
|
208
|
-
count += amount;
|
|
209
|
-
updateUI();
|
|
210
|
-
return {
|
|
211
|
-
content: [{ type: 'text', text: `Counter incremented to ${count}` }]
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
]
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
console.log('Web Model Context API: Tools registered');
|
|
219
|
-
} else {
|
|
220
|
-
console.warn('Web Model Context API not supported');
|
|
221
|
-
}
|
|
222
|
-
</script>
|
|
223
|
-
</body>
|
|
224
|
-
</html>
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
### Single Tool Registration Pattern
|
|
228
|
-
|
|
229
|
-
Like `navigator.permissions.query()`, you can register tools one at a time:
|
|
94
|
+
## API Reference
|
|
230
95
|
|
|
231
|
-
|
|
232
|
-
// Feature detection
|
|
233
|
-
if ('modelContext' in navigator) {
|
|
234
|
-
// Register a single tool (returns an object with unregister method)
|
|
235
|
-
const registration = navigator.modelContext.registerTool({
|
|
236
|
-
name: 'get_page_info',
|
|
237
|
-
description: 'Get information about the current page',
|
|
238
|
-
inputSchema: { type: 'object', properties: {} },
|
|
239
|
-
execute: async () => ({
|
|
240
|
-
content: [{
|
|
241
|
-
type: 'text',
|
|
242
|
-
text: JSON.stringify({
|
|
243
|
-
title: document.title,
|
|
244
|
-
url: location.href,
|
|
245
|
-
timestamp: new Date().toISOString()
|
|
246
|
-
}, null, 2)
|
|
247
|
-
}]
|
|
248
|
-
})
|
|
249
|
-
});
|
|
96
|
+
### Functions
|
|
250
97
|
|
|
251
|
-
|
|
252
|
-
// registration.unregister();
|
|
253
|
-
}
|
|
254
|
-
```
|
|
98
|
+
#### `initializeWebModelContext(options?)`
|
|
255
99
|
|
|
256
|
-
|
|
100
|
+
Initializes the global adapter. Replaces `navigator.modelContext` with a `BrowserMcpServer` instance that bridges WebMCP tools to the MCP protocol layer.
|
|
257
101
|
|
|
258
|
-
|
|
102
|
+
```typescript
|
|
103
|
+
import { initializeWebModelContext } from '@mcp-b/global';
|
|
259
104
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
// Optionally intercept and provide custom response
|
|
267
|
-
if (event.name === 'custom_handler') {
|
|
268
|
-
event.preventDefault();
|
|
269
|
-
event.respondWith({
|
|
270
|
-
content: [{ type: 'text', text: 'Custom response' }]
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
}
|
|
105
|
+
initializeWebModelContext({
|
|
106
|
+
transport: {
|
|
107
|
+
tabServer: { allowedOrigins: ['https://example.com'] },
|
|
108
|
+
},
|
|
109
|
+
nativeModelContextBehavior: 'preserve',
|
|
110
|
+
});
|
|
275
111
|
```
|
|
276
112
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
<!DOCTYPE html>
|
|
283
|
-
<html lang="en">
|
|
284
|
-
<head>
|
|
285
|
-
<meta charset="UTF-8">
|
|
286
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
287
|
-
<title>WebMCP Demo</title>
|
|
288
|
-
<script src="https://unpkg.com/@mcp-b/global@latest/dist/index.iife.js"></script>
|
|
289
|
-
<style>
|
|
290
|
-
body { font-family: system-ui; max-width: 600px; margin: 2rem auto; padding: 0 1rem; }
|
|
291
|
-
.card { border: 1px solid #ddd; border-radius: 8px; padding: 1rem; margin: 1rem 0; }
|
|
292
|
-
button { padding: 0.5rem 1rem; margin: 0.25rem; cursor: pointer; }
|
|
293
|
-
#log { font-family: monospace; font-size: 0.85rem; background: #f5f5f5; padding: 1rem; max-height: 200px; overflow-y: auto; }
|
|
294
|
-
</style>
|
|
295
|
-
</head>
|
|
296
|
-
<body>
|
|
297
|
-
<h1>🤖 WebMCP Demo</h1>
|
|
113
|
+
**Behavior:**
|
|
114
|
+
- Only operates in browser environments
|
|
115
|
+
- Idempotent - calling multiple times is a no-op after first initialization
|
|
116
|
+
- Preserves native `navigator.modelContext` by default (configurable)
|
|
117
|
+
- Auto-called on import unless `window.__webModelContextOptions.autoInitialize` is `false`
|
|
298
118
|
|
|
299
|
-
|
|
300
|
-
<h2>Notes App</h2>
|
|
301
|
-
<input type="text" id="noteInput" placeholder="Enter a note..." style="width: 100%; padding: 0.5rem; box-sizing: border-box;">
|
|
302
|
-
<button id="addNote">Add Note</button>
|
|
303
|
-
<ul id="notesList"></ul>
|
|
304
|
-
</div>
|
|
119
|
+
#### `cleanupWebModelContext()`
|
|
305
120
|
|
|
306
|
-
|
|
307
|
-
<h3>Tool Call Log</h3>
|
|
308
|
-
<div id="log">Waiting for AI tool calls...</div>
|
|
309
|
-
</div>
|
|
121
|
+
Tears down the adapter and restores `navigator.modelContext` to its original state. Allows re-initialization.
|
|
310
122
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const notes = [];
|
|
314
|
-
|
|
315
|
-
// DOM elements
|
|
316
|
-
const noteInput = document.getElementById('noteInput');
|
|
317
|
-
const addNoteBtn = document.getElementById('addNote');
|
|
318
|
-
const notesList = document.getElementById('notesList');
|
|
319
|
-
const logEl = document.getElementById('log');
|
|
320
|
-
|
|
321
|
-
// UI functions
|
|
322
|
-
function renderNotes() {
|
|
323
|
-
notesList.innerHTML = notes.map((note, i) =>
|
|
324
|
-
`<li>${note} <button onclick="deleteNote(${i})">×</button></li>`
|
|
325
|
-
).join('');
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
function log(message) {
|
|
329
|
-
const time = new Date().toLocaleTimeString();
|
|
330
|
-
logEl.innerHTML = `[${time}] ${message}\n` + logEl.innerHTML;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// User interactions
|
|
334
|
-
addNoteBtn.addEventListener('click', () => {
|
|
335
|
-
if (noteInput.value.trim()) {
|
|
336
|
-
notes.push(noteInput.value.trim());
|
|
337
|
-
noteInput.value = '';
|
|
338
|
-
renderNotes();
|
|
339
|
-
}
|
|
340
|
-
});
|
|
123
|
+
```typescript
|
|
124
|
+
import { cleanupWebModelContext, initializeWebModelContext } from '@mcp-b/global';
|
|
341
125
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
};
|
|
126
|
+
initializeWebModelContext();
|
|
127
|
+
// ... use tools ...
|
|
128
|
+
cleanupWebModelContext();
|
|
346
129
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
navigator.modelContext.provideContext({
|
|
350
|
-
tools: [
|
|
351
|
-
{
|
|
352
|
-
name: 'notes_list',
|
|
353
|
-
description: 'Get all notes',
|
|
354
|
-
inputSchema: { type: 'object', properties: {} },
|
|
355
|
-
execute: async () => {
|
|
356
|
-
log('🔧 notes_list called');
|
|
357
|
-
return {
|
|
358
|
-
content: [{
|
|
359
|
-
type: 'text',
|
|
360
|
-
text: notes.length ? notes.map((n, i) => `${i + 1}. ${n}`).join('\n') : 'No notes yet'
|
|
361
|
-
}]
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
},
|
|
365
|
-
{
|
|
366
|
-
name: 'notes_add',
|
|
367
|
-
description: 'Add a new note',
|
|
368
|
-
inputSchema: {
|
|
369
|
-
type: 'object',
|
|
370
|
-
properties: {
|
|
371
|
-
text: { type: 'string', description: 'The note text' }
|
|
372
|
-
},
|
|
373
|
-
required: ['text']
|
|
374
|
-
},
|
|
375
|
-
execute: async ({ text }) => {
|
|
376
|
-
log(`🔧 notes_add called: "${text}"`);
|
|
377
|
-
notes.push(text);
|
|
378
|
-
renderNotes();
|
|
379
|
-
return {
|
|
380
|
-
content: [{ type: 'text', text: `Added note: "${text}"` }]
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
},
|
|
384
|
-
{
|
|
385
|
-
name: 'notes_delete',
|
|
386
|
-
description: 'Delete a note by index (1-based)',
|
|
387
|
-
inputSchema: {
|
|
388
|
-
type: 'object',
|
|
389
|
-
properties: {
|
|
390
|
-
index: { type: 'number', description: 'Note index (1-based)' }
|
|
391
|
-
},
|
|
392
|
-
required: ['index']
|
|
393
|
-
},
|
|
394
|
-
execute: async ({ index }) => {
|
|
395
|
-
log(`🔧 notes_delete called: index ${index}`);
|
|
396
|
-
if (index < 1 || index > notes.length) {
|
|
397
|
-
return { content: [{ type: 'text', text: 'Invalid index' }], isError: true };
|
|
398
|
-
}
|
|
399
|
-
const deleted = notes.splice(index - 1, 1)[0];
|
|
400
|
-
renderNotes();
|
|
401
|
-
return {
|
|
402
|
-
content: [{ type: 'text', text: `Deleted: "${deleted}"` }]
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
},
|
|
406
|
-
{
|
|
407
|
-
name: 'notes_clear',
|
|
408
|
-
description: 'Delete all notes',
|
|
409
|
-
inputSchema: { type: 'object', properties: {} },
|
|
410
|
-
execute: async () => {
|
|
411
|
-
log('🔧 notes_clear called');
|
|
412
|
-
const count = notes.length;
|
|
413
|
-
notes.length = 0;
|
|
414
|
-
renderNotes();
|
|
415
|
-
return {
|
|
416
|
-
content: [{ type: 'text', text: `Cleared ${count} notes` }]
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
]
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
log('✅ Web Model Context API initialized');
|
|
424
|
-
log('📋 Tools: notes_list, notes_add, notes_delete, notes_clear');
|
|
425
|
-
} else {
|
|
426
|
-
log('❌ Web Model Context API not available');
|
|
427
|
-
}
|
|
428
|
-
</script>
|
|
429
|
-
</body>
|
|
430
|
-
</html>
|
|
130
|
+
// Can re-initialize after cleanup
|
|
131
|
+
initializeWebModelContext();
|
|
431
132
|
```
|
|
432
133
|
|
|
433
|
-
|
|
434
|
-
- **Feature detection** using `'modelContext' in navigator`
|
|
435
|
-
- **Tool registration** via `navigator.modelContext.provideContext()`
|
|
436
|
-
- **Standard input schemas** following JSON Schema specification
|
|
437
|
-
- **Async execute functions** returning MCP-compatible responses
|
|
438
|
-
- **Real-time UI updates** when AI agents call tools
|
|
439
|
-
|
|
440
|
-
## ⚙️ Configuration
|
|
441
|
-
|
|
442
|
-
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:
|
|
443
|
-
|
|
444
|
-
- **Disable auto init**: Set `window.__webModelContextOptions = { autoInitialize: false }` before importing, then call `initializeWebModelContext()` manually.
|
|
445
|
-
- **Configure via script tag**: When using the IIFE build, pass options through data attributes:
|
|
446
|
-
```html
|
|
447
|
-
<script
|
|
448
|
-
src="https://unpkg.com/@mcp-b/global@latest/dist/index.iife.js"
|
|
449
|
-
data-webmcp-auto-initialize="false"
|
|
450
|
-
data-webmcp-allowed-origins="https://example.com,https://docs.example.com"
|
|
451
|
-
></script>
|
|
452
|
-
<!-- Later in the page -->
|
|
453
|
-
<script>
|
|
454
|
-
window.navigator.modelContext.provideContext({ tools: [] });
|
|
455
|
-
</script>
|
|
456
|
-
```
|
|
457
|
-
Use `data-webmcp-options='{"transport":{"tabServer":{"allowedOrigins":["https://example.com"]}}}'` for advanced JSON configuration.
|
|
458
|
-
- **Supported data attributes**
|
|
459
|
-
- `data-webmcp-auto-initialize="false"`: Skip automatic setup.
|
|
460
|
-
- `data-webmcp-allowed-origins="https://a.com,https://b.com"`: Override `tabServer.allowedOrigins`.
|
|
461
|
-
- `data-webmcp-channel-id="custom-channel"`: Set the Tab transport channel.
|
|
462
|
-
|
|
463
|
-
### Dual-Server Mode (Tab + Iframe)
|
|
134
|
+
### `navigator.modelContext` Methods
|
|
464
135
|
|
|
465
|
-
|
|
136
|
+
After initialization, `navigator.modelContext` exposes these methods:
|
|
466
137
|
|
|
467
|
-
|
|
468
|
-
2. **Iframe Server** (`IframeChildTransport`) - Auto-enabled when running in an iframe (when `window.parent !== window`)
|
|
138
|
+
#### `provideContext(options?)`
|
|
469
139
|
|
|
470
|
-
|
|
471
|
-
- Same-window clients (e.g., browser extension content scripts)
|
|
472
|
-
- Parent page (when running in an iframe)
|
|
140
|
+
Replaces all currently registered tools with a new set. This is an atomic replacement - all previous tools are removed first.
|
|
473
141
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
When your app runs in an iframe, both servers are automatically enabled:
|
|
477
|
-
|
|
478
|
-
```ts
|
|
479
|
-
// In iframe: Auto-initializes with both servers
|
|
480
|
-
import '@mcp-b/global';
|
|
481
|
-
|
|
482
|
-
// Register tools - they're automatically available to:
|
|
483
|
-
// 1. Same-window clients (via TabServerTransport)
|
|
484
|
-
// 2. Parent page (via IframeChildTransport)
|
|
485
|
-
window.navigator.modelContext.provideContext({
|
|
142
|
+
```typescript
|
|
143
|
+
navigator.modelContext.provideContext({
|
|
486
144
|
tools: [
|
|
487
145
|
{
|
|
488
|
-
name:
|
|
489
|
-
description:
|
|
490
|
-
inputSchema: {
|
|
146
|
+
name: 'search-products',
|
|
147
|
+
description: 'Search the product catalog by query',
|
|
148
|
+
inputSchema: {
|
|
149
|
+
type: 'object',
|
|
150
|
+
properties: {
|
|
151
|
+
query: { type: 'string', description: 'Search query' },
|
|
152
|
+
limit: { type: 'integer', description: 'Max results' },
|
|
153
|
+
},
|
|
154
|
+
required: ['query'],
|
|
155
|
+
},
|
|
156
|
+
async execute(args) {
|
|
157
|
+
const results = await searchProducts(args.query, args.limit ?? 10);
|
|
158
|
+
return {
|
|
159
|
+
content: [{ type: 'text', text: JSON.stringify(results) }],
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: 'get-cart',
|
|
165
|
+
description: 'Get the current shopping cart contents',
|
|
166
|
+
inputSchema: { type: 'object', properties: {} },
|
|
491
167
|
async execute() {
|
|
492
168
|
return {
|
|
493
|
-
content: [{ type:
|
|
169
|
+
content: [{ type: 'text', text: JSON.stringify(getCart()) }],
|
|
494
170
|
};
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
]
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
],
|
|
498
174
|
});
|
|
499
175
|
```
|
|
500
176
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
You can customize or disable the iframe server:
|
|
177
|
+
#### `registerTool(tool)`
|
|
504
178
|
|
|
505
|
-
|
|
506
|
-
import { initializeWebModelContext } from '@mcp-b/global';
|
|
179
|
+
Registers a single tool. The tool name must be unique - throws if a tool with the same name already exists.
|
|
507
180
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
181
|
+
```typescript
|
|
182
|
+
navigator.modelContext.registerTool({
|
|
183
|
+
name: 'add-to-cart',
|
|
184
|
+
description: 'Add a product to the shopping cart',
|
|
185
|
+
inputSchema: {
|
|
186
|
+
type: 'object',
|
|
187
|
+
properties: {
|
|
188
|
+
productId: { type: 'string' },
|
|
189
|
+
quantity: { type: 'integer' },
|
|
514
190
|
},
|
|
191
|
+
required: ['productId'],
|
|
515
192
|
},
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
iframeServer: false, // Disable iframe server
|
|
522
|
-
},
|
|
523
|
-
});
|
|
524
|
-
|
|
525
|
-
// Disable tab server (only Iframe server runs)
|
|
526
|
-
initializeWebModelContext({
|
|
527
|
-
transport: {
|
|
528
|
-
tabServer: false, // Disable tab server
|
|
529
|
-
iframeServer: {
|
|
530
|
-
allowedOrigins: ['https://parent-app.com'],
|
|
531
|
-
},
|
|
193
|
+
async execute(args) {
|
|
194
|
+
const item = await addToCart(args.productId, args.quantity ?? 1);
|
|
195
|
+
return {
|
|
196
|
+
content: [{ type: 'text', text: `Added ${item.name} to cart` }],
|
|
197
|
+
};
|
|
532
198
|
},
|
|
533
199
|
});
|
|
534
200
|
```
|
|
535
201
|
|
|
536
|
-
|
|
202
|
+
#### `unregisterTool(name)`
|
|
537
203
|
|
|
538
|
-
|
|
204
|
+
Removes a tool by name.
|
|
539
205
|
|
|
540
|
-
```
|
|
541
|
-
|
|
542
|
-
|
|
206
|
+
```typescript
|
|
207
|
+
navigator.modelContext.unregisterTool('add-to-cart');
|
|
208
|
+
```
|
|
543
209
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
210
|
+
#### `clearContext()`
|
|
211
|
+
|
|
212
|
+
Removes all registered tools.
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
navigator.modelContext.clearContext();
|
|
549
216
|
```
|
|
550
217
|
|
|
551
|
-
|
|
218
|
+
#### `listTools()`
|
|
552
219
|
|
|
553
|
-
|
|
220
|
+
Returns metadata for all registered tools (without execute functions).
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
const tools = navigator.modelContext.listTools();
|
|
224
|
+
// [{ name: 'search-products', description: '...', inputSchema: {...} }, ...]
|
|
225
|
+
```
|
|
554
226
|
|
|
555
|
-
|
|
227
|
+
#### `callTool(params)`
|
|
556
228
|
|
|
557
|
-
|
|
229
|
+
Executes a registered tool by name.
|
|
558
230
|
|
|
559
|
-
|
|
231
|
+
```typescript
|
|
232
|
+
const result = await navigator.modelContext.callTool({
|
|
233
|
+
name: 'search-products',
|
|
234
|
+
arguments: { query: 'laptop', limit: 5 },
|
|
235
|
+
});
|
|
236
|
+
// { content: [{ type: 'text', text: '...' }] }
|
|
237
|
+
```
|
|
560
238
|
|
|
561
|
-
|
|
562
|
-
- Uses native Chromium implementation
|
|
563
|
-
- Creates MCP bridge and syncs tools automatically
|
|
564
|
-
- Registers callback to listen for native tool changes
|
|
565
|
-
- MCP clients stay synchronized with native tool registry
|
|
239
|
+
### Tool Descriptor
|
|
566
240
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
241
|
+
| Property | Type | Required | Description |
|
|
242
|
+
|----------|------|----------|-------------|
|
|
243
|
+
| `name` | `string` | Yes | Unique identifier for the tool |
|
|
244
|
+
| `description` | `string` | Yes | Natural language description of what the tool does |
|
|
245
|
+
| `inputSchema` | `InputSchema` | No | JSON Schema describing accepted input. Defaults to `{ type: 'object', properties: {} }` |
|
|
246
|
+
| `outputSchema` | `InputSchema` | No | JSON Schema describing the output payload shape |
|
|
247
|
+
| `annotations` | `ToolAnnotations` | No | Hints about tool behavior for LLM planners |
|
|
248
|
+
| `execute` | `(args, client) => Promise<ToolResponse>` | Yes | Async function implementing the tool logic |
|
|
570
249
|
|
|
571
|
-
|
|
250
|
+
### Tool Response Format
|
|
572
251
|
|
|
573
|
-
|
|
252
|
+
Tools return a `ToolResponse` object:
|
|
574
253
|
|
|
575
|
-
|
|
254
|
+
```typescript
|
|
255
|
+
// Success
|
|
256
|
+
{
|
|
257
|
+
content: [{ type: 'text', text: 'Result here' }]
|
|
258
|
+
}
|
|
576
259
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
260
|
+
// Error
|
|
261
|
+
{
|
|
262
|
+
content: [{ type: 'text', text: 'Something went wrong' }],
|
|
263
|
+
isError: true
|
|
264
|
+
}
|
|
265
|
+
```
|
|
582
266
|
|
|
583
|
-
|
|
267
|
+
## Configuration
|
|
584
268
|
|
|
585
|
-
|
|
269
|
+
### `WebModelContextInitOptions`
|
|
586
270
|
|
|
587
271
|
```typescript
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
272
|
+
interface WebModelContextInitOptions {
|
|
273
|
+
transport?: TransportConfiguration;
|
|
274
|
+
autoInitialize?: boolean;
|
|
275
|
+
nativeModelContextBehavior?: 'preserve' | 'patch';
|
|
276
|
+
installTestingShim?: boolean | 'always' | 'if-missing';
|
|
277
|
+
}
|
|
593
278
|
```
|
|
594
279
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
-
|
|
280
|
+
| Option | Default | Description |
|
|
281
|
+
|--------|---------|-------------|
|
|
282
|
+
| `transport` | Auto-detect | Transport layer configuration (tab server and/or iframe) |
|
|
283
|
+
| `autoInitialize` | `true` | Whether to auto-initialize on import |
|
|
284
|
+
| `nativeModelContextBehavior` | `'preserve'` | `'preserve'` keeps native implementation untouched. `'patch'` replaces it with a BrowserMcpServer that mirrors to the native object |
|
|
285
|
+
| `installTestingShim` | `'if-missing'` | Controls `navigator.modelContextTesting` installation. Only installs when not already present natively |
|
|
601
286
|
|
|
602
|
-
###
|
|
287
|
+
### Transport Configuration
|
|
603
288
|
|
|
604
|
-
|
|
605
|
-
# Method 1: Launch with flag
|
|
606
|
-
chromium --enable-experimental-web-platform-features
|
|
289
|
+
The transport is auto-selected based on context:
|
|
607
290
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
291
|
+
```typescript
|
|
292
|
+
interface TransportConfiguration {
|
|
293
|
+
tabServer?: Partial<TabServerTransportOptions> | false;
|
|
294
|
+
iframeServer?: Partial<IframeChildTransportOptions> | false;
|
|
295
|
+
}
|
|
612
296
|
```
|
|
613
297
|
|
|
614
|
-
|
|
298
|
+
- **In an iframe**: Uses `IframeChildTransport` to communicate with the parent page
|
|
299
|
+
- **In the main window**: Uses `TabServerTransport` for cross-tab communication
|
|
300
|
+
- Set either to `false` to disable it
|
|
615
301
|
|
|
616
302
|
```typescript
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
description: 'My tool',
|
|
623
|
-
inputSchema: { type: 'object', properties: {} },
|
|
624
|
-
async execute() {
|
|
625
|
-
return { content: [{ type: 'text', text: 'Hello!' }] };
|
|
626
|
-
}
|
|
303
|
+
// Restrict to specific origins
|
|
304
|
+
initializeWebModelContext({
|
|
305
|
+
transport: {
|
|
306
|
+
tabServer: { allowedOrigins: ['https://myapp.com'] },
|
|
307
|
+
},
|
|
627
308
|
});
|
|
628
309
|
|
|
629
|
-
//
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
310
|
+
// Disable tab transport (iframe only)
|
|
311
|
+
initializeWebModelContext({
|
|
312
|
+
transport: {
|
|
313
|
+
tabServer: false,
|
|
314
|
+
},
|
|
315
|
+
});
|
|
634
316
|
```
|
|
635
317
|
|
|
636
|
-
###
|
|
318
|
+
### Auto-Initialization
|
|
637
319
|
|
|
638
|
-
|
|
320
|
+
The package auto-initializes on import in browser environments. To customize before initialization:
|
|
639
321
|
|
|
640
322
|
```html
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
description: 'Tool from parent page',
|
|
649
|
-
inputSchema: { type: 'object', properties: {} },
|
|
650
|
-
async execute() {
|
|
651
|
-
return { content: [{ type: 'text', text: 'Parent tool' }] };
|
|
652
|
-
}
|
|
653
|
-
});
|
|
323
|
+
<script>
|
|
324
|
+
window.__webModelContextOptions = {
|
|
325
|
+
autoInitialize: true,
|
|
326
|
+
transport: {
|
|
327
|
+
tabServer: { allowedOrigins: ['https://myapp.com'] },
|
|
328
|
+
},
|
|
329
|
+
};
|
|
654
330
|
</script>
|
|
655
|
-
|
|
656
|
-
<iframe src="child.html"></iframe>
|
|
331
|
+
<script src="https://unpkg.com/@mcp-b/global@latest/dist/index.iife.js"></script>
|
|
657
332
|
```
|
|
658
333
|
|
|
659
|
-
|
|
660
|
-
<!-- child.html -->
|
|
661
|
-
<script type="module">
|
|
662
|
-
import '@mcp-b/global';
|
|
334
|
+
To prevent auto-initialization:
|
|
663
335
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
});
|
|
336
|
+
```html
|
|
337
|
+
<script>
|
|
338
|
+
window.__webModelContextOptions = { autoInitialize: false };
|
|
339
|
+
</script>
|
|
340
|
+
<script src="https://unpkg.com/@mcp-b/global@latest/dist/index.iife.js"></script>
|
|
341
|
+
<script>
|
|
342
|
+
// Initialize manually later
|
|
343
|
+
MCPB.initializeWebModelContext();
|
|
673
344
|
</script>
|
|
674
345
|
```
|
|
675
346
|
|
|
676
|
-
|
|
347
|
+
## Testing
|
|
677
348
|
|
|
678
|
-
|
|
349
|
+
`navigator.modelContextTesting` provides a testing shim that stays in sync with registered tools:
|
|
679
350
|
|
|
680
|
-
|
|
351
|
+
```typescript
|
|
352
|
+
// List registered tools
|
|
353
|
+
const tools = navigator.modelContextTesting?.listTools();
|
|
354
|
+
// [{ name: 'search-products', description: '...', inputSchema: '...' }]
|
|
681
355
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
MCP clients will receive automatic tool updates from native registry
|
|
356
|
+
// Execute a tool (input args as JSON string)
|
|
357
|
+
const result = await navigator.modelContextTesting?.executeTool(
|
|
358
|
+
'search-products',
|
|
359
|
+
'{"query": "laptop"}'
|
|
360
|
+
);
|
|
688
361
|
```
|
|
689
362
|
|
|
690
|
-
|
|
363
|
+
## Feature Detection
|
|
691
364
|
|
|
365
|
+
```javascript
|
|
366
|
+
if ('modelContext' in navigator) {
|
|
367
|
+
navigator.modelContext.provideContext({ tools: [...] });
|
|
368
|
+
}
|
|
692
369
|
```
|
|
693
|
-
[Web Model Context] Native API not detected, installing polyfill
|
|
694
|
-
✅ [Web Model Context] window.navigator.modelContext initialized successfully
|
|
695
|
-
[Model Context Testing] Installing polyfill
|
|
696
|
-
✅ [Model Context Testing] Polyfill installed at window.navigator.modelContextTesting
|
|
697
|
-
```
|
|
698
|
-
|
|
699
|
-
## 📖 API Reference
|
|
700
|
-
|
|
701
|
-
### Two-Bucket Tool Management System
|
|
702
|
-
|
|
703
|
-
This package uses a **two-bucket system** for tool management to support both app-level and component-level tools:
|
|
704
|
-
|
|
705
|
-
- **Bucket A (Base Tools)**: Registered via `provideContext()` - represents your app's core functionality
|
|
706
|
-
- **Bucket B (Dynamic Tools)**: Registered via `registerTool()` - component-scoped tools that persist across `provideContext()` calls
|
|
707
|
-
|
|
708
|
-
**Key behaviors:**
|
|
709
|
-
- ✅ `provideContext()` only clears Bucket A, leaving Bucket B intact
|
|
710
|
-
- ✅ `registerTool()` adds to Bucket B and persists across `provideContext()` calls
|
|
711
|
-
- ✅ Tool name collisions between buckets throw an error
|
|
712
|
-
- ✅ Cannot `unregister()` a tool that was registered via `provideContext()`
|
|
713
|
-
|
|
714
|
-
**Use case:** React components can use `registerTool()` in `useEffect()` to manage tool lifecycle independently of the app's base tools.
|
|
715
|
-
|
|
716
|
-
### `window.navigator.modelContext.provideContext(context)`
|
|
717
370
|
|
|
718
|
-
|
|
371
|
+
## Examples
|
|
719
372
|
|
|
720
|
-
|
|
721
|
-
- `context.tools` - Array of tool descriptors
|
|
373
|
+
### E-commerce: Product Search and Cart
|
|
722
374
|
|
|
723
|
-
|
|
375
|
+
```typescript
|
|
376
|
+
import '@mcp-b/global';
|
|
724
377
|
|
|
725
|
-
|
|
726
|
-
window.navigator.modelContext.provideContext({
|
|
378
|
+
navigator.modelContext.provideContext({
|
|
727
379
|
tools: [
|
|
728
380
|
{
|
|
729
|
-
name:
|
|
730
|
-
description:
|
|
381
|
+
name: 'search-products',
|
|
382
|
+
description: 'Search products by keyword, category, or price range',
|
|
731
383
|
inputSchema: {
|
|
732
|
-
type:
|
|
384
|
+
type: 'object',
|
|
733
385
|
properties: {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
},
|
|
738
|
-
priority: {
|
|
739
|
-
type: "string",
|
|
740
|
-
enum: ["low", "medium", "high"],
|
|
741
|
-
description: "Priority level"
|
|
742
|
-
}
|
|
386
|
+
query: { type: 'string', description: 'Search terms' },
|
|
387
|
+
category: { type: 'string', description: 'Product category' },
|
|
388
|
+
maxPrice: { type: 'number', description: 'Maximum price filter' },
|
|
743
389
|
},
|
|
744
|
-
required: [
|
|
390
|
+
required: ['query'],
|
|
745
391
|
},
|
|
746
|
-
async execute(
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
content: [{
|
|
752
|
-
type: "text",
|
|
753
|
-
text: `Added todo: "${text}" with ${priority} priority`
|
|
754
|
-
}]
|
|
755
|
-
};
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
]
|
|
759
|
-
});
|
|
760
|
-
```
|
|
761
|
-
|
|
762
|
-
### `window.navigator.modelContext.registerTool(tool)`
|
|
763
|
-
|
|
764
|
-
Register a single tool dynamically (Bucket B). Tools registered this way:
|
|
765
|
-
- ✅ Persist across `provideContext()` calls
|
|
766
|
-
- ✅ Perfect for component lifecycle management
|
|
767
|
-
- ✅ Can be unregistered via the returned `unregister()` function
|
|
768
|
-
- ❌ Cannot have the same name as a tool in Bucket A (provideContext)
|
|
769
|
-
|
|
770
|
-
**Parameters:**
|
|
771
|
-
- `tool` - A single tool descriptor
|
|
772
|
-
|
|
773
|
-
**Returns:**
|
|
774
|
-
- Object with `unregister()` function to remove the tool
|
|
775
|
-
|
|
776
|
-
**Example:**
|
|
777
|
-
|
|
778
|
-
```javascript
|
|
779
|
-
// Register a tool dynamically (Bucket B)
|
|
780
|
-
const registration = window.navigator.modelContext.registerTool({
|
|
781
|
-
name: "get-timestamp",
|
|
782
|
-
description: "Get the current timestamp",
|
|
783
|
-
inputSchema: {
|
|
784
|
-
type: "object",
|
|
785
|
-
properties: {}
|
|
786
|
-
},
|
|
787
|
-
async execute() {
|
|
788
|
-
return {
|
|
789
|
-
content: [{
|
|
790
|
-
type: "text",
|
|
791
|
-
text: new Date().toISOString()
|
|
792
|
-
}]
|
|
793
|
-
};
|
|
794
|
-
}
|
|
795
|
-
});
|
|
796
|
-
|
|
797
|
-
// Later, unregister the tool
|
|
798
|
-
registration.unregister();
|
|
799
|
-
|
|
800
|
-
// Note: You can call provideContext() and this tool will still be registered!
|
|
801
|
-
window.navigator.modelContext.provideContext({
|
|
802
|
-
tools: [/* other tools */]
|
|
803
|
-
});
|
|
804
|
-
// "get-timestamp" is still available because it's in Bucket B
|
|
805
|
-
```
|
|
806
|
-
|
|
807
|
-
### Tool Descriptor
|
|
808
|
-
|
|
809
|
-
Each tool must have:
|
|
810
|
-
|
|
811
|
-
| Property | Type | Description |
|
|
812
|
-
|----------|------|-------------|
|
|
813
|
-
| `name` | `string` | Unique identifier for the tool |
|
|
814
|
-
| `description` | `string` | Natural language description of what the tool does |
|
|
815
|
-
| `inputSchema` | `object` | JSON Schema defining input parameters |
|
|
816
|
-
| `outputSchema` | `object` | Optional JSON Schema defining structured output |
|
|
817
|
-
| `annotations` | `object` | Optional hints about tool behavior |
|
|
818
|
-
| `execute` | `function` | Async function that implements the tool logic |
|
|
819
|
-
|
|
820
|
-
### Output Schemas (Structured Output)
|
|
821
|
-
|
|
822
|
-
**Output schemas are essential for modern AI integrations.** Many AI providers compile tool definitions into TypeScript definitions, enabling the AI to generate type-safe responses. Without an output schema, AI agents can only return unstructured text.
|
|
823
|
-
|
|
824
|
-
**Benefits of output schemas:**
|
|
825
|
-
- **Type-safe responses** - AI generates structured JSON matching your schema
|
|
826
|
-
- **Better AI reasoning** - AI understands the expected output format
|
|
827
|
-
- **Client validation** - Responses are validated against the schema
|
|
828
|
-
- **IDE support** - TypeScript types inferred from schemas
|
|
829
|
-
|
|
830
|
-
#### Basic Output Schema Example
|
|
831
|
-
|
|
832
|
-
```javascript
|
|
833
|
-
window.navigator.modelContext.provideContext({
|
|
834
|
-
tools: [
|
|
392
|
+
async execute(args) {
|
|
393
|
+
const results = await fetch(`/api/products?q=${args.query}&cat=${args.category ?? ''}&max=${args.maxPrice ?? ''}`);
|
|
394
|
+
return { content: [{ type: 'text', text: await results.text() }] };
|
|
395
|
+
},
|
|
396
|
+
},
|
|
835
397
|
{
|
|
836
|
-
name:
|
|
837
|
-
description:
|
|
398
|
+
name: 'add-to-cart',
|
|
399
|
+
description: 'Add a product to the shopping cart',
|
|
838
400
|
inputSchema: {
|
|
839
|
-
type:
|
|
401
|
+
type: 'object',
|
|
840
402
|
properties: {
|
|
841
|
-
|
|
403
|
+
productId: { type: 'string' },
|
|
404
|
+
quantity: { type: 'integer' },
|
|
842
405
|
},
|
|
843
|
-
required: [
|
|
406
|
+
required: ['productId'],
|
|
844
407
|
},
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
email: { type: "string", description: "Email address" },
|
|
852
|
-
createdAt: { type: "string", description: "ISO date string" }
|
|
853
|
-
},
|
|
854
|
-
required: ["id", "name", "email"]
|
|
408
|
+
async execute(args) {
|
|
409
|
+
await fetch('/api/cart', {
|
|
410
|
+
method: 'POST',
|
|
411
|
+
body: JSON.stringify({ productId: args.productId, quantity: args.quantity ?? 1 }),
|
|
412
|
+
});
|
|
413
|
+
return { content: [{ type: 'text', text: `Added to cart` }] };
|
|
855
414
|
},
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
return {
|
|
859
|
-
content: [{ type: "text", text: `Found user: ${user.name}` }],
|
|
860
|
-
// Structured content matching the outputSchema
|
|
861
|
-
structuredContent: {
|
|
862
|
-
id: user.id,
|
|
863
|
-
name: user.name,
|
|
864
|
-
email: user.email,
|
|
865
|
-
createdAt: user.createdAt.toISOString()
|
|
866
|
-
}
|
|
867
|
-
};
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
]
|
|
415
|
+
},
|
|
416
|
+
],
|
|
871
417
|
});
|
|
872
418
|
```
|
|
873
419
|
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
For TypeScript projects, you can use Zod schemas for both input and output validation. Zod schemas are automatically converted to JSON Schema:
|
|
420
|
+
### Dynamic Tool Registration
|
|
877
421
|
|
|
878
422
|
```typescript
|
|
879
|
-
import
|
|
423
|
+
import '@mcp-b/global';
|
|
880
424
|
|
|
881
|
-
|
|
425
|
+
// Start with base tools
|
|
426
|
+
navigator.modelContext.provideContext({
|
|
882
427
|
tools: [
|
|
883
428
|
{
|
|
884
|
-
name:
|
|
885
|
-
description:
|
|
886
|
-
inputSchema: {
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
category: z.enum(["electronics", "clothing", "books"]).optional()
|
|
890
|
-
},
|
|
891
|
-
// Zod schema for output - provides TypeScript types
|
|
892
|
-
outputSchema: {
|
|
893
|
-
products: z.array(z.object({
|
|
894
|
-
id: z.string(),
|
|
895
|
-
name: z.string(),
|
|
896
|
-
price: z.number(),
|
|
897
|
-
inStock: z.boolean()
|
|
898
|
-
})),
|
|
899
|
-
total: z.number().describe("Total matching products"),
|
|
900
|
-
hasMore: z.boolean().describe("Whether more results exist")
|
|
429
|
+
name: 'get-user',
|
|
430
|
+
description: 'Get current user info',
|
|
431
|
+
inputSchema: { type: 'object', properties: {} },
|
|
432
|
+
async execute() {
|
|
433
|
+
return { content: [{ type: 'text', text: JSON.stringify(currentUser) }] };
|
|
901
434
|
},
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
return {
|
|
905
|
-
content: [{ type: "text", text: `Found ${results.total} products` }],
|
|
906
|
-
structuredContent: {
|
|
907
|
-
products: results.items,
|
|
908
|
-
total: results.total,
|
|
909
|
-
hasMore: results.total > limit
|
|
910
|
-
}
|
|
911
|
-
};
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
]
|
|
435
|
+
},
|
|
436
|
+
],
|
|
915
437
|
});
|
|
438
|
+
|
|
439
|
+
// Add tools dynamically based on user role
|
|
440
|
+
if (currentUser.isAdmin) {
|
|
441
|
+
navigator.modelContext.registerTool({
|
|
442
|
+
name: 'delete-user',
|
|
443
|
+
description: 'Delete a user account (admin only)',
|
|
444
|
+
inputSchema: {
|
|
445
|
+
type: 'object',
|
|
446
|
+
properties: { userId: { type: 'string' } },
|
|
447
|
+
required: ['userId'],
|
|
448
|
+
},
|
|
449
|
+
async execute(args) {
|
|
450
|
+
await fetch(`/api/users/${args.userId}`, { method: 'DELETE' });
|
|
451
|
+
return { content: [{ type: 'text', text: 'User deleted' }] };
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Remove tools when permissions change
|
|
457
|
+
function onLogout() {
|
|
458
|
+
navigator.modelContext.clearContext();
|
|
459
|
+
}
|
|
916
460
|
```
|
|
917
461
|
|
|
918
|
-
|
|
462
|
+
### Form Interaction
|
|
919
463
|
|
|
920
|
-
|
|
464
|
+
```typescript
|
|
465
|
+
import '@mcp-b/global';
|
|
921
466
|
|
|
922
|
-
|
|
923
|
-
window.navigator.modelContext.provideContext({
|
|
467
|
+
navigator.modelContext.provideContext({
|
|
924
468
|
tools: [
|
|
925
469
|
{
|
|
926
|
-
name:
|
|
927
|
-
description:
|
|
470
|
+
name: 'fill-contact-form',
|
|
471
|
+
description: 'Fill the contact form with provided details',
|
|
928
472
|
inputSchema: {
|
|
929
|
-
type:
|
|
473
|
+
type: 'object',
|
|
930
474
|
properties: {
|
|
931
|
-
|
|
932
|
-
|
|
475
|
+
name: { type: 'string' },
|
|
476
|
+
email: { type: 'string' },
|
|
477
|
+
message: { type: 'string' },
|
|
933
478
|
},
|
|
934
|
-
required: [
|
|
479
|
+
required: ['name', 'email', 'message'],
|
|
935
480
|
},
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
properties: {
|
|
942
|
-
linesOfCode: { type: "number" },
|
|
943
|
-
complexity: { type: "string", enum: ["low", "medium", "high"] }
|
|
944
|
-
}
|
|
945
|
-
},
|
|
946
|
-
issues: {
|
|
947
|
-
type: "array",
|
|
948
|
-
items: {
|
|
949
|
-
type: "object",
|
|
950
|
-
properties: {
|
|
951
|
-
severity: { type: "string", enum: ["error", "warning", "info"] },
|
|
952
|
-
line: { type: "number" },
|
|
953
|
-
message: { type: "string" },
|
|
954
|
-
suggestion: { type: "string" }
|
|
955
|
-
},
|
|
956
|
-
required: ["severity", "line", "message"]
|
|
957
|
-
}
|
|
958
|
-
},
|
|
959
|
-
score: {
|
|
960
|
-
type: "number",
|
|
961
|
-
minimum: 0,
|
|
962
|
-
maximum: 100,
|
|
963
|
-
description: "Code quality score"
|
|
964
|
-
}
|
|
965
|
-
},
|
|
966
|
-
required: ["summary", "issues", "score"]
|
|
481
|
+
async execute(args) {
|
|
482
|
+
document.querySelector('#name').value = args.name;
|
|
483
|
+
document.querySelector('#email').value = args.email;
|
|
484
|
+
document.querySelector('#message').value = args.message;
|
|
485
|
+
return { content: [{ type: 'text', text: 'Form filled' }] };
|
|
967
486
|
},
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
name: 'submit-form',
|
|
490
|
+
description: 'Submit the contact form',
|
|
491
|
+
inputSchema: { type: 'object', properties: {} },
|
|
492
|
+
async execute() {
|
|
493
|
+
document.querySelector('#contact-form').submit();
|
|
494
|
+
return { content: [{ type: 'text', text: 'Form submitted' }] };
|
|
495
|
+
},
|
|
496
|
+
},
|
|
497
|
+
],
|
|
977
498
|
});
|
|
978
499
|
```
|
|
979
500
|
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
Tools must return an object with:
|
|
983
|
-
|
|
984
|
-
```typescript
|
|
985
|
-
{
|
|
986
|
-
content: [
|
|
987
|
-
{
|
|
988
|
-
type: "text", // or "image", "resource"
|
|
989
|
-
text: "Result..." // the response content
|
|
990
|
-
}
|
|
991
|
-
],
|
|
992
|
-
isError?: boolean // optional error flag
|
|
993
|
-
}
|
|
994
|
-
```
|
|
995
|
-
|
|
996
|
-
## 🎯 Complete Examples
|
|
997
|
-
|
|
998
|
-
### Todo List Application
|
|
999
|
-
|
|
1000
|
-
```javascript
|
|
1001
|
-
let todos = [];
|
|
1002
|
-
|
|
1003
|
-
window.navigator.modelContext.provideContext({
|
|
1004
|
-
tools: [
|
|
1005
|
-
{
|
|
1006
|
-
name: "add-todo",
|
|
1007
|
-
description: "Add a new todo item",
|
|
1008
|
-
inputSchema: {
|
|
1009
|
-
type: "object",
|
|
1010
|
-
properties: {
|
|
1011
|
-
text: { type: "string", description: "Todo text" }
|
|
1012
|
-
},
|
|
1013
|
-
required: ["text"]
|
|
1014
|
-
},
|
|
1015
|
-
async execute({ text }) {
|
|
1016
|
-
const todo = { id: Date.now(), text, done: false };
|
|
1017
|
-
todos.push(todo);
|
|
1018
|
-
updateUI();
|
|
1019
|
-
return {
|
|
1020
|
-
content: [{ type: "text", text: `Added: "${text}"` }]
|
|
1021
|
-
};
|
|
1022
|
-
}
|
|
1023
|
-
},
|
|
1024
|
-
{
|
|
1025
|
-
name: "list-todos",
|
|
1026
|
-
description: "Get all todo items",
|
|
1027
|
-
inputSchema: { type: "object", properties: {} },
|
|
1028
|
-
async execute() {
|
|
1029
|
-
const list = todos.map(t =>
|
|
1030
|
-
`${t.done ? '✓' : '○'} ${t.text}`
|
|
1031
|
-
).join('\n');
|
|
1032
|
-
return {
|
|
1033
|
-
content: [{ type: "text", text: list || "No todos" }]
|
|
1034
|
-
};
|
|
1035
|
-
}
|
|
1036
|
-
},
|
|
1037
|
-
{
|
|
1038
|
-
name: "complete-todo",
|
|
1039
|
-
description: "Mark a todo as complete",
|
|
1040
|
-
inputSchema: {
|
|
1041
|
-
type: "object",
|
|
1042
|
-
properties: {
|
|
1043
|
-
id: { type: "number", description: "Todo ID" }
|
|
1044
|
-
},
|
|
1045
|
-
required: ["id"]
|
|
1046
|
-
},
|
|
1047
|
-
async execute({ id }) {
|
|
1048
|
-
const todo = todos.find(t => t.id === id);
|
|
1049
|
-
if (!todo) {
|
|
1050
|
-
return {
|
|
1051
|
-
content: [{ type: "text", text: "Todo not found" }],
|
|
1052
|
-
isError: true
|
|
1053
|
-
};
|
|
1054
|
-
}
|
|
1055
|
-
todo.done = true;
|
|
1056
|
-
updateUI();
|
|
1057
|
-
return {
|
|
1058
|
-
content: [{ type: "text", text: `Completed: "${todo.text}"` }]
|
|
1059
|
-
};
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
]
|
|
1063
|
-
});
|
|
1064
|
-
|
|
1065
|
-
function updateUI() {
|
|
1066
|
-
// Update your UI
|
|
1067
|
-
document.getElementById('todo-list').innerHTML =
|
|
1068
|
-
todos.map(t => `<li>${t.done ? '✓' : ''} ${t.text}</li>`).join('');
|
|
1069
|
-
}
|
|
1070
|
-
```
|
|
1071
|
-
|
|
1072
|
-
### E-commerce Product Search
|
|
1073
|
-
|
|
1074
|
-
```javascript
|
|
1075
|
-
window.navigator.modelContext.provideContext({
|
|
1076
|
-
tools: [
|
|
1077
|
-
{
|
|
1078
|
-
name: "search-products",
|
|
1079
|
-
description: "Search for products in the catalog",
|
|
1080
|
-
inputSchema: {
|
|
1081
|
-
type: "object",
|
|
1082
|
-
properties: {
|
|
1083
|
-
query: {
|
|
1084
|
-
type: "string",
|
|
1085
|
-
description: "Search query"
|
|
1086
|
-
},
|
|
1087
|
-
category: {
|
|
1088
|
-
type: "string",
|
|
1089
|
-
description: "Filter by category",
|
|
1090
|
-
enum: ["electronics", "clothing", "books", "all"]
|
|
1091
|
-
},
|
|
1092
|
-
maxPrice: {
|
|
1093
|
-
type: "number",
|
|
1094
|
-
description: "Maximum price filter"
|
|
1095
|
-
}
|
|
1096
|
-
},
|
|
1097
|
-
required: ["query"]
|
|
1098
|
-
},
|
|
1099
|
-
async execute({ query, category = "all", maxPrice }) {
|
|
1100
|
-
const results = await searchProducts({
|
|
1101
|
-
query,
|
|
1102
|
-
category: category !== "all" ? category : undefined,
|
|
1103
|
-
maxPrice
|
|
1104
|
-
});
|
|
1105
|
-
|
|
1106
|
-
const summary = results.map(p =>
|
|
1107
|
-
`${p.name} - $${p.price} (${p.category})`
|
|
1108
|
-
).join('\n');
|
|
1109
|
-
|
|
1110
|
-
return {
|
|
1111
|
-
content: [{
|
|
1112
|
-
type: "text",
|
|
1113
|
-
text: `Found ${results.length} products:\n${summary}`
|
|
1114
|
-
}]
|
|
1115
|
-
};
|
|
1116
|
-
}
|
|
1117
|
-
},
|
|
1118
|
-
{
|
|
1119
|
-
name: "add-to-cart",
|
|
1120
|
-
description: "Add a product to the shopping cart",
|
|
1121
|
-
inputSchema: {
|
|
1122
|
-
type: "object",
|
|
1123
|
-
properties: {
|
|
1124
|
-
productId: { type: "string" },
|
|
1125
|
-
quantity: { type: "number", default: 1 }
|
|
1126
|
-
},
|
|
1127
|
-
required: ["productId"]
|
|
1128
|
-
},
|
|
1129
|
-
async execute({ productId, quantity = 1 }) {
|
|
1130
|
-
await addToCart(productId, quantity);
|
|
1131
|
-
return {
|
|
1132
|
-
content: [{
|
|
1133
|
-
type: "text",
|
|
1134
|
-
text: `Added ${quantity}x product ${productId} to cart`
|
|
1135
|
-
}]
|
|
1136
|
-
};
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
]
|
|
1140
|
-
});
|
|
1141
|
-
```
|
|
1142
|
-
|
|
1143
|
-
## 🔧 Dynamic Tool Registration (Component Lifecycle)
|
|
1144
|
-
|
|
1145
|
-
### React Component Example
|
|
1146
|
-
|
|
1147
|
-
Perfect for managing tools tied to component lifecycle:
|
|
1148
|
-
|
|
1149
|
-
```javascript
|
|
1150
|
-
import { useEffect } from 'react';
|
|
1151
|
-
|
|
1152
|
-
function MyComponent() {
|
|
1153
|
-
useEffect(() => {
|
|
1154
|
-
// Register component-specific tool when component mounts (Bucket B)
|
|
1155
|
-
const registration = window.navigator.modelContext.registerTool({
|
|
1156
|
-
name: "component-action",
|
|
1157
|
-
description: "Action specific to this component",
|
|
1158
|
-
inputSchema: { type: "object", properties: {} },
|
|
1159
|
-
async execute() {
|
|
1160
|
-
// Access component state/methods here
|
|
1161
|
-
return {
|
|
1162
|
-
content: [{ type: "text", text: "Component action executed!" }]
|
|
1163
|
-
};
|
|
1164
|
-
}
|
|
1165
|
-
});
|
|
1166
|
-
|
|
1167
|
-
// Cleanup: unregister when component unmounts
|
|
1168
|
-
return () => {
|
|
1169
|
-
registration.unregister();
|
|
1170
|
-
};
|
|
1171
|
-
}, []);
|
|
1172
|
-
|
|
1173
|
-
return <div>My Component</div>;
|
|
1174
|
-
}
|
|
1175
|
-
```
|
|
1176
|
-
|
|
1177
|
-
### Persistence Across provideContext() Calls
|
|
1178
|
-
|
|
1179
|
-
```javascript
|
|
1180
|
-
// Step 1: Register base tools (Bucket A)
|
|
1181
|
-
window.navigator.modelContext.provideContext({
|
|
1182
|
-
tools: [
|
|
1183
|
-
{ name: "base-tool-1", description: "Base tool", inputSchema: {}, async execute() {} }
|
|
1184
|
-
]
|
|
1185
|
-
});
|
|
1186
|
-
// Tools: ["base-tool-1"]
|
|
1187
|
-
|
|
1188
|
-
// Step 2: Register dynamic tool (Bucket B)
|
|
1189
|
-
const reg = window.navigator.modelContext.registerTool({
|
|
1190
|
-
name: "dynamic-tool",
|
|
1191
|
-
description: "Dynamic tool",
|
|
1192
|
-
inputSchema: { type: "object", properties: {} },
|
|
1193
|
-
async execute() {
|
|
1194
|
-
return { content: [{ type: "text", text: "Dynamic!" }] };
|
|
1195
|
-
}
|
|
1196
|
-
});
|
|
1197
|
-
// Tools: ["base-tool-1", "dynamic-tool"]
|
|
1198
|
-
|
|
1199
|
-
// Step 3: Update base tools via provideContext
|
|
1200
|
-
window.navigator.modelContext.provideContext({
|
|
1201
|
-
tools: [
|
|
1202
|
-
{ name: "base-tool-2", description: "New base tool", inputSchema: {}, async execute() {} }
|
|
1203
|
-
]
|
|
1204
|
-
});
|
|
1205
|
-
// Tools: ["base-tool-2", "dynamic-tool"]
|
|
1206
|
-
// ✅ "dynamic-tool" persists! Only "base-tool-1" was cleared
|
|
1207
|
-
|
|
1208
|
-
// Step 4: Clean up dynamic tool
|
|
1209
|
-
reg.unregister();
|
|
1210
|
-
// Tools: ["base-tool-2"]
|
|
1211
|
-
```
|
|
1212
|
-
|
|
1213
|
-
### Name Collision Protection
|
|
1214
|
-
|
|
1215
|
-
```javascript
|
|
1216
|
-
// Register a base tool
|
|
1217
|
-
window.navigator.modelContext.provideContext({
|
|
1218
|
-
tools: [
|
|
1219
|
-
{ name: "my-tool", description: "Base", inputSchema: {}, async execute() {} }
|
|
1220
|
-
]
|
|
1221
|
-
});
|
|
1222
|
-
|
|
1223
|
-
// This will throw an error!
|
|
1224
|
-
try {
|
|
1225
|
-
window.navigator.modelContext.registerTool({
|
|
1226
|
-
name: "my-tool", // ❌ Name collision with Bucket A
|
|
1227
|
-
description: "Dynamic",
|
|
1228
|
-
inputSchema: {},
|
|
1229
|
-
async execute() {}
|
|
1230
|
-
});
|
|
1231
|
-
} catch (error) {
|
|
1232
|
-
console.error(error.message);
|
|
1233
|
-
// Error: Tool name collision: "my-tool" is already registered via provideContext()
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
// Similarly, can't unregister a base tool
|
|
1237
|
-
const baseToolList = window.navigator.modelContext.provideContext({
|
|
1238
|
-
tools: [{ name: "base", description: "Base", inputSchema: {}, async execute() {} }]
|
|
1239
|
-
});
|
|
1240
|
-
|
|
1241
|
-
// This will also throw an error!
|
|
1242
|
-
try {
|
|
1243
|
-
// Assuming we got a reference somehow
|
|
1244
|
-
// registration.unregister(); would fail for a base tool
|
|
1245
|
-
} catch (error) {
|
|
1246
|
-
// Error: Cannot unregister tool "base": This tool was registered via provideContext()
|
|
1247
|
-
}
|
|
1248
|
-
```
|
|
1249
|
-
|
|
1250
|
-
## 🔧 Event-Based Tool Calls (Advanced)
|
|
1251
|
-
|
|
1252
|
-
For manifest-based or advanced scenarios, you can handle tool calls as events:
|
|
1253
|
-
|
|
1254
|
-
```javascript
|
|
1255
|
-
window.navigator.modelContext.addEventListener('toolcall', async (event) => {
|
|
1256
|
-
console.log(`Tool called: ${event.name}`, event.arguments);
|
|
1257
|
-
|
|
1258
|
-
if (event.name === "custom-tool") {
|
|
1259
|
-
// Prevent default execution
|
|
1260
|
-
event.preventDefault();
|
|
1261
|
-
|
|
1262
|
-
// Provide custom response
|
|
1263
|
-
event.respondWith({
|
|
1264
|
-
content: [{
|
|
1265
|
-
type: "text",
|
|
1266
|
-
text: "Custom response from event handler"
|
|
1267
|
-
}]
|
|
1268
|
-
});
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
// If not prevented, the tool's execute function will run normally
|
|
1272
|
-
});
|
|
1273
|
-
```
|
|
1274
|
-
|
|
1275
|
-
### Hybrid Approach
|
|
1276
|
-
|
|
1277
|
-
The API supports both approaches simultaneously:
|
|
1278
|
-
|
|
1279
|
-
1. **Event dispatched first** - `toolcall` event is fired
|
|
1280
|
-
2. **Event can override** - Call `event.preventDefault()` and `event.respondWith()`
|
|
1281
|
-
3. **Default execution** - If not prevented, the tool's `execute()` function runs
|
|
1282
|
-
|
|
1283
|
-
This allows flexibility for different use cases.
|
|
1284
|
-
|
|
1285
|
-
## 🏗️ Architecture
|
|
1286
|
-
|
|
1287
|
-
```
|
|
1288
|
-
┌─────────────────┐
|
|
1289
|
-
│ AI Agent │
|
|
1290
|
-
│ (MCP Client) │
|
|
1291
|
-
└────────┬────────┘
|
|
1292
|
-
│ MCP Protocol
|
|
1293
|
-
│ (JSON-RPC)
|
|
1294
|
-
┌────────▼────────┐
|
|
1295
|
-
│ MCP Server │
|
|
1296
|
-
│ (Internal) │
|
|
1297
|
-
└────────┬────────┘
|
|
1298
|
-
│
|
|
1299
|
-
┌────────▼───────────────────┐
|
|
1300
|
-
│ navigator.modelContext │ ◄── Your app registers tools here
|
|
1301
|
-
│ (This pkg) │
|
|
1302
|
-
└────────────────────────────┘
|
|
1303
|
-
```
|
|
1304
|
-
|
|
1305
|
-
This package:
|
|
1306
|
-
1. Exposes `window.navigator.modelContext` API (W3C Web Model Context standard)
|
|
1307
|
-
2. Internally creates an MCP Server
|
|
1308
|
-
3. Bridges tool calls between the two protocols
|
|
1309
|
-
4. Uses TabServerTransport for browser communication
|
|
1310
|
-
|
|
1311
|
-
## 🔍 Feature Detection
|
|
1312
|
-
|
|
1313
|
-
Check if the API is available:
|
|
1314
|
-
|
|
1315
|
-
```javascript
|
|
1316
|
-
if ("modelContext" in navigator) {
|
|
1317
|
-
// API is available
|
|
1318
|
-
navigator.modelContext.provideContext({ tools: [...] });
|
|
1319
|
-
} else {
|
|
1320
|
-
console.warn("Web Model Context API not available");
|
|
1321
|
-
}
|
|
1322
|
-
```
|
|
1323
|
-
|
|
1324
|
-
## 🐛 Debugging
|
|
1325
|
-
|
|
1326
|
-
### Enable Debug Logging
|
|
1327
|
-
|
|
1328
|
-
The @mcp-b/global library includes a lightweight logging system that can be enabled in the browser console. By default, the console is kept clean (only errors and warnings are shown). You can enable detailed debug logging when troubleshooting:
|
|
1329
|
-
|
|
1330
|
-
```javascript
|
|
1331
|
-
// Enable all debug logging
|
|
1332
|
-
localStorage.setItem('WEBMCP_DEBUG', '*');
|
|
1333
|
-
|
|
1334
|
-
// Enable specific namespaces
|
|
1335
|
-
localStorage.setItem('WEBMCP_DEBUG', 'WebModelContext');
|
|
1336
|
-
localStorage.setItem('WEBMCP_DEBUG', 'NativeAdapter,MCPBridge');
|
|
1337
|
-
|
|
1338
|
-
// Refresh the page to apply changes
|
|
1339
|
-
location.reload();
|
|
1340
|
-
```
|
|
1341
|
-
|
|
1342
|
-
To disable debug logging:
|
|
1343
|
-
|
|
1344
|
-
```javascript
|
|
1345
|
-
localStorage.removeItem('WEBMCP_DEBUG');
|
|
1346
|
-
location.reload();
|
|
1347
|
-
```
|
|
1348
|
-
|
|
1349
|
-
**Available Namespaces:**
|
|
1350
|
-
- `WebModelContext` - Main polyfill implementation
|
|
1351
|
-
- `NativeAdapter` - Native Chromium API adapter
|
|
1352
|
-
- `MCPBridge` - MCP server and transport setup
|
|
1353
|
-
- `ModelContextTesting` - Testing API operations
|
|
1354
|
-
|
|
1355
|
-
**Log Levels:**
|
|
1356
|
-
- **Error** (always shown): Critical failures and exceptions
|
|
1357
|
-
- **Warn** (always shown): Compatibility warnings and potential issues
|
|
1358
|
-
- **Info** (debug mode only): Initialization and setup progress
|
|
1359
|
-
- **Debug** (debug mode only): Detailed operation traces
|
|
1360
|
-
|
|
1361
|
-
### Access Internal Bridge
|
|
1362
|
-
|
|
1363
|
-
In development mode, access the internal bridge:
|
|
1364
|
-
|
|
1365
|
-
```javascript
|
|
1366
|
-
if (window.__mcpBridge) {
|
|
1367
|
-
console.log("MCP Server:", window.__mcpBridge.server);
|
|
1368
|
-
console.log("Registered tools:", window.__mcpBridge.tools);
|
|
1369
|
-
}
|
|
1370
|
-
```
|
|
1371
|
-
|
|
1372
|
-
## 🧪 Testing API (`navigator.modelContextTesting`)
|
|
1373
|
-
|
|
1374
|
-
This package provides a **Model Context Testing API** at `window.navigator.modelContextTesting` for debugging and testing your tools during development.
|
|
1375
|
-
|
|
1376
|
-
> [!WARNING]
|
|
1377
|
-
> `navigator.modelContextTesting` is deprecated and kept for compatibility.
|
|
1378
|
-
> For in-page consumers, use `navigator.modelContext.callTool({ name, arguments })` and
|
|
1379
|
-
> `navigator.modelContext.addEventListener("toolschanged", ...)`.
|
|
1380
|
-
|
|
1381
|
-
### Unified Consumer API (Recommended)
|
|
1382
|
-
|
|
1383
|
-
```javascript
|
|
1384
|
-
// Execute tools with object args (no JSON stringification)
|
|
1385
|
-
const result = await navigator.modelContext.callTool({
|
|
1386
|
-
name: "greet",
|
|
1387
|
-
arguments: { name: "Alice" }
|
|
1388
|
-
});
|
|
1389
|
-
|
|
1390
|
-
// React to tool list changes
|
|
1391
|
-
navigator.modelContext.addEventListener("toolschanged", () => {
|
|
1392
|
-
console.log("Tools changed:", navigator.modelContext.listTools());
|
|
1393
|
-
});
|
|
1394
|
-
```
|
|
1395
|
-
|
|
1396
|
-
### Testing Helpers Module
|
|
1397
|
-
|
|
1398
|
-
Use `@mcp-b/global/testing` to avoid depending on global-only testing extensions directly:
|
|
1399
|
-
|
|
1400
|
-
```javascript
|
|
1401
|
-
import { createTestHelper } from "@mcp-b/global/testing";
|
|
1402
|
-
|
|
1403
|
-
const testing = createTestHelper();
|
|
1404
|
-
await testing.executeTool("greet", { name: "Alice" });
|
|
1405
|
-
```
|
|
1406
|
-
|
|
1407
|
-
### Native Support in Chromium
|
|
1408
|
-
|
|
1409
|
-
**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.
|
|
1410
|
-
|
|
1411
|
-
#### How to Enable Native API in Chromium:
|
|
1412
|
-
|
|
1413
|
-
**Option 1: Chrome Flags**
|
|
1414
|
-
1. Navigate to `chrome://flags`
|
|
1415
|
-
2. Search for "Experimental Web Platform Features"
|
|
1416
|
-
3. Enable the flag
|
|
1417
|
-
4. Restart your browser
|
|
1418
|
-
|
|
1419
|
-
**Option 2: Command Line**
|
|
1420
|
-
```bash
|
|
1421
|
-
# Launch Chrome/Edge with experimental features
|
|
1422
|
-
chrome --enable-experimental-web-platform-features
|
|
1423
|
-
```
|
|
1424
|
-
|
|
1425
|
-
**Detection**: When the native API is detected, you'll see this console message:
|
|
1426
|
-
```
|
|
1427
|
-
✅ [Model Context Testing] Native implementation detected (Chromium experimental feature)
|
|
1428
|
-
Using native window.navigator.modelContextTesting from browser
|
|
1429
|
-
```
|
|
1430
|
-
|
|
1431
|
-
### Polyfill Fallback
|
|
1432
|
-
|
|
1433
|
-
If the native API is not available, this package automatically provides a polyfill implementation with the same interface:
|
|
1434
|
-
|
|
1435
|
-
```
|
|
1436
|
-
[Model Context Testing] Native implementation not found, installing polyfill
|
|
1437
|
-
💡 To use the native implementation in Chromium:
|
|
1438
|
-
- Navigate to chrome://flags
|
|
1439
|
-
- Enable "Experimental Web Platform Features"
|
|
1440
|
-
- Or launch with: --enable-experimental-web-platform-features
|
|
1441
|
-
✅ [Model Context Testing] Polyfill installed at window.navigator.modelContextTesting
|
|
1442
|
-
```
|
|
1443
|
-
|
|
1444
|
-
### API Reference
|
|
1445
|
-
|
|
1446
|
-
#### `getToolCalls(): Array<ToolCall>`
|
|
1447
|
-
|
|
1448
|
-
Get a history of all tool calls made during the session.
|
|
1449
|
-
|
|
1450
|
-
```javascript
|
|
1451
|
-
// Register and call some tools
|
|
1452
|
-
window.navigator.modelContext.provideContext({
|
|
1453
|
-
tools: [{
|
|
1454
|
-
name: "greet",
|
|
1455
|
-
description: "Greet a user",
|
|
1456
|
-
inputSchema: {
|
|
1457
|
-
type: "object",
|
|
1458
|
-
properties: { name: { type: "string" } },
|
|
1459
|
-
required: ["name"]
|
|
1460
|
-
},
|
|
1461
|
-
async execute({ name }) {
|
|
1462
|
-
return { content: [{ type: "text", text: `Hello, ${name}!` }] };
|
|
1463
|
-
}
|
|
1464
|
-
}]
|
|
1465
|
-
});
|
|
1466
|
-
|
|
1467
|
-
// Simulate a tool call
|
|
1468
|
-
// (In practice, this would come from an AI agent)
|
|
1469
|
-
|
|
1470
|
-
// Later, inspect the tool call history
|
|
1471
|
-
const calls = window.navigator.modelContextTesting.getToolCalls();
|
|
1472
|
-
console.log(calls);
|
|
1473
|
-
// [
|
|
1474
|
-
// {
|
|
1475
|
-
// toolName: "greet",
|
|
1476
|
-
// arguments: { name: "Alice" },
|
|
1477
|
-
// timestamp: 1699123456789
|
|
1478
|
-
// }
|
|
1479
|
-
// ]
|
|
1480
|
-
```
|
|
1481
|
-
|
|
1482
|
-
#### `clearToolCalls(): void`
|
|
1483
|
-
|
|
1484
|
-
Clear the tool call history.
|
|
1485
|
-
|
|
1486
|
-
```javascript
|
|
1487
|
-
window.navigator.modelContextTesting.clearToolCalls();
|
|
1488
|
-
console.log(window.navigator.modelContextTesting.getToolCalls()); // []
|
|
1489
|
-
```
|
|
1490
|
-
|
|
1491
|
-
#### `setMockToolResponse(toolName: string, response: ToolResponse): void`
|
|
1492
|
-
|
|
1493
|
-
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.
|
|
1494
|
-
|
|
1495
|
-
```javascript
|
|
1496
|
-
// Mock the "greet" tool to always return a specific response
|
|
1497
|
-
window.navigator.modelContextTesting.setMockToolResponse("greet", {
|
|
1498
|
-
content: [{
|
|
1499
|
-
type: "text",
|
|
1500
|
-
text: "Mocked greeting!"
|
|
1501
|
-
}]
|
|
1502
|
-
});
|
|
1503
|
-
|
|
1504
|
-
// Now when the tool is called, it returns the mock response
|
|
1505
|
-
// (The execute function is never called)
|
|
1506
|
-
```
|
|
1507
|
-
|
|
1508
|
-
#### `clearMockToolResponse(toolName: string): void`
|
|
1509
|
-
|
|
1510
|
-
Remove the mock response for a specific tool.
|
|
1511
|
-
|
|
1512
|
-
```javascript
|
|
1513
|
-
window.navigator.modelContextTesting.clearMockToolResponse("greet");
|
|
1514
|
-
// Tool will now use its actual execute function
|
|
1515
|
-
```
|
|
1516
|
-
|
|
1517
|
-
#### `clearAllMockToolResponses(): void`
|
|
1518
|
-
|
|
1519
|
-
Remove all mock tool responses.
|
|
1520
|
-
|
|
1521
|
-
```javascript
|
|
1522
|
-
window.navigator.modelContextTesting.clearAllMockToolResponses();
|
|
1523
|
-
```
|
|
1524
|
-
|
|
1525
|
-
#### `getRegisteredTools(): Array<ToolDescriptor>`
|
|
1526
|
-
|
|
1527
|
-
Get the list of all currently registered tools (same as `modelContext.listTools()`).
|
|
1528
|
-
|
|
1529
|
-
```javascript
|
|
1530
|
-
const tools = window.navigator.modelContextTesting.getRegisteredTools();
|
|
1531
|
-
console.log(tools.map(t => t.name)); // ["greet", "add-todo", ...]
|
|
1532
|
-
```
|
|
1533
|
-
|
|
1534
|
-
#### `reset(): void`
|
|
1535
|
-
|
|
1536
|
-
Reset the entire testing state (clears tool call history and all mock responses).
|
|
1537
|
-
|
|
1538
|
-
```javascript
|
|
1539
|
-
window.navigator.modelContextTesting.reset();
|
|
1540
|
-
```
|
|
1541
|
-
|
|
1542
|
-
### Testing Workflow Example
|
|
1543
|
-
|
|
1544
|
-
Here's a complete example of using the testing API:
|
|
1545
|
-
|
|
1546
|
-
```javascript
|
|
1547
|
-
// 1. Register your tools
|
|
1548
|
-
window.navigator.modelContext.provideContext({
|
|
1549
|
-
tools: [
|
|
1550
|
-
{
|
|
1551
|
-
name: "add-todo",
|
|
1552
|
-
description: "Add a todo item",
|
|
1553
|
-
inputSchema: {
|
|
1554
|
-
type: "object",
|
|
1555
|
-
properties: { text: { type: "string" } },
|
|
1556
|
-
required: ["text"]
|
|
1557
|
-
},
|
|
1558
|
-
async execute({ text }) {
|
|
1559
|
-
// This would normally add to your app state
|
|
1560
|
-
return { content: [{ type: "text", text: `Added: ${text}` }] };
|
|
1561
|
-
}
|
|
1562
|
-
}
|
|
1563
|
-
]
|
|
1564
|
-
});
|
|
1565
|
-
|
|
1566
|
-
// 2. Set up mocks for testing
|
|
1567
|
-
window.navigator.modelContextTesting.setMockToolResponse("add-todo", {
|
|
1568
|
-
content: [{ type: "text", text: "Mock: Todo added successfully" }]
|
|
1569
|
-
});
|
|
1570
|
-
|
|
1571
|
-
// 3. Simulate tool calls (or let AI agent call them)
|
|
1572
|
-
// The tool will return the mock response instead of executing
|
|
1573
|
-
|
|
1574
|
-
// 4. Inspect tool call history
|
|
1575
|
-
const calls = window.navigator.modelContextTesting.getToolCalls();
|
|
1576
|
-
console.log(`${calls.length} tool calls made`);
|
|
1577
|
-
calls.forEach(call => {
|
|
1578
|
-
console.log(`- ${call.toolName}`, call.arguments);
|
|
1579
|
-
});
|
|
1580
|
-
|
|
1581
|
-
// 5. Clean up after testing
|
|
1582
|
-
window.navigator.modelContextTesting.reset();
|
|
1583
|
-
```
|
|
1584
|
-
|
|
1585
|
-
### Integration Testing Example
|
|
1586
|
-
|
|
1587
|
-
Perfect for automated testing with frameworks like Jest, Vitest, or Playwright:
|
|
1588
|
-
|
|
1589
|
-
```javascript
|
|
1590
|
-
// test/model-context.test.js
|
|
1591
|
-
import { test, expect } from 'vitest';
|
|
1592
|
-
|
|
1593
|
-
test('todo tool creates correct response', async () => {
|
|
1594
|
-
// Arrange
|
|
1595
|
-
const mockResponse = {
|
|
1596
|
-
content: [{ type: "text", text: "Test todo added" }]
|
|
1597
|
-
};
|
|
1598
|
-
|
|
1599
|
-
window.navigator.modelContextTesting.setMockToolResponse(
|
|
1600
|
-
"add-todo",
|
|
1601
|
-
mockResponse
|
|
1602
|
-
);
|
|
1603
|
-
|
|
1604
|
-
// Act
|
|
1605
|
-
// Trigger your AI agent or directly call the tool via MCP
|
|
1606
|
-
// ...
|
|
1607
|
-
|
|
1608
|
-
// Assert
|
|
1609
|
-
const calls = window.navigator.modelContextTesting.getToolCalls();
|
|
1610
|
-
expect(calls).toHaveLength(1);
|
|
1611
|
-
expect(calls[0].toolName).toBe("add-todo");
|
|
1612
|
-
expect(calls[0].arguments).toEqual({ text: "Test item" });
|
|
1613
|
-
|
|
1614
|
-
// Cleanup
|
|
1615
|
-
window.navigator.modelContextTesting.reset();
|
|
1616
|
-
});
|
|
1617
|
-
```
|
|
1618
|
-
|
|
1619
|
-
### Browser Compatibility
|
|
501
|
+
## Browser Compatibility
|
|
1620
502
|
|
|
1621
503
|
| Browser | Native Support | Polyfill |
|
|
1622
504
|
|---------|---------------|----------|
|
|
1623
|
-
| Chrome/Edge (with flag) |
|
|
1624
|
-
| Chrome/Edge (default) |
|
|
1625
|
-
| Firefox |
|
|
1626
|
-
| Safari |
|
|
1627
|
-
| Other browsers | ❌ No | ✅ Yes |
|
|
1628
|
-
|
|
1629
|
-
The polyfill automatically detects and defers to the native implementation when available, ensuring forward compatibility as browsers adopt this standard.
|
|
505
|
+
| Chrome/Edge (with flag) | Yes | N/A |
|
|
506
|
+
| Chrome/Edge (default) | No | Yes |
|
|
507
|
+
| Firefox | No | Yes |
|
|
508
|
+
| Safari | No | Yes |
|
|
1630
509
|
|
|
1631
510
|
## Zod Version Compatibility
|
|
1632
511
|
|
|
1633
|
-
This package supports **Zod 3.25+**
|
|
512
|
+
This package supports **Zod 3.25.76+** (3.x only). JSON Schema is also supported if you prefer not to use Zod.
|
|
1634
513
|
|
|
1635
|
-
|
|
1636
|
-
import { z } from 'zod';
|
|
514
|
+
## Type Exports
|
|
1637
515
|
|
|
1638
|
-
|
|
1639
|
-
tools: [{
|
|
1640
|
-
name: "my-tool",
|
|
1641
|
-
inputSchema: {
|
|
1642
|
-
name: z.string().describe('User name'),
|
|
1643
|
-
age: z.number().min(0)
|
|
1644
|
-
},
|
|
1645
|
-
async execute({ name, age }) {
|
|
1646
|
-
return { content: [{ type: "text", text: `Hello, ${name}!` }] };
|
|
1647
|
-
}
|
|
1648
|
-
}]
|
|
1649
|
-
});
|
|
1650
|
-
```
|
|
1651
|
-
|
|
1652
|
-
### JSON Schema Alternative
|
|
516
|
+
All types are re-exported for TypeScript consumers:
|
|
1653
517
|
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
}
|
|
1670
|
-
}]
|
|
1671
|
-
});
|
|
1672
|
-
```
|
|
1673
|
-
|
|
1674
|
-
## 📦 What's Included
|
|
1675
|
-
|
|
1676
|
-
- **Web Model Context API** - Standard `window.navigator.modelContext` interface
|
|
1677
|
-
- **Model Context Testing API** - `window.navigator.modelContextTesting` for debugging and testing (with native Chromium support detection)
|
|
1678
|
-
- **Dynamic Tool Registration** - `registerTool()` with `unregister()` function
|
|
1679
|
-
- **MCP Bridge** - Automatic bridging to Model Context Protocol
|
|
1680
|
-
- **Tab Transport** - Communication layer for browser contexts
|
|
1681
|
-
- **Event System** - Hybrid tool call handling
|
|
1682
|
-
- **TypeScript Types** - Full type definitions included
|
|
1683
|
-
|
|
1684
|
-
## 🔒 Security Considerations
|
|
1685
|
-
|
|
1686
|
-
### Origin Restrictions
|
|
1687
|
-
|
|
1688
|
-
By default, the MCP transport allows connections from any origin (`*`). For production, you should configure allowed origins:
|
|
1689
|
-
|
|
1690
|
-
```javascript
|
|
1691
|
-
// Future configuration API
|
|
1692
|
-
window.navigator.modelContext.configure({
|
|
1693
|
-
allowedOrigins: [
|
|
1694
|
-
'https://your-app.com',
|
|
1695
|
-
'https://trusted-agent.com'
|
|
1696
|
-
]
|
|
1697
|
-
});
|
|
1698
|
-
```
|
|
1699
|
-
|
|
1700
|
-
### Tool Validation
|
|
1701
|
-
|
|
1702
|
-
Always validate inputs in your tool implementations:
|
|
1703
|
-
|
|
1704
|
-
```javascript
|
|
1705
|
-
{
|
|
1706
|
-
name: "delete-item",
|
|
1707
|
-
description: "Delete an item",
|
|
1708
|
-
inputSchema: {
|
|
1709
|
-
type: "object",
|
|
1710
|
-
properties: {
|
|
1711
|
-
id: { type: "string", pattern: "^[a-zA-Z0-9]+$" }
|
|
1712
|
-
},
|
|
1713
|
-
required: ["id"]
|
|
1714
|
-
},
|
|
1715
|
-
async execute({ id }) {
|
|
1716
|
-
// Additional validation
|
|
1717
|
-
if (!isValidId(id)) {
|
|
1718
|
-
return {
|
|
1719
|
-
content: [{ type: "text", text: "Invalid ID" }],
|
|
1720
|
-
isError: true
|
|
1721
|
-
};
|
|
1722
|
-
}
|
|
1723
|
-
|
|
1724
|
-
// Proceed with deletion
|
|
1725
|
-
await deleteItem(id);
|
|
1726
|
-
return {
|
|
1727
|
-
content: [{ type: "text", text: "Item deleted" }]
|
|
1728
|
-
};
|
|
1729
|
-
}
|
|
1730
|
-
}
|
|
518
|
+
```typescript
|
|
519
|
+
import type {
|
|
520
|
+
CallToolResult,
|
|
521
|
+
InputSchema,
|
|
522
|
+
ModelContext,
|
|
523
|
+
ModelContextCore,
|
|
524
|
+
ModelContextOptions,
|
|
525
|
+
NativeModelContextBehavior,
|
|
526
|
+
ToolAnnotations,
|
|
527
|
+
ToolDescriptor,
|
|
528
|
+
ToolListItem,
|
|
529
|
+
ToolResponse,
|
|
530
|
+
TransportConfiguration,
|
|
531
|
+
WebModelContextInitOptions,
|
|
532
|
+
} from '@mcp-b/global';
|
|
1731
533
|
```
|
|
1732
534
|
|
|
1733
|
-
##
|
|
535
|
+
## Tool Routing Contract
|
|
1734
536
|
|
|
1735
|
-
|
|
537
|
+
- MCP `tools/list`, `tools/call`, and tool list update notifications are sourced from `navigator.modelContextTesting`.
|
|
538
|
+
- `@mcp-b/global` requires `navigator.modelContextTesting` to be available at initialization time.
|
|
1736
539
|
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
### Do I need a build step?
|
|
1740
|
-
|
|
1741
|
-
No! Use the IIFE version with a single `<script>` tag. For bundler users, the ESM version is also available.
|
|
1742
|
-
|
|
1743
|
-
### Is this production-ready?
|
|
1744
|
-
|
|
1745
|
-
Yes! The polyfill handles tool registration, lifecycle management, and automatically uses native Chromium implementation when available.
|
|
1746
|
-
|
|
1747
|
-
### What about browser support?
|
|
1748
|
-
|
|
1749
|
-
Works in all modern browsers. Native API support is available in Chromium with experimental flags enabled.
|
|
1750
|
-
|
|
1751
|
-
## 🤝 Related Packages
|
|
540
|
+
## Related Packages
|
|
1752
541
|
|
|
1753
542
|
- [`@mcp-b/transports`](https://docs.mcp-b.ai/packages/transports) - MCP transport implementations
|
|
1754
543
|
- [`@mcp-b/react-webmcp`](https://docs.mcp-b.ai/packages/react-webmcp) - React hooks for MCP
|
|
1755
544
|
- [`@mcp-b/extension-tools`](https://docs.mcp-b.ai/packages/extension-tools) - Chrome Extension API tools
|
|
1756
545
|
- [`@mcp-b/chrome-devtools-mcp`](https://docs.mcp-b.ai/packages/chrome-devtools-mcp) - Connect desktop AI agents to browser tools
|
|
1757
|
-
- [`@modelcontextprotocol/sdk`](https://www.npmjs.com/package/@modelcontextprotocol/sdk) - Official MCP SDK
|
|
1758
546
|
|
|
1759
|
-
##
|
|
547
|
+
## Resources
|
|
1760
548
|
|
|
1761
549
|
- [WebMCP Documentation](https://docs.mcp-b.ai)
|
|
1762
550
|
- [Web Model Context API Explainer](https://github.com/nicolo-ribaudo/model-context-protocol-api)
|
|
1763
551
|
- [Model Context Protocol Spec](https://modelcontextprotocol.io/)
|
|
1764
|
-
- [MCP GitHub Repository](https://github.com/modelcontextprotocol)
|
|
1765
552
|
|
|
1766
|
-
##
|
|
553
|
+
## License
|
|
1767
554
|
|
|
1768
555
|
MIT - see [LICENSE](../../LICENSE) for details
|
|
1769
|
-
|
|
1770
|
-
## 🙋 Support
|
|
1771
|
-
|
|
1772
|
-
- [GitHub Issues](https://github.com/WebMCP-org/npm-packages/issues)
|
|
1773
|
-
- [Documentation](https://docs.mcp-b.ai)
|
|
1774
|
-
- [Discord Community](https://discord.gg/a9fBR6Bw)
|