chrome-devtools-mcp 0.20.2 → 0.20.3

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.
@@ -6,52 +6,6 @@
6
6
  import { PuppeteerDevToolsConnection } from './DevToolsConnectionAdapter.js';
7
7
  import { Mutex } from './Mutex.js';
8
8
  import { DevTools } from './third_party/index.js';
9
- export function extractUrlLikeFromDevToolsTitle(title) {
10
- const match = title.match(new RegExp(`DevTools - (.*)`));
11
- return match?.[1] ?? undefined;
12
- }
13
- export function urlsEqual(url1, url2) {
14
- const normalizedUrl1 = normalizeUrl(url1);
15
- const normalizedUrl2 = normalizeUrl(url2);
16
- return normalizedUrl1 === normalizedUrl2;
17
- }
18
- /**
19
- * For the sake of the MCP server, when we determine if two URLs are equal we
20
- * remove some parts:
21
- *
22
- * 1. We do not care about the protocol.
23
- * 2. We do not care about trailing slashes.
24
- * 3. We do not care about "www".
25
- * 4. We ignore the hash parts.
26
- *
27
- * For example, if the user types "record a trace on foo.com", we would want to
28
- * match a tab in the connected Chrome instance that is showing "www.foo.com/"
29
- */
30
- function normalizeUrl(url) {
31
- let result = url.trim();
32
- // Remove protocols
33
- if (result.startsWith('https://')) {
34
- result = result.slice(8);
35
- }
36
- else if (result.startsWith('http://')) {
37
- result = result.slice(7);
38
- }
39
- // Remove 'www.'. This ensures that we find the right URL regardless of if the user adds `www` or not.
40
- if (result.startsWith('www.')) {
41
- result = result.slice(4);
42
- }
43
- // We use target URLs to locate DevTools but those often do
44
- // no include hash.
45
- const hashIdx = result.lastIndexOf('#');
46
- if (hashIdx !== -1) {
47
- result = result.slice(0, hashIdx);
48
- }
49
- // Remove trailing slash
50
- if (result.endsWith('/')) {
51
- result = result.slice(0, -1);
52
- }
53
- return result;
54
- }
55
9
  /**
56
10
  * A mock implementation of an issues manager that only implements the methods
57
11
  * that are actually used by the IssuesAggregator
@@ -5,9 +5,9 @@
5
5
  */
6
6
  import fs from 'node:fs/promises';
7
7
  import path from 'node:path';
8
- import { extractUrlLikeFromDevToolsTitle, UniverseManager, urlsEqual, } from './DevtoolsUtils.js';
8
+ import { UniverseManager } from './DevtoolsUtils.js';
9
9
  import { McpPage } from './McpPage.js';
10
- import { NetworkCollector, ConsoleCollector } from './PageCollector.js';
10
+ import { NetworkCollector, ConsoleCollector, } from './PageCollector.js';
11
11
  import { Locator } from './third_party/index.js';
12
12
  import { PredefinedNetworkConditions } from './third_party/index.js';
13
13
  import { listPages } from './tools/pages.js';
