BrowserTools MCP
If you are still on 1.x, upgrade. 1.x is unsupported. It has known critical
vulnerabilities (remote code execution, unauthenticated access to captured
data) that were fixed in 2.0 and will not be patched on 1.x. See
SECURITY.md and MIGRATION.md.
Give your AI coding agent eyes on the browser. BrowserTools MCP streams console
output, network activity, screenshots and Lighthouse audits from your real
Chrome session β the one already logged into your app β to any MCP-compatible
client: Cursor, Claude Code, Windsurf, Cline, Zed, Gemini CLI, and others.
Version 2.0 is a rewrite. One process instead of three, no unauthenticated
local server, credentials scrubbed before they leave the browser, and a real
test suite. Coming from 1.x? Read MIGRATION.md.
Why this instead of a CDP-based server
Tools like Chrome DevTools MCP and Playwright MCP drive a fresh, automated
browser. That is the right choice for writing tests. It is the wrong choice for
debugging the app you are actually looking at, because since Chrome 136 the
browser refuses remote debugging on your default profile β the one holding your
logins. So you end up recreating your auth state in a throwaway profile before
you can debug anything.
BrowserTools attaches to the session you are already in, through a DevTools
extension. You stay logged in, on the page you were already on, and your agent
reads what you see. It also reports Lighthouse-grade performance, accessibility
and SEO data, which the automation-first servers do not.
Install
Two pieces: an MCP server (one command) and a Chrome extension.
1. Point your MCP client at the server
{
"mcpServers": {
"browser-tools": {
"command": "npx",
"args": ["-y", "@agentdeskai/browser-tools-mcp@latest"]
}
}
}
On Windows, if your client cannot find npx, use "command": "cmd" with
"args": ["/c", "npx", "-y", "@agentdeskai/browser-tools-mcp@latest"].
Requires Node 22.19 or newer. Check with node --version; if you use nvm or
asdf, make sure your editor inherits the same version.
2. Load the Chrome extension
- Download or clone this repository.
- Open
chrome://extensions and turn on Developer mode.
- Choose Load unpacked and select the
chrome-extension directory.
That is the whole setup. There is no second server to start β the MCP server
runs the connector itself.
3. Use it
Open Chrome DevTools (F12) on the page you want to inspect. Capture begins as
soon as DevTools is open; the BrowserTools panel is only for settings and
status. Then ask your agent something like "check the console for errors" or
"run an accessibility audit on this page".
Not working? Run npx @agentdeskai/browser-tools-mcp --doctor, which reports
exactly which piece is missing.
To watch capture happen live β useful when checking a fresh install β start the
connector with --verbose:
npx @agentdeskai/browser-tools-server --verbose
Β· console error tab 42 Uncaught TypeError: total is not a function
Β· network 500 POST tab 42 https://myapp.local/api/pay (1310ms)
Without it the connector only reports connect and disconnect, so a working
setup and a silent one look the same.
Tools
| Tool | What it does |
|---|
getConsoleLogs | Console output, filterable by keyword with paging |
getConsoleErrors | Error-level output and uncaught exceptions |
getNetworkLogs | XHR/fetch requests with status, timing and bodies |
getNetworkErrors | Only failed and 4xx/5xx requests |
getSelectedElement | The element selected in the Elements panel |
getPageInfo | Which page the browser is currently on |
getConnectionStatus | Whether the extension is connected, and capture counts |
listBrowserTabs | Tabs with DevTools open, addressed by a DevTools-connected tabId |
listTabs | Tabs chrome.tabs returns (not every open tab), addressed by chromeTabId |
readTab | Headings, excerpt, and a snapshot of a chromeTabId, without the debugger |
navigateBrowser | Load a URL in a DevTools-connected tab, without focusing it |
navigateTab | Load a URL in the background from listTabs, visible window only |
softClick / softType | Untrusted click or type. Result is worked or needs a real gesture |
takeScreenshot | Screenshot returned as an image, plus its file path |
refreshBrowser | Reloads the inspected tab |
getBrowserStorage | localStorage, sessionStorage and cookies (values gated) |
wipeLogs | Clears captured telemetry before a clean reproduction |
runAccessibilityAudit | Lighthouse accessibility audit |
runPerformanceAudit | Lighthouse performance audit with Core Web Vitals |
runSEOAudit | Lighthouse SEO audit |
runBestPracticesAudit | Lighthouse best-practices audit |
Three prompts ship alongside them β debuggerMode, auditMode and
nextjsSeoAudit β giving your agent a systematic workflow instead of a wall of
static text in every tool listing.
All tools declare MCP output schemas, so clients receive structured data rather
than prose they have to parse, and read-only tools are annotated as such so
clients can auto-approve them safely.
Tabs without DevTools
listTabs, readTab, navigateTab, softClick, and softType use chrome.tabs and chrome.scripting. They do not attach the debugger, so they do not show the debugging banner, and they do not activate a tab or focus a window.
chromeTabId comes from listTabs. tabId on the other tools comes from listBrowserTabs. They are different id spaces. Pass each id only to the tool that issued it.
listTabs reports the tabs chrome.tabs returns in the windows it returns. Other Arc Spaces, Little Arc, pinned favorites, split-view partners, and slept tabs may be missing. Incognito is skipped. This is not every open tab. Background navigation stays in the visible window until that is measured on Arc.
The popup switch has to be on. Navigation and soft input also need --allow-input on browser-tools-mcp, or --allow-actions on the browser-tools CLI. The same origin rules as other input apply. An asleep tab is reported and not reloaded. A soft action says worked or needs a real gesture and does not claim a failed action worked.
Several tabs at once
Every tab with DevTools open is tracked separately. Telemetry is attributed to
the tab that produced it, and retention is per tab, so a chatty page cannot push
out the history of the one you care about.
Tools act on the current tab β the one you most recently opened DevTools on.
A tab whose connection drops and comes back does not steal that position, which
is what used to make screenshots capture the wrong page. Every result reports
the tabId and url it came from, plus otherTabs, so a wrong-tab answer is
visible rather than silent. To target a specific tab, call listBrowserTabs and
pass its tabId to any tool; pass allTabs: true to read across every tab.
Large data stays out of the context window
Whole-history payloads are exposed as MCP resources rather than inlined, and
tools link to them with resource_link so your agent fetches them only when it
decides to:
| Resource | What it is |
|---|
browser-tools://console/{tabId|all} | Every console entry, with no per-call budget |
browser-tools://network/{tabId|all} | Every captured request, including bodies |
browser-tools://har/{tabId|all} | The same traffic as a HAR 1.2 file |
browser-tools://screenshot/{name} | A screenshot you captured earlier |
browser-tools://audit/{reportId} | The unabridged Lighthouse result behind a summary |
Log tools attach a link when a read had to be cut short; network reads always
offer the HAR; screenshots always link to the stored image, which is the only
way to see one too large to inline. The last 20 full Lighthouse reports are kept
under audits/ in the screenshot directory.
Keeping responses small
Log payloads are the usual cause of a blown context window. Every read tool
takes limit and offset, and the log tools take keyword filters:
getConsoleErrors({ keywords: ["hydration"], limit: 20 })
getNetworkLogs({ urlKeywords: ["/api/"], bodyKeywords: ["quota"], limit: 10 })
Results are returned newest-first and always report total alongside
returned, so an agent knows when it is only seeing part of the picture.
Privacy and security
This tool captures whatever your browser sees, so it treats that data carefully: