trackmcp
Back to directory
bnomei

chromewright

View on GitHub

Browser automation via Chrome DevTools Protocol with built-in AI integration through a MCP Server

1 stars RustOthers Updated Jul 19, 2026
ai-agentautomationchromedevtoolsdommcpplaywrightrusttabs

Documentation

chromewright

Crates.io Version
Build Status
License

Chromewright is a local-first browser automation MCP server built on Chrome DevTools Protocol (CDP). It exposes a real Chrome or Chromium browser to MCP clients over stdio or loopback streamable HTTP, with high-level tools for navigation, page reading, tab management, screenshots, viewport emulation, and bounded interaction. The default build also includes a semantic terminal browser and a co-hosted loopback MCP companion for its live document state.

Use Chromewright when an agent needs browser state from a real browser without embedding a Node.js automation stack or writing raw CDP calls. Chromewright is not an end-to-end test runner; it is a browser control layer for AI agents and MCP clients.

When to use Chromewright

  • Attach an MCP client to an existing Chrome or Chromium profile on a DevTools endpoint.
  • Launch a dedicated local browser session for agent work.
  • Read pages through snapshots, markdown extraction, targeted inspection, and link inventory.
  • Browse a page's semantic DOM locally in the terminal, with a co-hosted MCP companion for its active document and selection state.
  • Drive bounded interactions such as click, input, select, hover, key press, scroll, and wait.
  • Capture managed PNG screenshots without letting callers choose arbitrary output paths.
  • Reuse revision-scoped `cursor` handles from snapshots instead of relying only on CSS selectors.

Installation

Chromewright requires Rust 1.88 or newer when you install or build it with Cargo.

Install from crates.io:

bash
cargo install chromewright

Install with Homebrew:

bash
brew install bnomei/chromewright/chromewright

Install from source:

bash
git clone https://github.com/bnomei/chromewright.git
cd chromewright
cargo install --path .

You can also download prebuilt archives from GitHub Releases and place the `chromewright` binary on your `PATH`.

Verify the binary is available:

bash
chromewright --version

Expected output:

txt
chromewright

Quickstart

This path starts a visible Chrome profile with DevTools enabled, then serves Chromewright over loopback HTTP at `http://127.0.0.1:3000/mcp`.

Prerequisites

  • `chromewright` on your `PATH`
  • Chrome, Chromium, or another CDP-compatible browser
  • An MCP client that supports streamable HTTP or stdio servers

1. Start a dedicated browser profile

On macOS, run:

bash
open -na "Google Chrome" --args \
  --remote-debugging-port=9222 \
  --user-data-dir="$HOME/.chromewright-agent-profile"

Use a dedicated profile when you do not want agent automation attached to your personal browser session. The default Chromewright attach mode expects DevTools at `http://127.0.0.1:9222`.

2. Start Chromewright

Run the streamable HTTP server:

bash
chromewright serve

Expected log line:

txt
Ready to accept MCP connections at http://127.0.0.1:3000/mcp

3. Connect an MCP client

For JSON-configured clients that support streamable HTTP:

json
{
  "mcpServers": {
    "chromewright": {
      "transport": "streamable_http",
      "url": "http://127.0.0.1:3000/mcp"
    }
  }
}

For Codex over stdio, let the client start the server:

toml
[mcp_servers.chromewright]
command = "/absolute/path/to/chromewright"
enabled = true

For Codex against the long-lived HTTP server from step 2:

toml
[mcp_servers.chromewright]
url = "http://127.0.0.1:3000/mcp"
enabled = true

4. Verify with the client

Use your MCP client to call `tab_list`. A connected session should return at least one tab with a stable `tab_id`. If no active tab is useful, call `new_tab` before calling `snapshot`.

Browser modes

Chromewright has two browser modes:

ModeHow to startWhat it does
AttachRun `chromewright` or `chromewright serve` with no launch flags.Connects to `http://127.0.0.1:9222` by default.
Attach to another endpointPass `--ws-endpoint `.Connects to a browser WebSocket URL or a DevTools HTTP origin such as `http://127.0.0.1:9333`.
LaunchPass any launch flag such as `--user-data-dir`, `--headless`, `--executable-path`, or `--debug-port`.Starts a local browser session. Launch mode is headed unless you pass `--headless`.

Examples:

bash
# Default: attach to http://127.0.0.1:9222 and serve MCP over stdio.
chromewright

