
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
browser-devtools-mcp
Advanced tools
Browser DevTools MCP
A powerful Model Context Protocol (MCP) server that provides AI coding assistants with comprehensive automation and debugging capabilities across multiple platforms. This server enables both execution-level debugging (logs, network requests) and visual debugging (screenshots, accessibility snapshots) to help AI assistants understand and interact with applications effectively.
Browser DevTools MCP is a platform-extensible MCP server designed to give AI agents deep inspection and control over application runtimes. The architecture supports multiple platforms through a unified interface, with each platform providing specialized tools for its environment.
| Platform | Description | Status |
|---|---|---|
| Browser | Playwright-powered browser automation with full DevTools integration | ✅ Available |
| Node | Non-blocking debugging for Node.js backend processes via Inspector Protocol | ✅ Available |
The Browser Platform exposes a Playwright-powered browser runtime to AI agents, enabling deep, bidirectional debugging and interaction with live web pages. It supports both visual understanding and code-level inspection of browser state, making it ideal for AI-driven exploration, diagnosis, and automation.
The Node Platform provides non-blocking debugging for Node.js backend processes. It connects to running Node.js processes via the Inspector Protocol (Chrome DevTools Protocol over WebSocket) and offers tracepoints, logpoints, exceptionpoints, watch expressions, and source map support—ideal for debugging APIs, workers, and server-side code.
Choose the platform by running the appropriate MCP server or CLI:
| Use Case | MCP Server | CLI |
|---|---|---|
| Browser automation & debugging | browser-devtools-mcp | browser-devtools-cli |
| Node.js backend debugging | node-devtools-mcp | node-devtools-cli |
debug_resolve-source-location translates stack traces and bundle locations to original source@opentelemetry/api, tracepoint/logpoint snapshots automatically include traceContext (traceId, spanId) for correlating backend traces with browser tracescontainerId / containerName)annotate: true overlays numbered labels (1, 2, ...) on elements from the last ARIA snapshot refs and returns an annotations array (ref, number, role, name, box). If the ref map is empty, a snapshot is taken automatically. Set annotateContent: true to also include content elements (headings, list items, etc.) in the overlay. Set annotateCursorInteractive: true to also include cursor-interactive elements (clickable/focusable by CSS without ARIA role) in the overlay. The selector parameter accepts a ref (e.g. e1, @e1), a getByRole/getByLabel/getByText/etc. expression, or a CSS selector; with selector set, only annotations overlapping that element are returned and box coordinates are element-relative; with fullPage: true (no selector), box coordinates are document-relative.content_start-recording, stop with content_stop-recording. Works in all modes (headless, headed, persistent, CDP attach). Chromium only.e1, @e1, ref=e1), Playwright-style expression (e.g. getByRole('button', { name: 'Login' }), getByLabel('Email'), getByText('Register'), getByPlaceholder('demo@example.com'), getByTitle('…'), getByAltText('…'), getByTestId('…')), or CSS selector. Refs come from a11y_take-aria-snapshot and are valid until the next snapshot or navigation.true: after navigation completes, waits for network idle before taking snapshot/screenshot; set waitForNavigation: false to skip the network idle wait. waitForTimeoutMs (default 30000) is the timeout for that wait when waitForNavigation is true. By default (includeSnapshot: true) returns an ARIA snapshot with refs (output, refs); set includeSnapshot: false for url/status/ok only. Use snapshotOptions (e.g. interactiveOnly, cursorInteractive) to control which elements get refs (same as a11y_take-aria-snapshot). Optional includeScreenshot saves a screenshot to disk and returns screenshotFilePath; use screenshotOptions (outputPath, name, fullPage, type, annotate, includeBase64) — defaults: OS temp dir, name "screenshot"; set includeBase64: true only when the file cannot be read from the path (e.g. remote, container).callTool(name, input, returnOutput?) to invoke any registered tool (canonical id such as navigation_go-to, or the same name the MCP host exposes when TOOL_NAME_PREFIX is set). Reduces round-trips and token usage. Includes wall-clock timeout, max tool call limit (50), console log capture, and fail-fast error handling with failedTool diagnostics. On the browser platform the VM also receives the session execution context: page (Playwright Page) is available — use the Playwright API (e.g. page.locator(), page.goto()) or page.evaluate() to run script in the browser. On the Node platform no extra bindings are injected. CLI: run execute --code '…' [--timeout-ms N] or run execute --file ./script.js (same daemon session as other tools; starts the daemon if needed). Boolean tool flags with default true also accept --no-<flag> (e.g. --no-wait-for-navigation). MCP or daemon HTTP API (POST /call with toolName: "execute" and toolInput: { code, timeoutMs? }) also work.includeRequestHeaders, includeResponseHeaders, includeResponseBody; default off). Response body is not stored for static assets (e.g. .js, .css, .map).browser-devtools-mcp/scenarios.json (project-level or global)execute — supports callTool(), composable via nested scenario-run calls (max depth: 5)sync_wait-for-network-idle like idleTimeMs / maxConnections), useful for SPA pages and before taking screenshotsrefs map; refs are stored in session context for use in interaction tools (click, fill, hover, select, drag, scroll, press-key) as the selector (e.g. e1, @e1, ref=e1). You can also use Playwright-style expressions in those tools: getByRole('button', { name: 'Login' }), getByLabel('Email'), getByText('Register'), getByPlaceholder('…'), getByTitle('…'), getByAltText('…'), getByTestId('…'), or CSS selectors. Refs are valid until the next ARIA snapshot or navigation—re-snapshot after page/DOM changes. Options: interactiveOnly (only interactive elements get refs); cursorInteractive: true (also assign refs to elements that are clickable/focusable by CSS but have no ARIA role, e.g. custom div/span buttons); selector (scope the snapshot to an element).Important Requirements for React Tools:
BROWSER_PERSISTENT_ENABLE=true)__REACT_DEVTOOLS_GLOBAL_HOOK____reactFiber$ pointers (best-effort, less reliable)Non-blocking debugging tools that capture snapshots without pausing execution. Ideal for AI-assisted debugging.
Probe Types:
Core Operations (per probe type):
put-*: Create a probe at a locationremove-*: Remove a specific probelist-*: List all probes of a typeclear-*: Remove all probes of a typeget-*-snapshots: Retrieve captured snapshots (supports fromSequence for polling)clear-*-snapshots: Clear captured snapshotsAdditional Tools:
Key Features:
get-probe-snapshots: default scopes = local only (both Browser and Node), 20 variables/scope. Override via maxCallStackDepth, includeScopes, maxVariablesPerScope.This MCP server (using STDIO or Streamable HTTP transport) can be added to any MCP Client
like VS Code, Claude, Cursor, Windsurf, GitHub Copilot via the browser-devtools-mcp NPM package.
No manual installation required! The server can be run directly using npx, which automatically downloads and runs the package.
The browser platform needs Playwright browser binaries (e.g. Chromium) to control the browser.
If you use npx to run the server, browsers are not downloaded at install time — you must install them once (see below). With a normalnpm install, Playwright’s own packages may install Chromium automatically.
| Goal | What to do |
|---|---|
| Install at first run (npx) | Set env before running: BROWSER_DEVTOOLS_INSTALL_CHROMIUM=true npx -y browser-devtools-mcp |
| Install manually anytime | Run: npx playwright install chromium (or firefox, webkit) |
| Skip download (CI / system browser) | Set: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 |
1. Opt-in at install time — set env vars before npm install or npx so the postinstall script downloads the chosen browsers:
BROWSER_DEVTOOLS_INSTALL_CHROMIUM=true npx -y browser-devtools-mcp
BROWSER_DEVTOOLS_INSTALL_FIREFOX=trueBROWSER_DEVTOOLS_INSTALL_WEBKIT=true
Combine as needed, e.g. BROWSER_DEVTOOLS_INSTALL_CHROMIUM=true BROWSER_DEVTOOLS_INSTALL_FIREFOX=true npx -y browser-devtools-mcp.2. Install via Playwright CLI (same global cache):
npx playwright install chromium # or firefox, webkit
On Linux, include system dependencies:
npx playwright install --with-deps chromium
Browser DevTools MCP server supports the following CLI arguments for configuration:
--transport <stdio|streamable-http> - Configures the transport protocol (defaults to stdio).--port <number> – Configures the port number to listen on when using streamable-http transport (defaults to 3000).Install browser automation capabilities as a skill for AI coding agents (Claude Code, Cursor, Windsurf, etc.) using the skills.sh ecosystem:
npx skills add serkan-ozal/browser-devtools-skills
This installs the CLI skill that enables AI agents to automate browsers for web testing, screenshots, form filling, accessibility audits, performance analysis, and more. See the Skills Repository for details.
To use the Node platform (Node.js backend debugging), use node-devtools-mcp instead of browser-devtools-mcp:
{
"mcpServers": {
"node-devtools": {
"command": "npx",
"args": ["-y", "-p", "browser-devtools-mcp", "node-devtools-mcp"]
}
}
}
Alternatively, set PLATFORM=node when running browser-devtools-mcp.
Add the following configuration into the claude_desktop_config.json file.
See the Claude Desktop MCP docs for more info.
Browser platform (default):
→ Browser platform requires Playwright browser binaries. See 📦 Playwright browser binaries for one-time install (env vars or
npx playwright install chromium).
{
"mcpServers": {
"browser-devtools": {
"command": "npx",
"args": ["-y", "browser-devtools-mcp"]
}
}
}
Node platform:
{
"mcpServers": {
"node-devtools": {
"command": "npx",
"args": ["-y", "-p", "browser-devtools-mcp", "node-devtools-mcp"]
}
}
}
First, start the server with HTTP transport:
npx -y browser-devtools-mcp --transport=streamable-http --port=3000
Then, go to Settings > Connectors > Add Custom Connector in Claude Desktop and add the MCP server with:
Browser DevToolshttp://localhost:3000/mcp if running locally, or https://your-server.com/mcp if hosted remotely)Run the following command. See Claude Code MCP docs for more info.
→ Browser platform requires Playwright browser binaries. See 📦 Playwright browser binaries for one-time install (env vars or
npx playwright install chromium).
claude mcp add browser-devtools -- npx -y browser-devtools-mcp
First, start the server with HTTP transport:
npx -y browser-devtools-mcp --transport=streamable-http --port=3000
Then add the MCP server:
claude mcp add --transport http browser-devtools <SERVER_URL>
Replace <SERVER_URL> with your server URL (e.g., http://localhost:3000/mcp if running locally, or https://your-server.com/mcp if hosted remotely).
Add the following configuration into the ~/.cursor/mcp.json file (or .cursor/mcp.json in your project folder).
See the Cursor MCP docs for more info.
→ Browser platform requires Playwright browser binaries. See 📦 Playwright browser binaries for one-time install (env vars or
npx playwright install chromium).
{
"mcpServers": {
"browser-devtools": {
"command": "npx",
"args": ["-y", "browser-devtools-mcp"]
}
}
}
First, start the server with HTTP transport:
npx -y browser-devtools-mcp --transport=streamable-http --port=3000
Then add the configuration:
{
"mcpServers": {
"browser-devtools": {
"url": "<SERVER_URL>"
}
}
}
Replace <SERVER_URL> with your server URL (e.g., http://localhost:3000/mcp if running locally, or https://your-server.com/mcp if hosted remotely).
Add the following configuration into the .vscode/mcp.json file.
See the VS Code MCP docs for more info.
→ Browser platform requires Playwright browser binaries. See 📦 Playwright browser binaries for one-time install (env vars or
npx playwright install chromium).
{
"mcp": {
"servers": {
"browser-devtools": {
"type": "stdio",
"command": "npx",
"args": ["-y", "browser-devtools-mcp"]
}
}
}
}
First, start the server with HTTP transport:
npx -y browser-devtools-mcp --transport=streamable-http --port=3000
Then add the configuration:
{
"mcp": {
"servers": {
"browser-devtools": {
"type": "http",
"url": "<SERVER_URL>"
}
}
}
}
Replace <SERVER_URL> with your server URL (e.g., http://localhost:3000/mcp if running locally, or https://your-server.com/mcp if hosted remotely).
Add the following configuration into the ~/.codeium/windsurf/mcp_config.json file.
See the Windsurf MCP docs for more info.
→ Browser platform requires Playwright browser binaries. See 📦 Playwright browser binaries for one-time install (env vars or
npx playwright install chromium).
{
"mcpServers": {
"browser-devtools": {
"command": "npx",
"args": ["-y", "browser-devtools-mcp"]
}
}
}
First, start the server with HTTP transport:
npx -y browser-devtools-mcp --transport=streamable-http --port=3000
Then add the configuration:
{
"mcpServers": {
"browser-devtools": {
"serverUrl": "<SERVER_URL>"
}
}
}
Replace <SERVER_URL> with your server URL (e.g., http://localhost:3000/mcp if running locally, or https://your-server.com/mcp if hosted remotely).
Add the following configuration to the mcpServers section of your Copilot Coding Agent configuration through
Repository > Settings > Copilot > Coding agent > MCP configuration.
See the Copilot Coding Agent MCP docs for more info.
→ Browser platform requires Playwright browser binaries. See 📦 Playwright browser binaries for one-time install (env vars or
npx playwright install chromium).
{
"mcpServers": {
"browser-devtools": {
"type": "local",
"command": "npx",
"args": ["-y", "browser-devtools-mcp"]
}
}
}
First, start the server with HTTP transport:
npx -y browser-devtools-mcp --transport=streamable-http --port=3000
Then add the configuration:
{
"mcpServers": {
"browser-devtools": {
"type": "http",
"url": "<SERVER_URL>"
}
}
}
Replace <SERVER_URL> with your server URL (e.g., http://localhost:3000/mcp if running locally, or https://your-server.com/mcp if hosted remotely).
Add the following configuration into the ~/.gemini/settings.json file.
See the Gemini CLI MCP docs for more info.
{
"mcpServers": {
"browser-devtools": {
"command": "npx",
"args": ["-y", "browser-devtools-mcp"]
}
}
}
First, start the server with HTTP transport:
npx -y browser-devtools-mcp --transport=streamable-http --port=3000
Then add the configuration:
{
"mcpServers": {
"browser-devtools": {
"httpUrl": "<SERVER_URL>"
}
}
}
Replace <SERVER_URL> with your server URL (e.g., http://localhost:3000/mcp if running locally, or https://your-server.com/mcp if hosted remotely).
Run the following command. You can find your Smithery API key here. See the Smithery CLI docs for more info.
npx -y @smithery/cli install serkan-ozal/browser-devtools-mcp --client <SMITHERY-CLIENT-NAME> --key <SMITHERY-API-KEY>
To use HTTP transport, start the server with:
npx -y browser-devtools-mcp --transport=streamable-http --port=3000
The server exposes the following endpoints:
GET /health - Health checkGET /ping - Ping endpointGET /mcp - MCP protocol infoPOST /mcp - MCP protocol messagesDELETE /mcp - Delete sessionImportant: When configuring remote MCP servers, use the actual URL where your server is hosted:
http://localhost:3000/mcp (or http://127.0.0.1:3000/mcp)https://your-server.com/mcp (replace with your actual server URL)Test the server using the MCP Inspector:
# For stdio transport
npx -y @modelcontextprotocol/inspector npx -y browser-devtools-mcp
# For HTTP transport (start server first)
npx -y browser-devtools-mcp --transport=streamable-http --port=3000
# Then in another terminal:
npx -y @modelcontextprotocol/inspector http://localhost:3000/mcp --transport http
Browser DevTools MCP includes standalone CLI tools for both platforms:
browser-devtools-cli: Browser platform—navigation, screenshots, interaction, a11y, debugging, etc.node-devtools-cli: Node platform—connect to Node.js processes, tracepoints, logpoints, exceptionpointsThis is particularly useful for:
The CLIs are included with the npm package:
# Run directly with npx
npx -y browser-devtools-cli --help
npx -y node-devtools-cli --help
# Or install globally
npm install -g browser-devtools-mcp
browser-devtools-cli --help
node-devtools-cli --help
To install Playwright browser binaries (required for the browser CLI when not using a system browser), run npx playwright install chromium (see Playwright browser binaries).
| Option | Description | Default |
|---|---|---|
--port <number> | Daemon server port | 2020 |
--session-id <string> | Session ID for maintaining browser state across commands | (none) |
--json | Output results as JSON | false |
--quiet | Suppress log messages, only show output | false |
--verbose | Enable verbose/debug output for troubleshooting | false |
--timeout <ms> | Timeout for operations in milliseconds | 30000 |
--no-telemetry | Disable anonymous usage telemetry for this invocation | false |
| Option | Description | Default |
|---|---|---|
--headless / --no-headless | Run browser in headless (no visible window) or headful mode | true |
--persistent / --no-persistent | Use persistent browser context (preserves cookies, localStorage) | false |
--user-data-dir <path> | Directory for persistent browser context user data | ./browser-devtools-mcp |
--use-system-browser | Use system-installed Chrome instead of bundled browser | false |
--browser-path <path> | Custom browser executable path | (none) |
Note: Browser options are applied when the daemon server starts. If the daemon is already running, stop it first (daemon stop) then start with new options.
node-devtools-cli provides Node.js backend debugging:
node-devtools-cli
├── daemon # Manage the daemon server
├── session # Manage Node.js debugging sessions
├── tools # List and inspect available tools
├── config # Show current configuration
├── completion # Generate shell completion scripts
├── interactive (repl) # Start interactive REPL mode
├── update # Check for updates
└── debug # Debug commands
├── connect # Connect to Node.js process (pid, processName, inspectorPort, containerId, etc.)
├── disconnect # Disconnect from current process
├── status # Show connection status
├── put-tracepoint # Set a tracepoint
├── put-logpoint # Set a logpoint
├── put-exceptionpoint # Configure exception catching
└── ... # Watch, snapshots, probes management
browser-devtools-cli organizes tools into domain-based subcommands. Object parameters (e.g. screenshotOptions, snapshotOptions) must be passed as a JSON string: --screenshot-options '{"outputPath":"/tmp","name":"myshot"}' or --snapshot-options '{"interactiveOnly":false}'. Boolean parameters with default true can be turned off with --no-<kebab-flag> (e.g. --no-wait-for-navigation). Run browser-devtools-cli navigation go-to --help (or the relevant subcommand) to see all options.
browser-devtools-cli
├── daemon # Manage the daemon server
│ ├── start # Start the daemon server
│ ├── stop # Stop the daemon server
│ ├── restart # Restart the daemon server (stop + start)
│ ├── status # Check daemon server status
│ └── info # Get detailed daemon info (version, uptime, sessions)
├── session # Manage browser sessions
│ ├── list # List all active sessions
│ ├── info <session-id> # Get information about a session
│ └── delete <session-id> # Delete a specific session
├── tools # Inspect available tools
│ ├── list # List all tools (with --domain filter)
│ ├── info <tool-name> # Get detailed tool info and parameters
│ └── search <query> # Search tools by name or description
├── config # Show current configuration
├── completion # Generate shell completion scripts
│ ├── bash # Generate bash completion script
│ └── zsh # Generate zsh completion script
├── interactive (repl) # Start interactive REPL mode
├── update # Check for and install updates
├── navigation # Navigation commands
│ ├── go-to # Navigate to a URL
│ ├── go-back-or-forward # Navigate back or forward in history (direction: back | forward)
│ └── reload # Reload the page
├── content # Content extraction commands
│ ├── take-screenshot # Take a screenshot
│ ├── get-as-html # Get HTML content
│ ├── get-as-text # Get text content
│ └── save-as-pdf # Save as PDF
├── interaction # Interaction commands
│ ├── click # Click an element
│ ├── fill # Fill a form field
│ ├── hover # Hover over an element
│ ├── press-key # Press a keyboard key
│ ├── select # Select from dropdown
│ ├── drag # Drag and drop
│ ├── scroll # Scroll the page
│ ├── resize-viewport # Resize viewport
│ └── resize-window # Resize browser window
├── a11y # Accessibility commands
│ └── take-aria-snapshot # Take ARIA snapshot
├── accessibility # Extended accessibility commands
│ └── take-ax-tree-snapshot # Take AX tree snapshot
├── o11y # Observability commands
│ ├── get-console-messages # Get console logs
│ ├── get-http-requests # Get HTTP requests
│ ├── get-web-vitals # Get Web Vitals metrics
│ ├── get-trace-context # Get current trace context
│ ├── new-trace-id # Generate new trace ID
│ └── set-trace-context # Set trace context
├── react # React debugging commands
│ ├── get-component-for-element
│ └── get-element-for-component
├── run # Batch execute (MCP parity): run execute --code '…' or --file path.js
│ └── execute
├── stub # HTTP stubbing commands
│ ├── mock-http-response # Mock HTTP responses
│ ├── intercept-http-request # Intercept requests
│ ├── list # List stubs
│ └── clear # Clear stubs
├── sync # Synchronization commands
│ └── wait-for-network-idle # Wait for network idle (configurable idle time / threshold)
├── debug # Non-blocking debugging commands
│ ├── put-tracepoint # Set a tracepoint (captures call stack)
│ ├── remove-probe # Remove a tracepoint, logpoint, or watch by ID (type + id)
│ ├── list-probes # List tracepoints, logpoints, and/or watches (optional types; omit to list all)
│ ├── put-logpoint # Set a logpoint (evaluates expression)
│ ├── put-exceptionpoint # Enable exception catching
│ ├── add-watch # Add a watch expression
│ ├── clear-probes # Clear tracepoints, logpoints, and/or watches (optional types; omit to clear all)
│ ├── get-probe-snapshots # Get tracepoint/logpoint/exceptionpoint snapshots (1 frame captured; default scopes: local only; 20 vars/scope; override: maxCallStackDepth, includeScopes, maxVariablesPerScope)
│ ├── clear-probe-snapshots # Clear tracepoint/logpoint/exceptionpoint snapshots (optional types; omit to clear all)
│ └── status # Get debugging status
└── figma # Figma integration commands
└── compare-page-with-design
# Navigate to a URL
browser-devtools-cli navigation go-to --url "https://example.com"
# Take a screenshot
browser-devtools-cli content take-screenshot --name "homepage"
Configure browser behavior when starting the daemon:
# Run browser in headful mode (visible window)
browser-devtools-cli --no-headless navigation go-to --url "https://example.com"
# Use persistent browser context
browser-devtools-cli --persistent --user-data-dir ./my-profile navigation go-to --url "https://example.com"
# Use system Chrome instead of bundled Chromium
browser-devtools-cli --use-system-browser navigation go-to --url "https://example.com"
# Use a custom browser executable
browser-devtools-cli --browser-path /path/to/chrome navigation go-to --url "https://example.com"
Maintain browser state across multiple commands using session IDs:
# Start a session and navigate
browser-devtools-cli --session-id my-session navigation go-to --url "https://example.com"
# Interact with the page (same session)
browser-devtools-cli --session-id my-session interaction click --selector "button.login"
# Fill a form
browser-devtools-cli --session-id my-session interaction fill --selector "#username" --value "user@example.com"
# Take a screenshot
browser-devtools-cli --session-id my-session content take-screenshot --name "after-login"
# Clean up session when done
browser-devtools-cli session delete my-session
Use --json and --quiet flags for machine-readable output:
# Get page content as JSON
browser-devtools-cli --json --quiet --session-id test navigation go-to --url "https://api.example.com"
# Output:
# {
# "url": "https://api.example.com/",
# "status": 200,
# "statusText": "",
# "ok": true
# }
# Check daemon status
browser-devtools-cli daemon status
# Start daemon manually
browser-devtools-cli daemon start
# Stop daemon
browser-devtools-cli daemon stop
# Restart daemon (useful when changing browser options)
browser-devtools-cli daemon restart
# Check status with JSON output
browser-devtools-cli daemon status --json
# Output: {"status":"running","port":2020}
# Get detailed daemon information
browser-devtools-cli daemon info
# Output:
# Daemon Server Information:
# Version: 0.5.0
# Port: 2020
# Uptime: 5m 23s
# Sessions: 2
# List all active sessions
browser-devtools-cli session list
# Output:
# Active Sessions (2):
# my-session
# Created: 2025-01-26T10:00:00.000Z
# Last Active: 2025-01-26T10:05:30.000Z
# Idle: 30s
# #default
# Created: 2025-01-26T09:55:00.000Z
# Last Active: 2025-01-26T10:04:00.000Z
# Idle: 1m 30s
# Get info about a specific session
browser-devtools-cli session info my-session
# Delete a session
browser-devtools-cli session delete my-session
# List all available tools
browser-devtools-cli tools list
# Output:
# Available Tools (35 total):
#
# navigation:
# go-to Navigate the browser to the given URL...
# go-back-or-forward Navigate back or forward in history (direction: back | forward)
# ...
# Filter tools by domain
browser-devtools-cli tools list --domain interaction
# Search for tools by keyword
browser-devtools-cli tools search click
# Output:
# Tools matching "click" (2 found):
#
# interaction/click
# Clicks an element on the page.
# ...
# Get detailed info about a specific tool
browser-devtools-cli tools info navigation_go-to
# Output:
# Tool: navigation_go-to
# Domain: navigation
#
# Description:
# Navigate the browser to the given URL...
#
# Parameters:
# --url <string> (required)
# The URL to navigate to
# --wait-until <load | domcontentloaded | commit> (optional)
# When to consider navigation succeeded
# Default: "load"
#
# Usage:
# browser-devtools-cli navigation go-to [options]
# Show current configuration
browser-devtools-cli config
# Output:
# Current Configuration:
#
# Daemon:
# Port: 2020
# Session Idle (sec): 300
# Idle Check Interval: 30
#
# Browser:
# Headless: true
# Persistent: false
# ...
# Show config as JSON
browser-devtools-cli config --json
Enable verbose output for troubleshooting:
# Run any command with --verbose for detailed debug logs
browser-devtools-cli --verbose navigation go-to --url "https://example.com"
# Output:
# [2025-01-26T10:00:00.000Z] [DEBUG] Verbose mode enabled
# [2025-01-26T10:00:00.001Z] [DEBUG] CLI version: 0.5.0
# [2025-01-26T10:00:00.001Z] [DEBUG] Node version: v20.10.0
# [2025-01-26T10:00:00.001Z] [DEBUG] Platform: darwin
# [2025-01-26T10:00:00.002Z] [DEBUG] Checking if daemon is running on port 2020
# [2025-01-26T10:00:00.010Z] [DEBUG] Daemon health check result: running
# [2025-01-26T10:00:00.011Z] [DEBUG] Calling tool: navigation_go-to
# [2025-01-26T10:00:00.011Z] [DEBUG] Tool input: { url: "https://example.com" }
# ...
Find tools by keyword:
# Search for tools related to "screenshot"
browser-devtools-cli tools search screenshot
# Output:
# Tools matching "screenshot" (2 found):
#
# content/take-screenshot
# Takes a screenshot of the current page or a specific element.
#
# figma/compare-page-with-design
# Compares the CURRENT PAGE UI against a Figma design snapshot...
# Search for tools related to "network"
browser-devtools-cli tools search network
Enable tab completion for faster command entry. Shell completions require a one-time setup:
For Bash:
# Option 1: Add to ~/.bashrc (recommended)
echo 'eval "$(browser-devtools-cli completion bash)"' >> ~/.bashrc
source ~/.bashrc
# Option 2: Or add manually to ~/.bashrc
eval "$(browser-devtools-cli completion bash)"
For Zsh (macOS default):
# Option 1: Add to ~/.zshrc (recommended)
echo 'eval "$(browser-devtools-cli completion zsh)"' >> ~/.zshrc
source ~/.zshrc
# Option 2: Or add manually to ~/.zshrc
eval "$(browser-devtools-cli completion zsh)"
Using with npx:
# If using npx instead of global install:
echo 'eval "$(npx -y browser-devtools-cli completion zsh)"' >> ~/.zshrc
source ~/.zshrc
After setup, press TAB for completions:
browser-devtools-cli dae<TAB> # Completes to "daemon"
browser-devtools-cli daemon st<TAB> # Shows "start", "stop", "status", "restart"
browser-devtools-cli --<TAB> # Shows all global options
Start an interactive session for continuous command entry:
# Start in headless mode (default)
browser-devtools-cli interactive
# Start with visible browser window
browser-devtools-cli --no-headless interactive
# Start with persistent context (preserves cookies, localStorage)
browser-devtools-cli --no-headless --persistent interactive
# Aliases
browser-devtools-cli repl
browser-devtools-cli --no-headless repl
Example session:
Browser DevTools CLI - Interactive Mode
Type "help" for available commands, "exit" to quit
browser> navigation go-to --url "https://example.com"
url: https://example.com/
status: 200
ok: true
browser> content take-screenshot --name "homepage"
path: /path/to/homepage.png
browser> interaction click --ref "Login"
clicked: true
browser> interaction fill --ref "Email" --value "test@example.com"
filled: true
browser> tools search screenshot
Found 2 tools:
content_take-screenshot - Take a screenshot of the current page
content_save-as-pdf - Save the current page as a PDF file
browser> daemon info
Version: 0.5.0
Uptime: 5m 23s
Sessions: 1
Port: 2020
browser> session list
Active sessions: 1
#default (idle: 30s)
browser> config
Current Configuration:
port = 2020
headless = false
persistent = true
...
browser> exit
Goodbye!
Available commands in interactive mode:
| Command | Description |
|---|---|
help | Show available commands |
exit, quit | Exit interactive mode |
status | Show daemon status summary |
config | Show current configuration |
daemon <cmd> | Daemon management (start, stop, restart, status, info) |
session <cmd> | Session management (list, info, delete) |
tools <cmd> | Tool discovery (list, search, info) |
update | Check for CLI updates |
<domain> <tool> | Execute a tool |
Keep your CLI up to date:
# Check for updates without installing
browser-devtools-cli update --check
# Output:
# Checking for updates...
#
# Current version: 0.5.0
# Latest version: 0.5.1
#
# ⚠ Update available: 0.5.0 → 0.5.1
#
# To update, run:
# npm install -g browser-devtools-mcp@latest
# Check and install updates interactively
browser-devtools-cli update
# Output:
# ...
# Do you want to update now? (y/N) y
# Updating...
# ✓ Update complete!
#!/bin/bash
SESSION="test-$(date +%s)"
CLI="browser-devtools-cli --json --quiet --session-id $SESSION"
# Navigate
$CLI navigation go-to --url "https://example.com"
# Get text content
CONTENT=$($CLI content get-as-text)
echo "Page content: $CONTENT"
# Take screenshot
$CLI content take-screenshot --name "test-result"
# Cleanup
browser-devtools-cli session delete $SESSION
The CLI uses a daemon server architecture for efficient browser management:
navigation go-to). execute maps to run execute (not listed under tools list, which only reflects the static tool registry).PLATFORM=browser on the daemon it spawns (and the Node CLI forces PLATFORM=node), so a shell-wide PLATFORM=node cannot accidentally make browser-devtools-cli start a Node-platform daemon. If you still see errors, check error.message on failed POST /call responses — the daemon includes the underlying exception message there.The daemon listens on port 2020 by default. Use --port to specify a different port.
Comprehensive documentation for AI agents and automation is available in the browser-devtools-skills repository.
Install as an AI agent skill:
npx skills add serkan-ozal/browser-devtools-skills
For the full list of available skills and documentation, see the browser-devtools-skills repository.
The server can be configured using environment variables. Configuration is divided into server-level settings and platform-specific settings.
| Variable | Description | Default |
|---|---|---|
PORT | Port for HTTP transport | 3000 |
TELEMETRY_ENABLE | Set to false to disable anonymous usage telemetry | (unset) |
SESSION_IDLE_SECONDS | Idle session timeout (seconds) | 300 |
SESSION_IDLE_CHECK_SECONDS | Interval for checking idle sessions (seconds) | 30 |
SESSION_CLOSE_ON_SOCKET_CLOSE | Close session when socket closes | false |
TOOL_OUTPUT_SCHEMA_DISABLE | When true, omit tool output schema from MCP tool registration (can reduce token usage for some clients) | false |
TOOL_NAME_PREFIX | Optional string prepended to every MCP-registered tool name (stdio / streamable HTTP only), including execute. MCP-facing TypeScript text is rewritten by applyNormalizedToolNamesInText in src/config.ts: only angle-bracket-wrapped substrings that match a registered canonical tool id are replaced with the prefixed name, so plain words like “execute” are never rewritten. README and non-MCP comments should use backticks only, e.g. navigation_go-to and callTool('navigation_go-to', …). In normalized sources (tool descriptions, server instructions, execute templates), authors wrap cross-tool references in angle brackets inside those string literals so clients receive the correct prefixed names. ToolRegistry.runTool also accepts a name that is a single bracket-wrapped id. Unrelated bracket tokens are left unchanged. CLI is unchanged. | (unset) |
AVAILABLE_TOOL_DOMAINS | Optional comma-separated list of tool domains to enable. When set, only tools from these domains are registered; unset means all tools. Browser domains: a11y, content, debug, figma, interaction, navigation, o11y, react, run, stub, sync. Node domains: debug, run. Example: AVAILABLE_TOOL_DOMAINS=navigation,interaction,a11y | (all tools) |
Tool inputs are validated with a strict schema; unknown or misspelled argument keys (e.g. port instead of inspectorPort) cause a validation error.
| Variable | Description | Default |
|---|---|---|
NODE_SERVER_INSTRUCTIONS_ENABLE | When true, include server instructions in MCP server info | true |
NODE_POLICY_DEBUGGING_ENABLE | When true, include NODE_DEBUGGING_POLICY in server policies | false |
NODE_CONSOLE_MESSAGES_BUFFER_SIZE | Maximum console messages to buffer from Node.js process | 1000 |
NODE_INSPECTOR_HOST | Inspector host for debug_connect when MCP runs in Docker (e.g. host.docker.internal). Use with host-mapped inspectorPort so the MCP connects to the right address. | 127.0.0.1 |
PLATFORM | Platform to use: browser or node | browser |
| Variable | Description | Default |
|---|---|---|
BROWSER_SERVER_INSTRUCTIONS_ENABLE | When true, include server instructions in MCP server info | true |
BROWSER_POLICY_UI_DEBUGGING_ENABLE | When true, include UI_DEBUGGING_POLICY in server policies | false |
CONSOLE_MESSAGES_BUFFER_SIZE | Maximum console messages to buffer | 1000 |
HTTP_REQUESTS_BUFFER_SIZE | Maximum HTTP requests to buffer | 1000 |
BROWSER_HEADLESS_ENABLE | Run browser in headless mode | true |
BROWSER_PERSISTENT_ENABLE | Use persistent browser context (preserves cookies, localStorage, etc.). Required for React tools to work optimally. | false |
BROWSER_PERSISTENT_USER_DATA_DIR | Directory for persistent browser context user data | ./browser-devtools-mcp |
BROWSER_CDP_ENDPOINT_URL | CDP attach: http://host:port, or ws://… directly. HTTP URLs are resolved like agent-browser (/json/version, /json/list, then ws://…/devtools/browser). Chromium only. | (unset) |
BROWSER_CDP_ENABLE | When true and no endpoint URL: probes 127.0.0.1:9222 then :9229 (HTTP + WebSocket CDP discovery). With BROWSER_CDP_ENDPOINT_URL, connects only to that host/port. | false |
BROWSER_CDP_OPEN_INSPECT | On loopback CDP failure, if Chrome is running, opens chrome://inspect/#remote-debugging so you can enable remote debugging. The server does not start Chrome — start it yourself with --remote-debugging-port=9222. Set false to never open. | true |
BROWSER_USE_INSTALLED_ON_SYSTEM | Use system-installed Chrome browser instead of Playwright's bundled browser | false |
BROWSER_EXECUTABLE_PATH | Custom browser executable path | (uses Playwright default) |
OTEL_ENABLE | Enable OpenTelemetry integration | false |
OTEL_SERVICE_NAME | OpenTelemetry service name | frontend |
OTEL_SERVICE_VERSION | OpenTelemetry service version | (none) |
OTEL_ASSETS_DIR | Directory containing OpenTelemetry bundle files | (uses default) |
OTEL_EXPORTER_TYPE | OpenTelemetry exporter type: "otlp/http-json", "otlp/http-protobuf", "console", or "none" (alias: "otlp/http" = "otlp/http-json") | none |
OTEL_EXPORTER_HTTP_URL | OpenTelemetry collector base URL (e.g., "http://localhost:4318") | (none) |
OTEL_EXPORTER_HTTP_HEADERS | OpenTelemetry exporter HTTP headers (comma-separated key=value pairs) | (none) |
OTEL_INSTRUMENTATION_USER_INTERACTION_EVENTS | User interaction events to instrument (comma-separated, e.g., "click,submit") | click |
FIGMA_ACCESS_TOKEN | Figma API access token for design comparison | (none) |
FIGMA_API_BASE_URL | Figma API base URL | https://api.figma.com/v1 |
When BROWSER_CDP_ENABLE or BROWSER_CDP_ENDPOINT_URL is set, the server only attaches to an existing Chrome that already exposes CDP (e.g. started with --remote-debugging-port=9222). Without an explicit URL it tries 9222 then 9229 on loopback; with BROWSER_CDP_ENDPOINT_URL it uses that host/port only. It verifies reachability, then connectOverCDP. context.newPage() opens a new tab in that browser.
If CDP is not reachable on loopback and Chrome appears to be running, BROWSER_CDP_OPEN_INSPECT (default true) opens chrome://inspect/#remote-debugging so you can turn on remote debugging. Start Chrome with debugging, then retry the tool.
| Prefix | Stage | Typical cause |
|---|---|---|
[CDP discovery] | Finding the DevTools port / URL | Chrome not in remote-debugging mode, wrong URL, or nothing listening on the probed port(s). |
[CDP connect] | Playwright connectOverCDP | Approve Chrome’s remote-debugging prompt; or set BROWSER_CDP_ENDPOINT_URL to the correct ws:// / http:// endpoint. |
Browser DevTools MCP collects anonymous usage data to understand which tools are used, detect errors, and improve the product over time. Telemetry is opt-out — it is enabled by default and can be disabled at any time with zero friction.
Only non-personal, non-sensitive data is sent. No page content, URLs, error messages, or any application-specific data is ever included.
Events: tool_called (each tool invocation), mcp_server_started (MCP server is ready to accept traffic — stdio after the transport is connected, streamable HTTP after the listener is bound), and cli_command_executed (CLI subcommands).
| Property | Description |
|---|---|
tool_name | Name of the tool that was called |
source | How the tool was invoked (see table below) |
duration_ms | Tool execution time in milliseconds |
success | Whether the call succeeded (true / false) |
error_type | Error class name (e.g. TypeError) — only on failure |
error_code | Error code (e.g. ECONNREFUSED) — only on failure |
browser_devtools_version | Package version (e.g. 0.2.27) |
node_version | Node.js runtime version (e.g. v20.10.0) |
os_platform | Operating system (e.g. darwin, linux, win32) |
os_arch | CPU architecture (e.g. x64, arm64) |
timezone | Local timezone (e.g. America/New_York) |
timestamp | UTC timestamp of the event |
session_id | MCP session ID from the transport (MCP only) |
client_name | Raw MCP client name from the initialize handshake (MCP only) |
transport | MCP transport in use — stdio or streamable-http (mcp_server_started only) |
What is never collected:
error.message content — only the error class name and code are sentA persistent anonymous UUID is stored locally at ~/.browser-devtools-mcp/config.json. It is never linked to any user identity.
The source field identifies the calling context:
| Value | Meaning |
|---|---|
cli | Called via browser-devtools-cli or node-devtools-cli |
cli_cursor | CLI invoked from within Cursor (env var with CURSOR_ prefix detected) |
cli_claude | CLI invoked from within Claude (env var with CLAUDE_ prefix detected) |
cli_codex | CLI invoked from within Codex (env var with CODEX_ prefix detected) |
mcp-cursor | MCP client is Cursor |
mcp-claude | MCP client is Claude Desktop or Claude Code |
mcp-codex | MCP client is OpenAI Codex CLI |
mcp-unknown | MCP client could not be identified |
MCP server — set TELEMETRY_ENABLE=false in your MCP client config:
{
"mcpServers": {
"browser-devtools": {
"command": "npx",
"args": ["-y", "browser-devtools-mcp"],
"env": { "TELEMETRY_ENABLE": "false" }
}
}
}
CLI — pass --no-telemetry to any command:
browser-devtools-cli --no-telemetry navigation go-to --url "https://example.com"
To disable telemetry permanently for all CLI invocations, add an alias to your shell profile:
# ~/.zshrc or ~/.bashrc
alias browser-devtools-cli="browser-devtools-cli --no-telemetry"
Once disabled, no data is sent and no network requests are made to PostHog.
content_take-screenshot - Takes a screenshot of the current page or a specific element.Parameters:
outputPath (string, optional): Directory path where screenshot will be saved (default: OS temp directory)name (string, optional): Screenshot name (default: "screenshot")selector (string, optional): Ref (e.g. e1, @e1), getByRole/getByLabel/getByText/getByPlaceholder/getByTitle/getByAltText/getByTestId expression, or CSS selector for element to capturefullPage (boolean, optional): Capture full scrollable page (default: false)type (enum, optional): Image format - "png" or "jpeg" (default: "png")quality (number, optional): The quality of the image, between 0-100. Not applicable to PNG images, only used for JPEG format (default: 100)includeBase64 (boolean, optional): Include base64-encoded image data in the response (default: false)annotate (boolean, optional): Overlay numbered labels (1, 2, …) on elements from the session ref map; refs are built from the last ARIA snapshot or auto-taken if ref map is empty (default: false)annotateContent (boolean, optional): When true with annotate, include content elements (headings, list items, etc.) in the overlay; uses interactiveOnly: false when building refs for this screenshot (default: false)annotateCursorInteractive (boolean, optional): When true with annotate, also include cursor-interactive elements (clickable/focusable by CSS without ARIA role) in the overlay (default: false)Returns:
filePath (string): Full path of the saved screenshot fileimage (object, optional): Screenshot image data with mimeType (only included when includeBase64 is true)annotations (array, optional): When annotate is true, list of { ref, number, role, name, box } for each overlaid element; box coordinates are document-relative for fullPage, element-relative when selector is usedNotes:
includeBase64 to true when the AI assistant cannot access the MCP server's file system (e.g., remote server, containerized environment, or different machine)quality parameter only applies to JPEG images. PNG images are always saved at full qualityannotateContent to include headings/content, or annotateCursorInteractive to include CSS-clickable elements, without a prior ARIA snapshot with those optionscontent_get-as-html - Retrieves the HTML content of the current page or a specific element.Parameters:
selector (string, optional): CSS selector to limit the HTML content to a specific containerremoveScripts (boolean, optional): Remove all script tags from the HTML (default: true)removeComments (boolean, optional): Remove all HTML comments (default: false)removeStyles (boolean, optional): Remove all style tags from the HTML (default: false)removeMeta (boolean, optional): Remove all meta tags from the HTML (default: false)cleanHtml (boolean, optional): Perform comprehensive HTML cleaning (default: false)minify (boolean, optional): Minify the HTML output (default: false)maxLength (number, optional): Maximum number of characters to return (default: 50000)Returns:
output (string): The requested HTML content of the pagecontent_get-as-text - Retrieves the visible text content of the current page or a specific element.Parameters:
selector (string, optional): CSS selector to limit the text content to a specific containermaxLength (number, optional): Maximum number of characters to return (default: 50000)Returns:
output (string): The requested text content of the pagecontent_save-as-pdf - Saves the current page as a PDF document.Parameters:
outputPath (string, optional): Directory path where PDF will be saved (default: OS temp directory)name (string, optional): PDF name (default: "page")format (enum, optional): Page format - "Letter", "Legal", "Tabloid", "Ledger", "A0" through "A6" (default: "A4")printBackground (boolean, optional): Whether to print background graphics (default: false)margin (object, optional): Page margins with top, right, bottom, left (default: "1cm" for each)Returns:
filePath (string): Full path of the saved PDF filecontent_start-recording - Starts video recording of the browser page.Parameters:
outputDir (string, optional): Directory where the video file will be saved (default: OS temp directory)name (string, optional): Name for the video file without extension (default: "recording")
Returns:message (string): Status messageNotes:
content_stop-recording is calledcontent_stop-recording - Stops video recording and saves the video file.Returns:
filePath (string, optional): Full path of the saved WebM video fileNotes:
content_start-recordinginteraction_click - Clicks an element. Set waitForNavigation: true when click opens a new page.Parameters:
selector (string, required): Ref (e.g. e1, @e1, ref=e1), getByRole/getByLabel/getByText/getByPlaceholder/getByTitle/getByAltText/getByTestId expression, or CSS selector for the element to clicktimeoutMs (number, optional): Time to wait for the element in ms (default: 10000)waitForNavigation (boolean, optional): Wait for navigation triggered by click in parallel (race-free), then for network idle. Use when click opens a new page. Default: falsewaitForTimeoutMs (number, optional): Timeout for navigation and network idle wait in ms. Only when waitForNavigation is true. Default: 30000interaction_fill - Fills a form input field.Parameters:
selector (string, required): Ref (e.g. e1, @e1), getByRole/getByLabel/getByPlaceholder expression, or CSS selector for the input fieldvalue (string, required): Value to filltimeoutMs (number, optional): Time to wait for the element in ms (default: 10000)interaction_hover - Hovers over an element.Parameters:
selector (string, required): Ref (e.g. e1, @e1), getByRole/getByText/etc. expression, or CSS selector for the element to hovertimeoutMs (number, optional): Time to wait for the element in ms (default: 10000)interaction_press-key - Simulates keyboard input.Parameters:
key (string, required): Key to press (e.g., "Enter", "Escape", "Tab")selector (string, optional): Ref (e.g. e1, @e1), getByRole/getByLabel/etc. expression, or CSS selector to focus before sending the keyholdMs (number, optional): Duration in milliseconds to hold the key (repeat duration if repeat is true)repeat (boolean, optional, default: false): If true, simulates key auto-repeat by pressing repeatedly during holdMsrepeatIntervalMs (number, optional, default: 50, min: 10): Interval between repeated key presses in ms (only when repeat is true)timeoutMs (number, optional): Time to wait for the element when selector is set, in ms (default: 10000)interaction_select - Selects an option from a dropdown.Parameters:
selector (string, required): Ref (e.g. e1, @e1), getByRole/getByTestId expression, or CSS selector for the select elementvalue (string, required): Value to selecttimeoutMs (number, optional): Time to wait for the element in ms (default: 10000)interaction_drag - Performs drag and drop operation.Parameters:
sourceSelector (string, required): Ref (e.g. e1, @e1), getByRole/getByText/etc. expression, or CSS selector for the source elementtargetSelector (string, required): Ref, getByRole/getByText/etc. expression, or CSS selector for the target elementtimeoutMs (number, optional): Time to wait for source and target elements in ms (default: 10000)interaction_scroll - Scrolls the page viewport or a specific scrollable element.Parameters:
mode (enum, optional): Scroll mode - "by" (relative delta), "to" (absolute position), "top", "bottom", "left", "right" (default: "by")selector (string, optional): Ref (e.g. e1, @e1), getByRole/getByText/etc. expression, or CSS selector for a scrollable container. If omitted, scrolls the document viewportdx (number, optional): Horizontal scroll delta in pixels (used when mode="by", default: 0)dy (number, optional): Vertical scroll delta in pixels (used when mode="by", default: 0)x (number, optional): Absolute horizontal scroll position in pixels (used when mode="to")y (number, optional): Absolute vertical scroll position in pixels (used when mode="to")behavior (enum, optional): Native scroll behavior - "auto" or "smooth" (default: "auto")Returns:
mode (string): The scroll mode usedselector (string | null): The selector of the scroll container if provided; otherwise null (document viewport)behavior (string): The scroll behavior usedbefore (object): Scroll metrics before the scroll action (x, y, scrollWidth, scrollHeight, clientWidth, clientHeight)after (object): Scroll metrics after the scroll action (x, y, scrollWidth, scrollHeight, clientWidth, clientHeight)canScrollX (boolean): Whether horizontal scrolling is possiblecanScrollY (boolean): Whether vertical scrolling is possiblemaxScrollX (number): Maximum horizontal scrollLeftmaxScrollY (number): Maximum vertical scrollTopisAtLeft (boolean): Whether the scroll position is at the far leftisAtRight (boolean): Whether the scroll position is at the far rightisAtTop (boolean): Whether the scroll position is at the very topisAtBottom (boolean): Whether the scroll position is at the very bottomUsage:
interaction_resize-viewport - Resizes the page viewport using Playwright viewport emulation.Parameters:
width (number, required): Target viewport width in CSS pixels (minimum: 200)height (number, required): Target viewport height in CSS pixels (minimum: 200)Returns:
requested (object): Requested viewport configuration (width, height)viewport (object): Viewport metrics observed inside the page after resizing:
innerWidth, innerHeight: window.innerWidth/innerHeightouterWidth, outerHeight: window.outerWidth/outerHeightdevicePixelRatio: window.devicePixelRatioNotes:
window.innerWidth/innerHeight, CSS media queries, layout, rendering, and screenshotsinteraction_resize-window - Resizes the real browser window (OS-level window) for the current page using Chrome DevTools Protocol (CDP).Parameters:
width (number, optional): Target window width in pixels (required when state="normal", minimum: 200)height (number, optional): Target window height in pixels (required when state="normal", minimum: 200)state (enum, optional): Target window state - "normal", "maximized", "minimized", or "fullscreen" (default: "normal")Returns:
requested (object): Requested window change parameters (width, height, state)before (object): Window bounds before resizing (windowId, state, left, top, width, height)after (object): Window bounds after resizing (windowId, state, left, top, width, height)viewport (object): Page viewport metrics after resizing (innerWidth, innerHeight, outerWidth, outerHeight, devicePixelRatio)Notes:
navigation_go-to - Navigates to a URL.Parameters:
url (string, required): URL to navigate to (must include scheme)timeout (number, optional): Maximum operation time in milliseconds (default: 0 - no timeout)waitUntil (enum, optional): Playwright main-frame lifecycle — "load", "domcontentloaded", or "commit" (default: "load"). Not Playwright networkidle; use waitForNavigation for session network-idle after navigation.includeSnapshot (boolean, optional): When true (default), take an ARIA snapshot with refs after navigation and include output and refs in the response; when false, only url/status/ok are returned.snapshotOptions (object, optional): When includeSnapshot is true, options for the snapshot. interactiveOnly (boolean, default false): only interactive elements get refs; cursorInteractive (boolean, default false): include cursor-interactive elements (same as a11y_take-aria-snapshot).includeScreenshot (boolean, optional): When true, take a screenshot after navigation; saved to disk, path returned in screenshotFilePath. Default false.screenshotOptions (object, optional): When includeScreenshot is true. outputPath (string, default: OS temp dir), name (string, default: "screenshot"), fullPage (boolean, default true), type ("png" | "jpeg", default "png"), annotate (boolean, default true), includeBase64 (boolean, default false): include image in response as separate MCP content part — use only when file cannot be read from path (e.g. remote, container).Returns: (order: url, status, statusText, ok, screenshotFilePath, output, refs, image)
url, status, statusText, ok: Navigation result.screenshotFilePath (string, optional): When includeScreenshot is true, full path of the saved screenshot file.output (string, optional): When includeSnapshot is true, ARIA snapshot text (page URL, title, YAML tree).refs (record, optional): When includeSnapshot is true, map of ref id (e1, e2, ...) to role/name/selector; use in interaction tools (e.g. click @e1).image (object, optional): When includeScreenshot and screenshotOptions.includeBase64 are true, image data (data, mimeType) sent as separate image content part by MCP.navigation_go-back-or-forward - Navigates back or forward in browser history.Parameters: direction (required: "back" or "forward"), timeout, waitUntil, includeSnapshot (default true), snapshotOptions (object: interactiveOnly, cursorInteractive), includeScreenshot (boolean, default false), screenshotOptions (object: outputPath, name, fullPage, type, annotate, includeBase64) — same semantics as navigation_go-to.
Returns: Same shape as navigation_go-to (url, status, statusText, ok, screenshotFilePath, output, refs, image).
navigation_reload - Reloads the current page.Parameters:
timeout (number, optional): Maximum operation time in milliseconds (default: 0 - no timeout)waitUntil (enum, optional): Playwright main-frame lifecycle — "load", "domcontentloaded", or "commit" (default: "load"). Not Playwright networkidle; use waitForNavigation for session network-idle after navigation.includeSnapshot (boolean, optional): When true (default), take an ARIA snapshot with refs after reload and include output and refs; when false, only url/status/ok.snapshotOptions (object, optional): When includeSnapshot is true. interactiveOnly (boolean, default false), cursorInteractive (boolean, default false) — same as a11y_take-aria-snapshot.includeScreenshot (boolean, optional): When true, take a screenshot after reload; saved to disk. Default false.screenshotOptions (object, optional): When includeScreenshot is true; same shape as navigation_go-to (outputPath, name, fullPage, type, annotate, includeBase64).Returns: Same shape as navigation_go-to (url, status, statusText, ok, screenshotFilePath, output, refs, image).
execute - Batch-execute multiple tool calls in a single request via custom JavaScript. Reduces round-trips and token usage.Parameters:
code (string, required): JavaScript code to run in a sandboxed VM. Wrapped in an async IIFE, so await and return work directly. Use callTool(name, input, returnOutput?) to invoke any registered MCP tool.timeoutMs (number, optional): Wall-clock timeout for the entire execution in ms, including all awaited tool calls and sleep (default: 30000, max: 120000)Returns:
toolOutputs (array): Tool outputs where callTool was called with returnOutput=true. Each entry has name (tool name) and output (tool result).logs (array): Captured console.log/warn/error calls. Each entry has level and message.result (any, optional): Return value of the code (JSON-safe). Undefined on error or when nothing is returned.error (string, optional): Error message with stack trace on failure. Partial toolOutputs/logs are still returned.failedTool (object, optional): Present when a callTool invocation caused the error. Contains name (tool that failed) and error (error message).Bindings:
await callTool(name, input, returnOutput?): Invoke any registered MCP tool. Always use with await. When returnOutput=true, the output is included in the response toolOutputs array. Throws on failure — execution stops at the first error.console.log/warn/error: Captured in the response logs array (max 500 entries).sleep(ms): Async delay helper.Session execution context (injected into VM):
page (Playwright Page) is available. Use the Playwright API (e.g. page.locator(), page.goto()) or await page.evaluate(() => { ... }) / page.evaluateHandle() to run script in the browser.Built-ins (isolated via VM context):
NOT available:
Limits:
callTool invocations per executionExample — fill form, submit (with navigation wait), then snapshot and screenshot:
await callTool('interaction_fill', { selector: '#email', value: 'user@test.com' });
await callTool('interaction_fill', { selector: '#password', value: 'secret123' });
await callTool('interaction_click', { selector: 'button[type="submit"]', waitForNavigation: true });
await callTool('a11y_take-aria-snapshot', {}, true);
await callTool('content_take-screenshot', {}, true);
Notes:
setTimeout are automatically cleaned up when execution ends, preventing dangling callbacks.toolOutputs to keep the response compact; use screenshotFilePath to access images.o11y_get-console-messages - Retrieves console messages/logs from the browser with advanced filtering.Parameters:
type (enum, optional): Filter by message level - "ERROR", "WARNING", "INFO", "DEBUG"search (string, optional): Text to search for in messagestimestamp (number, optional): Start time filter (Unix epoch milliseconds)sequenceNumber (number, optional): Only return messages after this sequence numberlimit (object, optional): Limit results (default: last 100). Omit or set count: 0 for no limit.
count (number, default 100): Maximum number of messages; 0 = no limitfrom (enum): "start" or "end" (default: "end")Returns:
messages (array): Array of console messages with type, text, location, timestamp, and sequence numbero11y_get-http-requests - Retrieves HTTP requests from the browser with detailed filtering.Parameters:
resourceType (enum, optional): Filter by resource type (e.g., "document", "script", "stylesheet")status (object, optional): Filter by status code range
min (number): Minimum status codemax (number): Maximum status codeok (boolean, optional): Filter by success/failure (2xx = success)timestamp (number, optional): Start time filter (Unix epoch milliseconds)sequenceNumber (number, optional): Only return requests after this sequence numberlimit (object, optional): Limit results (default: last 100). Omit or set count: 0 for no limit.
count (number, default 100): Maximum number of requests; 0 = no limitfrom (enum): "start" or "end" (default: "end")includeRequestHeaders (boolean, optional): Include request headers in each item (default: false)includeResponseHeaders (boolean, optional): Include response headers in each item (default: false)includeResponseBody (boolean, optional): Include response body in each item (default: false)Returns:
requests (array): Array of HTTP requests with URL, method, resourceType, timing, and metadata. Request headers, response headers, and response body are present only when the corresponding include* parameter is true.o11y_get-web-vitals - Collects Web Vitals-style performance metrics and provides recommendations based on Google's thresholds.Parameters:
waitMs (number, optional): Optional wait duration in milliseconds before reading metrics (default: 0, max: 30000). Useful to allow LCP/INP/CLS to settle after interactionsincludeDebug (boolean, optional): If true, returns additional debug details such as entry counts and LCP element hint (default: false)Returns:
url (string): Current page URLtitle (string): Current page titletimestampMs (number): Unix epoch timestamp (ms) when the metrics were capturedmetrics (object): Raw metric values (null if unavailable):
lcpMs (number | null): Largest Contentful Paint in millisecondsinpMs (number | null): Interaction to Next Paint in milliseconds (best-effort approximation)cls (number | null): Cumulative Layout Shift scorettfbMs (number | null): Time to First Byte in millisecondsfcpMs (number | null): First Contentful Paint in millisecondsratings (object): Ratings computed from Google thresholds for each metric:
lcp, inp, cls, ttfb, fcp: Each contains:
rating (enum): "good", "needs_improvement", "poor", or "not_available"value (number | null): Metric valueunit (enum): "ms" or "score"thresholds (object): Thresholds used for rating (good, poor)recommendations (object): Recommendations based on measured values:
coreWebVitalsPassed (boolean): True if all Core Web Vitals are rated "good"summary (array): High-level summary and prioritization guidancelcp, inp, cls, ttfb, fcp (array): Specific recommendations for each metricgeneral (array): General measurement and debugging notesnotes (array): Notes about metric availability, browser limitations, and interpretationdebug (object, optional): Optional debug details (when includeDebug=true):
waitMs (number): Actual wait duration usedentries (object): Counts of PerformanceEntry types used to compute metricslastLcpSelectorHint (string | null): Best-effort selector hint for the last LCP elementlastLcpTagName (string | null): Tag name of the last LCP elementCore Web Vitals Thresholds:
Supporting Metrics Thresholds:
Usage:
o11y_get-trace-context - Gets the OpenTelemetry trace context of the current session.Parameters:
Returns:
traceId (string, optional): The OpenTelemetry compatible trace id of the current session if availabletraceState (string, optional): The W3C tracestate value of the current session if availableNote: Requires OpenTelemetry to be enabled (OTEL_ENABLE=true).
o11y_new-trace-id - Generates a new OpenTelemetry compatible trace id and sets it to the current session.Parameters:
Returns:
traceId (string): The generated new OpenTelemetry compatible trace idNote: Requires OpenTelemetry to be enabled (OTEL_ENABLE=true). The new trace ID is automatically set and will be used for all subsequent traces in the session.
o11y_set-trace-context - Sets or clears the OpenTelemetry trace context of the current session.Parameters:
traceId (string, optional): 32-char lowercase hex trace id (non-all-zero). Pass empty string to clear the MCP-pinned trace id (new root traces use random ids until you set an id again). Use o11y_new-trace-id to pin a fresh random id.traceState (string, optional): W3C tracestate list (key=value members, comma-separated; max 512 chars / 32 members). Pass empty string to clear tracestate (no MCP tracestate on outgoing requests).Returns:
Note: Requires OpenTelemetry to be enabled (OTEL_ENABLE=true). At least one of traceId or traceState must appear in the input (each may be an empty string for clear). Invalid traceState is rejected with an error. Values are propagated via tracing headers when applicable.
sync_wait-for-network-idle - Waits until the page reaches a network-idle condition based on the session's tracked in-flight request count.Parameters:
timeoutMs (number, optional): Maximum time to wait before failing (milliseconds, default: 30000)idleTimeMs (number, optional): How long the network must stay idle continuously before resolving (milliseconds, default: 500)maxConnections (number, optional): Idle threshold - network is considered idle when in-flight requests <= maxConnections (default: 0)pollIntervalMs (number, optional): Polling interval used to sample the in-flight request count (milliseconds, default: 50)Returns:
waitedMs (number): Total time waited until the network became idle or the tool timed outidleTimeMs (number): Idle duration required for successtimeoutMs (number): Maximum allowed wait timemaxConnections (number): Idle threshold usedpollIntervalMs (number): Polling interval usedfinalInFlightRequests (number): The last observed number of in-flight requestsobservedIdleMs (number): How long the in-flight request count stayed <= maxConnectionsUsage:
Note: This tool uses server-side tracking, so it works reliably even with strict CSP. It does NOT rely on window globals or page-injected counters.
a11y_take-aria-snapshot - Captures an ARIA (accessibility) snapshot of the current page or a specific element.Parameters:
selector (string, optional): CSS selector for element to snapshotReturns:
output (string): Includes the page URL, title, and a YAML-formatted accessibility treeUsage:
a11y_take-ax-tree-snapshot for comprehensive UI analysisa11y_take-ax-tree-snapshot - Captures a UI-focused snapshot by combining Chromium's Accessibility (AX) tree with runtime visual diagnostics.Parameters:
roles (array, optional): Optional role allowlist (button, link, textbox, checkbox, radio, combobox, switch, tab, menuitem, dialog, heading, listbox, listitem, option). If omitted, a built-in set of interactive roles is usedincludeStyles (boolean, optional): Whether to include computed CSS styles for each node (default: true)includeRuntimeVisual (boolean, optional): Whether to compute runtime visual information (bounding box, visibility, viewport) (default: true)checkOcclusion (boolean, optional): If true, checks whether each element is visually occluded by another element using elementFromPoint() sampled at multiple points (default: false)onlyVisible (boolean, optional): If true, only visually visible nodes are returned (default: false)onlyInViewport (boolean, optional): If true, only nodes intersecting the viewport are returned (default: false)textPreviewMaxLength (number, optional): Maximum length of the text preview extracted from each element (default: 80)styleProperties (array, optional): List of CSS computed style properties to extract (default: includes display, visibility, opacity, position, z-index, colors, fonts, etc.)Returns:
url (string): The current page URL at the time the AX snapshot was capturedtitle (string): The document title of the page at the time of the snapshotaxNodeCount (number): Total number of nodes returned by Chromium Accessibility.getFullAXTree before filteringcandidateCount (number): Number of DOM-backed AX nodes that passed role filtering before enrichmentenrichedCount (number): Number of nodes included in the final enriched snapshot outputtruncatedBySafetyCap (boolean): Indicates whether the result set was truncated by an internal safety capnodes (array): List of enriched DOM-backed AX nodes combining accessibility metadata with visual diagnostics, including:
axNodeId, parentAxNodeId, childAxNodeIds: Tree structurerole, name, ignored: Accessibility propertiesbackendDOMNodeId, domNodeId, frameId: DOM referenceslocalName, id, className, selectorHint: Element identificationtextPreview: Short preview of rendered text contentstyles: Computed CSS styles (if includeStyles is true)runtime: Visual diagnostics including boundingBox, isVisible, isInViewport, and optional occlusion dataUsage:
checkOcclusion: true when investigating UI/layout problemsa11y_take-aria-snapshot tool for complete UI analysisstub_intercept-http-request - Installs a request interceptor stub that can modify outgoing requests before they are sent.Parameters:
pattern (string, required): Glob pattern matched against the full request URL (picomatch)modifications (object, optional): Request modifications to apply
headers (object, optional): Headers to merge into the outgoing request headersbody (string | object, optional): Override request body. If object/array, it will be JSON-stringifiedmethod (string, optional): Override HTTP method (e.g., POST, PUT)delayMs (number, optional): Artificial delay in milliseconds before continuing the request (default: 0)times (number, optional): Apply only N times, then let through. Omit for infiniteReturns:
stubId (string): Unique id of the installed stubkind (string): Stub kind (always "intercept_http_request")pattern (string): Glob pattern usedenabled (boolean): Whether the stub is enableddelayMs (number): Applied artificial delay in millisecondstimes (number): Max applications (-1 means infinite)Use cases:
Notes:
stub_mock-http-response - Installs a response stub for matching requests using glob patterns (picomatch).Parameters:
pattern (string, required): Glob pattern matched against the full request URL (picomatch)response (object, required): Mock response configuration
action (enum, optional): "fulfill" or "abort" (default: "fulfill")status (number, optional): HTTP status code (used when action="fulfill", range: 100-599)headers (object, optional): HTTP headers for the mocked responsebody (string | object, optional): Response body. If object/array, it will be JSON-stringifiedabortErrorCode (string, optional): Playwright abort error code (used when action="abort"), e.g., "timedout"delayMs (number, optional): Artificial delay in milliseconds before applying the stub (default: 0)times (number, optional): Apply only N times, then let through. Omit for infinitechance (number, optional): Probability (0..1) to apply the stub per request (flaky testing)Returns:
stubId (string): Unique id of the installed stub (use it to clear later)kind (string): Stub kind (always "mock_http_response")pattern (string): Glob pattern usedenabled (boolean): Whether the stub is enableddelayMs (number): Applied artificial delay in millisecondstimes (number): Max applications (-1 means infinite)chance (number, optional): Apply probability (omit means always)action (string): Applied action ("fulfill" or "abort")status (number, optional): HTTP status (present when action="fulfill")Use cases:
Notes:
stub_list - Lists currently installed stubs for the active browser context/session.Parameters:
Returns:
stubs (array): Array of installed stubs, each containing:
id (string): Stub idkind (string): Stub kind ("intercept_http_request" or "mock_http_response")enabled (boolean): Whether stub is enabledpattern (string): Glob pattern (picomatch)delayMs (number): Artificial delay in mstimes (number): Max applications (-1 means infinite)usedCount (number): How many times it has been appliedaction (string, optional): For mock_response: "fulfill" or "abort"status (number, optional): For mock_response: HTTP status (if set)Usage:
stub_clear - Clears stubs installed.Parameters:
stubId (string, optional): Stub id to remove. Omit to remove all stubsReturns:
clearedCount (number): Number of stubs removedUsage:
figma_compare-page-with-design - Compares the current page UI against a Figma design snapshot and returns a combined similarity score.Parameters:
figmaFileKey (string, required): Figma file key (the part after /file/ in Figma URL)figmaNodeId (string, required): Figma node id (frame/component node, usually looks like "12:34")selector (string, optional): Optional CSS selector to screenshot only a specific element instead of the whole pagefullPage (boolean, optional): If true, captures the full scrollable page. Ignored when selector is provided (default: true)figmaScale (number, optional): Optional scale for Figma raster export (e.g., 1, 2, 3)figmaFormat (enum, optional): Optional format for Figma export - "png" or "jpg" (default: "png")weights (object, optional): Optional weights for combining signals. Missing/inactive signals are ignored and weights are renormalized:
mssim (number, optional): Weight for MSSIM signalimageEmbedding (number, optional): Weight for image embedding signaltextEmbedding (number, optional): Weight for vision→text→text embedding signalmssimMode (enum, optional): MSSIM mode - "raw" (stricter) or "semantic" (more layout-oriented, default: "semantic")maxDim (number, optional): Optional preprocessing max dimension forwarded to compare pipelinejpegQuality (number, optional): Optional JPEG quality forwarded to compare pipeline (used only when JPEG encoding is selected internally, range: 50-100)Returns:
score (number): Combined similarity score in the range [0..1]. Higher means more similarnotes (array): Human-readable notes explaining which signals were used and their individual scoresmeta (object): Metadata about what was compared:
pageUrl (string): URL of the page that was comparedpageTitle (string): Title of the page that was comparedfigmaFileKey (string): Figma file key used for the design snapshotfigmaNodeId (string): Figma node id used for the design snapshotselector (string | null): Selector used for page screenshot, if any. Null means full pagefullPage (boolean): Whether the page screenshot was full-pagepageImageType (enum): Image type of the captured page screenshot ("png" or "jpeg")figmaImageType (enum): Image type of the captured Figma snapshot ("png" or "jpeg")How it works:
Usage:
Use cases:
Browser DevTools MCP is built on a platform-extensible architecture that allows for supporting multiple runtime environments through a unified MCP interface. Each platform provides:
The Browser Platform is powered by Playwright for comprehensive browser automation and DevTools integration. The Node Platform provides non-blocking debugging for Node.js backend processes via the Chrome DevTools Protocol over WebSocket.
The server uses session-based architecture where each MCP client connection gets its own isolated runtime context. For the Browser platform, this means each session gets its own browser context and page. Sessions are automatically cleaned up when:
The Browser platform supports multiple browser engines:
Browser Configuration:
Headless Mode: By default, browsers run in headless mode (BROWSER_HEADLESS_ENABLE=true). Set to false to see the browser window.
Persistent Context: When enabled (BROWSER_PERSISTENT_ENABLE=true), browser contexts persist across sessions, preserving:
Persistent contexts are shared across sessions and are not automatically closed when sessions end.
Important for React Tools: React tools work best with persistent browser context enabled. This allows you to manually install the React DevTools extension in the browser profile, which enables reliable root discovery and component search via __REACT_DEVTOOLS_GLOBAL_HOOK__.
System Browser: When enabled (BROWSER_USE_INSTALLED_ON_SYSTEM=true), the server uses the system-installed Chrome browser instead of Playwright's bundled browser. This is useful for:
Note: System browser support is currently only available for Chromium/Chrome.
React DevTools Extension Setup:
react_get-component-for-element, react_get-element-for-component) work best when the React DevTools extension is installed in the browser profileBROWSER_PERSISTENT_ENABLE=true__reactFiber$ pointers, which is less reliable than using the DevTools hookBrowser instances are shared across sessions for efficiency. Each session gets its own isolated browser context, unless persistent context is enabled (in which case contexts are shared).
Console messages and HTTP requests are buffered in memory with configurable buffer sizes. Both tools support advanced filtering:
When enabled (OTEL_ENABLE=true), the server automatically injects OpenTelemetry instrumentation into all web pages navigated by the browser. This enables:
OTEL_EXPORTER_HTTP_URL)The OpenTelemetry integration uses a proxy mechanism (/__mcp_otel/) to forward traces from the browser to the configured collector, ensuring proper CORS handling and trace context propagation.
# Clone the repository
git clone https://github.com/serkan-ozal/browser-devtools-mcp.git
cd browser-devtools-mcp
# Install dependencies
npm install
# Build the project
npm run build
npm run build - Build TypeScript to JavaScriptnpm run start - Start server with stdio transportnpm run start:http - Start server with HTTP transportnpm run watch - Watch mode for developmentnpm run inspector - Run MCP Inspector (stdio)npm run inspector:http - Run MCP Inspector (HTTP)npm run lint:check - Check code formattingnpm run lint:format - Format codenpm run tools:token-report - Generate tool definition token consumption report (see below)The script counts tokens for each tool’s MCP definition (name, description, inputSchema, outputSchema) using gpt-tokenizer (OpenAI-style BPE). Useful for understanding context size when clients load the tool list.
When run without --platform, both browser and node are measured (default). The script starts the MCP server separately for each platform (PLATFORM=browser then PLATFORM=node), connects as an MCP client, calls tools/list for each, and counts characters from the actual payload (so the report matches what clients receive per platform). Requires a built server (npm run build). Run from the repo root.
# Via npm (recommended): MCP-based, writes to docs/TOOL-DEFINITION-TOKENS.md
npm run tools:token-report
Options:
| Option | Description |
|---|---|
| (default) | Run server with output schema disabled (measure name + description + inputSchema only; Output schema column omitted); write to docs/TOOL-DEFINITION-TOKENS.md |
--platform browser or --platform node | Run only one platform (faster; report has a single section) |
--output-schema | Run server with output schema enabled (include output schema in measurement and table) |
--no-output-schema | Explicitly run without output schema (same as default) |
--stdout | Print report to stdout instead of writing to the default file |
--output=path or -o path | Write report to the given file path |
Examples:
# Default: measure without output schema, write to docs/TOOL-DEFINITION-TOKENS.md
npm run tools:token-report
# Include output schema in the report
npm run tools:token-report -- --output-schema
# Print to console
npm run tools:token-report -- --stdout
# Only browser or only node platform
npm run tools:token-report -- --platform browser
npm run tools:token-report -- --platform node
# Write to a custom file
npm run tools:token-report -- --output=./my-report.md
npm run tools:token-report -- -o reports/tokens.md
The generated report is docs/TOOL-DEFINITION-TOKENS.md.
The Node platform enables AI assistants to:
NODE_INSPECTOR_HOST=host.docker.internal and pass the host-mapped debug port to debug_connect (e.g. debug_connect({ containerName: "my-service", inspectorPort: 30019 })).The Browser platform enables AI assistants to:
navigation_go-tosync_wait-for-network-idle if needed (for SPA pages)content_take-screenshot to see the current stateo11y_get-console-messages for errorso11y_get-http-requests to see API callsa11y_take-aria-snapshot and a11y_take-ax-tree-snapshot to understand page structurefigma_compare-page-with-design to validate design parityinteraction_click, interaction_fill, etc.content_get-as-html or content_get-as-textcontent_save-as-pdf for documentationBrowser DevTools MCP is available as a plugin for various AI coding assistants.
A dedicated Claude Code plugin is available with slash commands, skills, and agents for browser automation and testing. The plugin lives in a separate repository.
# Add the marketplace
/plugin marketplace add https://github.com/serkan-ozal/browser-devtools-claude
# Install the plugin
/plugin install browser-devtools-mcp@browser-devtools
Slash Commands (29 commands):
/browse, /back, /forward, /reload/screenshot, /html, /text, /pdf/click, /fill, /hover, /keypress, /select, /drag, /scroll, /resize/console, /network, /webvitals, /react/trace, /otel/mock, /intercept, /wait/accessibility/figma/run-js, /sandboxSkills (6 skills):
browser-testing - General browser test capabilitiesweb-debugging - Console, network, JS debuggingnode-debugging - Node.js backend debugging (tracepoints, logpoints)performance-audit - Web Vitals and performance analysisvisual-testing - Visual testing and responsive designobservability - Distributed tracing and monitoringAgents (5 agents):
qa-tester - Automated QA testing agentaccessibility-auditor - WCAG compliance auditorperformance-analyzer - Performance analysis agentscraper - Web scraping agentdesign-qa - Figma design QA agentThe plugin can be configured via environment variables. See CONFIG.md for all available options.
Example configuration in ~/.claude/settings.json:
{
"mcpServers": {
"browser-devtools": {
"command": "npx",
"args": ["-y", "browser-devtools-mcp@latest"],
"env": {
"PLATFORM": "browser",
"BROWSER_HEADLESS_ENABLE": "false"
}
}
}
}
Contributions are welcome! Please feel free to submit a Pull Request.
Elastic License 2.0 (ELv2) - see LICENSE file for details.
This license allows free use for any purpose (personal, commercial, local, production) but prohibits providing the software as a hosted/managed service.
Serkan Ozal
FAQs
MCP Server for Browser Dev Tools
We found that browser-devtools-mcp demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.