Skip to main content
Glama

BrowserAttach

M8ven Score BrowserAttach MCP server – quality and maintenance score on Glama BrowserAttach on Agensi BrowserAttach MCP on mcprush

npm version license

Drive the Chrome / Edge tabs you already have open and already signed in from an MCP client. BrowserAttach connects to the browser through a locally paired extension, keeps your existing sign-in state, and needs neither a remote debugging port nor a separate automation browser.

npm · Usage guide · Architecture · Agent integration · Test report · Issues · Contributing

What it does

  • Discover multiple browser profiles, pick one precisely, claim a tab exclusively, and release it when done.

  • Control existing ordinary file:// tabs after enabling BrowserAttach's Allow access to file URLs setting in the browser's extension details; use the same discovery, claim, observation and input tools.

  • Read the accessibility tree, query names/values/nearby field context, inspect field values and supported editor models; click, fill and type through refs or strict CSS/XPath, HTML label, placeholder and AX role/name locators. Type can also target the currently focused field explicitly.

  • Native pointer hover, stepped drags and conditional waits; select HTML options without opening a native popup.

  • Navigate and go back/forward, open or close tabs, handle JavaScript dialogs, upload local files, and capture the whole page or a single element.

  • Evaluate JavaScript expressions in the main page; read console, page-error, network and navigation logs captured after claiming, with bounded available stacks and request initiators; opt in to bounded, redacted request/response details when XHR/fetch needs verification.

  • Read one HTTP(S) URL in a temporary background tab with the profile's sign-in, returning rendered text, current DOM, or complete JSON with the main-document HTTP status when observed.

  • Standard stdio MCP with 24 tools. The Hub listens on loopback only and pairs with the extension through a random key.

Related MCP server: byob

Requirements

  • Node.js 22 or newer, plus npm.

  • Chrome / Edge 125 or newer with permission to load unpacked extensions. Latest stable is recommended.

  • An MCP client that speaks stdio MCP and can find Node/npm on PATH.

Quick start

1. Install and pair

npm install --global @evan_zhong/browser-attach
browser-attach setup

setup prints a stable extension directory and starts the local Hub. The directory is usually:

  • Windows: %USERPROFILE%\.browser-attach\extension

  • macOS / Linux: ~/.local/share/browser-attach/extension

Open chrome://extensions or edge://extensions, enable developer mode, choose "Load unpacked" and load that directory. Load it once for every browser profile you want to expose; the extension options page lets you set a recognizable profile name.

Or without a global install:

npx --yes --package=@evan_zhong/browser-attach@latest browser-attach setup

2. Configure the MCP client

Merge the following into your client's MCP configuration, or see mcp-config.json:

{
  "mcpServers": {
    "browser_attach": {
      "command": "npx",
      "args": ["--yes", "--package=@evan_zhong/browser-attach@latest", "browser-attach", "mcp"]
    }
  }
}

On Windows clients that require a full command extension, use npx.cmd. With a global install you can also configure "command": "browser-attach", "args": ["mcp"]. Restart the MCP client to discover the tools.

@latest selects the package at launch; upgrading an existing pair is coordinated at startup. The first extension load, and pairs still on an older protocol, need one manual extension reload — see local data and upgrades.

Host entry points are collected in agent integration, and the connection parameters, JSON/TOML examples and verification steps are in the setup reference; other clients use the stdio entry above.

The npm package includes an optional browser-attach skill with setup, recovery and cross-site recipes. Copy the whole skills/browser-attach directory to your host's skill location if desired; npm installation does not register it with the host.

3. Start driving

  1. browser_discover(kind="browsers") → browser_discover(kind="tabs") to confirm the browser, tab title and URL.

  2. browser_claim_tab with the tab_id from discover(kind="tabs") (optional expected_url / expected_title exact guards) to take control and receive this connection's tab_handle.

  3. browser_snapshot to get element refs, then click, fill or run other actions.

  4. Observe when the next decision needs new state, and finish with browser_tab_control(action="release"). Use snapshot(diff_since="latest") or action observe:{diff_since:"latest"} for page changes; inspect mode/reset_reason and finish pagination. A full-page refresh is not required after every action.

