chrome-devtools-mcp 0.18.0 → 0.18.1

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.
@@ -53,6 +53,7 @@ export class McpContext {
53
53
  // Auto-generated name counter for when no name is provided.
54
54
  #nextIsolatedContextId = 1;
55
55
  #pages = [];
56
+ #extensionServiceWorkers = [];
56
57
  #pageToDevToolsPage = new Map();
57
58
  #selectedPage;
58
59
  #textSnapshot = null;
@@ -66,6 +67,8 @@ export class McpContext {
66
67
  #dialog;
67
68
  #pageIdMap = new WeakMap();
68
69
  #nextPageId = 1;
70
+ #extensionServiceWorkerMap = new WeakMap();
71
+ #nextExtensionServiceWorkerId = 1;
69
72
  #nextSnapshotId = 1;
70
73
  #traceResults = [];
71
74
  #locatorClass;
@@ -94,6 +97,7 @@ export class McpContext {
94
97
  }
95
98
  async #init() {
96
99
  const pages = await this.createPagesSnapshot();
100
+ await this.createExtensionServiceWorkersSnapshot();
97
101
  await this.#networkCollector.init(pages);
98
102
  await this.#consoleCollector.init(pages);
99
103
  await this.#devtoolsUniverseManager.init(pages);
@@ -350,7 +354,7 @@ export class McpContext {
350
354
  throw new Error('No page selected');
351
355
  }
352
356
  if (page.isClosed()) {
353
- throw new Error(`The selected page has been closed. Call ${listPages.name} to see open pages.`);
357
+ throw new Error(`The selected page has been closed. Call ${listPages().name} to see open pages.`);
354
358
  }
355
359
  return page;
356
360
  }
@@ -426,6 +430,29 @@ export class McpContext {
426
430
  });
427
431
  }
428
432
  }
433
+ /**
434
+ * Creates a snapshot of the extension service workers.
435
+ */
436
+ async createExtensionServiceWorkersSnapshot() {
437
+ const allTargets = await this.browser.targets();
438
+ const serviceWorkers = allTargets.filter(target => {
439
+ return (target.type() === 'service_worker' &&
440
+ target.url().includes('chrome-extension://'));
441
+ });
442
+ for (const serviceWorker of serviceWorkers) {
443
+ if (!this.#extensionServiceWorkerMap.has(serviceWorker)) {
444
+ this.#extensionServiceWorkerMap.set(serviceWorker, 'sw-' + this.#nextExtensionServiceWorkerId++);
445
+ }
446
+ }
447
+ this.#extensionServiceWorkers = serviceWorkers.map(serviceWorker => {
448
+ return {
449
+ target: serviceWorker,
450
+ id: this.#extensionServiceWorkerMap.get(serviceWorker),
451
+ url: serviceWorker.url(),
452
+ };
453
+ });
454
+ return this.#extensionServiceWorkers;
455
+ }
429
456
  async createPagesSnapshot() {
430
457
  const allPages = await this.#getAllPages();
431
458
  for (const page of allPages) {
@@ -502,6 +529,12 @@ export class McpContext {
502
529
  }
503
530
  }
504
531
  }
532
+ getExtensionServiceWorkers() {
533
+ return this.#extensionServiceWorkers;
534
+ }
535
+ getExtensionServiceWorkerId(extensionServiceWorker) {
536
+ return this.#extensionServiceWorkerMap.get(extensionServiceWorker.target);
537
+ }
505
538
  getPages() {
506
539
  return this.#pages;
507
540
  }
@@ -14,6 +14,7 @@ import { getInsightOutput, getTraceSummary } from './trace-processing/parse.js';
14
14
  import { paginate } from './utils/pagination.js';
