chrome-devtools-mcp 0.9.0 → 0.10.0

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.
Files changed (66) hide show
  1. package/README.md +14 -9
  2. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Console.js +1 -8
  3. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ParsedURL.js +10 -20
  4. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/SegmentedRange.js +1 -2
  5. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Settings.js +3 -0
  6. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/StringOutputStream.js +1 -4
  7. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/AidaClient.js +19 -0
  8. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/DispatchHttpRequestClient.js +54 -0
  9. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/GdpClient.js +6 -51
  10. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHost.js +2 -2
  11. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHostAPI.js +32 -29
  12. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/UserMetrics.js +14 -6
  13. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/host.js +2 -1
  14. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/CDPConnection.js +17 -0
  15. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/InspectorBackend.js +68 -188
  16. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/protocol_client.js +2 -1
  17. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/AnimationModel.js +1 -2
  18. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMatchedStyles.js +3 -3
  19. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSModel.js +1 -1
  20. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSProperty.js +3 -6
  21. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParserMatchers.js +14 -10
  22. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSStyleDeclaration.js +4 -4
  23. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ChildTargetManager.js +5 -33
  24. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Connections.js +9 -46
  25. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DOMModel.js +1 -0
  26. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DebuggerModel.js +1 -2
  27. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/EnhancedTracesParser.js +17 -3
  28. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.js +59 -37
  29. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkRequest.js +5 -0
  30. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RehydratingConnection.js +102 -4
  31. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMap.js +2 -3
  32. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/sdk-meta.js +8 -1
  33. package/build/node_modules/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +1 -39
  34. package/build/node_modules/chrome-devtools-frontend/front_end/generated/SupportedCSSProperties.js +58 -0
  35. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js +46 -45
  36. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIContext.js +10 -25
  37. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CompilerScriptMapping.js +1 -1
  38. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/DebuggerWorkspaceBinding.js +1 -1
  39. package/build/node_modules/chrome-devtools-frontend/front_end/models/cpu_profile/ProfileTreeModel.js +6 -7
  40. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceModel.js +1 -1
  41. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/NetworkRequestsHandler.js +12 -3
  42. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/types/TraceEvents.js +3 -0
  43. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace_source_maps_resolver/SourceMapsResolver.js +1 -1
  44. package/build/node_modules/chrome-devtools-frontend/mcp/mcp.js +14 -0
  45. package/build/src/DevToolsConnectionAdapter.js +1 -0
  46. package/build/src/DevtoolsUtils.js +44 -0
  47. package/build/src/McpContext.js +117 -7
  48. package/build/src/McpResponse.js +32 -21
  49. package/build/src/PageCollector.js +21 -9
  50. package/build/src/browser.js +8 -8
  51. package/build/src/cli.js +8 -3
  52. package/build/src/formatters/networkFormatter.js +2 -2
  53. package/build/src/formatters/snapshotFormatter.js +18 -6
  54. package/build/src/main.js +7 -2
  55. package/build/src/third_party/THIRD_PARTY_NOTICES +72 -52
  56. package/build/src/third_party/index.js +12684 -6052
  57. package/build/src/tools/emulation.js +37 -44
  58. package/build/src/tools/input.js +36 -6
  59. package/build/src/tools/network.js +27 -5
  60. package/build/src/tools/pages.js +59 -33
  61. package/build/src/tools/performance.js +5 -2
  62. package/build/src/tools/screenshot.js +2 -1
  63. package/build/src/tools/snapshot.js +13 -4
  64. package/build/src/trace-processing/parse.js +6 -16
  65. package/build/src/utils/keyboard.js +291 -0
  66. package/package.json +7 -6
@@ -6,6 +6,7 @@
6
6
  import fs from 'node:fs/promises';
7
7
  import os from 'node:os';
8
8
  import path from 'node:path';
9
+ import { extractUrlLikeFromDevToolsTitle, urlsEqual } from './DevtoolsUtils.js';
9
10
  import { NetworkCollector, PageCollector } from './PageCollector.js';
