Skip to main content

Browser Tools

CI Python 3.13+ License: MIT Code style: ruff

Browser automation, debugging, and anti-detect browsing CLI. Provides:

  • Raw CDP passthrough - Any Domain.method goes straight to the running browser, with protocol help read live from it
  • Snapshot-based page automation - Accessibility-tree snapshots and UID interaction (snapshot, click --uid, fill --uid), in Python over CDP
  • Named browser instances - Long-lived Chrome with a registry and named profiles that keep a login across restarts
  • External browsers - --endpoint http://127.0.0.1:9222 drives a browser you already have open and logged in, writing nothing to the registry
  • Frame-aware tools - Iframe/CDP frame tree management, execution context resolution, and storage inspection
  • Interstitial detection - Multi-signal heuristic detection for Cloudflare, DataDome, Akamai, PerimeterX, Imperva, AWS WAF, and other challenge pages
  • Camoufox anti-detect browsing - Fingerprint-injected Firefox-based browsing for bot-protected sites
  • Anything else CDP can do - File upload, viewport emulation, cookies, PDF export and page management have no curated verb and need none; bt guide has a worked example of each
  • CPU profiling - browser-tools-profiler, a second command installed alongside bt, with timed capture and threshold-triggered capture

Quick Start

Installation

# Using uv (recommended)
uv tool install browser-tools

# Or with pip
pip install browser-tools

Usage

# Run the CLI (`bt` is an alias for `browser-tools`)
bt --help

# Launch a browser. It is registered by name and outlives this command, so
# later verbs drive the same Chrome and share its page and login state.
bt launch

# Navigate through the raw CDP passthrough, then read the page.
bt Page.navigate '{"url": "https://example.com"}'
bt snapshot

# Interact by UID from that snapshot.
bt click --uid 1-13

# When the page is blocking, ask directly.
bt detect

bt status          # registered instances and their page targets
bt stop            # close the browser and retire its registry entry

Omit INSTANCE while exactly one instance is running; with several, every verb names the candidates rather than guessing. bt help Domain.method reads the protocol schema from the running browser.

bt guide is the manual. It is the complete CLI surface -- every verb, every refusal with its exit code, the login walkthrough, the UID rule, and the --endpoint rules -- and reading it is enough to drive a browser with this tool. A test fails the build when a verb has no entry in it.

To drive a browser you started yourself, pass its debugging port instead of an instance name:

# Chrome started with --remote-debugging-port=9222, already logged in.
bt snapshot --endpoint http://127.0.0.1:9222
bt click --uid 0BDAEF756714-14 --endpoint http://127.0.0.1:9222

Nothing is written to the registry, so status does not list it and stop and cleanup cannot reach it -- which is what keeps them away from your real Chrome profile directory. Only 127.0.0.1 and ::1 are accepted; forward a remote browser with ssh -L 9222:127.0.0.1:9222 <host>. Browser.close and Browser.crash are refused over --endpoint.

Development setup

# Clone and install in editable mode
git clone https://github.com/dungle-scrubs/browser-tools.git
cd browser-tools
uv sync

Runtime requirements:

  • Chrome Canary by default (the default channel is canary). Use another installed channel with --channel stable|beta|dev, e.g. --channel stable for regular Google Chrome.
  • Camoufox (camoufox fetch) for anti-detect Firefox workflows, with the camoufox extra

The default install depends on websockets only. There is no Node.js dependency: the Node chrome-devtools-mcp path was removed in RFC-01.

Architecture

Five layers. Each upper layer consumes only the layer below it, and every curated tool calls the same CDP send path the raw passthrough uses -- so any method the installed browser supports works through bt Domain.method whether or not a verb exists for it.

