chrome-devtools-mcp-for-extension 0.8.2 → 0.8.4
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/browser.js +5 -2
- package/build/src/main.js +12 -0
- package/build/src/tools/extensions.js +88 -8
- package/package.json +1 -1
package/build/src/browser.js
CHANGED
|
@@ -443,16 +443,19 @@ export async function launch(options) {
|
|
|
443
443
|
}
|
|
444
444
|
}
|
|
445
445
|
async function ensureBrowserLaunched(options) {
|
|
446
|
+
console.error(`[ensureBrowserLaunched] browser exists: ${!!browser}, connected: ${browser?.connected}`);
|
|
446
447
|
if (browser?.connected) {
|
|
448
|
+
console.error(`[ensureBrowserLaunched] Reusing existing browser`);
|
|
447
449
|
return browser;
|
|
448
450
|
}
|
|
451
|
+
console.error(`[ensureBrowserLaunched] Launching new browser`);
|
|
449
452
|
browser = await launch(options);
|
|
450
453
|
return browser;
|
|
451
454
|
}
|
|
452
455
|
export async function resolveBrowser(options) {
|
|
453
|
-
const
|
|
456
|
+
const resolvedBrowser = options.browserUrl
|
|
454
457
|
? await ensureBrowserConnected(options.browserUrl)
|
|
455
458
|
: await ensureBrowserLaunched(options);
|
|
456
|
-
return
|
|
459
|
+
return resolvedBrowser;
|
|
457
460
|
}
|
|
458
461
|
export { scanExtensionsDirectory, discoverSystemExtensions, getChromeExtensionsDirectory, validateExtensionManifest };
|
package/build/src/main.js
CHANGED
|
@@ -102,6 +102,18 @@ function registerTool(tool) {
|
|
|
102
102
|
}
|
|
103
103
|
catch (error) {
|
|
104
104
|
const errorText = error instanceof Error ? error.message : String(error);
|
|
105
|
+
// Detect browser closed error and provide helpful message
|
|
106
|
+
if (errorText.includes('Target closed') || errorText.includes('Session closed')) {
|
|
107
|
+
return {
|
|
108
|
+
content: [
|
|
109
|
+
{
|
|
110
|
+
type: 'text',
|
|
111
|
+
text: `Browser connection lost. The Chrome instance was closed or disconnected.\n\nPlease restart the MCP server to reconnect.`,
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
isError: true,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
105
117
|
return {
|
|
106
118
|
content: [
|
|
107
119
|
{
|
|
@@ -23,7 +23,13 @@ export const listExtensions = defineTool({
|
|
|
23
23
|
await context.waitForEventsAfterAction(async () => {
|
|
24
24
|
await page.goto('chrome://extensions/', { waitUntil: 'networkidle0' });
|
|
25
25
|
const extensions = await page.evaluate(() => {
|
|
26
|
-
const
|
|
26
|
+
const manager = document.querySelector('extensions-manager');
|
|
27
|
+
if (!manager?.shadowRoot)
|
|
28
|
+
return null;
|
|
29
|
+
const itemList = manager.shadowRoot.querySelector('extensions-item-list');
|
|
30
|
+
if (!itemList?.shadowRoot)
|
|
31
|
+
return null;
|
|
32
|
+
const extensionCards = itemList.shadowRoot.querySelectorAll('extensions-item');
|
|
27
33
|
const results = [];
|
|
28
34
|
Array.from(extensionCards).forEach(card => {
|
|
29
35
|
const shadowRoot = card.shadowRoot;
|
|
@@ -50,6 +56,10 @@ export const listExtensions = defineTool({
|
|
|
50
56
|
});
|
|
51
57
|
return results;
|
|
52
58
|
});
|
|
59
|
+
if (!extensions) {
|
|
60
|
+
response.appendResponseLine('❌ Failed to query extensions page');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
53
63
|
response.appendResponseLine('Installed Chrome Extensions:');
|
|
54
64
|
response.appendResponseLine('');
|
|
55
65
|
if (extensions.length === 0) {
|
|
@@ -88,7 +98,13 @@ export const getExtensionInfo = defineTool({
|
|
|
88
98
|
await context.waitForEventsAfterAction(async () => {
|
|
89
99
|
await page.goto('chrome://extensions/', { waitUntil: 'networkidle0' });
|
|
90
100
|
const extensionInfo = await page.evaluate((searchName) => {
|
|
91
|
-
const
|
|
101
|
+
const manager = document.querySelector('extensions-manager');
|
|
102
|
+
if (!manager?.shadowRoot)
|
|
103
|
+
return null;
|
|
104
|
+
const itemList = manager.shadowRoot.querySelector('extensions-item-list');
|
|
105
|
+
if (!itemList?.shadowRoot)
|
|
106
|
+
return null;
|
|
107
|
+
const extensionCards = itemList.shadowRoot.querySelectorAll('extensions-item');
|
|
92
108
|
for (const card of Array.from(extensionCards)) {
|
|
93
109
|
const shadowRoot = card.shadowRoot;
|
|
94
110
|
if (shadowRoot) {
|
|
@@ -139,6 +155,10 @@ export const getExtensionInfo = defineTool({
|
|
|
139
155
|
}
|
|
140
156
|
return { found: false };
|
|
141
157
|
}, extensionName);
|
|
158
|
+
if (!extensionInfo) {
|
|
159
|
+
response.appendResponseLine('❌ Failed to query extensions page');
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
142
162
|
if (extensionInfo.found) {
|
|
143
163
|
response.appendResponseLine(`## Extension: ${extensionInfo.name}`);
|
|
144
164
|
response.appendResponseLine('');
|
|
@@ -194,7 +214,13 @@ export const reloadExtension = defineTool({
|
|
|
194
214
|
await page.goto('chrome://extensions/', { waitUntil: 'networkidle0' });
|
|
195
215
|
// Get extension info before reload
|
|
196
216
|
const beforeState = await page.evaluate((searchName) => {
|
|
197
|
-
const
|
|
217
|
+
const manager = document.querySelector('extensions-manager');
|
|
218
|
+
if (!manager?.shadowRoot)
|
|
219
|
+
return null;
|
|
220
|
+
const itemList = manager.shadowRoot.querySelector('extensions-item-list');
|
|
221
|
+
if (!itemList?.shadowRoot)
|
|
222
|
+
return null;
|
|
223
|
+
const extensionCards = itemList.shadowRoot.querySelectorAll('extensions-item');
|
|
198
224
|
for (const card of Array.from(extensionCards)) {
|
|
199
225
|
const shadowRoot = card.shadowRoot;
|
|
200
226
|
if (shadowRoot) {
|
|
@@ -216,6 +242,10 @@ export const reloadExtension = defineTool({
|
|
|
216
242
|
}
|
|
217
243
|
return { found: false };
|
|
218
244
|
}, extensionName);
|
|
245
|
+
if (!beforeState) {
|
|
246
|
+
response.appendResponseLine('❌ Failed to query extensions page');
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
219
249
|
if (!beforeState.found) {
|
|
220
250
|
response.appendResponseLine(`❌ Extension not found: "${extensionName}"`);
|
|
221
251
|
return;
|
|
@@ -226,7 +256,13 @@ export const reloadExtension = defineTool({
|
|
|
226
256
|
response.appendResponseLine(`🔄 Reloading: ${beforeState.name} v${beforeState.version}`);
|
|
227
257
|
// Perform reload
|
|
228
258
|
const reloadResult = await page.evaluate((searchName) => {
|
|
229
|
-
const
|
|
259
|
+
const manager = document.querySelector('extensions-manager');
|
|
260
|
+
if (!manager?.shadowRoot)
|
|
261
|
+
return null;
|
|
262
|
+
const itemList = manager.shadowRoot.querySelector('extensions-item-list');
|
|
263
|
+
if (!itemList?.shadowRoot)
|
|
264
|
+
return null;
|
|
265
|
+
const extensionCards = itemList.shadowRoot.querySelectorAll('extensions-item');
|
|
230
266
|
for (const card of Array.from(extensionCards)) {
|
|
231
267
|
const shadowRoot = card.shadowRoot;
|
|
232
268
|
if (shadowRoot) {
|
|
@@ -248,6 +284,10 @@ export const reloadExtension = defineTool({
|
|
|
248
284
|
}
|
|
249
285
|
return { success: false, reason: 'Extension not found' };
|
|
250
286
|
}, extensionName);
|
|
287
|
+
if (!reloadResult) {
|
|
288
|
+
response.appendResponseLine('❌ Failed to execute reload');
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
251
291
|
if (!reloadResult.success) {
|
|
252
292
|
response.appendResponseLine(`❌ Failed: ${reloadResult.reason}`);
|
|
253
293
|
return;
|
|
@@ -256,7 +296,13 @@ export const reloadExtension = defineTool({
|
|
|
256
296
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
257
297
|
// Check for errors after reload
|
|
258
298
|
const afterState = await page.evaluate((searchName) => {
|
|
259
|
-
const
|
|
299
|
+
const manager = document.querySelector('extensions-manager');
|
|
300
|
+
if (!manager?.shadowRoot)
|
|
301
|
+
return null;
|
|
302
|
+
const itemList = manager.shadowRoot.querySelector('extensions-item-list');
|
|
303
|
+
if (!itemList?.shadowRoot)
|
|
304
|
+
return null;
|
|
305
|
+
const extensionCards = itemList.shadowRoot.querySelectorAll('extensions-item');
|
|
260
306
|
for (const card of Array.from(extensionCards)) {
|
|
261
307
|
const shadowRoot = card.shadowRoot;
|
|
262
308
|
if (shadowRoot) {
|
|
@@ -277,6 +323,10 @@ export const reloadExtension = defineTool({
|
|
|
277
323
|
}
|
|
278
324
|
return { found: false, hasErrors: false };
|
|
279
325
|
}, extensionName);
|
|
326
|
+
if (!afterState) {
|
|
327
|
+
response.appendResponseLine('⚠️ Warning: Could not verify reload status');
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
280
330
|
response.appendResponseLine('');
|
|
281
331
|
if (afterState.hasErrors) {
|
|
282
332
|
response.appendResponseLine(`⚠️ Extension reloaded but has errors (v${afterState.version})`);
|
|
@@ -310,7 +360,13 @@ export const toggleExtensionState = defineTool({
|
|
|
310
360
|
await context.waitForEventsAfterAction(async () => {
|
|
311
361
|
await page.goto('chrome://extensions/', { waitUntil: 'networkidle0' });
|
|
312
362
|
const result = await page.evaluate((searchName, targetEnabled) => {
|
|
313
|
-
const
|
|
363
|
+
const manager = document.querySelector('extensions-manager');
|
|
364
|
+
if (!manager?.shadowRoot)
|
|
365
|
+
return null;
|
|
366
|
+
const itemList = manager.shadowRoot.querySelector('extensions-item-list');
|
|
367
|
+
if (!itemList?.shadowRoot)
|
|
368
|
+
return null;
|
|
369
|
+
const extensionCards = itemList.shadowRoot.querySelectorAll('extensions-item');
|
|
314
370
|
for (const card of Array.from(extensionCards)) {
|
|
315
371
|
const shadowRoot = card.shadowRoot;
|
|
316
372
|
if (shadowRoot) {
|
|
@@ -349,6 +405,10 @@ export const toggleExtensionState = defineTool({
|
|
|
349
405
|
}
|
|
350
406
|
return { success: false, reason: 'Extension not found' };
|
|
351
407
|
}, extensionName, desiredEnabled);
|
|
408
|
+
if (!result) {
|
|
409
|
+
response.appendResponseLine('❌ Failed to query extensions page');
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
352
412
|
if (!result.success) {
|
|
353
413
|
response.appendResponseLine(`❌ Failed: ${result.reason}`);
|
|
354
414
|
return;
|
|
@@ -381,7 +441,13 @@ export const openExtensionPopup = defineTool({
|
|
|
381
441
|
// First, get extension ID
|
|
382
442
|
await page.goto('chrome://extensions/', { waitUntil: 'networkidle0' });
|
|
383
443
|
const extensionInfo = await page.evaluate((searchName) => {
|
|
384
|
-
const
|
|
444
|
+
const manager = document.querySelector('extensions-manager');
|
|
445
|
+
if (!manager?.shadowRoot)
|
|
446
|
+
return null;
|
|
447
|
+
const itemList = manager.shadowRoot.querySelector('extensions-item-list');
|
|
448
|
+
if (!itemList?.shadowRoot)
|
|
449
|
+
return null;
|
|
450
|
+
const extensionCards = itemList.shadowRoot.querySelectorAll('extensions-item');
|
|
385
451
|
for (const card of Array.from(extensionCards)) {
|
|
386
452
|
const shadowRoot = card.shadowRoot;
|
|
387
453
|
if (shadowRoot) {
|
|
@@ -394,6 +460,10 @@ export const openExtensionPopup = defineTool({
|
|
|
394
460
|
}
|
|
395
461
|
return { found: false };
|
|
396
462
|
}, extensionName);
|
|
463
|
+
if (!extensionInfo) {
|
|
464
|
+
response.appendResponseLine('❌ Failed to query extensions page');
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
397
467
|
if (!extensionInfo.found) {
|
|
398
468
|
response.appendResponseLine(`❌ Extension not found: "${extensionName}"`);
|
|
399
469
|
return;
|
|
@@ -498,7 +568,13 @@ export const inspectServiceWorker = defineTool({
|
|
|
498
568
|
await context.waitForEventsAfterAction(async () => {
|
|
499
569
|
await page.goto('chrome://extensions/', { waitUntil: 'networkidle0' });
|
|
500
570
|
const inspectResult = await page.evaluate((searchName) => {
|
|
501
|
-
const
|
|
571
|
+
const manager = document.querySelector('extensions-manager');
|
|
572
|
+
if (!manager?.shadowRoot)
|
|
573
|
+
return null;
|
|
574
|
+
const itemList = manager.shadowRoot.querySelector('extensions-item-list');
|
|
575
|
+
if (!itemList?.shadowRoot)
|
|
576
|
+
return null;
|
|
577
|
+
const extensionCards = itemList.shadowRoot.querySelectorAll('extensions-item');
|
|
502
578
|
for (const card of Array.from(extensionCards)) {
|
|
503
579
|
const shadowRoot = card.shadowRoot;
|
|
504
580
|
if (shadowRoot) {
|
|
@@ -533,6 +609,10 @@ export const inspectServiceWorker = defineTool({
|
|
|
533
609
|
}
|
|
534
610
|
return { success: false, reason: 'Extension not found' };
|
|
535
611
|
}, extensionName);
|
|
612
|
+
if (!inspectResult) {
|
|
613
|
+
response.appendResponseLine('❌ Failed to find extension');
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
536
616
|
if (inspectResult.success) {
|
|
537
617
|
response.appendResponseLine(`✅ Opened DevTools for ${inspectResult.type} of: ${inspectResult.extensionName}`);
|
|
538
618
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chrome-devtools-mcp-for-extension",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4",
|
|
4
4
|
"description": "MCP server for Chrome extension development with Web Store automation. Fork of chrome-devtools-mcp with extension-specific tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": "./build/src/index.js",
|