When titles change often, omit expected_title and check the identity returned by claim instead. Elements may be addressed by ref or by a selector re-resolved on every call; design trade-offs: architecture notes.

Claiming and ordinary input never activate a tab or window; call browser_tab_control(action="activate") explicitly when foreground rendering is needed. When an outcome is unknown, observe again first and avoid duplicate submissions. Full parameters, pagination, uploads and input semantics: usage guide.

Tools

Tool

Purpose

browser_discover

kind=browsers/tabs; discover browser profiles, tab identity and control status

browser_claim_tab

Exclusively claim a discovered tab

browser_open_tab / browser_tab_control

Create a tab, optionally claim it with claim:true; explicit release/activate/close

browser_fetch

Read one HTTP(S) URL in a temporary background tab using the profile's sign-in; text/html/json, optional HTTP status and cursor continuation

browser_snapshot / browser_read_text

AX snapshot, literal name/value query with actionable refs, and page text

browser_read_control

Control state, related popups and active/selected options; preserves existing refs

browser_wait

Read-only checks with uniform expected/actual, pass/fail/unavailable and safe read retry; one top-level condition or checks[]

browser_click / browser_hover / browser_drag / browser_select_option / browser_type / browser_fill / browser_press_key / browser_scroll

Native/DOM input, pointer hover and drag, option selection, character typing and scrolling

browser_batch

Run 1–30 click/fill/type/press_key/hover/scroll/select_option/wait steps in one queue; stops on the first failure and returns partial results

browser_navigate

HTTP(S)/file navigation, back/forward and load waiting; local destinations require file access

browser_handle_dialog

alert, confirm, prompt

browser_upload_file

Native file input or an intercepted file chooser

browser_screenshot

Viewport or full-page PNG with coordinate mapping

browser_evaluate

Main-page JS evaluation, promise awaiting, bounded results

browser_logs

Read kind=console/network/navigation with independent cursors; buffered reads survive blank or unresponsive pages

Local data and upgrades

Platform

Default data directory

Windows

%USERPROFILE%/.browser-attach

macOS / Linux

${XDG_DATA_HOME:-~/.local/share}/browser-attach

Override it explicitly with the absolute path BROWSER_ATTACH_HOME. The Windows default avoids AppData, which packaged hosts may virtualize; no other host's private cache is searched or merged automatically. The directory holds the pairing configuration, Hub logs and the stable extension copy, and depends on neither the clone location, the client working directory nor the npm cache. If you already use a custom directory, keep using the same environment variable or --config instead of accidentally creating a second pairing under the new default.

A host assembly step that writes the client entry pins the absolute paths of the current Node, CLI and the actual pairing file and sets BROWSER_ATTACH_HOME; it works together with a custom --config. When the MCP launches the Hub it passes the same absolute pairing path and directory, so different launch environments cannot derive a second configuration. See agent integration.

To upgrade, install again with the command from step 1 and reconnect the MCP client. With @latest and a pair on 0.17 or newer the restart is enough; a pair still on an older protocol needs browser-attach stop, a restart and one manual extension reload. Custom pairing paths, ports and extension directories are retained, closed browser profiles update when they next connect, and an older MCP package cannot downgrade a newer Hub or installed extension.

An upgrade waits for extension cleanup and confirms the new version after reload; cleanup or reload failures are reported instead of replaying page actions. Manual reload remains available on the extension options page for recovery, and Hub and extension versions must match for page control. The default port is 47831; choose another port with setup --port when creating a pairing.

Architecture and boundaries

MCP client → Node.js stdio MCP → 127.0.0.1 Hub
                                  ↕ local pairing
                          Chrome / Edge MV3 extension
                                  ↓ chrome.debugger / CDP
                               existing HTTP(S) / authorized file:// tabs

Each MCP connection holds its own control and handles cannot be used across connections. Disconnecting or stopping debugging releases control. The client endpoint requires a Bearer key and rejects web page Origins; the extension verifies a fixed Origin, key and version. browser_evaluate can read and write data with the page's existing sign-in state; a timeout or error may already have had an effect and must not be replayed automatically. No generic CDP tool is exposed.

