chromedevtools--chrome-devtools-mcp
7548c97663
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
349 行
8.9 KiB
TypeScript
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);
|
|
});
|
|
});
|