chrome-devtools-mcp 0.20.3 → 0.22.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 (55) hide show
  1. package/README.md +97 -20
  2. package/build/src/HeapSnapshotManager.js +94 -0
  3. package/build/src/McpContext.js +26 -49
  4. package/build/src/McpPage.js +16 -0
  5. package/build/src/McpResponse.js +220 -12
  6. package/build/src/PageCollector.js +14 -28
  7. package/build/src/WaitForHelper.js +31 -0
  8. package/build/src/bin/check-latest-version.js +25 -0
  9. package/build/src/bin/chrome-devtools-mcp-cli-options.js +28 -9
  10. package/build/src/bin/chrome-devtools-mcp-main.js +2 -0
  11. package/build/src/bin/chrome-devtools-mcp.js +1 -0
  12. package/build/src/bin/chrome-devtools.js +9 -3
  13. package/build/src/bin/cliDefinitions.js +15 -9
  14. package/build/src/daemon/client.js +1 -1
  15. package/build/src/daemon/daemon.js +2 -6
  16. package/build/src/daemon/utils.js +1 -0
  17. package/build/src/formatters/HeapSnapshotFormatter.js +38 -0
  18. package/build/src/formatters/NetworkFormatter.js +24 -7
  19. package/build/src/index.js +22 -1
  20. package/build/src/telemetry/ClearcutLogger.js +145 -6
  21. package/build/src/telemetry/flagUtils.js +46 -4
  22. package/build/src/telemetry/toolMetricsUtils.js +88 -0
  23. package/build/src/telemetry/types.js +5 -0
  24. package/build/src/telemetry/watchdog/ClearcutSender.js +4 -3
  25. package/build/src/third_party/THIRD_PARTY_NOTICES +1400 -483
  26. package/build/src/third_party/bundled-packages.json +6 -5
  27. package/build/src/third_party/devtools-formatter-worker.js +61 -66
  28. package/build/src/third_party/devtools-heap-snapshot-worker.js +9690 -0
  29. package/build/src/third_party/index.js +61622 -52803
  30. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorCrossOriginNoCorsRequest.md +1 -0
  31. package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +10589 -4647
  32. package/build/src/tools/categories.js +5 -0
  33. package/build/src/tools/console.js +42 -39
  34. package/build/src/tools/emulation.js +1 -1
  35. package/build/src/tools/extensions.js +5 -11
  36. package/build/src/tools/inPage.js +105 -0
  37. package/build/src/tools/input.js +18 -16
  38. package/build/src/tools/lighthouse.js +3 -3
  39. package/build/src/tools/memory.js +50 -5
  40. package/build/src/tools/network.js +2 -2
  41. package/build/src/tools/pages.js +14 -6
  42. package/build/src/tools/performance.js +1 -1
  43. package/build/src/tools/screencast.js +2 -1
  44. package/build/src/tools/screenshot.js +3 -3
  45. package/build/src/tools/script.js +22 -16
  46. package/build/src/tools/tools.js +4 -0
  47. package/build/src/tools/webmcp.js +63 -0
  48. package/build/src/utils/check-for-updates.js +73 -0
  49. package/build/src/utils/files.js +4 -0
  50. package/build/src/utils/id.js +15 -0
  51. package/build/src/version.js +1 -1
  52. package/package.json +13 -9
  53. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorNoCorpCrossOriginNoCorsRequest.md +0 -3
  54. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNoCorpCossOriginNoCorsRequest.md +0 -3
  55. package/build/src/utils/ExtensionRegistry.js +0 -35
@@ -1,9 +1,10 @@
1
1
  {
2
- "@modelcontextprotocol/sdk": "1.27.1",
3
- "chrome-devtools-frontend": "1.0.1599001",
4
- "core-js": "3.48.0",
2
+ "@modelcontextprotocol/sdk": "1.29.0",
3
+ "chrome-devtools-frontend": "1.0.1613625",
4
+ "core-js": "3.49.0",
5
5
  "debug": "4.4.3",
6
- "lighthouse": "13.0.3",
6
+ "lighthouse": "13.1.0",
7
+ "semver": "^7.7.4",
7
8
  "yargs": "18.0.0",
8
- "puppeteer-core": "24.39.1"
9
+ "puppeteer-core": "24.42.0"
9
10
  }
