Octop Browser Banner

Agent-first lightweight browser automation — direct CDP, no Playwright, more accurate and more reliable.

Python 3.11+ License: MIT PyPI Code Style: Ruff GitHub stars

Overview · Highlights · Core Technology · Features · Quick Start · Contents

English · 中文


📌 Overview

Octop Browser is a lightweight browser-use style tool that makes an agent’s browser usage more accurate and reliable. Unlike typical stacks that route every action through Playwright, it launches a real Chromium and talks to the Chrome DevTools Protocol (CDP) directly — removing the intermediate layer so element targeting is driven by stable, live DOM refs and rarely misses.

Under the hood it starts a Chromium process with a --remote-debugging-port and speaks CDP itself. Because element references come straight from the live page, the agent acts on the real nodes, and those refs stay valid through reflows and re-renders — the result is higher action accuracy and lower token usage, with a persistent profile so logins don’t expire mid-task.

Octop Browser’s design goal: give an agent a browser it can act on with confidence — accurate clicks, low token cost, and persistent logins — through a small, well-shaped set of tools and a matching CLI.

Note: the install-browser command may bootstrap Playwright once purely to download a Chromium binary. Playwright is not a runtime dependency — the agent always drives the browser over CDP.

✨ Highlights

FeatureDescription
⚡Direct CDPConnects straight to Chrome via CDP — no Playwright / intermediate layer
🎯Ref-based targetingStable element refs survive layout reflows, so clicks land where intended
🪶Lightweight DOMToken-efficient multi-level DOM keeps prompts small
🔐Auth persistenceProfile-based logins persist across sessions — no repeated sign-in
🛠️Agent toolsOne stateless browser_tool with ~20 actions
💻CLIEvery action available as a first-class command
🎬Record & replayCapture a workflow, then replay it as skill-guided agentic execution
🤖MCP serverExpose the browser to any MCP-capable agent

🧠 Core Technology

LayerTechnology
LanguagePython 3.11+
TransportCDP over websockets (hand-rolled async client)
Launchersubprocess Chromium with --remote-debugging-port
DOMMulti-level builder + stable ref system
ToolsStateless browser_tool action set
RecordingInjected JS recorder + semantic collapse + skill generator
InterfacesCLI + MCP server
Build / qualityhatchling · ruff · mypy · pytest

🤔 Features

Browser tools

browser_tool(action=...) exposes the following actions:

ActionDescriptionActionDescription
navigateNavigate the active tabselectPick a <select> option
dom_treeDump the multi-level DOMscrollScroll the viewport
screenshotCapture a screenshothoverHover an element
clickClick by refeval_jsRun JavaScript
typeType text by refgo_back / go_forwardHistory nav
fillFill a field by refreloadReload the page
pressPress a keynew_tab / close_tabTab control
waitWait for a conditionswitch_tab / list_tabsTab management
close_sessionClose the CDP connection (pass kill=True to also stop Chrome)

CDP session

  • BrowserSession.create(profile=...) opens a persistent Chromium session.
  • Stateless helper: browser_tool(action="navigate", url=..., profile="work").
  • Set BROWSER_USE_IDLE_TIMEOUT_MINUTES to stop a local Chrome profile after that many minutes without real CDP activity (0, the default, disables it).
  • await session.close(kill=True) or browser_tool(action="close_session", profile="work", kill=True) stops the local Chrome process immediately. Profile data and login state stay on disk.

CLI

Every tool is also a CLI command:

octop-browser install-browser     # fetch a Chromium binary (one-time)
octop-browser navigate "https://example.com" --profile work
octop-browser dom-tree --profile work
octop-browser click --ref inp_1 --profile work
octop-browser type "octop" --ref inp_1 --profile work
octop-browser screenshot --profile work
octop-browser close-session --kill --profile work   # also stop Chrome
# session: open / close-session / new-tab / switch-tab / close-tab / list-tabs

Record & replay

Octop Browser can record a real browsing session and replay it:

  1. octop-browser record daemon-start — launch the long-lived recording daemon.
  2. octop-browser record start — begin capturing the active tab.
  3. Browse normally. A small injected script captures clicks, typed text, navigations, and submits, with privacy redaction of sensitive fields.
  4. octop-browser record stop — stop capturing.
  5. octop-browser record generate-steps <id> — inspect the semantic steps; record generate-skill <id> emits a draft Skill (draft.skill.md).
  6. octop-browser replay run <id> — re-execute as skill-guided agentic execution (the model replays intent, not brittle coordinates/refs).

Use record list / record show <id> / record status / record doctor to manage recordings.

MCP server

octop-browser ships an MCP server, so any MCP-capable agent can drive the browser through the same tool set.

