项目文件夹

文件
Wolfgang Beyer 7548c97663 feat: support third-party developer tools (#1982)
Enables "third-party developer tools" feature. This allows the inspected
web page to expose tools which provide debugging information to Chrome
DevTools for Agents.

Third-party developer tools enable web applications to expose internal
state, component hierarchies, or specific debug data that cannot be
deduced through static analysis. This allows Chrome DevTools for Agents
to provide richer, more actionable context to AI agents during debugging
sessions.

2 additional tools are enabled in Chrome DevTools for Agents for
interacting with third-party developer tools:
`list_3p_developer_tools()` and `execute_3p_developer_tool`.

Code changes in this PR:
- Rename "in-page tools" to "third-party developer tools"
- Unhide
- Make available in CLI
- Add documentation
2026-05-06 12:43:34 +00:00

349 行
8.9 KiB
TypeScript

/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import {describe, it} from 'node:test';
import {parseArguments} from '../src/bin/chrome-devtools-mcp-cli-options.js';
describe('cli args parsing', () => {
const defaultArgs = {
'category-emulation': true,
categoryEmulation: true,
'category-performance': true,
categoryPerformance: true,
'category-network': true,
categoryNetwork: true,
'category-extensions': false,
categoryExtensions: false,
'category-experimental-third-party': false,
categoryExperimentalThirdParty: false,
'auto-connect': undefined,
autoConnect: undefined,
'performance-crux': true,
performanceCrux: true,
'usage-statistics': true,
usageStatistics: true,
'redact-network-headers': false,
redactNetworkHeaders: false,
};
it('parses with default args', async () => {
const args = parseArguments('1.0.0', ['node', 'main.js'], {});
assert.deepStrictEqual(args, {
...defaultArgs,
_: [],
headless: false,
$0: 'npx chrome-devtools-mcp@latest',
channel: 'stable',
});
});
it('parses with browser url', async () => {
const args = parseArguments(
'1.0.0',
['node', 'main.js', '--browserUrl', 'http://localhost:3000'],
{},
);
assert.deepStrictEqual(args, {
...defaultArgs,
_: [],
headless: false,
$0: 'npx chrome-devtools-mcp@latest',
'browser-url': 'http://localhost:3000',
browserUrl: 'http://localhost:3000',
u: 'http://localhost:3000',
});
});
it('parses with user data dir', async () => {
const args = parseArguments(
'1.0.0',
['node', 'main.js', '--user-data-dir', '/tmp/chrome-profile'],
{},
);
assert.deepStrictEqual(args, {
...defaultArgs,
_: [],
headless: false,
$0: 'npx chrome-devtools-mcp@latest',
channel: 'stable',
'user-data-dir': '/tmp/chrome-profile',
userDataDir: '/tmp/chrome-profile',
});
});
it('parses an empty browser url', async () => {
const args = parseArguments(
'1.0.0',
['node', 'main.js', '--browserUrl', ''],
{},
);
assert.deepStrictEqual(args, {
...defaultArgs,
_: [],
headless: false,
$0: 'npx chrome-devtools-mcp@latest',
'browser-url': undefined,
browserUrl: undefined,
u: undefined,
channel: 'stable',
});
});
it('parses with executable path', async () => {
const args = parseArguments(
'1.0.0',
['node', 'main.js', '--executablePath', '/tmp/test 123/chrome'],
{},
);
assert.deepStrictEqual(args, {
...defaultArgs,
_: [],
headless: false,
$0: 'npx chrome-devtools-mcp@latest',
'executable-path': '/tmp/test 123/chrome',
e: '/tmp/test 123/chrome',
executablePath: '/tmp/test 123/chrome',
});
});
it('parses viewport', async () => {
const args = parseArguments(
'1.0.0',
['node', 'main.js', '--viewport', '888x777'],
{},
);
assert.deepStrictEqual(args, {
...defaultArgs,
_: [],
headless: false,
$0: 'npx chrome-devtools-mcp@latest',
channel: 'stable',
viewport: {
width: 888,
height: 777,
},
});
});
it('parses chrome args', async () => {
const args = parseArguments(
'1.0.0',
[
'node',
'main.js',
`--chrome-arg='--no-sandbox'`,
`--chrome-arg='--disable-setuid-sandbox'`,
],
{},
);
assert.deepStrictEqual(args, {
...defaultArgs,
_: [],
headless: false,
$0: 'npx chrome-devtools-mcp@latest',
channel: 'stable',
'chrome-arg': ['--no-sandbox', '--disable-setuid-sandbox'],
chromeArg: ['--no-sandbox', '--disable-setuid-sandbox'],
});
});
it('parses ignore chrome args', async () => {
const args = parseArguments(
'1.0.0',
[
'node',
'main.js',
`--ignore-default-chrome-arg='--disable-extensions'`,
`--ignore-default-chrome-arg='--disable-cancel-all-touches'`,
],
{},
);
assert.deepStrictEqual(args, {
...defaultArgs,
_: [],
headless: false,
$0: 'npx chrome-devtools-mcp@latest',
channel: 'stable',
'ignore-default-chrome-arg': [
'--disable-extensions',
'--disable-cancel-all-touches',
],
ignoreDefaultChromeArg: [
'--disable-extensions',
'--disable-cancel-all-touches',
],
});
});
it('parses wsEndpoint with ws:// protocol', async () => {
const args = parseArguments(
'1.0.0',
[
'node',
'main.js',
'--wsEndpoint',
'ws://127.0.0.1:9222/devtools/browser/abc123',
],
{},
);
assert.deepStrictEqual(args, {
...defaultArgs,
_: [],
headless: false,
$0: 'npx chrome-devtools-mcp@latest',
'ws-endpoint': 'ws://127.0.0.1:9222/devtools/browser/abc123',
wsEndpoint: 'ws://127.0.0.1:9222/devtools/browser/abc123',
w: 'ws://127.0.0.1:9222/devtools/browser/abc123',
});
});
it('parses wsEndpoint with wss:// protocol', async () => {
const args = parseArguments(
'1.0.0',
[
'node',
'main.js',
'--wsEndpoint',
'wss://example.com:9222/devtools/browser/abc123',
],
{},
);
assert.deepStrictEqual(args, {
...defaultArgs,
_: [],
headless: false,
$0: 'npx chrome-devtools-mcp@latest',
'ws-endpoint': 'wss://example.com:9222/devtools/browser/abc123',
wsEndpoint: 'wss://example.com:9222/devtools/browser/abc123',
w: 'wss://example.com:9222/devtools/browser/abc123',
});
});
it('parses wsHeaders with valid JSON', async () => {
const args = parseArguments(
'1.0.0',
[
'node',
'main.js',
'--wsEndpoint',
'ws://127.0.0.1:9222/devtools/browser/abc123',
'--wsHeaders',
'{"Authorization":"Bearer token","X-Custom":"value"}',
],
{},
);
assert.deepStrictEqual(args.wsHeaders, {
Authorization: 'Bearer token',
'X-Custom': 'value',
});
});
it('parses disabled category', async () => {
const args = parseArguments(
'1.0.0',
['node', 'main.js', '--no-category-emulation'],
{},
);
assert.deepStrictEqual(args, {
...defaultArgs,
_: [],
headless: false,
$0: 'npx chrome-devtools-mcp@latest',
channel: 'stable',
'category-emulation': false,
categoryEmulation: false,
});
});
it('parses auto-connect', async () => {
const args = parseArguments(
'1.0.0',
['node', 'main.js', '--auto-connect'],
{},
);
assert.deepStrictEqual(args, {
...defaultArgs,
_: [],
headless: false,
$0: 'npx chrome-devtools-mcp@latest',
channel: 'stable',
'auto-connect': true,
autoConnect: true,
});
});
it('parses usage statistics flag', async () => {
// Test default (should be true).
const defaultArgs = parseArguments('1.0.0', ['node', 'main.js'], {});
assert.strictEqual(defaultArgs.usageStatistics, true);
// Test enabling it
const enabledArgs = parseArguments(
'1.0.0',
['node', 'main.js', '--usage-statistics'],
{},
);
assert.strictEqual(enabledArgs.usageStatistics, true);
// Test disabling it
const disabledArgs = parseArguments(
'1.0.0',
['node', 'main.js', '--no-usage-statistics'],
{},
);
assert.strictEqual(disabledArgs.usageStatistics, false);
});
it('respects env variable', async () => {
// Test default (should be true).
const defaultArgs = parseArguments('1.0.0', ['node', 'main.js'], {
CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS: 'true',
});
assert.strictEqual(defaultArgs.usageStatistics, false);
// Test enabling it
const enabledArgs = parseArguments(
'1.0.0',
['node', 'main.js', '--usage-statistics'],
{
CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS: 'true',
},
);
assert.strictEqual(enabledArgs.usageStatistics, false);
// Test disabling it
const disabledArgs = parseArguments(
'1.0.0',
['node', 'main.js', '--no-usage-statistics'],
{
CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS: 'true',
},
);
assert.strictEqual(disabledArgs.usageStatistics, false);
});
it('parses performance crux flag', async () => {
const defaultArgs = parseArguments('1.0.0', ['node', 'main.js']);
assert.strictEqual(defaultArgs.performanceCrux, true);
// force enable
const enabledArgs = parseArguments(
'1.0.0',
['node', 'main.js', '--performance-crux'],
{},
);
assert.strictEqual(enabledArgs.performanceCrux, true);
const disabledArgs = parseArguments(
'1.0.0',
['node', 'main.js', '--no-performance-crux'],
{},
);
assert.strictEqual(disabledArgs.performanceCrux, false);
});
});