# Serve streamable HTTP on the default loopback endpoint.
chromewright serve

# Serve streamable HTTP on a custom port and path.
chromewright serve --port 3333 --http-path /browser

# Attach to a different DevTools endpoint.
chromewright --ws-endpoint http://127.0.0.1:9333

# Launch a visible browser with a dedicated profile.
chromewright --user-data-dir /tmp/chromewright-profile

# Launch a headless browser and serve streamable HTTP.
chromewright --headless --user-data-dir /tmp/chromewright-profile serve

# Seed two managed tabs at startup. This works with attach, launch, serve, and tui modes.
chromewright --url https://example.com --url https://example.org serve

`--url` is repeatable and seeds one separate managed tab for every value, in

the order supplied; the final seeded tab becomes active. It accepts absolute

`http:`, `https:`, and `about:` URLs. Relative, unsafe, and protocol-relative

URLs are rejected before any startup tab is opened. In attach mode, existing

tabs are untouched; only seeded tabs become managed by chromewright.

CLI reference

Option or commandDefaultDescription
`chromewright`stdio transportStarts the MCP server over stdio.
`chromewright serve``127.0.0.1:3000/mcp`Starts the MCP server over loopback streamable HTTP.
`serve --port `, `serve -p ``3000`Sets the HTTP port.
`serve --http-path ``/mcp`Sets the HTTP endpoint path.
`--ws-endpoint ``http://127.0.0.1:9222` when no launch flags are presentConnects to an existing browser WebSocket URL or DevTools HTTP origin. This conflicts with launch flags.
`--headless``false`Launches a new browser in headless mode.
`--executable-path `auto-detected by the browser backendUses a specific browser executable in launch mode.
`--user-data-dir `backend defaultUses a persistent browser profile directory in launch mode.
`--debug-port `auto-selectedUses a specific DevTools port for a locally launched browser.
`--url `none; repeatableSeeds each supplied safe URL in its own managed startup tab. The final seeded tab is active.
`chromewright tui`(feature `tui`, default-on)Starts the semantic terminal browser against the same browser session. Always co-hosts a loopback MCP companion.
`tui --config ``$XDG_CONFIG_HOME/chromewright/tui.toml` or `~/.config/chromewright/tui.toml`TOML keymap plus optional `[theme]` and `[layout]` overlays. An explicit path must exist and parse; a missing default file keeps built-in bindings.
`tui --companion-port ``0` (ephemeral)Loopback port for the co-hosted streamable-HTTP MCP companion.
`tui --companion-path ``/mcp`HTTP path for the co-hosted companion.
`--browser-session ``reuse` with `--headless tui`Reuse or replace Chromewright's owned managed headless browser. Only valid with `--headless tui`.

Source: src/bin/mcp_server.rs.

Terminal browser (TUI)

The default build includes a semantic terminal browser. It attaches to the same Chromewright browser session and renders semantic DOM content only (not pixels, CSS, or browser layout).

bash
# Attach to an existing DevTools endpoint (default http://127.0.0.1:9222).
chromewright tui

# Managed private headless Chrome for the normal terminal-browser flow.
chromewright --headless tui

To connect an MCP client to the co-hosted companion, choose a fixed loopback

port; the default `0` selects an ephemeral port that the TUI does not print.

bash
chromewright --headless tui --companion-port 3334
toml
[mcp_servers.chromewright_tui]
url = "http://127.0.0.1:3334/mcp"
enabled = true

The companion exposes eight `tui_*` coordination tools and bounded semantic

resources. `semantic_ref` values are tied to one document revision; the active

capture plus the previous eight revisions are retained, while stale or evicted

references fail closed. Attention messages are limited to 512 characters.

Semantic Markdown resources paginate at 32,000 characters by default and cap

at 200,000; JSON resources fail instead of returning a truncated document.

The header is a single browser-like bar: tab ordinal (`2/5`) left of the history arrows, then location/title, with a lifecycle glyph on the right. Keyboard bindings are not shown in the terminal chrome. Defaults are Vimari-compatible. Multi-key sequences such as `gg` and `gi` wait for the full chord; an unbound prefix is rejected rather than re-firing the last key.

Default keymap

Browser-first (Vimari) defaults, with md-tui-style aliases where they do not collide. Overlaying an action replaces all of its sequences (primary + aliases).

