@mcp-b/global 1.0.11 → 1.0.14
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/LICENSE +21 -0
- package/README.md +555 -326
- package/dist/index.d.ts +199 -16
- package/dist/index.d.ts.map +1 -0
- package/dist/index.iife.js +6 -0
- package/dist/index.js +431 -2
- package/dist/index.js.map +1 -1
- package/package.json +53 -49
- package/dist/.tsbuildinfo +0 -1
- package/dist/index.cjs +0 -3
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -23
- package/dist/index.umd.js +0 -11
- package/dist/index.umd.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,432 @@
|
|
|
1
|
-
import {TabServerTransport}from
|
|
2
|
-
|
|
1
|
+
import { TabServerTransport } from "@mcp-b/transports";
|
|
2
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, Server } from "@mcp-b/webmcp-ts-sdk";
|
|
3
|
+
import { jsonSchemaToZod } from "@composio/json-schema-to-zod";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
//#region src/validation.ts
|
|
7
|
+
/**
|
|
8
|
+
* Detect if a schema is a Zod schema object (Record<string, ZodType>)
|
|
9
|
+
* or a JSON Schema object
|
|
10
|
+
*/
|
|
11
|
+
function isZodSchema(schema) {
|
|
12
|
+
if (typeof schema !== "object" || schema === null) return false;
|
|
13
|
+
if ("type" in schema && typeof schema.type === "string") return false;
|
|
14
|
+
const values = Object.values(schema);
|
|
15
|
+
if (values.length === 0) return false;
|
|
16
|
+
return values.some((val) => val instanceof z.ZodType);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Convert JSON Schema to Zod validator
|
|
20
|
+
* Uses @composio/json-schema-to-zod for conversion
|
|
21
|
+
*/
|
|
22
|
+
function jsonSchemaToZod$1(jsonSchema) {
|
|
23
|
+
try {
|
|
24
|
+
return jsonSchemaToZod(jsonSchema);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.warn("[Web Model Context] Failed to convert JSON Schema to Zod:", error);
|
|
27
|
+
return z.object({}).passthrough();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Convert Zod schema object to JSON Schema
|
|
32
|
+
* Based on react-webmcp implementation
|
|
33
|
+
*/
|
|
34
|
+
function zodToJsonSchema(schema) {
|
|
35
|
+
const properties = {};
|
|
36
|
+
const required = [];
|
|
37
|
+
for (const [key, zodType] of Object.entries(schema)) {
|
|
38
|
+
const description = zodType.description || void 0;
|
|
39
|
+
let type = "string";
|
|
40
|
+
let enumValues;
|
|
41
|
+
let items;
|
|
42
|
+
if (zodType instanceof z.ZodString) type = "string";
|
|
43
|
+
else if (zodType instanceof z.ZodNumber) type = "number";
|
|
44
|
+
else if (zodType instanceof z.ZodBoolean) type = "boolean";
|
|
45
|
+
else if (zodType instanceof z.ZodArray) {
|
|
46
|
+
type = "array";
|
|
47
|
+
const elementType = zodType.element;
|
|
48
|
+
if (elementType instanceof z.ZodString) items = { type: "string" };
|
|
49
|
+
else if (elementType instanceof z.ZodNumber) items = { type: "number" };
|
|
50
|
+
else if (elementType instanceof z.ZodBoolean) items = { type: "boolean" };
|
|
51
|
+
else items = { type: "string" };
|
|
52
|
+
} else if (zodType instanceof z.ZodObject) type = "object";
|
|
53
|
+
else if (zodType instanceof z.ZodEnum) {
|
|
54
|
+
type = "string";
|
|
55
|
+
const enumDef = zodType._def;
|
|
56
|
+
if (enumDef?.values) enumValues = enumDef.values;
|
|
57
|
+
}
|
|
58
|
+
const propertySchema = { type };
|
|
59
|
+
if (description) propertySchema.description = description;
|
|
60
|
+
if (enumValues) propertySchema.enum = enumValues;
|
|
61
|
+
if (items) propertySchema.items = items;
|
|
62
|
+
properties[key] = propertySchema;
|
|
63
|
+
if (!zodType.isOptional()) required.push(key);
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
type: "object",
|
|
67
|
+
properties,
|
|
68
|
+
...required.length > 0 && { required }
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Normalize a schema to both JSON Schema and Zod formats
|
|
73
|
+
* Detects which format is provided and converts to the other
|
|
74
|
+
*/
|
|
75
|
+
function normalizeSchema(schema) {
|
|
76
|
+
if (isZodSchema(schema)) return {
|
|
77
|
+
jsonSchema: zodToJsonSchema(schema),
|
|
78
|
+
zodValidator: z.object(schema)
|
|
79
|
+
};
|
|
80
|
+
const jsonSchema = schema;
|
|
81
|
+
return {
|
|
82
|
+
jsonSchema,
|
|
83
|
+
zodValidator: jsonSchemaToZod$1(jsonSchema)
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Validate data with Zod schema and return formatted result
|
|
88
|
+
*/
|
|
89
|
+
function validateWithZod(data, validator) {
|
|
90
|
+
const result = validator.safeParse(data);
|
|
91
|
+
if (!result.success) return {
|
|
92
|
+
success: false,
|
|
93
|
+
error: `Validation failed:\n${result.error.errors.map((err) => ` - ${err.path.join(".") || "root"}: ${err.message}`).join("\n")}`
|
|
94
|
+
};
|
|
95
|
+
return {
|
|
96
|
+
success: true,
|
|
97
|
+
data: result.data
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
//#endregion
|
|
102
|
+
//#region src/global.ts
|
|
103
|
+
/**
|
|
104
|
+
* Custom ToolCallEvent implementation
|
|
105
|
+
*/
|
|
106
|
+
var WebToolCallEvent = class extends Event {
|
|
107
|
+
name;
|
|
108
|
+
arguments;
|
|
109
|
+
_response = null;
|
|
110
|
+
_responded = false;
|
|
111
|
+
constructor(toolName, args) {
|
|
112
|
+
super("toolcall", { cancelable: true });
|
|
113
|
+
this.name = toolName;
|
|
114
|
+
this.arguments = args;
|
|
115
|
+
}
|
|
116
|
+
respondWith(response) {
|
|
117
|
+
if (this._responded) throw new Error("Response already provided for this tool call");
|
|
118
|
+
this._response = response;
|
|
119
|
+
this._responded = true;
|
|
120
|
+
}
|
|
121
|
+
getResponse() {
|
|
122
|
+
return this._response;
|
|
123
|
+
}
|
|
124
|
+
hasResponse() {
|
|
125
|
+
return this._responded;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* Time window (in ms) to detect rapid duplicate registrations
|
|
130
|
+
* Registrations within this window are likely due to React Strict Mode
|
|
131
|
+
*/
|
|
132
|
+
const RAPID_DUPLICATE_WINDOW_MS = 50;
|
|
133
|
+
/**
|
|
134
|
+
* ModelContext implementation that bridges to MCP SDK
|
|
135
|
+
* Implements the W3C Web Model Context API proposal with two-bucket tool management
|
|
136
|
+
*
|
|
137
|
+
* Two-Bucket System:
|
|
138
|
+
* - Bucket A (provideContextTools): Tools registered via provideContext() - base/app-level tools
|
|
139
|
+
* - Bucket B (dynamicTools): Tools registered via registerTool() - component-scoped tools
|
|
140
|
+
*
|
|
141
|
+
* Benefits:
|
|
142
|
+
* - provideContext() only clears Bucket A, leaving Bucket B intact
|
|
143
|
+
* - Components can manage their own tool lifecycle independently
|
|
144
|
+
* - Final tool list = Bucket A + Bucket B (merged, with collision detection)
|
|
145
|
+
*/
|
|
146
|
+
var WebModelContext = class {
|
|
147
|
+
bridge;
|
|
148
|
+
eventTarget;
|
|
149
|
+
provideContextTools;
|
|
150
|
+
dynamicTools;
|
|
151
|
+
registrationTimestamps;
|
|
152
|
+
unregisterFunctions;
|
|
153
|
+
constructor(bridge) {
|
|
154
|
+
this.bridge = bridge;
|
|
155
|
+
this.eventTarget = new EventTarget();
|
|
156
|
+
this.provideContextTools = /* @__PURE__ */ new Map();
|
|
157
|
+
this.dynamicTools = /* @__PURE__ */ new Map();
|
|
158
|
+
this.registrationTimestamps = /* @__PURE__ */ new Map();
|
|
159
|
+
this.unregisterFunctions = /* @__PURE__ */ new Map();
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Add event listener (compatible with ModelContext interface)
|
|
163
|
+
*/
|
|
164
|
+
addEventListener(type, listener, options) {
|
|
165
|
+
this.eventTarget.addEventListener(type, listener, options);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Remove event listener
|
|
169
|
+
*/
|
|
170
|
+
removeEventListener(type, listener, options) {
|
|
171
|
+
this.eventTarget.removeEventListener(type, listener, options);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Dispatch event
|
|
175
|
+
*/
|
|
176
|
+
dispatchEvent(event) {
|
|
177
|
+
return this.eventTarget.dispatchEvent(event);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Provide context (tools) to AI models
|
|
181
|
+
* Clears and replaces Bucket A (provideContext tools), leaving Bucket B (dynamic tools) intact
|
|
182
|
+
*/
|
|
183
|
+
provideContext(context) {
|
|
184
|
+
console.log(`[Web Model Context] Registering ${context.tools.length} tools via provideContext`);
|
|
185
|
+
this.provideContextTools.clear();
|
|
186
|
+
for (const tool of context.tools) {
|
|
187
|
+
if (this.dynamicTools.has(tool.name)) throw new Error(`[Web Model Context] Tool name collision: "${tool.name}" is already registered via registerTool(). Please use a different name or unregister the dynamic tool first.`);
|
|
188
|
+
const { jsonSchema: inputJson, zodValidator: inputZod } = normalizeSchema(tool.inputSchema);
|
|
189
|
+
const normalizedOutput = tool.outputSchema ? normalizeSchema(tool.outputSchema) : null;
|
|
190
|
+
const validatedTool = {
|
|
191
|
+
name: tool.name,
|
|
192
|
+
description: tool.description,
|
|
193
|
+
inputSchema: inputJson,
|
|
194
|
+
...normalizedOutput && { outputSchema: normalizedOutput.jsonSchema },
|
|
195
|
+
...tool.annotations && { annotations: tool.annotations },
|
|
196
|
+
execute: tool.execute,
|
|
197
|
+
inputValidator: inputZod,
|
|
198
|
+
...normalizedOutput && { outputValidator: normalizedOutput.zodValidator }
|
|
199
|
+
};
|
|
200
|
+
this.provideContextTools.set(tool.name, validatedTool);
|
|
201
|
+
}
|
|
202
|
+
this.updateBridgeTools();
|
|
203
|
+
if (this.bridge.server.notification) this.bridge.server.notification({
|
|
204
|
+
method: "notifications/tools/list_changed",
|
|
205
|
+
params: {}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Register a single tool dynamically (Bucket B)
|
|
210
|
+
* Returns an object with an unregister function to remove the tool
|
|
211
|
+
* Tools registered via this method persist across provideContext() calls
|
|
212
|
+
*/
|
|
213
|
+
registerTool(tool) {
|
|
214
|
+
console.log(`[Web Model Context] Registering tool dynamically: ${tool.name}`);
|
|
215
|
+
const now = Date.now();
|
|
216
|
+
const lastRegistration = this.registrationTimestamps.get(tool.name);
|
|
217
|
+
if (lastRegistration && now - lastRegistration < RAPID_DUPLICATE_WINDOW_MS) {
|
|
218
|
+
console.warn(`[Web Model Context] Tool "${tool.name}" registered multiple times within ${RAPID_DUPLICATE_WINDOW_MS}ms. This is likely due to React Strict Mode double-mounting. Ignoring duplicate registration.`);
|
|
219
|
+
const existingUnregister = this.unregisterFunctions.get(tool.name);
|
|
220
|
+
if (existingUnregister) return { unregister: existingUnregister };
|
|
221
|
+
}
|
|
222
|
+
if (this.provideContextTools.has(tool.name)) throw new Error(`[Web Model Context] Tool name collision: "${tool.name}" is already registered via provideContext(). Please use a different name or update your provideContext() call.`);
|
|
223
|
+
if (this.dynamicTools.has(tool.name)) throw new Error(`[Web Model Context] Tool name collision: "${tool.name}" is already registered via registerTool(). Please unregister it first or use a different name.`);
|
|
224
|
+
const { jsonSchema: inputJson, zodValidator: inputZod } = normalizeSchema(tool.inputSchema);
|
|
225
|
+
const normalizedOutput = tool.outputSchema ? normalizeSchema(tool.outputSchema) : null;
|
|
226
|
+
const validatedTool = {
|
|
227
|
+
name: tool.name,
|
|
228
|
+
description: tool.description,
|
|
229
|
+
inputSchema: inputJson,
|
|
230
|
+
...normalizedOutput && { outputSchema: normalizedOutput.jsonSchema },
|
|
231
|
+
...tool.annotations && { annotations: tool.annotations },
|
|
232
|
+
execute: tool.execute,
|
|
233
|
+
inputValidator: inputZod,
|
|
234
|
+
...normalizedOutput && { outputValidator: normalizedOutput.zodValidator }
|
|
235
|
+
};
|
|
236
|
+
this.dynamicTools.set(tool.name, validatedTool);
|
|
237
|
+
this.registrationTimestamps.set(tool.name, now);
|
|
238
|
+
this.updateBridgeTools();
|
|
239
|
+
if (this.bridge.server.notification) this.bridge.server.notification({
|
|
240
|
+
method: "notifications/tools/list_changed",
|
|
241
|
+
params: {}
|
|
242
|
+
});
|
|
243
|
+
const unregisterFn = () => {
|
|
244
|
+
console.log(`[Web Model Context] Unregistering tool: ${tool.name}`);
|
|
245
|
+
if (this.provideContextTools.has(tool.name)) throw new Error(`[Web Model Context] Cannot unregister tool "${tool.name}": This tool was registered via provideContext(). Use provideContext() to update the base tool set.`);
|
|
246
|
+
if (!this.dynamicTools.has(tool.name)) {
|
|
247
|
+
console.warn(`[Web Model Context] Tool "${tool.name}" is not registered, ignoring unregister call`);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
this.dynamicTools.delete(tool.name);
|
|
251
|
+
this.registrationTimestamps.delete(tool.name);
|
|
252
|
+
this.unregisterFunctions.delete(tool.name);
|
|
253
|
+
this.updateBridgeTools();
|
|
254
|
+
if (this.bridge.server.notification) this.bridge.server.notification({
|
|
255
|
+
method: "notifications/tools/list_changed",
|
|
256
|
+
params: {}
|
|
257
|
+
});
|
|
258
|
+
};
|
|
259
|
+
this.unregisterFunctions.set(tool.name, unregisterFn);
|
|
260
|
+
return { unregister: unregisterFn };
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Update the bridge tools map with merged tools from both buckets
|
|
264
|
+
* Final tool list = Bucket A (provideContext) + Bucket B (dynamic)
|
|
265
|
+
*/
|
|
266
|
+
updateBridgeTools() {
|
|
267
|
+
this.bridge.tools.clear();
|
|
268
|
+
for (const [name, tool] of this.provideContextTools) this.bridge.tools.set(name, tool);
|
|
269
|
+
for (const [name, tool] of this.dynamicTools) this.bridge.tools.set(name, tool);
|
|
270
|
+
console.log(`[Web Model Context] Updated bridge with ${this.provideContextTools.size} base tools + ${this.dynamicTools.size} dynamic tools = ${this.bridge.tools.size} total`);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Execute a tool with hybrid approach:
|
|
274
|
+
* 1. Validate input arguments
|
|
275
|
+
* 2. Dispatch toolcall event first
|
|
276
|
+
* 3. If not prevented, call tool's execute function
|
|
277
|
+
* 4. Validate output (permissive mode - warn only)
|
|
278
|
+
*/
|
|
279
|
+
async executeTool(toolName, args) {
|
|
280
|
+
const tool = this.bridge.tools.get(toolName);
|
|
281
|
+
if (!tool) throw new Error(`Tool not found: ${toolName}`);
|
|
282
|
+
console.log(`[Web Model Context] Validating input for tool: ${toolName}`);
|
|
283
|
+
const validation = validateWithZod(args, tool.inputValidator);
|
|
284
|
+
if (!validation.success) {
|
|
285
|
+
console.error(`[Web Model Context] Input validation failed for ${toolName}:`, validation.error);
|
|
286
|
+
return {
|
|
287
|
+
content: [{
|
|
288
|
+
type: "text",
|
|
289
|
+
text: `Input validation error for tool "${toolName}":\n${validation.error}`
|
|
290
|
+
}],
|
|
291
|
+
isError: true
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
const validatedArgs = validation.data;
|
|
295
|
+
const event = new WebToolCallEvent(toolName, validatedArgs);
|
|
296
|
+
this.dispatchEvent(event);
|
|
297
|
+
if (event.defaultPrevented && event.hasResponse()) {
|
|
298
|
+
const response = event.getResponse();
|
|
299
|
+
if (response) {
|
|
300
|
+
console.log(`[Web Model Context] Tool ${toolName} handled by event listener`);
|
|
301
|
+
return response;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
console.log(`[Web Model Context] Executing tool: ${toolName}`);
|
|
305
|
+
try {
|
|
306
|
+
const response = await tool.execute(validatedArgs);
|
|
307
|
+
if (tool.outputValidator && response.structuredContent) {
|
|
308
|
+
const outputValidation = validateWithZod(response.structuredContent, tool.outputValidator);
|
|
309
|
+
if (!outputValidation.success) console.warn(`[Web Model Context] Output validation failed for ${toolName}:`, outputValidation.error);
|
|
310
|
+
}
|
|
311
|
+
return response;
|
|
312
|
+
} catch (error) {
|
|
313
|
+
console.error(`[Web Model Context] Error executing tool ${toolName}:`, error);
|
|
314
|
+
return {
|
|
315
|
+
content: [{
|
|
316
|
+
type: "text",
|
|
317
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
318
|
+
}],
|
|
319
|
+
isError: true
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Get list of registered tools in MCP format
|
|
325
|
+
* Includes full MCP spec: annotations, outputSchema, etc.
|
|
326
|
+
*/
|
|
327
|
+
listTools() {
|
|
328
|
+
return Array.from(this.bridge.tools.values()).map((tool) => ({
|
|
329
|
+
name: tool.name,
|
|
330
|
+
description: tool.description,
|
|
331
|
+
inputSchema: tool.inputSchema,
|
|
332
|
+
...tool.outputSchema && { outputSchema: tool.outputSchema },
|
|
333
|
+
...tool.annotations && { annotations: tool.annotations }
|
|
334
|
+
}));
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
/**
|
|
338
|
+
* Initialize the MCP bridge
|
|
339
|
+
*/
|
|
340
|
+
function initializeMCPBridge() {
|
|
341
|
+
console.log("[Web Model Context] Initializing MCP bridge");
|
|
342
|
+
const server = new Server({
|
|
343
|
+
name: window.location.hostname || "localhost",
|
|
344
|
+
version: "1.0.0"
|
|
345
|
+
}, { capabilities: { tools: { listChanged: true } } });
|
|
346
|
+
const bridge = {
|
|
347
|
+
server,
|
|
348
|
+
tools: /* @__PURE__ */ new Map(),
|
|
349
|
+
modelContext: void 0,
|
|
350
|
+
isInitialized: true
|
|
351
|
+
};
|
|
352
|
+
bridge.modelContext = new WebModelContext(bridge);
|
|
353
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
354
|
+
console.log("[MCP Bridge] Handling list_tools request");
|
|
355
|
+
return { tools: bridge.modelContext.listTools() };
|
|
356
|
+
});
|
|
357
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
358
|
+
console.log(`[MCP Bridge] Handling call_tool request: ${request.params.name}`);
|
|
359
|
+
const toolName = request.params.name;
|
|
360
|
+
const args = request.params.arguments || {};
|
|
361
|
+
try {
|
|
362
|
+
const response = await bridge.modelContext.executeTool(toolName, args);
|
|
363
|
+
return {
|
|
364
|
+
content: response.content,
|
|
365
|
+
isError: response.isError
|
|
366
|
+
};
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.error(`[MCP Bridge] Error calling tool ${toolName}:`, error);
|
|
369
|
+
throw error;
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
const transport = new TabServerTransport({ allowedOrigins: ["*"] });
|
|
373
|
+
server.connect(transport);
|
|
374
|
+
console.log("[Web Model Context] MCP server connected");
|
|
375
|
+
return bridge;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Initialize the Web Model Context API (window.navigator.modelContext)
|
|
379
|
+
*/
|
|
380
|
+
function initializeWebModelContext() {
|
|
381
|
+
if (typeof window === "undefined") {
|
|
382
|
+
console.warn("[Web Model Context] Not in browser environment, skipping initialization");
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (window.navigator.modelContext) {
|
|
386
|
+
console.warn("[Web Model Context] window.navigator.modelContext already exists, skipping initialization");
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
try {
|
|
390
|
+
const bridge = initializeMCPBridge();
|
|
391
|
+
Object.defineProperty(window.navigator, "modelContext", {
|
|
392
|
+
value: bridge.modelContext,
|
|
393
|
+
writable: false,
|
|
394
|
+
configurable: false
|
|
395
|
+
});
|
|
396
|
+
Object.defineProperty(window, "__mcpBridge", {
|
|
397
|
+
value: bridge,
|
|
398
|
+
writable: false,
|
|
399
|
+
configurable: true
|
|
400
|
+
});
|
|
401
|
+
console.log("✅ [Web Model Context] window.navigator.modelContext initialized successfully");
|
|
402
|
+
} catch (error) {
|
|
403
|
+
console.error("[Web Model Context] Failed to initialize:", error);
|
|
404
|
+
throw error;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Cleanup function (for testing/development)
|
|
409
|
+
*/
|
|
410
|
+
function cleanupWebModelContext() {
|
|
411
|
+
if (typeof window === "undefined") return;
|
|
412
|
+
if (window.__mcpBridge) try {
|
|
413
|
+
window.__mcpBridge.server.close();
|
|
414
|
+
} catch (error) {
|
|
415
|
+
console.warn("[Web Model Context] Error closing MCP server:", error);
|
|
416
|
+
}
|
|
417
|
+
delete window.navigator.modelContext;
|
|
418
|
+
delete window.__mcpBridge;
|
|
419
|
+
console.log("[Web Model Context] Cleaned up");
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
//#endregion
|
|
423
|
+
//#region src/index.ts
|
|
424
|
+
if (typeof window !== "undefined" && typeof document !== "undefined") try {
|
|
425
|
+
initializeWebModelContext();
|
|
426
|
+
} catch (error) {
|
|
427
|
+
console.error("[Web Model Context] Auto-initialization failed:", error);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
//#endregion
|
|
431
|
+
export { cleanupWebModelContext, initializeWebModelContext };
|
|
3
432
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/global.ts","../src/index.ts"],"names":["serverInstance","isInitialized","initializeMCP","hostname","McpServer","error","transport","TabServerTransport","errorMessage","initializeGlobalMCP","cleanupGlobalMCP","autoInitialize","index_default"],"mappings":"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","file":"index.js","sourcesContent":["// global.ts\n\nimport { TabServerTransport } from '@mcp-b/transports';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nlet serverInstance: McpServer | null = null;\nlet isInitialized = false;\n\n/**\n * Internal initialization function that creates and configures the MCP server.\n * This function is idempotent and handles errors gracefully.\n * @throws {Error} If initialization fails\n */\nfunction initializeMCP(): void {\n if (serverInstance) {\n return;\n }\n\n try {\n const hostname = window.location.hostname || 'localhost';\n\n serverInstance = new McpServer(\n {\n name: hostname,\n version: '1.0.0',\n },\n {\n capabilities: {\n tools: {\n listChanged: true,\n },\n debouncedNotificationMethods: ['notifications/tools/list_changed'],\n },\n }\n );\n\n // Register default tool with proper error handling in the callback\n serverInstance.registerTool(\n 'get_current_website_title',\n {\n description: 'Get the title of the current website',\n },\n async () => {\n try {\n const title = document.title || 'Untitled';\n return { content: [{ type: 'text', text: title }] };\n } catch (error) {\n console.error('Error in get_current_website_title tool:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error getting website title ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n\n // Configure transport with restricted origins if possible; '*' is insecure, consider environment-specific origins\n // For best security practices, replace '*' with specific allowed origins in production\n const transport = new TabServerTransport({\n allowedOrigins: ['*'],\n });\n\n serverInstance.connect(transport);\n } catch (error) {\n serverInstance = null;\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n throw new Error(`Failed to initialize MCP server: ${errorMessage}`);\n }\n}\n\n/**\n * Initializes the global MCP server and exposes it on window.mcp.\n * This function is safe to call multiple times - it will only initialize once.\n * It performs environment checks and handles browser-only execution.\n */\nexport function initializeGlobalMCP(): void {\n if (typeof window === 'undefined') {\n console.warn('initializeGlobalMCP called in non-browser environment; skipping.');\n return;\n }\n\n if (isInitialized && window.mcp && serverInstance) {\n return;\n }\n\n try {\n initializeMCP();\n if (serverInstance) {\n window.mcp = serverInstance;\n isInitialized = true;\n }\n } catch (error) {\n console.error('Failed to initialize global MCP:', error);\n throw error;\n }\n}\n\n/**\n * Cleanup function to properly dispose of the MCP server and remove global reference.\n * This is useful for testing, hot module replacement, or resetting the state.\n * It handles errors during cleanup gracefully.\n */\nexport function cleanupGlobalMCP(): void {\n if (serverInstance) {\n try {\n serverInstance.close();\n } catch (error) {\n console.warn('Error closing MCP server:', error);\n } finally {\n serverInstance = null;\n }\n }\n\n if (typeof window !== 'undefined' && 'mcp' in window) {\n delete (window as unknown as { mcp?: unknown }).mcp;\n }\n\n isInitialized = false;\n}\n","// index.ts\n\nimport { initializeGlobalMCP } from './global.js';\n\nexport * from './types.js';\nexport { initializeGlobalMCP, cleanupGlobalMCP } from './global.js';\n\n// Auto-initialize when script loads in browser environments\n// Using DOMContentLoaded for better timing instead of setTimeout(0), ensuring DOM is ready\nif (typeof window !== 'undefined' && typeof document !== 'undefined') {\n const autoInitialize = () => {\n try {\n initializeGlobalMCP();\n } catch (error) {\n console.error('Auto-initialization of global MCP failed:', error);\n }\n };\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', autoInitialize);\n } else {\n autoInitialize();\n }\n}\n\n// For manual initialization (when using as ES module)\nexport default initializeGlobalMCP;\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","names":["jsonSchemaToZod","convertJsonSchemaToZod","properties: Record<string, { type: string; description?: string; [key: string]: unknown }>","required: string[]","enumValues: unknown[] | undefined","items: unknown | undefined","propertySchema: { type: string; description?: string; [key: string]: unknown }","validatedTool: ValidatedToolDescriptor","McpServer","bridge: MCPBridge"],"sources":["../src/validation.ts","../src/global.ts","../src/index.ts"],"sourcesContent":["// validation.ts - JSON Schema <-> Zod conversion and validation utilities\n\nimport { jsonSchemaToZod as convertJsonSchemaToZod } from '@composio/json-schema-to-zod';\nimport { z } from 'zod';\nimport type { InputSchema } from './types.js';\n\n/**\n * Detect if a schema is a Zod schema object (Record<string, ZodType>)\n * or a JSON Schema object\n */\nexport function isZodSchema(schema: unknown): boolean {\n if (typeof schema !== 'object' || schema === null) {\n return false;\n }\n\n // JSON Schema always has a 'type' property at the root\n if ('type' in schema && typeof (schema as { type: unknown }).type === 'string') {\n return false; // This is JSON Schema\n }\n\n // Check if any property value is a Zod type instance\n const values = Object.values(schema);\n if (values.length === 0) {\n return false; // Empty object, treat as JSON Schema\n }\n\n // If any value is a ZodType, it's a Zod schema\n return values.some((val) => val instanceof z.ZodType);\n}\n\n/**\n * Convert JSON Schema to Zod validator\n * Uses @composio/json-schema-to-zod for conversion\n */\nexport function jsonSchemaToZod(jsonSchema: InputSchema): z.ZodType {\n try {\n // convertJsonSchemaToZod returns a Zod schema from JSON Schema\n const zodSchema = convertJsonSchemaToZod(jsonSchema as unknown as object);\n return zodSchema;\n } catch (error) {\n console.warn('[Web Model Context] Failed to convert JSON Schema to Zod:', error);\n // Fallback: accept anything with passthrough\n return z.object({}).passthrough();\n }\n}\n\n/**\n * Convert Zod schema object to JSON Schema\n * Based on react-webmcp implementation\n */\nexport function zodToJsonSchema(schema: Record<string, z.ZodTypeAny>): InputSchema {\n const properties: Record<string, { type: string; description?: string; [key: string]: unknown }> =\n {};\n const required: string[] = [];\n\n for (const [key, zodType] of Object.entries(schema)) {\n // Extract description if available\n const description = (zodType as { description?: string }).description || undefined;\n\n // Infer JSON Schema type from Zod type\n let type = 'string';\n let enumValues: unknown[] | undefined;\n let items: unknown | undefined;\n\n if (zodType instanceof z.ZodString) {\n type = 'string';\n } else if (zodType instanceof z.ZodNumber) {\n type = 'number';\n } else if (zodType instanceof z.ZodBoolean) {\n type = 'boolean';\n } else if (zodType instanceof z.ZodArray) {\n type = 'array';\n // Try to get array item type\n const elementType = (zodType as { element?: z.ZodTypeAny }).element;\n if (elementType instanceof z.ZodString) {\n items = { type: 'string' };\n } else if (elementType instanceof z.ZodNumber) {\n items = { type: 'number' };\n } else if (elementType instanceof z.ZodBoolean) {\n items = { type: 'boolean' };\n } else {\n items = { type: 'string' };\n }\n } else if (zodType instanceof z.ZodObject) {\n type = 'object';\n } else if (zodType instanceof z.ZodEnum) {\n type = 'string';\n // Extract enum values\n const enumDef = (zodType as { _def?: { values?: unknown[] } })._def;\n if (enumDef?.values) {\n enumValues = enumDef.values;\n }\n }\n\n const propertySchema: { type: string; description?: string; [key: string]: unknown } = { type };\n if (description) {\n propertySchema.description = description;\n }\n if (enumValues) {\n propertySchema.enum = enumValues;\n }\n if (items) {\n propertySchema.items = items;\n }\n\n properties[key] = propertySchema;\n\n // Check if field is required (not optional)\n if (!zodType.isOptional()) {\n required.push(key);\n }\n }\n\n return {\n type: 'object',\n properties,\n ...(required.length > 0 && { required }),\n };\n}\n\n/**\n * Normalize a schema to both JSON Schema and Zod formats\n * Detects which format is provided and converts to the other\n */\nexport function normalizeSchema(schema: InputSchema | Record<string, z.ZodTypeAny>): {\n jsonSchema: InputSchema;\n zodValidator: z.ZodType;\n} {\n const isZod = isZodSchema(schema);\n\n if (isZod) {\n // Input is Zod schema object → convert to JSON Schema and wrap in z.object()\n const jsonSchema = zodToJsonSchema(schema as Record<string, z.ZodTypeAny>);\n const zodValidator = z.object(schema as Record<string, z.ZodTypeAny>);\n return { jsonSchema, zodValidator };\n }\n\n // Input is JSON Schema → convert to Zod\n const jsonSchema = schema as InputSchema;\n const zodValidator = jsonSchemaToZod(jsonSchema);\n return { jsonSchema, zodValidator };\n}\n\n/**\n * Validate data with Zod schema and return formatted result\n */\nexport function validateWithZod(\n data: unknown,\n validator: z.ZodType\n): { success: true; data: unknown } | { success: false; error: string } {\n const result = validator.safeParse(data);\n\n if (!result.success) {\n // Format Zod errors into readable message\n const errors = result.error.errors\n .map((err) => ` - ${err.path.join('.') || 'root'}: ${err.message}`)\n .join('\\n');\n return {\n success: false,\n error: `Validation failed:\\n${errors}`,\n };\n }\n\n return {\n success: true,\n data: result.data,\n };\n}\n","// global.ts - Web Model Context API Implementation\n// Bridges the Web Model Context API (window.navigator.modelContext) to MCP SDK\n\nimport { TabServerTransport } from '@mcp-b/transports';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n Server as McpServer,\n} from '@mcp-b/webmcp-ts-sdk';\nimport type {\n InternalModelContext,\n MCPBridge,\n ModelContextInput,\n ToolCallEvent,\n ToolDescriptor,\n ToolResponse,\n ValidatedToolDescriptor,\n} from './types.js';\nimport { normalizeSchema, validateWithZod } from './validation.js';\n\n/**\n * Custom ToolCallEvent implementation\n */\nclass WebToolCallEvent extends Event implements ToolCallEvent {\n public name: string;\n public arguments: Record<string, unknown>;\n private _response: ToolResponse | null = null;\n private _responded = false;\n\n constructor(toolName: string, args: Record<string, unknown>) {\n super('toolcall', { cancelable: true });\n this.name = toolName;\n this.arguments = args;\n }\n\n respondWith(response: ToolResponse): void {\n if (this._responded) {\n throw new Error('Response already provided for this tool call');\n }\n this._response = response;\n this._responded = true;\n }\n\n getResponse(): ToolResponse | null {\n return this._response;\n }\n\n hasResponse(): boolean {\n return this._responded;\n }\n}\n\n/**\n * Time window (in ms) to detect rapid duplicate registrations\n * Registrations within this window are likely due to React Strict Mode\n */\nconst RAPID_DUPLICATE_WINDOW_MS = 50;\n\n/**\n * ModelContext implementation that bridges to MCP SDK\n * Implements the W3C Web Model Context API proposal with two-bucket tool management\n *\n * Two-Bucket System:\n * - Bucket A (provideContextTools): Tools registered via provideContext() - base/app-level tools\n * - Bucket B (dynamicTools): Tools registered via registerTool() - component-scoped tools\n *\n * Benefits:\n * - provideContext() only clears Bucket A, leaving Bucket B intact\n * - Components can manage their own tool lifecycle independently\n * - Final tool list = Bucket A + Bucket B (merged, with collision detection)\n */\nclass WebModelContext implements InternalModelContext {\n private bridge: MCPBridge;\n private eventTarget: EventTarget;\n\n // Bucket A: Tools from provideContext() - cleared when provideContext is called again\n private provideContextTools: Map<string, ValidatedToolDescriptor>;\n\n // Bucket B: Tools from registerTool() - persist across provideContext calls\n private dynamicTools: Map<string, ValidatedToolDescriptor>;\n\n // Track registration timestamps for rapid duplicate detection (React Strict Mode)\n private registrationTimestamps: Map<string, number>;\n\n // Store unregister functions for returning on rapid duplicates\n private unregisterFunctions: Map<string, () => void>;\n\n constructor(bridge: MCPBridge) {\n this.bridge = bridge;\n this.eventTarget = new EventTarget();\n this.provideContextTools = new Map();\n this.dynamicTools = new Map();\n this.registrationTimestamps = new Map();\n this.unregisterFunctions = new Map();\n }\n\n /**\n * Add event listener (compatible with ModelContext interface)\n */\n addEventListener(\n type: 'toolcall',\n listener: (event: ToolCallEvent) => void | Promise<void>,\n options?: boolean | AddEventListenerOptions\n ): void {\n this.eventTarget.addEventListener(type, listener as EventListener, options);\n }\n\n /**\n * Remove event listener\n */\n removeEventListener(\n type: 'toolcall',\n listener: (event: ToolCallEvent) => void | Promise<void>,\n options?: boolean | EventListenerOptions\n ): void {\n this.eventTarget.removeEventListener(type, listener as EventListener, options);\n }\n\n /**\n * Dispatch event\n */\n dispatchEvent(event: Event): boolean {\n return this.eventTarget.dispatchEvent(event);\n }\n\n /**\n * Provide context (tools) to AI models\n * Clears and replaces Bucket A (provideContext tools), leaving Bucket B (dynamic tools) intact\n */\n provideContext(context: ModelContextInput): void {\n console.log(`[Web Model Context] Registering ${context.tools.length} tools via provideContext`);\n\n // Clear only Bucket A (provideContext tools)\n this.provideContextTools.clear();\n\n // Process each tool: normalize schemas and create validated descriptors\n for (const tool of context.tools) {\n // Check for name collisions with Bucket B (dynamic tools)\n if (this.dynamicTools.has(tool.name)) {\n throw new Error(\n `[Web Model Context] Tool name collision: \"${tool.name}\" is already registered via registerTool(). ` +\n 'Please use a different name or unregister the dynamic tool first.'\n );\n }\n\n // Normalize input schema (convert to both JSON Schema and Zod)\n const { jsonSchema: inputJson, zodValidator: inputZod } = normalizeSchema(tool.inputSchema);\n\n // Normalize output schema if provided\n const normalizedOutput = tool.outputSchema ? normalizeSchema(tool.outputSchema) : null;\n\n // Create validated tool descriptor\n const validatedTool: ValidatedToolDescriptor = {\n name: tool.name,\n description: tool.description,\n inputSchema: inputJson,\n ...(normalizedOutput && { outputSchema: normalizedOutput.jsonSchema }),\n ...(tool.annotations && { annotations: tool.annotations }),\n execute: tool.execute,\n inputValidator: inputZod,\n ...(normalizedOutput && { outputValidator: normalizedOutput.zodValidator }),\n };\n\n // Add to Bucket A\n this.provideContextTools.set(tool.name, validatedTool);\n }\n\n // Update the merged tool list in bridge\n this.updateBridgeTools();\n\n // Notify that tools list changed\n if (this.bridge.server.notification) {\n this.bridge.server.notification({\n method: 'notifications/tools/list_changed',\n params: {},\n });\n }\n }\n\n /**\n * Register a single tool dynamically (Bucket B)\n * Returns an object with an unregister function to remove the tool\n * Tools registered via this method persist across provideContext() calls\n */\n registerTool(tool: ToolDescriptor<any, any>): { unregister: () => void } {\n console.log(`[Web Model Context] Registering tool dynamically: ${tool.name}`);\n\n // Check for rapid duplicate registration (React Strict Mode detection)\n const now = Date.now();\n const lastRegistration = this.registrationTimestamps.get(tool.name);\n\n if (lastRegistration && now - lastRegistration < RAPID_DUPLICATE_WINDOW_MS) {\n console.warn(\n `[Web Model Context] Tool \"${tool.name}\" registered multiple times within ${RAPID_DUPLICATE_WINDOW_MS}ms. ` +\n 'This is likely due to React Strict Mode double-mounting. Ignoring duplicate registration.'\n );\n\n // Return the existing unregister function\n const existingUnregister = this.unregisterFunctions.get(tool.name);\n if (existingUnregister) {\n return { unregister: existingUnregister };\n }\n }\n\n // Check for name collision with Bucket A (provideContext tools)\n if (this.provideContextTools.has(tool.name)) {\n throw new Error(\n `[Web Model Context] Tool name collision: \"${tool.name}\" is already registered via provideContext(). ` +\n 'Please use a different name or update your provideContext() call.'\n );\n }\n\n // Check for name collision within Bucket B (genuine duplicate, not rapid)\n if (this.dynamicTools.has(tool.name)) {\n throw new Error(\n `[Web Model Context] Tool name collision: \"${tool.name}\" is already registered via registerTool(). ` +\n 'Please unregister it first or use a different name.'\n );\n }\n\n // Normalize input schema (convert to both JSON Schema and Zod)\n const { jsonSchema: inputJson, zodValidator: inputZod } = normalizeSchema(tool.inputSchema);\n\n // Normalize output schema if provided\n const normalizedOutput = tool.outputSchema ? normalizeSchema(tool.outputSchema) : null;\n\n // Create validated tool descriptor\n const validatedTool: ValidatedToolDescriptor = {\n name: tool.name,\n description: tool.description,\n inputSchema: inputJson,\n ...(normalizedOutput && { outputSchema: normalizedOutput.jsonSchema }),\n ...(tool.annotations && { annotations: tool.annotations }),\n execute: tool.execute,\n inputValidator: inputZod,\n ...(normalizedOutput && { outputValidator: normalizedOutput.zodValidator }),\n };\n\n // Add to Bucket B (dynamic tools)\n this.dynamicTools.set(tool.name, validatedTool);\n\n // Store registration timestamp for rapid duplicate detection\n this.registrationTimestamps.set(tool.name, now);\n\n // Update the merged tool list in bridge\n this.updateBridgeTools();\n\n // Notify that tools list changed\n if (this.bridge.server.notification) {\n this.bridge.server.notification({\n method: 'notifications/tools/list_changed',\n params: {},\n });\n }\n\n // Create unregister function\n const unregisterFn = () => {\n console.log(`[Web Model Context] Unregistering tool: ${tool.name}`);\n\n // Check if this tool was registered via provideContext\n if (this.provideContextTools.has(tool.name)) {\n throw new Error(\n `[Web Model Context] Cannot unregister tool \"${tool.name}\": ` +\n 'This tool was registered via provideContext(). Use provideContext() to update the base tool set.'\n );\n }\n\n // Remove from Bucket B\n if (!this.dynamicTools.has(tool.name)) {\n console.warn(\n `[Web Model Context] Tool \"${tool.name}\" is not registered, ignoring unregister call`\n );\n return;\n }\n\n this.dynamicTools.delete(tool.name);\n\n // Clean up tracking data\n this.registrationTimestamps.delete(tool.name);\n this.unregisterFunctions.delete(tool.name);\n\n // Update the merged tool list in bridge\n this.updateBridgeTools();\n\n // Notify that tools list changed\n if (this.bridge.server.notification) {\n this.bridge.server.notification({\n method: 'notifications/tools/list_changed',\n params: {},\n });\n }\n };\n\n // Store unregister function for rapid duplicate detection\n this.unregisterFunctions.set(tool.name, unregisterFn);\n\n // Return unregister function\n return { unregister: unregisterFn };\n }\n\n /**\n * Update the bridge tools map with merged tools from both buckets\n * Final tool list = Bucket A (provideContext) + Bucket B (dynamic)\n */\n private updateBridgeTools(): void {\n // Clear the bridge tools map\n this.bridge.tools.clear();\n\n // Add tools from Bucket A (provideContext tools)\n for (const [name, tool] of this.provideContextTools) {\n this.bridge.tools.set(name, tool);\n }\n\n // Add tools from Bucket B (dynamic tools)\n for (const [name, tool] of this.dynamicTools) {\n this.bridge.tools.set(name, tool);\n }\n\n console.log(\n `[Web Model Context] Updated bridge with ${this.provideContextTools.size} base tools + ${this.dynamicTools.size} dynamic tools = ${this.bridge.tools.size} total`\n );\n }\n\n /**\n * Execute a tool with hybrid approach:\n * 1. Validate input arguments\n * 2. Dispatch toolcall event first\n * 3. If not prevented, call tool's execute function\n * 4. Validate output (permissive mode - warn only)\n */\n async executeTool(toolName: string, args: Record<string, unknown>): Promise<ToolResponse> {\n const tool = this.bridge.tools.get(toolName);\n if (!tool) {\n throw new Error(`Tool not found: ${toolName}`);\n }\n\n // 1. VALIDATE INPUT ARGUMENTS\n console.log(`[Web Model Context] Validating input for tool: ${toolName}`);\n const validation = validateWithZod(args, tool.inputValidator);\n if (!validation.success) {\n console.error(\n `[Web Model Context] Input validation failed for ${toolName}:`,\n validation.error\n );\n return {\n content: [\n {\n type: 'text',\n text: `Input validation error for tool \"${toolName}\":\\n${validation.error}`,\n },\n ],\n isError: true,\n };\n }\n\n // Use validated data for execution\n const validatedArgs = validation.data as Record<string, unknown>;\n\n // 2. Create toolcall event\n const event = new WebToolCallEvent(toolName, validatedArgs);\n\n // Dispatch event to listeners\n this.dispatchEvent(event);\n\n // If event was prevented and response provided, use that\n if (event.defaultPrevented && event.hasResponse()) {\n const response = event.getResponse();\n if (response) {\n console.log(`[Web Model Context] Tool ${toolName} handled by event listener`);\n return response;\n }\n }\n\n // 3. Execute the tool's execute function\n console.log(`[Web Model Context] Executing tool: ${toolName}`);\n try {\n const response = await tool.execute(validatedArgs);\n\n // 4. VALIDATE OUTPUT (permissive mode - warn only, don't block)\n if (tool.outputValidator && response.structuredContent) {\n const outputValidation = validateWithZod(response.structuredContent, tool.outputValidator);\n if (!outputValidation.success) {\n console.warn(\n `[Web Model Context] Output validation failed for ${toolName}:`,\n outputValidation.error\n );\n // Continue anyway - permissive mode\n }\n }\n\n return response;\n } catch (error) {\n console.error(`[Web Model Context] Error executing tool ${toolName}:`, error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n /**\n * Get list of registered tools in MCP format\n * Includes full MCP spec: annotations, outputSchema, etc.\n */\n listTools() {\n return Array.from(this.bridge.tools.values()).map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n ...(tool.outputSchema && { outputSchema: tool.outputSchema }),\n ...(tool.annotations && { annotations: tool.annotations }),\n }));\n }\n}\n\n/**\n * Initialize the MCP bridge\n */\nfunction initializeMCPBridge(): MCPBridge {\n console.log('[Web Model Context] Initializing MCP bridge');\n\n const hostname = window.location.hostname || 'localhost';\n\n // Create MCP server\n const server = new McpServer(\n {\n name: hostname,\n version: '1.0.0',\n },\n {\n capabilities: {\n tools: {\n listChanged: true,\n },\n },\n }\n );\n\n // Create bridge object (modelContext is assigned after instantiation)\n const bridge: MCPBridge = {\n server,\n tools: new Map(),\n modelContext: undefined as unknown as InternalModelContext,\n isInitialized: true,\n };\n\n // Create modelContext and attach to bridge\n const modelContext = new WebModelContext(bridge);\n bridge.modelContext = modelContext;\n\n // Set up MCP server handlers\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n console.log('[MCP Bridge] Handling list_tools request');\n return {\n tools: bridge.modelContext.listTools(),\n };\n });\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n console.log(`[MCP Bridge] Handling call_tool request: ${request.params.name}`);\n\n const toolName = request.params.name;\n const args = (request.params.arguments || {}) as Record<string, unknown>;\n\n try {\n const response = await bridge.modelContext.executeTool(toolName, args);\n // Return in MCP SDK format\n return {\n content: response.content,\n isError: response.isError,\n };\n } catch (error) {\n console.error(`[MCP Bridge] Error calling tool ${toolName}:`, error);\n throw error;\n }\n });\n\n // Connect transport\n const transport = new TabServerTransport({\n allowedOrigins: ['*'], // TODO: Make this configurable\n });\n\n server.connect(transport);\n\n console.log('[Web Model Context] MCP server connected');\n\n return bridge;\n}\n\n/**\n * Initialize the Web Model Context API (window.navigator.modelContext)\n */\nexport function initializeWebModelContext(): void {\n if (typeof window === 'undefined') {\n console.warn('[Web Model Context] Not in browser environment, skipping initialization');\n return;\n }\n\n if (window.navigator.modelContext) {\n console.warn(\n '[Web Model Context] window.navigator.modelContext already exists, skipping initialization'\n );\n return;\n }\n\n try {\n // Initialize MCP bridge\n const bridge = initializeMCPBridge();\n\n // Expose shared modelContext instance\n Object.defineProperty(window.navigator, 'modelContext', {\n value: bridge.modelContext,\n writable: false,\n configurable: false,\n });\n\n // Expose bridge for debugging\n Object.defineProperty(window, '__mcpBridge', {\n value: bridge,\n writable: false,\n configurable: true,\n });\n\n console.log('✅ [Web Model Context] window.navigator.modelContext initialized successfully');\n } catch (error) {\n console.error('[Web Model Context] Failed to initialize:', error);\n throw error;\n }\n}\n\n/**\n * Cleanup function (for testing/development)\n */\nexport function cleanupWebModelContext(): void {\n if (typeof window === 'undefined') return;\n\n if (window.__mcpBridge) {\n try {\n window.__mcpBridge.server.close();\n } catch (error) {\n console.warn('[Web Model Context] Error closing MCP server:', error);\n }\n }\n\n delete (window.navigator as unknown as { modelContext?: unknown }).modelContext;\n delete (window as unknown as { __mcpBridge?: unknown }).__mcpBridge;\n\n console.log('[Web Model Context] Cleaned up');\n}\n","// index.ts - Entry point for Web Model Context API polyfill\n\nimport { initializeWebModelContext } from './global.js';\n\n// Auto-initialize immediately when script loads in browser environments\nif (typeof window !== 'undefined' && typeof document !== 'undefined') {\n try {\n initializeWebModelContext();\n } catch (error) {\n console.error('[Web Model Context] Auto-initialization failed:', error);\n }\n}\n\n// For manual initialization (when using as ES module)\nexport { cleanupWebModelContext, initializeWebModelContext } from './global.js';\nexport type * from './types.js';\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,75 +1,79 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-b/global",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": "Alex Nahas",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/MiguelsPizza/WebMCP.git",
|
|
10
|
-
"directory": "packages/global"
|
|
11
|
-
},
|
|
12
|
-
"homepage": "https://github.com/MiguelsPizza/WebMCP#readme",
|
|
13
|
-
"bugs": {
|
|
14
|
-
"url": "https://github.com/MiguelsPizza/WebMCP/issues"
|
|
15
|
-
},
|
|
3
|
+
"version": "1.0.14",
|
|
4
|
+
"description": "W3C Web Model Context API polyfill - implements window.navigator.modelContext bridging to Model Context Protocol",
|
|
16
5
|
"keywords": [
|
|
17
6
|
"mcp",
|
|
18
7
|
"model-context-protocol",
|
|
8
|
+
"web-model-context",
|
|
9
|
+
"navigator.modelContext",
|
|
10
|
+
"w3c",
|
|
19
11
|
"browser",
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
12
|
+
"polyfill",
|
|
13
|
+
"web-standard",
|
|
14
|
+
"ai-agents",
|
|
15
|
+
"tools"
|
|
24
16
|
],
|
|
17
|
+
"homepage": "https://github.com/MiguelsPizza/WebMCP#readme",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/MiguelsPizza/WebMCP/issues"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/MiguelsPizza/WebMCP.git",
|
|
24
|
+
"directory": "packages/global"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"author": "Alex Nahas",
|
|
28
|
+
"sideEffects": true,
|
|
25
29
|
"type": "module",
|
|
26
|
-
"main": "./dist/index.cjs",
|
|
27
|
-
"module": "./dist/index.js",
|
|
28
|
-
"browser": "./dist/index.umd.js",
|
|
29
|
-
"unpkg": "./dist/index.umd.js",
|
|
30
|
-
"types": "./dist/index.d.ts",
|
|
31
30
|
"exports": {
|
|
32
31
|
".": {
|
|
33
32
|
"types": "./dist/index.d.ts",
|
|
34
33
|
"import": "./dist/index.js",
|
|
35
|
-
"
|
|
36
|
-
"browser": "./dist/index.umd.js"
|
|
34
|
+
"default": "./dist/index.js"
|
|
37
35
|
},
|
|
38
|
-
"./
|
|
36
|
+
"./iife": {
|
|
37
|
+
"script": "./dist/index.iife.js",
|
|
38
|
+
"default": "./dist/index.iife.js"
|
|
39
|
+
}
|
|
39
40
|
},
|
|
41
|
+
"main": "./dist/index.js",
|
|
42
|
+
"module": "./dist/index.js",
|
|
43
|
+
"browser": "./dist/index.iife.js",
|
|
44
|
+
"types": "./dist/index.d.ts",
|
|
40
45
|
"files": [
|
|
41
46
|
"dist"
|
|
42
47
|
],
|
|
43
|
-
"scripts": {
|
|
44
|
-
"build": "tsup",
|
|
45
|
-
"typecheck": "tsc --noEmit",
|
|
46
|
-
"clean": "rm -rf dist .turbo",
|
|
47
|
-
"prepublishOnly": "pnpm run build",
|
|
48
|
-
"prepare:publish": "node ../../scripts/replace-workspace-deps.cjs ./package.json",
|
|
49
|
-
"publish:npm": "npm publish --access public --no-git-checks",
|
|
50
|
-
"publish:dry": "pnpm publish --access public --no-git-checks --dry-run",
|
|
51
|
-
"version:patch": "pnpm version patch --no-git-tag-version",
|
|
52
|
-
"version:minor": "pnpm version minor --no-git-tag-version",
|
|
53
|
-
"version:major": "pnpm version major --no-git-tag-version",
|
|
54
|
-
"lint": "biome lint --write .",
|
|
55
|
-
"format": "biome format --write .",
|
|
56
|
-
"check": "biome check --write ."
|
|
57
|
-
},
|
|
58
48
|
"dependencies": {
|
|
59
|
-
"@
|
|
60
|
-
"
|
|
49
|
+
"@composio/json-schema-to-zod": "^0.1.17",
|
|
50
|
+
"zod": "^3.25.76",
|
|
51
|
+
"@mcp-b/transports": "1.0.3",
|
|
52
|
+
"@mcp-b/webmcp-ts-sdk": "1.0.1"
|
|
61
53
|
},
|
|
62
54
|
"devDependencies": {
|
|
63
|
-
"@types/node": "
|
|
64
|
-
"
|
|
65
|
-
"typescript": "
|
|
55
|
+
"@types/node": "^22.15.21",
|
|
56
|
+
"tsdown": "^0.15.10",
|
|
57
|
+
"typescript": "^5.8.3"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=18"
|
|
66
61
|
},
|
|
67
62
|
"publishConfig": {
|
|
68
63
|
"access": "public",
|
|
69
64
|
"registry": "https://registry.npmjs.org/"
|
|
70
65
|
},
|
|
71
|
-
"
|
|
72
|
-
|
|
73
|
-
"
|
|
66
|
+
"scripts": {
|
|
67
|
+
"build": "tsdown",
|
|
68
|
+
"check": "biome check --write .",
|
|
69
|
+
"clean": "rm -rf dist .turbo",
|
|
70
|
+
"format": "biome format --write .",
|
|
71
|
+
"lint": "biome lint --write .",
|
|
72
|
+
"publish:dry": "pnpm publish --access public --dry-run",
|
|
73
|
+
"publish:npm": "pnpm publish --access public",
|
|
74
|
+
"typecheck": "tsc --noEmit",
|
|
75
|
+
"version:major": "pnpm version major --no-git-tag-version",
|
|
76
|
+
"version:minor": "pnpm version minor --no-git-tag-version",
|
|
77
|
+
"version:patch": "pnpm version patch --no-git-tag-version"
|
|
74
78
|
}
|
|
75
|
-
}
|
|
79
|
+
}
|