chrome-devtools-mcp 1.0.1 → 1.1.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.
- package/README.md +7 -7
- package/build/src/DevtoolsUtils.js +1 -1
- package/build/src/HeapSnapshotManager.js +16 -16
- package/build/src/McpContext.js +58 -23
- package/build/src/McpResponse.js +1 -1
- package/build/src/bin/chrome-devtools-cli-options.js +55 -49
- package/build/src/bin/chrome-devtools-mcp-main.js +38 -0
- package/build/src/browser.js +36 -2
- package/build/src/daemon/daemon.js +62 -5
- package/build/src/formatters/HeapSnapshotFormatter.js +7 -7
- package/build/src/third_party/THIRD_PARTY_NOTICES +4 -4
- package/build/src/third_party/bundled-packages.json +2 -2
- package/build/src/third_party/devtools-formatter-worker.js +2 -0
- package/build/src/third_party/devtools-heap-snapshot-worker.js +2 -0
- package/build/src/third_party/index.js +197 -75
- package/build/src/tools/ToolDefinition.js +1 -1
- package/build/src/tools/emulation.js +25 -0
- package/build/src/tools/extensions.js +2 -0
- package/build/src/tools/input.js +1 -1
- package/build/src/tools/lighthouse.js +1 -1
- package/build/src/tools/memory.js +19 -21
- package/build/src/tools/network.js +2 -2
- package/build/src/tools/performance.js +9 -6
- package/build/src/tools/screencast.js +1 -1
- package/build/src/tools/screenshot.js +1 -1
- package/build/src/tools/script.js +1 -1
- package/build/src/tools/snapshot.js +1 -1
- package/build/src/trace-processing/parse.js +2 -2
- package/build/src/utils/files.js +43 -0
- package/build/src/version.js +1 -1
- package/package.json +3 -3
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Copyright 2026 Google LLC
|
|
5
5
|
* SPDX-License-Identifier: Apache-2.0
|
|
6
6
|
*/
|
|
7
|
-
import fs from 'node:fs';
|
|
7
|
+
import fs, { constants, openSync, writeSync, closeSync } from 'node:fs';
|
|
8
8
|
import { createServer } from 'node:net';
|
|
9
|
+
import os from 'node:os';
|
|
9
10
|
import path from 'node:path';
|
|
10
11
|
import process from 'node:process';
|
|
11
12
|
import { logger } from '../logger.js';
|
|
@@ -19,10 +20,66 @@ if (isDaemonRunning(sessionId)) {
|
|
|
19
20
|
process.exit(1);
|
|
20
21
|
}
|
|
21
22
|
const pidFilePath = getPidFilePath(sessionId);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
fs.
|
|
23
|
+
const pidDir = path.dirname(pidFilePath);
|
|
24
|
+
const currentUserUid = os.userInfo().uid;
|
|
25
|
+
try {
|
|
26
|
+
fs.mkdirSync(pidDir, { recursive: true });
|
|
27
|
+
if (os.platform() !== 'win32') {
|
|
28
|
+
// POSIX specific checks
|
|
29
|
+
try {
|
|
30
|
+
const stats = fs.statSync(pidDir);
|
|
31
|
+
// 1. Check Ownership: Ensure the directory is owned by the current user.
|
|
32
|
+
if (stats.uid !== currentUserUid) {
|
|
33
|
+
console.error(`[MCP Daemon] Critical error: PID directory ${pidDir} is not owned by the current user (Expected: ${currentUserUid}, Found: ${stats.uid}). Possible tampering.`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
// 2. Check Permissions: Ensure the directory is not group or world-writable.
|
|
37
|
+
// Mode is a number, e.g., 0o700. We check if bits for group/world write are set.
|
|
38
|
+
const mode = stats.mode;
|
|
39
|
+
if (mode & constants.S_IWGRP || mode & constants.S_IWOTH) {
|
|
40
|
+
console.error(`[MCP Daemon] Critical error: PID directory ${pidDir} has insecure permissions (Mode: ${mode.toString(8)}). It should not be writable by group or others.`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (statErr) {
|
|
45
|
+
console.error(`[MCP Daemon] Critical error stating PID directory ${pidDir}:`, statErr);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
console.error(`[MCP Daemon] Critical error creating/validating PID directory: ${pidDir}`, err);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
let fd = -1;
|
|
55
|
+
try {
|
|
56
|
+
// Open the file with flags to:
|
|
57
|
+
// - O_WRONLY: Write-only
|
|
58
|
+
// - O_CREAT: Create if it doesn't exist
|
|
59
|
+
// - O_TRUNC: Truncate to zero length if it exists
|
|
60
|
+
// - O_NOFOLLOW: DO NOT follow symlinks.
|
|
61
|
+
// - 0o600: Permissions: read/write for owner, no permissions for others.
|
|
62
|
+
fd = openSync(pidFilePath, constants.O_WRONLY |
|
|
63
|
+
constants.O_CREAT |
|
|
64
|
+
constants.O_TRUNC |
|
|
65
|
+
constants.O_NOFOLLOW, 0o600);
|
|
66
|
+
writeSync(fd, process.pid.toString());
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
console.error(`[MCP Daemon] Critical error writing PID file: ${pidFilePath}`, err);
|
|
70
|
+
// If openSync fails due to O_NOFOLLOW on a symlink, the error will be caught here.
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
if (fd !== -1) {
|
|
75
|
+
try {
|
|
76
|
+
closeSync(fd);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
console.error(`[MCP Daemon] Error closing PID file: ${pidFilePath}`, err);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
26
83
|
logger(`Writing ${process.pid.toString()} to ${pidFilePath}`);
|
|
27
84
|
const socketPath = getSocketPath(sessionId);
|
|
28
85
|
const startDate = new Date();
|
|
@@ -29,10 +29,10 @@ export class HeapSnapshotFormatter {
|
|
|
29
29
|
if (items.length > 0) {
|
|
30
30
|
const firstItem = items[0];
|
|
31
31
|
if (isNodeLike(firstItem)) {
|
|
32
|
-
lines.push('
|
|
32
|
+
lines.push('nodeId,nodeName,type,distance,selfSize,retainedSize');
|
|
33
33
|
}
|
|
34
34
|
else if (isEdgeLike(firstItem)) {
|
|
35
|
-
lines.push('
|
|
35
|
+
lines.push('name,type,nodeId,nodeName');
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
for (const item of items) {
|
|
@@ -40,7 +40,7 @@ export class HeapSnapshotFormatter {
|
|
|
40
40
|
lines.push(`${item.id},${item.name},${item.type},${item.distance},${DevTools.I18n.ByteUtilities.formatBytesToKb(item.selfSize)},${DevTools.I18n.ByteUtilities.formatBytesToKb(item.retainedSize)}`);
|
|
41
41
|
}
|
|
42
42
|
else if (isEdgeLike(item)) {
|
|
43
|
-
lines.push(`${item.
|
|
43
|
+
lines.push(`${item.name},${item.type},${item.node.id},${item.node.name}`);
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
return lines.join('\n');
|
|
@@ -51,17 +51,17 @@ export class HeapSnapshotFormatter {
|
|
|
51
51
|
toString() {
|
|
52
52
|
const sorted = this.#getSortedAggregates();
|
|
53
53
|
const lines = [];
|
|
54
|
-
lines.push('
|
|
54
|
+
lines.push('id,name,count,selfSize,maxRetainedSize');
|
|
55
55
|
for (const info of sorted) {
|
|
56
|
-
const
|
|
57
|
-
lines.push(`${
|
|
56
|
+
const id = info[stableIdSymbol] ?? '';
|
|
57
|
+
lines.push(`${id},${info.name},${info.count},${DevTools.I18n.ByteUtilities.formatBytesToKb(info.self)},${DevTools.I18n.ByteUtilities.formatBytesToKb(info.maxRet)}`);
|
|
58
58
|
}
|
|
59
59
|
return lines.join('\n');
|
|
60
60
|
}
|
|
61
61
|
toJSON() {
|
|
62
62
|
const sorted = this.#getSortedAggregates();
|
|
63
63
|
return sorted.map(info => ({
|
|
64
|
-
|
|
64
|
+
id: info[stableIdSymbol],
|
|
65
65
|
className: info.name,
|
|
66
66
|
count: info.count,
|
|
67
67
|
selfSize: DevTools.I18n.ByteUtilities.formatBytesToKb(info.self),
|
|
@@ -324,7 +324,7 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH RE
|
|
|
324
324
|
|
|
325
325
|
Name: semver
|
|
326
326
|
URL: git+https://github.com/npm/node-semver.git
|
|
327
|
-
Version: 7.8.
|
|
327
|
+
Version: 7.8.1
|
|
328
328
|
License: ISC
|
|
329
329
|
|
|
330
330
|
The ISC License
|
|
@@ -867,21 +867,21 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
867
867
|
|
|
868
868
|
Name: puppeteer-core
|
|
869
869
|
URL: https://github.com/puppeteer/puppeteer/tree/main/packages/puppeteer-core
|
|
870
|
-
Version: 25.0
|
|
870
|
+
Version: 25.1.0
|
|
871
871
|
License: Apache-2.0
|
|
872
872
|
|
|
873
873
|
-------------------- DEPENDENCY DIVIDER --------------------
|
|
874
874
|
|
|
875
875
|
Name: @puppeteer/browsers
|
|
876
876
|
URL: https://github.com/puppeteer/puppeteer/tree/main/packages/browsers
|
|
877
|
-
Version: 3.0.
|
|
877
|
+
Version: 3.0.4
|
|
878
878
|
License: Apache-2.0
|
|
879
879
|
|
|
880
880
|
-------------------- DEPENDENCY DIVIDER --------------------
|
|
881
881
|
|
|
882
882
|
Name: ws
|
|
883
883
|
URL: https://github.com/websockets/ws
|
|
884
|
-
Version: 8.
|
|
884
|
+
Version: 8.21.0
|
|
885
885
|
License: MIT
|
|
886
886
|
|
|
887
887
|
Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
3
|
-
"chrome-devtools-frontend": "1.0.
|
|
3
|
+
"chrome-devtools-frontend": "1.0.1632065",
|
|
4
4
|
"core-js": "3.49.0",
|
|
5
5
|
"debug": "4.4.3",
|
|
6
6
|
"lighthouse": "13.3.0",
|
|
7
7
|
"semver": "^7.7.4",
|
|
8
8
|
"urlpattern-polyfill": "^10.1.0",
|
|
9
9
|
"yargs": "18.0.0",
|
|
10
|
-
"puppeteer-core": "25.0
|
|
10
|
+
"puppeteer-core": "25.1.0"
|
|
11
11
|
}
|
|
@@ -12262,6 +12262,7 @@ const UIStrings$2 = {
|
|
|
12262
12262
|
memoryInspectorPanel: 'Memory inspector panel',
|
|
12263
12263
|
developerResourcesPanel: 'Developer Resources panel',
|
|
12264
12264
|
animationsPanel: 'Animations panel',
|
|
12265
|
+
lighthousePanel: 'Lighthouse panel',
|
|
12265
12266
|
};
|
|
12266
12267
|
const str_$2 = registerUIStrings('core/common/Revealer.ts', UIStrings$2);
|
|
12267
12268
|
const i18nLazyString$1 = getLazilyComputedLocalizedString.bind(undefined, str_$2);
|
|
@@ -12278,6 +12279,7 @@ const i18nLazyString$1 = getLazilyComputedLocalizedString.bind(undefined, str_$2
|
|
|
12278
12279
|
SOURCES_PANEL: i18nLazyString$1(UIStrings$2.sourcesPanel),
|
|
12279
12280
|
MEMORY_INSPECTOR_PANEL: i18nLazyString$1(UIStrings$2.memoryInspectorPanel),
|
|
12280
12281
|
ANIMATIONS_PANEL: i18nLazyString$1(UIStrings$2.animationsPanel),
|
|
12282
|
+
LIGHTHOUSE_PANEL: i18nLazyString$1(UIStrings$2.lighthousePanel),
|
|
12281
12283
|
});
|
|
12282
12284
|
|
|
12283
12285
|
// Copyright 2014 The Chromium Authors
|
|
@@ -4808,6 +4808,7 @@ const UIStrings$2 = {
|
|
|
4808
4808
|
memoryInspectorPanel: 'Memory inspector panel',
|
|
4809
4809
|
developerResourcesPanel: 'Developer Resources panel',
|
|
4810
4810
|
animationsPanel: 'Animations panel',
|
|
4811
|
+
lighthousePanel: 'Lighthouse panel',
|
|
4811
4812
|
};
|
|
4812
4813
|
const str_$2 = registerUIStrings('core/common/Revealer.ts', UIStrings$2);
|
|
4813
4814
|
const i18nLazyString$1 = getLazilyComputedLocalizedString.bind(undefined, str_$2);
|
|
@@ -4863,6 +4864,7 @@ async function reveal(revealable, omitFocus = false) {
|
|
|
4863
4864
|
SOURCES_PANEL: i18nLazyString$1(UIStrings$2.sourcesPanel),
|
|
4864
4865
|
MEMORY_INSPECTOR_PANEL: i18nLazyString$1(UIStrings$2.memoryInspectorPanel),
|
|
4865
4866
|
ANIMATIONS_PANEL: i18nLazyString$1(UIStrings$2.animationsPanel),
|
|
4867
|
+
LIGHTHOUSE_PANEL: i18nLazyString$1(UIStrings$2.lighthousePanel),
|
|
4866
4868
|
});
|
|
4867
4869
|
|
|
4868
4870
|
// Copyright 2014 The Chromium Authors
|