@@ -467,38 +467,26 @@ export class McpContext {
467
467
  async detectOpenDevToolsWindows() {
468
468
  this.logger('Detecting open DevTools windows');
469
469
  const { pages } = await this.#getAllPages();
470
- // Clear all devToolsPage references before re-detecting.
471
- for (const mcpPage of this.#mcpPages.values()) {
472
- mcpPage.devToolsPage = undefined;
473
- }
474
- for (const devToolsPage of pages) {
475
- if (devToolsPage.url().startsWith('devtools://')) {
476
- try {
477
- this.logger('Calling getTargetInfo for ' + devToolsPage.url());
478
- const data = await devToolsPage
479
- // @ts-expect-error no types for _client().
480
- ._client()
481
- .send('Target.getTargetInfo');
482
- const devtoolsPageTitle = data.targetInfo.title;
483
- const urlLike = extractUrlLikeFromDevToolsTitle(devtoolsPageTitle);
484
- if (!urlLike) {
485
- continue;
486
- }
487
- // TODO: lookup without a loop.
488
- for (const page of this.#pages) {
489
- if (urlsEqual(page.url(), urlLike)) {
490
- const mcpPage = this.#mcpPages.get(page);
491
- if (mcpPage) {
492
- mcpPage.devToolsPage = devToolsPage;
493
- }
494
- }
495
- }
470
+ await Promise.all(pages.map(async (page) => {
471
+ const mcpPage = this.#mcpPages.get(page);
472
+ if (!mcpPage) {
473
+ return;
474
+ }
475
+ // Prior to Chrome 144.0.7559.59, the command fails,
476
+ // Some Electron apps still use older version
477
+ // Fall back to not exposing DevTools at all.
478
+ try {
479
+ if (await page.hasDevTools()) {
480
+ mcpPage.devToolsPage = await page.openDevTools();
496
481
  }
497
- catch (error) {
498
- this.logger('Issue occurred while trying to find DevTools', error);
482
+ else {
483
+ mcpPage.devToolsPage = undefined;
499
484
  }
500
485
  }
501
- }
486
+ catch {
487
+ mcpPage.devToolsPage = undefined;
488
+ }
489
+ }));
502
490
  }
503
491
  getExtensionServiceWorkers() {
504
492
  return this.#extensionServiceWorkers;
@@ -28,7 +28,7 @@ export class PageCollector {
28
28
  #browser;
29
29
  #listenersInitializer;
30
30
  #listeners = new WeakMap();
31
- #maxNavigationSaved = 3;
31
+ maxNavigationSaved = 3;
32
32
  /**
33
33
  * This maps a Page to a list of navigations with a sub-list
34
34
  * of all collected resources.
@@ -109,7 +109,7 @@ export class PageCollector {
109
109
  }
110
110
  // Add the latest navigation first
111
111
  navigations.unshift([]);
112
- navigations.splice(this.#maxNavigationSaved);
112
+ navigations.splice(this.maxNavigationSaved);
113
113
  }
114
114
  cleanupPageDestroyed(page) {
115
115
  const listeners = this.#listeners.get(page);
@@ -129,7 +129,7 @@ export class PageCollector {
129
129
  return navigations[0];
130
130
  }
131
131
  const data = [];
132
- for (let index = this.#maxNavigationSaved; index >= 0; index--) {
132
+ for (let index = this.maxNavigationSaved; index >= 0; index--) {
133
133
  if (navigations[index]) {
134
134
  data.push(...navigations[index]);
135
135
  }
@@ -305,5 +305,6 @@ export class NetworkCollector extends PageCollector {
305
305
  else {
306
306
  navigations.unshift([]);
307
307
  }
308
+ navigations.splice(this.maxNavigationSaved);
308
309
  }
309
310
  }
@@ -8,7 +8,7 @@ export const cliOptions = {
8
8
  autoConnect: {
9
9
  type: 'boolean',
10
10
  description: 'If specified, automatically connects to a browser (Chrome 144+) running locally from the user data directory identified by the channel param (default channel is stable). Requires the remoted debugging server to be started in the Chrome instance via chrome://inspect/#remote-debugging.',
11
- conflicts: ['isolated', 'executablePath'],
11
+ conflicts: ['isolated', 'executablePath', 'categoryExtensions'],
12
12
  default: false,
13
13
  coerce: (value) => {
14
14
  if (!value) {
@@ -21,7 +21,7 @@ export const cliOptions = {
21
21
  type: 'string',
22
22
  description: 'Connect to a running, debuggable Chrome instance (e.g. `http://127.0.0.1:9222`). For more details see: https://github.com/ChromeDevTools/chrome-devtools-mcp#connecting-to-a-running-chrome-instance.',
23
23
  alias: 'u',
24
- conflicts: 'wsEndpoint',
24
+ conflicts: ['wsEndpoint', 'categoryExtensions'],
25
25
  coerce: (url) => {
26
26
  if (!url) {
27
27
  return;
@@ -39,7 +39,7 @@ export const cliOptions = {
39
39
  type: 'string',
40
40
  description: 'WebSocket endpoint to connect to a running Chrome instance (e.g., ws://127.0.0.1:9222/devtools/browser/<id>). Alternative to --browserUrl.',
41
41
  alias: 'w',
42
- conflicts: 'browserUrl',
42
+ conflicts: ['browserUrl', 'categoryExtensions'],
43
43
  coerce: (url) => {
44
44
  if (!url) {
45
45
  return;
@@ -193,9 +193,9 @@ export const cliOptions = {
193
193
  },
194
194
  categoryExtensions: {
195
195
  type: 'boolean',
196
- default: false,
197
196
  hidden: true,
198
- describe: 'Set to false to exclude tools related to extensions.',
197
+ conflicts: ['browserUrl', 'autoConnect', 'wsEndpoint'],
198
+ describe: 'Set to true to include tools related to extensions. Note: This feature is only supported with a pipe connection. autoConnect is not supported.',
199
199
  },
200
200
  performanceCrux: {
201
201
  type: 'boolean',
@@ -95,7 +95,7 @@ export async function createMcpServer(serverArgs, options) {
95
95
  return;
96
96
  }
97
97
  if (tool.annotations.category === ToolCategory.EXTENSIONS &&
98
- serverArgs.categoryExtensions === false) {
98
+ !serverArgs.categoryExtensions) {
99
99
  return;
100
100
  }
101
101
  if (tool.annotations.conditions?.includes('computerVision') &&
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "@modelcontextprotocol/sdk": "1.27.1",
3
- "chrome-devtools-frontend": "1.0.1596260",
3
+ "chrome-devtools-frontend": "1.0.1599001",
4
4
  "core-js": "3.48.0",
5
5
  "debug": "4.4.3",
6
6
  "lighthouse": "13.0.3",
@@ -3117,7 +3117,6 @@ var ExperimentName;
3117
3117
  ExperimentName["SHOW_OPTION_TO_EXPOSE_INTERNALS_IN_HEAP_SNAPSHOT"] = "show-option-to-expose-internals-in-heap-snapshot";
3118
3118
  ExperimentName["TIMELINE_INVALIDATION_TRACKING"] = "timeline-invalidation-tracking";
3119
3119
  ExperimentName["TIMELINE_SHOW_ALL_EVENTS"] = "timeline-show-all-events";
3120
- ExperimentName["TIMELINE_V8_RUNTIME_CALL_STATS"] = "timeline-v8-runtime-call-stats";
3121
3120
  ExperimentName["APCA"] = "apca";
3122
3121
  ExperimentName["FONT_EDITOR"] = "font-editor";
3123
3122
  ExperimentName["FULL_ACCESSIBILITY_TREE"] = "full-accessibility-tree";