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.
@@ -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 browser = options.browserUrl
456
+ const resolvedBrowser = options.browserUrl
454
457
  ? await ensureBrowserConnected(options.browserUrl)
455
458
  : await ensureBrowserLaunched(options);
456
- return browser;
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 extensionCards = document.querySelectorAll('extensions-item');
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 extensionCards = document.querySelectorAll('extensions-item');
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 extensionCards = document.querySelectorAll('extensions-item');
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 extensionCards = document.querySelectorAll('extensions-item');
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 extensionCards = document.querySelectorAll('extensions-item');
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 extensionCards = document.querySelectorAll('extensions-item');
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 extensionCards = document.querySelectorAll('extensions-item');
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 extensionCards = document.querySelectorAll('extensions-item');
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.2",
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",