KeyAction nameBehavior
`f` / `s``link_hints_follow`Enter hint mode over viewport-visible links and form controls; type the two-key label. Links follow in the current tab; text inputs start edit mode; other controls are selected. One label per target (first line only if wrapped). (`s` = md-tui select-link.)
`F` / `S``link_hints_new_tab`Same targets as `f`; links open in a new tab (hint mode stays open for chaining until Esc). Form targets still select/edit in the current tab. (`S` = md-tui select-link alt.)
`j` / `↓``scroll_down`Scroll or move selection down one block.
`k` / `↑``scroll_up`Scroll or move selection up one block.
`h``scroll_left`Horizontal scroll left when content overflows. (Not md-tui half-page; browser tables/code need pan.)
`l``scroll_right`Horizontal scroll right when content overflows.
`u` / `→``half_page_up`Pan the view half a page up (selection unchanged).
`d` / `←``half_page_down`Pan the view half a page down (selection unchanged).
`Ctrl-u``page_select_up`Move selection up by about half a page.
`Ctrl-d``page_select_down`Move selection down by about half a page.
`gg``go_top`Jump to the document top. (Single `g` is not bound so `gi` stays available.)
`G``go_bottom`Jump to the document bottom.
`gi``focus_first_input`Focus the first form control and start editing.
`H` / `b``history_back`Browser history back. (`b` = md-tui back.)
`L``history_forward`Browser history forward.
`r``reload`Reload the active page.
`[``prev_tab`Switch to the previous browser tab.
`]``next_tab`Switch to the next browser tab.
`x``close_tab`Close the current tab.
`t``new_tab`Open a new tab.
`o``open_url`Open the URL entry prompt with an empty buffer. While editing, Tab / Shift-Tab accept or cycle local URL history (ghost suffix shows the best match).
`O``edit_url`Open the URL entry prompt prefilled with the current address (edit from the end). Same Tab history completion as `o`.
`/``search`Start forward search by exact semantic content. (md-tui also binds `f` to search; we keep `f` for link hints.)
`n``search_next`Repeat the last search forward.
`N``search_previous`Repeat the last search backward.
`Space``collapse`Collapse or expand the selected block.
`zw``toggle_wrap`Toggle soft word-wrap (on by default). When on, long lines wrap to the viewport and `h`/`l` horizontal pan is disabled.
`zs``toggle_structure`Toggle prose (default) vs structure projection. Prose hides landmark/list/group chrome and flattens indent; structure shows DOM-like containers. Collapse (`Space`) only works in structure mode.
`i``inspect`Open a compact inspect panel under the selected block; title is the full DOM path (`main > … > tag#id`), body has action fields and ref/rev; follows selection until Esc.
`y``copy_block`Copy selection: link/image URL (resolved), otherwise rendered block text (OSC 52).
`Y``copy_ref`Copy the opaque `semantic_ref` (OSC 52).
`Tab``tab_next`Next focusable control; writes the leaving field to the live DOM + same-doc patch, then moves focus.
`Shift-Tab``tab_prev`Previous focusable control; same write-on-leave behavior as Tab.
`Enter``confirm`Text input: apply value to the live DOM + same-doc patch (no submit needed; live search works). Checkbox/radio: toggle. Select: cycle options. Submit button: write staged fields + click + recapture. Link/other: activate.
`Esc``escape`Leave prompt, hint, or inspect mode. In Normal mode, also clears sticky `/search` footer (`/{query} n/m`). After a failed page action, dismiss Error back to Ready (retained page stays) so keys work again.
`w``toggle_full_width`Toggle full-width content vs the configured `content_max_width` column (default 100, capped by default).
`e``edit_external`Open the current page’s semantic markdown in `$VISUAL`, then `$EDITOR`, then `vi` (read-only; TUI suspends like Nereid).
`q` / `Ctrl-c``quit`Quit the TUI.

Hints use deterministic two-key labels from the alphabet `asdfgqwertzxcvb` (for example `aa`, `as`), assigned to viewport-visible links and form controls (one label per target; painted on the first line only when a target wraps).

After navigation or a link follow settles, a URL fragment such as `#section` moves the TUI selection to the matching component (`id`, then named anchor), expands collapsed ancestors, and scrolls it into view. Unmatched fragments keep the prior selection.