10
11
  import { Locator } from './third_party/index.js';
11
12
  import { listPages } from './tools/pages.js';
@@ -44,6 +45,7 @@ export class McpContext {
44
45
  logger;
45
46
  // The most recent page state.
46
47
  #pages = [];
48
+ #pageToDevToolsPage = new Map();
47
49
  #selectedPageIdx = 0;
48
50
  // The most recent snapshot.
49
51
  #textSnapshot = null;
@@ -56,11 +58,13 @@ export class McpContext {
56
58
  #nextSnapshotId = 1;
57
59
  #traceResults = [];
58
60
  #locatorClass;
59
- constructor(browser, logger, locatorClass) {
61
+ #options;
62
+ constructor(browser, logger, options, locatorClass) {
60
63
  this.browser = browser;
61
64
  this.logger = logger;
62
65
  this.#locatorClass = locatorClass;
63
- this.#networkCollector = new NetworkCollector(this.browser);
66
+ this.#options = options;
67
+ this.#networkCollector = new NetworkCollector(this.browser, undefined, this.#options.experimentalIncludeAllPages);
64
68
  this.#consoleCollector = new PageCollector(this.browser, collect => {
65
69
  return {
66
70
  console: event => {
@@ -77,7 +81,7 @@ export class McpContext {
77
81
  }
78
82
  },
79
83
  };
80
- });
84
+ }, this.#options.experimentalIncludeAllPages);
81
85
  }
82
86
  async #init() {
83
87
  await this.createPagesSnapshot();
@@ -85,13 +89,47 @@ export class McpContext {
85
89
  await this.#networkCollector.init();
86
90
  await this.#consoleCollector.init();
87
91
  }