python -m octop_browser.mcp_server   # or: make mcp

It speaks MCP over stdio, which is the transport most agent clients expect.

Over MCP each action is exposed as its own tool named after it — browser_navigate, browser_click, browser_dom_tree, browser_close_session, browser_record_start, install_browser, … — rather than as a single dispatching tool. Arguments match the Python API, so browser_close_session also takes kill to terminate Chrome.

Agent skills

The skills/ directory ships ready-made Skill files for agents that support skill loading — skills/octop-browser/SKILL.md (English) and skills/octop-browser-zh/SKILL.md (Chinese). They encode the safe usage pattern: run dom-tree before every click, pass ref s instead of coordinates, and reuse named profiles for login state. record generate-skill emits a draft Skill in the same format.

🚀 Quick Start

Prerequisites

  • Python 3.11+ — verified on 3.11 and 3.12. Higher-level applications built on top of it (e.g. Octop) may require a newer Python; follow their own docs.
  • A Chromium / Chrome binary (auto-downloaded by install-browser)

1. Install

pip install octop-browser
octop-browser install-browser    # fetch a Chromium binary once

2. Use as a library

from octop_browser import BrowserSession

async with await BrowserSession.create(profile="default") as session:
    await session.navigate("https://example.com")
    dom = await session.dom_tree()
    await session.click(ref="btn_1")

Or statelessly:

from octop_browser import browser_tool

await browser_tool(action="navigate", url="https://example.com", profile="work")

3. Use as a CLI

octop-browser --help
octop-browser --version
octop-browser navigate "https://example.com" --profile work
octop-browser dom-tree --profile work
octop-browser dom-tree --help

The top-level help lists every command and common examples. Every command also has its own help page, including nested commands such as octop-browser record start --help.

4. Record a workflow

octop-browser record daemon-start
octop-browser record start
# ... interact with the page ...
octop-browser record stop
octop-browser record generate-skill <recording_id>   # emit a draft Skill
octop-browser replay run <recording_id>              # replay it

📑 Contents

📖 CLI reference

Use octop-browser --help for the complete command list and octop-browser <command> --help for command-specific options. The --version flag prints the installed package version.

CommandDescription
install-browserDownload a Chromium binary (one-time bootstrap)
navigateNavigate the active tab to a URL
openStart a session and open one or more URLs as separate tabs
dom-treePrint the multi-level DOM
screenshotCapture a screenshot
click / type / fill / pressInteract by ref
wait / select / scroll / hoverPage control
eval-jsRun JavaScript
go-back / go-forward / reloadHistory / reload
new-tab / switch-tab / close-tab / list-tabsTab management
close-sessionDetach from a profile’s CDP session. Chrome keeps running so a later command re-attaches; add --kill to terminate it and free its port
record ...doctor, daemon-start, daemon-stop, status, start, stop, list, show, generate-steps, generate-skill
replay run <id>Replay a recorded workflow

Record & replay options

OptionValuesPurpose
record start --privacynone · mask-sensitive (default) · mask-allRedact typed values before they are written to disk. mask-sensitive masks fields that look sensitive (password, token, secret, otp, jwt, … matched against the field’s type / name / id / placeholder / label); mask-all masks every typed value
replay run --input key=valuerepeatableSupply a value for a {{placeholder}} step at replay time

Redacted values are stored as the placeholder {{sensitive_value}}, so a recording that captured a login can still be replayed safely — keep the secret in the environment and pass it in at replay time:

octop-browser replay run <recording_id> --input sensitive_value="$MY_PASSWORD"

DOM levels

dom-tree (and the dom_tree action) takes --level to trade detail for tokens:

LevelCostContents
minimal~50 tokensTitle / URL only
interactive (default)~200–500Clickable / typeable elements with ref s
full~1k–3kFull readable page
structuredvariesJSON, for programmatic use
octop-browser dom-tree --level minimal      # cheap page check
octop-browser dom-tree --level structured   # JSON for scripts

🐍 Python API

Exports

Everything below is importable from the package root: from octop_browser import ...

SymbolKindPurpose
BrowserSessionclassPersistent CDP session — await BrowserSession.create(profile="work")
browser_toolasync fnStateless one-shot action — await browser_tool(action="click", ref="btn_1")
ToolResultmodelReturn value of every session method and browser_tool()
ActionMetricsmodelPer-action timing / token counters
TabInfomodelOne tab: tab_id · url · title · active
OctopSettingsmodelFull settings object — pass via BrowserSession.create(settings=...)
settingsinstanceProcess-wide default, built from the environment at import time
BrowserModetypeLiteral["auto", "headed", "headless"]
chromium_executablefnPath of the managed Chromium binary, or None if absent
ensure_chromiumfnBlocking install — returns True once a binary is ready
install_chromium_streamasync genSame install as an event stream, for progress UIs
InstallEventTypedDictOne install event: a log line, or terminal done / success / error