The content pane uses a terminal-native ANSI-16 role palette (clearer heading ladder H1–H6, blue links, light-cyan forms, yellow hints) with reverse-video selection applied last. Colors inherit the terminal light/dark theme; override individual roles under `[theme]` in `tui.toml`. By default the markdown/content area has 1 column of left/right padding and is capped at 100 columns (centered; header and footer stay full width); press `w` for full width. A one-column Amp-style block scrollbar sits on the far right of the content band. Override layout under `[layout]` in `tui.toml`.

Default reading mode is prose (markdown-like): no `▾ [main]` / `ol` / group chrome, fully flat lines. Press `zs` for structure (DOM-like outline). Wrap (`zw`) and structure (`zs`) are not shown in the header bar; toggle feedback appears in the status line.

Search follows Vim semantics: a new `/pattern` starts after the current selection and wraps at the end; `n` repeats forward, `N` repeats backward, and submitting an empty `/` prompt repeats the previous pattern. The footer shows the cmdline while typing (`/…`) and keeps `/{pattern} n/m` while a search is active — press Esc in Normal mode to clear it (Esc while typing `/…` only cancels the prompt and keeps the prior pattern for `n`/`N`). Link hints (`f` / `F`) also use the footer (`f as`) rather than the header. Bracketed paste is accepted only in URL, search, and form input modes and is bounded to 4096 characters.

Custom keymap

Bindings are replaceable by action name. `--config PATH` takes precedence; if omitted, Chromewright reads `$XDG_CONFIG_HOME/chromewright/tui.toml`, falling back to `~/.config/chromewright/tui.toml`. A missing default file keeps the built-ins; an explicitly requested file must parse successfully.

toml
# Only list keys you want to change. Unknown names or conflicting
# bindings abort startup rather than partially applying the overlay.
[keymap]
reload = "ctrl-r"
quit = "ctrl-q"
tab_prev = "shift-tab"

# Optional content-pane padding + column width (header/footer stay full width).
# Defaults: 1 col L/R, 0 row T/B, content_max_width = 100 (0 = always full).
# Press w to toggle full width vs the capped column.
[layout]
# content_padding_x = 1
# content_padding_y = 0
# content_max_width = 100

[theme]
# Optional ANSI names, reset, or #rrggbb — defaults already use a clear ladder.
# link = "blue"
# h1 = "lightblue"
# h2 = "green"
# h3 = "magenta"
# h4 = "cyan"
# h5 = "yellow"
# h6 = "lightred"
# form_control = "lightcyan"
# hint_label = "yellow"

Binding specs accept single keys (`r`, `space`, `esc`, `enter`, `tab`), multi-key letter sequences (`gg`, `gi`), and chords with `-`, `+`, or space separators (`ctrl-c`, `C-c`, `shift-tab`). Supported named keys include `esc`, `enter`, `tab`, `backtab` / `shift-tab`, `backspace`, arrow keys, `home`, `end`, `pageup` / `pgup`, `pagedown` / `pgdn`, `space`, and function keys such as `f1`.

Theme roles: `link`, `h1`–`h6`, `landmark`, `group`, `list`, `image`, `form_control`, `hint_label`, `muted`, `chrome_ready`, `chrome_loading`, `chrome_error`, `chrome_mode`, `attention_fg`, `attention_bg`.

Layout keys: `content_padding_x`, `content_padding_y` (symmetric inset around the markdown/content pane only), `content_max_width` (default 100; `0` disables the cap; `w` toggles full vs capped).

The source of truth for defaults is src/tui/keymap.rs and src/tui/action.rs.

Tool workflow

A typical agent workflow is:

1. Call `tab_list` or `new_tab` to establish an active tab.

2. Call `snapshot` to read the current page and collect actionable nodes.

3. Prefer a fresh `cursor` from `snapshot` or `inspect_node` when targeting follow-up actions.

4. Use `inspect_node`, `get_markdown`, `extract`, or `read_links` for more focused reads.

5. Use `click`, `input`, `select`, `hover`, `press_key`, `scroll`, `wait`, or tab tools for bounded interaction.

6. Call `snapshot` again after navigation, DOM-changing actions, viewport changes, or ambiguous target recovery.

`snapshot` supports these modes:

ModeUse it when
`viewport`You want the default local reread of the current visible scope.
`delta`You want the changed local surface when a compatible prior snapshot base exists.
`full`You need an exhaustive page-wide read.