88
- static async from(browser, logger,
92
+ static async from(browser, logger, opts,
89
93
  /* Let tests use unbundled Locator class to avoid overly strict checks within puppeteer that fail when mixing bundled and unbundled class instances */
90
94
  locatorClass = Locator) {
91
- const context = new McpContext(browser, logger, locatorClass);
95
+ const context = new McpContext(browser, logger, opts, locatorClass);
92
96
  await context.#init();
93
97
  return context;
94
98
  }
99
+ resolveCdpRequestId(cdpRequestId) {
100
+ const selectedPage = this.getSelectedPage();
101
+ if (!cdpRequestId) {
102
+ this.logger('no network request');
103
+ return;
104
+ }
105
+ const request = this.#networkCollector.find(selectedPage, request => {
106
+ // @ts-expect-error id is internal.
107
+ return request.id === cdpRequestId;
108
+ });
109
+ if (!request) {
110
+ this.logger('no network request for ' + cdpRequestId);
111
+ return;
112
+ }
113
+ return this.#networkCollector.getIdForResource(request);
114
+ }
115
+ resolveCdpElementId(cdpBackendNodeId) {
116
+ if (!cdpBackendNodeId) {
117
+ this.logger('no cdpBackendNodeId');
118
+ return;
119
+ }
120
+ // TODO: index by backendNodeId instead.
121
+ const queue = [this.#textSnapshot?.root];
122
+ while (queue.length) {
123
+ const current = queue.pop();
124
+ if (current.backendNodeId === cdpBackendNodeId) {
125
+ return current.id;
126
+ }
127
+ for (const child of current.children) {
128
+ queue.push(child);
129
+ }
130
+ }
131
+ return;
132
+ }
95
133
  getNetworkRequests(includePreservedRequests) {
96
134
  const page = this.getSelectedPage();
97
135
  return this.#networkCollector.getData(page, includePreservedRequests);
@@ -233,16 +271,84 @@ export class McpContext {
233
271
  * Creates a snapshot of the pages.
234
272
  */
235
273
  async createPagesSnapshot() {
236
- this.#pages = await this.browser.pages();
274
+ const allPages = await this.browser.pages(this.#options.experimentalIncludeAllPages);
275
+ this.#pages = allPages.filter(page => {
276
+ // If we allow debugging DevTools windows, return all pages.
277
+ // If we are in regular mode, the user should only see non-DevTools page.
278
+ return (this.#options.experimentalDevToolsDebugging ||
279
+ !page.url().startsWith('devtools://'));
280
+ });
281
+ await this.detectOpenDevToolsWindows();
237
282
  return this.#pages;
238
283
  }
284
+ async detectOpenDevToolsWindows() {
285
+ this.logger('Detecting open DevTools windows');
286
+ const pages = await this.browser.pages(this.#options.experimentalIncludeAllPages);
287
+ this.#pageToDevToolsPage = new Map();
288
+ for (const devToolsPage of pages) {
289
+ if (devToolsPage.url().startsWith('devtools://')) {
290
+ try {
291
+ this.logger('Calling getTargetInfo for ' + devToolsPage.url());
292
+ const data = await devToolsPage
293
+ // @ts-expect-error no types for _client().
294
+ ._client()
295
+ .send('Target.getTargetInfo');
296
+ const devtoolsPageTitle = data.targetInfo.title;
297
+ const urlLike = extractUrlLikeFromDevToolsTitle(devtoolsPageTitle);
298
+ if (!urlLike) {
299
+ continue;
300
+ }
301
+ // TODO: lookup without a loop.
302
+ for (const page of this.#pages) {
303
+ if (urlsEqual(page.url(), urlLike)) {
304
+ this.#pageToDevToolsPage.set(page, devToolsPage);
305
+ }
306
+ }
307
+ }
308
+ catch (error) {
309
+ this.logger('Issue occurred while trying to find DevTools', error);
310
+ }
311
+ }
312
+ }
313
+ }
239
314
  getPages() {
240
315
  return this.#pages;
241
316
  }
317
+ getDevToolsPage(page) {
318
+ return this.#pageToDevToolsPage.get(page);
319
+ }
320
+ async getDevToolsData() {
321
+ try {
322
+ this.logger('Getting DevTools UI data');
323
+ const selectedPage = this.getSelectedPage();
324
+ const devtoolsPage = this.getDevToolsPage(selectedPage);
325
+ if (!devtoolsPage) {
326
+ this.logger('No DevTools page detected');
327
+ return {};
328
+ }
329
+ const { cdpRequestId, cdpBackendNodeId } = await devtoolsPage.evaluate(async () => {
330
+ // @ts-expect-error no types
331
+ const UI = await import('/bundled/ui/legacy/legacy.js');
332
+ // @ts-expect-error no types
333
+ const SDK = await import('/bundled/core/sdk/sdk.js');
334
+ const request = UI.Context.Context.instance().flavor(SDK.NetworkRequest.NetworkRequest);
335
+ const node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
336
+ return {
337
+ cdpRequestId: request?.requestId(),
338
+ cdpBackendNodeId: node?.backendNodeId(),
339
+ };
340
+ });
341
+ return { cdpBackendNodeId, cdpRequestId };
342
+ }
343
+ catch (err) {
344
+ this.logger('error getting devtools data', err);
345
+ }
346
+ return {};
347
+ }
242
348
  /**
243
349
  * Creates a text snapshot of a page.
244
350
  */
245
- async createTextSnapshot(verbose = false) {
351
+ async createTextSnapshot(verbose = false, devtoolsData = undefined) {
246
352
  const page = this.getSelectedPage();
247
353
  const rootNode = await page.accessibility.snapshot({
248
354
  includeIframes: true,
@@ -281,6 +387,10 @@ export class McpContext {
281
387
  snapshotId: String(snapshotId),
282
388
  idToNode,
283
389
  };
390
+ const data = devtoolsData ?? (await this.getDevToolsData());
391
+ if (data?.cdpBackendNodeId) {
392
+ this.#textSnapshot.selectedElementUid = this.resolveCdpElementId(data?.cdpBackendNodeId);
393
+ }
284
394
  }
285
395
  getTextSnapshot() {
286
396
  return this.#textSnapshot;
@@ -1,24 +1,28 @@
1
1
  import { formatConsoleEventShort, formatConsoleEventVerbose, } from './formatters/consoleFormatter.js';
2
2
  import { getFormattedHeaderValue, getFormattedResponseBody, getFormattedRequestBody, getShortDescriptionForRequest, getStatusFromRequest, } from './formatters/networkFormatter.js';
3
- import { formatA11ySnapshot } from './formatters/snapshotFormatter.js';
3
+ import { formatSnapshotNode } from './formatters/snapshotFormatter.js';
4
4
  import { handleDialog } from './tools/pages.js';
5
5
  import { paginate } from './utils/pagination.js';
6
6
  export class McpResponse {
7
7
  #includePages = false;
8
- #includeSnapshot = false;
9
- #includeVerboseSnapshot = false;
8
+ #snapshotParams;
10
9
  #attachedNetworkRequestId;
11
10
  #attachedConsoleMessageId;
12
11
  #textResponseLines = [];
13
12
  #images = [];
14
13
  #networkRequestsOptions;
15
14
  #consoleDataOptions;
15
+ #devToolsData;
16
+ attachDevToolsData(data) {
17
+ this.#devToolsData = data;
18
+ }
16
19
  setIncludePages(value) {
17
20
  this.#includePages = value;
18
21
  }
19
- setIncludeSnapshot(value, verbose = false) {
20
- this.#includeSnapshot = value;
21
- this.#includeVerboseSnapshot = verbose;
22
+ includeSnapshot(params) {
23
+ this.#snapshotParams = params ?? {
24
+ verbose: false,
25
+ };
22
26
  }
23
27
  setIncludeNetworkRequests(value, options) {
24
28
  if (!value) {
@@ -35,6 +39,7 @@ export class McpResponse {
35
39
  : undefined,
36
40
  resourceTypes: options?.resourceTypes,
37
41
  includePreservedRequests: options?.includePreservedRequests,
42
+ networkRequestIdInDevToolsUI: options?.networkRequestIdInDevToolsUI,
38
43
  };
39
44
  }
40
45
  setIncludeConsoleData(value, options) {
@@ -93,18 +98,26 @@ export class McpResponse {
93
98
  get images() {
94
99
  return this.#images;
95
100
  }
96
- get includeSnapshot() {
97
- return this.#includeSnapshot;
98
- }
99
- get includeVersboseSnapshot() {
100
- return this.#includeVerboseSnapshot;
101
+ get snapshotParams() {
102
+ return this.#snapshotParams;
101
103
  }
102
104
  async handle(toolName, context) {
103
105
  if (this.#includePages) {
104
106
  await context.createPagesSnapshot();
105
107
  }
106
- if (this.#includeSnapshot) {
107
- await context.createTextSnapshot(this.#includeVerboseSnapshot);
108
+ let formattedSnapshot;
109
+ if (this.#snapshotParams) {
110
+ await context.createTextSnapshot(this.#snapshotParams.verbose, this.#devToolsData);
111
+ const snapshot = context.getTextSnapshot();
112
+ if (snapshot) {
113
+ if (this.#snapshotParams.filePath) {
114
+ await context.saveFile(new TextEncoder().encode(formatSnapshotNode(snapshot.root, snapshot)), this.#snapshotParams.filePath);
115
+ formattedSnapshot = `Saved snapshot to ${this.#snapshotParams.filePath}.`;
116
+ }
117
+ else {
118
+ formattedSnapshot = formatSnapshotNode(snapshot.root, snapshot);
119
+ }
120
+ }
108
121
  }
109
122
  const bodies = {};
110
123
  if (this.#attachedNetworkRequestId) {
@@ -186,6 +199,7 @@ export class McpResponse {
186
199
  bodies,
187
200
  consoleData,
188
201
  consoleListData,
202
+ formattedSnapshot,
189
203
  });
190
204
  }
191
205
  format(toolName, context, data) {
@@ -222,13 +236,9 @@ Call ${handleDialog.name} to handle it before continuing.`);
222
236
  }
223
237
  response.push(...parts);
224
238
  }
225
- if (this.#includeSnapshot) {
226
- const snapshot = context.getTextSnapshot();
227
- if (snapshot) {
228
- const formattedSnapshot = formatA11ySnapshot(snapshot.root);
229
- response.push('## Page content');
230
- response.push(formattedSnapshot);
231
- }
239
+ if (data.formattedSnapshot) {
240
+ response.push('## Page content');
241
+ response.push(data.formattedSnapshot);
232
242
  }
233
243
  response.push(...this.#formatNetworkRequestData(context, data.bodies));
234
244
  response.push(...this.#formatConsoleData(data.consoleData));
@@ -247,7 +257,8 @@ Call ${handleDialog.name} to handle it before continuing.`);
247
257
  const data = this.#dataWithPagination(requests, this.#networkRequestsOptions.pagination);
248
258
  response.push(...data.info);
249
259
  for (const request of data.items) {
250
- response.push(getShortDescriptionForRequest(request, context.getNetworkRequestStableId(request)));
260
+ response.push(getShortDescriptionForRequest(request, context.getNetworkRequestStableId(request), context.getNetworkRequestStableId(request) ===
261
+ this.#networkRequestsOptions?.networkRequestIdInDevToolsUI));
251
262
  }
252
263
  }
253
264
  else {
@@ -18,18 +18,20 @@ export class PageCollector {
18
18
  #listenersInitializer;
19
19
  #listeners = new WeakMap();
20
20
  #maxNavigationSaved = 3;
21
+ #includeAllPages;
21
22
  /**
22
23
  * This maps a Page to a list of navigations with a sub-list
23
24
  * of all collected resources.
24
25
  * The newer navigations come first.
25
26
  */
26
27
  storage = new WeakMap();
27
- constructor(browser, listeners) {
28
+ constructor(browser, listeners, includeAllPages) {
28
29
  this.#browser = browser;
29
30
  this.#listenersInitializer = listeners;
31
+ this.#includeAllPages = includeAllPages;
30
32
  }
31
33
  async init() {
32
- const pages = await this.#browser.pages();
34
+ const pages = await this.#browser.pages(this.#includeAllPages);
33
35
  for (const page of pages) {
34
36
  this.#initializePage(page);
35
37
  }
@@ -118,14 +120,24 @@ export class PageCollector {
118
120
  if (!navigations) {
119
121
  throw new Error('No requests found for selected page');
120
122
  }
123
+ const item = this.find(page, item => item[stableIdSymbol] === stableId);
124
+ if (item) {
125
+ return item;
126
+ }
127
+ throw new Error('Request not found for selected page');
128
+ }
129
+ find(page, filter) {
130
+ const navigations = this.storage.get(page);
131
+ if (!navigations) {
132
+ return;
133
+ }
121
134
  for (const navigation of navigations) {
122
- for (const collected of navigation) {
123
- if (collected[stableIdSymbol] === stableId) {
124
- return collected;
125
- }
135
+ const item = navigation.find(filter);
136
+ if (item) {
137
+ return item;
126
138
  }
127
139
  }
128
- throw new Error('Request not found for selected page');
140
+ return;
129
141
  }
130
142
  }
131
143
  export class NetworkCollector extends PageCollector {
@@ -135,8 +147,8 @@ export class NetworkCollector extends PageCollector {
135
147
  collect(req);
136
148
  },
137
149
  };
138
- }) {
139
- super(browser, listeners);
150
+ }, includeAllPages) {
151
+ super(browser, listeners, includeAllPages);
140
152
  }
141
153
  splitAfterNavigation(page) {
142
154
  const navigations = this.storage.get(page) ?? [];
@@ -6,17 +6,15 @@
6
6
  import fs from 'node:fs';
7
7
  import os from 'node:os';
8
8
  import path from 'node:path';
9
+ import { logger } from './logger.js';
9
10
  import { puppeteer } from './third_party/index.js';
10
11
  let browser;
11
- function makeTargetFilter(devtools) {
12
+ function makeTargetFilter() {
12
13
  const ignoredPrefixes = new Set([
13
14
  'chrome://',
14
15
  'chrome-extension://',
15
16
  'chrome-untrusted://',
16
17
  ]);
17
- if (!devtools) {
18
- ignoredPrefixes.add('devtools://');
19
- }
20
18
  return function targetFilter(target) {
21
19
  if (target.url() === 'chrome://newtab/') {
22
20
  return true;
@@ -34,9 +32,9 @@ export async function ensureBrowserConnected(options) {
34
32
  return browser;
35
33
  }
36
34
  const connectOptions = {
37
- targetFilter: makeTargetFilter(options.devtools),
35
+ targetFilter: makeTargetFilter(),
38
36
  defaultViewport: null,
39
- handleDevToolsAsPage: options.devtools,
37
+ handleDevToolsAsPage: true,
40
38
  };
41
39
  if (options.wsEndpoint) {
42
40
  connectOptions.browserWSEndpoint = options.wsEndpoint;
@@ -50,7 +48,9 @@ export async function ensureBrowserConnected(options) {
50
48
  else {
51
49
  throw new Error('Either browserURL or wsEndpoint must be provided');
52
50
  }
51
+ logger('Connecting Puppeteer to ', JSON.stringify(connectOptions));
53
52
  browser = await puppeteer.connect(connectOptions);
53
+ logger('Connected Puppeteer');
54
54
  return browser;
55
55
  }
56
56
  export async function launch(options) {
@@ -85,7 +85,7 @@ export async function launch(options) {
85
85
  try {
86
86
  const browser = await puppeteer.launch({
87
87
  channel: puppeteerChannel,
88
- targetFilter: makeTargetFilter(options.devtools),
88
+ targetFilter: makeTargetFilter(),
89
89
  executablePath,
90
90
  defaultViewport: null,
91
91
  userDataDir,
@@ -93,7 +93,7 @@ export async function launch(options) {
93
93
  headless,
94
94
  args,
95
95
  acceptInsecureCerts: options.acceptInsecureCerts,
96
- handleDevToolsAsPage: options.devtools,
96
+ handleDevToolsAsPage: true,
97
97
  });
98
98
  if (options.logFile) {
99
99
  // FIXME: we are probably subscribing too late to catch startup logs. We
package/build/src/cli.js CHANGED
@@ -123,6 +123,11 @@ export const cliOptions = {
123
123
  describe: 'Whether to enable automation over DevTools targets',
124
124
  hidden: true,
125
125
  },
126
+ experimentalIncludeAllPages: {
127
+ type: 'boolean',
128
+ describe: 'Whether to include all kinds of pages such as webviews or background pages as pages.',
129
+ hidden: true,
130
+ },
126
131
  chromeArg: {
127
132
  type: 'array',
128
133
  describe: 'Additional arguments for Chrome. Only applies when Chrome is launched by chrome-devtools-mcp.',
@@ -130,17 +135,17 @@ export const cliOptions = {
130
135
  categoryEmulation: {
131
136
  type: 'boolean',
132
137
  default: true,
133
- describe: 'Set to false to exlcude tools related to emulation.',
138
+ describe: 'Set to false to exclude tools related to emulation.',
134
139
  },
135
140
  categoryPerformance: {
136
141
  type: 'boolean',
137
142
  default: true,
138
- describe: 'Set to false to exlcude tools related to performance.',
143
+ describe: 'Set to false to exclude tools related to performance.',
139
144
  },
140
145
  categoryNetwork: {
141
146
  type: 'boolean',
142
147
  default: true,
143
- describe: 'Set to false to exlcude tools related to network.',
148
+ describe: 'Set to false to exclude tools related to network.',
144
149
  },
145
150
  };
146
151
  export function parseArguments(version, argv = process.argv) {
@@ -5,9 +5,9 @@
5
5
  */
6
6
  import { isUtf8 } from 'node:buffer';
7
7
  const BODY_CONTEXT_SIZE_LIMIT = 10000;
8
- export function getShortDescriptionForRequest(request, id) {
8
+ export function getShortDescriptionForRequest(request, id, selectedInDevToolsUI = false) {
9
9
  // TODO truncate the URL
10
- return `reqid=${id} ${request.method()} ${request.url()} ${getStatusFromRequest(request)}`;
10
+ return `reqid=${id} ${request.method()} ${request.url()} ${getStatusFromRequest(request)}${selectedInDevToolsUI ? ` [selected in the DevTools Network panel]` : ''}`;
11
11
  }
12
12
  export function getStatusFromRequest(request) {
13
13
  const httpResponse = request.response();
@@ -1,10 +1,15 @@
1
- export function formatA11ySnapshot(serializedAXNodeRoot, depth = 0) {
1
+ export function formatSnapshotNode(root, snapshot, depth = 0) {
2
2
  let result = '';
3
- const attributes = getAttributes(serializedAXNodeRoot);
4
- const line = ' '.repeat(depth * 2) + attributes.join(' ') + '\n';
3
+ const attributes = getAttributes(root);
4
+ const line = ' '.repeat(depth * 2) +
5
+ attributes.join(' ') +
6
+ (root.id === snapshot?.selectedElementUid
7
+ ? ' [selected in the DevTools Elements panel]'
8
+ : '') +
9
+ '\n';
5
10
  result += line;
6
- for (const child of serializedAXNodeRoot.children) {
7
- result += formatA11ySnapshot(child, depth + 1);
11
+ for (const child of root.children) {
12
+ result += formatSnapshotNode(child, snapshot, depth + 1);
8
13
  }
9
14
  return result;
10
15
  }
@@ -19,7 +24,14 @@ function getAttributes(serializedAXNodeRoot) {
19
24
  if (serializedAXNodeRoot.name) {
20
25
  attributes.push(`"${serializedAXNodeRoot.name}"`);
21
26
  }
22
- const excluded = new Set(['id', 'role', 'name', 'elementHandle', 'children']);
27
+ const excluded = new Set([
28
+ 'id',
29
+ 'role',
30
+ 'name',
31
+ 'elementHandle',
32
+ 'children',
33
+ 'backendNodeId',
34
+ ]);
23
35
  const booleanPropertyMap = {
24
36
  disabled: 'disableable',
25
37
  expanded: 'expandable',
package/build/src/main.js CHANGED
@@ -23,7 +23,7 @@ import * as scriptTools from './tools/script.js';
23
23
  import * as snapshotTools from './tools/snapshot.js';
24
24
  // If moved update release-please config
25
25
  // x-release-please-start-version
26
- const VERSION = '0.9.0';
26
+ const VERSION = '0.10.0';
27
27
  // x-release-please-end
28
28
  export const args = parseArguments(VERSION);
29
29
  const logFile = args.logFile ? saveLogsToFile(args.logFile) : undefined;
@@ -62,7 +62,10 @@ async function getContext() {
62
62
  devtools,
63
63
  });
64
64
  if (context?.browser !== browser) {
65
- context = await McpContext.from(browser, logger);
65
+ context = await McpContext.from(browser, logger, {
66
+ experimentalDevToolsDebugging: devtools,
67
+ experimentalIncludeAllPages: args.experimentalIncludeAllPages,
68
+ });
66
69
  }
67
70
  return context;
68
71
  }
@@ -94,6 +97,8 @@ function registerTool(tool) {
94
97
  try {
95
98
  logger(`${tool.name} request: ${JSON.stringify(params, null, ' ')}`);
96
99
  const context = await getContext();
100
+ logger(`${tool.name} context: resolved`);
101
+ await context.detectOpenDevToolsWindows();
97
102
  const response = new McpResponse();
98
103
  await tool.handler({
99
104
  params,