```
├── .claude-plugin/
├── marketplace.json (100 tokens)
├── plugin.json (100 tokens)
├── .gemini/
├── settings.json
├── .gitattributes (omitted)
├── .github/
├── ISSUE_TEMPLATE/
├── 01-bug.yml (400 tokens)
├── 02-feature.yml (200 tokens)
├── 03-task.yml (100 tokens)
├── config.yml
├── dependabot.yml (200 tokens)
├── plugin/
├── plugin.json (100 tokens)
├── workflows/
├── conventional-commit.yml (200 tokens)
├── pre-release.yml (200 tokens)
├── presubmit.yml (300 tokens)
├── publish-to-npm-on-tag.yml (600 tokens)
├── release-please.yml (100 tokens)
├── run-tests.yml (400 tokens)
├── .gitignore (500 tokens)
├── .mcp.json
├── .npmrc
├── .nvmrc
├── .prettierignore (omitted)
├── .prettierrc.cjs (100 tokens)
├── .release-please-manifest.json
├── AGENTS.md (200 tokens)
├── CHANGELOG.md (16.7k tokens)
├── CONTRIBUTING.md (1100 tokens)
├── LICENSE (omitted)
├── README.md (6.7k tokens)
├── SECURITY.md (200 tokens)
├── docs/
├── cli.md (700 tokens)
├── debugging-android.md (300 tokens)
├── design-principles.md (200 tokens)
├── slim-tool-reference.md (100 tokens)
├── third-party-developer-tools.md (700 tokens)
├── tool-reference.md (4.6k tokens)
├── troubleshooting.md (1700 tokens)
├── eslint.config.mjs (800 tokens)
├── gemini-extension.json
├── package-lock.json (omitted)
├── package.json (800 tokens)
├── puppeteer.config.cjs (100 tokens)
├── release-please-config.json (300 tokens)
├── rollup.config.mjs (1900 tokens)
├── scripts/
├── append-lighthouse-notices.ts (200 tokens)
├── count_tokens.ts (200 tokens)
├── eslint_rules/
├── check-license-rule.js (500 tokens)
├── enforce-zod-schema-rule.js (300 tokens)
├── local-plugin.js (100 tokens)
├── no-direct-third-party-imports-rule.js (800 tokens)
├── eval_gemini.ts (1500 tokens)
├── eval_scenarios/
├── console_test.ts (200 tokens)
├── emulation_test.ts (100 tokens)
├── emulation_userAgent_test.ts (100 tokens)
├── emulation_viewport_test.ts (100 tokens)
├── fill_select_and_checkboxes_test.ts (500 tokens)
├── fix_webpage_issues_test.ts (400 tokens)
├── frontend_snapshot_test.ts (200 tokens)
├── input_parallel_test.ts (200 tokens)
├── input_test.ts (200 tokens)
├── isolated_context_test.ts (200 tokens)
├── lighthouse_a11y_test.ts (100 tokens)
├── lighthouse_best_practices_test.ts (100 tokens)
├── navigation_test.ts (100 tokens)
├── network_test.ts (200 tokens)
├── page_focus_keyboard_test.ts (500 tokens)
├── page_id_routing_test.ts (300 tokens)
├── performance_test.ts (100 tokens)
├── select_page_test.ts (200 tokens)
├── snapshot_test.ts (100 tokens)
├── generate-cli.ts (1200 tokens)
├── generate-docs.ts (3.3k tokens)
├── post-build.ts (1000 tokens)
├── prepare.ts (400 tokens)
├── test.mjs (600 tokens)
├── tsconfig.json (100 tokens)
├── update-lighthouse.ts (300 tokens)
├── update_metrics.ts (800 tokens)
├── verify-npm-package.mjs (200 tokens)
├── verify-server-json-version.ts (500 tokens)
├── server.json (100 tokens)
├── skills/
├── a11y-debugging/
├── SKILL.md (1100 tokens)
├── references/
├── a11y-snippets.md (600 tokens)
├── chrome-devtools-cli/
├── SKILL.md (1700 tokens)
├── references/
├── installation.md (200 tokens)
├── chrome-devtools/
├── SKILL.md (600 tokens)
├── debug-optimize-lcp/
├── SKILL.md (1300 tokens)
├── references/
├── elements-and-size.md (300 tokens)
├── lcp-breakdown.md (500 tokens)
├── lcp-snippets.md (500 tokens)
├── optimization-strategies.md (400 tokens)
├── memory-leak-debugging/
├── SKILL.md (600 tokens)
├── references/
├── common-leaks.md (400 tokens)
├── compare_snapshots.js (600 tokens)
├── memlab.md (200 tokens)
├── troubleshooting/
├── SKILL.md (1400 tokens)
├── src/
├── DevToolsConnectionAdapter.ts (700 tokens)
├── DevtoolsUtils.ts (2.7k tokens)
├── HeapSnapshotManager.ts (1100 tokens)
├── McpContext.ts (4.9k tokens)
├── McpPage.ts (2.6k tokens)
├── McpResponse.ts (7.6k tokens)
├── Mutex.ts (200 tokens)
├── PageCollector.ts (2.2k tokens)
├── SlimMcpResponse.ts (100 tokens)
├── TextSnapshot.ts (2k tokens)
├── ToolHandler.ts (1800 tokens)
├── WaitForHelper.ts (1300 tokens)
├── bin/
├── check-latest-version.ts (300 tokens)
├── chrome-devtools-cli-options.ts (6k tokens)
├── chrome-devtools-mcp-cli-options.ts (2.6k tokens)
├── chrome-devtools-mcp-main.ts (300 tokens)
├── chrome-devtools-mcp.ts (200 tokens)
├── chrome-devtools.ts (1500 tokens)
├── browser.ts (1600 tokens)
├── daemon/
├── client.ts (1000 tokens)
├── daemon.ts (1100 tokens)
├── types.ts (100 tokens)
├── utils.ts (800 tokens)
├── devtools.d.ts (omitted)
├── formatters/
├── ConsoleFormatter.ts (2.3k tokens)
├── HeapSnapshotFormatter.ts (500 tokens)
├── IssueFormatter.ts (1500 tokens)
├── NetworkFormatter.ts (1900 tokens)
├── SnapshotFormatter.ts (900 tokens)
├── index.ts (1200 tokens)
├── issue-descriptions.ts (300 tokens)
├── logger.ts (200 tokens)
├── polyfill.ts
├── telemetry/
├── ClearcutLogger.ts (1200 tokens)
├── WatchdogClient.ts (500 tokens)
├── errors.ts (100 tokens)
├── flagUtils.ts (700 tokens)
├── flag_usage_metrics.json (1100 tokens)
├── metricsRegistry.ts (700 tokens)
├── persistence.ts (500 tokens)
├── tool_call_metrics.json (2.1k tokens)
├── transformation.ts (800 tokens)
├── types.ts (400 tokens)
├── watchdog/
├── ClearcutSender.ts (1400 tokens)
├── main.ts (900 tokens)
├── third_party/
├── LIGHTHOUSE_MCP_BUNDLE_THIRD_PARTY_NOTICES (19k tokens)
├── devtools-formatter-worker.ts (100 tokens)
├── devtools-heap-snapshot-worker.ts (100 tokens)
├── index.ts (500 tokens)
├── lighthouse-devtools-mcp-bundle.js (1625.7k tokens)
├── tools/
├── ToolDefinition.ts (2.3k tokens)
├── categories.ts (200 tokens)
├── console.ts (600 tokens)
├── emulation.ts (400 tokens)
├── extensions.ts (600 tokens)
├── input.ts (3.4k tokens)
├── lighthouse.ts (800 tokens)
├── memory.ts (800 tokens)
├── network.ts (900 tokens)
├── pages.ts (3k tokens)
├── performance.ts (1700 tokens)
├── screencast.ts (700 tokens)
├── screenshot.ts (700 tokens)
├── script.ts (1300 tokens)
├── slim/
├── tools.ts (500 tokens)
├── snapshot.ts (500 tokens)
├── thirdPartyDeveloper.ts (700 tokens)
├── tools.ts (400 tokens)
├── webmcp.ts (400 tokens)
├── trace-processing/
├── parse.ts (700 tokens)
├── types.ts (100 tokens)
├── utils/
├── check-for-updates.ts (500 tokens)
├── files.ts (100 tokens)
├── id.ts (100 tokens)
├── keyboard.ts (800 tokens)
├── pagination.ts (400 tokens)
├── string.ts (300 tokens)
├── types.ts
├── version.ts
├── tests/
├── DevtoolsUtils.test.ts (500 tokens)
├── McpContext.test.js.snapshot (200 tokens)
├── McpContext.test.ts (2000 tokens)
├── McpResponse.test.js.snapshot (9.3k tokens)
├── McpResponse.test.ts (9.9k tokens)
├── PageCollector.test.ts (2.6k tokens)
├── TextSnapshot.test.ts (1000 tokens)
├── ToolHandler.test.ts (1100 tokens)
├── browser.test.ts (600 tokens)
├── check-for-updates.test.ts (1100 tokens)
├── cli.test.ts (1800 tokens)
├── daemon/
├── client.test.ts (800 tokens)
├── utils.test.ts (500 tokens)
├── e2e/
├── chrome-devtools-commands.test.ts (600 tokens)
├── chrome-devtools-disclaimers.test.ts (200 tokens)
├── chrome-devtools-start-stop.test.ts (400 tokens)
├── chrome-devtools-status.test.ts (200 tokens)
├── telemetry.test.ts (1400 tokens)
├── fixtures/
├── example.heapsnapshot (473.9k tokens)
├── formatters/
├── ConsoleFormatter.test.js.snapshot (2.5k tokens)
├── ConsoleFormatter.test.ts (3.4k tokens)
├── ConsoleFormatterGrouping.test.ts (1000 tokens)
├── HeapSnapshotFormatter.test.js.snapshot
├── HeapSnapshotFormatter.test.ts (500 tokens)
├── IssueFormatter.test.js.snapshot (200 tokens)
├── IssueFormatter.test.ts (1300 tokens)
├── NetworkFormatter.test.js.snapshot
├── NetworkFormatter.test.ts (3.2k tokens)
├── snapshotFormatter.test.js.snapshot (100 tokens)
├── snapshotFormatter.test.ts (1500 tokens)
├── index.test.js.snapshot (200 tokens)
├── index.test.ts (2.1k tokens)
├── roots.test.ts (300 tokens)
├── server.ts (600 tokens)
├── setup.ts (200 tokens)
├── snapshot.ts (100 tokens)
├── telemetry/
├── ClearcutLogger.test.ts (1900 tokens)
├── WatchdogClient.test.ts (700 tokens)
├── flagUtils.test.ts (1100 tokens)
├── metricsRegistry.test.ts (1700 tokens)
├── persistence.test.ts (1000 tokens)
├── transformation.test.ts (900 tokens)
├── watchdog/
├── ClearcutSender.test.ts (2.6k tokens)
├── third_party_notices.test.js.snapshot (42.9k tokens)
├── third_party_notices.test.ts (100 tokens)
├── tools/
├── console.test.js.snapshot (1200 tokens)
├── console.test.ts (4.6k tokens)
├── emulation.test.ts (3.7k tokens)
├── extensions.test.ts (1300 tokens)
├── fixtures/
├── extension-content-script/
├── content.js
├── manifest.json (100 tokens)
├── extension-side-panel/
├── manifest.json (100 tokens)
├── sidepanel.html
├── sw.js
├── extension-sw/
├── manifest.json
├── popup.html
├── sw.js
├── extension/
├── manifest.json
├── popup.html
├── input.test.ts (8.1k tokens)
├── lighthouse.test.js.snapshot (1400 tokens)
├── lighthouse.test.ts (1100 tokens)
├── memory.test.js.snapshot (1400 tokens)
├── memory.test.ts (800 tokens)
├── network.test.js.snapshot (300 tokens)
├── network.test.ts (1200 tokens)
├── pages.test.js.snapshot (600 tokens)
├── pages.test.ts (8.3k tokens)
├── pagesNavigateAllowlist.test.ts (600 tokens)
├── performance.test.js.snapshot (2.2k tokens)
├── performance.test.ts (3.2k tokens)
├── screencast.test.ts (1300 tokens)
├── screenshot.test.ts (1900 tokens)
├── script.test.ts (2.6k tokens)
├── slim/
├── tools.test.js.snapshot
├── tools.test.ts (500 tokens)
├── snapshot.test.ts (1000 tokens)
├── thirdPartyDeveloper.test.ts (4.9k tokens)
├── webmcp.test.ts (900 tokens)
├── trace-processing/
├── fixtures/
├── basic-trace.json.gz
├── load.ts (200 tokens)
├── web-dev-with-commit.json.gz
├── parse.test.js.snapshot (1800 tokens)
├── parse.test.ts (300 tokens)
├── utils.ts (2.2k tokens)
├── utils/
├── files.test.ts (300 tokens)
├── tsconfig.json (900 tokens)
```
## /.claude-plugin/marketplace.json
```json path="/.claude-plugin/marketplace.json"
{
"name": "chrome-devtools-plugins",
"version": "0.26.0",
"description": "Bundled plugins for actuating and debugging the Chrome browser.",
"repository": "https://github.com/ChromeDevTools/chrome-devtools-mcp",
"owner": {
"name": "Chrome DevTools Team",
"email": "devtools-dev@chromium.org"
},
"plugins": [
{
"name": "chrome-devtools-mcp",
"source": "./",
"description": "Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer"
}
]
}
```
## /.claude-plugin/plugin.json
```json path="/.claude-plugin/plugin.json"
{
"name": "chrome-devtools-mcp",
"version": "0.26.0",
"description": "Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer",
"mcpServers": {
"chrome-devtools": {
"command": "npx",
"args": [
"chrome-devtools-mcp@latest"
]
}
}
}
```
## /.gemini/settings.json
```json path="/.gemini/settings.json"
{
"context": {
"fileName": ["AGENTS.md", "GEMINI.md"]
}
}
```
## /.github/ISSUE_TEMPLATE/01-bug.yml
```yml path="/.github/ISSUE_TEMPLATE/01-bug.yml"
name: Bug report
description: File a bug report for chrome-devtools-mcp
title: '<short description of the bug>'
type: 'Bug'
body:
- id: description
type: textarea
attributes:
label: Description of the bug
description: >
A clear and concise description of what the bug is.
placeholder:
validations:
required: true
- id: reproduce
type: textarea
attributes:
label: Reproduction
description: >
Steps to reproduce the behavior:
placeholder: |
1. Use tool '...'
2. Then use tool '...'
- id: expectation
type: textarea
attributes:
label: Expectation
description: A clear and concise description of what you expected to happen.
- id: mcp-configuration
type: textarea
attributes:
label: MCP configuration
- id: chrome-devtools-mcp-version
type: input
attributes:
label: Chrome DevTools MCP version
validations:
required: true
- id: chrome-version
type: input
attributes:
label: Chrome version
- id: coding-agent-version
type: input
attributes:
label: Coding agent version
- id: model-version
type: input
attributes:
label: Model version
- id: chat-log
type: input
attributes:
label: Chat log
- id: node-version
type: input
attributes:
label: Node version
description: >
Please verify you have the minimal supported version listed in the README.md
- id: operating-system
type: dropdown
attributes:
label: Operating system
description: What supported operating system are you running?
options:
- Windows
- Windows Subsystem for Linux (WSL)
- macOS
- Linux
- type: checkboxes
id: provide-pr
attributes:
label: Extra checklist
options:
- label: I want to provide a PR to fix this bug
```
## /.github/ISSUE_TEMPLATE/02-feature.yml
```yml path="/.github/ISSUE_TEMPLATE/02-feature.yml"
name: Feature
description: Suggest an idea for for chrome-devtools-mcp
title: '<short description of the feature request>'
type: 'Feature'
labels:
- feature
body:
- id: description
type: textarea
attributes:
label: Is your feature request related to a problem? Please describe.
description: >
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
placeholder:
validations:
required: true
- id: solution
type: textarea
attributes:
label: Describe the solution you'd like
description: >
A clear and concise description of what you want to happen.
placeholder:
validations:
required: true
- id: alternatives
type: textarea
attributes:
label: Describe alternatives you've considered
description: >
A clear and concise description of any alternative solutions or features you've considered.
placeholder:
validations:
required: true
- id: additional-context
type: textarea
attributes:
label: Additional context
description: >
Add any other context or screenshots about the feature request here.
placeholder:
```
## /.github/ISSUE_TEMPLATE/03-task.yml
```yml path="/.github/ISSUE_TEMPLATE/03-task.yml"
name: Task
description: Work tracking for mainainers only!
title: '[Task]:'
type: 'Task'
body:
- type: markdown
attributes:
value: |
### This issue type should be used only by mainainers!
Task are to track small non user facing issue or improvements.
The issue will be closed if it does not follow those rules.
- type: textarea
attributes:
label: 'Task to do:'
id: task
validations:
required: true
```
## /.github/ISSUE_TEMPLATE/config.yml
```yml path="/.github/ISSUE_TEMPLATE/config.yml"
blank_issues_enabled: false
```
## /.github/dependabot.yml
```yml path="/.github/dependabot.yml"
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
day: 'sunday'
time: '02:00'
timezone: Europe/Berlin
groups:
dependencies:
dependency-type: production
patterns:
- '*'
dev-dependencies:
dependency-type: development
exclude-patterns:
- 'puppeteer*'
- 'chrome-devtools-frontend'
- '@modelcontextprotocol/sdk'
- 'yargs'
- 'debug'
- 'core-js'
patterns:
- '*'
# breaks often so better to roll separetely.
bundled-devtools:
patterns:
- 'chrome-devtools-frontend'
bundled:
patterns:
- 'puppeteer*'
- '@modelcontextprotocol/sdk'
- 'yargs'
- 'debug'
- 'core-js'
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
day: 'sunday'
time: '04:00'
timezone: Europe/Berlin
groups:
all:
patterns:
- '*'
```
## /.github/plugin/plugin.json
```json path="/.github/plugin/plugin.json"
{
"name": "chrome-devtools-mcp",
"version": "0.26.0",
"description": "Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer",
"mcpServers": {
"chrome-devtools": {
"command": "npx",
"args": [
"chrome-devtools-mcp@latest"
]
}
}
}
```
## /.github/workflows/conventional-commit.yml
```yml path="/.github/workflows/conventional-commit.yml"
name: 'Conventional Commit'
on:
merge_group:
pull_request_target:
types:
# Defaults
# https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#pull_request_target
- opened
- reopened
- synchronize
# Tracks editing PR title or description, or base branch changes
# https://docs.github.com/en/webhooks/webhook-events-and-payloads?actionType=edited#pull_request
- edited
jobs:
main:
name: '[Required] Validate PR title'
runs-on: ubuntu-latest
permissions:
pull-requests: read
steps:
- if: github.event_name != 'merge_group'
uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
## /.github/workflows/pre-release.yml
```yml path="/.github/workflows/pre-release.yml"
name: Pre-release
permissions: read-all
on:
workflow_dispatch:
push:
branches:
- release-please-*
jobs:
pre-release:
name: 'Verify artifacts before release'
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 2
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
cache: npm
node-version-file: '.nvmrc'
registry-url: 'https://registry.npmjs.org'
# Ensure npm 11.5.1 or later is installed
- name: Update npm
run: npm install -g npm@latest
- name: Install dependencies
run: npm ci
- name: Build and bundle
run: npm run bundle
env:
NODE_ENV: 'production'
- name: Verify server.json
run: npm run verify-server-json-version
- name: Verify npm package
run: npm run verify-npm-package
```
## /.github/workflows/presubmit.yml
```yml path="/.github/workflows/presubmit.yml"
name: Check code before submitting
permissions: read-all
on:
merge_group:
push:
branches:
- main
pull_request:
jobs:
check-format:
name: '[Required] Check correct format'
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 2
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
cache: npm
node-version-file: '.nvmrc'
- name: Install dependencies
run: npm ci
- name: Run format check
run: npm run check-format
check-docs:
name: '[Required] Check docs updated'
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 2
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
cache: npm
node-version-file: '.nvmrc'
- name: Install dependencies
run: npm ci
- name: Generate
run: npm run gen
- name: Check if autogenerated code and docs are out of date
run: |
diff_file=$(mktemp doc_diff_XXXXXX)
git diff --color > $diff_file
if [[ -s $diff_file ]]; then
echo "Please update the documentation by running 'npm run gen'. The following was the diff"
cat $diff_file
rm $diff_file
exit 1
fi
rm $diff_file
```
## /.github/workflows/publish-to-npm-on-tag.yml
```yml path="/.github/workflows/publish-to-npm-on-tag.yml"
name: publish-on-tag
on:
push:
tags:
- 'chrome-devtools-mcp-v*'
workflow_dispatch:
inputs:
npm-publish:
description: 'Try to publish to npm'
default: false
type: boolean
mcp-publish:
description: 'Try to publish to MCP registry'
default: true
type: boolean
permissions:
id-token: write # Required for OIDC
contents: read
jobs:
publish-to-npm:
runs-on: ubuntu-latest
if: ${{ (github.event_name != 'workflow_dispatch') || (inputs.npm-publish && always()) }}
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 2
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
cache: npm
node-version-file: '.nvmrc'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm ci
- name: Build and bundle
run: npm run bundle
env:
NODE_ENV: 'production'
- name: Publish chrome-devtools-mcp
run: |
npm publish --provenance --access public
- name: Publish chrome-devtools
run: |
node --input-type=module --eval '
import * as fs from "node:fs/promises";
const json = await fs.readFile("package.json", "utf8");
const pkg = JSON.parse(json);
pkg.name = "chrome-devtools";
await fs.writeFile("package.json", JSON.stringify(pkg, null, 2) + "\n");
'
echo 'This is the **Chrome DevTools for agents** package. Docs: https://github.com/ChromeDevTools/chrome-devtools-mcp' > README.md
npm publish --provenance --access public
publish-to-mcp-registry:
runs-on: ubuntu-latest
needs: publish-to-npm
if: ${{ (github.event_name != 'workflow_dispatch' && needs.publish-to-npm.result == 'success') || (inputs.mcp-publish && always()) }}
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 2
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
cache: npm
node-version-file: '.nvmrc'
registry-url: 'https://registry.npmjs.org'
# Ensure npm 11.5.1 or later is installed
- name: Update npm
run: npm install -g npm@latest
- name: Install dependencies
run: npm ci
- name: Build and bundle
run: npm run bundle
env:
NODE_ENV: 'production'
- name: Install MCP Publisher
run: |
export OS=$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_${OS}.tar.gz" | tar xz mcp-publisher
- name: Login to MCP Registry
run: ./mcp-publisher login github-oidc
- name: Publish to MCP Registry
run: ./mcp-publisher publish
```
## /.github/workflows/release-please.yml
```yml path="/.github/workflows/release-please.yml"
on:
push:
branches:
- main
permissions: read-all
name: release-please
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: googleapis/release-please-action@v5
with:
token: ${{ secrets.BROWSER_AUTOMATION_BOT_TOKEN }}
target-branch: main
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
```
## /.github/workflows/run-tests.yml
```yml path="/.github/workflows/run-tests.yml"
name: Compile and run tests
permissions: read-all
on:
merge_group:
push:
branches:
- main
pull_request:
jobs:
run-tests:
name: Tests on ${{ matrix.os }} with node ${{ matrix.node }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- windows-latest
- macos-latest
node:
- 20
- 22
- 23
- 24
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 2
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
cache: npm
node-version: 22 # build works only with 22+.
- name: Install dependencies
shell: bash
run: npm ci
- name: Build
run: npm run bundle
env:
NODE_OPTIONS: '--max_old_space_size=4096'
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
cache: npm
node-version: ${{ matrix.node }}
- name: Disable AppArmor
if: ${{ matrix.os == 'ubuntu-latest' }}
shell: bash
run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
- name: Run tests
shell: bash
# Retry tests if they fail in the merge queue.
run: npm run test:no-build -- ${{ github.event_name == 'merge_group' && '--retry' || '' }}
# Gating job for branch protection.
test-success:
name: '[Required] Tests passed'
runs-on: ubuntu-latest
needs: run-tests
if: ${{ !cancelled() }}
steps:
- if: ${{ needs.run-tests.result != 'success' }}
run: 'exit 1'
- run: 'exit 0'
```
## /.gitignore
```gitignore path="/.gitignore"
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
trace.json
trace.json.gz
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# vitepress build output
**/.vitepress/dist
# vitepress cache directory
**/.vitepress/cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# Stores VSCode specific settings
.vscode
!.vscode/*.template.json
!.vscode/extensions.json
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# Build output directory
build/
log.txt
.DS_Store
```
## /.mcp.json
```json path="/.mcp.json"
{
"mcpServers": {
"chrome-devtools": {
"command": "npx",
"args": ["chrome-devtools-mcp@latest"]
}
}
}
```
## /.npmrc
```npmrc path="/.npmrc"
package-lock=true
```
## /.nvmrc
```nvmrc path="/.nvmrc"
v24
```
## /.prettierrc.cjs
```cjs path="/.prettierrc.cjs"
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @type {import('prettier').Config}
*/
module.exports = {
bracketSpacing: false,
singleQuote: true,
trailingComma: 'all',
arrowParens: 'avoid',
singleAttributePerLine: true,
htmlWhitespaceSensitivity: 'strict',
endOfLine: 'lf',
};
```
## /.release-please-manifest.json
```json path="/.release-please-manifest.json"
{
".": "0.26.0"
}
```
## /AGENTS.md
This repository contains an MCP server and CLI for Chrome DevTools.
# Instructions
- Use only scripts from `package.json` to run commands.
- Use `npm run build` to run tsc and test build.
- Use `npm run test` to build and run tests, run all tests to verify correctness.
- Use `npm run test path-to-test.ts` to build and run a single test file, for example, `npm run test tests/McpContext.test.ts`.
- Use `npm run format` to fix formatting and get linting errors.
## Rules for TypeScript
- Do not use `any` type.
- Do not use `as` keyword for type casting.
- Do not use `!` operator for type assertion.
- Do not use `// @ts-ignore` comments.
- Do not use `// @ts-nocheck` comments.
- Do not use `// @ts-expect-error` comments.
- Prefer `for..of` instead of `forEach`.
## /CHANGELOG.md
# Changelog
## [0.26.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.25.0...chrome-devtools-mcp-v0.26.0) (2026-05-11)
### 🎉 Features
* add an error logging method ([#2006](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/2006)) ([06e0ab6](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/06e0ab602258edf90adafd954f9a7d55f5ca05e4))
### 🛠️ Fixes
* **cli:** allow --autoConnect on CLI start ([#2015](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/2015)) ([9882391](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/98823918dde184759f47095ce386705b0fabb653)), closes [#1859](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1859) [#1184](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1184)
* Make fill_form more appealing when filling forms with checkboxes ([#1971](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1971)) ([407c2bd](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/407c2bd03fbdc45a66690a32cfeac1937ce86f10))
* only require a page in page-scoped tools ([#2030](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/2030)) ([8e06761](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/8e06761572592764327da00ee653bd4ec2a4a30e))
* **telemetry:** re-run the update metrics script ([#2005](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/2005)) ([e9ec375](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/e9ec375f95622a21d9f9f8b1e8210436cc7695d5))
### 📄 Documentation
* Fix Claude Code instructions ([#2018](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/2018)) ([a5ad67b](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a5ad67bdafa60a774f9c7dea490393b9c845777b))
### 🏗️ Refactor
* extract ToolHandler ([#2032](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/2032)) ([178b790](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/178b79049318a63d1df1bd40e069f0627fa06fcc))
## [0.25.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.24.0...chrome-devtools-mcp-v0.25.0) (2026-05-06)
### 🎉 Features
* support third-party developer tools ([#1982](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1982)) ([7548c97](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/7548c97663b71f7ef6a5e41cccf38c6525887410))
### 🛠️ Fixes
* **input:** stop native select option clicks from timing out ([#1960](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1960)) ([510ec0f](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/510ec0f48bbfc7cad3d5d1f9805e901cc992ea89))
* make sure env variables are consistently applied when parsing args ([#1994](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1994)) ([f45f068](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/f45f0681a295e96a66c8247bbc1d9fe445ee04ac))
### 📄 Documentation
* extract WebMCP into its own category ([#1993](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1993)) ([da0441d](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/da0441d4250702898f5f07815ffb9c708393fe96))
* remove token estimates ([#2003](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/2003)) ([14938ac](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/14938acd089770ba32a124839c4b7c3a064c7320))
* update generate-docs.ts tools output order ([#1991](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1991)) ([895fc65](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/895fc65a1f2b426c8644baa0cda23ee5de98624e)), closes [#1932](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1932)
## [0.24.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.23.0...chrome-devtools-mcp-v0.24.0) (2026-05-02)
### 🎉 Features
* agentic browsing in lighthouse ([#1931](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1931)) ([5fa2750](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/5fa2750456d8ea5b73ca851c7c44dcec0a2be01e))
* **cli:** generate commands for conditional tools ([#1962](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1962)) ([b2b3e99](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b2b3e99d67e573e65a4cf84258da8560b2753405))
* group identical consecutive console messages in list_console_messages ([#1939](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1939)) ([dbddb2e](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/dbddb2e4efb01c25ce4c6d96fd45b3ab29a498eb))
* support MCP client roots feature ([#1945](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1945)) ([def53dd](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/def53ddf1d0d3fe04c41f1572919cef208161151))
### 🛠️ Fixes
* add proactive tool rejection when dialog is open ([#1978](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1978)) ([6ce254e](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/6ce254e54153212bf305e28846bc77f1d6b82b74))
* always allow tmpdir access with client roots ([#1984](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1984)) ([a90378a](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a90378adf3226e8b27a05cdcfdd801c199acaa93))
* **cli:** re-generate cli correctly ([#1969](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1969)) ([8cbdb8d](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/8cbdb8d49491e332c1d30a3884304e1f7a519db2))
* handle errors due to open dialogs during tool calls ([#1953](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1953)) ([06b331f](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/06b331f403056727850a633dd64b290d60bdb906))
* note about missing elements should not show in verbose mode ([#1950](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1950)) ([80bee1e](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/80bee1e6cd0e62995496ea9d001994c78ec9dcf0))
* **telemetry:** bucketize string length ([#1972](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1972)) ([bf3cb58](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/bf3cb58d27589ddc5156d7a6ee1b2bb81d9a2cee))
## [0.23.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.22.0...chrome-devtools-mcp-v0.23.0) (2026-04-22)
### 🎉 Features
* add an option to customize ffmpeg path ([#1937](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1937)) ([b377454](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b377454b1bcf9706e8d34c7241593b04f3635257))
* support experimental allowlist for navigate tool calls ([#1935](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1935)) ([d502557](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d50255778a6dcb30fb702755f0dd38f8ee2cd858))
* support webm format in screencast ([#1934](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1934)) ([85b8993](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/85b89931ed7d59ab939abe4c72a63ad02febc29c))
### 📄 Documentation
* clarify resource limitations around the number of tabs ([#1927](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1927)) ([42be7c3](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/42be7c32272448b72091d008d1a0edb9b1ad6ec7))
### 🏗️ Refactor
* add support for CLI sessionIds in tests ([#1919](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1919)) ([82b67b0](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/82b67b07d3c7b5a1ec22d21c2376d24d7393cb82))
## [0.22.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.21.0...chrome-devtools-mcp-v0.22.0) (2026-04-21)
### 🎉 Features
* add update notification to both binaries ([#1783](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1783)) ([e01e333](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/e01e33355e85c3b38e7aba6aceff57271b99a830))
* auto handle dialogs during script evaluation ([#1839](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1839)) ([da33cb5](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/da33cb5b957fb87bbbab67e4c1521535065881f1))
* ensure extensions for file outputs ([#1867](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1867)) ([e7a0d50](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/e7a0d509778578ceb8ba357f5857a86f95cfb533))
* experimental click_at(x,y) tool ([#1788](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1788)) ([c4f5471](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c4f54710d9d7c3d1167628e5135b4cf92beaec45))
* support Chrome extensions debugging ([#1922](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1922)) ([3ff21cf](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/3ff21cf30dae19a6af85d836b1b55314f53ff401))
* support DevTools header redactions as an option ([#1848](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1848)) ([5c398c4](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/5c398c4e7ce17facf62316fb1b617c39daa461ef))
* **webmcp:** Add experimental tool to execute WebMCP tool ([#1873](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1873)) ([0aff266](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/0aff266111408acfbce39e231c23ce866d0f26c0))
* **webmcp:** Add experimental tool to list WebMCP tools page exposes ([#1845](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1845)) ([f97b573](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/f97b573d70ec670df8bb2b42167e08681f3b488e))
### 🛠️ Fixes
* avoid showing update notification for local builds ([#1889](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1889)) ([3f0cf10](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/3f0cf1068ba35d81c800a81fc6272acaff715b41)), closes [#1886](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1886)
* **cli:** correct WebP MIME type check in handleResponse ('webp' → 'image/webp') ([#1899](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1899)) ([e3a5f6b](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/e3a5f6bb69f0dc4e626146d5b4165af97bad8fe4)), closes [#1898](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1898)
* ignore unmapped PerformanceIssue events ([#1852](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1852)) ([ea57e86](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/ea57e863f8b5b48a210c7a2fccd552f5824a7a96))
* **network:** trailing data in Network redirect chain ([#1880](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1880)) ([2f458c1](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/2f458c11ebbb4b8061e8e4375346e5449b222281))
* remove double space in navigate error message ([#1847](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1847)) ([429e0ca](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/429e0ca7b82568de1c0fab27dacb439b3898965c))
### 📄 Documentation
* clarify tools included into CLI ([#1925](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1925)) ([76ab9fa](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/76ab9fa5643dfa6eb93fcb50fe747a948e9a9d63))
* document network response and request extensions ([#1887](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1887)) ([796d6f2](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/796d6f2e242065de1e2cf27f729d66bc71676299))
* fix skill and reference documentation issues ([#1249](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1249)) ([9236834](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/92368345dd62fce0a65a1081f80c23790edbf7d1))
* Include Mistral Vibe setup in README ([#1801](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1801)) ([582c9e0](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/582c9e01e9a5ca1b9bb9e4b816662008430aaf2d))
* Rename project and enhance README content ([#1856](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1856)) ([c066488](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c0664883a5eb6a3e23bb0a48ea348e5cdab052f2))
* update the README on installing as a VS code agent plugin ([#1796](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1796)) ([1b5dcae](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/1b5dcae2a03a9de8c29c9f25a4d04cdfbad416a7))
### 🏗️ Refactor
* move waitForEventsAfterAction to McpPage ([#1780](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1780)) ([c7c8f50](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c7c8f50f802643fd90bd9d0419acfb1bb8dd58ad))
* use puppeteer Extension API ([#1911](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1911)) ([ec895f1](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/ec895f195aa21b36c1bf4373184f281b181ea3e9))
## [0.21.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.20.3...chrome-devtools-mcp-v0.21.0) (2026-04-01)
### 🎉 Features
* add a skill for detecting memory leaks using take_memory_snapshot tool ([#1162](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1162)) ([d19a235](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d19a2350f975ec2fbf8ee61b35163a48c0146c32))
### 🛠️ Fixes
* **cli:** avoid defaulting to isolated when userDataDir is provided ([#1258](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1258)) ([73e1e24](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/73e1e24b26f9e42a83116b586e34d47276a6a2fa))
* list_pages should work after selected page is closed ([#1145](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1145)) ([2664455](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/26644553028d8404fd65a005ea9c19a278671f4d))
* mark lighthouse and memory as non-read-only ([#1769](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1769)) ([bec9dae](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/bec9dae2d26b728feedcd660189f386e6228f3ae))
* **telemetry:** record client name ([9a47b65](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/9a47b657d7b17b9bc64508530c93d55e8033e2a6))
* versioning for Claude Code plugin ([#1233](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1233)) ([966b86f](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/966b86f3aaa9f87c2599b954c4e7f990c2a697ea))
* wrap .mcp.json config in mcpServers key ([#1246](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1246)) ([c7948cf](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c7948cf0621d80b080220d4cfd36b62bef2790b9))
### 📄 Documentation
* Command Code CLI instructions for MCP server ([0a7c0a7](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/0a7c0a74b471935a3e2f5ca0fd93556e8e5165ec))
* provide disclaimer about supported browsers ([#1237](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1237)) ([8676b72](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/8676b7216c66dfd323c2f6c272544a75dbab4dba))
### 🏗️ Refactor
* set process titles for easier debugging ([#1770](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1770)) ([0fe3896](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/0fe3896d85c74ce8b2dc189fe8a310727f795344))
## [0.20.3](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.20.2...chrome-devtools-mcp-v0.20.3) (2026-03-20)
### 🛠️ Fixes
* mark categoryExtensions flag mutually exclusive with autoConnect ([#1202](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1202)) ([8c2a7fc](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/8c2a7fc21ead6091567e85608f7916c001ccc7db)), closes [#1072](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1072)
### ⚡ Performance
* **memory:** release old navigation request in NetworkCollector ([#1200](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1200)) ([1e6456c](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/1e6456ce222a8f392341a530b2340336c7a1ab02))
* use CDP to find open DevTools pages (reland) ([#1210](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1210)) ([53483bc](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/53483bc637566658754d781d88f4353ad47f44a7))
## [0.20.2](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.20.1...chrome-devtools-mcp-v0.20.2) (2026-03-18)
### 📄 Documentation
* add troubleshooting for Claude Code plugin HTTPS clone failures ([#1195](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1195)) ([d082ca4](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d082ca4ecd35a023d09f9c1ff949d5fb0c3fb069))
## [0.20.1](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.20.0...chrome-devtools-mcp-v0.20.1) (2026-03-16)
### 🛠️ Fixes
* update VS Code manual installation powershell command ([#1151](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1151)) ([6c64a5b](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/6c64a5b543714796b25a12dc6f2be7a1e683e8bd))
### ⚡ Performance
* use CDP to find open DevTools pages. ([#1150](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1150)) ([94de19c](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/94de19cdcdae9e31d0962b273ce352dc248eb5a8))
## [0.20.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.19.0...chrome-devtools-mcp-v0.20.0) (2026-03-11)
### 🎉 Features
* experimental `chrome-devtools` CLI ([#1100](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1100)) ([1ac574e](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/1ac574e7154948e86e414e5149fb975a190d5bb0))
### 📄 Documentation
* fix typo ([#1155](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1155)) ([b59cabc](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b59cabcc1d59802ffd7d9667040188e46192357d))
* fix typos and improve phrasing ([#1130](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1130)) ([70d4f36](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/70d4f365dc619a5743e697c30800f7065bc6227d))
* revise contribution process and add release process ([#1134](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1134)) ([d7d26a1](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d7d26a103b840e2feb7cb9af6a242edda94f1ddf))
* **troubleshooting:** add symptom for missing tools due to read-only mode ([#1148](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1148)) ([57e7d51](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/57e7d51e8ca1e2ee325a9e7a9c64c033acbe6d6a))
* Update troubleshooting for MCP server connection errors ([#1017](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1017)) ([00f9c31](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/00f9c3108ab9caefca57998439052c728298920b))
### 🏗️ Refactor
* move main files ([#1120](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1120)) ([c2d8009](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c2d8009ff75f76bce1ec4cf79c2467b50d81725e))
## [0.19.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.18.1...chrome-devtools-mcp-v0.19.0) (2026-03-05)
### 🎉 Features
* add pageId routing for parallel multi-agent workflows ([#1022](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1022)) ([caf601a](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/caf601a32832bb87cfac801a6bbeacb87508412f)), closes [#1019](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1019)
* Add skill which helps with onboarding of the mcp server ([#1083](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1083)) ([7273f16](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/7273f16ec08f6d5b46a2693b0ad4d559086ded89))
* integrate Lighthouse audits ([#831](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/831)) ([dfdac26](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/dfdac2648e560d756a8711ad3bb1fa470be8e7c9))
### 🛠️ Fixes
* improve error messages around --auto-connect ([#1075](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1075)) ([bcb852d](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/bcb852dd2e440b0005f4a9ad270a1a7998767907))
* improve tool descriptions ([#965](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/965)) ([bdbbc84](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/bdbbc84c125bdd48f4be48aa476bec0323de611c))
* repair broken markdown and extract snippets in a11y-debugging skill ([#1096](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1096)) ([adac7c5](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/adac7c537ee304f324c5e7284fb363396d1773f5))
* simplify emulation and script tools ([#1073](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1073)) ([e51ba47](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/e51ba4720338951e621585b77efc6a0e07678d99))
* simplify focus state management ([#1063](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1063)) ([f763da2](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/f763da24a10e27605c0a5069853ce7c92974eec2))
* tweak lighthouse description ([#1112](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1112)) ([5538180](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/55381804ae7ffa8a1e5933b621a9b8390b3000ff))
### 📄 Documentation
* Adapt a11y skill to utilize Lighthouse ([#1054](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1054)) ([21634e6](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/21634e660c346e469ae62116b1824538f51567dd))
* add feature release checklist to CONTRIBUTING.md ([#1118](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1118)) ([0378457](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/03784577ffb6e238bcb2d637bff9ad759723ea7b))
* fix typo in README regarding slim mode ([#1093](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1093)) ([92f2c7b](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/92f2c7b48b56a6b1d6ac7c9e2f2e92beb26bcf62))
### 🏗️ Refactor
* clean up more of the context getters ([#1062](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1062)) ([9628dab](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/9628dabcb4d39f0b94d152a0fc419e049246a29d))
* consistently use McpPage in tools ([#1057](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1057)) ([302e5a0](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/302e5a04191ba0558e3c79f1486d01d5eb0f6896))
* improve type safety for page scoped tools ([#1051](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1051)) ([5f694c6](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/5f694c60ffd21f8b022554c92b2ad4cbdb457375))
* make cdp resolvers use McpPage ([#1060](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1060)) ([d6c06c5](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d6c06c56a7b8e4968318adc9fc7c820ace9f5bd9))
* move dialog handling to McpPage ([#1059](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1059)) ([40c241b](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/40c241bbfc80d6282953ab325b30a597d3d85ade))
* move server to a separate file ([#1043](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1043)) ([a8bf3e5](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a8bf3e585682c3126dfd378e9f98b5dc7ab6045d))
* remove page passing via context ([#1061](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1061)) ([4cb5a17](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/4cb5a17b57f57d8a367cd423c960ba122b9952e3))
* set defaults to performance trace tool ([#1090](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1090)) ([dfa9b79](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/dfa9b79a4ecc9e67f5b043f2dd97f6889d1fee0b))
* simplify the response texts ([#1095](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1095)) ([cb0079e](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/cb0079efbbd41874f6913772fe3f2a037e9f5f8f))
* type-cast as internal CdpPage interface ([#1064](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1064)) ([2d5e4fa](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/2d5e4fa3579650a384ff21c88c2e6b9cda031e1a))
## [0.18.1](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.18.0...chrome-devtools-mcp-v0.18.1) (2026-02-25)
### 🛠️ Fixes
* remove endsWith for filePath in memory tools ([#1041](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1041)) ([d0622d5](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d0622d52d46ac72a28bc22f93a337fb5007214c7))
## [0.18.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.17.3...chrome-devtools-mcp-v0.18.0) (2026-02-24)
### 🎉 Features
* `--slim` mode for maximum token savings ([#958](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/958)) ([c402b43](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c402b43697d834994c4fc141305189082da14bee))
* add a new skill for accessibility debugging and auditing with Chrome DevTools MCP. ([#1002](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1002)) ([b0c6d04](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b0c6d042e4d68763acf989edc8097ce07e85dc7a))
* add experimental screencast recording tools ([#941](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/941)) ([33446d4](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/33446d457e4386fadcfe4ddf6c7a43b2e9098c9a))
* add skill to debug and optimize LCP ([#993](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/993)) ([2cd9b95](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/2cd9b95346226aa52cce18f6ab889a2ae194806c))
* add storage-isolated browser contexts ([#991](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/991)) ([59f6477](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/59f6477a70eb07585e9a510089f1dfc840a012fd))
* add take_memory_snapshot tool ([#1023](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1023)) ([7ffdc5e](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/7ffdc5ee4d9df9f62f03354fa758fb4d022c3b08))
* support any-match text arrays in wait_for ([#1011](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1011)) ([496ab1b](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/496ab1b45f7a283a1432643777e0795a17f33667))
* support type_text ([#1026](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1026)) ([b5d01b5](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b5d01b5fe65fa20f9b76555b86a749960a5d1738))
### 🛠️ Fixes
* detect X server display on Linux ([#1027](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1027)) ([1746ed9](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/1746ed9ee11c212f78dcbb00af99a0400595e778))
### ♻️ Chores
* cleanup string and structured console formatters ([#1005](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1005)) ([0d78685](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/0d78685a5b37dc68bb11a1088ff8816ecff3bb82))
* extract version in a seprate file ([#1032](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1032)) ([0106865](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/0106865aad6d51b6cb590bf98ccaf7078e8d7436))
* move emulation settings to context ([#1000](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1000)) ([bc3c40e](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/bc3c40e8f961433fb2ae858482d66f9a55fdde32))
* optimize slim tool descriptions and params ([#1028](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1028)) ([ca6635d](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/ca6635d5a5d5e8b7b9944fa8b4e1063e6269a5f2))
* simplify JavaScript code examples, update code block language, and refine descriptions in a11y debugging skill documentation. ([#1009](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1009)) ([5cedcaa](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/5cedcaad2c8a5e488064e21fb56cbd8643345440))
* types for JSON output of IssueFormatter ([#1007](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1007)) ([9ef4479](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/9ef4479bec39c5f2651d6ebb63e9ec0fecf8bf89))
## [0.17.3](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.17.2...chrome-devtools-mcp-v0.17.3) (2026-02-19)
### 🛠️ Fixes
* remove clean from prepare ([#997](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/997)) ([2016b98](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/2016b98217bf5aa8d65c6668b1e46c8a3400276f))
## [0.17.2](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.17.1...chrome-devtools-mcp-v0.17.2) (2026-02-19)
### 🛠️ Fixes
* check that combobox is actually a select element before filling out options ([#979](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/979)) ([d2bc489](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d2bc489e4351551ba62a104433839c4198ecae84))
* handle network request pagination correctly ([#980](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/980)) ([0d9f422](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/0d9f422201538aa847a50417f1ed370e3a6c95b2))
### 📄 Documentation
* Add a note about previously installed server installations ([#982](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/982)) ([c0009f7](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c0009f7ab2f15bedd1c4ceb609db77bcb3c96f2d))
* update codex doc URL ([#987](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/987)) ([ebbbea7](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/ebbbea7f9d20e4dea902d06e9b86dfe1cc9b221f))
### ♻️ Chores
* **network:** de-duplicate String and JSON formatters ([#985](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/985)) ([1896dbb](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/1896dbb5a7cdc3fc0bcc5e665aee986a1180b014))
* remove text from the status code for Network requests ([#778](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/778)) ([327a388](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/327a3884d8443b8591c06ddb3f9081771ae973c3))
## [0.17.1](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.17.0...chrome-devtools-mcp-v0.17.1) (2026-02-16)
### 📄 Documentation
* Add 'Progressive Complexity' and 'Reference over Value' design principles. ([#939](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/939)) ([8d765c0](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/8d765c0aef7bbcd476c7e7fbe9ea63ee26cf4fa6))
* add Katalon Studio setup instructions to README ([#929](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/929)) ([6cfef24](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/6cfef24ec734ed62221c66bdf03b09ce000f5bfe))
* add MCP config for Claude plugin + docs ([#944](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/944)) ([a781da4](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a781da4434c3490901b28017bc7aa40493ef8dcc))
* estimate tokens using tiktoken ([#959](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/959)) ([fd0a919](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/fd0a9193b37be4c5cda21dc4904093c7b58d61be))
* improve Claude Code installation instructions ([#947](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/947)) ([3ec5b7e](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/3ec5b7e7a2d97c9f0165c5af3317c531a9dc058f))
* Update README with WSL configuration details ([#946](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/946)) ([107c46a](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/107c46a4dbd2ba7c7b9217a75ae2b1871d3c7f0d))
### ♻️ Chores
* rename files to have more consistent style ([#935](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/935)) ([9e1f9ac](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/9e1f9ac69667ddc3e2917e2c30e5ee940a03d853))
## [0.17.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.16.0...chrome-devtools-mcp-v0.17.0) (2026-02-10)
### 🎉 Features
* include Error.cause chain for uncaught errors and logged Errors ([#906](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/906)) ([05b01ec](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/05b01ecaba47cf1ce38564636663222c9cab46de))
* Integrate CrUX data into performance trace summaries ([#733](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/733)) ([b747f9d](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b747f9d74a12d2119b6531476b2f88ab66be0ff8))
* show message and stack trace in details when console.log'ging Error objects ([#902](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/902)) ([ffa00da](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/ffa00dab1b65b2eac8db215e0289317b8ed9b725))
### 🛠️ Fixes
* console formatter hides frames from ignored scripts ([#927](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/927)) ([8e2380b](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/8e2380b434d9659ffa8a7043d2589261772fa04f))
* limit stack traces to 50 lines ([#923](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/923)) ([caea23a](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/caea23a7cf33c87cd4ce426eb2a10724aba3cc71))
### 📄 Documentation
* add macOS Web Bluetooth troubleshooting note ([#930](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/930)) ([3c9528b](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/3c9528b43d9bbff166fcfcfee321149ff44ddd21)), closes [#917](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/917)
## [0.16.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.15.1...chrome-devtools-mcp-v0.16.0) (2026-02-04)
### 🎉 Features
* include source-mapped stack trace for uncaught errors ([#876](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/876)) ([ecef712](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/ecef712e70b47ae81eb3364d0aed801ec1c91a70))
### 🛠️ Fixes
* accidental extra typing in the fill tool ([#886](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/886)) ([3d6e59d](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/3d6e59dda42be3c6fd97446344a28cbbaa5809b3))
* update evaluateScript description formatting ([#880](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/880)) ([24db9dd](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/24db9dd78cd4f054d291322685b4f47601da3f5a))
* use 1-based line/column and fix wasm offsets in stack frames ([#884](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/884)) ([7e1ec81](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/7e1ec81fb63ec8b7c6d77dbdc88beef4240243ba))
### 📄 Documentation
* mention source-mapped stack traces in 'Key features' ([#883](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/883)) ([579d18a](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/579d18a3f4d1d8d05bf267a39de7f2f53e719b17))
* remove outdated --channel=beta note ([#882](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/882)) ([acdb5c9](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/acdb5c9bb3f249c5a9ce1d4a3e84c580af999141))
## [0.15.1](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.15.0...chrome-devtools-mcp-v0.15.1) (2026-01-30)
### 🛠️ Fixes
* disable usage statistics when CI or CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS env is set ([#862](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/862)) ([c0435a2](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c0435a2d53eb51b7500fc5cce50344520ea164e7))
* respect custom timeouts in navigate tools ([#865](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/865)) ([a0aeb97](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a0aeb97693fd5ca641f45ebcd4ce3b4b08ce21b8))
## [0.15.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.14.0...chrome-devtools-mcp-v0.15.0) (2026-01-28)
### 🎉 Features
* Add ability to inject script to run on page load ([#568](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/568)) ([d845ad4](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d845ad48584a49aa57b11de308beeb17ed0b2e10))
* enable usage statistics by default with opt-out ([#855](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/855)) ([7e279f1](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/7e279f1b67c5cfd4ad033a4147c51fe20a7833f7))
* support testing light and dark mode ([#858](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/858)) ([5a23a8c](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/5a23a8c201d30d40395e283f4434d933826333fa))
## [0.14.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.13.0...chrome-devtools-mcp-v0.14.0) (2026-01-27)
### 🎉 Features
* add a skill for using chrome-devtools-mcp ([#830](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/830)) ([aa0a367](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/aa0a3679f59ab441908d31252afee1cd56102da8))
* add background parameter to new_page tool ([#837](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/837)) ([d756888](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d7568881ba4aa0e2c10dc6148fd0ef941fee10d5))
* allow skipping snapshot generation for input tools ([#821](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/821)) ([4b8e9f2](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/4b8e9f287572e0a95c30b5ca612acf08bf79595b))
* include stack trace in 'get_console_message' tool ([#740](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/740)) ([a3a0021](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a3a00210a30f78045244bc897ee736bdbdc36007))
* support device viewport and user agent emulation ([#798](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/798)) ([a816967](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a8169676f920f88965a2574f53affe15c1278b43))
* support filePath for network request and response bodies ([#795](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/795)) ([6d0e4ca](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/6d0e4cab28a8498c2783c1c0c6436c655de7b336))
### 🛠️ Fixes
* handle beforeunload dialogs in navigations ([#788](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/788)) ([9b21f8b](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/9b21f8b2e972f78f58c6f633851466356330c77d))
* improve error handling for console messages ([#844](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/844)) ([dc43ede](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/dc43ede1f20302bd2feb706e63bcf992b4a66a96))
* improve error reporting when retrieving the element ([#845](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/845)) ([f7dd003](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/f7dd00340a8ac5af7fbe4922f2a1d27d99d933cc))
* improve performance tool description ([#800](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/800)) ([aa9a176](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/aa9a1769568aca2a357f186b2e80b38b2ed76323))
* increase timeouts for long text input ([#787](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/787)) ([a83a338](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a83a33835148905b538b39be93f6115774f91696))
* make request and response handling more robust ([#846](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/846)) ([695817f](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/695817f6d6da5fcb94934fb1c2be8b006522f53b))
* re-use node ids across snapshots ([#814](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/814)) ([a6cd2cd](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a6cd2cd3f2bd823f0e044d7796fd8ff2c100cda3))
### 📄 Documentation
* add a mention of evals into contributing.md ([#773](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/773)) ([9a31ac7](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/9a31ac7abab5890d11fec627bbdcbb8051452453))
* document how to add extensions to gemini-cli ([#834](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/834)) ([0610d11](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/0610d11aa9add484951b76adef557eed5e2bd275))
* update auto-connect docs ([#779](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/779)) ([a106fba](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a106fbadbc1a487ce4c53a9eb783c98e524c0a9e))
* Update README.md to include a link to Android debugging ([#783](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/783)) ([6e52e66](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/6e52e66a7a7ebbf1f2e2080a857f72192036eb0c))
## [0.13.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.12.1...chrome-devtools-mcp-v0.13.0) (2026-01-14)
### 🎉 Features
* Allow opting out of default Chrome launch arguments ([#729](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/729)) ([9a51af2](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/9a51af219fc9216cd463bef9363716283f41f36a))
* support filePath in performance tools ([#686](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/686)) ([68ae2f8](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/68ae2f8253e2ba5c34436e25df114874c537f6df))
### 🛠️ Fixes
* support resize_page when browser window is maximized/fullscreenwindow state ([#748](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/748)) ([4d9ac22](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/4d9ac227ddff6fc4aec44e46673f6e44a8168db9))
* use relative path for plugin source in marketplace ([#724](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/724)) ([5c1ecf8](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/5c1ecf835ac8aad4947d0a8f82c899acd4115b64))
### 📄 Documentation
* add experimental chrome on android guide ([#691](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/691)) ([4a87702](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/4a87702ca6913ed62987f71e080f3d481d13b8d8))
* autoConnect - clarify how the mcp server selects a profile ([#693](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/693)) ([28b8ff8](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/28b8ff816461760c82e9b19b70f288bc7fa2fa38))
* claude code broken link ([#707](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/707)) ([1f532b8](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/1f532b8fafa0fa60aaf94c302bad663fab1c12ea))
* enhance cli docs + sort required vs opt params ([#674](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/674)) ([81cbd99](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/81cbd99f52d013d07bdcf21a0840f61a16bacd33))
* update auto connect docs to mention min Chrome version ([#681](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/681)) ([ab2340f](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/ab2340f40127dcdabde6887a411163ce9d130394))
* Update Claude Code instructions in README.md ([#711](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/711)) ([f81cd2d](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/f81cd2d8dfc35da8c718b227e0ee4c4d7c5daca8))
* update readme to include OpenCode example ([#560](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/560)) ([fbba3c9](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/fbba3c9461cec8113216fa4569e879c85312ea29))
### ♻️ Chores
* change pageIdx to page ids ([#741](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/741)) ([a23c6ba](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a23c6ba8c9e1da90c885e68946635a8cc536a11e))
## [0.12.1](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.12.0...chrome-devtools-mcp-v0.12.1) (2025-12-12)
### 🛠️ Fixes
* catch unexpected error in event handlers ([#672](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/672)) ([ca0f560](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/ca0f5607f18bf04134e85ea1f61d1a839a47827b))
* log unhandledRejection instead of crashing ([#673](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/673)) ([f59b4a2](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/f59b4a2ed8b09e1d64916552ee6db49b978fe9a7))
* make bringToFront optional in select_page ([#668](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/668)) ([ceae17b](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/ceae17be26b0a812f1b013dcebaed9beb510e7b3))
* Update installation badges in README.md for VS Code ([#660](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/660)) ([61ede1c](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/61ede1c0531ea8b028d9a5cbb28fcdc00cc521e0))
### 📄 Documentation
* Add debug instructions ([#670](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/670)) ([a8aae66](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a8aae6652e205b87ac2efa29217b7cbd18dcbbe6))
* explain new auto connection feature ([#664](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/664)) ([a537a8c](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a537a8c8cef4f2a3493e9f7de47345d565b6fc9f))
## [0.12.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.11.0...chrome-devtools-mcp-v0.12.0) (2025-12-09)
### 🎉 Features
* support --auto-connect to a Chrome instance ([#651](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/651)) ([6ab6d85](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/6ab6d85d50226cf12a62563430f552e783f428b2))
* support --user-data-dir with --auto-connect ([#654](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/654)) ([e3c59bc](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/e3c59bcd9c284f3be99cc15e22116b887f04cdab))
### 🛠️ Fixes
* map channel for resolveDefaultUserDataDir ([#658](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/658)) ([6f59b39](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/6f59b3975abda50536f8b890f3245662b22e3657))
### 📄 Documentation
* Add AX design principles ([#643](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/643)) ([90ed192](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/90ed192c558d36faf9f6300be1c1fd5abd464d8a))
* improve autoConnect docs ([#653](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/653)) ([09111cc](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/09111cc16464bed27cd623f3b345d3885db12521))
## [0.11.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.10.2...chrome-devtools-mcp-v0.11.0) (2025-12-03)
### 🎉 Features
* **emulation:** add geolocation emulation tool ([#634](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/634)) ([3991e4c](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/3991e4c2a9c28bf8180f9057ce804d978c39529d))
* integrate DevTools issues into the console tools ([#636](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/636)) ([d892145](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d8921453c77a1c0815059fb9bc72c0cd769a7bd4))
* support --user-data-dir ([#622](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/622)) ([fcaf553](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/fcaf55354c2afbdbae538e27eb4b6d02f2e87985))
### 🛠️ Fixes
* handle error messages that are not instanceof Error ([#618](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/618)) ([a67528a](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a67528a046746c7131d5265f6c94613d607aaf90))
* handle the case when all pages are filtered out ([#616](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/616)) ([bff5c65](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/bff5c6569003fdbc207448d89a8be6a9a8172ca0))
* ignore hash parts of URLs when finding DevTools ([#608](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/608)) ([52533d0](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/52533d0c695354b816807de253f0ec17099aa9d7))
* ignore quality for png ([#589](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/589)) ([2eaf268](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/2eaf2689c3360f88479f4cdab8ddde5899378e33))
* include a note about selected elements missing from the snapshot ([#593](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/593)) ([80e77fd](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/80e77fd9a35a3dc5c451cc5b070b8baa574c686c))
* prevent dropping license notices on some files when publishing ([#604](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/604)) ([94752ff](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/94752ffade847671ebfd15e4013a5b5cdf8377df))
* rename page content to latest page snapshot ([#579](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/579)) ([9cb99ad](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/9cb99ad3e65054f4ea12a39358719f6630a020d0))
* **wait_for:** respect the provided timeout ([#630](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/630)) ([6b0984a](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/6b0984aa7dca6f651afd1fed56246893810781c9)), closes [#624](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/624)
### 📄 Documentation
* add Antigravity config ([#580](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/580)) ([6f9182f](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/6f9182f4b60f1f6ff8d321fec35545712828686e))
* add Qoder CLI to the MCP client configuration section in the README. ([#552](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/552)) ([1a16f15](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/1a16f15546e227a0708f89d3084c98d4916db53f))
* add VS Code install badges ([#532](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/532)) ([cc4d065](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/cc4d065dd6081a2a9fbcc3d8ebb1536e5426116e))
* clarify browser-url parameter in README ([#613](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/613)) ([05cf8cb](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/05cf8cb8a6c68506282075bc1522c81f0b84f07b))
* Fix Antigravity docs ([#605](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/605)) ([fae2608](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/fae260888748ece77b368a13ee913153caffcef7))
* update readme to explain agy's browser integration ([#612](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/612)) ([2d89865](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/2d89865ddbff6e77332c6157f687dcc2f0bef892))
### ♻️ Chores
* avoid throwing in resolveCdpElementId ([#606](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/606)) ([eb261fd](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/eb261fd48b6753db246d24b77e1f477dc7a9455e))
## [0.10.2](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.10.1...chrome-devtools-mcp-v0.10.2) (2025-11-19)
### 📄 Documentation
* add Factory CLI configuration to MCP clients ([#523](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/523)) ([016e2fd](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/016e2fd6ee57447103f7385285dd503b5576a860))
### ♻️ Chores
* clear issue aggregator on page navigation ([#565](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/565)) ([c3784d1](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c3784d1990a926f651951e4eef05520c5c448964))
* disable issues in list_console_messages for now ([#575](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/575)) ([08e9a9f](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/08e9a9f42e6ff1a92c60b3e958b0817c7b785afc))
* simplify issue management ([#564](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/564)) ([3b016f1](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/3b016f1a814b1a69750813548b3f35e79bfb6fef))
## [0.10.1](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.10.0...chrome-devtools-mcp-v0.10.1) (2025-11-07)
### 🛠️ Fixes
* avoid no page selected errors ([#537](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/537)) ([4724bbb](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/4724bbba9327fc162cd1f0372e608f6ebefc59cc))
## [0.10.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.9.0...chrome-devtools-mcp-v0.10.0) (2025-11-05)
### 🎉 Features
* add a press_key tool ([#458](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/458)) ([b427392](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b4273923928704e718e0a0f8b5cc86758416e994))
* add insightSetId to performance_analyze_insight ([#518](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/518)) ([36504d2](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/36504d29caf637b2d7bf231204c0478b54220c83))
* an option to ignore cache on reload ([#485](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/485)) ([8e56307](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/8e56307d623fe3651262287b30544ed70426b0b8))
* detect network requests inspected in DevTools UI ([#477](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/477)) ([796aed7](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/796aed72b7126ed4332888ffbc06d6cb678265ef))
* fetch DOM node selected in the DevTools Elements panel ([#486](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/486)) ([4a83574](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/4a83574961d8d6b974037db56fc8bdbbb91f79b6))
* support page reload ([#462](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/462)) ([d177087](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d17708798194486b2571092aa67838085da7231e))
* support saving snapshots to file ([#463](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/463)) ([b0ce08a](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b0ce08ae2ce422813fef3f28c18f2cb6c976d9fc))
### 🛠️ Fixes
* Augment fix to prevent stray OGS frames in NTP from causing hangs. ([#521](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/521)) ([d90abd4](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d90abd4e9e534417622d7f4676e9c3dbeb39ea8d))
* improve get_network_request description ([#500](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/500)) ([2f448e8](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/2f448e84ea8d3a44687c74b3577edf882ef2c19f))
* work around NTP iframes causing hangs ([#504](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/504)) ([cca5ff4](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/cca5ff471c2d2c663e63ade1e2ea58f9a7f5a2cd))
### 📄 Documentation
* add Windsurf to the editor config README ([#493](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/493)) ([63a5d82](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/63a5d824c2d914c9007e2b837fa292f5ba74ceed))
* fix typos in README.md exlcude -> exclude ([#513](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/513)) ([8854a34](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/8854a3400c3a6b84c761bf8ed82769fc2dec7366))
* remove unnecessary replace ([#475](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/475)) ([40e1753](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/40e1753d2e874bb22005dbebdb551da304a80033))
### ♻️ Chores
* connect to DevTools targets by default ([#466](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/466)) ([a41e440](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a41e4407996b8090f8cccc85f6c4696006fc31ec))
* detect DevTools page for each page ([#467](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/467)) ([1560ff2](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/1560ff23cad28ab63c1cf9fb1b961db886bc4a3e))
* merge emulate tools into one ([#494](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/494)) ([c06f452](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c06f4522ee8f762b59c60c2fd23a0deaaa544766))
## [0.9.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.8.1...chrome-devtools-mcp-v0.9.0) (2025-10-22)
### 🎉 Features
* add claude marketplace and plugin json ([#396](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/396)) ([0498611](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/0498611429f769c6ccae365674003d2bd538c292))
* add filters and pagination to the console messages tool ([#387](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/387)) ([15d942c](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/15d942c4f3335b35f1cba8e8634651688323663d))
* add WebSocket endpoint and custom headers support ([#404](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/404)) ([41d6a10](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/41d6a107baee0d14a1c14573f958d44198de23aa))
* allow configuring tool categories ([#454](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/454)) ([0fe2b8a](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/0fe2b8a2b4d64b9da5f7d1adccc5425fd7cbec34))
* expose previous navigations to the MCP ([#419](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/419)) ([165cf9c](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/165cf9c70b7f91dc116558547a870281f29da710))
* support previous navigation for Console messages ([#452](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/452)) ([6f24362](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/6f243620391f0c608f51d464257cf3222d653e9e))
* support stable id for network requests ([#375](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/375)) ([f4d7b49](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/f4d7b49bb112b4336bef0d90059485f41f71e4f1))
* support verbose snapshots ([#388](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/388)) ([d47aaa9](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d47aaa96ff990c49dd07a481ea1924f85881eafa))
* tool to get a verbose single console message ([#435](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/435)) ([9205593](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/92055933dc44e5d200dda2ee4ae0e365b24281bb))
* use stable id for network request querying ([#382](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/382)) ([579819b](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/579819b5e76f7a34c7c5c0877ac1e5e284beb328))
### 🛠️ Fixes
* allow evaluating in Frames ([#443](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/443)) ([053f1f8](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/053f1f830d051ec415f4b00e645f5a1aff8554a1))
* better wording for evaluate_script ([#392](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/392)) ([2313fda](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/2313fdacad72a1bc5c4d8f1cbdd80fd64ba91771))
* indicate when request and response bodies are not available ([#446](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/446)) ([7d47d6b](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/7d47d6b2f40bf08def29de3ca37b1a4a28ce6777))
* pageerror for non-error types ([#442](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/442)) ([b6b42ec](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b6b42ecb998dd4f8fbf4a8e7a49f461333a41103))
* retrieve data correctly with fewer than 3 navigations ([#451](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/451)) ([4c65f59](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/4c65f59cf9f62662cf903fbbd19b67a8828d674a))
### 📄 Documentation
* add instructions for Qoder ([#386](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/386)) ([d8df784](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d8df784127afd590eb02e0060378465ae115a7a4))
* add VM-to-host remote debugging workaround ([#399](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/399)) ([9f9dab0](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/9f9dab0787f19c5730b65daf148c382fb2d9e365))
* Update Copilot CLI instructions ([#423](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/423)) ([c7733a8](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c7733a818050e50830c9a8e3d62bb80892cf9121))
### ♻️ Chores
* bundle all dependencies together ([#450](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/450)) ([914b980](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/914b980113353fd41b301da397aa45975090487a))
* bundle modelcontextprotocol-sdk ([#409](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/409)) ([6c8432b](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/6c8432b6b69d5d56d0dee01968882492033f2dc1))
* bundle puppeteer-core ([#417](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/417)) ([b443033](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b443033000e46a992ea7fa071af0f9ec304b9ea7))
* bundle zod together with modelcontextprotocol/sdk ([#414](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/414)) ([800e7e8](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/800e7e836433f3f1b2bfafa12ed35a991404d270))
* cleanup data fetching ([#441](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/441)) ([5c871c3](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/5c871c3bd98127996011f269faddd8d8e7163917))
* dispose listeners on page destroyed ([#318](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/318)) ([76d5e94](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/76d5e9416d833299561242ac45c0ce7813e61dbe))
* extract common paginate type ([#415](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/415)) ([29fd602](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/29fd60216ca1394c46a266c6f853f8d65418e861))
* store the last 3 navigations in PageCollector ([#411](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/411)) ([b873822](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b8738221d8cf8322d5f968ee829f03dc83238a05))
* use different format for reqid ([#380](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/380)) ([78bf66a](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/78bf66a7b1eefc93768f39d6d38fd141104fe812))
## [0.8.1](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.8.0...chrome-devtools-mcp-v0.8.1) (2025-10-13)
### Bug Fixes
* add an option value to the snapshot ([#362](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/362)) ([207137e](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/207137edd6d8af2f49277d88a30d8afa51671631))
* improve navigate_page_history error messages ([#321](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/321)) ([0624029](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/0624029e0f8735345d202d29dde446b8869d9561))
* return the default dialog value correctly ([#366](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/366)) ([f08f808](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/f08f8080d0be1074a48e5c2ab0a6533f01f65928))
* update puppeteer to 24.24.1 ([#370](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/370)) ([477eef4](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/477eef481a2e6241121ee4aaaed34e8342a8b347))
## [0.8.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.7.1...chrome-devtools-mcp-v0.8.0) (2025-10-10)
### Features
* support passing args to Chrome ([#338](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/338)) ([e1b5363](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/e1b536365363e1e1a3aa7661dd84290c794510ad))
## [0.7.1](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.7.0...chrome-devtools-mcp-v0.7.1) (2025-10-10)
### Bug Fixes
* document that console and requests are since the last nav ([#335](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/335)) ([9ad7cbb](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/9ad7cbb2de3d285e46e5f3e7c098b0a7535c7e7a))
## [0.7.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.6.1...chrome-devtools-mcp-v0.7.0) (2025-10-10)
### Features
* Add offline network emulation support to emulate_network command ([#326](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/326)) ([139ce60](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/139ce607814bf25ba541a7264ce96a04b2fac871))
* add request and response body ([#267](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/267)) ([dd3c143](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/dd3c14336ee44d057d06231a5bfd5c5bcf661029))
### Bug Fixes
* ordering of information in performance trace summary ([#334](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/334)) ([2d4484a](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/2d4484a123968754b4840d112b9c1ca59fb29997))
* publishing to MCP registry ([#313](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/313)) ([1faec78](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/1faec78f84569a03f63585fb84df35992bcfe81a))
* use default ProtocolTimeout ([#315](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/315)) ([a525f19](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a525f199458afb266db4540bf0fa8007323f3301))
## [0.6.1](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.6.0...chrome-devtools-mcp-v0.6.1) (2025-10-07)
### Bug Fixes
* change default screen size in headless ([#299](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/299)) ([357db65](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/357db65d18f87b1299a0f6212b7ec982ef187171))
* **cli:** tolerate empty browser URLs ([#298](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/298)) ([098a904](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/098a904b363f3ad81595ed58c25d34dd7d82bcd8))
* guard performance_stop_trace when tracing inactive ([#295](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/295)) ([8200194](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/8200194c8037cc30b8ab815e5ee0d0b2b000bea6))
## [0.6.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.5.1...chrome-devtools-mcp-v0.6.0) (2025-10-01)
### Features
* **screenshot:** add WebP format support with quality parameter ([#220](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/220)) ([03e02a2](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/03e02a2d769fbfc0c98599444dfed5413d15ae6e))
* **screenshot:** adds ability to output screenshot to a specific pat… ([#172](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/172)) ([f030726](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/f03072698ddda8587ce23229d733405f88b7c89e))
* support --accept-insecure-certs CLI ([#231](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/231)) ([efb106d](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/efb106dc94af0057f88c89f810beb65114eeaa4b))
* support --proxy-server CLI ([#230](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/230)) ([dfacc75](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/dfacc75ee9f46137b5194e35fc604b89a00ff53f))
* support initial viewport in the CLI ([#229](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/229)) ([ef61a08](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/ef61a08707056c5078d268a83a2c95d10e224f31))
* support timeouts in wait_for and navigations ([#228](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/228)) ([36e64d5](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/36e64d5ae21e8bb244a18201a23a16932947e938))
### Bug Fixes
* **network:** show only selected request ([#236](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/236)) ([73f0aec](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/73f0aecd8a48b9d1ee354897fe14d785c80e863e))
* PageCollector subscribing multiple times ([#241](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/241)) ([0412878](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/0412878bf51ae46e48a171183bb38cfbbee1038a))
* snapshot does not capture Iframe content ([#217](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/217)) ([ce356f2](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/ce356f256545e805db74664797de5f42e7b92bed)), closes [#186](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/186)
## [0.5.1](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.5.0...chrome-devtools-mcp-v0.5.1) (2025-09-29)
### Bug Fixes
* update package.json engines to reflect node20 support ([#210](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/210)) ([b31e647](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b31e64713e0524f28cbf760fad27b25829ec419d))
## [0.5.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.4.0...chrome-devtools-mcp-v0.5.0) (2025-09-29)
### Features
* **screenshot:** add JPEG quality parameter support ([#184](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/184)) ([139cfd1](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/139cfd135cdb07573fe87d824631fcdb6153186e))
### Bug Fixes
* do not error if the dialog was already handled ([#208](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/208)) ([d9f77f8](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d9f77f85098ffe851308c5de05effb03ac21237b))
* reference to handle_dialog tool ([#209](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/209)) ([205eef5](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/205eef5cdff19ccb7ddbd113bb1450cb87e8f398))
* support node20 ([#52](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/52)) ([13613b4](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/13613b4a33ab7cf2d4fb1f4849bfa6b82f546945))
* update tool reference in an error ([#205](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/205)) ([7765bb3](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/7765bb381ad9d01219547faf879a74978188754a))
## [0.4.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.3.0...chrome-devtools-mcp-v0.4.0) (2025-09-26)
### Features
* add network request filtering by resource type ([#162](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/162)) ([59d81a3](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/59d81a33258a199a3f993c9e02a415f62ef05ce4))
### Bug Fixes
* add core web vitals to performance_start_trace description ([#168](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/168)) ([6cfc977](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/6cfc9774f4ec7944c70842999506b2bc2018a667))
* add data format information to trace summary ([#166](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/166)) ([869dd42](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/869dd4273e42309c1bb57d44e0e5a6a9506ffad7))
* expose --debug-file argument ([#164](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/164)) ([22ec7ee](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/22ec7ee45cc04892000cf6dc32f3fe58d33855c1))
* typo in the disclaimers ([#156](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/156)) ([90f686e](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/90f686e5df3d880c35ec566c837ee5a98824be28))
## [0.3.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.2.7...chrome-devtools-mcp-v0.3.0) (2025-09-25)
### Features
* Add pagination list_network_requests ([#145](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/145)) ([4c909bb](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/4c909bb8d7c4a420cb8e3219ec98abf28f5cc664))
### Bug Fixes
* avoid reporting page close errors as errors ([#127](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/127)) ([44cfc8f](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/44cfc8f945edf9370efe26247f322a59a4a4a7be))
* clarify the node version message ([#135](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/135)) ([0cc907a](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/0cc907a9ad79289a6785e9690c3c6940f0a5de52))
* do not set channel if executablePath is provided ([#150](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/150)) ([03b59f0](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/03b59f0bca024173ad45d7a617994e919d9cbbad))
* **performance:** ImageDelivery insight errors ([#144](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/144)) ([d64ba0d](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d64ba0d9027540eb707381e2577ae3c1fe014346))
* roll latest DevTools to handle Insight errors ([#149](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/149)) ([b2e1e39](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b2e1e3944c7fa170584ce36c7b8923b0e6d6c6cb))
## [0.2.7](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.2.6...chrome-devtools-mcp-v0.2.7) (2025-09-24)
### Bug Fixes
* validate and report incompatible Node versions ([#113](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/113)) ([adfcecf](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/adfcecf9871938b1ad5d1460e0050b849fb2aa49))
## [0.2.6](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.2.5...chrome-devtools-mcp-v0.2.6) (2025-09-24)
### Bug Fixes
* manually bump server.json versions based on package.json ([#105](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/105)) ([cae1cf1](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/cae1cf13d5a97add3b96f20c425f720a1ceabf94))
## [0.2.5](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.2.4...chrome-devtools-mcp-v0.2.5) (2025-09-24)
### Bug Fixes
* add mcpName to package.json ([#103](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/103)) ([bd0351f](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/bd0351fd36ae35e41e613f0d15df40aeca17ba94))
## [0.2.4](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.2.3...chrome-devtools-mcp-v0.2.4) (2025-09-24)
### Bug Fixes
* forbid closing the last page ([#90](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/90)) ([0ca2434](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/0ca2434a29eb4bc6e570a4ebe21a135d85f4c0f3))
## [0.2.3](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.2.2...chrome-devtools-mcp-v0.2.3) (2025-09-24)
### Bug Fixes
* add a message indicating that no console messages exist ([#91](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/91)) ([1a4ba4d](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/1a4ba4d3e05f51a85747816f8638f31230881437))
* clean up pending promises on action errors ([#84](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/84)) ([4e7001a](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/4e7001ac375ec51f55b29e9faf68aff0dd09fa0f))
## [0.2.2](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.2.1...chrome-devtools-mcp-v0.2.2) (2025-09-23)
### Bug Fixes
* cli version being reported as unknown ([#74](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/74)) ([d6bab91](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d6bab912df55dc2e96a8d7893d1906f1fc608d0a))
* remove unnecessary waiting for navigation ([#83](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/83)) ([924c042](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/924c042492222a555074063841ce765342e3b5b9))
* rework performance parsing & error handling ([#75](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/75)) ([e8fb30c](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/e8fb30c1bfdc2b4ea8c2daf74b24aa82210f99be))
## [0.2.1](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.2.0...chrome-devtools-mcp-v0.2.1) (2025-09-23)
### Bug Fixes
* add 'on the selected page' to performance tools ([#69](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/69)) ([b877f7a](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b877f7a3053d0cdf2aad1fefc26cf7b913eb95ce))
* **emulation:** correctly report info for selected page ([#63](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/63)) ([1e8662f](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/1e8662f06860aecb5c01ed4ff1515ceb9dac26e4))
* expose timeout when Emulation is enabled ([#73](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/73)) ([0208bfd](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/0208bfdcf6924953879408c18f4c20da544bf4ff))
* fix browserUrl not working ([#53](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/53)) ([a6923b8](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a6923b8d9397d12ee0f9fe67dd62b10088ec6e87))
* increase timeouts in case of Emulation ([#71](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/71)) ([c509c64](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c509c64576e1be1ddc283653004ef08a117907a2))
* **windows:** work around Chrome not reporting reasons for crash ([#64](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/64)) ([d545741](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d5457412a4a76726547190fb3a46bb78c9d6645c))
## [0.2.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.1.0...chrome-devtools-mcp-v0.2.0) (2025-09-17)
### Features
* add performance_analyze_insight tool. ([#42](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/42)) ([21e175b](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/21e175b862c624d7a2d07802141187edf2d2e489))
* support script evaluate arguments ([#40](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/40)) ([c663f4d](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c663f4d7f9c0b868e8b4750f6441525939bfe920))
* use Performance Trace Formatter in trace output ([#36](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/36)) ([0cb6147](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/0cb6147b870e17bc3a624e9c6396d963a3e16b44))
* validate uids ([#37](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/37)) ([014a8bc](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/014a8bc52ecc58080cedeb8023d44f4a55055a05))
### Bug Fixes
* change profile folder name to browser-profile ([#39](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/39)) ([36115d7](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/36115d757abbae0502ffee814f55368d2ca59b9e))
* refresh context based on the browser instance ([#44](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/44)) ([93f4579](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/93f4579dd9aca3beef2bd9f2930ddfcc4069c0e3))
* update puppeteer to fix a11y snapshot issues ([#43](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/43)) ([b58f787](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b58f787234a34d5fcb01b336f5fb14e1c55ecdd5))
## [0.1.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.0.2...chrome-devtools-mcp-v0.1.0) (2025-09-16)
### Features
* improve tools with awaiting common events ([#10](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/10)) ([dba8b3c](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/dba8b3c5fad0d1bca26aaf172751c51188799927))
* initial version ([31a0bdc](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/31a0bdce266a33eaca9a7daae4611abb78ff5a25))
### Bug Fixes
* define tracing categories ([#21](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/21)) ([c939456](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c93945657cc96ac7ba213730a750c16e9ab87526))
* detect multiple instances and throw ([#12](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/12)) ([732267d](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/732267db5fea0048ed1fcc530bcdd074df4126be))
* make sure tool calls are processed sequentially ([#22](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/22)) ([a76b23d](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a76b23dccf074a13304b0341178665465a2c3399))
## /CONTRIBUTING.md
# How to contribute
We'd love to accept your patches and contributions to this project.
## Before you begin
### Sign our Contributor License Agreement
Contributions to this project must be accompanied by a
[Contributor License Agreement](https://cla.developers.google.com/about) (CLA).
You (or your employer) retain the copyright to your contribution; this simply
gives us permission to use and redistribute your contributions as part of the
project.
If you or your current employer have already signed the Google CLA (even if it
was for a different project), you probably don't need to do it again.
Visit <https://cla.developers.google.com/> to see your current agreements or to
sign a new one.
### Review our community guidelines
This project follows
[Google's Open Source Community Guidelines](https://opensource.google/conduct/).
## Development process
### Code reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.
### Conventional commits
Please follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/)
for PR and commit titles.
### Feature release checklist
Use `chore:` for commits containing incomplete features that are not available
to users yet. Once the feature is ready to be released, create a PR with a
`feat:` prefix that enables the feature. The following criteria need to be
completed:
- Documentation for the feature is up to date. For example, README.md and tools
reference are updated.
- The feature can be used with Chrome stable or version restrictions are
documented otherwise.
- Corresponding skills are updated or new skills are added if needed.
- The feature fulfills the use case by its own or in conjunction with existing
features (we want to avoid features that offer some tools but cannot be used
successfully to debug things).
### Release process
Releasing `chrome-devtools-mcp` is automated by GitHub Actions. To release a new
version, [search for a PR titled `chore(main): release chrome-devtools-mcp`](https://github.com/ChromeDevTools/chrome-devtools-mcp/pulls?q=is%3Apr+is%3Aopen+%22chore%28main%29%3A+release+chrome-devtools-mcp%22)
and review, test, and land it. The release PR is automatically opened if there
are any changes on the main branch that show up in the changelog.
### How to update the Lighthouse dependency
- Update the Lighthouse version in package.json and run `npm install`. The npm version is currently used for types.
- Check out the corresponding Lighthouse repository revision to a sibling directory (`../lighthouse`).
- Run `npm run update-lighthouse` (Note that Lighthouse requires yarn).
- Commit the bundle. If new dependencies are added via the bundle, update `tests/third_party_notices.test.ts`.
## Installation
Check that you are using node version specified in .nvmrc, then run following commands:
```sh
git clone https://github.com/ChromeDevTools/chrome-devtools-mcp.git
cd chrome-devtools-mcp
npm ci
npm run build
```
### Testing with @modelcontextprotocol/inspector
```sh
npx @modelcontextprotocol/inspector node /build/src/bin/chrome-devtools-mcp.js
```
### Testing with an MCP client
Add the MCP server to your client's config.
```json
{
"mcpServers": {
"chrome-devtools": {
"command": "node",
"args": ["/path-to/build/src/bin/chrome-devtools-mcp.js"]
}
}
}
```
#### Using with VS Code SSH
When running the `@modelcontextprotocol/inspector` it spawns 2 services - one on port `6274` and one on `6277`.
Usually VS Code automatically detects and forwards `6274` but fails to detect `6277` so you need to manually forward it.
### Debugging
To write debug logs to `log.txt` in the working directory, run with the following commands:
```sh
npx @modelcontextprotocol/inspector node /build/src/bin/chrome-devtools-mcp.js --log-file=/your/desired/path/log.txt
```
You can use the `DEBUG` environment variable as usual to control categories that are logged.
### Updating documentation
When adding a new tool or updating a tool name or description, make sure to run `npm run gen` to generate the tool reference documentation.
### Contributing to Evals
We use Gemini to evaluate the MCP server tools in `scripts/eval_scenarios`.
Each scenario is a TypeScript file that exports a `scenario` object implementing `TestScenario`.
- **prompt**: The prompt to send to the model.
- **maxTurns**: Maximum number of conversation turns.
- **expectations**: A function that verifies the tool calls made by the model.
- **htmlRoute** (Optional): Serve custom HTML content for the test at a specific path.
We look to test that the tools are used correctly without too rigid assertions. Avoid asserting exact argument values if they can vary (e.g., natural language reasoning), but ensure the core parameters (like URLs or selectors) were correct.
Example:
```ts
import {TestScenario} from '../eval_gemini.js';
export const scenario: TestScenario = {
prompt: 'Navigate to example.com',
maxTurns: 2,
expectations: calls => {
// Check that at least one call was 'browse_page'
const navigation = calls.find(c => c.name === 'browse_page');
if (!navigation) throw new Error('Model did not browse the page');
// Verify essential args
if (navigation.args.url !== 'http://example.com') {
throw new Error(`Wrong URL: ${navigation.args.url}`);
}
},
};
```
## Restrictions on JSON schema
- no .nullable(), no .object() types. Enforced by the `@local/enforce-zod-schema` ESLint rule.
- represent complex object as a short formatted string.
## /README.md
# Chrome DevTools for Agents
[](https://npmjs.org/package/chrome-devtools-mcp)
Chrome DevTools for Agents (`chrome-devtools-mcp`) lets your coding agent (such as Gemini, Claude, Cursor or Copilot)
control and inspect a live Chrome browser. It acts as a Model-Context-Protocol
(MCP) server, giving your AI coding assistant access to the full power of
Chrome DevTools for reliable automation, in-depth debugging, and performance analysis.
A [CLI](docs/cli.md) is also provided for use without MCP.
## [Tool reference](./docs/tool-reference.md) | [Changelog](./CHANGELOG.md) | [Contributing](./CONTRIBUTING.md) | [Troubleshooting](./docs/troubleshooting.md) | [Design Principles](./docs/design-principles.md)
## Key features
- **Get performance insights**: Uses [Chrome
DevTools](https://github.com/ChromeDevTools/devtools-frontend) to record
traces and extract actionable performance insights.
- **Advanced browser debugging**: Analyze network requests, take screenshots and
check browser console messages (with source-mapped stack traces).
- **Reliable automation**. Uses
[puppeteer](https://github.com/puppeteer/puppeteer) to automate actions in
Chrome and automatically wait for action results.
## Disclaimers
`chrome-devtools-mcp` exposes content of the browser instance to the MCP clients
allowing them to inspect, debug, and modify any data in the browser or DevTools.
Avoid sharing sensitive or personal information that you don't want to share with
MCP clients.
`chrome-devtools-mcp` officially supports Google Chrome and [Chrome for Testing](https://developer.chrome.com/blog/chrome-for-testing/) only.
Other Chromium-based browsers may work, but this is not guaranteed, and you may encounter unexpected behavior. Use at your own discretion.
We are committed to providing fixes and support for the latest version of [Extended Stable Chrome](https://chromiumdash.appspot.com/schedule).
Performance tools may send trace URLs to the Google CrUX API to fetch real-user
experience data. This helps provide a holistic performance picture by
presenting field data alongside lab data. This data is collected by the [Chrome
User Experience Report (CrUX)](https://developer.chrome.com/docs/crux). To disable
this, run with the `--no-performance-crux` flag.
## **Usage statistics**
Google collects usage statistics (such as tool invocation success rates, latency, and environment information) to improve the reliability and performance of Chrome DevTools MCP.
Data collection is **enabled by default**. You can opt-out by passing the `--no-usage-statistics` flag when starting the server:
```json
"args": ["-y", "chrome-devtools-mcp@latest", "--no-usage-statistics"]
```
Google handles this data in accordance with the [Google Privacy Policy](https://policies.google.com/privacy).
Google's collection of usage statistics for Chrome DevTools MCP is independent from the Chrome browser's usage statistics. Opting out of Chrome metrics does not automatically opt you out of this tool, and vice-versa.
Collection is disabled if `CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS` or `CI` env variables are set.
## Update checks
By default, the server periodically checks the npm registry for updates and logs a notification when a newer version is available.
You can disable these update checks by setting the `CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS` environment variable.
## Requirements
- [Node.js](https://nodejs.org/) v20.19 or a newer [latest maintenance LTS](https://github.com/nodejs/Release#release-schedule) version.
- [Chrome](https://www.google.com/chrome/) current stable version or newer.
- [npm](https://www.npmjs.com/)
## Getting started
Add the following config to your MCP client:
```json
{
"mcpServers": {
"chrome-devtools": {
"command": "npx",
"args": ["-y", "chrome-devtools-mcp@latest"]
}
}
}
```
> [!NOTE]
> Using `chrome-devtools-mcp@latest` ensures that your MCP client will always use the latest version of the Chrome DevTools MCP server.
If you are interested in doing only basic browser tasks, use the `--slim` mode:
```json
{
"mcpServers": {
"chrome-devtools": {
"command": "npx",
"args": ["-y", "chrome-devtools-mcp@latest", "--slim", "--headless"]
}
}
}
```
See [Slim tool reference](./docs/slim-tool-reference.md).
### MCP Client configuration
<details>
<summary>Amp</summary>
Follow https://ampcode.com/manual#mcp and use the config provided above. You can also install the Chrome DevTools MCP server using the CLI:
```bash
amp mcp add chrome-devtools -- npx chrome-devtools-mcp@latest
```
</details>
<details>
<summary>Antigravity</summary>
To use the Chrome DevTools MCP server follow the instructions from <a href="https://antigravity.google/docs/mcp">Antigravity's docs</a> to install a custom MCP server. Add the following config to the MCP servers config:
```bash
{
"mcpServers": {
"chrome-devtools": {
"command": "npx",
"args": [
"chrome-devtools-mcp@latest",
"--browser-url=http://127.0.0.1:9222",
"-y"
]
}
}
}
```
This will make the Chrome DevTools MCP server automatically connect to the browser that Antigravity is using. If you are not using port 9222, make sure to adjust accordingly.
Chrome DevTools MCP will not start the browser instance automatically using this approach because the Chrome DevTools MCP server connects to Antigravity's built-in browser. If the browser is not already running, you have to start it first by clicking the Chrome icon at the top right corner.
</details>
<details>
<summary>Claude Code</summary>
**Install via CLI (MCP only)**
Use the Claude Code CLI to add the Chrome DevTools MCP server (<a href="https://code.claude.com/docs/en/mcp">guide</a>):
```bash
claude mcp add chrome-devtools --scope user npx chrome-devtools-mcp@latest
```
**Install as a Plugin (MCP + Skills)**
> [!NOTE]
> If you already had Chrome DevTools MCP installed previously for Claude Code, make sure to remove it first from your installation and configuration files.
To install Chrome DevTools MCP with skills, add the marketplace registry in Claude Code:
```sh
/plugin marketplace add ChromeDevTools/chrome-devtools-mcp
```
Then, install the plugin:
```sh
/plugin install chrome-devtools-mcp@chrome-devtools-plugins
```
Restart Claude Code to have the MCP server and skills load (check with `/skills`).
> [!TIP]
> If the plugin installation fails with a `Failed to clone repository` error (e.g., HTTPS connectivity issues behind a corporate firewall), see the [troubleshooting guide](./docs/troubleshooting.md#claude-code-plugin-installation-fails-with-failed-to-clone-repository) for workarounds, or use the CLI installation method above instead.
</details>
<details>
<summary>Cline</summary>
Follow https://docs.cline.bot/mcp/configuring-mcp-servers and use the config provided above.
</details>
<details>
<summary>Codex</summary>
Follow the <a href="https://developers.openai.com/codex/mcp/#configure-with-the-cli">configure MCP guide</a>
using the standard config from above. You can also install the Chrome DevTools MCP server using the Codex CLI:
```bash
codex mcp add chrome-devtools -- npx chrome-devtools-mcp@latest
```
**On Windows 11**
Configure the Chrome install location and increase the startup timeout by updating `.codex/config.toml` and adding the following `env` and `startup_timeout_ms` parameters:
```
[mcp_servers.chrome-devtools]
command = "cmd"
args = [
"/c",
"npx",
"-y",
"chrome-devtools-mcp@latest",
]
env = { SystemRoot="C:\\Windows", PROGRAMFILES="C:\\Program Files" }
startup_timeout_ms = 20_000
```
</details>
<details>
<summary>Command Code</summary>
Use the Command Code CLI to add the Chrome DevTools MCP server (<a href="https://commandcode.ai/docs/mcp">MCP guide</a>):
```bash
cmd mcp add chrome-devtools --scope user npx chrome-devtools-mcp@latest
```
</details>
<details>
<summary>Copilot CLI</summary>
Start Copilot CLI:
```
copilot
```
Start the dialog to add a new MCP server by running:
```
/mcp add
```
Configure the following fields and press `CTRL+S` to save the configuration:
- **Server name:** `chrome-devtools`
- **Server Type:** `[1] Local`
- **Command:** `npx -y chrome-devtools-mcp@latest`
</details>
<details>
<summary>Copilot / VS Code</summary>
**Install as a Plugin (Recommended)**
The easiest way to get up and running is to install `chrome-devtools-mcp` as an agent plugin.
This bundles the **MCP server** and all **skills** together, so your agent gets both the tools
and the expert guidance it needs to use them effectively.
1. Open the **Command Palette** (`Cmd+Shift+P` on macOS or `Ctrl+Shift+P` on Windows/Linux).
2. Search for and run the **Chat: Install Plugin From Source** command.
3. Paste in our repository URL: `https://github.com/ChromeDevTools/chrome-devtools-mcp`
That's it! Your agent is now supercharged with Chrome DevTools capabilities.
---
**Install as an MCP Server (MCP only)**
**Click the button to install:**
[<img src="https://img.shields.io/badge/VS_Code-VS_Code?style=flat-square&label=Install%20Server&color=0098FF" alt="Install in VS Code">](https://vscode.dev/redirect/mcp/install?name=io.github.ChromeDevTools%2Fchrome-devtools-mcp&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22chrome-devtools-mcp%22%5D%2C%22env%22%3A%7B%7D%7D)
[<img src="https://img.shields.io/badge/VS_Code_Insiders-VS_Code_Insiders?style=flat-square&label=Install%20Server&color=24bfa5" alt="Install in VS Code Insiders">](https://insiders.vscode.dev/redirect?url=vscode-insiders%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522io.github.ChromeDevTools%252Fchrome-devtools-mcp%2522%252C%2522config%2522%253A%257B%2522command%2522%253A%2522npx%2522%252C%2522args%2522%253A%255B%2522-y%2522%252C%2522chrome-devtools-mcp%2522%255D%252C%2522env%2522%253A%257B%257D%257D%257D)
**Or install manually:**
Follow the VS Code [MCP configuration guide](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server) using the standard config from above, or use the CLI:
For macOS and Linux:
```bash
code --add-mcp '{"name":"io.github.ChromeDevTools/chrome-devtools-mcp","command":"npx","args":["-y","chrome-devtools-mcp"],"env":{}}'
```
For Windows (PowerShell):
```powershell
code --add-mcp '{"""name""":"""io.github.ChromeDevTools/chrome-devtools-mcp""","""command""":"""npx""","""args""":["""-y""","""chrome-devtools-mcp"""]}'
```
</details>
<details>
<summary>Cursor</summary>
**Click the button to install:**
[<img src="https://cursor.com/deeplink/mcp-install-dark.svg" alt="Install in Cursor">](https://cursor.com/en/install-mcp?name=chrome-devtools&config=eyJjb21tYW5kIjoibnB4IC15IGNocm9tZS1kZXZ0b29scy1tY3BAbGF0ZXN0In0%3D)
**Or install manually:**
Go to `Cursor Settings` -> `MCP` -> `New MCP Server`. Use the config provided above.
</details>
<details>
<summary>Factory CLI</summary>
Use the Factory CLI to add the Chrome DevTools MCP server (<a href="https://docs.factory.ai/cli/configuration/mcp">guide</a>):
```bash
droid mcp add chrome-devtools "npx -y chrome-devtools-mcp@latest"
```
</details>
<details>
<summary>Gemini CLI</summary>
Install the Chrome DevTools MCP server using the Gemini CLI.
**Project wide:**
```bash
# Either MCP only:
gemini mcp add chrome-devtools npx chrome-devtools-mcp@latest
# Or as a Gemini extension (MCP+Skills):
gemini extensions install --auto-update https://github.com/ChromeDevTools/chrome-devtools-mcp
```
**Globally:**
```bash
gemini mcp add -s user chrome-devtools npx chrome-devtools-mcp@latest
```
Alternatively, follow the <a href="https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md#how-to-set-up-your-mcp-server">MCP guide</a> and use the standard config from above.
</details>
<details>
<summary>Gemini Code Assist</summary>
Follow the <a href="https://cloud.google.com/gemini/docs/codeassist/use-agentic-chat-pair-programmer#configure-mcp-servers">configure MCP guide</a>
using the standard config from above.
</details>
<details>
<summary>JetBrains AI Assistant & Junie</summary>
Go to `Settings | Tools | AI Assistant | Model Context Protocol (MCP)` -> `Add`. Use the config provided above.
The same way chrome-devtools-mcp can be configured for JetBrains Junie in `Settings | Tools | Junie | MCP Settings` -> `Add`. Use the config provided above.
</details>
<details>
<summary>Kiro</summary>
In **Kiro Settings**, go to `Configure MCP` > `Open Workspace or User MCP Config` > Use the configuration snippet provided above.
Or, from the IDE **Activity Bar** > `Kiro` > `MCP Servers` > `Click Open MCP Config`. Use the configuration snippet provided above.
</details>
<details>
<summary>Katalon Studio</summary>
The Chrome DevTools MCP server can be used with <a href="https://docs.katalon.com/katalon-studio/studioassist/mcp-servers/setting-up-chrome-devtools-mcp-server-for-studioassist">Katalon StudioAssist</a> via an MCP proxy.
**Step 1:** Install the MCP proxy by following the <a href="https://docs.katalon.com/katalon-studio/studioassist/mcp-servers/setting-up-mcp-proxy-for-stdio-mcp-servers">MCP proxy setup guide</a>.
**Step 2:** Start the Chrome DevTools MCP server with the proxy:
```bash
mcp-proxy --transport streamablehttp --port 8080 -- npx -y chrome-devtools-mcp@latest
```
**Note:** You may need to pick another port if 8080 is already in use.
**Step 3:** In Katalon Studio, add the server to StudioAssist with the following settings:
- **Connection URL:** `http://127.0.0.1:8080/mcp`
- **Transport type:** `HTTP`
Once connected, the Chrome DevTools MCP tools will be available in StudioAssist.
</details>
<details>
<summary>Mistral Vibe</summary>
Add in ~/.vibe/config.toml:
```toml
[[mcp_servers]]
name = "chrome-devtools"
transport = "stdio"
command = "npx"
args = ["chrome-devtools-mcp@latest"]
```
</details>
<details>
<summary>OpenCode</summary>
Add the following configuration to your `opencode.json` file. If you don't have one, create it at `~/.config/opencode/opencode.json` (<a href="https://opencode.ai/docs/mcp-servers">guide</a>):
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"chrome-devtools": {
"type": "local",
"command": ["npx", "-y", "chrome-devtools-mcp@latest"]
}
}
}
```
</details>
<details>
<summary>Qoder</summary>
In **Qoder Settings**, go to `MCP Server` > `+ Add` > Use the configuration snippet provided above.
Alternatively, follow the <a href="https://docs.qoder.com/user-guide/chat/model-context-protocol">MCP guide</a> and use the standard config from above.
</details>
<details>
<summary>Qoder CLI</summary>
Install the Chrome DevTools MCP server using the Qoder CLI (<a href="https://docs.qoder.com/cli/using-cli#mcp-servers">guide</a>):
**Project wide:**
```bash
qodercli mcp add chrome-devtools -- npx chrome-devtools-mcp@latest
```
**Globally:**
```bash
qodercli mcp add -s user chrome-devtools -- npx chrome-devtools-mcp@latest
```
</details>
<details>
<summary>Visual Studio</summary>
**Click the button to install:**
[<img src="https://img.shields.io/badge/Visual_Studio-Install-C16FDE?logo=visualstudio&logoColor=white" alt="Install in Visual Studio">](https://vs-open.link/mcp-install?%7B%22name%22%3A%22chrome-devtools%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22chrome-devtools-mcp%40latest%22%5D%7D)
</details>
<details>
<summary>Warp</summary>
Go to `Settings | AI | Manage MCP Servers` -> `+ Add` to [add an MCP Server](https://docs.warp.dev/knowledge-and-collaboration/mcp#adding-an-mcp-server). Use the config provided above.
</details>
<details>
<summary>Windsurf</summary>
Follow the <a href="https://docs.windsurf.com/windsurf/cascade/mcp#mcp-config-json">configure MCP guide</a>
using the standard config from above.
</details>
### Your first prompt
Enter the following prompt in your MCP Client to check if everything is working:
```
Check the performance of https://developers.chrome.com
```
Your MCP client should open the browser and record a performance trace.
> [!NOTE]
> The MCP server will start the browser automatically once the MCP client uses a tool that requires a running browser instance. Connecting to the Chrome DevTools MCP server on its own will not automatically start the browser.
## Tools
If you run into any issues, checkout our [troubleshooting guide](./docs/troubleshooting.md).
<!-- BEGIN AUTO GENERATED TOOLS -->
- **Input automation** (10 tools)
- [`click`](docs/tool-reference.md#click)
- [`drag`](docs/tool-reference.md#drag)
- [`fill`](docs/tool-reference.md#fill)
- [`fill_form`](docs/tool-reference.md#fill_form)
- [`handle_dialog`](docs/tool-reference.md#handle_dialog)
- [`hover`](docs/tool-reference.md#hover)
- [`press_key`](docs/tool-reference.md#press_key)
- [`type_text`](docs/tool-reference.md#type_text)
- [`upload_file`](docs/tool-reference.md#upload_file)
- [`click_at`](docs/tool-reference.md#click_at)
- **Navigation automation** (6 tools)
- [`close_page`](docs/tool-reference.md#close_page)
- [`list_pages`](docs/tool-reference.md#list_pages)
- [`navigate_page`](docs/tool-reference.md#navigate_page)
- [`new_page`](docs/tool-reference.md#new_page)
- [`select_page`](docs/tool-reference.md#select_page)
- [`wait_for`](docs/tool-reference.md#wait_for)
- **Emulation** (2 tools)
- [`emulate`](docs/tool-reference.md#emulate)
- [`resize_page`](docs/tool-reference.md#resize_page)
- **Performance** (3 tools)
- [`performance_analyze_insight`](docs/tool-reference.md#performance_analyze_insight)
- [`performance_start_trace`](docs/tool-reference.md#performance_start_trace)
- [`performance_stop_trace`](docs/tool-reference.md#performance_stop_trace)
- **Network** (2 tools)
- [`get_network_request`](docs/tool-reference.md#get_network_request)
- [`list_network_requests`](docs/tool-reference.md#list_network_requests)
- **Debugging** (8 tools)
- [`evaluate_script`](docs/tool-reference.md#evaluate_script)
- [`get_console_message`](docs/tool-reference.md#get_console_message)
- [`lighthouse_audit`](docs/tool-reference.md#lighthouse_audit)
- [`list_console_messages`](docs/tool-reference.md#list_console_messages)
- [`take_screenshot`](docs/tool-reference.md#take_screenshot)
- [`take_snapshot`](docs/tool-reference.md#take_snapshot)
- [`screencast_start`](docs/tool-reference.md#screencast_start)
- [`screencast_stop`](docs/tool-reference.md#screencast_stop)
- **Memory** (4 tools)
- [`take_memory_snapshot`](docs/tool-reference.md#take_memory_snapshot)
- [`get_memory_snapshot_details`](docs/tool-reference.md#get_memory_snapshot_details)
- [`get_nodes_by_class`](docs/tool-reference.md#get_nodes_by_class)
- [`load_memory_snapshot`](docs/tool-reference.md#load_memory_snapshot)
- **Extensions** (5 tools)
- [`install_extension`](docs/tool-reference.md#install_extension)
- [`list_extensions`](docs/tool-reference.md#list_extensions)
- [`reload_extension`](docs/tool-reference.md#reload_extension)
- [`trigger_extension_action`](docs/tool-reference.md#trigger_extension_action)
- [`uninstall_extension`](docs/tool-reference.md#uninstall_extension)
- **Third-party** (2 tools)
- [`execute_3p_developer_tool`](docs/tool-reference.md#execute_3p_developer_tool)
- [`list_3p_developer_tools`](docs/tool-reference.md#list_3p_developer_tools)
- **WebMCP** (2 tools)
- [`execute_webmcp_tool`](docs/tool-reference.md#execute_webmcp_tool)
- [`list_webmcp_tools`](docs/tool-reference.md#list_webmcp_tools)
<!-- END AUTO GENERATED TOOLS -->
## Configuration
The Chrome DevTools MCP server supports the following configuration option:
<!-- BEGIN AUTO GENERATED OPTIONS -->
- **`--autoConnect`/ `--auto-connect`**
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 remote debugging server to be started in the Chrome instance via chrome://inspect/#remote-debugging.
- **Type:** boolean
- **Default:** `false`
- **`--browserUrl`/ `--browser-url`, `-u`**
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.
- **Type:** string
- **`--wsEndpoint`/ `--ws-endpoint`, `-w`**
WebSocket endpoint to connect to a running Chrome instance (e.g., ws://127.0.0.1:9222/devtools/browser/<id>). Alternative to --browserUrl.
- **Type:** string
- **`--wsHeaders`/ `--ws-headers`**
Custom headers for WebSocket connection in JSON format (e.g., '{"Authorization":"Bearer token"}'). Only works with --wsEndpoint.
- **Type:** string
- **`--headless`**
Whether to run in headless (no UI) mode.
- **Type:** boolean
- **Default:** `false`
- **`--executablePath`/ `--executable-path`, `-e`**
Path to custom Chrome executable.
- **Type:** string
- **`--isolated`**
If specified, creates a temporary user-data-dir that is automatically cleaned up after the browser is closed. Defaults to false.
- **Type:** boolean
- **`--userDataDir`/ `--user-data-dir`**
Path to the user data directory for Chrome. Default is $HOME/.cache/chrome-devtools-mcp/chrome-profile$CHANNEL_SUFFIX_IF_NON_STABLE
- **Type:** string
- **`--channel`**
Specify a different Chrome channel that should be used. The default is the stable channel version.
- **Type:** string
- **Choices:** `canary`, `dev`, `beta`, `stable`
- **`--logFile`/ `--log-file`**
Path to a file to write debug logs to. Set the env variable `DEBUG` to `*` to enable verbose logs. Useful for submitting bug reports.
- **Type:** string
- **`--viewport`**
Initial viewport size for the Chrome instances started by the server. For example, `1280x720`. In headless mode, max size is 3840x2160px.
- **Type:** string
- **`--proxyServer`/ `--proxy-server`**
Proxy server configuration for Chrome passed as --proxy-server when launching the browser. See https://www.chromium.org/developers/design-documents/network-settings/ for details.
- **Type:** string
- **`--acceptInsecureCerts`/ `--accept-insecure-certs`**
If enabled, ignores errors relative to self-signed and expired certificates. Use with caution.
- **Type:** boolean
- **`--experimentalPageIdRouting`/ `--experimental-page-id-routing`**
Whether to expose pageId on page-scoped tools and route requests by page ID (useful for concurrent agent sessions).
- **Type:** boolean
- **`--experimentalDevtools`/ `--experimental-devtools`**
Whether to enable automation over DevTools targets
- **Type:** boolean
- **`--experimentalVision`/ `--experimental-vision`**
Whether to enable coordinate-based tools such as click_at(x,y). Usually requires a computer-use model able to produce accurate coordinates by looking at screenshots.
- **Type:** boolean
- **`--experimentalMemory`/ `--experimental-memory`**
Whether to enable experimental memory tools.
- **Type:** boolean
- **`--experimentalStructuredContent`/ `--experimental-structured-content`**
Whether to output structured formatted content.
- **Type:** boolean
- **`--experimentalIncludeAllPages`/ `--experimental-include-all-pages`**
Whether to include all kinds of pages such as webviews or background pages as pages.
- **Type:** boolean
- **`--experimentalScreencast`/ `--experimental-screencast`**
Exposes experimental screencast tools (requires ffmpeg). Install ffmpeg https://www.ffmpeg.org/download.html and ensure it is available in the MCP server PATH.
- **Type:** boolean
- **`--experimentalFfmpegPath`/ `--experimental-ffmpeg-path`**
Path to ffmpeg executable for screencast recording.
- **Type:** string
- **`--categoryExperimentalWebmcp`/ `--category-experimental-webmcp`**
Set to true to enable debugging WebMCP tools. Requires Chrome 149+ with the following flags: `--enable-features=WebMCPTesting,DevToolsWebMCPSupport`
- **Type:** boolean
- **`--chromeArg`/ `--chrome-arg`**
Additional arguments for Chrome. Only applies when Chrome is launched by chrome-devtools-mcp.
- **Type:** array
- **`--ignoreDefaultChromeArg`/ `--ignore-default-chrome-arg`**
Explicitly disable default arguments for Chrome. Only applies when Chrome is launched by chrome-devtools-mcp.
- **Type:** array
- **`--categoryEmulation`/ `--category-emulation`**
Set to false to exclude tools related to emulation.
- **Type:** boolean
- **Default:** `true`
- **`--categoryPerformance`/ `--category-performance`**
Set to false to exclude tools related to performance.
- **Type:** boolean
- **Default:** `true`
- **`--categoryNetwork`/ `--category-network`**
Set to false to exclude tools related to network.
- **Type:** boolean
- **Default:** `true`
- **`--categoryExtensions`/ `--category-extensions`**
Set to true to include tools related to extensions. Note: This feature is currently only supported with a pipe connection. autoConnect, browserUrl, and wsEndpoint are not supported with this feature until 149 will be released.
- **Type:** boolean
- **Default:** `false`
- **`--categoryExperimentalThirdParty`/ `--category-experimental-third-party`**
Set to true to enable third-party developer tools exposed by the inspected page itself
- **Type:** boolean
- **Default:** `false`
- **`--performanceCrux`/ `--performance-crux`**
Set to false to disable sending URLs from performance traces to CrUX API to get field performance data.
- **Type:** boolean
- **Default:** `true`
- **`--usageStatistics`/ `--usage-statistics`**
Set to false to opt-out of usage statistics collection. Google collects usage data to improve the tool, handled under the Google Privacy Policy (https://policies.google.com/privacy). This is independent from Chrome browser metrics. Disabled if `CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS` or `CI` env variables are set.
- **Type:** boolean
- **Default:** `true`
- **`--slim`**
Exposes a "slim" set of 3 tools covering navigation, script execution and screenshots only. Useful for basic browser tasks.
- **Type:** boolean
- **`--redactNetworkHeaders`/ `--redact-network-headers`**
If true, redacts some of the network headers considered senstive before returning to the client.
- **Type:** boolean
- **Default:** `false`
<!-- END AUTO GENERATED OPTIONS -->
Pass them via the `args` property in the JSON configuration. For example:
```json
{
"mcpServers": {
"chrome-devtools": {
"command": "npx",
"args": [
"chrome-devtools-mcp@latest",
"--channel=canary",
"--headless=true",
"--isolated=true"
]
}
}
}
```
### Connecting via WebSocket with custom headers
You can connect directly to a Chrome WebSocket endpoint and include custom headers (e.g., for authentication):
```json
{
"mcpServers": {
"chrome-devtools": {
"command": "npx",
"args": [
"chrome-devtools-mcp@latest",
"--wsEndpoint=ws://127.0.0.1:9222/devtools/browser/<id>",
"--wsHeaders={\"Authorization\":\"Bearer YOUR_TOKEN\"}"
]
}
}
}
```
To get the WebSocket endpoint from a running Chrome instance, visit `http://127.0.0.1:9222/json/version` and look for the `webSocketDebuggerUrl` field.
You can also run `npx chrome-devtools-mcp@latest --help` to see all available configuration options.
## Concepts
### User data directory
`chrome-devtools-mcp` starts a Chrome's stable channel instance using the following user
data directory:
- Linux / macOS: `$HOME/.cache/chrome-devtools-mcp/chrome-profile-$CHANNEL`
- Windows: `%HOMEPATH%/.cache/chrome-devtools-mcp/chrome-profile-$CHANNEL`
The user data directory is not cleared between runs and shared across
all instances of `chrome-devtools-mcp`. Set the `isolated` option to `true`
to use a temporary user data dir instead which will be cleared automatically after
the browser is closed.
### Connecting to a running Chrome instance
By default, the Chrome DevTools MCP server will start a new Chrome instance with a dedicated profile. This might not be ideal in all situations:
- If you would like to maintain the same application state when alternating between manual site testing and agent-driven testing.
- When the MCP needs to sign into a website. Some accounts may prevent sign-in when the browser is controlled via WebDriver (the default launch mechanism for the Chrome DevTools MCP server).
- If you're running your LLM inside a sandboxed environment, but you would like to connect to a Chrome instance that runs outside the sandbox.
In these cases, start Chrome first and let the Chrome DevTools MCP server connect to it. There are two ways to do so:
- **Automatic connection (available in Chrome 144)**: best for sharing state between manual and agent-driven testing.
- **Manual connection via remote debugging port**: best when running inside a sandboxed environment.
#### Automatically connecting to a running Chrome instance
**Step 1:** Set up remote debugging in Chrome
In Chrome (\>= M144), do the following to set up remote debugging:
1. Navigate to `chrome://inspect/#remote-debugging` to enable remote debugging.
2. Follow the dialog UI to allow or disallow incoming debugging connections.
**Step 2:** Configure Chrome DevTools MCP server to automatically connect to a running Chrome Instance
To connect the `chrome-devtools-mcp` server to the running Chrome instance, use
`--autoConnect` command line argument for the MCP server.
The following code snippet is an example configuration for gemini-cli:
```json
{
"mcpServers": {
"chrome-devtools": {
"command": "npx",
"args": ["chrome-devtools-mcp@latest", "--autoConnect"]
}
}
}
```
**Step 3:** Test your setup
Make sure your browser is running. Open gemini-cli and run the following prompt:
```none
Check the performance of https://developers.chrome.com
```
> [!NOTE]
> The <code>autoConnect</code> option requires the user to start Chrome. If the user has multiple active profiles, the MCP server will connect to the default profile (as determined by Chrome). The MCP server has access to all open windows for the selected profile.
The Chrome DevTools MCP server will try to connect to your running Chrome
instance. It shows a dialog asking for user permission.
Clicking **Allow** results in the Chrome DevTools MCP server opening
[developers.chrome.com](http://developers.chrome.com) and taking a performance
trace.
#### Manual connection using port forwarding
You can connect to a running Chrome instance by using the `--browser-url` option. This is useful if you are running the MCP server in a sandboxed environment that does not allow starting a new Chrome instance.
Here is a step-by-step guide on how to connect to a running Chrome instance:
**Step 1: Configure the MCP client**
Add the `--browser-url` option to your MCP client configuration. The value of this option should be the URL of the running Chrome instance. `http://127.0.0.1:9222` is a common default.
```json
{
"mcpServers": {
"chrome-devtools": {
"command": "npx",
"args": [
"chrome-devtools-mcp@latest",
"--browser-url=http://127.0.0.1:9222"
]
}
}
}
```
**Step 2: Start the Chrome browser**
> [!WARNING]
> Enabling the remote debugging port opens up a debugging port on the running browser instance. Any application on your machine can connect to this port and control the browser. Make sure that you are not browsing any sensitive websites while the debugging port is open.
Start the Chrome browser with the remote debugging port enabled. Make sure to close any running Chrome instances before starting a new one with the debugging port enabled. The port number you choose must be the same as the one you specified in the `--browser-url` option in your MCP client configuration.
For security reasons, [Chrome requires you to use a non-default user data directory](https://developer.chrome.com/blog/remote-debugging-port) when enabling the remote debugging port. You can specify a custom directory using the `--user-data-dir` flag. This ensures that your regular browsing profile and data are not exposed to the debugging session.
**macOS**
```bash
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-profile-stable
```
**Linux**
```bash
/usr/bin/google-chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-profile-stable
```
**Windows**
```bash
"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --user-data-dir="%TEMP%\chrome-profile-stable"
```
**Step 3: Test your setup**
After configuring the MCP client and starting the Chrome browser, you can test your setup by running a simple prompt in your MCP client:
```
Check the performance of https://developers.chrome.com
```
Your MCP client should connect to the running Chrome instance and receive a performance report.
If you hit VM-to-host port forwarding issues, see the “Remote debugging between virtual machine (VM) and host fails” section in [`docs/troubleshooting.md`](./docs/troubleshooting.md#remote-debugging-between-virtual-machine-vm-and-host-fails).
For more details on remote debugging, see the [Chrome DevTools documentation](https://developer.chrome.com/docs/devtools/remote-debugging/).
### Debugging Chrome on Android
Please consult [these instructions](./docs/debugging-android.md).
## Known limitations
See [Troubleshooting](./docs/troubleshooting.md).
## /SECURITY.md
## Security policy
The Chrome DevTools MCP project takes security very seriously. Please use [Chromium’s process to report security issues](https://www.chromium.org/Home/chromium-security/reporting-security-bugs/).
### Scope
In general, it is the expectation that the AI agent or client using this MCP server validates any input (including tool calls and parameters) before sending it. The server provides powerful capabilities for browser automation and inspection, and it is the responsibility of the calling agent to ensure these are used safely and as intended.
Several tools in this project have the ability to perform actions such as writing files to disk (e.g., via browser downloads or screenshots) or dynamically loading Chrome extensions. These are intentional, documented features and are not vulnerabilities.
We appreciate feedback and suggestions from developers on how this tool can make it easier for them to build a more secure user experience, but will treat these exclusively as feature requests, and not vulnerabilities in chrome-devtools-mcp itself.
## /docs/cli.md
# Chrome DevTools CLI
The `chrome-devtools-mcp` package includes an **experimental** CLI interface that allows you to interact with the browser directly from your terminal. This is particularly useful for debugging or when you want an agent to generate scripts that automate browser actions.
## Getting started
Install the package globally to make the `chrome-devtools` command available:
```sh
npm i chrome-devtools-mcp@latest -g
chrome-devtools status # check if install worked.
```
## How it works
The CLI acts as a client to a background `chrome-devtools-mcp` daemon (uses Unix sockets on Linux/Mac and named pipes on Windows).
- **Automatic Start**: The first time you call a tool (e.g., `list_pages`), the CLI automatically starts the MCP server and the browser in the background if they aren't already running.
- **Persistence**: The same background instance is reused for subsequent commands, preserving the browser state (open pages, cookies, etc.).
- **Manual Control**: You can explicitly manage the background process using `start`, `stop`, and `status`. The `start` command forwards all subsequent arguments to the underlying MCP server (e.g., `--headless`, `--userDataDir`) but not all args are supported. Run `chrome-devtools start --help` for supported args. Headless is enabled by default. Isolated is enabled by default unless `--userDataDir` is provided.
```sh
# Check if the daemon is running
chrome-devtools status
# Navigate the current page to a URL
chrome-devtools navigate_page "https://google.com"
# Take a screenshot and save it to a file
chrome-devtools take_screenshot --filePath screenshot.png
# Stop the background daemon when finished
chrome-devtools stop
```
## Command Usage
The CLI only supports tools available in the MCP server without additional arguments (see [Tool reference](./tool-reference.md)).
Thus, `--categoryExtensions` tools are currently not available in the CLI.
```sh
chrome-devtools <tool> [arguments] [flags]
```
- **Required Arguments**: Passed as positional arguments.
- **Optional Arguments**: Passed as flags (e.g., `--filePath`, `--fullPage`).
### Examples
**New Page and Navigation:**
```sh
chrome-devtools new_page "https://example.com"
chrome-devtools navigate_page "https://web.dev" --type url
```
**Interaction:**
```sh
# Click an element by its UID from a snapshot
chrome-devtools click "element-uid-123"
# Fill a form field
chrome-devtools fill "input-uid-456" "search query"
```
**Analysis:**
```sh
# Run a Lighthouse audit (defaults to navigation mode)
chrome-devtools lighthouse_audit --mode snapshot
```
## Output format
By default, the CLI outputs a human-readable summary of the tool's result. For programmatic use, you can request raw JSON:
```sh
chrome-devtools list_pages --output-format=json
```
## Troubleshooting
If the CLI hangs or fails to connect, try stopping the background process:
```sh
chrome-devtools stop
```
For more verbose logs, set the `DEBUG` environment variable:
```sh
DEBUG=* chrome-devtools list_pages
```
## CLI generation
Implemented in `scripts/generate-cli.ts`. Some commands are excluded from CLI
generation such as `wait_for` and `fill_form`.
`chrome-devtools-mcp` args are also filtered in `src/bin/chrome-devtools.ts`
because not all args make sense in a CLI interface.
## /docs/debugging-android.md
# Experimental: Debugging Chrome on Android
This is an experimental feature as Puppeteer does not officially support Chrome on Android as a target.
The workflow below works for most users. See [Troubleshooting: DevTools is not detecting the Android device for more help](https://developer.chrome.com/docs/devtools/remote-debugging#troubleshooting) for more help.
1. Open the Developer Options screen on your Android. See [Configure on-device developer Options](https://developer.android.com/studio/debug/dev-options.html).
2. Select Enable USB Debugging.
3. Connect your Android device directly to your development machine using a USB cable.
4. On your development machine setup port forwarding from your development machine to your android device:
```shell
adb forward tcp:9222 localabstract:chrome_devtools_remote
```
5. Configure your MCP server to connect to the Chrome
```json
"chrome-devtools": {
"command": "npx",
"args": [
"chrome-devtools-mcp@latest",
"--wsEndpoint=ws://127.0.0.1:9222/devtools/browser/"
],
"trust": true
}
```
6. Test your setup by running the following prompt in your coding agent:
```none
Check the performance of developers.chrome.com
```
The Chrome DevTools MCP server should now control Chrome on your Android device.
## /docs/design-principles.md
# Design Principles
These are rough guidelines to follow when shipping features for the MCP server.
Apply them with nuance.
- **Agent-Agnostic API**: Use standards like MCP. Don't lock in to one LLM. Interoperability is key.
- **Token-Optimized**: Return semantic summaries. "LCP was 3.2s" is better than 50k lines of JSON. Files are the right location for large amounts of data.
- **Small, Deterministic Blocks**: Give agents composable tools (Click, Screenshot), not magic buttons.
- **Self-Healing Errors**: Return actionable errors that include context and potential fixes.
- **Human-Agent Collaboration**: Output must be readable by machines (structured) AND humans (summaries).
- **Progressive Complexity**: Tools should be simple by default (high-level actions) but offer advanced optional arguments for power users.
- **Reference over Value**: for heavy assets (screenshots, traces, videos), return a file path or resource URI, never the raw data stream. Some MCP clients support a built-in handling of heavy assets e.g. directly displaying images. This _could_ be an exception.
## /docs/slim-tool-reference.md
<!-- AUTO GENERATED DO NOT EDIT - run 'npm run gen' to update-->
# Chrome DevTools MCP Slim Tool Reference
- **[Navigation automation](#navigation-automation)** (1 tools)
- [`navigate`](#navigate)
- **[Debugging](#debugging)** (2 tools)
- [`evaluate`](#evaluate)
- [`screenshot`](#screenshot)
## Navigation automation
### `navigate`
**Description:** Loads a URL
**Parameters:**
- **url** (string) **(required)**: URL to [`navigate`](#navigate) to
---
## Debugging
### `evaluate`
**Description:** Evaluates a JavaScript script
**Parameters:**
- **script** (string) **(required)**: JS script to run on the page
---
### `screenshot`
**Description:** Takes a [`screenshot`](#screenshot)
**Parameters:** None
---
## /docs/third-party-developer-tools.md
# Developer Guide: Building third-party developer tools
This documentation outlines how to expose custom runtime data and tools from your web application to Chrome DevTools for Agents.
## Overview
Third-party developer tools enable your web application 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.
## How It Works: Tool Discovery
Chrome DevTools for Agents uses an event-based mechanism to discover tools exposed by the page. The process follows these steps:
1. **Event Dispatch:** Chrome DevTools for Agents dispatches a `devtoolstooldiscovery` event on the global `window` object.
2. **Listener:** Your application listens for this event and provides the tool definitions.
3. **Response:** Your application must call `event.respondWith()` to register a `ToolGroup` object.
_Note: Chrome DevTools for Agents requests this list automatically after page navigations (e.g., `new_page`, `navigate_page`) or when explicitly requested via the `list_3p_developer_tools()` MCP tool._
## Implementation
To expose tools, implement a listener for the `devtoolstooldiscovery` event and provide a `ToolGroup` containing your tool definitions.
### Type Definitions
Your tools must follow the `ToolDefinition` and `ToolGroup` interfaces:
```typescript
export interface ToolDefinition {
name: string;
description: string;
inputSchema: JSONSchema7;
execute: (args: Record<string, unknown>) => unknown;
}
export interface ToolGroup {
name: string;
description: string;
tools: ToolDefinition[];
}
```
### Example Implementation
```typescript
window.addEventListener(
'devtoolstooldiscovery',
(event: DevtoolsToolDiscoveryEvent) => {
event.respondWith({
name: 'Page-specific DevTools',
description: "Provide runtime info directly from the page's JavaScript",
tools: [
{
name: 'add',
description: 'Calculates the sum of two numbers.',
inputSchema: {
type: 'object',
properties: {
a: {type: 'number'},
b: {type: 'number'},
},
required: ['a', 'b'],
},
execute: async (input: {a: number; b: number}) => {
return input.a + input.b;
},
},
],
});
},
);
```
## Tool Invocation
Once discovered, MCP clients can execute your tools through Chrome DevTools for Agents using:
- **`execute_3p_developer_tool`**: The standard way to invoke a specific registered tool by name with validated parameters.
- **`evaluate_script`**: Allows for more complex interactions by running a custom script that calls `window.__dtmcp.executeTool()` directly, enabling you to compose functionality.
## Important Considerations
- **Experimental Status:** This feature is currently experimental. APIs may change, and there are no guarantees regarding stability.
- **Security & Scope:**
- **Context:** Third-party developer tools execute only within the context of the page that defines them. They do not persist across origins.
- **Capabilities:** These tools do not grant expanded privileges; they can only execute code that an attacker would already be able to run on that page.
- **DOM Elements:** If your tools require DOM elements as inputs or outputs, they are handled via special UIDs referenced in the accessibility tree.
- **Flags:** The implementation is gated behind the `--categoryExperimentalThirdParty=true` command-line flag.
## /docs/tool-reference.md
<!-- AUTO GENERATED DO NOT EDIT - run 'npm run gen' to update-->
# Chrome DevTools MCP Tool Reference
- **[Input automation](#input-automation)** (10 tools)
- [`click`](#click)
- [`drag`](#drag)
- [`fill`](#fill)
- [`fill_form`](#fill_form)
- [`handle_dialog`](#handle_dialog)
- [`hover`](#hover)
- [`press_key`](#press_key)
- [`type_text`](#type_text)
- [`upload_file`](#upload_file)
- [`click_at`](#click_at)
- **[Navigation automation](#navigation-automation)** (6 tools)
- [`close_page`](#close_page)
- [`list_pages`](#list_pages)
- [`navigate_page`](#navigate_page)
- [`new_page`](#new_page)
- [`select_page`](#select_page)
- [`wait_for`](#wait_for)
- **[Emulation](#emulation)** (2 tools)
- [`emulate`](#emulate)
- [`resize_page`](#resize_page)
- **[Performance](#performance)** (3 tools)
- [`performance_analyze_insight`](#performance_analyze_insight)
- [`performance_start_trace`](#performance_start_trace)
- [`performance_stop_trace`](#performance_stop_trace)
- **[Network](#network)** (2 tools)
- [`get_network_request`](#get_network_request)
- [`list_network_requests`](#list_network_requests)
- **[Debugging](#debugging)** (8 tools)
- [`evaluate_script`](#evaluate_script)
- [`get_console_message`](#get_console_message)
- [`lighthouse_audit`](#lighthouse_audit)
- [`list_console_messages`](#list_console_messages)
- [`take_screenshot`](#take_screenshot)
- [`take_snapshot`](#take_snapshot)
- [`screencast_start`](#screencast_start)
- [`screencast_stop`](#screencast_stop)
- **[Memory](#memory)** (4 tools)
- [`take_memory_snapshot`](#take_memory_snapshot)
- [`get_memory_snapshot_details`](#get_memory_snapshot_details)
- [`get_nodes_by_class`](#get_nodes_by_class)
- [`load_memory_snapshot`](#load_memory_snapshot)
- **[Extensions](#extensions)** (5 tools)
- [`install_extension`](#install_extension)
- [`list_extensions`](#list_extensions)
- [`reload_extension`](#reload_extension)
- [`trigger_extension_action`](#trigger_extension_action)
- [`uninstall_extension`](#uninstall_extension)
- **[Third-party](#third-party)** (2 tools)
- [`execute_3p_developer_tool`](#execute_3p_developer_tool)
- [`list_3p_developer_tools`](#list_3p_developer_tools)
- **[WebMCP](#webmcp)** (2 tools)
- [`execute_webmcp_tool`](#execute_webmcp_tool)
- [`list_webmcp_tools`](#list_webmcp_tools)
## Input automation
### `click`
**Description:** Clicks on the provided element
**Parameters:**
- **uid** (string) **(required)**: The uid of an element on the page from the page content snapshot
- **dblClick** (boolean) _(optional)_: Set to true for double clicks. Default is false.
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
---
### `drag`
**Description:** [`Drag`](#drag) an element onto another element
**Parameters:**
- **from_uid** (string) **(required)**: The uid of the element to [`drag`](#drag)
- **to_uid** (string) **(required)**: The uid of the element to drop into
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
---
### `fill`
**Description:** Type text into an input, text area or select an option from a <select> element.
**Parameters:**
- **uid** (string) **(required)**: The uid of an element on the page from the page content snapshot
- **value** (string) **(required)**: The value to [`fill`](#fill) in. "true" or "false" for checkboxes and toggles, "true" for radio buttons.
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
---
### `fill_form`
**Description:** [`Fill`](#fill) out multiple form elements (inputs, selects, checkboxes, radios) at once. ALWAYS prefer this tool over multiple individual '[`fill`](#fill)' or '[`click`](#click)' calls when interacting with forms. It is significantly faster, more reliable, and reduces turn count. Example: [`Fill`](#fill) username, password, and check "Remember Me" in one call.
**Parameters:**
- **elements** (array) **(required)**: Elements from snapshot to [`fill`](#fill) out.
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
---
### `handle_dialog`
**Description:** If a browser dialog was opened, use this command to handle it
**Parameters:**
- **action** (enum: "accept", "dismiss") **(required)**: Whether to dismiss or accept the dialog
- **promptText** (string) _(optional)_: Optional prompt text to enter into the dialog.
---
### `hover`
**Description:** [`Hover`](#hover) over the provided element
**Parameters:**
- **uid** (string) **(required)**: The uid of an element on the page from the page content snapshot
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
---
### `press_key`
**Description:** Press a key or key combination. Use this when other input methods like [`fill`](#fill)() cannot be used (e.g., keyboard shortcuts, navigation keys, or special key combinations).
**Parameters:**
- **key** (string) **(required)**: A key or a combination (e.g., "Enter", "Control+A", "Control++", "Control+Shift+R"). Modifiers: Control, Shift, Alt, Meta
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
---
### `type_text`
**Description:** Type text using keyboard into a previously focused input
**Parameters:**
- **text** (string) **(required)**: The text to type
- **submitKey** (string) _(optional)_: Optional key to press after typing. E.g., "Enter", "Tab", "Escape"
---
### `upload_file`
**Description:** Upload a file through a provided element.
**Parameters:**
- **filePath** (string) **(required)**: The local path of the file to upload
- **uid** (string) **(required)**: The uid of the file input element or an element that will open file chooser on the page from the page content snapshot
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
---
### `click_at`
**Description:** Clicks at the provided coordinates (requires flag: --experimentalVision=true)
**Parameters:**
- **x** (number) **(required)**: The x coordinate
- **y** (number) **(required)**: The y coordinate
- **dblClick** (boolean) _(optional)_: Set to true for double clicks. Default is false.
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
---
## Navigation automation
### `close_page`
**Description:** Closes the page by its index. The last open page cannot be closed.
**Parameters:**
- **pageId** (number) **(required)**: The ID of the page to close. Call [`list_pages`](#list_pages) to list pages.
---
### `list_pages`
**Description:** Get a list of pages open in the browser.
**Parameters:** None
---
### `navigate_page`
**Description:** Go to a URL, or back, forward, or reload. Use project URL if not specified otherwise.
**Parameters:**
- **handleBeforeUnload** (enum: "accept", "decline") _(optional)_: Whether to auto accept or beforeunload dialogs triggered by this navigation. Default is accept.
- **ignoreCache** (boolean) _(optional)_: Whether to ignore cache on reload.
- **initScript** (string) _(optional)_: A JavaScript script to be executed on each new document before any other scripts for the next navigation.
- **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
- **type** (enum: "url", "back", "forward", "reload") _(optional)_: Navigate the page by URL, back or forward in history, or reload.
- **url** (string) _(optional)_: Target URL (only type=url)
---
### `new_page`
**Description:** Open a new tab and load a URL. Use project URL if not specified otherwise.
**Parameters:**
- **url** (string) **(required)**: URL to load in a new page.
- **background** (boolean) _(optional)_: Whether to open the page in the background without bringing it to the front. Default is false (foreground).
- **isolatedContext** (string) _(optional)_: If specified, the page is created in an isolated browser context with the given name. Pages in the same browser context share cookies and storage. Pages in different browser contexts are fully isolated.
- **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
---
### `select_page`
**Description:** Select a page as a context for future tool calls.
**Parameters:**
- **pageId** (number) **(required)**: The ID of the page to select. Call [`list_pages`](#list_pages) to get available pages.
- **bringToFront** (boolean) _(optional)_: Whether to focus the page and bring it to the top.
---
### `wait_for`
**Description:** Wait for the specified text to appear on the selected page.
**Parameters:**
- **text** (array) **(required)**: Non-empty list of texts. Resolves when any value appears on the page.
- **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
---
## Emulation
### `emulate`
**Description:** Emulates various features on the selected page.
**Parameters:**
- **colorScheme** (enum: "dark", "light", "auto") _(optional)_: [`Emulate`](#emulate) the dark or the light mode. Set to "auto" to reset to the default.
- **cpuThrottlingRate** (number) _(optional)_: Represents the CPU slowdown factor. Omit or set the rate to 1 to disable throttling
- **geolocation** (string) _(optional)_: Geolocation (`<latitude>x<longitude>`) to [`emulate`](#emulate). Latitude between -90 and 90. Longitude between -180 and 180. Omit to clear the geolocation override.
- **networkConditions** (enum: "Offline", "Slow 3G", "Fast 3G", "Slow 4G", "Fast 4G") _(optional)_: Throttle network. Omit to disable throttling.
- **userAgent** (string) _(optional)_: User agent to [`emulate`](#emulate). Set to empty string to clear the user agent override.
- **viewport** (string) _(optional)_: [`Emulate`](#emulate) device viewports '<width>x<height>x<devicePixelRatio>[,mobile][,touch][,landscape]'. 'touch' and 'mobile' to [`emulate`](#emulate) mobile devices. 'landscape' to [`emulate`](#emulate) landscape mode.
---
### `resize_page`
**Description:** Resizes the selected page's window so that the page has specified dimension
**Parameters:**
- **height** (number) **(required)**: Page height
- **width** (number) **(required)**: Page width
---
## Performance
### `performance_analyze_insight`
**Description:** Provides more detailed information on a specific Performance Insight of an insight set that was highlighted in the results of a trace recording.
**Parameters:**
- **insightName** (string) **(required)**: The name of the Insight you want more information on. For example: "DocumentLatency" or "LCPBreakdown"
- **insightSetId** (string) **(required)**: The id for the specific insight set. Only use the ids given in the "Available insight sets" list.
---
### `performance_start_trace`
**Description:** Start a performance trace on the selected webpage. Use to find frontend performance issues, Core Web Vitals (LCP, INP, CLS), and improve page load speed.
**Parameters:**
- **autoStop** (boolean) _(optional)_: Determines if the trace recording should be automatically stopped.
- **filePath** (string) _(optional)_: The absolute file path, or a file path relative to the current working directory, to save the raw trace data. For example, trace.json.gz (compressed) or trace.json (uncompressed).
- **reload** (boolean) _(optional)_: Determines if, once tracing has started, the current selected page should be automatically reloaded. Navigate the page to the right URL using the [`navigate_page`](#navigate_page) tool BEFORE starting the trace if reload or autoStop is set to true.
---
### `performance_stop_trace`
**Description:** Stop the active performance trace recording on the selected webpage.
**Parameters:**
- **filePath** (string) _(optional)_: The absolute file path, or a file path relative to the current working directory, to save the raw trace data. For example, trace.json.gz (compressed) or trace.json (uncompressed).
---
## Network
### `get_network_request`
**Description:** Gets a network request by an optional reqid, if omitted returns the currently selected request in the DevTools Network panel.
**Parameters:**
- **reqid** (number) _(optional)_: The reqid of the network request. If omitted returns the currently selected request in the DevTools Network panel.
- **requestFilePath** (string) _(optional)_: The absolute or relative path to a .network-request file to save the request body to. If omitted, the body is returned inline.
- **responseFilePath** (string) _(optional)_: The absolute or relative path to a .network-response file to save the response body to. If omitted, the body is returned inline.
---
### `list_network_requests`
**Description:** List all requests for the currently selected page since the last navigation.
**Parameters:**
- **includePreservedRequests** (boolean) _(optional)_: Set to true to return the preserved requests over the last 3 navigations.
- **pageIdx** (integer) _(optional)_: Page number to return (0-based). When omitted, returns the first page.
- **pageSize** (integer) _(optional)_: Maximum number of requests to return. When omitted, returns all requests.
- **resourceTypes** (array) _(optional)_: Filter requests to only return requests of the specified resource types. When omitted or empty, returns all requests.
---
## Debugging
### `evaluate_script`
**Description:** Evaluate a JavaScript function inside the currently selected page. Returns the response as JSON,
so returned values have to be JSON-serializable.
**Parameters:**
- **function** (string) **(required)**: A JavaScript function declaration to be executed by the tool in the currently selected page.
Example without arguments: `() => {
return document.title
}` or `async () => {
return await fetch("example.com")
}`.
Example with arguments: `(el) => {
return el.innerText;
}`
- **args** (array) _(optional)_: An optional list of arguments to pass to the function.
- **dialogAction** (string) _(optional)_: Handle dialogs while execution. "accept", "dismiss", or string for response of window.prompt. Defaults to accept.
- **filePath** (string) _(optional)_: The absolute or relative path to a file to save the script output to. If omitted, the output is returned inline.
---
### `get_console_message`
**Description:** Gets a console message by its ID. You can get all messages by calling [`list_console_messages`](#list_console_messages).
**Parameters:**
- **msgid** (number) **(required)**: The msgid of a console message on the page from the listed console messages
---
### `lighthouse_audit`
**Description:** Get Lighthouse score and reports for accessibility, SEO, best practices, and agentic browsing. This excludes performance. For performance audits, run [`performance_start_trace`](#performance_start_trace)
**Parameters:**
- **device** (enum: "desktop", "mobile") _(optional)_: Device to [`emulate`](#emulate).
- **mode** (enum: "navigation", "snapshot") _(optional)_: "navigation" reloads & audits. "snapshot" analyzes current state.
- **outputDirPath** (string) _(optional)_: Directory for reports. If omitted, uses temporary files.
---
### `list_console_messages`
**Description:** List all console messages for the currently selected page since the last navigation.
**Parameters:**
- **includePreservedMessages** (boolean) _(optional)_: Set to true to return the preserved messages over the last 3 navigations.
- **pageIdx** (integer) _(optional)_: Page number to return (0-based). When omitted, returns the first page.
- **pageSize** (integer) _(optional)_: Maximum number of messages to return. When omitted, returns all messages.
- **types** (array) _(optional)_: Filter messages to only return messages of the specified resource types. When omitted or empty, returns all messages.
---
### `take_screenshot`
**Description:** Take a screenshot of the page or element.
**Parameters:**
- **filePath** (string) _(optional)_: The absolute path, or a path relative to the current working directory, to save the screenshot to instead of attaching it to the response.
- **format** (enum: "png", "jpeg", "webp") _(optional)_: Type of format to save the screenshot as. Default is "png"
- **fullPage** (boolean) _(optional)_: If set to true takes a screenshot of the full page instead of the currently visible viewport. Incompatible with uid.
- **quality** (number) _(optional)_: Compression quality for JPEG and WebP formats (0-100). Higher values mean better quality but larger file sizes. Ignored for PNG format.
- **uid** (string) _(optional)_: The uid of an element on the page from the page content snapshot. If omitted, takes a page screenshot.
---
### `take_snapshot`
**Description:** Take a text snapshot of the currently selected page based on the a11y tree. The snapshot lists page elements along with a unique
identifier (uid). Always use the latest snapshot. Prefer taking a snapshot over taking a screenshot. The snapshot indicates the element selected
in the DevTools Elements panel (if any).
**Parameters:**
- **filePath** (string) _(optional)_: The absolute path, or a path relative to the current working directory, to save the snapshot to instead of attaching it to the response.
- **verbose** (boolean) _(optional)_: Whether to include all possible information available in the full a11y tree. Default is false.
---
### `screencast_start`
**Description:** Starts recording a screencast (video) of the selected page in specified format. (requires flag: --experimentalScreencast=true)
**Parameters:**
- **filePath** (string) _(optional)_: Output file path (.webm,.mp4 are supported). Uses mkdtemp to generate a unique path if not provided.
---
### `screencast_stop`
**Description:** Stops the active screencast recording on the selected page. (requires flag: --experimentalScreencast=true)
**Parameters:** None
---
## Memory
### `take_memory_snapshot`
**Description:** Capture a heap snapshot of the currently selected page. Use to analyze the memory distribution of JavaScript objects and debug memory leaks.
**Parameters:**
- **filePath** (string) **(required)**: A path to a .heapsnapshot file to save the heapsnapshot to.
---
### `get_memory_snapshot_details`
**Description:** Loads a memory heapsnapshot and returns all available information including statistics, static data, and aggregated node information. Supports pagination for aggregates. (requires flag: --experimentalMemory=true)
**Parameters:**
- **filePath** (string) **(required)**: A path to a .heapsnapshot file to read.
- **pageIdx** (number) _(optional)_: The page index for pagination of aggregates.
- **pageSize** (number) _(optional)_: The page size for pagination of aggregates.
---
### `get_nodes_by_class`
**Description:** Loads a memory heapsnapshot and returns instances of a specific class with their stable IDs. (requires flag: --experimentalMemory=true)
**Parameters:**
- **filePath** (string) **(required)**: A path to a .heapsnapshot file to read.
- **uid** (number) **(required)**: The unique UID for the class, obtained from aggregates listing.
- **pageIdx** (number) _(optional)_: The page index for pagination.
- **pageSize** (number) _(optional)_: The page size for pagination.
---
### `load_memory_snapshot`
**Description:** Loads a memory heapsnapshot and returns snapshot summary stats. (requires flag: --experimentalMemory=true)
**Parameters:**
- **filePath** (string) **(required)**: A path to a .heapsnapshot file to read.
---
## Extensions
> NOTE: The Extensions category is not active by default. Use the '--categoryExtensions' flag.
### `install_extension`
**Description:** Installs a Chrome extension from the given path. (requires flag: --categoryExtensions=true)
**Parameters:**
- **path** (string) **(required)**: Absolute path to the unpacked extension folder.
---
### `list_extensions`
**Description:** Lists all the Chrome extensions installed in the browser. This includes their name, ID, version, and enabled status. (requires flag: --categoryExtensions=true)
**Parameters:** None
---
### `reload_extension`
**Description:** Reloads an unpacked Chrome extension by its ID. (requires flag: --categoryExtensions=true)
**Parameters:**
- **id** (string) **(required)**: ID of the extension to reload.
---
### `trigger_extension_action`
**Description:** Triggers the default action of an extension by its ID. (requires flag: --categoryExtensions=true)
**Parameters:**
- **id** (string) **(required)**: ID of the extension to trigger the action for.
---
### `uninstall_extension`
**Description:** Uninstalls a Chrome extension by its ID. (requires flag: --categoryExtensions=true)
**Parameters:**
- **id** (string) **(required)**: ID of the extension to uninstall.
---
## Third-party
> NOTE: The Third-party category is not active by default. Use the '--categoryExperimentalThirdParty' flag.
### `execute_3p_developer_tool`
**Description:** Executes a tool exposed by the page. (requires flag: --categoryExperimentalThirdParty=true)
**Parameters:**
- **toolName** (string) **(required)**: The name of the tool to execute
- **params** (string) _(optional)_: The JSON-stringified parameters to pass to the tool
---
### `list_3p_developer_tools`
**Description:** Lists all third-party developer tools the page exposes for providing runtime information.
Third-party developer tools can be called via the '[`execute_3p_developer_tool`](#execute_3p_developer_tool)()' MCP tool.
Alternatively, third-party developer tools can be executed by calling '[`evaluate_script`](#evaluate_script)' and adding the
following command to the script:
'window.\_\_dtmcp.executeTool(toolName, params)'
This might be helpful when the third-party developer tools return non-serializable values or when composing
third-party developer tools with additional functionality. (requires flag: --categoryExperimentalThirdParty=true)
**Parameters:** None
---
## WebMCP
> NOTE: The WebMCP category is not active by default. Use the '--categoryExperimentalWebmcp' flag.
### `execute_webmcp_tool`
**Description:** Executes a WebMCP tool exposed by the page. (requires flag: --categoryExperimentalWebmcp=true)
**Parameters:**
- **toolName** (string) **(required)**: The name of the WebMCP tool to execute
- **input** (string) _(optional)_: The JSON-stringified parameters to pass to the WebMCP tool
---
### `list_webmcp_tools`
**Description:** Lists all WebMCP tools the page exposes. (requires flag: --categoryExperimentalWebmcp=true)
**Parameters:** None
---
## /docs/troubleshooting.md
# Troubleshooting
## General tips
- Run `npx chrome-devtools-mcp@latest --help` to test if the MCP server runs on your machine.
- Make sure that your MCP client uses the same npm and node version as your terminal.
- When configuring your MCP client, try using the `--yes` argument to `npx` to
auto-accept installation prompt.
- Find a specific error in the output of the `chrome-devtools-mcp` server.
Usually, if your client is an IDE, logs would be in the Output pane.
- Search the [GitHub repository issues and discussions](https://github.com/ChromeDevTools/chrome-devtools-mcp) for help or existing similar problems.
## Debugging
Start the MCP server with debugging enabled and a log file:
- `DEBUG=* npx chrome-devtools-mcp@latest --log-file=/path/to/chrome-devtools-mcp.log`
Using `.mcp.json` to debug while using a client:
```json
{
"mcpServers": {
"chrome-devtools": {
"type": "stdio",
"command": "npx",
"args": [
"chrome-devtools-mcp@latest",
"--log-file",
"/path/to/chrome-devtools-mcp.log"
],
"env": {
"DEBUG": "*"
}
}
}
}
```
## Specific problems
### `Error [ERR_MODULE_NOT_FOUND]: Cannot find module ...`
This usually indicates either a non-supported Node version is in use or that the
`npm`/`npx` cache is corrupted. Try clearing the cache, uninstalling
`chrome-devtools-mcp` and installing it again. Clear the cache by running:
```sh
rm -rf ~/.npm/_npx # NOTE: this might remove other installed npx executables.
npm cache clean --force
```
### `Target closed` error
This indicates that the browser could not be started. Make sure that no Chrome
instances are running or close them. Make sure you have the latest stable Chrome
installed and that [your system is able to run Chrome](https://support.google.com/chrome/a/answer/7100626?hl=en).
### Chrome crashes on macOS when using Web Bluetooth
On macOS, Chrome launched by an MCP client application (such as Claude Desktop) may crash when a Web Bluetooth prompt appears. This is caused by a macOS privacy permission violation (TCC).
To resolve this, grant Bluetooth permission to the MCP client application in `System Settings > Privacy & Security > Bluetooth`. After granting permission, restart the client application and start a new MCP session.
### Remote debugging between virtual machine (VM) and host fails
When attempting to connect to Chrome running on a host machine from within a virtual machine (VM), Chrome may reject the connection due to 'Host' header validation. You can bypass this restriction by creating an SSH tunnel from the VM to the host. In the VM, run:
```sh
ssh -N -L 127.0.0.1:9222:127.0.0.1:9222 <user>@<host-ip>
```
Point the MCP connection inside the VM to `http://127.0.0.1:9222`. This allows DevTools to reach the host browser without triggering the Host validation error.
### Operating system sandboxes
Some MCP clients allow sandboxing the MCP server using macOS Seatbelt or Linux
containers. If sandboxes are enabled, `chrome-devtools-mcp` is not able to start
Chrome that requires permissions to create its own sandboxes. As a workaround,
either disable sandboxing for `chrome-devtools-mcp` in your MCP client or use
`--browser-url` to connect to a Chrome instance that you start manually outside
of the MCP client sandbox.
### WSL
By default, `chrome-devtools-mcp` in WSL requires Chrome to be installed within the Linux environment. While it normally attempts to launch Chrome on the Windows side, this currently fails due to a [known WSL issue](https://github.com/microsoft/WSL/issues/14201). Ensure you are using a [Linux distribution compatible with Chrome](https://support.google.com/chrome/a/answer/7100626).
Possible workarounds include:
- **Install Google Chrome in WSL:**
- `wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb`
- `sudo dpkg -i google-chrome-stable_current_amd64.deb`
- **Use Mirrored networking:**
1. Configure [Mirrored networking for WSL](https://learn.microsoft.com/en-us/windows/wsl/networking).
2. Start Chrome on the Windows side with:
`chrome.exe --remote-debugging-port=9222 --user-data-dir=C:\path\to\dir`
3. Start `chrome-devtools-mcp` with:
`npx chrome-devtools-mcp --browser-url http://127.0.0.1:9222`
- **Use PowerShell or Git Bash** instead of WSL.
### Windows 10: Error during discovery for MCP server 'chrome-devtools': MCP error -32000: Connection closed
- **Solution 1** Call using `cmd` (For more info https://github.com/modelcontextprotocol/servers/issues/1082#issuecomment-2791786310)
```json
"mcpServers": {
"chrome-devtools": {
"command": "cmd",
"args": ["/c", "npx", "-y", "chrome-devtools-mcp@latest"]
}
}
```
> **The Key Change:** On Windows, running a Node.js package via `npx` often requires the `cmd /c` prefix to be executed correctly from within another process like VSCode's extension host. Therefore, `"command": "npx"` was replaced with `"command": "cmd"`, and the actual `npx` command was moved into the `"args"` array, preceded by `"/c"`. This fix allows Windows to interpret the command correctly and launch the server.
- **Solution 2** Instead of another layer of shell you can write the absolute path to `npx`:
> Note: The path below is an example. You must adjust it to match the actual location of `npx` on your machine. Depending on your setup, the file extension might be `.cmd`, `.bat`, or `.exe` rather than `.ps1`. Also, ensure you use double backslashes (`\\`) as path delimiters, as required by the JSON format.
```json
"mcpServers": {
"chrome-devtools": {
"command": "C:\\nvm4w\\nodejs\\npx.ps1",
"args": ["-y", "chrome-devtools-mcp@latest"]
}
}
```
### Claude Code plugin installation fails with `Failed to clone repository`
When installing `chrome-devtools-mcp` as a Claude Code plugin (either from the
official marketplace or via `/plugin marketplace add`), the installation may fail
with a timeout error if your environment cannot reach `github.com` on port 443
(HTTPS):
```
Failed to download/cache plugin chrome-devtools-mcp: Failed to clone repository:
Cloning into '...'...
fatal: unable to access 'https://github.com/ChromeDevTools/chrome-devtools-mcp.git/':
Failed to connect to github.com port 443
```
This can happen in environments with restricted outbound HTTPS connectivity,
corporate firewalls, or proxy configurations that block HTTPS git operations.
**Workaround 1: Use SSH instead of HTTPS**
If you have SSH access to GitHub configured, you can redirect all GitHub HTTPS
URLs to use SSH by running:
```sh
git config --global url."git@github.com:".insteadOf "https://github.com/"
```
Then retry the plugin installation. This tells git to use your SSH key for all
GitHub operations instead of HTTPS.
**Workaround 2: Install via CLI instead**
If the plugin marketplace approach fails, you can install `chrome-devtools-mcp`
as an MCP server directly without cloning the repository:
```sh
claude mcp add chrome-devtools --scope user npx chrome-devtools-mcp@latest
```
This bypasses the git clone entirely and uses npm/npx to fetch the package. Note
that this method installs only the MCP server without the bundled skills.
### Connection timeouts with `--autoConnect`
If you are using the `--autoConnect` flag and tools like `list_pages`, `new_page`, or `navigate_page` fail with a timeout (e.g., `ProtocolError: Network.enable timed out` or `The socket connection was closed unexpectedly`), this usually means the MCP server cannot handshake with the running Chrome instance correctly. Ensure:
1. Chrome 144+ is **already** running.
2. Remote debugging is enabled in Chrome via `chrome://inspect/#remote-debugging`.
3. You have allowed the remote debugging connection prompt in the browser.
4. There is no other MCP server or tool trying to connect to the same debugging port.
> [!IMPORTANT]
> In Chrome versions up to 149, connection issues may be caused by frozen or unloaded tabs.
> Chrome DevTools MCP forces all tabs to be loaded, so ensure your system has sufficient resources.
> It is currently not recommended to use Chrome DevTools MCP with browser instances running hundreds of tabs.
> See [Issue #1921](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1921) for more details.
## /eslint.config.mjs
```mjs path="/eslint.config.mjs"
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import js from '@eslint/js';
import stylisticPlugin from '@stylistic/eslint-plugin';
import {defineConfig, globalIgnores} from 'eslint/config';
import importPlugin from 'eslint-plugin-import';
import globals from 'globals';
import tseslint from 'typescript-eslint';
import localPlugin from './scripts/eslint_rules/local-plugin.js';
export default defineConfig([
globalIgnores([
'**/node_modules',
'**/build/',
'tests/tools/fixtures/',
'src/third_party/lighthouse-devtools-mcp-bundle.js',
]),
importPlugin.flatConfigs.typescript,
{
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
globals: {
...globals.node,
},
parserOptions: {
projectService: {
allowDefaultProject: [
'.prettierrc.cjs',
'puppeteer.config.cjs',
'eslint.config.mjs',
'rollup.config.mjs',
'skills/memory-leak-debugging/references/compare_snapshots.js',
],
},
},
parser: tseslint.parser,
},
plugins: {
js,
'@local': localPlugin,
'@typescript-eslint': tseslint.plugin,
'@stylistic': stylisticPlugin,
},
settings: {
'import/resolver': {
typescript: true,
},
},
extends: ['js/recommended'],
},
tseslint.configs.recommended,
tseslint.configs.stylistic,
{
name: 'TypeScript rules',
rules: {
'@local/check-license': 'error',
curly: ['error', 'all'],
'no-undef': 'off',
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'@typescript-eslint/no-explicit-any': [
'error',
{
ignoreRestArgs: true,
},
],
// This optimizes the dependency tracking for type-only files.
'@typescript-eslint/consistent-type-imports': 'error',
// So type-only exports get elided.
'@typescript-eslint/consistent-type-exports': 'error',
// Prefer interfaces over types for shape like.
'@typescript-eslint/consistent-type-definitions': ['error', 'interface'],
'@typescript-eslint/array-type': [
'error',
{
default: 'array-simple',
},
],
'@typescript-eslint/no-floating-promises': 'error',
'import/order': [
'error',
{
'newlines-between': 'always',
alphabetize: {
order: 'asc',
caseInsensitive: true,
},
},
],
'import/no-cycle': [
'error',
{
maxDepth: Infinity,
},
],
'import/enforce-node-protocol-usage': ['error', 'always'],
'@stylistic/function-call-spacing': 'error',
'@stylistic/semi': 'error',
'no-restricted-imports': [
'error',
{
patterns: [
{
regex: '.*chrome-devtools-frontend/(?!mcp/mcp.js$).*',
message:
'Import only the devtools-frontend code exported via node_modules/chrome-devtools-frontend/mcp/mcp.js',
},
],
},
],
},
},
{
name: 'Source files',
files: ['src/**/*.ts'],
rules: {
'@local/no-direct-third-party-imports': 'error',
},
},
{
name: 'Tools definitions',
files: ['src/tools/**/*.ts'],
rules: {
'@local/enforce-zod-schema': 'error',
},
},
{
name: 'Tests',
files: ['**/*.test.ts'],
rules: {
// With the Node.js test runner, `describe` and `it` are technically
// promises, but we don't need to await them.
'@typescript-eslint/no-floating-promises': 'off',
},
},
]);
```
## /gemini-extension.json
```json path="/gemini-extension.json"
{
"name": "chrome-devtools-mcp",
"version": "latest",
"mcpServers": {
"chrome-devtools": {
"command": "npx",
"args": ["chrome-devtools-mcp@latest"]
}
}
}
```
## /package.json
```json path="/package.json"
{
"name": "chrome-devtools-mcp",
"version": "0.26.0",
"description": "MCP server for Chrome DevTools",
"type": "module",
"bin": {
"chrome-devtools-mcp": "./build/src/bin/chrome-devtools-mcp.js",
"chrome-devtools": "./build/src/bin/chrome-devtools.js"
},
"main": "./build/src/index.js",
"scripts": {
"cli:generate": "node --experimental-strip-types scripts/generate-cli.ts",
"clean": "node -e \"require('fs').rmSync('build', {recursive: true, force: true})\"",
"bundle": "npm run clean && npm run build && rollup -c rollup.config.mjs && node -e \"require('fs').rmSync('build/node_modules', {recursive: true, force: true})\" && node --experimental-strip-types scripts/append-lighthouse-notices.ts",
"build": "tsc && node --experimental-strip-types --no-warnings=ExperimentalWarning scripts/post-build.ts",
"typecheck": "tsc --noEmit",
"format": "eslint --cache --fix . && prettier --write --cache .",
"check-format": "eslint --cache . && prettier --check --cache .;",
"gen": "npm run build && npm run docs:generate && npm run cli:generate && npm run update-metrics && npm run format",
"docs:generate": "node --experimental-strip-types scripts/generate-docs.ts",
"start": "npm run build && node build/src/bin/chrome-devtools-mcp.js",
"start-debug": "DEBUG=mcp:* DEBUG_COLORS=false npm run build && node build/src/bin/chrome-devtools-mcp.js",
"test": "npm run build && node scripts/test.mjs",
"test:no-build": "node scripts/test.mjs",
"test:only": "npm run build && node scripts/test.mjs --test-only",
"test:update-snapshots": "npm run build && node scripts/test.mjs --test-update-snapshots",
"prepare": "node --experimental-strip-types scripts/prepare.ts",
"verify-server-json-version": "node --experimental-strip-types scripts/verify-server-json-version.ts",
"update-lighthouse": "node --experimental-strip-types scripts/update-lighthouse.ts",
"update-metrics": "node --experimental-strip-types scripts/update_metrics.ts",
"verify-npm-package": "node scripts/verify-npm-package.mjs",
"eval": "npm run build && node --experimental-strip-types scripts/eval_gemini.ts",
"count-tokens": "node --experimental-strip-types scripts/count_tokens.ts"
},
"files": [
"build/src",
"LICENSE",
"!*.tsbuildinfo",
"!*.js.map"
],
"repository": "ChromeDevTools/chrome-devtools-mcp",
"author": "Google LLC",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/ChromeDevTools/chrome-devtools-mcp/issues"
},
"homepage": "https://github.com/ChromeDevTools/chrome-devtools-mcp#readme",
"mcpName": "io.github.ChromeDevTools/chrome-devtools-mcp",
"devDependencies": {
"@eslint/js": "^9.35.0",
"@google/genai": "^2.0.1",
"@modelcontextprotocol/sdk": "1.29.0",
"@rollup/plugin-commonjs": "^29.0.0",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^16.0.3",
"@stylistic/eslint-plugin": "^5.4.0",
"@types/debug": "^4.1.12",
"@types/filesystem": "^0.0.36",
"@types/node": "^25.0.0",
"@types/semver": "^7.7.1",
"@types/sinon": "^21.0.0",
"@types/yargs": "^17.0.33",
"@typescript-eslint/eslint-plugin": "^8.43.0",
"@typescript-eslint/parser": "^8.43.0",
"chrome-devtools-frontend": "1.0.1628368",
"core-js": "3.49.0",
"debug": "4.4.3",
"eslint": "^9.35.0",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import": "^2.32.0",
"globals": "^17.0.0",
"lighthouse": "13.3.0",
"prettier": "^3.6.2",
"puppeteer": "24.43.0",
"rollup": "4.60.3",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-license": "^3.6.0",
"semver": "^7.7.4",
"sinon": "^22.0.0",
"typescript": "^6.0.2",
"typescript-eslint": "^8.43.0",
"urlpattern-polyfill": "^10.1.0",
"yargs": "18.0.0"
},
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=23"
}
}
```
## /puppeteer.config.cjs
```cjs path="/puppeteer.config.cjs"
/**
* @license
* Copyright 2025 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @type {import("puppeteer").Configuration}
*/
module.exports = {
chrome: {
skipDownload: false,
},
['chrome-headless-shell']: {
skipDownload: true,
},
firefox: {
skipDownload: true,
},
};
```
## /release-please-config.json
```json path="/release-please-config.json"
{
"changelog-sections": [
{"type": "feat", "section": "🎉 Features", "hidden": false},
{"type": "fix", "section": "🛠️ Fixes", "hidden": false},
{"type": "docs", "section": "📄 Documentation", "hidden": false},
{"type": "perf", "section": "⚡ Performance", "hidden": false},
{"type": "refactor", "section": "🏗️ Refactor", "hidden": false},
{"type": "chore", "section": "♻️ Chores", "hidden": true},
{"type": "test", "section": "♻️ Chores", "hidden": true},
{"type": "build", "section": "⚙️ Automation", "hidden": true},
{"type": "ci", "section": "⚙️ Automation", "hidden": true}
],
"packages": {
".": {
"extra-files": [
{
"type": "generic",
"path": "src/version.ts"
},
{
"type": "json",
"path": "server.json",
"jsonpath": "version"
},
{
"type": "json",
"path": "server.json",
"jsonpath": "packages[0].version"
},
{
"type": "json",
"path": ".claude-plugin/marketplace.json",
"jsonpath": "version"
},
{
"type": "json",
"path": ".claude-plugin/plugin.json",
"jsonpath": "version"
},
{
"type": "json",
"path": ".github/plugin/plugin.json",
"jsonpath": "version"
}
]
}
}
}
```
## /rollup.config.mjs
```mjs path="/rollup.config.mjs"
/**
* Copyright 2021 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview taken from {@link https://github.com/GoogleChromeLabs/chromium-bidi/blob/main/rollup.config.mjs | chromium-bidi}
* and modified to specific requirement.
*/
import fs from 'node:fs';
import path from 'node:path';
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import {nodeResolve} from '@rollup/plugin-node-resolve';
import cleanup from 'rollup-plugin-cleanup';
import license from 'rollup-plugin-license';
const isProduction = process.env.NODE_ENV === 'production';
const allowedLicenses = [
'MIT',
'Apache 2.0',
'Apache-2.0',
'BSD-3-Clause',
'BSD-2-Clause',
'ISC',
'0BSD',
];
const thirdPartyDir = './build/src/third_party';
const {devDependencies = {}} = JSON.parse(
fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf-8'),
);
// special case for puppeteer, from which we only bundle puppeteer-core
devDependencies['puppeteer-core'] = devDependencies['puppeteer'];
const aggregatedStats = {
bundlesProcessed: 0,
totalBundles: 0,
bundledPackages: new Set(),
};
const projectNodeModulesPath =
path.join(process.cwd(), 'node_modules') + path.sep;
function getPackageName(modulePath) {
// Handle rollup's virtual module paths (paths starting with 0x00)
const absolutePathStart = modulePath.indexOf(projectNodeModulesPath);
if (absolutePathStart < 0) {
return null;
}
const relativePath = modulePath.slice(
projectNodeModulesPath.length + absolutePathStart,
);
const segments = relativePath.split(path.sep);
// handle scoped packages
if (segments[0].startsWith('@') && segments[1]) {
return `${segments[0]}/${segments[1]}`;
}
return segments[0];
}
/**
* @returns {import('rollup').Plugin}
*/
function listBundledDeps() {
aggregatedStats.totalBundles++;
return {
name: 'gather-bundled-dependencies',
generateBundle(options, bundle) {
for (const chunk of Object.values(bundle)) {
if (chunk.type === 'chunk' && chunk.modules) {
// chunk.modules is an object where keys are the absolute file paths
Object.keys(chunk.modules).forEach(modulePath => {
const packageName = getPackageName(modulePath);
if (packageName) {
aggregatedStats.bundledPackages.add(packageName);
}
});
}
}
aggregatedStats.bundlesProcessed++;
// Only write the file when the last bundle is finished
if (aggregatedStats.bundlesProcessed === aggregatedStats.totalBundles) {
const outputPath = path.join(thirdPartyDir, 'bundled-packages.json');
const bundledDevDeps = Object.fromEntries(
Object.entries(devDependencies).filter(
([name]) =>
aggregatedStats.bundledPackages.has(name) ||
name === 'chrome-devtools-frontend' ||
name === 'lighthouse',
),
);
fs.writeFileSync(outputPath, JSON.stringify(bundledDevDeps, null, 2));
}
},
};
}
const seenDependencies = new Map();
/**
* @param {string} wrapperIndexName
* @param {import('rollup').OutputOptions} [extraOutputOptions={}]
* @param {import('rollup').ExternalOption} [external=[]]
* @returns {import('rollup').RollupOptions}
*/
const bundleDependency = (
wrapperIndexName,
extraOutputOptions = {},
external = [],
) => ({
input: path.join(thirdPartyDir, wrapperIndexName),
output: {
...extraOutputOptions,
file: path.join(thirdPartyDir, wrapperIndexName),
sourcemap: !isProduction,
format: 'esm',
},
plugins: [
cleanup({
// Keep license comments. Other comments are removed due to
// http://b/390559299 and
// https://github.com/microsoft/TypeScript/issues/60811.
comments: [/Copyright/i],
}),
license({
thirdParty: {
allow: {
test: dependency => {
return allowedLicenses.includes(dependency.license);
},
failOnUnlicensed: true,
failOnViolation: true,
},
output: {
file: path.join(thirdPartyDir, 'THIRD_PARTY_NOTICES'),
template(dependencies) {
for (const dependency of dependencies) {
const key = `${dependency.name}:${dependency.version}`;
seenDependencies.set(key, dependency);
}
const stringifiedDependencies = Array.from(
seenDependencies.values(),
).map(dependency => {
let arr = [];
arr.push(`Name: ${dependency.name ?? 'N/A'}`);
let url = dependency.homepage ?? dependency.repository;
if (url !== null && typeof url !== 'string') {
url = url.url;
}
arr.push(`URL: ${url ?? 'N/A'}`);
arr.push(`Version: ${dependency.version ?? 'N/A'}`);
arr.push(`License: ${dependency.license ?? 'N/A'}`);
if (dependency.licenseText !== null) {
arr.push('');
arr.push(dependency.licenseText.replaceAll('\r', ''));
}
return arr.join('\n');
});
// Manual license handling for chrome-devtools-frontend third_party
const tsConfig = JSON.parse(
fs.readFileSync(
path.join(process.cwd(), 'tsconfig.json'),
'utf-8',
),
);
const thirdPartyDirectories = tsConfig.include.filter(location =>
location.includes(
'node_modules/chrome-devtools-frontend/front_end/third_party',
),
);
const manualLicenses = [];
// Add chrome-devtools-frontend main license
const cdtfLicensePath = path.join(
process.cwd(),
'node_modules/chrome-devtools-frontend/LICENSE',
);
if (fs.existsSync(cdtfLicensePath)) {
manualLicenses.push(
[
'Name: chrome-devtools-frontend',
'License: Apache-2.0',
'',
fs.readFileSync(cdtfLicensePath, 'utf-8'),
].join('\n'),
);
}
// Add chrome-devtools-frontend main license
const lighthouseLicensePath = path.join(
process.cwd(),
'node_modules/lighthouse/LICENSE',
);
if (fs.existsSync(lighthouseLicensePath)) {
manualLicenses.push(
[
'Name: lighthouse',
'License: Apache-2.0',
'',
fs.readFileSync(lighthouseLicensePath, 'utf-8'),
].join('\n'),
);
}
for (const thirdPartyDir of thirdPartyDirectories) {
const fullPath = path.join(process.cwd(), thirdPartyDir);
const licenseFile = path.join(fullPath, 'LICENSE');
if (fs.existsSync(licenseFile)) {
const name = path.basename(thirdPartyDir);
manualLicenses.push(
[
`Name: ${name}`,
`License:`,
'',
fs.readFileSync(licenseFile, 'utf-8').replaceAll('\r', ''),
].join('\n'),
);
}
}
if (manualLicenses.length > 0) {
stringifiedDependencies.push(...manualLicenses);
}
const divider =
'\n\n-------------------- DEPENDENCY DIVIDER --------------------\n\n';
return stringifiedDependencies.join(divider);
},
},
},
}),
listBundledDeps(),
commonjs(),
json(),
nodeResolve(),
],
external,
});
export default [
bundleDependency(
'index.js',
{
inlineDynamicImports: true,
},
(source, importer, _isResolved) => {
if (
source === 'yargs' &&
importer &&
importer.includes('puppeteer-core')
) {
return true;
}
const existingExternals = [
'./bidi.js',
'../bidi/bidi.js',
'./lighthouse-devtools-mcp-bundle.js',
];
if (existingExternals.includes(source)) {
return true;
}
return false;
},
),
bundleDependency(
'devtools-formatter-worker.js',
{
inlineDynamicImports: true,
},
(_source, _importer, _isResolved) => false,
),
bundleDependency(
'devtools-heap-snapshot-worker.js',
{
inlineDynamicImports: true,
},
(_source, _importer, _isResolved) => false,
),
];
```
## /scripts/append-lighthouse-notices.ts
```ts path="/scripts/append-lighthouse-notices.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import fs from 'node:fs';
import path from 'node:path';
const ROOT_DIR = process.cwd();
const TARGET_DIR = path.join(ROOT_DIR, 'build/src/third_party');
const SOURCE_DIR = path.join(ROOT_DIR, 'src/third_party');
function main() {
const lighthouseNotices = fs.readFileSync(
path.join(SOURCE_DIR, 'LIGHTHOUSE_MCP_BUNDLE_THIRD_PARTY_NOTICES'),
'utf8',
);
const bundledNotices = fs.readFileSync(
path.join(TARGET_DIR, 'THIRD_PARTY_NOTICES'),
'utf8',
);
fs.writeFileSync(
path.join(TARGET_DIR, 'THIRD_PARTY_NOTICES'),
bundledNotices +
'\n\n-------------------- DEPENDENCY DIVIDER --------------------\n\n' +
lighthouseNotices,
);
console.log('Done.');
}
main();
```
## /scripts/count_tokens.ts
```ts path="/scripts/count_tokens.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {readFileSync} from 'node:fs';
import {parseArgs} from 'node:util';
import {GoogleGenAI} from '@google/genai';
const ai = new GoogleGenAI({apiKey: process.env.GEMINI_API_KEY});
const {values, positionals} = parseArgs({
options: {
model: {
type: 'string',
default: 'gemini-2.5-flash',
},
file: {
type: 'string',
short: 'f',
},
},
allowPositionals: true,
});
let contents = positionals[0];
if (values.file) {
contents = readFileSync(values.file, 'utf8');
}
if (!contents) {
console.error('Usage: npm run count-tokens -- [-f <file>] [<text>]');
process.exit(1);
}
const response = await ai.models.countTokens({
model: values.model,
contents,
});
console.log(`Input: ${values.file || positionals[0]}`);
console.log(`Tokens: ${response.totalTokens}`);
```
## /scripts/eslint_rules/check-license-rule.js
```js path="/scripts/eslint_rules/check-license-rule.js"
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
const currentYear = new Date().getFullYear();
const licenseHeader = `
/**
* @license
* Copyright ${currentYear} Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
`;
export default {
name: 'check-license',
meta: {
type: 'layout',
docs: {
description: 'Validate existence of license header',
},
fixable: 'code',
schema: [],
messages: {
licenseRule: 'Add license header.',
emptyLine: 'Add empty line after license header.',
},
},
defaultOptions: [],
create(context) {
const sourceCode = context.getSourceCode();
const comments = sourceCode.getAllComments();
let insertAfter = [0, 0];
let header = null;
// Check only the first 2 comments
for (let index = 0; index < 2; index++) {
const comment = comments[index];
if (!comment) {
break;
}
// Shebang comments should be at the top
if (
comment.type === 'Shebang' ||
(comment.type === 'Line' && comment.value.startsWith('#!'))
) {
insertAfter = comment.range;
continue;
}
if (comment.type === 'Block') {
header = comment;
break;
}
}
return {
Program(node) {
if (context.getFilename().endsWith('.json')) {
return;
}
if (
header &&
(header.value.includes('@license') ||
header.value.includes('License') ||
header.value.includes('Copyright'))
) {
const nextToken = sourceCode.getTokenAfter(header, {
includeComments: true,
});
if (
nextToken &&
nextToken.loc.start.line === header.loc.end.line + 1
) {
context.report({
node: node,
loc: header.loc,
messageId: 'emptyLine',
fix(fixer) {
return fixer.insertTextAfter(header, '\n');
},
});
}
return;
}
// Add header license
if (!header || !header.value.includes('@license')) {
context.report({
node: node,
messageId: 'licenseRule',
fix(fixer) {
return fixer.insertTextAfterRange(insertAfter, licenseHeader);
},
});
}
},
};
},
};
```
## /scripts/eslint_rules/enforce-zod-schema-rule.js
```js path="/scripts/eslint_rules/enforce-zod-schema-rule.js"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
export default {
name: 'enforce-zod-schema',
meta: {
type: 'problem',
docs: {
description:
'Disallow .nullable() and .object() in tool schemas. Use optional strings to represent complex objects.',
},
schema: [],
messages: {
noNullable:
'Do not use .nullable() in tool schemas. Use .optional() instead.',
noObject:
'Do not use .object() in tool schemas. Represent complex objects as a short formatted string.',
},
},
defaultOptions: [],
create(context) {
return {
CallExpression(node) {
if (
node.callee.type !== 'MemberExpression' ||
node.callee.property.type !== 'Identifier'
) {
return;
}
const methodName = node.callee.property.name;
// We don't validate that .nullable() is called on a ZodObject
// specifically - this intentionally catches all .nullable() calls
// in tool schema files.
if (methodName === 'nullable') {
context.report({
node: node.callee.property,
messageId: 'noNullable',
});
}
if (methodName === 'object') {
// Only flag zod.object() calls, not arbitrary .object() calls.
const obj = node.callee.object;
if (
obj.type === 'Identifier' &&
(obj.name === 'zod' || obj.name === 'z')
) {
context.report({
node: node.callee.property,
messageId: 'noObject',
});
}
}
},
};
},
};
```
## /scripts/eslint_rules/local-plugin.js
```js path="/scripts/eslint_rules/local-plugin.js"
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import checkLicenseRule from './check-license-rule.js';
import enforceZodSchemaRule from './enforce-zod-schema-rule.js';
import noDirectThirdPartyImportsRule from './no-direct-third-party-imports-rule.js';
export default {
rules: {
'check-license': checkLicenseRule,
'no-direct-third-party-imports': noDirectThirdPartyImportsRule,
'enforce-zod-schema': enforceZodSchemaRule,
},
};
```
## /scripts/eslint_rules/no-direct-third-party-imports-rule.js
```js path="/scripts/eslint_rules/no-direct-third-party-imports-rule.js"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* ESLint rule that prevents value (non-type) imports of third-party packages
* that should go through the `src/third_party/index.ts` barrel file.
*
* Type-only imports are allowed because they are erased at compile time and
* do not affect the bundle.
*
* This catches a class of bugs where a direct import works in development
* (because devDependencies are installed) but fails once the package is
* bundled and published via `npm pack`.
*
* The list of bundled packages is derived dynamically by scanning
* `src/third_party/*.ts` for import/export statements at ESLint load time.
*
* See https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1123
*/
import {readdirSync, readFileSync} from 'node:fs';
import {join} from 'node:path';
const THIRD_PARTY_DIR = join(
import.meta.dirname,
'..',
'..',
'src',
'third_party',
);
/**
* Parse all .ts files in src/third_party/ and extract the bare package names
* from import/export statements. Relative imports and node_modules paths
* (used for chrome-devtools-frontend) are skipped.
*/
function discoverBundledPackages() {
const packages = new Set();
// Match `from 'pkg'` (may appear on a different line than `import`)
// and side-effect imports like `import 'pkg'`.
const fromRe = /from\s+['"]([^'"]+)['"]/g;
const sideEffectRe = /^import\s+['"]([^'"]+)['"]/gm;
let files;
try {
files = readdirSync(THIRD_PARTY_DIR).filter(f => f.endsWith('.ts'));
} catch {
return [];
}
for (const file of files) {
const content = readFileSync(join(THIRD_PARTY_DIR, file), 'utf8');
for (const re of [fromRe, sideEffectRe]) {
re.lastIndex = 0;
let match;
while ((match = re.exec(content)) !== null) {
const source = match[1];
// Skip relative imports and node_modules paths.
if (source.startsWith('.') || source.startsWith('/')) {
continue;
}
// Extract the bare package name (handle scoped packages like @foo/bar).
const parts = source.split('/');
const pkg = source.startsWith('@')
? parts.slice(0, 2).join('/')
: parts[0];
packages.add(pkg);
}
}
}
return [...packages];
}
const THIRD_PARTY_PACKAGES = discoverBundledPackages();
/** Matches any import source that starts with one of the restricted packages. */
function isRestrictedSource(source) {
return THIRD_PARTY_PACKAGES.some(
pkg => source === pkg || source.startsWith(pkg + '/'),
);
}
/** Returns true when the file is inside src/third_party/. */
function isThirdPartyBarrel(filename) {
const normalized = filename.replace(/\\/g, '/');
return normalized.includes('/src/third_party/');
}
export default {
name: 'no-direct-third-party-imports',
meta: {
type: 'problem',
docs: {
description:
'Disallow value imports of bundled third-party packages outside of src/third_party/',
},
schema: [],
messages: {
noDirectImport:
'Do not import "{{source}}" directly. Use the re-export from "src/third_party/index.js" instead so the import survives bundling. (Type-only imports are fine.)',
},
},
defaultOptions: [],
create(context) {
const filename = context.filename;
if (isThirdPartyBarrel(filename)) {
return {};
}
return {
ImportDeclaration(node) {
// `import type { Foo } from '...'` is always safe.
if (node.importKind === 'type') {
return;
}
const source = node.source.value;
if (!isRestrictedSource(source)) {
return;
}
// If every specifier is `type`, the import is still safe.
// e.g. `import { type Foo, type Bar } from '...'`
const hasValueSpecifier = node.specifiers.some(
s => s.type !== 'ImportSpecifier' || s.importKind !== 'type',
);
if (!hasValueSpecifier) {
return;
}
context.report({
node,
messageId: 'noDirectImport',
data: {source},
});
},
};
},
};
```
## /scripts/eval_gemini.ts
```ts path="/scripts/eval_gemini.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import fs from 'node:fs';
import path from 'node:path';
import {pathToFileURL} from 'node:url';
import {parseArgs} from 'node:util';
import {GoogleGenAI, mcpToTool} from '@google/genai';
import {Client} from '@modelcontextprotocol/sdk/client/index.js';
import {StdioClientTransport} from '@modelcontextprotocol/sdk/client/stdio.js';
import {TestServer} from '../build/tests/server.js';
const ROOT_DIR = path.resolve(import.meta.dirname, '..');
const SCENARIOS_DIR = path.join(import.meta.dirname, 'eval_scenarios');
const SKILL_PATH = path.join(ROOT_DIR, 'skills', 'chrome-devtools', 'SKILL.md');
// Define schema for our test scenarios
export interface CapturedFunctionCall {
name: string;
args: Record<string, unknown>;
}
export interface TestScenario {
prompt: string;
maxTurns: number;
expectations: (calls: CapturedFunctionCall[]) => void;
htmlRoute?: {
path: string;
htmlContent: string;
};
/** Extra CLI flags passed to the MCP server (e.g. '--experimental-page-id-routing'). */
serverArgs?: string[];
}
async function loadScenario(scenarioPath: string): Promise<TestScenario> {
const module = await import(pathToFileURL(scenarioPath).href);
if (!module.scenario) {
throw new Error(
`Scenario file ${scenarioPath} does not export a 'scenario' object.`,
);
}
return module.scenario;
}
async function runSingleScenario(
scenarioPath: string,
apiKey: string,
server: TestServer,
modelId: string,
debug: boolean,
includeSkill: boolean,
): Promise<void> {
const debugLog = (...args: unknown[]) => {
if (debug) {
console.log(...args);
}
};
const absolutePath = path.resolve(scenarioPath);
debugLog(
`\n### Running Scenario: ${path.relative(ROOT_DIR, absolutePath)} ###`,
);
let client: Client | undefined;
let transport: StdioClientTransport | undefined;
try {
const loadedScenario = await loadScenario(absolutePath);
const scenario = {...loadedScenario};
// Prepend skill content if requested
if (includeSkill) {
if (!fs.existsSync(SKILL_PATH)) {
throw new Error(
`Skill file not found at ${SKILL_PATH}. Please ensure the skill file exists.`,
);
}
const skillContent = fs.readFileSync(SKILL_PATH, 'utf-8');
scenario.prompt = `${skillContent}\n\n---\n\n${scenario.prompt}`;
}
// Append random queryid to avoid caching issues and test distinct runs
const randomId = Math.floor(Math.random() * 1000000);
scenario.prompt = `${scenario.prompt}\nqueryid=${randomId}`;
if (scenario.htmlRoute) {
server.addHtmlRoute(
scenario.htmlRoute.path,
scenario.htmlRoute.htmlContent,
);
scenario.prompt = scenario.prompt.replace(
'<TEST_URL>',
server.getRoute(scenario.htmlRoute.path),
);
}
// Path to the compiled MCP server
const serverPath = path.join(
ROOT_DIR,
'build/src/bin/chrome-devtools-mcp.js',
);
if (!fs.existsSync(serverPath)) {
throw new Error(
`MCP server not found at ${serverPath}. Please run 'npm run build' first.`,
);
}
// Environment variables
const env: Record<string, string> = {};
Object.entries(process.env).forEach(([key, value]) => {
if (value !== undefined) {
env[key] = value;
}
});
env['CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS'] = 'true';
const args = [serverPath];
if (!debug) {
args.push('--headless');
}
if (scenario.serverArgs) {
args.push(...scenario.serverArgs);
}
transport = new StdioClientTransport({
command: 'node',
args,
env,
stderr: debug ? 'inherit' : 'ignore',
});
client = new Client(
{name: 'gemini-eval-client', version: '1.0.0'},
{capabilities: {}},
);
await client.connect(transport);
const allCalls: CapturedFunctionCall[] = [];
const originalCallTool = client.callTool.bind(client);
client.callTool = async (request, schema) => {
// NOTE: request.name is the original name as the MCP client sees it.
// mcpToTool handles the conversion from Gemini sanitized name to original name.
debugLog(
`Executing tool: ${request.name} with args: ${JSON.stringify(request.arguments)}`,
);
allCalls.push({
name: request.name,
args: (request.arguments as Record<string, unknown>) || {},
});
const response = await originalCallTool(request, schema);
debugLog(`Tool response: ${JSON.stringify(response)}`);
return response;
};
const ai = new GoogleGenAI({apiKey});
debugLog(`\n--- Prompt ---\n${scenario.prompt}`);
const result = await ai.models.generateContent({
model: modelId,
contents: scenario.prompt,
config: {
tools: [mcpToTool(client)],
automaticFunctionCalling: {
maximumRemoteCalls: scenario.maxTurns,
},
},
});
debugLog(`\n--- Response ---\n${result.text}`);
debugLog('\nVerifying expectations...');
scenario.expectations(allCalls);
} finally {
try {
await client?.close();
} catch (e) {
console.error('Error closing client:', e);
}
}
}
async function main() {
const apiKey = process.env.GEMINI_API_KEY;
if (!apiKey) {
throw new Error('GEMINI_API_KEY environment variable is required.');
}
const {values, positionals} = parseArgs({
options: {
model: {
type: 'string',
default: 'gemini-2.5-flash',
},
debug: {
type: 'boolean',
default: false,
},
repeat: {
type: 'boolean',
default: false,
},
'include-skill': {
type: 'boolean',
default: false,
},
},
allowPositionals: true,
});
const modelId = values.model;
const debug = values.debug;
const repeat = values.repeat;
const includeSkill = values['include-skill'];
const scenarioFiles =
positionals.length > 0
? positionals.map(p => path.resolve(p))
: fs
.readdirSync(SCENARIOS_DIR)
.filter(file => file.endsWith('.ts') || file.endsWith('.js'))
.map(file => path.join(SCENARIOS_DIR, file));
const server = new TestServer(TestServer.randomPort());
await server.start();
let successCount = 0;
let failureCount = 0;
try {
for (const scenarioPath of scenarioFiles) {
for (let i = 1; i <= (repeat ? 3 : 1); i++) {
try {
if (debug) {
console.log(
`Running scenario: ${path.relative(ROOT_DIR, scenarioPath)} (Run ${i}/3)`,
);
}
await runSingleScenario(
scenarioPath,
apiKey,
server,
modelId,
debug,
includeSkill,
);
console.log(`✔ ${path.relative(ROOT_DIR, scenarioPath)} (Run ${i})`);
successCount++;
} catch (e) {
console.error(
`✖ ${path.relative(ROOT_DIR, scenarioPath)} (Run ${i})`,
);
console.error(e);
failureCount++;
} finally {
server.restore();
}
}
}
} finally {
await server.stop();
}
console.log(`\nSummary: ${successCount} passed, ${failureCount} failed`);
if (failureCount > 0) {
process.exit(1);
}
}
main().catch(error => {
console.error('Fatal error:', error);
process.exit(1);
});
```
## /scripts/eval_scenarios/console_test.ts
```ts path="/scripts/eval_scenarios/console_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
prompt: 'Navigate to <TEST_URL> and check the console messages.',
maxTurns: 2,
htmlRoute: {
path: '/console_test.html',
htmlContent: `
<script>
console.log('Test log message');
console.error('Test error message');
</script>
`,
},
expectations: calls => {
assert.strictEqual(calls.length, 2);
assert.ok(
calls[0].name === 'navigate_page' || calls[0].name === 'new_page',
'First call should be navigation',
);
assert.strictEqual(
calls[1].name,
'list_console_messages',
'Second call should be list_console_messages',
);
},
};
```
## /scripts/eval_scenarios/emulation_test.ts
```ts path="/scripts/eval_scenarios/emulation_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
prompt: 'Emulate offline network conditions.',
maxTurns: 2,
expectations: calls => {
assert.strictEqual(calls.length, 1);
assert.strictEqual(calls[0].name, 'emulate');
assert.strictEqual(calls[0].args.networkConditions, 'Offline');
},
};
```
## /scripts/eval_scenarios/emulation_userAgent_test.ts
```ts path="/scripts/eval_scenarios/emulation_userAgent_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
prompt: 'Emulate iPhone 14 user agent',
maxTurns: 2,
expectations: calls => {
assert.strictEqual(calls.length, 1);
assert.strictEqual(calls[0].name, 'emulate');
assert.deepStrictEqual(
calls[0].args.userAgent,
'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1',
);
},
};
```
## /scripts/eval_scenarios/emulation_viewport_test.ts
```ts path="/scripts/eval_scenarios/emulation_viewport_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
prompt: 'Emulate iPhone 14 viewport',
maxTurns: 2,
expectations: calls => {
assert.strictEqual(calls.length, 1);
assert.strictEqual(calls[0].name, 'emulate');
assert.deepStrictEqual(calls[0].args.viewport, '390x844x3,mobile,touch');
},
};
```
## /scripts/eval_scenarios/fill_select_and_checkboxes_test.ts
```ts path="/scripts/eval_scenarios/fill_select_and_checkboxes_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
prompt:
'Go to <TEST_URL>, fill the form with size = 2 CPUs and components = [docker, nginx].',
maxTurns: 4, // allow for at least one extra turn to verify there are no extra clicks after fill_form
htmlRoute: {
path: '/input_test.html',
htmlContent: `
<form action="/post" method="POST">
<div>
<label for="size">CPU/Memory size:</label>
<select id="size" name="size" required>
<option value="small">1 vCPU, 2GB RAM</option>
<option value="medium">2 vCPU, 4GB RAM</option>
<option value="large">4 vCPU, 8GB RAM</option>
</select>
</div>
<br>
<div>
<p>Pre-installed components:</p>
<input type="checkbox" id="docker" name="components" value="docker">
<label for="docker">Docker</label><br>
<input type="checkbox" id="nodejs" name="components" value="nodejs">
<label for="nodejs">Node.js</label><br>
<input type="checkbox" id="python" name="components" value="python">
<label for="python">Python</label><br>
<input type="checkbox" id="nginx" name="components" value="nginx">
<label for="nginx">Nginx</label>
</div>
<button type="submit">Spawn Server</button>
</form>
`,
},
expectations: calls => {
assert.ok(calls.length >= 3, 'Not enough calls made');
assert.ok(
calls[0].name === 'navigate_page' || calls[0].name === 'new_page',
);
assert.strictEqual(calls[1].name, 'take_snapshot');
assert.strictEqual(calls[2].name, 'fill_form');
const elements = calls[2].args.elements as Array<{
uid: string;
value: string;
}>;
assert.strictEqual(
elements.length,
3,
'fill_form should be used with all form elements at once',
);
const uids = new Set(elements.map(e => e.uid));
assert.strictEqual(
uids.size,
3,
'fill_form should target three distinct elements',
);
const values = elements.map(e => e.value).sort();
assert.deepStrictEqual(
values,
['2 vCPU, 4GB RAM', 'true', 'true'],
'fill_form should be used with correct values',
);
const submitUid = '1_15';
const extraFormInteractions = calls
.slice(3)
.filter(
c => ['fill', 'click'].includes(c.name) && c.args.uid !== submitUid,
);
assert.deepEqual(
extraFormInteractions.length,
0,
'No extra clicks and fills after fill_form',
);
},
};
```
## /scripts/eval_scenarios/fix_webpage_issues_test.ts
```ts path="/scripts/eval_scenarios/fix_webpage_issues_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*
* Eval scenario: user asks to fix issues with their webpage (no URL given).
* When no URL is provided, the model should pick the current frontend and run
* and inspect it. Verifies the MCP server is invoked and the model opens the
* frontend and inspects it (snapshot, console, or network).
*
* Note: Tools like performance_start_trace, take_snapshot, list_console_messages,
* and list_network_requests do not require a URL in the prompt—they operate on
* the currently selected page. Only navigate_page/new_page need a URL to open
* a page; the eval runner injects the test URL when htmlRoute is set.
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
const INSPECTION_TOOLS = [
'take_snapshot',
'list_console_messages',
'list_network_requests',
];
export const scenario: TestScenario = {
prompt: 'Can you fix issues with my webpage?',
maxTurns: 4,
htmlRoute: {
path: '/fix_issues_test.html',
htmlContent: `
<h1>Test Page</h1>
<p>Some content</p>
<script>
console.error('Intentional error for testing');
</script>
`,
},
expectations: calls => {
const NAVIGATION_TOOLS = ['navigate_page', 'new_page'];
assert.ok(
calls.length >= 2,
'Expected at least navigation and one inspection',
);
const navigationIndex = calls.findIndex(c =>
NAVIGATION_TOOLS.includes(c.name),
);
assert.ok(
navigationIndex !== -1,
`Expected a navigation call (${NAVIGATION_TOOLS.join(' or ')}), got: ${calls.map(c => c.name).join(', ')}`,
);
const afterNavigation = calls.slice(navigationIndex + 1);
const inspectionCalls = afterNavigation.filter(c =>
INSPECTION_TOOLS.includes(c.name),
);
assert.ok(
inspectionCalls.length >= 1,
`Expected at least one inspection tool (${INSPECTION_TOOLS.join(', ')}) after navigation, got: ${calls.map(c => c.name).join(', ')}`,
);
},
};
```
## /scripts/eval_scenarios/frontend_snapshot_test.ts
```ts path="/scripts/eval_scenarios/frontend_snapshot_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*
* Eval scenario using "website"/"webpage" wording to verify the model invokes
* the right tools when users ask to open a site and read its content.
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
prompt:
'Open the website at <TEST_URL> and tell me what content is on the page.',
maxTurns: 3,
htmlRoute: {
path: '/frontend_snapshot.html',
htmlContent: '<h1>Frontend Test</h1><p>This is a test webpage.</p>',
},
expectations: calls => {
assert.strictEqual(calls.length, 2);
assert.ok(
calls[0].name === 'navigate_page' || calls[0].name === 'new_page',
'First call should be navigation',
);
assert.strictEqual(
calls[1].name,
'take_snapshot',
'Second call should be take_snapshot to read page content',
);
},
};
```
## /scripts/eval_scenarios/input_parallel_test.ts
```ts path="/scripts/eval_scenarios/input_parallel_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
prompt:
'Go to <TEST_URL>, fill the input with "hello world" and click the button five times in parallel.',
maxTurns: 10,
htmlRoute: {
path: '/input_test.html',
htmlContent: `
<input type="text" id="test-input" />
<button id="test-button">Submit</button>
`,
},
expectations: calls => {
assert.strictEqual(calls.length, 8);
assert.ok(
calls[0].name === 'navigate_page' || calls[0].name === 'new_page',
);
assert.ok(calls[1].name === 'take_snapshot');
assert.ok(calls[2].name === 'fill');
for (let i = 3; i < 8; i++) {
assert.ok(calls[i].name === 'click');
assert.strictEqual(Boolean(calls[i].args.includeSnapshot), false);
}
},
};
```
## /scripts/eval_scenarios/input_test.ts
```ts path="/scripts/eval_scenarios/input_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
prompt:
'Go to <TEST_URL>, fill the input with "hello world" and click the button.',
maxTurns: 4,
htmlRoute: {
path: '/input_test.html',
htmlContent: `
<input type="text" id="test-input" />
<button id="test-button">Submit</button>
`,
},
expectations: calls => {
assert.strictEqual(calls.length, 4);
assert.ok(
calls[0].name === 'navigate_page' || calls[0].name === 'new_page',
);
assert.ok(calls[1].name === 'take_snapshot');
assert.ok(calls[2].name === 'fill');
assert.ok(calls[3].name === 'click');
},
};
```
## /scripts/eval_scenarios/isolated_context_test.ts
```ts path="/scripts/eval_scenarios/isolated_context_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
prompt:
'Create a new page <TEST_URL> in an isolated context called contextB. Take a screenshot there.',
maxTurns: 3,
htmlRoute: {
path: '/test.html',
htmlContent: `
<h1>test</h1>
`,
},
expectations: calls => {
console.log(JSON.stringify(calls, null, 2));
assert.strictEqual(calls.length, 2);
assert.ok(calls[0].name === 'new_page', 'First call should be navigation');
assert.deepStrictEqual(calls[0].args.isolatedContext, 'contextB');
assert.ok(
calls[1].name === 'take_screenshot',
'Second call should be a screenshot',
);
},
};
```
## /scripts/eval_scenarios/lighthouse_a11y_test.ts
```ts path="/scripts/eval_scenarios/lighthouse_a11y_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
prompt: 'Check a11y issues on the current page',
maxTurns: 1,
expectations: calls => {
assert.strictEqual(calls.length, 1);
assert.ok(
calls[0].name === 'lighthouse_audit',
'First call should be lighthouse_audit',
);
},
};
```
## /scripts/eval_scenarios/lighthouse_best_practices_test.ts
```ts path="/scripts/eval_scenarios/lighthouse_best_practices_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
prompt: 'Check for best practices on the current page',
maxTurns: 1,
expectations: calls => {
assert.strictEqual(calls.length, 1);
assert.ok(
calls[0].name === 'lighthouse_audit',
'First call should be lighthouse_audit',
);
},
};
```
## /scripts/eval_scenarios/navigation_test.ts
```ts path="/scripts/eval_scenarios/navigation_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
prompt: 'Navigate to https://developers.chrome.com and tell me if it worked.',
maxTurns: 1,
expectations: calls => {
assert.deepStrictEqual(calls, [
{
name: 'navigate_page',
args: {url: 'https://developers.chrome.com'},
},
]);
},
};
```
## /scripts/eval_scenarios/network_test.ts
```ts path="/scripts/eval_scenarios/network_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
prompt: 'Navigate to <TEST_URL> and list all network requests.',
maxTurns: 2,
htmlRoute: {
path: '/network_test.html',
htmlContent: `
<h1>Network Test</h1>
<script>
fetch('/network_test.html'); // Self fetch to ensure at least one request
</script>
`,
},
expectations: calls => {
assert.strictEqual(calls.length, 2);
assert.ok(
calls[0].name === 'navigate_page' || calls[0].name === 'new_page',
'First call should be navigation',
);
assert.strictEqual(
calls[1].name,
'list_network_requests',
'Second call should be list_network_requests',
);
},
};
```
## /scripts/eval_scenarios/page_focus_keyboard_test.ts
```ts path="/scripts/eval_scenarios/page_focus_keyboard_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
serverArgs: ['--experimental-page-id-routing'],
prompt: `Open two pages in the same isolated context "session":
- Page 1 at data:text/html,<textarea id="ta"></textarea>
- Page 2 at data:text/html,<h1>Other</h1>
Now use the press_key tool to type "a" on Page 1 without selecting it first. You must use press_key, not fill or type_text. If you encounter any errors, recover from them.`,
maxTurns: 10,
expectations: calls => {
// Should open 2 pages in the same context.
const newPages = calls.filter(c => c.name === 'new_page');
assert.strictEqual(newPages.length, 2, 'Should open 2 pages');
assert.strictEqual(newPages[0].args.isolatedContext, 'session');
assert.strictEqual(newPages[1].args.isolatedContext, 'session');
// Should attempt press_key at least once.
const pressKeys = calls.filter(c => c.name === 'press_key');
assert.ok(pressKeys.length >= 1, 'Should attempt press_key at least once');
const selectPages = calls.filter(c => c.name === 'select_page');
if (selectPages.length > 0) {
const firstPressKeyIndex = calls.indexOf(pressKeys[0]);
const firstSelectPageIndex = calls.indexOf(selectPages[0]);
if (firstPressKeyIndex < firstSelectPageIndex) {
// Error path: press_key was attempted first and failed.
// Verify recovery: must have a second press_key after select_page.
assert.ok(
pressKeys.length >= 2,
'Should retry press_key after error recovery',
);
const lastPressKeyIndex = calls.lastIndexOf(pressKeys.at(-1)!);
assert.ok(
firstSelectPageIndex < lastPressKeyIndex,
'select_page should precede the successful press_key',
);
} else {
// Proactive path: model selected page first.
assert.ok(
firstSelectPageIndex < firstPressKeyIndex,
'select_page should precede press_key',
);
}
}
// If no select_page was called, the model found another recovery path.
// This is acceptable as long as press_key was attempted.
},
};
```
## /scripts/eval_scenarios/page_id_routing_test.ts
```ts path="/scripts/eval_scenarios/page_id_routing_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
serverArgs: ['--experimental-page-id-routing'],
prompt: `Open two new pages in isolated contexts:
- Page A (isolatedContext "contextA") at data:text/html,<button>Click A</button>
- Page B (isolatedContext "contextB") at data:text/html,<button>Click B</button>
Then take a snapshot of Page A, take a snapshot of Page B, and then click the button on Page A.`,
maxTurns: 12,
expectations: calls => {
// Should have 2 new_page calls with isolatedContext.
const newPages = calls.filter(c => c.name === 'new_page');
assert.strictEqual(newPages.length, 2, 'Should open 2 pages');
for (const np of newPages) {
assert.strictEqual(
typeof np.args.isolatedContext,
'string',
'new_page should use isolatedContext',
);
}
// Should have at least 2 take_snapshot calls (one per page).
// The model may use pageId directly or select_page before each snapshot.
const snapshots = calls.filter(c => c.name === 'take_snapshot');
assert.ok(snapshots.length >= 2, 'Should take at least 2 snapshots');
// Should have a click call (resolving uid from Page A's snapshot
// even though Page B was snapshotted after).
const clicks = calls.filter(c => c.name === 'click');
assert.ok(clicks.length >= 1, 'Should click the button on Page A');
},
};
```
## /scripts/eval_scenarios/performance_test.ts
```ts path="/scripts/eval_scenarios/performance_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
prompt: 'Check the performance of https://developers.chrome.com',
maxTurns: 2,
expectations: calls => {
assert.strictEqual(calls.length, 2);
assert.ok(
calls[0].name === 'navigate_page' || calls[0].name === 'new_page',
);
assert.ok(calls[1].name === 'performance_start_trace');
},
};
```
## /scripts/eval_scenarios/select_page_test.ts
```ts path="/scripts/eval_scenarios/select_page_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
prompt:
'Open new page <TEST_URL> and then open new page https://developers.chrome.com. Select the <TEST_URL> page.',
maxTurns: 3,
htmlRoute: {
path: '/test.html',
htmlContent: `
<h1>test</h1>
`,
},
expectations: calls => {
assert.strictEqual(calls.length, 3);
assert.ok(calls[0].name === 'new_page', 'First call should be navigation');
assert.ok(calls[1].name === 'new_page', 'Second call should be navigation');
assert.ok(
calls[2].name === 'select_page',
'Third call should be select_page',
);
assert.strictEqual(
calls[2].args.pageId,
2,
'PageId has to be set to 2. about:blank is 1, <TEST_URL> is 2, https://developers.chrome.com is 3.',
);
assert.strictEqual(
calls[2].args.bringToFront,
undefined,
'bringToFront should use the default value.',
);
},
};
```
## /scripts/eval_scenarios/snapshot_test.ts
```ts path="/scripts/eval_scenarios/snapshot_test.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import type {TestScenario} from '../eval_gemini.ts';
export const scenario: TestScenario = {
prompt: 'Read the content of <TEST_URL>',
maxTurns: 3,
htmlRoute: {
path: '/test.html',
htmlContent: '<h1>Hello World</h1><p>This is a test.</p>',
},
expectations: calls => {
assert.strictEqual(calls.length, 2);
assert.ok(
calls[0].name === 'navigate_page' || calls[0].name === 'new_page',
);
assert.ok(calls[1].name === 'take_snapshot');
},
};
```
## /scripts/prepare.ts
```ts path="/scripts/prepare.ts"
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {readFileSync, writeFileSync} from 'node:fs';
import {rm} from 'node:fs/promises';
import {resolve} from 'node:path';
const projectRoot = process.cwd();
const filesToRemove = [
'node_modules/chrome-devtools-frontend/package.json',
'node_modules/chrome-devtools-frontend/front_end/models/trace/lantern/testing',
'node_modules/chrome-devtools-frontend/front_end/third_party/intl-messageformat/package/package.json',
];
/**
* Removes the conflicting global HTMLElementEventMap declaration from
* @paulirish/trace_engine/models/trace/ModelImpl.d.ts to avoid TS2717 error
* when both chrome-devtools-frontend and @paulirish/trace_engine declare
* the same property.
*/
function removeConflictingGlobalDeclaration(): void {
const filePath = resolve(
projectRoot,
'node_modules/@paulirish/trace_engine/models/trace/ModelImpl.d.ts',
);
console.log(
'Removing conflicting global declaration from @paulirish/trace_engine...',
);
const content = readFileSync(filePath, 'utf-8');
// Remove the declare global block using regex
// Matches: declare global { ... interface HTMLElementEventMap { ... } ... }
const newContent = content.replace(
/declare global\s*\{\s*interface HTMLElementEventMap\s*\{[^}]*\[ModelUpdateEvent\.eventName\]:\s*ModelUpdateEvent;\s*\}\s*\}/s,
'',
);
writeFileSync(filePath, newContent, 'utf-8');
console.log('Successfully removed conflicting global declaration.');
}
async function main() {
console.log('Running prepare script to clean up chrome-devtools-frontend...');
for (const file of filesToRemove) {
const fullPath = resolve(projectRoot, file);
console.log(`Removing: ${file}`);
try {
await rm(fullPath, {recursive: true, force: true});
} catch (error) {
console.error(`Failed to remove ${file}:`, error);
process.exit(1);
}
}
console.log('Clean up of chrome-devtools-frontend complete.');
removeConflictingGlobalDeclaration();
}
void main();
```
## /scripts/tsconfig.json
```json path="/scripts/tsconfig.json"
{
"extends": "../tsconfig.json",
"compilerOptions": {
"target": "esnext",
"module": "nodenext",
"moduleResolution": "nodenext",
"outDir": "./ignored",
"rootDir": ".",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noImplicitOverride": true,
"noFallthroughCasesInSwitch": true,
"incremental": true,
"allowJs": true,
"allowImportingTsExtensions": true,
"noEmit": true,
"useUnknownInCatchVariables": false
},
"include": ["./**/*.ts", "./**/*.js", "./**/*.mjs"]
}
```
## /scripts/update-lighthouse.ts
```ts path="/scripts/update-lighthouse.ts"
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {execSync} from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
const ROOT_DIR = process.cwd();
const LIGHTHOUSE_DIR = path.resolve(ROOT_DIR, '../lighthouse');
const DEST_DIR = path.join(ROOT_DIR, 'src/third_party');
function main() {
if (!fs.existsSync(LIGHTHOUSE_DIR)) {
console.error(`Lighthouse directory not found at ${LIGHTHOUSE_DIR}`);
process.exit(1);
}
console.log('Running yarn in lighthouse directory...');
execSync('yarn', {cwd: LIGHTHOUSE_DIR, stdio: 'inherit'});
console.log('Building lighthouse-devtools-mcp bundle...');
execSync('yarn build-devtools-mcp', {cwd: LIGHTHOUSE_DIR, stdio: 'inherit'});
const bundlePath = path.join(
LIGHTHOUSE_DIR,
'dist',
'lighthouse-devtools-mcp-bundle.js',
);
console.log(`Copying bundle from ${bundlePath} to ${DEST_DIR}...`);
fs.copyFileSync(
bundlePath,
path.join(DEST_DIR, 'lighthouse-devtools-mcp-bundle.js'),
);
const noticesPath = path.join(
LIGHTHOUSE_DIR,
'dist',
'LIGHTHOUSE_MCP_BUNDLE_THIRD_PARTY_NOTICES',
);
console.log(`Copying notices from ${noticesPath} to ${DEST_DIR}...`);
fs.copyFileSync(
noticesPath,
path.join(DEST_DIR, 'LIGHTHOUSE_MCP_BUNDLE_THIRD_PARTY_NOTICES'),
);
console.log('Done.');
}
main();
```
## /src/polyfill.ts
```ts path="/src/polyfill.ts"
/**
* @license
* Copyright 2025 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
// polyfills are now bundled with all other dependencies
import './third_party/index.js';
```
The content has been capped at 50000 tokens. The user could consider applying other filters to refine the result. The better and more specific the context, the better the LLM can follow instructions. If the context seems verbose, the user can refine the filter using uithub. Thank you for using https://uithub.com - Perfect LLM context for any GitHub repo.