brave-browser-mcpBLOCK
MCP server that gives AI assistants full control of your real Brave/Chrome profile via a lightweight MV3 extension — tabs, clicks, typing, screenshots, PDF, cookies, CAPTCHA wait, network capture, and 9-platform search
Overview
From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.
A Model Context Protocol server that gives AI assistants full control over Brave / Chrome via a Manifest V3 extension (no external browser download).
The extension (extension/background.js service worker + content.js DOM ops) bridges to the local MCP server over WebSocket `ws://127.0.0.1:9224`. The server speaks MCP over stdio and drives the real user profile — tabs, windows, clicks, typing, screenshots, PDFs, cookies, video — through chrome.tabs, chrome.windows, chrome.scripting and chrome.debugger.
Built so that an LLM can operate complex, dynamic UIs (dialogs, modals, dropdowns, token chips) without reverse-engineering the DOM: list_elements + inspect_dom + focus_element + press_key + scope.
Highlights
- 🧭 Full browser control — navigate, back/forward, click, type, scroll, hover, keyboard input (extension
cs.eval+debuggerInput) - 🗂 Adaptive DOM tools —
list_elements,inspect_dom,focus_element,press_keydiscover how a UI is built and interact with it - 🪟 Window & tab management — list, open, switch, and close windows and tabs (
chrome.tabs/windows) - 🔐 CAPTCHA detection — detects reCAPTCHA, hCaptcha, Cloudflare Turnstile & challenges, pauses automation, and waits for a human
- 🍪 Session persistence — save/load cookies via
chrome.cookies(encrypted),data/JSON stores - 🎥 Video control — play/pause/seek/volume/mute on any HTML5 player
- 🔍 Social search —
search+search_tabs(TF‐IDF) across 9 platforms - 📄 Export — screenshots (
Page.captureScreenshot+captureVisibleTab) and PDF (Page.printToPDF) todata/screenshots/ - 🔧 Arbitrary JS —
execute_js(wrappedasync () => (code), requiresconfirm=true) - 🏥 Health check — server + extension transport state (
waiting/connected), window/tab counts - 🧩 UI — toolbar
popup.html(380px) + fulldashboard.html(overview/browser/tool
19aecbe4db13OBSERVED · 2026-10-11Connect
Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control. Replace the environment placeholders with a token scoped to the least it needs.
claude mcp add browser-navigator-mcp --env BROWSER_NAV_TOKEN=${BROWSER_NAV_TOKEN} --env COOKIE_ENCRYPTION_KEY=${COOKIE_ENCRYPTION_KEY} -- npx -y [email protected]{
"mcpServers": {
"browser-navigator-mcp": {
"command": "npx",
"args": [
"-y",
"[email protected]"
],
"env": {
"BROWSER_NAV_TOKEN": "${BROWSER_NAV_TOKEN}",
"COOKIE_ENCRYPTION_KEY": "${COOKIE_ENCRYPTION_KEY}"
}
}
}
}Exposed tools (43)
35 read · 7 write · 1 destructive. Blast radius: 1 tool can delete or overwrite — an agent that can be talked into calling a tool can be talked into calling this one.
| Tool | Risk | Description |
|---|---|---|
bookmark_add | write | Save bookmark to data/bookmarks.json. Args: url (required), title (required), tags string[] (lowercased deduped). Upserts by exact url. Returns created/updated, id, url, tags. Use to remember important pages for later bookmark_search. |
bookmark_delete | destructive | Remove bookmark by id or exact url. Args: id optional, url optional (one required). Returns deleted bool, id/url. No-op if not found. |
bookmark_list | read | List all saved bookmarks sorted by createdAt desc. No args. Returns array or \ |
bookmark_search | read | Search saved bookmarks by keyword (title/url/tags substring) and/or tags (must include). Args: query string, tags string[], limit 1-200. Sorted by createdAt desc. Returns matches or \ |
click | read | |
computer | read | |
connect_brave | read | |
cookies | read | |
detect_captcha | read | Detect CAPTCHA presence via selector checks (no heavy outerHTML). Returns detected bool, kind (recaptcha|hcaptcha|turnstile|geetest|funcaptcha|unknown), signals, frameCount. Quick CSP-friendly version of content.js detect. Check before automated form submit. |
disconnect | read | Disconnect the MCP transport (extension stays connected to browser). Use to reset pending calls, clear refMap, and force next call to re-handshake. No args. Returns ok. Browser tabs remain open. |
execute_js | write | |
focus_element | read | Focus an element so subsequent press_key goes to the right target. Args: selector or by_text + scope. Uses el.focus() with click fallback. Returns focused bool, tag, activeElement check. Call before press_key on custom widgets/editors. |
get_page_content | read | Extract page content as clean text or raw HTML. Args: format (text|html, default text), limit 100-200000 chars, selector optional (null = whole page, prefers <main>). Text uses TreeWalker+visibility filter (excludes nav/aside). HTML is outerHTML truncated. Returns url/title/content/truncated flag. |
get_page_info | read | Quick page snapshot: URL, title, readyState, viewport, scroll, referrer, interactiveCount, and captcha detection (recaptcha/hcaptcha/turnstile). Tries content.js first (CSP-safe), falls back to eval. Optional tab_id. Use to verify navigation succeeded or check for bot challenge before acting. |
handle_dialog | read | Handle JavaScript dialog (alert/confirm/prompt). Args: action (accept|dismiss), promptText optional (for prompt), tab_id optional (active tab). Uses CDP Page.enable + handleJavaScriptDialog with debugger ref. Returns handled, action. Throws if no dialog is open. |
handle_download | read | Poll chrome.downloads until file completes. Args: filenameContains substring (matches basename or url), timeout_ms 1s-300s. Polls 500ms, checks state complete vs interrupted. Returns found, id, filename, url, fileSize. Requires downloads permission. |
health | read | Server health probe: no browser needed. Returns server v2.0.18, connected bool, transport (websocket|null), wsPort, uptimeSec, browser {windows,tabs,activeTabId,extVersion}, currentTabId, liveRefs (refMap size), bookmarks/history counts. Call anytime to check readiness. |
history_search | read | Search navigation history (data/history.json, capped 5000). Args: query string, hours 1-168 (default 24h cutoff), limit 1-500. Filters by title/url then slices most recent. Returns matches or \ |
hover | read | Hover to reveal tooltips/menus. Args: selector/by_text/scope/ref or x/y coords (dispatches mouseMoved at coords). Tries content.js scrollIntoView + pointer/mouse sequence, falls back to eval pageHover. Returns hovered bool, x/y. Call before clicking hover-only controls. |
inject_script | read | |
inspect_dom | read | |
list_elements | read | |
navigate | read | |
navigate_history | read | Navigate session history like browser Back/Forward. Requires active tab. Args: direction (back|forward, default back), steps 1-50. Uses history.go() then 400ms settle. Returns new url/title. Saved to history. |
network_list | read | Peek snapshot of ongoing capture without stopping (last 20). Returns capturing, count, requests tail. Use for live debugging while page loads. Empty if no capture started. |
network_request | read | |
network_start | write | Start CDP network capture (Network+Fetch). Args: max_time 1s-600s, include_static bool (default false filters css/js/img). Holds debugger ref for window. Returns capturing, tabId, startedAt. Call network_list/peek or network_stop to get results. Supersedes previous capture. |
network_stop | write | Stop network capture, disable Fetch/Network, release debugger, sort by ts. Returns capturing:false, count, requests[{requestId,url,method,status,mimeType,resourceType,fromCache,state,bodyPreview,truncated}]. Compare before/after to find API calls. |
pdf_export | write | Save page as PDF via CDP Page.printToPDF (A4, printBackground:true, preferCSSPageSize). Args: save_path optional. Writes 0o600. Returns savedTo, bytes. Use for archiving or offline analysis. |
press_key | write | Send keyboard shortcut/keys to the focused element (or selector). Args: key (e.g. |
read_page | read | |
screenshot | read | Screenshot to PNG via CDP Page.captureScreenshot. Args: full_page bool, selector optional (clip to element rect, viewport-relative), save_path optional (inside data/screenshots, traversal-blocked). Writes 0o600. Returns savedTo path, bytes, clippedTo. If selector has zero size, throws. |
scroll | read | Scroll page or scrollable element. Args: direction (up|down|left|right, default down), amount 1-100000px (default 800), selector optional (element selector to scroll inside). Returns before/after scrollX/Y. Call repeatedly for infinite scroll / lazy-load pages. |
search | read | |
search_tabs | read | Semantic search across open tabs by title+URL (TF-IDF + substring boost). Args: query string (tokenized, stopwords removed), limit 1-100. Caches idf 5s. Returns ranked [{id,windowId,active,title,url,score}] or \ |
send_to_injected | write | |
tabs | read | |
type | read | |
video_control | read | |
wait_for | read | |
wait_for_captcha | read | Block until human solves CAPTCHA: delegates to content.js waitForCaptchaSolved (visibility checks + mcp:captcha-solved event) in 8s slices. Args: timeout_ms 1s-180s. Returns solved bool, kind, elapsedMs, or CAPTCHA_WAIT_TIMEOUT. Requires user interaction — notify user to solve. |
wait_for_load | read | Wait for tab load state via background waitTabSettled (tabs.onUpdated + readyState). Args: until (commit|domcontentloaded|load|networkidle, default load), timeout_ms 1s-300s. Returns tabId/url/status/title. Use after navigate or for SPA transitions. |
windows | read | Manage windows: list (populated tabs), focus (window_id), close (window_id). Returns windows[{id,focused,type,state,tabs}]. Use to switch focus before tab ops or to detect popups. |
Trust audit
BLOCKgrade F · trust 54/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | PASS |
| L1 | Static analysis of the code | FAIL |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | WARN |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- declared (1 observation(s))
- Network
- declared (15 observation(s))
- Shell
- declared (5 observation(s))
- Dependencies
- not all pinned
- Secrets in source
- none-found
Findings (16)
try { fn = new Function('return (' + src + ')')(); } catch { fn = undefined; }try { fn = new Function('return (function (...a) { ' + src + ' })')(); }fn = new Function(src);
const r=await chrome.scripting.executeScript({target:{tabId:t.id}, world:'MAIN', func:(src)=>{ try{ return new Function('return ('+src+')')() }catch(e){ return 'Error: '+e.message } }, args:[code]});return new Function('"use strict";\nreturn (\n' + code + "\n);");"127.0.0.1", "0.0.0.0", "::", "::1", "[::1]", "169.254.169.254", "100.100.100.200",
"metadata", "metadata.google.internal", "instance-data", "instance-data.ec2.internal",
"169.254.169.254", "100.100.100.200",
"metadata", "metadata.google.internal", "instance-data", "instance-data.ec2.internal",
log(`listening on ws://127.0.0.1:${port} (health: http://127.0.0.1:${port}/health)`);bookmark_delete
The extension probes `http://127.0.0.1:9224/health` every 2s and shows `Waiting for MCP server...` (yellow) until the server is up — the console stays silent in the meantime — then `Connected`.
try { text = atob(text); }const bytes = b64 ? atob(b64).length : 0;
const b64=result.data||''; const bytes=b64?atob(b64).length:0;
@modelcontextprotocol/sdk, ws, zod
Gates applied: no_behavioural_pass.
19aecbe4db13full audit observations/trust-audit/mcp-server/ahmadhass0un__brave-browser-mcp.json · Report an issue / request a re-scanAudit history
Every audit this server has had. A grade with a past is a grade somebody is still checking.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-11 | 19aecbe4db13 | BLOCK | F | 54 | first audit |
Questions
What is the brave-browser-mcp MCP server?
MCP server that gives AI assistants full control of your real Brave/Chrome profile via a lightweight MV3 extension — tabs, clicks, typing, screenshots, PDF, cookies, CAPTCHA wait, network capture, and 9-platform search
What tools does brave-browser-mcp expose?
43 in total: 35 read-only, 7 that write, and 1 that can delete or overwrite (bookmark_delete). Every one is listed on this page with its risk.
Is brave-browser-mcp safe to connect to an agent?
No — not without reading the findings first. The audit graded it F (54/100) and found 9 critical or high issues in the source. Each one is listed on this page with the file and line it is on. Separately from the audit: 1 of its tools can destroy data, so scope the token you give it to what you actually need.
What credentials does brave-browser-mcp need?
It reads BROWSER_NAV_TOKEN and COOKIE_ENCRYPTION_KEY from the environment. Give it a token scoped to the least it needs — an agent that can be talked into calling a tool can be talked into calling it with your credentials.
How does brave-browser-mcp run?
It speaks stdio, so it runs as a local process your client starts. It is published on npm as browser-navigator-mcp at 2.0.17.
How current is this page?
The grade is for one exact copy of the source (19aecbe4db13), read on 2026-10-11. The repository is watched and re-audited when it changes.