Browser Tools
Browser automation, debugging, and anti-detect browsing CLI. Provides:
- Raw CDP passthrough - Any
Domain.methodgoes 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:9222drives 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 guidehas a worked example of each - CPU profiling -
browser-tools-profiler, a second command installed alongsidebt, 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 stablefor regular Google Chrome. - Camoufox (
camoufox fetch) for anti-detect Firefox workflows, with thecamoufoxextra
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 listshows every profile with its path and its live holder, andbt profile delete NAMEremoves one. Deleting a profile deletes its login state;cleanupnever 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.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| browser_tools-0.5.0.tar.gz | 398.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| browser_tools-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 707.2 kB
Release files / browser_tools-0.5.0.tar.gz
| Download URL | browser_tools-0.5.0.tar.gz |
|---|---|
| Size | 398.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f4f692ea213218c64550581b5a9d243daf0ddc2c1fe31bb010026279c4422047
|
|
BLAKE2b-256 checksum How to use checksums |
f5b60ddcdae77636d68e55e343e41236192c02128b8bbfc712431ec99b073dd3
|
| 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.5.0-py3-none-any.whl
| Download URL | browser_tools-0.5.0-py3-none-any.whl |
|---|---|
| Size | 309.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
407573da49081fd6b708a8800c6227acf67c198318cc0a145d16b0e2d2def62a
|
|
BLAKE2b-256 checksum How to use checksums |
232f9b549e742819219acda7fee76d47f8122c8360aec671528a1dfdf1554e79
|
| 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}
|