Atlas / MCP servers / ahmadhass0un / brave-browser-mcp

brave-browser-mcpBLOCK

mcp/ahmadhass0un/brave-browser-mcp

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

Verdict
BLOCK
Grade
F
Trust score
54 /100
Exposed tools
43 35r · 7w · 1d
Transport
stdio
License
NOASSERTION
Stars
1
01

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 + debugger Input)
  • 🗂 Adaptive DOM tools — list_elements, inspect_dom, focus_element, press_key discover 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) to data/screenshots/
  • 🔧 Arbitrary JS — execute_js (wrapped async () => (code), requires confirm=true)
  • 🏥 Health check — server + extension transport state (waiting/connected), window/tab counts
  • 🧩 UI — toolbar popup.html (380px) + full dashboard.html (overview/browser/tool
Read from source at commit 19aecbe4db13OBSERVED · 2026-10-11
Advertisement
02

Connect

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-code
claude mcp add browser-navigator-mcp --env BROWSER_NAV_TOKEN=${BROWSER_NAV_TOKEN} --env COOKIE_ENCRYPTION_KEY=${COOKIE_ENCRYPTION_KEY} -- npx -y [email protected]
claude-desktop
{
  "mcpServers": {
    "browser-navigator-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "[email protected]"
      ],
      "env": {
        "BROWSER_NAV_TOKEN": "${BROWSER_NAV_TOKEN}",
        "COOKIE_ENCRYPTION_KEY": "${COOKIE_ENCRYPTION_KEY}"
      }
    }
  }
}
03

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.

ToolRiskDescription
bookmark_addwriteSave 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_deletedestructiveRemove bookmark by id or exact url. Args: id optional, url optional (one required). Returns deleted bool, id/url. No-op if not found.
bookmark_listreadList all saved bookmarks sorted by createdAt desc. No args. Returns array or \
bookmark_searchreadSearch 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 \
clickread
computerread
connect_braveread
cookiesread
detect_captchareadDetect 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.
disconnectreadDisconnect 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_jswrite
focus_elementreadFocus 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_contentreadExtract 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_inforeadQuick 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_dialogreadHandle 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_downloadreadPoll 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.
healthreadServer 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_searchreadSearch 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 \
hoverreadHover 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_scriptread
inspect_domread
list_elementsread
navigateread
navigate_historyreadNavigate 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_listreadPeek 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_requestread
network_startwriteStart 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_stopwriteStop 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_exportwriteSave 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_keywriteSend keyboard shortcut/keys to the focused element (or selector). Args: key (e.g.
read_pageread
screenshotreadScreenshot 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.
scrollreadScroll 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.
searchread
search_tabsreadSemantic 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_injectedwrite
tabsread
typeread
video_controlread
wait_forread
wait_for_captchareadBlock 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_loadreadWait 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.
windowsreadManage 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.
04

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.

LayerWhat it checksResult
L0Provenance & inventoryPASS
L1Static analysis of the codeFAIL
L2Instruction surface (what it tells the agent)PASS
L3Class-specific surfaceWARN
L4Behavioural (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)

HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
extension/background.js:1772
try { fn = new Function('return (' + src + ')')(); } catch { fn = undefined; }
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
extension/background.js:1774
try { fn = new Function('return (function (...a) { ' + src + ' })')(); }
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
extension/content.js:974
fn = new Function(src);
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
extension/dashboard.js:226
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]});
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
extension/injected.js:197
return new Function('"use strict";\nreturn (\n' + code + "\n);");
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHNetwork egress · net.metadata_ip · CWE-200, CWE-319
extension/background.js:232
"127.0.0.1", "0.0.0.0", "::", "::1", "[::1]", "169.254.169.254", "100.100.100.200",
Why it matters. cloud metadata endpoint: the classic SSRF credential grab
HIGHNetwork egress · net.metadata_ip · CWE-200, CWE-319
extension/background.js:233
"metadata", "metadata.google.internal", "instance-data", "instance-data.ec2.internal",
Why it matters. cloud metadata endpoint: the classic SSRF credential grab
HIGHNetwork egress · net.metadata_ip · CWE-200, CWE-319
lib/security.js:27
"169.254.169.254", "100.100.100.200",
Why it matters. cloud metadata endpoint: the classic SSRF credential grab
HIGHNetwork egress · net.metadata_ip · CWE-200, CWE-319
lib/security.js:28
"metadata", "metadata.google.internal", "instance-data", "instance-data.ec2.internal",
Why it matters. cloud metadata endpoint: the classic SSRF credential grab
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
ws-server.js:65
log(`listening on ws://127.0.0.1:${port} (health: http://127.0.0.1:${port}/health)`);
MEDIUMFilesystem / path · mcp.destructive_tools · CWE-22, CWE-59
bookmark_delete
Why it matters. 1 tool(s) can delete or overwrite
Fix. prefer a read-only mode or scoped tokens; the page states the blast radius
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
README.md:43
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`.
LOWObfuscation / stealth · obf.decode_call · CWE-506, CWE-94
extension/background.js:1084
try { text = atob(text); }
LOWObfuscation / stealth · obf.decode_call · CWE-506, CWE-94
extension/dashboard.js:300
const bytes = b64 ? atob(b64).length : 0;
LOWObfuscation / stealth · obf.decode_call · CWE-506, CWE-94
extension/dashboard.js:391
const b64=result.data||''; const bytes=b64?atob(b64).length:0;
LOWSupply chain · supply.unpinned · CWE-829, CWE-1357
package.json
@modelcontextprotocol/sdk, ws, zod
Why it matters. 3 dependency range(s) float
Fix. pin exact versions or ship a lockfile

Gates applied: no_behavioural_pass.

Audited 2026-10-11 · audit v0.4.1 · source sha 19aecbe4db13full audit observations/trust-audit/mcp-server/ahmadhass0un__brave-browser-mcp.json · Report an issue / request a re-scan
05

Audit history

Every audit this server has had. A grade with a past is a grade somebody is still checking.

DateSourceVerdictGradeScoreChange
2026-10-1119aecbe4db13BLOCKF54first audit
06

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.

Advertisement