Layer Name Contents
4 Front CLI verbs (the only surface), bt guide, the agent skill that points at it
3 Policy Named profiles and the profile root, fingerprint profiles, engine routing
2 Native toolset Curated tools as plain CDP consumers; profiling; window marking
1 Core (vendored) Registry, liveness, launcher, supervisor, attach, passthrough, live-schema help
0 Browser Chrome/Chromium over CDP; Camoufox for anti-detect paths
cli.py                       CLI entry point (argparse, verb dispatch, exit codes)
        |
        +-- lifecycle.py             launch / status / stop / cleanup over the registry
        |       +-- camoufox_runner.py   Camoufox host process (`launch --engine camoufox`)
        |       +-- process_utils.py     Chrome process and port utilities
        |
        +-- endpoint.py              `--endpoint URL`: loopback check, refusals
        +-- passthrough.py           Raw `Domain.method` send + the focus guard
        +-- curated.py               The curated verbs over one short-lived CDP handler
        +-- one_shot.py              Connect, resolve a page target, attach, detach
        +-- events.py                attach / wait / console-list / network-list
        +-- list_verbs.py            frames / storage
        +-- user_settings.py         The persistent settings file (window border)
        |
        +-- core/                    Vendored chrome-agent core (RFC-01; see NOTICE)
        |       +-- registry.py          Named instances, liveness, retirement
        |       +-- launcher.py          Chrome launch (windowless, background first window)
        |       +-- supervisor.py        Per-instance window marking and retirement
        |       +-- cdp_client.py        CDP WebSocket client and target discovery
        |       +-- attach.py            Isolated event subscriptions
        |       +-- protocol.py          Live protocol schema for `help`
        |       +-- fingerprint.py       Launch-flag fingerprint profiles
        |
        +-- cdp_handler.py           CDP tool implementations (snapshot, click, fill, ...)
        |       +-- native_snapshot.py   Accessibility-tree snapshot and UID scheme
        |       +-- native_interaction.py UID click / fill
        |       +-- frame_manager.py     Frame tree management
        |       +-- interstitial.py      Challenge detection and the retry policy
        |       +-- screencast.py        Screencast capture state machine
        |       +-- screenshot_utils.py  Blank-frame detection
        |       +-- tool_registry.py     Single source of truth for CDP tool routing
        |       +-- mcp_response.py      Response envelope builders
        |
        +-- profiler.py              Standalone CPU profiler (`browser-tools-profiler`)

The CLI is the only surface. The optional MCP front and the persistent-session stack behind it were deleted in RFC-01 Phase 5; there is no daemon, no broker, and no listening socket.

Keeping login state across calls

Login state lives in a browser profile directory and survives only while the same directory is reused. To keep a session logged in:

  • Use a named profile: bt launch --profile <name>. Named profiles persist across restarts and are unaffected by headed/headless switches, viewport, or which directory you invoke from.
  • A profile is held by at most one live instance. Launching into a profile another instance holds fails naming the holder rather than opening a second browser on the same directory.
  • Without --profile, a launch gets a fresh ephemeral directory and starts logged out.
  • bt profile list shows every profile with its path and its live holder, and bt profile delete NAME removes one. Deleting a profile deletes its login state; cleanup never removes a profile on age.

Profiles live in durable storage: $BROWSER_TOOLS_PROFILES_DIR, else $XDG_DATA_HOME/browser-tools/profiles, else ~/.local/share/browser-tools/profiles. They used to live under /tmp, where the operating system deleted every signed-in session at boot, silently. A profile still there is listed with "legacy": true; bt profile migrate moves them all across (--dry-run first, --back to reverse it), and launch --profile NAME brings that one forward by itself.

  • Camoufox persists login state only when you pass --profile; without it, every launch starts logged out.

Development

uv sync
uv run ruff check src/ tests/
uv run pytest

License

MIT - see LICENSE.

Contributing

See CONTRIBUTING.md for development setup and guidelines. All contributors are expected to follow the Code of Conduct.

Security

Found a vulnerability? See SECURITY.md for responsible disclosure.

Metadata

Release files for browser-tools 0.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for browser-tools 0.8.0
File Size Uploaded
browser_tools-0.8.0.tar.gz 496.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for browser-tools 0.8.0
File Interpreter ABI Platform
browser_tools-0.8.0-py3-none-any.whl Python 3 none any Details

Total release size: 850.6 kB

Release files / browser_tools-0.8.0.tar.gz

Download URL browser_tools-0.8.0.tar.gz
Size 496.2 kB
Tags Source
SHA-256 checksum
How to use checksums
e9c1ba9c0df75d6af19d4cbc441ff9aaa705db60a8e086a0a896ad265fb8880d
BLAKE2b-256 checksum
How to use checksums
99d36099e39b5ae41f7c5fca00ed2a332489226520ba27fcf990693400cf69f9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / browser_tools-0.8.0-py3-none-any.whl

Download URL browser_tools-0.8.0-py3-none-any.whl
Size 354.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a38cf8b3c978c90b557870522fa4b8482cc473845b4ff877e4cd378ace65769c
BLAKE2b-256 checksum
How to use checksums
4c96bd4e07bca7b6d4eae6627b167780dab11bfe163d70d5585e4008b2f6ae2a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.9.0

2 release files

This release

0.8.0 This release

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page