@@ -26,36 +26,36 @@ function arrayDoesNotContainNullOrUndefined(arr) {
26
26
  const EmptyUrlString = '';
27
27
 
28
28
  // Copyright 2025 The Chromium Authors
29
- class NodeWorkerScope {
29
+ class WebWorkerScope {
30
30
  postMessage(message) {
31
- WorkerThreads.parentPort?.postMessage(message);
31
+ self.postMessage(message);
32
32
  }
33
33
  set onmessage(listener) {
34
- WorkerThreads.parentPort?.on('message', data => {
35
- listener({ data });
36
- });
34
+ self.addEventListener('message', listener);
37
35
  }
38
36
  }
39
- class NodeWorker {
37
+ class WebWorker {
40
38
  #workerPromise;
41
- #disposed = false;
39
+ #disposed;
42
40
  #rejectWorkerPromise;
43
- constructor(url) {
44
- this.#workerPromise = new Promise((resolve, reject) => {
41
+ constructor(workerLocation) {
42
+ this.#workerPromise = new Promise((fulfill, reject) => {
45
43
  this.#rejectWorkerPromise = reject;
46
- const worker = new WorkerThreads.Worker(new URL(url));
47
- worker.once('message', (message) => {
48
- if (message === 'workerReady') {
49
- resolve(worker);
50
- }
51
- });
52
- worker.on('error', reject);
44
+ const worker = new Worker(new URL(workerLocation), { type: 'module' });
45
+ worker.onerror = event => {
46
+ console.error(`Failed to load worker for ${workerLocation}:`, event);
47
+ };
48
+ worker.onmessage = (event) => {
49
+ console.assert(event.data === 'workerReady');
50
+ worker.onmessage = null;
51
+ fulfill(worker);
52
+ };
53
53
  });
54
54
  }
55
- postMessage(message) {
55
+ postMessage(message, transfer) {
56
56
  void this.#workerPromise.then(worker => {
57
57
  if (!this.#disposed) {
58
- worker.postMessage(message);
58
+ worker.postMessage(message, transfer ?? []);
59
59
  }
60
60
  });
61
61
  }
@@ -63,7 +63,7 @@ class NodeWorker {
63
63
  this.#disposed = true;
64
64
  void this.#workerPromise.then(worker => worker.terminate());
65
65
  }
66
- terminate(immediately) {
66
+ terminate(immediately = false) {
67
67
  if (immediately) {
68
68
  this.#rejectWorkerPromise?.(new Error('Worker terminated'));
69
69
  }
@@ -71,28 +71,20 @@ class NodeWorker {
71
71
  }
72
72
  set onmessage(listener) {
73
73
  void this.#workerPromise.then(worker => {
74
- worker.on('message', data => {
75
- if (!this.#disposed) {
76
- listener({ data });
77
- }
78
- });
74
+ worker.onmessage = listener;
79
75
  });
80
76
  }
81
77
  set onerror(listener) {
82
78
  void this.#workerPromise.then(worker => {
83
- worker.on('error', (error) => {
84
- if (!this.#disposed) {
85
- listener({ type: 'error', ...error });
86
- }
87
- });
79
+ worker.onerror = listener;
88
80
  });
89
81
  }
90
82
  }
91
83
  const HOST_RUNTIME$2 = {
92
84
  createWorker(url) {
93
- return new NodeWorker(url);
85
+ return new WebWorker(url);
94
86
  },
95
- workerScope: new NodeWorkerScope(),
87
+ workerScope: new WebWorkerScope(),
96
88
  };
97
89
 