Tool results

Every session method and browser_tool() call returns a ToolResult:

FieldTypeNotes
successboolFalse means the action failed — inspect error
contentstr | dictMain payload: DOM text, screenshot path, JS return value, …
errorstr | NoneFailure reason; None on success
metricsActionMetricsaction · duration_ms · dom_nodes_scanned · estimated_tokens · screenshot_size_kb
metadatadict | NoneOptional side-channel — e.g. screenshot reports page url / title here
res = await session.dom_tree(level="interactive")
if res.success:
    print(res.content, res.metrics.estimated_tokens)

session.metrics_summary() aggregates running totals for the whole session.

Hooks

BrowserSession mixes in HooksMixin, so callbacks can observe every action:

@session.on("after_action")
async def log_metrics(metrics):  # metrics: ActionMetrics
    print(metrics.action, metrics.duration_ms)
EventPayloadFired
before_actiondict — {"action": str, "params": dict}Before each action is dispatched
after_actionActionMetricsAfter an action succeeds
action_errorToolResultAfter an action returns success=False
page_navigated—Reserved: accepted by on(), not emitted yet

Callbacks may be sync or async, and an exception inside a hook is logged without breaking the action.

⚙️ Configuration

Every setting is read from the environment at import time. You can also build a OctopSettings object explicitly and hand it to BrowserSession.create():

from octop_browser import BrowserSession, OctopSettings

settings = OctopSettings(cdp_port_start=9300, cdp_timeout=60.0)
session = await BrowserSession.create(profile="work", settings=settings)
VariableDefaultPurpose
BROWSER_USE_MODEautoLaunch mode: auto / headed / headless (auto picks headed on macOS/Windows or when DISPLAY is set)
BROWSER_USE_CDP_WS_URL(unset)Connect straight to an existing Chrome DevTools WebSocket URL — use this for remote / Docker Chrome and skip the launcher
BROWSER_USE_CHROME_BINauto-detectAbsolute path to the Chrome / Chromium binary
BROWSER_USE_PROFILES_DIR~/.octop-browser/profilesBase directory for Chrome user-data-dirs
BROWSER_USE_SCREENSHOTS_DIR~/.octop-browser/screenshotsWhere the screenshot action writes PNGs
BROWSER_USE_CDP_HOSTlocalhostHost serving Chrome’s CDP endpoint
BROWSER_USE_CDP_PORT_START9222First CDP debug port assigned to profiles
BROWSER_USE_CDP_TIMEOUT30.0Seconds to wait for a CDP command response
BROWSER_USE_VIEWPORT_WIDTH1440Viewport width in CSS pixels
BROWSER_USE_VIEWPORT_HEIGHT900Viewport height in CSS pixels
BROWSER_USE_IDLE_TIMEOUT_MINUTES0 (off)Stop a locally managed Chrome after N minutes without real CDP activity
BROWSER_USE_LAUNCH_RETRIES20How many times to poll Chrome after launch
BROWSER_USE_LAUNCH_DELAY0.25Seconds between launch polls
BROWSER_USE_CDP_MAX_MESSAGE_SIZE33554432 (32 MiB)Max CDP WebSocket frame size; 0 disables the cap (raise it for very large screenshots)
BROWSER_USE_NO_SANDBOXauto-detectForce (1) or forbid (0) --no-sandbox. When unset, it is added only if the kernel denies unprivileged user namespaces (typical in containers)
BROWSER_USE_DISABLE_GPUauto (on in headless)Force (1) / forbid (0) --disable-gpu. Defaults to on in headless mode, off in headed mode
BROWSER_USE_DISABLE_DEV_SHM_USAGEauto (on in Linux)Force (1) / forbid (0) --disable-dev-shm-usage. Defaults to on on Linux, where Docker’s 64 MiB /dev/shm is too small for Chrome

🛠️ Development

Prerequisites: Python 3.11+, uv

make install          # uv sync --extra dev (falls back to pip install -e ".[dev]")
make all              # format + lint + typecheck + test

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Run make all before submitting
  4. Open a Pull Request against develop

See CONTRIBUTING.md for branching, PR, and release details (release/* → main auto-publishes to PyPI).

Found a vulnerability? Please follow the private disclosure process in SECURITY.md instead of opening a public issue. Release notes for every version live in CHANGELOG.md.

ProjectDescription
harness-agentAgent runtime that drives the browser tools
harness-memoryMemory system for browser-backed agents
harness-gatewayMulti-platform IM channel bridge
OctopThe self-hosted assistant that composes the Harness stack