> [!WARNING]
> **This package is deprecated and no longer maintained.**
>
> `browser-devtools-mcp` has been renamed and moved to **[`@ironbee-ai/devtools`](https://www.npmjs.com/package/@ironbee-ai/devtools)**. All future releases, fixes, and new features will ship there.
>
> **Migration:**
> ```bash
> npm uninstall browser-devtools-mcp
> npm install @ironbee-ai/devtools
> ```
>
> The new package provides the same MCP server and CLI binaries (browser / node / backend platforms). Update your MCP client configuration to point at `@ironbee-ai/devtools`.

---

<p align="center">
  <img src="https://browser-devtools.com/assets/icons/icon.svg" alt="Browser DevTools MCP" width="128" height="128">
  <br>
  <strong style="font-size: 2em;">Browser DevTools MCP</strong>
</p>

<p align="center">
  <a href="https://www.npmjs.com/package/browser-devtools-mcp"><img src="https://badge.fury.io/js/browser-devtools-mcp.svg" alt="NPM Version"></a>
  <a href="https://browser-devtools.com"><img src="https://img.shields.io/badge/license-Elastic--2.0-blue" alt="License"></a>
</p>

<p align="center">
A powerful <a href="https://modelcontextprotocol.io">Model Context Protocol (MCP)</a> server that provides AI coding assistants with comprehensive automation and debugging capabilities across multiple platforms. This server enables both <strong>execution-level debugging</strong> (logs, network requests) and <strong>visual debugging</strong> (screenshots, accessibility snapshots) to help AI assistants understand and interact with applications effectively.
</p>

## Overview

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.

### Supported Platforms

| 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 |
| **Backend** | Runtime- and language-agnostic backend verification (HTTP / gRPC / GraphQL / WebSocket) + log capture (file / docker / kubernetes) + database verification (Postgres / MySQL / SQLite) | ✅ 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.

The **Backend Platform** binds to wire protocols, not specific frameworks: agents drive backend services via HTTP/1.1, HTTP/2, gRPC (unary + 3 streaming modes), GraphQL, and stateful WebSockets, with automatic W3C trace propagation so every request becomes a verifiable correlation root. Paired with the `log_*` domain, agents capture server-side log lines (from files, docker containers, or kubernetes pods) for the same trace; paired with the `db_*` domain, agents inspect database state directly (schema discovery, parameterized reads, before-vs-after snapshot diffs, polling change feeds) against Postgres / MySQL / SQLite — closing the "trigger and verify" loop without locking in to any framework, language, or runtime.

### Platform Selection

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` |
| Backend verification (HTTP/gRPC/GraphQL/WS) + log capture + database (Postgres/MySQL/SQLite) | `backend-devtools-mcp` | `backend-devtools-cli` |

### Browser Platform Capabilities

- **Visual Inspection**: Screenshots, ARIA snapshots, HTML/text extraction, PDF generation
- **Design Comparison**: Compare live page UI against Figma designs with similarity scoring
- **DOM & Code-Level Debugging**: Element inspection, computed styles, accessibility data
- **Browser Automation**: Navigation, input, clicking, scrolling, viewport control
- **Execution Monitoring**: Console message capture, HTTP request/response tracking
- **OpenTelemetry Integration**: Automatic trace injection into web pages, UI trace collection, and backend trace correlation via trace context propagation
- **JavaScript Execution**: Use the **execute** tool to batch-execute tool calls and run custom JavaScript; on the browser platform the VM receives \`page\` (Playwright Page) so you can use \`page.evaluate()\` or the Playwright API
- **Session Management**: Long-lived, session-based debugging with automatic cleanup
- **Closed tab**: If the MCP session’s browser tab was closed, the next tool run opens a new tab for that session (previous element refs are invalid until you take a new snapshot). Session network-idle / in-flight tracking state is also reset for the new tab.
- **Multiple Transport Modes**: Supports both stdio and HTTP transports

### Node Platform Capabilities

- **Connection**: Connect to Node.js processes by PID, process name, port, WebSocket URL, or Docker container
- **Non-Blocking Debugging**: Tracepoints, logpoints, exceptionpoints without pausing execution
- **Source Map Support**: Resolves bundled/minified code to original source locations; `debug_resolve-source-location` translates stack traces and bundle locations to original source
- **OpenTelemetry Integration**: When the Node process uses `@opentelemetry/api`, tracepoint/logpoint snapshots automatically include `traceContext` (traceId, spanId) for correlating backend traces with browser traces
- **Docker Support**: Connect to Node.js processes running inside Docker containers (`containerId` / `containerName`)

### Backend Platform Capabilities

- **Protocol Coverage**: HTTP/1.1 + HTTP/2 (via ALPN auto-negotiation, TLS), gRPC (unary + 3 streaming modes; .proto file or descriptor), GraphQL (query/mutation, persisted queries), and stateful multi-tool WebSocket (open / send / receive / close / list)
- **Trace Propagation**: Every request auto-injects W3C `traceparent` so the resulting `traceId` chains into log/log_read for verification — turns the platform into a true "trigger and verify" loop, not just an HTTP client. Session-pinned trace context via `o11y_new-trace-id` / `o11y_set-trace-context` / `o11y_get-trace-context` lets the agent anchor a multi-tool flow to one trace without per-call plumbing; the pin is silently shadowed by an MCP-client-supplied `_metadata.traceId` so the orchestrator's correlation boundary stays intact
- **Cookie Jar**: Session-scoped, RFC 6265 host/path scoped, auto-populated from `Set-Cookie`; agent-writable via `request_set-cookies` / `request_list-cookies` / `request_clear-cookies`
- **Default Headers / Metadata**: Host-scoped HTTP default headers and target-scoped gRPC default metadata (`request_set-default-headers`, `request_set-default-metadata`) — auth tokens stay pinned to their host/target
- **Replay**: Replay a captured curl command or HAR entry verbatim via `request_replay`
- **Log Capture (file / docker / kubernetes)**: Register a log source by name (`log_register-source`), point-in-time read (`log_read`) or live stream (`log_follow` → `log_get-followed` → `log_stop-follow`), and merge across multiple sources chronologically (`log_read-multi`)
- **Log Pipeline**: `tail` / `since` / `until` / substring `pattern` / heuristic `level` / `parseJson` + dot-path `jsonFilter` / `coalesce` for multi-line stack traces / `contextBefore` + `contextAfter` for grep-style windows / `select` for JSONL projection — all composable on a single read call
- **Rotation-Aware Following**: File source survives logrotate's rename-and-recreate, copy-and-truncate, and in-place truncate without `fs.watch` — deterministic cross-platform behavior (Linux/macOS/Windows/NFS/container FS)
- **Auto-Redaction**: Returned log lines have Authorization / Cookie / X-API-Key headers, Bearer/Basic tokens, `password=` / `token=` / `api_key=` pairs, `*_SECRET` / `*_TOKEN` env-style assignments, and matching JSON keys redacted before reaching the agent — credentials never reach the LLM context window (`BACKEND_LOG_REDACT_ENABLE` toggle, default on)
- **Source Liveness Probe**: `log_check-source` reports `{ reachable, sizeBytes, lastModified, recentLineCount, error }` for fast "why is log_read returning empty" diagnostics
- **Database Verification (Postgres / MySQL / SQLite)**: Open named connections (`db_connect` with `connectionStringEnv` so secrets stay in `process.env`, never in the agent's context), discover schema (`db_list-tables`, `db_describe-table` returns columns + primary key + indexes + foreign keys + row count estimate), run parameterized reads (`db_query`), and inspect state across protocol calls — paired with `request_*` this lets agents assert "the POST actually created the row with these values"
- **Snapshot & Diff State Verification**: `db_snapshot` captures a point-in-time set of rows keyed by primary key; two snapshots fed to `db_diff` yield exact `added` / `removed` / `changed` (with `changedColumns`) — the canonical before/after assertion primitive for AI-agent verification
- **Polling Change Feed**: `db_watch-changes` + `db_get-changes` stream diffs into a ring buffer at configurable cadence — agent watches `audit_logs` (or any table), triggers the request, drains the buffer to see exactly which rows the system wrote async
- **Readonly Defense-in-Depth**: Connections open in server-side READ ONLY mode by default (Postgres `default_transaction_read_only`, MySQL `SESSION TRANSACTION READ ONLY`, SQLite OS-level readonly file) — the actual security boundary, independent of the SQL parser whitelist that fast-fails write keywords. Writes flow only through `db_transaction-begin({ writable: true })` on connections explicitly opened with `allowWrites:true`, plus `db_seed` (structured row INSERTs) and `db_run-script` (raw multi-statement DDL/fixtures)
- **Column-Aware Redaction**: Result-set columns matching `BACKEND_DB_QUERY_REDACT_KEYS` (default `password`, `token`, `api_key`, `password_hash`, …) are replaced with `[REDACTED]` before reaching the agent; redaction recurses into JSON/JSONB column values too — `users.metadata.api_key` is caught without explicit configuration
- **Optional Peer Deps**: Engine drivers (`pg`, `mysql2`, `better-sqlite3`) are loaded only when actually used — install only what your verification scenarios need

## Browser Platform Features

### Content Tools
- **Screenshots**: Capture full page or specific elements (PNG/JPEG) with image data; optional `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.
- **HTML/Text Extraction**: Get page content with filtering, cleaning, and minification options
- **PDF Export**: Save pages as PDF documents with customizable format and margins
- **Video Recording**: Record browser page interactions as WebM video using Playwright's native `page.screencast` API (1.59+). Start with `content_start-recording`, stop with `content_stop-recording`. Works in all modes (headless, headed, persistent, CDP attach).

### Interaction Tools
- **Click**: Click elements by snapshot ref (e.g. `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.
- **Fill**: Fill form inputs (ref, getByRole/getByLabel/getByPlaceholder/etc., or CSS selector)
- **Hover**: Hover over elements (ref, getByXYZ expression, or CSS selector)
- **Press Key**: Simulate keyboard input; optional ref, getByXYZ expression, or CSS selector to focus an element before sending the key
- **Select**: Select dropdown options (ref, getByRole/getByTestId/etc., or CSS selector)
- **Drag**: Drag and drop (source and target as ref, getByXYZ expression, or CSS selector)
- **Scroll**: Scroll the page viewport or specific scrollable elements with multiple modes (by, to, top, bottom, left, right); optional ref, getByXYZ, or CSS selector for scrollable container
- **Resize Viewport**: Resize the page viewport using Playwright viewport emulation
- **Resize Window**: Resize the real browser window (OS-level) using Chrome DevTools Protocol

### Navigation Tools
- **Go To**: Navigate to URLs with configurable wait strategies. By default **waitForNavigation** is `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).
- **Go Back / Go Forward**: Navigate backward or forward in history. Same **waitForNavigation** (default true), **waitForTimeoutMs**, snapshot/refs and optional screenshot behavior as Go To.
- **Reload**: Reload the current page. Same **waitForNavigation** (default true), **waitForTimeoutMs**, snapshot/refs and optional screenshot behavior as Go To.

### Execute tool (batch)
- **Execute**: Batch-execute multiple tool calls in a single request via custom JavaScript. Use `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.

### Observability (O11Y) Tools
- **Console Messages**: Capture and filter browser console logs with advanced filtering (level, search, timestamp, sequence number)
- **HTTP Requests**: Monitor network traffic with filtering by resource type, status code, and more. Request headers, response headers, and response body are opt-in (`includeRequestHeaders`, `includeResponseHeaders`, `includeResponseBody`; default off). Response body is not stored for static assets (e.g. .js, .css, .map)
- **Web Vitals**: Collect Core Web Vitals (LCP, INP, CLS) and supporting metrics (TTFB, FCP) with ratings and recommendations based on Google's thresholds
- **OpenTelemetry Tracing**: Automatic trace injection into web pages, UI trace collection (document load, fetch, XMLHttpRequest, user interactions), and trace context propagation for backend correlation
- **Trace ID Management**: Get, set, and generate OpenTelemetry compatible trace IDs for distributed tracing across API calls

### Scenario Tools
- **Scenario Add/Update/Delete**: Manage reusable test scenarios stored as JS scripts in `.browser-devtools-mcp/scenarios.json` (project-level or global)
- **Scenario Run**: Execute a saved scenario by name. Runs in the same sandbox as `execute` — supports `callTool()`, composable via nested `scenario-run` calls (max depth: 5)
- **Scenario List**: List all available scenarios from project and/or global scope
- **Scenario Search**: Search scenarios by description using configurable strategy (simple default or FTS5)

### Synchronization Tools
- **Wait for Network Idle**: Wait until the page reaches a network-idle condition based on the session’s tracked in-flight request count (tunable via `sync_wait-for-network-idle` like `idleTimeMs` / `maxConnections`), useful for SPA pages and before taking screenshots

### Accessibility (A11Y) Tools
- **ARIA Snapshots**: Capture semantic structure and accessibility roles in YAML format. Returns a tree with element refs (e1, e2, ...) and a `refs` 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).
- **AX Tree Snapshots**: Combine Chromium's Accessibility tree with runtime visual diagnostics (bounding boxes, visibility, occlusion detection, computed styles)

### Stub Tools
- **Intercept HTTP Request**: Intercept and modify outgoing HTTP requests (headers, body, method) using glob patterns
- **Mock HTTP Response**: Mock HTTP responses (fulfill with custom status/headers/body or abort) with configurable delay, times limit, and probability (flaky testing)
- **List Stubs**: List all currently installed stubs for the active browser context
- **Clear Stubs**: Remove one or all installed stubs

### Figma Tools
- **Compare Page with Design**: Compare the current page UI against a Figma design snapshot and return a combined similarity score using multiple signals (MSSIM, image embedding, text embedding)

### React Tools
- **Get Component for Element**: Find React component(s) associated with a DOM element using React Fiber (best-effort)
- **Get Element for Component**: Map a React component instance to the DOM elements it renders by traversing the React Fiber graph

**Important Requirements for React Tools:**
- **Persistent Browser Context**: React tools work best with persistent browser context enabled (`BROWSER_PERSISTENT_ENABLE=true`)
- **React DevTools Extension**: For optimal reliability, manually install the "React Developer Tools" Chrome extension in the browser profile. The MCP server does NOT automatically install the extension.
  - Chrome Web Store: https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi
  - The extension enables reliable root discovery and component search via `__REACT_DEVTOOLS_GLOBAL_HOOK__`
  - Without the extension, tools fall back to scanning DOM for `__reactFiber$` pointers (best-effort, less reliable)

### Debug Tools (Non-Blocking)
Non-blocking debugging tools that capture snapshots without pausing execution. Ideal for AI-assisted debugging.

**Probe Types:**
- **Tracepoint**: Captures call stack, local variables, and async stack traces at a code location
- **Logpoint**: Evaluates and logs expressions without full debug context (lightweight)
- **Exceptionpoint**: Captures snapshots when exceptions occur (uncaught or all)

**Core Operations (per probe type):**
- `put-*`: Create a probe at a location
- `remove-*`: Remove a specific probe
- `list-*`: List all probes of a type
- `clear-*`: Remove all probes of a type
- `get-*-snapshots`: Retrieve captured snapshots (supports `fromSequence` for polling)
- `clear-*-snapshots`: Clear captured snapshots

**Additional Tools:**
- **Resolve Source Location**: Resolve a generated/bundle location (URL + line + column) to original source via source maps—useful for translating minified stack traces
- **Watch Expressions**: Add expressions to evaluate at every tracepoint/exceptionpoint hit
- **Status**: Get current debugging status (enabled, probe counts, snapshot stats)

**Key Features:**
- Source map support for debugging bundled/minified code with original source locations
- Automatic debugging enablement on first tool use
- Configurable limits (max snapshots, call stack depth, async segments)
- Sequence numbers for efficient snapshot polling
- OpenTelemetry trace context in Node snapshots (traceId, spanId when process uses @opentelemetry/api)
- **Snapshot capture**: 1 call stack frame with scopes (both platforms) to keep payloads small. **Output trimming** for `get-probe-snapshots`: default scopes = `local` only (both Browser and Node), 20 variables/scope. Override via `maxCallStackDepth`, `includeScopes`, `maxVariablesPerScope`.

## Prerequisites

- Node.js 18+
- An AI assistant (with MCP client) like Cursor, Claude (Desktop or Code), VS Code, Windsurf, etc.

## Quick Start

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.

---

### 📦 Playwright browser binaries (required for browser platform)

> **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 normal `npm 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:
   - **Chromium** (chromium + chromium-headless-shell + ffmpeg):
     ```bash
     BROWSER_DEVTOOLS_INSTALL_CHROMIUM=true npx -y browser-devtools-mcp
     ```
   - **Firefox:** `BROWSER_DEVTOOLS_INSTALL_FIREFOX=true`
   - **WebKit:** `BROWSER_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):
   ```bash
   npx playwright install chromium   # or firefox, webkit
   ```
   On Linux, include system dependencies:
   ```bash
   npx playwright install --with-deps chromium
   ```

---

### CLI Arguments

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 as AI Agent Skill

Install browser automation capabilities as a skill for AI coding agents (Claude Code, Cursor, Windsurf, etc.) using the [skills.sh](https://skills.sh) ecosystem:

```bash
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](https://github.com/serkan-ozal/browser-devtools-skills) for details.

## MCP Client Configuration

To use the **Node platform** (Node.js backend debugging), use `node-devtools-mcp` instead of `browser-devtools-mcp`:

```json
{
  "mcpServers": {
    "node-devtools": {
      "command": "npx",
      "args": ["-y", "-p", "browser-devtools-mcp", "node-devtools-mcp"]
    }
  }
}
```

Alternatively, set `PLATFORM=node` when running `browser-devtools-mcp`.

<details>
<summary><strong>Claude Desktop</strong></summary>

#### Local Server
Add the following configuration into the `claude_desktop_config.json` file.
See the [Claude Desktop MCP docs](https://modelcontextprotocol.io/docs/develop/connect-local-servers) for more info.

**Browser platform (default):**

> **→ Browser platform requires Playwright browser binaries.** See [📦 Playwright browser binaries](#-playwright-browser-binaries-required-for-browser-platform) for one-time install (env vars or `npx playwright install chromium`).

```json
{
  "mcpServers": {
    "browser-devtools": {
      "command": "npx",
      "args": ["-y", "browser-devtools-mcp"]
    }
  }
}
```

**Node platform:**
```json
{
  "mcpServers": {
    "node-devtools": {
      "command": "npx",
      "args": ["-y", "-p", "browser-devtools-mcp", "node-devtools-mcp"]
    }
  }
}
```

#### Remote Server (HTTP Transport)
First, start the server with HTTP transport:
```bash
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:
- Name: `Browser DevTools`
- Remote MCP server URL: Point to where your server is hosted (e.g., `http://localhost:3000/mcp` if running locally, or `https://your-server.com/mcp` if hosted remotely)

</details>

<details>
<summary><strong>Claude Code</strong></summary>

Run the following command.
See [Claude Code MCP docs](https://docs.anthropic.com/en/docs/claude-code/mcp) for more info.

> **→ Browser platform requires Playwright browser binaries.** See [📦 Playwright browser binaries](#-playwright-browser-binaries-required-for-browser-platform) for one-time install (env vars or `npx playwright install chromium`).

#### Local Server
```bash
claude mcp add browser-devtools -- npx -y browser-devtools-mcp
```

#### Remote Server
First, start the server with HTTP transport:
```bash
npx -y browser-devtools-mcp --transport=streamable-http --port=3000
```

Then add the MCP server:
```bash
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).

</details>

<details>
<summary><strong>Cursor</strong></summary>

Add the following configuration into the `~/.cursor/mcp.json` file (or `.cursor/mcp.json` in your project folder).
See the [Cursor MCP docs](https://docs.cursor.com/context/model-context-protocol) for more info.

> **→ Browser platform requires Playwright browser binaries.** See [📦 Playwright browser binaries](#-playwright-browser-binaries-required-for-browser-platform) for one-time install (env vars or `npx playwright install chromium`).

#### Local Server
```json
{
  "mcpServers": {
    "browser-devtools": {
      "command": "npx",
      "args": ["-y", "browser-devtools-mcp"]
    }
  }
}
```

#### Remote Server
First, start the server with HTTP transport:
```bash
npx -y browser-devtools-mcp --transport=streamable-http --port=3000
```

Then add the configuration:
```json
{
  "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).

</details>

<details>
<summary><strong>VS Code</strong></summary>

Add the following configuration into the `.vscode/mcp.json` file.
See the [VS Code MCP docs](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) for more info.

> **→ Browser platform requires Playwright browser binaries.** See [📦 Playwright browser binaries](#-playwright-browser-binaries-required-for-browser-platform) for one-time install (env vars or `npx playwright install chromium`).

#### Local Server
```json
{
  "mcp": {
    "servers": {
      "browser-devtools": {
        "type": "stdio",
        "command": "npx",
        "args": ["-y", "browser-devtools-mcp"]
      }
    }
  }
}
```

#### Remote Server
First, start the server with HTTP transport:
```bash
npx -y browser-devtools-mcp --transport=streamable-http --port=3000
```

Then add the configuration:
```json
{
  "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).

</details>

<details>
<summary><strong>Windsurf</strong></summary>

Add the following configuration into the `~/.codeium/windsurf/mcp_config.json` file. 
See the [Windsurf MCP docs](https://docs.windsurf.com/windsurf/cascade/mcp) for more info.

> **→ Browser platform requires Playwright browser binaries.** See [📦 Playwright browser binaries](#-playwright-browser-binaries-required-for-browser-platform) for one-time install (env vars or `npx playwright install chromium`).

#### Local Server
```json
{
  "mcpServers": {
    "browser-devtools": {
      "command": "npx",
      "args": ["-y", "browser-devtools-mcp"]
    }
  }
}
```

#### Remote Server
First, start the server with HTTP transport:
```bash
npx -y browser-devtools-mcp --transport=streamable-http --port=3000
```

Then add the configuration:
```json
{
  "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).

</details>

<details>
<summary><strong>Copilot Coding Agent</strong></summary>

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](https://docs.github.com/en/enterprise-cloud@latest/copilot/how-tos/agents/copilot-coding-agent/extending-copilot-coding-agent-with-mcp) for more info.

> **→ Browser platform requires Playwright browser binaries.** See [📦 Playwright browser binaries](#-playwright-browser-binaries-required-for-browser-platform) for one-time install (env vars or `npx playwright install chromium`).

#### Local Server
```json
{
  "mcpServers": {
    "browser-devtools": {
      "type": "local",
      "command": "npx",
      "args": ["-y", "browser-devtools-mcp"]
    }
  }
}
```

#### Remote Server
First, start the server with HTTP transport:
```bash
npx -y browser-devtools-mcp --transport=streamable-http --port=3000
```

Then add the configuration:
```json
{
  "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).

</details>

<details>
<summary><strong>Gemini CLI</strong></summary>

Add the following configuration into the `~/.gemini/settings.json` file.
See the [Gemini CLI MCP docs](https://google-gemini.github.io/gemini-cli/docs/tools/mcp-server.html) for more info.

#### Local Server
```json
{
  "mcpServers": {
    "browser-devtools": {
      "command": "npx",
      "args": ["-y", "browser-devtools-mcp"]
    }
  }
}
```

#### Remote Server
First, start the server with HTTP transport:
```bash
npx -y browser-devtools-mcp --transport=streamable-http --port=3000
```

Then add the configuration:
```json
{
  "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).

</details>

<details>
<summary><strong>Smithery</strong></summary>

Run the following command.
You can find your Smithery API key [here](https://smithery.ai/account/api-keys).
See the [Smithery CLI docs](https://smithery.ai/docs/concepts/cli) for more info.

```bash
npx -y @smithery/cli install serkan-ozal/browser-devtools-mcp --client <SMITHERY-CLIENT-NAME> --key <SMITHERY-API-KEY>
```

</details>

## HTTP Transport

To use HTTP transport, start the server with:
```bash
npx -y browser-devtools-mcp --transport=streamable-http --port=3000
```

The server exposes the following endpoints:

- `GET /health` - Health check
- `GET /ping` - Ping endpoint
- `GET /mcp` - MCP protocol info
- `POST /mcp` - MCP protocol messages
- `DELETE /mcp` - Delete session

**Important**: When configuring remote MCP servers, use the actual URL where your server is hosted:
- If running locally: `http://localhost:3000/mcp` (or `http://127.0.0.1:3000/mcp`)
- If hosted remotely: `https://your-server.com/mcp` (replace with your actual server URL)

## MCP Inspector

Test the server using the MCP Inspector:

```bash
# 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
```

## CLI Tools

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, exceptionpoints

This is particularly useful for:

- **Scripting and automation**: Use in shell scripts, CI/CD pipelines, or automated workflows
- **Session-based testing**: Maintain browser state across multiple commands with session IDs
- **Skill-based workflows**: Build reusable automation sequences

### Installation

The CLIs are included with the npm package:

```bash
# 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](#playwright-browser-binaries)).

### Global Options

| 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` |

### Browser Options

| 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 CLI Commands

`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 CLI Commands

`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
```

### Usage Examples

#### Basic Navigation and Screenshot

```bash
# 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"
```

#### Browser Options

Configure browser behavior when starting the daemon:

```bash
# 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"
```

#### Session-Based Workflow

Maintain browser state across multiple commands using session IDs:

```bash
# 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
```

#### JSON Output for Scripting

Use `--json` and `--quiet` flags for machine-readable output:

```bash
# 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
# }
```

#### Daemon Management

```bash
# 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
```

#### Session Management

```bash
# 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
```

#### Tool Discovery

```bash
# 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]
```

#### Configuration

```bash
# 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
```

#### Verbose/Debug Mode

Enable verbose output for troubleshooting:

```bash
# 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" }
# ...
```

#### Tool Search

Find tools by keyword:

```bash
# 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
```

#### Shell Completions

Enable tab completion for faster command entry. Shell completions require a one-time setup:

**For Bash:**
```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):**
```bash
# 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:**
```bash
# 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:
```bash
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
```

#### Interactive REPL Mode

Start an interactive session for continuous command entry:

```bash
# 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 |

#### Check for Updates

Keep your CLI up to date:

```bash
# 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!
```

#### Shell Script Example

```bash
#!/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
```

### Daemon Architecture

The CLI uses a daemon server architecture for efficient browser management:

1. **Auto-start**: The daemon starts automatically when you run any tool command
2. **Shared browser**: Multiple CLI invocations share the same browser instance
3. **Session isolation**: Each session ID gets its own isolated browser context
4. **Auto-cleanup**: Idle sessions are automatically cleaned up after inactivity
5. **Full tool set**: The daemon exposes the same tools as MCP (including **execute** for batch execution). Most tools map to domain subcommands (e.g. `navigation go-to`). **execute** maps to **`run execute`** (not listed under `tools list`, which only reflects the static tool registry).
6. **Platform env**: The browser CLI forces `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.

### CLI Skills Documentation

Comprehensive documentation for AI agents and automation is available in the [browser-devtools-skills](https://github.com/serkan-ozal/browser-devtools-skills) repository.

Install as an AI agent skill:

```bash
npx skills add serkan-ozal/browser-devtools-skills
```

For the full list of available skills and documentation, see the [browser-devtools-skills](https://github.com/serkan-ozal/browser-devtools-skills) repository.

## Configuration

The server can be configured using environment variables. Configuration is divided into server-level settings and platform-specific settings.

### Server Configuration

| 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.

### Node Platform Configuration

| 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` |

### Browser Platform Configuration

| 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) |
| `BROWSER_CHROMIUM_SANDBOX` | When `false`, launches Chromium with `--no-sandbox`. Required for Docker/Lambda/CI. | `true` |
| `BROWSER_LAUNCH_ARGS` | Extra Chromium launch args (comma-separated). e.g. `--disable-dev-shm-usage,--single-process` | (none) |
| `BROWSER_ALLOWED_DOMAINS` | Comma-separated list of allowed hostnames. When set, all requests to non-matching hosts are aborted. Subdomains match automatically (`example.com` matches `www.example.com`). | (unset — no restriction) |
| `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` |

#### CDP attach (no auto-launch)

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.

##### CDP errors (what the message means)

| 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. |

## Telemetry

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.

### What is collected

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:**

- Any personally identifiable information (PII)
- `error.message` content — only the error class name and code are sent
- URLs, page content, screenshots, or any application data

A persistent anonymous UUID is stored locally at `~/.browser-devtools-mcp/config.json`. It is never linked to any user identity.

### Source values

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 |

### How to disable telemetry

**MCP server** — set `TELEMETRY_ENABLE=false` in your MCP client config:

```json
{
  "mcpServers": {
    "browser-devtools": {
      "command": "npx",
      "args": ["-y", "browser-devtools-mcp"],
      "env": { "TELEMETRY_ENABLE": "false" }
    }
  }
}
```

**CLI** — pass `--no-telemetry` to any command:

```bash
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:

```bash
# ~/.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.

## Browser Platform Tools

### Content Tools

<details>
<summary><code>content_take-screenshot</code> - Takes a screenshot of the current page or a specific element.</summary>

**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 capture
- `fullPage` (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 file
- `image` (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 used

**Notes:**
- The screenshot is always saved to the file system and the file path is returned
- By default, image data is NOT included in the response to reduce payload size
- Set `includeBase64` to true when the AI assistant cannot access the MCP server's file system (e.g., remote server, containerized environment, or different machine)
- The `quality` parameter only applies to JPEG images. PNG images are always saved at full quality
- Lower quality values (e.g., 50-70) result in smaller file sizes but reduced image quality
- Annotation uses the session ref map; use `annotateContent` to include headings/content, or `annotateCursorInteractive` to include CSS-clickable elements, without a prior ARIA snapshot with those options
</details>

<details>
<summary><code>content_get-as-html</code> - Retrieves the HTML content of the current page or a specific element.</summary>

**Parameters:**
- `selector` (string, optional): CSS selector to limit the HTML content to a specific container
- `removeScripts` (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 page
</details>

<details>
<summary><code>content_get-as-text</code> - Retrieves the visible text content of the current page or a specific element.</summary>

**Parameters:**
- `selector` (string, optional): CSS selector to limit the text content to a specific container
- `maxLength` (number, optional): Maximum number of characters to return (default: 50000)

**Returns:**
- `output` (string): The requested text content of the page
</details>

<details>
<summary><code>content_save-as-pdf</code> - Saves the current page as a PDF document.</summary>

**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 file
</details>

<details>
<summary><code>content_start-recording</code> - Starts video recording of the browser page.</summary>

**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 message
- `startTimestamp` (number, optional): Wall-clock time (ms since epoch) of the video's first frame. Use this to align video time with other timestamps (logs, network events, etc.) — an event's position in the video is `(eventTimestampMs - startTimestamp) / 1000` seconds. Omitted when recording is already in progress.

**Notes:**
- Uses Playwright's native `page.screencast` API (1.59+) — works in all modes (headless, headed, persistent, CDP attach)
- Recording captures all page interactions until `content_stop-recording` is called
- `startTimestamp` is the wall-clock time (ms) when the **first encoded frame** arrives — anchored to the same point Playwright uses for video t=0, so `(eventTimestampMs - startTimestamp) / 1000` is the exact video position of any event. A 1px transient overlay is auto-injected right after start to force a prompt first frame; failures fall back to `Date.now()` after 1500ms.
- Playwright actions (click, fill, hover, drag, scroll, etc.) are automatically annotated with labels in the video via `showActions` — recordings double as self-narrated walkthroughs.
</details>

<details>
<summary><code>content_stop-recording</code> - Stops video recording and saves the video file.</summary>

**Returns:**
- `filePath` (string, optional): Full path of the saved WebM video file

**Notes:**
- Must be called after `content_start-recording`
- The video is saved as a WebM file (VP8 codec)
</details>

### Interaction Tools

<details>
<summary><code>interaction_click</code> - Clicks an element. Set waitForNavigation: true when click opens a new page.</summary>

**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 click
- `timeoutMs` (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: false
- `waitForTimeoutMs` (number, optional): Timeout for navigation and network idle wait in ms. Only when waitForNavigation is true. Default: 30000
</details>

<details>
<summary><code>interaction_fill</code> - Fills a form input field.</summary>

**Parameters:**
- `selector` (string, required): Ref (e.g. `e1`, `@e1`), getByRole/getByLabel/getByPlaceholder expression, or CSS selector for the input field
- `value` (string, required): Value to fill
- `timeoutMs` (number, optional): Time to wait for the element in ms (default: 10000)
</details>

<details>
<summary><code>interaction_hover</code> - Hovers over an element.</summary>

**Parameters:**
- `selector` (string, required): Ref (e.g. `e1`, `@e1`), getByRole/getByText/etc. expression, or CSS selector for the element to hover
- `timeoutMs` (number, optional): Time to wait for the element in ms (default: 10000)
</details>

<details>
<summary><code>interaction_press-key</code> - Simulates keyboard input.</summary>

**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 key
- `holdMs` (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 `holdMs`
- `repeatIntervalMs` (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)
</details>

<details>
<summary><code>interaction_select</code> - Selects an option from a dropdown.</summary>

**Parameters:**
- `selector` (string, required): Ref (e.g. `e1`, `@e1`), getByRole/getByTestId expression, or CSS selector for the select element
- `value` (string, required): Value to select
- `timeoutMs` (number, optional): Time to wait for the element in ms (default: 10000)
</details>

<details>
<summary><code>interaction_drag</code> - Performs drag and drop operation.</summary>

**Parameters:**
- `sourceSelector` (string, required): Ref (e.g. `e1`, `@e1`), getByRole/getByText/etc. expression, or CSS selector for the source element
- `targetSelector` (string, required): Ref, getByRole/getByText/etc. expression, or CSS selector for the target element
- `timeoutMs` (number, optional): Time to wait for source and target elements in ms (default: 10000)
</details>

<details>
<summary><code>interaction_scroll</code> - Scrolls the page viewport or a specific scrollable element.</summary>

**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 viewport
- `dx` (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 used
- `selector` (string | null): The selector of the scroll container if provided; otherwise null (document viewport)
- `behavior` (string): The scroll behavior used
- `before` (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 possible
- `canScrollY` (boolean): Whether vertical scrolling is possible
- `maxScrollX` (number): Maximum horizontal scrollLeft
- `maxScrollY` (number): Maximum vertical scrollTop
- `isAtLeft` (boolean): Whether the scroll position is at the far left
- `isAtRight` (boolean): Whether the scroll position is at the far right
- `isAtTop` (boolean): Whether the scroll position is at the very top
- `isAtBottom` (boolean): Whether the scroll position is at the very bottom

**Usage:**
- Reveal content below the fold
- Jump to the top/bottom without knowing exact positions
- Bring elements into view before clicking
- Inspect lazy-loaded content that appears on scroll
</details>

<details>
<summary><code>interaction_resize-viewport</code> - Resizes the page viewport using Playwright viewport emulation.</summary>

**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/innerHeight
  - `outerWidth`, `outerHeight`: window.outerWidth/outerHeight
  - `devicePixelRatio`: window.devicePixelRatio

**Notes:**
- This affects `window.innerWidth/innerHeight`, CSS media queries, layout, rendering, and screenshots
- This does NOT resize the OS-level browser window
- Runtime switching to viewport=null (binding to real window size) is not supported by Playwright
- If you need real window-driven responsive behavior, start the BrowserContext with viewport: null and use the window resize tool instead
</details>

<details>
<summary><code>interaction_resize-window</code> - Resizes the real browser window (OS-level window) for the current page using Chrome DevTools Protocol (CDP).</summary>

**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:**
- Works best on Chromium-based browsers (Chromium/Chrome/Edge)
- Especially useful in headful sessions when running with viewport emulation disabled (viewport: null)
- If Playwright viewport emulation is enabled (viewport is NOT null), resizing the OS window may not change page layout
- On non-Chromium browsers (Firefox/WebKit), CDP is not available and this tool will fail
</details>

### Navigation Tools

<details>
<summary><code>navigation_go-to</code> - Navigates to a URL.</summary>

**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.
</details>

<details>
<summary><code>navigation_go-back-or-forward</code> - Navigates back or forward in browser history.</summary>

**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).
</details>

<details>
<summary><code>navigation_reload</code> - Reloads the current page.</summary>

**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).
</details>

### Run Tools

<details>
<summary><code>execute</code> - Batch-execute multiple tool calls in a single request via custom JavaScript. Reduces round-trips and token usage.</summary>

**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):**
- **Browser platform:** `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.
- **Node platform:** No extra bindings (empty object).

**Built-ins (isolated via VM context):**
- Math, JSON, Date, RegExp, Number, String, Boolean, Array, Object, Promise, Map, Set, WeakMap, WeakSet, Symbol, Proxy, Reflect
- URL, URLSearchParams, TextEncoder, TextDecoder, structuredClone
- crypto.randomUUID(), AbortController, setTimeout, clearTimeout

**NOT available:**
- require, import, process, fs, Buffer, fetch

**Limits:**
- Max 50 `callTool` invocations per execution
- Max 500 console log entries
- Wall-clock timeout covers all async work (tool calls, sleep, etc.)
- Separate sync CPU guard (10s) prevents tight infinite loops

**Example — fill form, submit (with navigation wait), then snapshot and screenshot:**
```js
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:**
- This is the **recommended** way to perform multi-step interactions. Instead of separate tool calls for each fill/click/select, batch them together for fewer round-trips and lower token usage.
- Execution runs in an isolated VM context — prototype modifications inside the sandbox do not leak to the host process.
- All timers created via `setTimeout` are automatically cleaned up when execution ends, preventing dangling callbacks.
- Image buffers are stripped from `toolOutputs` to keep the response compact; use `screenshotFilePath` to access images.
</details>

### Observability (O11Y) Tools

<details>
<summary><code>o11y_get-console-messages</code> - Retrieves console messages/logs from the browser with advanced filtering.</summary>

**Parameters:**
- `type` (enum, optional): Filter by message level - "ERROR", "WARNING", "INFO", "DEBUG"
- `search` (string, optional): Text to search for in messages
- `timestamp` (number, optional): Start time filter (Unix epoch milliseconds)
- `sequenceNumber` (number, optional): Only return messages after this sequence number
- `limit` (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 limit
  - `from` (enum): "start" or "end" (default: "end")

**Returns:**
- `messages` (array): Array of console messages with type, text, location, timestamp, and sequence number
</details>

<details>
<summary><code>o11y_get-http-requests</code> - Retrieves HTTP requests from the browser with detailed filtering.</summary>

**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 code
  - `max` (number): Maximum status code
- `ok` (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 number
- `limit` (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 limit
  - `from` (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.
</details>

<details>
<summary><code>o11y_get-web-vitals</code> - Collects Web Vitals-style performance metrics and provides recommendations based on Google's thresholds.</summary>

**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 interactions
- `includeDebug` (boolean, optional): If true, returns additional debug details such as entry counts and LCP element hint (default: false)

**Returns:**
- `url` (string): Current page URL
- `title` (string): Current page title
- `timestampMs` (number): Unix epoch timestamp (ms) when the metrics were captured
- `metrics` (object): Raw metric values (null if unavailable):
  - `lcpMs` (number | null): Largest Contentful Paint in milliseconds
  - `inpMs` (number | null): Interaction to Next Paint in milliseconds (best-effort approximation)
  - `cls` (number | null): Cumulative Layout Shift score
  - `ttfbMs` (number | null): Time to First Byte in milliseconds
  - `fcpMs` (number | null): First Contentful Paint in milliseconds
- `ratings` (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 value
    - `unit` (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 guidance
  - `lcp`, `inp`, `cls`, `ttfb`, `fcp` (array): Specific recommendations for each metric
  - `general` (array): General measurement and debugging notes
- `notes` (array): Notes about metric availability, browser limitations, and interpretation
- `debug` (object, optional): Optional debug details (when includeDebug=true):
  - `waitMs` (number): Actual wait duration used
  - `entries` (object): Counts of PerformanceEntry types used to compute metrics
  - `lastLcpSelectorHint` (string | null): Best-effort selector hint for the last LCP element
  - `lastLcpTagName` (string | null): Tag name of the last LCP element

**Core Web Vitals Thresholds:**
- **LCP** (Largest Contentful Paint): good <= 2500ms, poor > 4000ms
- **INP** (Interaction to Next Paint): good <= 200ms, poor > 500ms
- **CLS** (Cumulative Layout Shift): good <= 0.1, poor > 0.25

**Supporting Metrics Thresholds:**
- **TTFB** (Time to First Byte): good <= 800ms, poor > 1800ms
- **FCP** (First Contentful Paint): good <= 1800ms, poor > 3000ms

**Usage:**
- Call after navigation and after user actions
- If you need more stable LCP/CLS/INP, pass waitMs (e.g., 1000-3000ms)
- Some metrics may be unavailable depending on browser support and whether interactions occurred
</details>

<details>
<summary><code>o11y_get-trace-context</code> - Gets the OpenTelemetry trace context of the current session.</summary>

**Parameters:**
- No input parameters

**Returns:**
- `traceId` (string, optional): The OpenTelemetry compatible trace id of the current session if available
- `traceState` (string, optional): The W3C tracestate value of the current session if available

**Note:** Requires OpenTelemetry to be enabled (`OTEL_ENABLE=true`).
</details>

<details>
<summary><code>o11y_new-trace-id</code> - Generates a new OpenTelemetry compatible trace id and sets it to the current session.</summary>

**Parameters:**
- No input parameters

**Returns:**
- `traceId` (string): The generated new OpenTelemetry compatible trace id

**Note:** 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.
</details>

<details>
<summary><code>o11y_set-trace-context</code> - Sets or clears the OpenTelemetry trace context of the current session.</summary>

**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:**
- No return value

**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.
</details>

### Synchronization Tools

<details>
<summary><code>sync_wait-for-network-idle</code> - Waits until the page reaches a network-idle condition based on the session's tracked in-flight request count.</summary>

**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 out
- `idleTimeMs` (number): Idle duration required for success
- `timeoutMs` (number): Maximum allowed wait time
- `maxConnections` (number): Idle threshold used
- `pollIntervalMs` (number): Polling interval used
- `finalInFlightRequests` (number): The last observed number of in-flight requests
- `observedIdleMs` (number): How long the in-flight request count stayed <= maxConnections

**Usage:**
- Use before interacting with SPA pages that load data asynchronously
- Use before taking screenshots or AX tree snapshots for more stable results
- Use after actions that trigger background fetch/XHR activity

**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.
</details>

### Accessibility (A11Y) Tools

<details>
<summary><code>a11y_take-aria-snapshot</code> - Captures an ARIA (accessibility) snapshot of the current page or a specific element.</summary>

**Parameters:**
- `selector` (string, optional): CSS selector for element to snapshot

**Returns:**
- `output` (string): Includes the page URL, title, and a YAML-formatted accessibility tree

**Usage:**
- Use in combination with `a11y_take-ax-tree-snapshot` for comprehensive UI analysis
- Provides semantic structure and accessibility roles
- Helps identify accessibility issues and page hierarchy problems
</details>

<details>
<summary><code>a11y_take-ax-tree-snapshot</code> - Captures a UI-focused snapshot by combining Chromium's Accessibility (AX) tree with runtime visual diagnostics.</summary>

**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 used
- `includeStyles` (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 captured
- `title` (string): The document title of the page at the time of the snapshot
- `axNodeCount` (number): Total number of nodes returned by Chromium Accessibility.getFullAXTree before filtering
- `candidateCount` (number): Number of DOM-backed AX nodes that passed role filtering before enrichment
- `enrichedCount` (number): Number of nodes included in the final enriched snapshot output
- `truncatedBySafetyCap` (boolean): Indicates whether the result set was truncated by an internal safety cap
- `nodes` (array): List of enriched DOM-backed AX nodes combining accessibility metadata with visual diagnostics, including:
  - `axNodeId`, `parentAxNodeId`, `childAxNodeIds`: Tree structure
  - `role`, `name`, `ignored`: Accessibility properties
  - `backendDOMNodeId`, `domNodeId`, `frameId`: DOM references
  - `localName`, `id`, `className`, `selectorHint`: Element identification
  - `textPreview`: Short preview of rendered text content
  - `styles`: Computed CSS styles (if includeStyles is true)
  - `runtime`: Visual diagnostics including boundingBox, isVisible, isInViewport, and optional occlusion data

**Usage:**
- Use to detect UI issues like elements that exist semantically but are visually hidden or off-screen
- Identify wrong layout/geometry, styling issues, and overlap/stacking/occlusion problems
- ALWAYS use `checkOcclusion: true` when investigating UI/layout problems
- Use alongside `a11y_take-aria-snapshot` tool for complete UI analysis
</details>

### Stub Tools

<details>
<summary><code>stub_intercept-http-request</code> - Installs a request interceptor stub that can modify outgoing requests before they are sent.</summary>

**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 headers
  - `body` (string | object, optional): Override request body. If object/array, it will be JSON-stringified
  - `method` (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 infinite

**Returns:**
- `stubId` (string): Unique id of the installed stub
- `kind` (string): Stub kind (always "intercept_http_request")
- `pattern` (string): Glob pattern used
- `enabled` (boolean): Whether the stub is enabled
- `delayMs` (number): Applied artificial delay in milliseconds
- `times` (number): Max applications (-1 means infinite)

**Use cases:**
- A/B testing / feature flags (inject headers)
- Security testing (inject malformed headers / payload)
- Edge cases (special characters, large payload)
- Auth simulation (add API keys / tokens in headers)

**Notes:**
- Pattern is a glob matched against the full request URL (picomatch)
- This modifies requests; it does not change responses
- Times limits how many times the interceptor applies (-1 means infinite)
</details>

<details>
<summary><code>stub_mock-http-response</code> - Installs a response stub for matching requests using glob patterns (picomatch).</summary>

**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 response
  - `body` (string | object, optional): Response body. If object/array, it will be JSON-stringified
  - `abortErrorCode` (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 infinite
- `chance` (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 used
- `enabled` (boolean): Whether the stub is enabled
- `delayMs` (number): Applied artificial delay in milliseconds
- `times` (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:**
- Offline testing (return 200 with local JSON)
- Error scenarios (force 500/404 or abort with timedout)
- Edge cases (empty data / huge payload / special characters)
- Flaky API testing (chance < 1.0)
- Performance testing (delayMs)

**Notes:**
- Pattern is a glob matched against the full request URL
- Stubs are evaluated in insertion order; first match wins
- Times limits how many times the stub applies (-1 means infinite)
</details>

<details>
<summary><code>stub_list</code> - Lists currently installed stubs for the active browser context/session.</summary>

**Parameters:**
- No input parameters

**Returns:**
- `stubs` (array): Array of installed stubs, each containing:
  - `id` (string): Stub id
  - `kind` (string): Stub kind ("intercept_http_request" or "mock_http_response")
  - `enabled` (boolean): Whether stub is enabled
  - `pattern` (string): Glob pattern (picomatch)
  - `delayMs` (number): Artificial delay in ms
  - `times` (number): Max applications (-1 means infinite)
  - `usedCount` (number): How many times it has been applied
  - `action` (string, optional): For mock_response: "fulfill" or "abort"
  - `status` (number, optional): For mock_response: HTTP status (if set)

**Usage:**
- Useful to debug why certain calls are being mocked/intercepted
- Check stub status and usage statistics
- Verify stub configuration before debugging issues
</details>

<details>
<summary><code>stub_clear</code> - Clears stubs installed.</summary>

**Parameters:**
- `stubId` (string, optional): Stub id to remove. Omit to remove all stubs

**Returns:**
- `clearedCount` (number): Number of stubs removed

**Usage:**
- Remove specific stub by ID when no longer needed
- Clear all stubs to reset the browser context
- Useful after testing or debugging sessions
</details>

### Figma Tools

<details>
<summary><code>figma_compare-page-with-design</code> - Compares the current page UI against a Figma design snapshot and returns a combined similarity score.</summary>

**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 page
- `fullPage` (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 signal
  - `imageEmbedding` (number, optional): Weight for image embedding signal
  - `textEmbedding` (number, optional): Weight for vision→text→text embedding signal
- `mssimMode` (enum, optional): MSSIM mode - "raw" (stricter) or "semantic" (more layout-oriented, default: "semantic")
- `maxDim` (number, optional): Optional preprocessing max dimension forwarded to compare pipeline
- `jpegQuality` (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 similar
- `notes` (array): Human-readable notes explaining which signals were used and their individual scores
- `meta` (object): Metadata about what was compared:
  - `pageUrl` (string): URL of the page that was compared
  - `pageTitle` (string): Title of the page that was compared
  - `figmaFileKey` (string): Figma file key used for the design snapshot
  - `figmaNodeId` (string): Figma node id used for the design snapshot
  - `selector` (string | null): Selector used for page screenshot, if any. Null means full page
  - `fullPage` (boolean): Whether the page screenshot was full-page
  - `pageImageType` (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:**
1. Fetches a raster snapshot from Figma (frame/node screenshot)
2. Takes a screenshot of the live browser page (full page or a specific selector)
3. Computes multiple similarity signals and combines them into one score:
   - MSSIM (structural similarity; always available)
   - Image embedding similarity (optional; may be skipped if provider is not configured)
   - Vision→text→text embedding similarity (optional; may be skipped if provider is not configured)

**Usage:**
- Prefer 'semantic' MSSIM mode when comparing Figma sample data vs real data (less sensitive to text/value differences)
- Use 'raw' MSSIM mode only when you expect near pixel-identical output
- If you suspect layout/structure mismatch, run with fullPage=true first, then retry with a selector for the problematic region
- Notes explain which signals were used or skipped; skipped signals usually mean missing cloud configuration (e.g., AWS_REGION, inference profile, etc.)

**Use cases:**
- UI regression checks
- Design parity validation
- "Does this page still match the intended layout?" validation
- Automated visual testing
</details>

## Architecture

### Platform Architecture

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:

- **Platform-specific tools**: Specialized tools optimized for the target environment
- **Unified session management**: Consistent session handling across platforms
- **Shared infrastructure**: Common transport, configuration, and plugin systems

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.

### Session Management

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 client disconnects
- The session becomes idle (configurable timeout)
- The session is explicitly closed

### Browser Platform Details

The Browser platform supports multiple browser engines:
- **Chromium** (default)
- **Firefox**
- **WebKit**

**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:
  - Cookies and session data
  - LocalStorage and IndexedDB
  - Browser extensions and settings
  - User preferences
  
  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:
  - Testing with the exact browser version users have
  - Using browser extensions installed on the system
  - Better compatibility with certain web applications
  
  **Note:** System browser support is currently only available for Chromium/Chrome.

**React DevTools Extension Setup:**
- React tools (`react_get-component-for-element`, `react_get-element-for-component`) work best when the React DevTools extension is installed in the browser profile
- The MCP server does NOT automatically install the extension - you must install it manually
- **Installation Steps:**
  1. Enable persistent browser context: `BROWSER_PERSISTENT_ENABLE=true`
  2. Start the MCP server (or navigate to a page in headful mode)
  3. Manually install the React Developer Tools extension from Chrome Web Store:
     - https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi
  4. The extension will be available in all subsequent sessions using the same persistent context
- **Without the Extension:** Tools will still work but use best-effort DOM scanning for `__reactFiber$` pointers, which is less reliable than using the DevTools hook

Browser 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).

### Buffering & Filtering

Console messages and HTTP requests are buffered in memory with configurable buffer sizes. Both tools support advanced filtering:

- **Level-based filtering**: Filter by severity/type
- **Text search**: Search within message/request content
- **Time-based filtering**: Filter by timestamp
- **Incremental retrieval**: Use sequence numbers to fetch only new items
- **Pagination**: Limit results with start/end trimming

### OpenTelemetry Integration

When enabled (`OTEL_ENABLE=true`), the server automatically injects OpenTelemetry instrumentation into all web pages navigated by the browser. This enables:

- **Automatic Trace Collection**: UI traces are automatically collected for:
  - Document load events
  - Fetch/XHR requests
  - User interactions (clicks, form submissions, etc.)
  
- **Trace Context Propagation**: Trace IDs are automatically propagated in HTTP headers (traceparent) for all API calls, enabling:
  - Correlation between frontend and backend traces
  - End-to-end distributed tracing across the entire application stack
  
- **Trace ID Management**: Tools allow you to:
  - Get the current session's trace ID
  - Generate new trace IDs
  - Set custom trace IDs (e.g., from backend trace context)
  
- **Exporter Configuration**: Traces can be exported to:
  - **OTLP/HTTP**: Send to OpenTelemetry collector (configure via `OTEL_EXPORTER_HTTP_URL`)
  - **Console**: Log traces to browser console (for debugging)
  - **None**: Collect traces but don't export (for testing)

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.

## Development

### Prerequisites

- Node.js 18+
- npm or yarn

### Setup

```bash
# 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
```

### Scripts

- `npm run build` - Build TypeScript to JavaScript
- `npm run start` - Start server with stdio transport
- `npm run start:http` - Start server with HTTP transport
- `npm run watch` - Watch mode for development
- `npm run inspector` - Run MCP Inspector (stdio)
- `npm run inspector:http` - Run MCP Inspector (HTTP)
- `npm run lint:check` - Check code formatting
- `npm run lint:format` - Format code
- `npm run tools:token-report` - Generate tool definition token consumption report (see below)

#### Tool definition token report

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.

```bash
# 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:

```bash
# 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](docs/TOOL-DEFINITION-TOKENS.md).

## Use Cases

### Node Platform Use Cases

The Node platform enables AI assistants to:

1. **Debug Node.js APIs**: Connect to a running server, set tracepoints at API handlers, capture request/response context
2. **Inspect Backend State**: Use watch expressions and tracepoints to understand variable values, call stacks
3. **Catch Exceptions**: Enable exception breakpoints to capture uncaught errors with full stack traces
4. **Docker Debugging**: Connect to Node.js processes running inside Docker containers. When the MCP runs in a container, set `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 })`).

### Browser Platform Use Cases

The Browser platform enables AI assistants to:

1. **Debug Web Applications**: Capture screenshots, inspect DOM, check console errors
2. **Monitor Network Activity**: Track API calls, analyze request/response patterns
3. **Distributed Tracing**: Enable OpenTelemetry to correlate frontend and backend traces for end-to-end debugging
4. **Test User Flows**: Automate navigation and interactions
5. **Visual Verification**: Compare visual states, verify UI changes
6. **Design Comparison**: Compare live page UI against Figma designs with automated similarity scoring
7. **Content Extraction**: Get HTML/text content with filtering and cleaning options
8. **Accessibility Analysis**: Use ARIA and AX tree snapshots to understand page structure and detect UI issues
9. **Performance Analysis**: Monitor HTTP request timing and failures

### Example Workflow

1. Navigate to a web page using `navigation_go-to`
2. Wait for network idle with `sync_wait-for-network-idle` if needed (for SPA pages)
3. Take a screenshot with `content_take-screenshot` to see the current state
4. Check console messages with `o11y_get-console-messages` for errors
5. Monitor HTTP requests with `o11y_get-http-requests` to see API calls
6. Capture accessibility snapshots with `a11y_take-aria-snapshot` and `a11y_take-ax-tree-snapshot` to understand page structure
7. Compare page with Figma design using `figma_compare-page-with-design` to validate design parity
8. Interact with elements using `interaction_click`, `interaction_fill`, etc.
9. Extract content using `content_get-as-html` or `content_get-as-text`
10. Save the page as PDF using `content_save-as-pdf` for documentation

## Plugins

Browser DevTools MCP is available as a plugin for various AI coding assistants.

### Claude Code

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](https://github.com/serkan-ozal/browser-devtools-claude).

#### Installation

```bash
# Add the marketplace
/plugin marketplace add https://github.com/serkan-ozal/browser-devtools-claude

# Install the plugin
/plugin install browser-devtools-mcp@browser-devtools
```

#### Features

**Slash Commands (29 commands):**
- Navigation: `/browse`, `/back`, `/forward`, `/reload`
- Content: `/screenshot`, `/html`, `/text`, `/pdf`
- Interaction: `/click`, `/fill`, `/hover`, `/keypress`, `/select`, `/drag`, `/scroll`, `/resize`
- Debugging: `/console`, `/network`, `/webvitals`, `/react`
- Observability: `/trace`, `/otel`
- Testing: `/mock`, `/intercept`, `/wait`
- Accessibility: `/accessibility`
- Design: `/figma`
- Execution: `/run-js`, `/sandbox`

**Skills (6 skills):**
- `browser-testing` - General browser test capabilities
- `web-debugging` - Console, network, JS debugging
- `node-debugging` - Node.js backend debugging (tracepoints, logpoints)
- `performance-audit` - Web Vitals and performance analysis
- `visual-testing` - Visual testing and responsive design
- `observability` - Distributed tracing and monitoring

**Agents (5 agents):**
- `qa-tester` - Automated QA testing agent
- `accessibility-auditor` - WCAG compliance auditor
- `performance-analyzer` - Performance analysis agent
- `scraper` - Web scraping agent
- `design-qa` - Figma design QA agent

#### Configuration

The plugin can be configured via environment variables. See [CONFIG.md](https://github.com/serkan-ozal/browser-devtools-claude/blob/master/CONFIG.md) for all available options.

Example configuration in `~/.claude/settings.json`:

```json
{
  "mcpServers": {
    "browser-devtools": {
      "command": "npx",
      "args": ["-y", "browser-devtools-mcp@latest"],
      "env": {
        "PLATFORM": "browser",
        "BROWSER_HEADLESS_ENABLE": "false"
      }
    }
  }
}
```

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## License

Elastic License 2.0 (ELv2) - see [LICENSE](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.

## Author

**Serkan Ozal**

- Email: serkanozal86@gmail.com
- GitHub: [@serkan-ozal](https://github.com/serkan-ozal)

## Acknowledgments

- Built with [Playwright](https://playwright.dev) for browser automation
- Uses [Model Context Protocol SDK](https://github.com/modelcontextprotocol/sdk) for MCP implementation
- HTTP transport powered by [Hono](https://hono.dev)