98
90
  var HostRuntime$1 = /*#__PURE__*/Object.freeze({
@@ -102,42 +94,42 @@ var HostRuntime$1 = /*#__PURE__*/Object.freeze({
102
94
 
103
95
  // Copyright 2025 The Chromium Authors
104
96
 
105
- var node = /*#__PURE__*/Object.freeze({
97
+ var browser = /*#__PURE__*/Object.freeze({
106
98
  __proto__: null,
107
99
  HostRuntime: HostRuntime$1
108
100
  });
109
101
 
110
102
  // Copyright 2025 The Chromium Authors
111
- class WebWorkerScope {
103
+ class NodeWorkerScope {
112
104
  postMessage(message) {
113
- self.postMessage(message);
105
+ WorkerThreads.parentPort?.postMessage(message);
114
106
  }
115
107
  set onmessage(listener) {
116
- self.onmessage = listener;
108
+ WorkerThreads.parentPort?.addEventListener('message', msg => {
109
+ listener(msg);
110
+ });
117
111
  }
118
112
  }
119
- class WebWorker {
113
+ class NodeWorker {
120
114
  #workerPromise;
121
- #disposed;
115
+ #disposed = false;
122
116
  #rejectWorkerPromise;
123
- constructor(workerLocation) {
124
- this.#workerPromise = new Promise((fulfill, reject) => {
117
+ constructor(url) {
118
+ this.#workerPromise = new Promise((resolve, reject) => {
125
119
  this.#rejectWorkerPromise = reject;
126
- const worker = new Worker(new URL(workerLocation), { type: 'module' });
127
- worker.onerror = event => {
128
- console.error(`Failed to load worker for ${workerLocation}:`, event);
129
- };
130
- worker.onmessage = (event) => {
131
- console.assert(event.data === 'workerReady');
132
- worker.onmessage = null;
133
- fulfill(worker);
134
- };
120
+ const worker = new WorkerThreads.Worker(new URL(url));
121
+ worker.once('message', (message) => {
122
+ if (message === 'workerReady') {
123
+ resolve(worker);
124
+ }
125
+ });
126
+ worker.on('error', reject);
135
127
  });
136
128
  }
137
129
  postMessage(message, transfer) {
138
130
  void this.#workerPromise.then(worker => {
139
131
  if (!this.#disposed) {
140
- worker.postMessage(message, transfer ?? []);
132
+ worker.postMessage(message, transfer);
141
133
  }
142
134
  });
143
135
  }
@@ -145,7 +137,7 @@ class WebWorker {
145
137
  this.#disposed = true;
146
138
  void this.#workerPromise.then(worker => worker.terminate());
147
139
  }
148
- terminate(immediately = false) {
140
+ terminate(immediately) {
149
141
  if (immediately) {
150
142
  this.#rejectWorkerPromise?.(new Error('Worker terminated'));
151
143
  }
@@ -153,20 +145,28 @@ class WebWorker {
153
145
  }
154
146
  set onmessage(listener) {
155
147
  void this.#workerPromise.then(worker => {
156
- worker.onmessage = listener;
148
+ worker.on('message', (data) => {
149
+ if (!this.#disposed) {
150
+ listener({ data, ports: [] });
151
+ }
152
+ });
157
153
  });
158
154
  }
159
155
  set onerror(listener) {
160
156
  void this.#workerPromise.then(worker => {
161
- worker.onerror = listener;
157
+ worker.on('error', (error) => {
158
+ if (!this.#disposed) {
159
+ listener({ type: 'error', ...error });
160
+ }
161
+ });
162
162
  });
163
163
  }
164
164
  }
165
165
  const HOST_RUNTIME$1 = {
166
166
  createWorker(url) {
167
- return new WebWorker(url);
167
+ return new NodeWorker(url);
168
168
  },
169
- workerScope: new WebWorkerScope(),
169
+ workerScope: new NodeWorkerScope(),
170
170
  };
171
171
 
172
172
  var HostRuntime = /*#__PURE__*/Object.freeze({
@@ -176,7 +176,7 @@ var HostRuntime = /*#__PURE__*/Object.freeze({
176
176
 
177
177
  // Copyright 2025 The Chromium Authors
178
178
 
179
- var browser = /*#__PURE__*/Object.freeze({
179
+ var node = /*#__PURE__*/Object.freeze({
180
180
  __proto__: null,
181
181
  HostRuntime: HostRuntime
182
182
  });
@@ -186,12 +186,12 @@ const IS_NODE = typeof process !== 'undefined' && process.versions?.node !== nul
186
186
  const IS_BROWSER =
187
187
  typeof window !== 'undefined' || (typeof self !== 'undefined' && typeof self.postMessage === 'function');
188
188
  const HOST_RUNTIME = await (async () => {
189
- if (IS_NODE) {
190
- return (await Promise.resolve().then(function () { return node; })).HostRuntime.HOST_RUNTIME;
191
- }
192
189
  if (IS_BROWSER) {
193
190
  return (await Promise.resolve().then(function () { return browser; })).HostRuntime.HOST_RUNTIME;
194
191
  }
192
+ if (IS_NODE) {
193
+ return (await Promise.resolve().then(function () { return node; })).HostRuntime.HOST_RUNTIME;
194
+ }
195
195
  throw new Error('Unknown runtime!');
196
196
  })();
197
197
 
@@ -3114,16 +3114,10 @@ var ExperimentName;
3114
3114
  ExperimentName["LIVE_HEAP_PROFILE"] = "live-heap-profile";
3115
3115
  ExperimentName["PROTOCOL_MONITOR"] = "protocol-monitor";
3116
3116
  ExperimentName["SAMPLING_HEAP_PROFILER_TIMELINE"] = "sampling-heap-profiler-timeline";
3117
- ExperimentName["SHOW_OPTION_TO_EXPOSE_INTERNALS_IN_HEAP_SNAPSHOT"] = "show-option-to-expose-internals-in-heap-snapshot";
3118
3117
  ExperimentName["TIMELINE_INVALIDATION_TRACKING"] = "timeline-invalidation-tracking";
3119
- ExperimentName["TIMELINE_SHOW_ALL_EVENTS"] = "timeline-show-all-events";
3120
3118
  ExperimentName["APCA"] = "apca";
3121
3119
  ExperimentName["FONT_EDITOR"] = "font-editor";
3122
- ExperimentName["FULL_ACCESSIBILITY_TREE"] = "full-accessibility-tree";
3123
- ExperimentName["EXPERIMENTAL_COOKIE_FEATURES"] = "experimental-cookie-features";
3124
3120
  ExperimentName["INSTRUMENTATION_BREAKPOINTS"] = "instrumentation-breakpoints";
3125
- ExperimentName["AUTHORED_DEPLOYED_GROUPING"] = "authored-deployed-grouping";
3126
- ExperimentName["JUST_MY_CODE"] = "just-my-code";
3127
3121
  ExperimentName["USE_SOURCE_MAP_SCOPES"] = "use-source-map-scopes";
3128
3122
  ExperimentName["TIMELINE_DEBUG_MODE"] = "timeline-debug-mode";
3129
3123
  ExperimentName["DURABLE_MESSAGES"] = "durable-messages";
@@ -9113,6 +9107,7 @@ function hsva2rgba(hsva) {
9113
9107
  }
9114
9108
  const EPSILON = 0.01;
9115
9109
  const WIDE_RANGE_EPSILON = 1;
9110
+ const STRICT_EPSILON = 1e-4;
9116
9111
  function equals(a, b, accuracy = EPSILON) {
9117
9112
  if (Array.isArray(a) && Array.isArray(b)) {
9118
9113
  if (a.length !== b.length) {
@@ -9351,7 +9346,7 @@ class LCH {
9351
9346
  return false;
9352
9347
  }
9353
9348
  isHuePowerless() {
9354
- return equals(this.c, 0);
9349
+ return equals(this.c, 0, STRICT_EPSILON);
9355
9350
  }
9356
9351
  static fromSpec(spec, text) {
9357
9352
  const L = parsePercentage(spec[0], [0, 100]) ?? parseNumber(spec[0]);
@@ -9823,7 +9818,7 @@ class HSL {
9823
9818
  this.l = clamp(l, { min: 0, max: 1 });
9824
9819
  s = equals(this.l, 0) || equals(this.l, 1) ? 0 : s;
9825
9820
  this.s = clamp(s, { min: 0, max: 1 });
9826
- h = equals(this.s, 0) ? 0 : h;
9821
+ h = equals(this.s, 0, STRICT_EPSILON) ? 0 : h;
9827
9822
  this.h = normalizeHue(h * 360) / 360;
9828
9823
  this.alpha = clamp(alpha ?? null, { min: 0, max: 1 });
9829
9824
  this.#authoredText = authoredText;