Logs are collected from claim time, survive navigation and are cleared on release, with bounded buffers that report data_loss on eviction. Buffered reads use the existing claim without requiring a supported or responsive current page; capture changes still require page access. Network logs store lifecycle metadata and available initiators by default; an explicit capture reads bounded, redacted headers/bodies of subsequent XHR/fetch requests without changing caches or intercepting traffic. Navigation requests and commits remain separate observations. Buffer limits and parameters: usage guide.

The extension holds debugger permission and client actions can use existing site sign-in state; only connect clients you trust. Web page content must not be treated as user authorization. The pairing key and the generated config.generated.js must not be committed or published; the packaging check rejects packages that contain that file.

Chrome / Edge HTTP(S) pages and existing ordinary file:// tabs with extension file access enabled are supported, and file access is checked during discovery, claim, operations and navigation. open_tab and fetch still accept HTTP(S) only. Browser internal pages, arbitrary extension pages, download management, network interception and Firefox / WebKit remain outside scope, and built-in PDF viewer content is browser-dependent. Native pointer input requires an unsuspended tab in a non-minimized window; an unmodified left single click may target an inactive tab, while hover, drag and other click variants require an active tab. Full limits and failure modes: usage guide.

Local development

git clone https://github.com/EvannZhongg/BrowserAttach.git
cd BrowserAttach
npm ci
npm test
npm run test:package

Run the source directly and pair the extension in the repository:

node bin/browser-attach.mjs setup --extension-dir ./extension
node bin/browser-attach.mjs mcp

Development mode deliberately uses the repository extension path. For normal installs, use setup without --extension-dir so it keeps working after the repository moves.

Real-browser tests need a Chromium / Edge executable that allows loading extensions from the command line; the path is supplied by the developer:

npm run test:live -- --executable /path/to/chromium --family chromium
npm run test:interaction -- --executable /path/to/chromium --family chromium
npm run test:interaction -- --tab-indicators --executable /path/to/chromium --family chromium
npm run test:upgrade -- --executable /path/to/chromium

These tests use temporary profiles and local fixtures. Coverage and limits: test report; implementation and design trade-offs: architecture.

Contributing

Reproducible issues via Issues, or Pull Requests. Please include Node, OS, browser and extension versions, and remove pairing keys and private page data. See CONTRIBUTING.md for the process.

Released under the MIT License.

Available Tools

24 tools
browser_batchA

Run ordered steps in one tab queue under a shared deadline; per-step timeouts cap remaining time. Prevalidates all steps, including skipped ones. Stops on failure with failed_step/prior results and isError; no rollback or automatic replay.

Inputs: steps (required), tab_handle (required), observe, start_at, +2 more.

browser_claim_tabB

Exclusively claim a tab_id from this connection's latest tab discovery; optional expected_url/title must match exactly.

Inputs: tab_id (required), browser_id (required), expected_url, expected_title.

browser_clickB

Click exactly one ref/selector/x+y; elements auto-scroll and may recenter once before dispatch, with hit checks. Unmodified left single clicks may target inactive tabs. Native target_verified requires an element; coordinates return null and verify trusted delivery to the hit only. Native select requires select_option.

Inputs: tab_handle (required), x, y, nth, +16 more.

browser_discoverB

List connected profiles/capabilities or tabs with URL/title and control status. Tab discovery records this connection's latest observation for claim_tab.

Inputs: kind (required), browser_id.

browser_dragB

Drag between endpoints, each exactly one ref/selector/x+y; destination auto-scrolls while held.

Inputs: source (required), tab_handle (required), destination (required), mode, +6 more.

browser_evaluateB

Evaluate expression once; call a returned function once and await its Promise, without user activation. Use an async IIFE for statements. capture_truncated marks capture loss; required_chars/next_item signal page-budget needs. cursor reads cache without re-execution.

Inputs: tab_handle (required), frame, cursor, frame_id, +5 more.

browser_fetchA

Read a URL's main document using the profile's sign-in in a temporary background tab, then close it. Site scripts run and may affect other tabs. status/ok describe HTTP only (unknown=null); readiness/degradation are separate. body is a string. next_cursor reads immutable cache without revisiting; up to 4 captures per connection/profile.