DOM-targeted tools accept a public `target` object:

json
{
  "target": {
    "kind": "selector",
    "selector": "h1"
  }
}

or:

json
{
  "target": {
    "kind": "cursor",
    "cursor": ""
  }
}

Selector strings are still accepted for compatibility by tools that use the public target type, but the object form is the canonical contract.

Production MCP tool surface

Production MCP sessions register the default high-level tools plus the guarded operator tool `evaluate`.

CategoryTools
Navigation`navigate`, `go_back`, `go_forward`, `wait`
Interaction and viewport`click`, `input`, `select`, `hover`, `press_key`, `scroll`, `set_viewport`
Tabs and lifecycle`new_tab`, `tab_list`, `switch_tab`, `close_tab`, `close`
Reading and inspection`snapshot`, `inspect_node`, `get_markdown`, `extract`, `read_links`
Managed artifacts`screenshot`
Operator diagnostics`evaluate`

`evaluate` executes JavaScript in the active page and requires `confirm_unsafe = true` on each call. It is available for diagnostics and escape-hatch inspection when bounded tools cannot answer a page-specific question.

Source: src/tools/core/mod.rs and src/browser/session.rs.

Screenshots and viewport emulation

Use `screenshot` when a caller needs a managed PNG artifact. The tool accepts:

  • `mode`: `viewport`, `full_page`, `element`, or `region`
  • `scale`: `device` or `css`
  • optional `tab_id`
  • optional `target` for `element` captures
  • optional `region` for `region` captures

Successful calls return managed artifact metadata, including `artifact_uri`, `artifact_path`, `mime_type`, `byte_count`, image dimensions, CSS dimensions, device pixel ratio, pixel scale, `revealed_from_offscreen`, and optional clip data. Callers do not provide output paths.

Use `set_viewport` to emulate responsive breakpoints through CDP. Successful calls return `viewport_metrics_after`; later `snapshot` calls expose the live metrics under `scope.viewport`.

Safety boundaries

  • Attach mode can see the tabs, cookies, and authenticated state in the browser profile you connect to.
  • Use a dedicated browser profile for agent work when you do not want automation attached to a personal browser session.
  • `evaluate` requires `confirm_unsafe = true` because it runs arbitrary JavaScript in the active page.
  • `navigate` and `new_tab` allow `http:`, `https:`, `about:`, and same-origin relative paths by default. `data:`, `file:`, other absolute schemes, and protocol-relative URLs require `allow_unsafe = true`.
  • `go_back` and `go_forward` apply the same unsafe-scheme gate and revert rejected history moves.
  • `close_tab` requires `confirm_destructive = true` before closing an unmanaged active tab in a connected session.
  • `close` requires `confirm_destructive = true` before expanding connected-session cleanup from managed tabs to all tabs.
  • `cursor` and `node_ref` targets are revision-scoped. After navigation or DOM-changing actions, refresh with `snapshot`.

Operation metrics

Finished tool results include `operation_metrics` metadata when a tool records non-zero metrics. `operation_metrics.output_bytes` is optional; it appears only when a tool path measures the exact serialized output size.

Measured paths may include:

  • browser evaluation count
  • poll iterations
  • DOM extraction count and extraction time
  • last DOM node count
  • snapshot render time
  • handoff rebuild count and time
  • exact serialized output size when measured

Run the focused operation metrics tests:

bash
cargo test --locked --all-features operation_metrics

Local development

Build from source:

bash
cargo build

Run the normal test suite:

bash
cargo test

Run the browser smoke suite from the repository root:

bash
scripts/browser-smoke.sh

The smoke script runs:

bash
cargo test --test browser_smoke -- --nocapture

Browser smoke checks launch a local browser and are intended for maintainer workstations. CI covers formatting, clippy, MSRV, cargo check, tests, and packaging without requiring a live browser attach target.

Source anchors

License

MIT

Frequently asked questions

What is chromewright?

chromewright is Browser automation via Chrome DevTools Protocol with built-in AI integration through a MCP Server

How do I install chromewright?

Open the GitHub repository and follow its README. Most MCP servers are added to your client's MCP config, then called by your agent.

Is chromewright open source?

Yes — it is hosted on GitHub at https://github.com/bnomei/chromewright and has 1 stars.

Related MCP tools

Run your own MCP server? See who uses it and what to fix.

Measure it with TrackMCP