15
15
  export class McpResponse {
16
16
  #includePages = false;
17
+ #includeExtensionServiceWorkers = false;
17
18
  #snapshotParams;
18
19
  #attachedNetworkRequestId;
19
20
  #attachedNetworkRequestOptions;
@@ -27,6 +28,10 @@ export class McpResponse {
27
28
  #listExtensions;
28
29
  #devToolsData;
29
30
  #tabId;
31
+ #args;
32
+ constructor(args) {
33
+ this.#args = args;
34
+ }
30
35
  attachDevToolsData(data) {
31
36
  this.#devToolsData = data;
32
37
  }
@@ -35,6 +40,9 @@ export class McpResponse {
35
40
  }
36
41
  setIncludePages(value) {
37
42
  this.#includePages = value;
43
+ if (this.#args.categoryExtensions) {
44
+ this.#includeExtensionServiceWorkers = value;
45
+ }
38
46
  }
39
47
  includeSnapshot(params) {
40
48
  this.#snapshotParams = params ?? {
@@ -142,6 +150,9 @@ export class McpResponse {
142
150
  if (this.#includePages) {
143
151
  await context.createPagesSnapshot();
144
152
  }
153
+ if (this.#includeExtensionServiceWorkers) {
154
+ await context.createExtensionServiceWorkersSnapshot();
155
+ }
145
156
  let snapshot;
146
157
  if (this.#snapshotParams) {
147
158
  await context.createTextSnapshot(this.#snapshotParams.verbose, this.#devToolsData);
@@ -345,6 +356,22 @@ Call ${handleDialog.name} to handle it before continuing.`);
345
356
  return entry;
346
357
  });
347
358
  }
359
+ if (this.#includeExtensionServiceWorkers) {
360
+ if (!context.getExtensionServiceWorkers().length) {
361
+ response.push(`## Extension Service Workers`);
362
+ }
363
+ for (const extensionServiceWorker of context.getExtensionServiceWorkers()) {
364
+ response.push(`${extensionServiceWorker.id}: ${extensionServiceWorker.url}`);
365
+ }
366
+ structuredContent.extensionServiceWorkers = context
367
+ .getExtensionServiceWorkers()
368
+ .map(extensionServiceWorker => {
369
+ return {
370
+ id: extensionServiceWorker.id,
371
+ url: extensionServiceWorker.url,
372
+ };
373
+ });
374
+ }
348
375
  if (this.#tabId) {
349
376
  structuredContent.tabId = this.#tabId;
350
377
  }
package/build/src/main.js CHANGED
@@ -148,7 +148,9 @@ function registerTool(tool) {
148
148
  const context = await getContext();
149
149
  logger(`${tool.name} context: resolved`);
150
150
  await context.detectOpenDevToolsWindows();
151
- const response = args.slim ? new SlimMcpResponse() : new McpResponse();
151
+ const response = args.slim
152
+ ? new SlimMcpResponse(args)
153
+ : new McpResponse(args);
152
154
  await tool.handler({
153
155
  params,
154
156
  }, response, context);
@@ -16,8 +16,7 @@ export const takeMemorySnapshot = defineTool({
16
16
  schema: {
17
17
  filePath: zod
18
18
  .string()
19
- .describe('A path to a .heapsnapshot file to save the heapsnapshot to.')
20
- .endsWith('.heapsnapshot'),
19
+ .describe('A path to a .heapsnapshot file to save the heapsnapshot to.'),
21
20
  },
22
21
  handler: async (request, response, context) => {
23
22
  const page = context.getSelectedPage();
@@ -7,17 +7,19 @@ import { logger } from '../logger.js';
7
7
  import { zod } from '../third_party/index.js';
8
8
  import { ToolCategory } from './categories.js';
9
9
  import { CLOSE_PAGE_ERROR, defineTool, timeoutSchema } from './ToolDefinition.js';
10
- export const listPages = defineTool({
11
- name: 'list_pages',
12
- description: `Get a list of pages open in the browser.`,
13
- annotations: {
14
- category: ToolCategory.NAVIGATION,
15
- readOnlyHint: true,
16
- },
17
- schema: {},
18
- handler: async (_request, response) => {
19
- response.setIncludePages(true);
20
- },
10
+ export const listPages = defineTool(args => {
11
+ return {
12
+ name: 'list_pages',
13
+ description: `Get a list of pages ${args?.categoryExtensions ? 'including extension service workers' : ''} open in the browser.`,
14
+ annotations: {
15
+ category: ToolCategory.NAVIGATION,
16
+ readOnlyHint: true,
17
+ },
18
+ schema: {},
19
+ handler: async (_request, response) => {
20
+ response.setIncludePages(true);
21
+ },
22
+ };
21
23
  });
22
24
  export const selectPage = defineTool({
23
25
  name: 'select_page',
@@ -29,7 +31,7 @@ export const selectPage = defineTool({
29
31
  schema: {
30
32
  pageId: zod
31
33
  .number()
32
- .describe(`The ID of the page to select. Call ${listPages.name} to get available pages.`),
34
+ .describe(`The ID of the page to select. Call ${listPages().name} to get available pages.`),
33
35
  bringToFront: zod
34
36
  .boolean()
35
37
  .optional()
@@ -318,7 +320,7 @@ export const getTabId = defineTool({
318
320
  schema: {
319
321
  pageId: zod
320
322
  .number()
321
- .describe(`The ID of the page to get the tab ID for. Call ${listPages.name} to get available pages.`),
323
+ .describe(`The ID of the page to get the tab ID for. Call ${listPages().name} to get available pages.`),
322
324
  },
323
325
  handler: async (request, response, context) => {
324
326
  const page = context.getPageById(request.params.pageId);
@@ -36,7 +36,6 @@ export const createTools = (args) => {
36
36
  const tools = [];
37
37
  for (const tool of rawTools) {
38
38
  if (typeof tool === 'function') {
39
- // @ts-expect-error none of the tools for now implement the function type tool has type "never"
40
39
  tools.push(tool(args));
41
40
  }
42
41
  else {
@@ -5,5 +5,5 @@
5
5
  */
6
6
  // If moved update release-please config
7
7
  // x-release-please-start-version
8
- export const VERSION = '0.18.0';
8
+ export const VERSION = '0.18.1';
9
9
  // x-release-please-end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chrome-devtools-mcp",
3
- "version": "0.18.0",
3
+ "version": "0.18.1",
4
4
  "description": "MCP server for Chrome DevTools",
5
5
  "type": "module",
6
6
  "bin": "./build/src/index.js",