Inputs: browser_id (required), url, cursor, format, +3 more.

browser_fillA

Replace one ref/selector input/textarea/contenteditable and verify retained text. Known Monaco/CM5 proxy textareas are rejected before focus/writes. verification_basis identifies the expectation; verification_source identifies DOM value, selected text or innerText. Native contenteditable readback temporarily selects/reads plain text, then restores selection. CR/CRLF count as LF; requested spaces may retain NBSP. No editor model write, paste, blur or dropdown commit.

Inputs: text (required), tab_handle (required), nth, ref, +10 more.

browser_handle_dialogC

Accept/dismiss a JS dialog and return its original details and accepted.

Inputs: accept (required), tab_handle (required), prompt_text.

browser_hoverA

Move to exactly one ref/selector/x+y, verify trusted mousemove, and leave the pointer there without clicking.

Inputs: tab_handle (required), x, y, nth, +11 more.

browser_logsA

Read buffered console, network or navigation events since claim, even if the page is blank or unresponsive. Capture changes require a supported page. Continue next_seq as after_seq for the same claim/kind; restart at 0 when changing filters. data_loss reports eviction.

Inputs: kind (required), tab_handle (required), level, limit, +4 more.

browser_navigateB

Navigate a claimed tab to an HTTP(S)/file URL or history entry and optionally return a fresh snapshot; also recovers a claimed tab currently at about:blank. File destinations require extension file access. Snapshot observation has a separate deadline; observation_error does not mean navigation failed.

Inputs: tab_handle (required), url, direction, timeout_ms, +3 more.

browser_open_tabB

Create a new HTTP(S) tab in the selected profile even if the URL is already open. claim=true also acquires a tab_handle without tab discovery; it does not wait for page load. A claim failure leaves the tab open and reports its identity in error.open_tab; never automatically repeat creation.

Inputs: url (required), browser_id (required), claim, active.

browser_press_keyA

Send a native key/chord to current focus and report its trusted keydown target. Optional ref/selector guards focus without focusing it; a mismatch before dispatch sends no key. Supports F1–F12, navigation/editing keys and US ASCII punctuation.

Inputs: key (required), tab_handle (required), nth, ref, +8 more.

browser_read_controlA

Read one explicit control: separate native DOM/AX values, rendered text and state; related popups, active descendant and currently exposed options in AX order. Bounded refs preserve the existing snapshot. No library-specific inference, complete virtual list, application commit or atomic DOM/AX view.

Inputs: tab_handle (required), nth, ref, name, +8 more.

browser_read_textA

Read page/frame text. Visible text excludes input/textarea values; use value for one field or snapshot profile=fields for forms. complete_source is source coverage, not pagination; next_offset continues live text, which may change.

Inputs: tab_handle (required), nth, mode, name, +6 more.

browser_screenshotA

Capture viewport, full_page or one ref/selector element as PNG with image-to-viewport CSS mapping. Element/full_page are exclusive; locator options need selector. Full-page points may need scrolling before input. If background rendering stalls, explicitly activate or use AX/text.

Inputs: tab_handle (required), nth, ref, name, +6 more.

browser_scrollA

Scroll one ref/selector container or viewport x/y (default center). DOM emits no wheel events; wheel sends native wheel input. until returns a fresh ref. Stopped searches report stop_reason, scroll_pending and confirmed progress; a boundary/miss does not prove end of data.

Inputs: tab_handle (required), x, y, nth, +14 more.

browser_select_optionA

Set an option on one ref/selector HTML select, including hidden selects, via synthetic input/change (events_trusted=false). Disabled/inert targets fail. Verifies retained selection only.

Inputs: tab_handle (required), nth, ref, name, +10 more.

browser_snapshotB

Read AX with refs, or profile=fields for DOM/AX field facts, labels, context and native values. Popup/controls/owns facts are browser-observed, not inferred ownership. New observations replace refs and retry one document/context race within 30s; next_cursor continues the captured view.

Inputs: tab_handle (required), text, query, cursor, +6 more.

browser_tab_controlA

