@mcp-b/chrome-devtools-mcp 1.5.6 → 1.5.7
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/build/src/main.js +16 -8
- package/build/src/tools/WebMCPToolHub.js +1 -15
- package/build/src/tools/webmcp.js +61 -222
- package/package.json +1 -1
package/build/src/main.js
CHANGED
|
@@ -59,6 +59,7 @@ async function getContext() {
|
|
|
59
59
|
}
|
|
60
60
|
const devtools = args.experimentalDevtools ?? false;
|
|
61
61
|
let browser;
|
|
62
|
+
let wasLaunched = false;
|
|
62
63
|
// If explicit browserUrl or wsEndpoint is provided, connect without fallback
|
|
63
64
|
if (args.browserUrl || args.wsEndpoint) {
|
|
64
65
|
browser = await ensureBrowserConnected({
|
|
@@ -98,6 +99,7 @@ async function getContext() {
|
|
|
98
99
|
acceptInsecureCerts: args.acceptInsecureCerts,
|
|
99
100
|
devtools,
|
|
100
101
|
});
|
|
102
|
+
wasLaunched = true;
|
|
101
103
|
}
|
|
102
104
|
}
|
|
103
105
|
// Otherwise, just launch a new browser
|
|
@@ -114,16 +116,25 @@ async function getContext() {
|
|
|
114
116
|
acceptInsecureCerts: args.acceptInsecureCerts,
|
|
115
117
|
devtools,
|
|
116
118
|
});
|
|
119
|
+
wasLaunched = true;
|
|
117
120
|
}
|
|
118
121
|
if (context?.browser !== browser) {
|
|
119
122
|
context = await McpContext.from(browser, logger, {
|
|
120
123
|
experimentalDevToolsDebugging: devtools,
|
|
121
124
|
experimentalIncludeAllPages: args.experimentalIncludeAllPages,
|
|
122
125
|
});
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
126
|
+
if (wasLaunched) {
|
|
127
|
+
// Fresh browser launch - use the existing default page
|
|
128
|
+
// Mark it as explicitly selected so this session stays pinned to it
|
|
129
|
+
context.selectPage(context.getSelectedPage(), true);
|
|
130
|
+
logger('Using existing window for this MCP session');
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// Connected to existing browser - create new window for isolation
|
|
134
|
+
// This ensures multiple MCP clients don't step on each other's toes
|
|
135
|
+
await context.newWindow();
|
|
136
|
+
logger('Created new window for this MCP session');
|
|
137
|
+
}
|
|
127
138
|
// Initialize WebMCP tool hub for dynamic tool registration
|
|
128
139
|
const toolHub = new WebMCPToolHub(server, context);
|
|
129
140
|
context.setToolHub(toolHub);
|
|
@@ -168,10 +179,7 @@ function registerTool(tool) {
|
|
|
168
179
|
}
|
|
169
180
|
server.registerTool(tool.name, {
|
|
170
181
|
description: tool.description,
|
|
171
|
-
|
|
172
|
-
inputSchema: tool.name === 'call_webmcp_tool'
|
|
173
|
-
? zod.record(zod.unknown())
|
|
174
|
-
: zod.object(tool.schema).passthrough(),
|
|
182
|
+
inputSchema: zod.object(tool.schema).passthrough(),
|
|
175
183
|
annotations: tool.annotations,
|
|
176
184
|
}, async (params) => {
|
|
177
185
|
const guard = await toolMutex.acquire();
|
|
@@ -27,8 +27,6 @@ export class WebMCPToolHub {
|
|
|
27
27
|
#syncInProgress = new WeakSet();
|
|
28
28
|
/** Whether automatic tool tracking is enabled. */
|
|
29
29
|
#enabled = true;
|
|
30
|
-
/** Last seen tool IDs for list_webmcp_tools. */
|
|
31
|
-
#lastSeenToolIds = null;
|
|
32
30
|
constructor(server, context, enabled = true) {
|
|
33
31
|
this.#server = server;
|
|
34
32
|
this.#context = context;
|
|
@@ -212,23 +210,11 @@ export class WebMCPToolHub {
|
|
|
212
210
|
domain: rt.domain,
|
|
213
211
|
pageIdx: this.#context.getPages().indexOf(rt.page),
|
|
214
212
|
description: rt.description,
|
|
213
|
+
inputSchema: rt.inputSchema,
|
|
215
214
|
}));
|
|
216
215
|
}
|
|
217
|
-
/**
|
|
218
|
-
* Get the last seen tool IDs (for list_webmcp_tools)
|
|
219
|
-
*/
|
|
220
|
-
getLastSeenToolIds() {
|
|
221
|
-
return this.#lastSeenToolIds;
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Set the last seen tool IDs (for list_webmcp_tools)
|
|
225
|
-
*/
|
|
226
|
-
setLastSeenToolIds(toolIds) {
|
|
227
|
-
this.#lastSeenToolIds = toolIds;
|
|
228
|
-
}
|
|
229
216
|
/**
|
|
230
217
|
* Get a tracked tool by name and page.
|
|
231
|
-
* Used by get_webmcp_tool_schema to retrieve schema.
|
|
232
218
|
*/
|
|
233
219
|
getToolByName(toolName, page) {
|
|
234
220
|
const pageToolIds = this.#pageTools.get(page);
|
|
@@ -78,190 +78,40 @@ import { defineTool } from './ToolDefinition.js';
|
|
|
78
78
|
// response.appendResponseLine(' 2. take_snapshot - verify page state');
|
|
79
79
|
// }
|
|
80
80
|
/**
|
|
81
|
-
*
|
|
82
|
-
* First call returns full list. Subsequent calls return only added/removed tools.
|
|
81
|
+
* List all WebMCP tools registered across all pages with full definitions including schemas.
|
|
83
82
|
*/
|
|
84
83
|
export const listWebMCPTools = defineTool({
|
|
85
84
|
name: 'list_webmcp_tools',
|
|
86
|
-
description: 'List all WebMCP tools registered across all pages
|
|
87
|
-
'
|
|
88
|
-
'
|
|
89
|
-
'To call these tools, use call_webmcp_tool with the ORIGINAL tool name (without the webmcp_ prefix). ' +
|
|
90
|
-
'Example: call_webmcp_tool({ name: "idp_config_get" }) NOT "webmcp_localhost_3000_page0_idp_config_get".',
|
|
85
|
+
description: 'List all WebMCP tools registered across all pages. ' +
|
|
86
|
+
'Returns full tool definitions including input schemas. ' +
|
|
87
|
+
'To call a tool, use call_webmcp_tool({ name: "tool_name", arguments: {...} }).',
|
|
91
88
|
annotations: {
|
|
92
|
-
title: '
|
|
89
|
+
title: 'List Website MCP Tools',
|
|
93
90
|
category: ToolCategory.WEBMCP,
|
|
94
91
|
readOnlyHint: true,
|
|
95
92
|
},
|
|
96
|
-
schema: {
|
|
97
|
-
|
|
98
|
-
.boolean()
|
|
99
|
-
.optional()
|
|
100
|
-
.describe('Force full tool list instead of diff. Default: false'),
|
|
101
|
-
},
|
|
102
|
-
handler: async (request, response, context) => {
|
|
103
|
-
const { full } = request.params;
|
|
93
|
+
schema: {},
|
|
94
|
+
handler: async (_request, response, context) => {
|
|
104
95
|
const toolHub = context.getToolHub();
|
|
105
96
|
if (!toolHub) {
|
|
106
97
|
response.appendResponseLine('WebMCPToolHub not initialized.');
|
|
107
98
|
return;
|
|
108
99
|
}
|
|
109
100
|
const tools = toolHub.getRegisteredTools();
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (!lastSeen || full) {
|
|
114
|
-
toolHub.setLastSeenToolIds(currentToolIds);
|
|
115
|
-
if (tools.length === 0) {
|
|
116
|
-
response.appendResponseLine('No WebMCP tools registered.');
|
|
117
|
-
response.appendResponseLine('Navigate to a page with @mcp-b/global loaded to discover tools.');
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
response.appendResponseLine(`Found ${tools.length} WebMCP tool(s):`);
|
|
121
|
-
response.appendResponseLine('');
|
|
122
|
-
// Group tools by page
|
|
123
|
-
const toolsByPage = new Map();
|
|
124
|
-
for (const tool of tools) {
|
|
125
|
-
if (!toolsByPage.has(tool.pageIdx)) {
|
|
126
|
-
toolsByPage.set(tool.pageIdx, []);
|
|
127
|
-
}
|
|
128
|
-
toolsByPage.get(tool.pageIdx).push(tool);
|
|
129
|
-
}
|
|
130
|
-
// Sort pages by index
|
|
131
|
-
const sortedPages = Array.from(toolsByPage.keys()).sort((a, b) => a - b);
|
|
132
|
-
for (const pageIdx of sortedPages) {
|
|
133
|
-
const pageTools = toolsByPage.get(pageIdx);
|
|
134
|
-
const domain = pageTools[0].domain;
|
|
135
|
-
response.appendResponseLine(`Page ${pageIdx}: ${domain}`);
|
|
136
|
-
for (const tool of pageTools) {
|
|
137
|
-
response.appendResponseLine(` • ${tool.originalName}`);
|
|
138
|
-
if (tool.description) {
|
|
139
|
-
response.appendResponseLine(` ${tool.description}`);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
response.appendResponseLine('');
|
|
143
|
-
}
|
|
144
|
-
response.appendResponseLine('To call a tool: call_webmcp_tool({ name: "tool_name", arguments: {...} })');
|
|
145
|
-
if (tools.length > 0) {
|
|
146
|
-
response.appendResponseLine(`Example: call_webmcp_tool({ name: "${tools[0].originalName}" })`);
|
|
147
|
-
}
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
// Subsequent calls: return diff
|
|
151
|
-
const added = tools.filter(t => !lastSeen.has(t.toolId));
|
|
152
|
-
const removed = [...lastSeen].filter(id => !currentToolIds.has(id));
|
|
153
|
-
toolHub.setLastSeenToolIds(currentToolIds);
|
|
154
|
-
if (added.length === 0 && removed.length === 0) {
|
|
155
|
-
response.appendResponseLine('No changes since last poll.');
|
|
156
|
-
response.appendResponseLine('');
|
|
157
|
-
if (tools.length > 0) {
|
|
158
|
-
// Group tools by page
|
|
159
|
-
const toolsByPage = new Map();
|
|
160
|
-
for (const tool of tools) {
|
|
161
|
-
if (!toolsByPage.has(tool.pageIdx)) {
|
|
162
|
-
toolsByPage.set(tool.pageIdx, []);
|
|
163
|
-
}
|
|
164
|
-
toolsByPage.get(tool.pageIdx).push(tool);
|
|
165
|
-
}
|
|
166
|
-
response.appendResponseLine(`${tools.length} tool(s) available:`);
|
|
167
|
-
const sortedPages = Array.from(toolsByPage.keys()).sort((a, b) => a - b);
|
|
168
|
-
for (const pageIdx of sortedPages) {
|
|
169
|
-
const pageTools = toolsByPage.get(pageIdx);
|
|
170
|
-
const toolNames = pageTools.map(t => t.originalName).join(', ');
|
|
171
|
-
response.appendResponseLine(` Page ${pageIdx}: ${toolNames}`);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
if (added.length > 0) {
|
|
177
|
-
response.appendResponseLine(`Added ${added.length} new tool(s):`);
|
|
178
|
-
response.appendResponseLine('');
|
|
179
|
-
// Group by page
|
|
180
|
-
const addedByPage = new Map();
|
|
181
|
-
for (const tool of added) {
|
|
182
|
-
if (!addedByPage.has(tool.pageIdx)) {
|
|
183
|
-
addedByPage.set(tool.pageIdx, []);
|
|
184
|
-
}
|
|
185
|
-
addedByPage.get(tool.pageIdx).push(tool);
|
|
186
|
-
}
|
|
187
|
-
const sortedPages = Array.from(addedByPage.keys()).sort((a, b) => a - b);
|
|
188
|
-
for (const pageIdx of sortedPages) {
|
|
189
|
-
const pageTools = addedByPage.get(pageIdx);
|
|
190
|
-
const domain = pageTools[0].domain;
|
|
191
|
-
response.appendResponseLine(`Page ${pageIdx}: ${domain}`);
|
|
192
|
-
for (const tool of pageTools) {
|
|
193
|
-
response.appendResponseLine(` + ${tool.originalName}`);
|
|
194
|
-
if (tool.description) {
|
|
195
|
-
response.appendResponseLine(` ${tool.description}`);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
response.appendResponseLine('');
|
|
199
|
-
}
|
|
200
|
-
response.appendResponseLine(`Call with: call_webmcp_tool({ name: "${added[0].originalName}" })`);
|
|
201
|
-
response.appendResponseLine('');
|
|
202
|
-
}
|
|
203
|
-
if (removed.length > 0) {
|
|
204
|
-
response.appendResponseLine(`Removed ${removed.length} tool(s)`);
|
|
205
|
-
response.appendResponseLine('');
|
|
206
|
-
for (const id of removed) {
|
|
207
|
-
// Extract original name from toolId format: webmcp_{domain}_page{idx}_{name}
|
|
208
|
-
const parts = id.split('_');
|
|
209
|
-
const name = parts.slice(3).join('_'); // Everything after page index
|
|
210
|
-
response.appendResponseLine(` - ${name || id}`);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
},
|
|
214
|
-
});
|
|
215
|
-
/**
|
|
216
|
-
* Get the JSON Schema for a WebMCP tool.
|
|
217
|
-
* Use this to understand what arguments a tool expects before calling it.
|
|
218
|
-
*/
|
|
219
|
-
export const getWebMCPToolSchema = defineTool({
|
|
220
|
-
name: 'get_webmcp_tool_schema',
|
|
221
|
-
description: 'Get the JSON Schema for a WebMCP tool registered on a webpage. ' +
|
|
222
|
-
'Use this to understand what arguments a tool expects before calling it with call_webmcp_tool. ' +
|
|
223
|
-
'Returns the inputSchema from the tool definition.',
|
|
224
|
-
annotations: {
|
|
225
|
-
title: 'Get WebMCP Tool Schema',
|
|
226
|
-
category: ToolCategory.WEBMCP,
|
|
227
|
-
readOnlyHint: true,
|
|
228
|
-
},
|
|
229
|
-
schema: {
|
|
230
|
-
name: zod.string().describe('The name of the tool to get the schema for'),
|
|
231
|
-
page_index: zod
|
|
232
|
-
.number()
|
|
233
|
-
.int()
|
|
234
|
-
.optional()
|
|
235
|
-
.describe('Index of the page where the tool is registered. If not specified, uses the currently selected page. ' +
|
|
236
|
-
'Use list_pages to see available pages and their indices.'),
|
|
237
|
-
},
|
|
238
|
-
handler: async (request, response, context) => {
|
|
239
|
-
const { name, page_index } = request.params;
|
|
240
|
-
// Get the target page
|
|
241
|
-
const page = page_index !== undefined
|
|
242
|
-
? context.getPageByIdx(page_index)
|
|
243
|
-
: context.getSelectedPage();
|
|
244
|
-
const toolHub = context.getToolHub();
|
|
245
|
-
if (!toolHub) {
|
|
246
|
-
response.appendResponseLine('WebMCPToolHub not initialized.');
|
|
247
|
-
response.setIsError(true);
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
const trackedTool = toolHub.getToolByName(name, page);
|
|
251
|
-
if (!trackedTool) {
|
|
252
|
-
response.appendResponseLine(`Tool "${name}" not found on this page.`);
|
|
253
|
-
response.appendResponseLine('');
|
|
254
|
-
response.appendResponseLine('Use list_webmcp_tools to see available tools.');
|
|
255
|
-
response.setIsError(true);
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
if (!trackedTool.inputSchema) {
|
|
259
|
-
response.appendResponseLine(`Tool "${name}" has no schema defined.`);
|
|
101
|
+
if (tools.length === 0) {
|
|
102
|
+
response.appendResponseLine('No WebMCP tools registered.');
|
|
103
|
+
response.appendResponseLine('Navigate to a page with @mcp-b/global loaded to discover tools.');
|
|
260
104
|
return;
|
|
261
105
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
106
|
+
// Format as JSON for easy parsing by the model
|
|
107
|
+
const toolDefinitions = tools.map(tool => ({
|
|
108
|
+
name: tool.originalName,
|
|
109
|
+
description: tool.description,
|
|
110
|
+
inputSchema: tool.inputSchema,
|
|
111
|
+
pageIdx: tool.pageIdx,
|
|
112
|
+
domain: tool.domain,
|
|
113
|
+
}));
|
|
114
|
+
response.appendResponseLine(JSON.stringify({ tools: toolDefinitions }, null, 2));
|
|
265
115
|
},
|
|
266
116
|
});
|
|
267
117
|
/**
|
|
@@ -271,7 +121,7 @@ export const getWebMCPToolSchema = defineTool({
|
|
|
271
121
|
export const callWebMCPTool = defineTool({
|
|
272
122
|
name: 'call_webmcp_tool',
|
|
273
123
|
description: 'Call a tool registered on a webpage via WebMCP. ' +
|
|
274
|
-
'
|
|
124
|
+
'Usage: call_webmcp_tool({ name: "tool_name", arguments: { key: "value" } }). ' +
|
|
275
125
|
'Use list_webmcp_tools to see available tools and their schemas. ' +
|
|
276
126
|
'Use page_index to target a specific page.',
|
|
277
127
|
annotations: {
|
|
@@ -317,20 +167,11 @@ export const callWebMCPTool = defineTool({
|
|
|
317
167
|
}
|
|
318
168
|
const client = result.client;
|
|
319
169
|
try {
|
|
320
|
-
if (page_index !== undefined) {
|
|
321
|
-
response.appendResponseLine(`Page ${page_index}: ${page.url()}`);
|
|
322
|
-
}
|
|
323
|
-
response.appendResponseLine(`Calling tool: ${name}`);
|
|
324
|
-
if (args && Object.keys(args).length > 0) {
|
|
325
|
-
response.appendResponseLine(`Arguments: ${JSON.stringify(args)}`);
|
|
326
|
-
}
|
|
327
|
-
response.appendResponseLine('');
|
|
328
170
|
const callResult = await client.callTool({
|
|
329
171
|
name,
|
|
330
172
|
arguments: args || {},
|
|
331
173
|
});
|
|
332
|
-
|
|
333
|
-
// Format the result content
|
|
174
|
+
// Pass through the result content directly (includes Zod validation errors)
|
|
334
175
|
if (callResult.content && Array.isArray(callResult.content)) {
|
|
335
176
|
for (const content of callResult.content) {
|
|
336
177
|
if (content.type === 'text') {
|
|
@@ -351,8 +192,6 @@ export const callWebMCPTool = defineTool({
|
|
|
351
192
|
response.appendResponseLine(JSON.stringify(callResult, null, 2));
|
|
352
193
|
}
|
|
353
194
|
if (callResult.isError) {
|
|
354
|
-
response.appendResponseLine('');
|
|
355
|
-
response.appendResponseLine('(Tool returned an error)');
|
|
356
195
|
response.setIsError(true);
|
|
357
196
|
}
|
|
358
197
|
}
|
|
@@ -458,7 +297,7 @@ export const callWebMCPTool = defineTool({
|
|
|
458
297
|
// timeout = 5000,
|
|
459
298
|
// page_index,
|
|
460
299
|
// } = request.params;
|
|
461
|
-
//
|
|
300
|
+
//
|
|
462
301
|
// // Validate that exactly one of code or file_path is provided
|
|
463
302
|
// if (!code && !file_path) {
|
|
464
303
|
// response.appendResponseLine(
|
|
@@ -466,20 +305,20 @@ export const callWebMCPTool = defineTool({
|
|
|
466
305
|
// );
|
|
467
306
|
// return;
|
|
468
307
|
// }
|
|
469
|
-
//
|
|
308
|
+
//
|
|
470
309
|
// if (code && file_path) {
|
|
471
310
|
// response.appendResponseLine(
|
|
472
311
|
// 'Error: Provide either code or file_path, not both.',
|
|
473
312
|
// );
|
|
474
313
|
// return;
|
|
475
314
|
// }
|
|
476
|
-
//
|
|
315
|
+
//
|
|
477
316
|
// // Get the script code - from file or inline
|
|
478
317
|
// let scriptCode: string;
|
|
479
318
|
// if (file_path) {
|
|
480
319
|
// const ext = extname(file_path).toLowerCase();
|
|
481
320
|
// const isTypeScript = ext === '.ts' || ext === '.tsx';
|
|
482
|
-
//
|
|
321
|
+
//
|
|
483
322
|
// try {
|
|
484
323
|
// if (isTypeScript) {
|
|
485
324
|
// // Bundle TypeScript with esbuild (in-memory, ~10ms)
|
|
@@ -494,7 +333,7 @@ export const callWebMCPTool = defineTool({
|
|
|
494
333
|
// }
|
|
495
334
|
// } catch (err) {
|
|
496
335
|
// const message = err instanceof Error ? err.message : String(err);
|
|
497
|
-
//
|
|
336
|
+
//
|
|
498
337
|
// // Log the error with full context for debugging
|
|
499
338
|
// console.error('[injectWebMCPScript] File operation failed', {
|
|
500
339
|
// file_path,
|
|
@@ -504,7 +343,7 @@ export const callWebMCPTool = defineTool({
|
|
|
504
343
|
// errno: (err as NodeJS.ErrnoException).errno,
|
|
505
344
|
// code: (err as NodeJS.ErrnoException).code,
|
|
506
345
|
// });
|
|
507
|
-
//
|
|
346
|
+
//
|
|
508
347
|
// if (isTypeScript) {
|
|
509
348
|
// response.appendResponseLine(`Error bundling TypeScript: ${message}`);
|
|
510
349
|
// response.appendResponseLine('');
|
|
@@ -514,7 +353,7 @@ export const callWebMCPTool = defineTool({
|
|
|
514
353
|
// response.appendResponseLine(' - Invalid import paths');
|
|
515
354
|
// } else {
|
|
516
355
|
// response.appendResponseLine(`Error reading file: ${message}`);
|
|
517
|
-
//
|
|
356
|
+
//
|
|
518
357
|
// // Provide specific guidance based on error code
|
|
519
358
|
// if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
520
359
|
// response.appendResponseLine('');
|
|
@@ -529,7 +368,7 @@ export const callWebMCPTool = defineTool({
|
|
|
529
368
|
// } else {
|
|
530
369
|
// scriptCode = code!;
|
|
531
370
|
// }
|
|
532
|
-
//
|
|
371
|
+
//
|
|
533
372
|
// // Get the target page with proper error handling
|
|
534
373
|
// let page;
|
|
535
374
|
// try {
|
|
@@ -542,10 +381,10 @@ export const callWebMCPTool = defineTool({
|
|
|
542
381
|
// response.appendResponseLine(`Error: Invalid page_index - ${message}`);
|
|
543
382
|
// return;
|
|
544
383
|
// }
|
|
545
|
-
//
|
|
384
|
+
//
|
|
546
385
|
// response.appendResponseLine(`Target: ${page.url()}`);
|
|
547
386
|
// response.appendResponseLine('');
|
|
548
|
-
//
|
|
387
|
+
//
|
|
549
388
|
// try {
|
|
550
389
|
// // Check if polyfill already exists
|
|
551
390
|
// const hasPolyfill = await page.evaluate(() =>
|
|
@@ -553,9 +392,9 @@ export const callWebMCPTool = defineTool({
|
|
|
553
392
|
// typeof (navigator as Navigator & {modelContext?: unknown}).modelContext !==
|
|
554
393
|
// 'undefined',
|
|
555
394
|
// );
|
|
556
|
-
//
|
|
395
|
+
//
|
|
557
396
|
// let codeToInject = scriptCode;
|
|
558
|
-
//
|
|
397
|
+
//
|
|
559
398
|
// if (hasPolyfill) {
|
|
560
399
|
// response.appendResponseLine('Polyfill already present');
|
|
561
400
|
// } else {
|
|
@@ -574,10 +413,10 @@ export const callWebMCPTool = defineTool({
|
|
|
574
413
|
// return;
|
|
575
414
|
// }
|
|
576
415
|
// }
|
|
577
|
-
//
|
|
416
|
+
//
|
|
578
417
|
// // Inject the script
|
|
579
418
|
// response.appendResponseLine('Injecting userscript...');
|
|
580
|
-
//
|
|
419
|
+
//
|
|
581
420
|
// await page.evaluate((bundleCode: string) => {
|
|
582
421
|
// const script = document.createElement('script');
|
|
583
422
|
// script.textContent = bundleCode;
|
|
@@ -585,9 +424,9 @@ export const callWebMCPTool = defineTool({
|
|
|
585
424
|
// document.getElementById('__webmcp_injected_script__')?.remove();
|
|
586
425
|
// document.head.appendChild(script);
|
|
587
426
|
// }, codeToInject);
|
|
588
|
-
//
|
|
427
|
+
//
|
|
589
428
|
// response.appendResponseLine('Script injected');
|
|
590
|
-
//
|
|
429
|
+
//
|
|
591
430
|
// if (!wait_for_tools) {
|
|
592
431
|
// response.appendResponseLine('');
|
|
593
432
|
// response.appendResponseLine(
|
|
@@ -595,17 +434,17 @@ export const callWebMCPTool = defineTool({
|
|
|
595
434
|
// );
|
|
596
435
|
// return;
|
|
597
436
|
// }
|
|
598
|
-
//
|
|
437
|
+
//
|
|
599
438
|
// // Poll for polyfill initialization instead of using a magic sleep number.
|
|
600
439
|
// // TabServerTransport registers asynchronously after the polyfill script executes.
|
|
601
440
|
// // We poll at 100ms intervals until navigator.modelContext is available.
|
|
602
441
|
// response.appendResponseLine('Waiting for polyfill to initialize...');
|
|
603
|
-
//
|
|
442
|
+
//
|
|
604
443
|
// const polyfillTimeout = Math.min(timeout, 5000); // Cap at 5s for polyfill init
|
|
605
444
|
// const polyfillStart = Date.now();
|
|
606
445
|
// let polyfillReady = false;
|
|
607
446
|
// const polyfillErrors: Array<{time: number; error: string}> = [];
|
|
608
|
-
//
|
|
447
|
+
//
|
|
609
448
|
// while (Date.now() - polyfillStart < polyfillTimeout) {
|
|
610
449
|
// try {
|
|
611
450
|
// polyfillReady = await page.evaluate(() =>
|
|
@@ -619,7 +458,7 @@ export const callWebMCPTool = defineTool({
|
|
|
619
458
|
// } catch (err) {
|
|
620
459
|
// const message = err instanceof Error ? err.message : String(err);
|
|
621
460
|
// polyfillErrors.push({time: Date.now() - polyfillStart, error: message});
|
|
622
|
-
//
|
|
461
|
+
//
|
|
623
462
|
// // Abort on non-retryable errors
|
|
624
463
|
// if (
|
|
625
464
|
// message.includes('Target closed') ||
|
|
@@ -635,7 +474,7 @@ export const callWebMCPTool = defineTool({
|
|
|
635
474
|
// }
|
|
636
475
|
// await new Promise(r => setTimeout(r, 100));
|
|
637
476
|
// }
|
|
638
|
-
//
|
|
477
|
+
//
|
|
639
478
|
// if (!polyfillReady) {
|
|
640
479
|
// response.appendResponseLine('');
|
|
641
480
|
// response.appendResponseLine(
|
|
@@ -657,18 +496,18 @@ export const callWebMCPTool = defineTool({
|
|
|
657
496
|
// appendDebugSteps(response);
|
|
658
497
|
// return;
|
|
659
498
|
// }
|
|
660
|
-
//
|
|
499
|
+
//
|
|
661
500
|
// response.appendResponseLine('Polyfill initialized');
|
|
662
|
-
//
|
|
501
|
+
//
|
|
663
502
|
// // Make a single connection attempt (don't poll - that creates racing transports)
|
|
664
503
|
// response.appendResponseLine('Connecting to WebMCP server...');
|
|
665
|
-
//
|
|
504
|
+
//
|
|
666
505
|
// const result = await context.getWebMCPClient(page);
|
|
667
506
|
// if (!result.connected) {
|
|
668
507
|
// response.appendResponseLine('');
|
|
669
508
|
// response.appendResponseLine(`Connection failed: ${result.error}`);
|
|
670
509
|
// response.appendResponseLine('');
|
|
671
|
-
//
|
|
510
|
+
//
|
|
672
511
|
// // Provide error-specific guidance
|
|
673
512
|
// const errorLower = result.error.toLowerCase();
|
|
674
513
|
// if (errorLower.includes('timeout')) {
|
|
@@ -688,28 +527,28 @@ export const callWebMCPTool = defineTool({
|
|
|
688
527
|
// );
|
|
689
528
|
// response.appendResponseLine('');
|
|
690
529
|
// }
|
|
691
|
-
//
|
|
530
|
+
//
|
|
692
531
|
// appendDebugSteps(response);
|
|
693
532
|
// return;
|
|
694
533
|
// }
|
|
695
|
-
//
|
|
534
|
+
//
|
|
696
535
|
// // TypeScript now knows result is {connected: true; client: Client}
|
|
697
536
|
// response.appendResponseLine('Connected to WebMCP server');
|
|
698
|
-
//
|
|
537
|
+
//
|
|
699
538
|
// // Now poll for tools (using the established connection)
|
|
700
539
|
// response.appendResponseLine(`Waiting for tools (${timeout}ms)...`);
|
|
701
|
-
//
|
|
540
|
+
//
|
|
702
541
|
// const startTime = Date.now();
|
|
703
542
|
// let lastError: Error | null = null;
|
|
704
543
|
// let successfulPolls = 0;
|
|
705
544
|
// let failedPolls = 0;
|
|
706
|
-
//
|
|
545
|
+
//
|
|
707
546
|
// while (Date.now() - startTime < timeout) {
|
|
708
547
|
// try {
|
|
709
548
|
// const {tools} = await result.client.listTools();
|
|
710
549
|
// successfulPolls++;
|
|
711
550
|
// lastError = null;
|
|
712
|
-
//
|
|
551
|
+
//
|
|
713
552
|
// if (tools.length > 0) {
|
|
714
553
|
// const toolHub = context.getToolHub();
|
|
715
554
|
// if (toolHub) {
|
|
@@ -723,15 +562,15 @@ export const callWebMCPTool = defineTool({
|
|
|
723
562
|
// response.appendResponseLine('⚠️ Warning: Tool hub unavailable. Tools detected but may not be callable.');
|
|
724
563
|
// response.appendResponseLine('');
|
|
725
564
|
// }
|
|
726
|
-
//
|
|
565
|
+
//
|
|
727
566
|
// response.appendResponseLine('');
|
|
728
567
|
// response.appendResponseLine(`${tools.length} tool(s) detected:`);
|
|
729
568
|
// response.appendResponseLine('');
|
|
730
|
-
//
|
|
569
|
+
//
|
|
731
570
|
// const domain = extractDomain(page.url());
|
|
732
571
|
// const pages = context.getPages();
|
|
733
572
|
// const pageIdx = pages.indexOf(page);
|
|
734
|
-
//
|
|
573
|
+
//
|
|
735
574
|
// for (const tool of tools) {
|
|
736
575
|
// const toolId = `webmcp_${domain}_page${pageIdx}_${tool.name}`;
|
|
737
576
|
// response.appendResponseLine(` - ${tool.name}`);
|
|
@@ -763,7 +602,7 @@ export const callWebMCPTool = defineTool({
|
|
|
763
602
|
// } catch (err) {
|
|
764
603
|
// lastError = err instanceof Error ? err : new Error(String(err));
|
|
765
604
|
// failedPolls++;
|
|
766
|
-
//
|
|
605
|
+
//
|
|
767
606
|
// // Non-retryable errors should abort immediately
|
|
768
607
|
// const message = lastError.message.toLowerCase();
|
|
769
608
|
// if (
|
|
@@ -781,10 +620,10 @@ export const callWebMCPTool = defineTool({
|
|
|
781
620
|
// return;
|
|
782
621
|
// }
|
|
783
622
|
// }
|
|
784
|
-
//
|
|
623
|
+
//
|
|
785
624
|
// await new Promise(r => setTimeout(r, 200));
|
|
786
625
|
// }
|
|
787
|
-
//
|
|
626
|
+
//
|
|
788
627
|
// // Timeout reached - provide context about what happened
|
|
789
628
|
// response.appendResponseLine('');
|
|
790
629
|
// response.appendResponseLine(`No tools registered within ${timeout}ms.`);
|
|
@@ -799,7 +638,7 @@ export const callWebMCPTool = defineTool({
|
|
|
799
638
|
// appendDebugSteps(response);
|
|
800
639
|
// } catch (err) {
|
|
801
640
|
// const message = err instanceof Error ? err.message : String(err);
|
|
802
|
-
//
|
|
641
|
+
//
|
|
803
642
|
// if (
|
|
804
643
|
// message.includes('Content Security Policy') ||
|
|
805
644
|
// message.includes('script-src')
|
|
@@ -818,11 +657,11 @@ export const callWebMCPTool = defineTool({
|
|
|
818
657
|
// );
|
|
819
658
|
// return;
|
|
820
659
|
// }
|
|
821
|
-
//
|
|
660
|
+
//
|
|
822
661
|
// // Categorize the error for better user guidance
|
|
823
662
|
// response.appendResponseLine(`Error: ${message}`);
|
|
824
663
|
// response.appendResponseLine('');
|
|
825
|
-
//
|
|
664
|
+
//
|
|
826
665
|
// if (
|
|
827
666
|
// message.includes('Execution context was destroyed') ||
|
|
828
667
|
// message.includes('page has been closed')
|