Release control and leave the page open, activate the tab, or close it (may discard unsaved state). Release/close invalidate tab_handle.

Inputs: action (required), tab_handle (required), focus_window.

browser_typeA

Type printable characters with trusted keys without clearing. Choose exactly one ref/selector/focused=true; stop on focus loss/detachment. Enter/Tab use press_key; ordinary multiline replacement uses fill.

Inputs: text (required), tab_handle (required), nth, ref, +10 more.

browser_upload_fileA

Set browser-host absolute file paths on one file-input/label ref/selector, or omit target for the pending chooser. [] clears/cancels; multiple files need a multiple input; directories unsupported. assignment_acknowledged confirms the command, not event delivery/upload. count_verified compares retained FileList count (null if unreadable); mismatch is not assignment failure. Observe before uploading again.

Inputs: files (required), tab_handle (required), nth, ref, +8 more.

browser_waitA

Read-only wait for condition. State uses native properties/ARIA; missing state is unavailable, not false. Role absence need not mean DOM removal. Prefer a result/status container for text; compares full text, reports a bounded preview with length/truncation. Timeout reports last state; field values are omitted.

Inputs: condition (required), tab_handle (required), nth, name, +9 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.4/5.0

    Scored across 24 tools

    Disambiguation4/5

    Most tools target a clearly distinct action (click, hover, drag, scroll, press_key, screenshot, upload, dialog), and the tab-lifecycle group (discover, claim_tab, open_tab, tab_control) is well separated. The main ambiguity is in the read/input clusters: browser_type vs browser_fill, and browser_read_text vs browser_snapshot vs browser_read_control overlap in purpose, though descriptions do draw explicit boundaries (fill replaces, type appends; snapshot=AX refs, read_text=visible text, read_control=single control).

    Naming Consistency4/5

    All 24 tools use a uniform browser_ prefix with snake_case, which is highly predictable. The pattern is verb-first (browser_click, browser_fill, browser_navigate, browser_read_text) with one structural outlier, browser_tab_control, which reads as noun_verb and slightly breaks the convention.

    Tool Count4/5

    At 24 tools this sits at the top of the reasonable band, and it is on the heavy side. However, browser automation genuinely spans input primitives, reading modes, tab lifecycle, dialogs, uploads, logs, batch and evaluate, so nearly every tool covers a real capability rather than padding.

    Completeness4/5

    Coverage is broad and lifecycle-complete: discover/claim/open/navigate/tab_control for tabs, a full input set, multiple read modes, screenshots, dialogs, uploads, logs and batch orchestration. Gaps are minor — no download handling, cookie/storage or network interception tools, and no dedicated frame-switching tool (frames are only implied in read_text), which agents must work around via evaluate.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables any MCP client to drive the user's real Chrome profile through a Chrome extension bridge, with tools for navigation, accessibility snapshots, ref-based interactions, console/network reading, screenshots, tab management, and restricted script evaluation.
      1
      MIT
    • A
      license
      Not graded
      quality
      A
      maintenance
      Enables AI coding tools to drive your real, already-logged-in Chrome browser through an extension-based local MCP bridge, with no debugging port or headless setup required. It provides tools for navigating pages, reading content, clicking, typing, screenshots, tabs, cookies, network inspection, and more.
      1 npm
      ISC
    • A
      license
      Not graded
      quality
      A
      maintenance
      Enables AI coding tools to drive the user's real, already-logged-in Chrome through an MCP stdio interface, exposing 25 tools for navigating, clicking, typing, reading pages, taking screenshots, capturing network traffic, and accessing cookies and storage. The bundled Chrome extension talks to a local bridge instead of an open remote-debugging port, so logged-in sessions and bot-detection-proof human browsing work without extra setup.
      1 npm
      ISC
    • A
      license
      Not graded
      quality
      A
      maintenance
      Enables MCP-compatible AI assistants to drive the user's real, already-logged-in Chrome browser locally, so they can read and summarize pages, click, type, fill and submit forms, navigate tabs, and capture screenshots with full access to existing sessions. It also exposes cookies, storage, console, network capture, and emulation capabilities without opening a remote-debugging port or sending data off the machine.
      1 npm
      ISC