Browser Harness ♞BLOCK
Let Claude Code, Codex or Cursor use your real Chrome, logged in as you. One prompt to set up. Open source, MIT.
Overview
From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.
Browser Harness lets Claude Code, Codex or Cursor use your real Chrome browser: open pages, click, type, read and download, logged in as you. Paste one prompt into your coding agent to set it up. It talks to Chrome over the DevTools Protocol (CDP), and the agent writes any missing helper as it works, so the harness gets better with each task. Open source, MIT licensed.
Try browser-harness in Browser Use Cloud or paste the setup prompt into your coding agent.
● agent: wants to upload a file │ ● agent-workspace/agent_helpers.py → helper missing │ ● agent writes it agent_helpers.py │ + custom helper ✓ file uploaded
You will never use the browser again.
See it work
Task: "Open my X profile, find my latest 20 video posts, and download them."
[](https://browser-use.com/showcase/videos/download-latest-20-x-videos.mp4)
Setup prompt
Paste into Claude Code or Codex (Cursor: use the MCP config below):
Install or upgrade browser-harness to the latest stable version with uv using Python 3.12, register the skill from `browser-harness skill`, and connect it to my browser. Ask whether I want local browser recordings enabled; default to no and preserve my existing preference on upgrades. Follow https://github.com/browser-use/browser-harness/blob/main/install.md if setup or connection fails.
The agent will open chrome://inspect/#remote-debugging. On first setup, tick the checkbox so the agent can connect to your browser:
850b2014fd27OBSERVED · 2026-10-20Install
Commands as the repository documents them. They are shown, not run.
uv run --with pytest python -m pytest tests/unit -q
uvx --from 'browser-harness[mcp]' browser-harness-mcp
claude mcp add browser-harness \
uvx --from 'browser-harness[mcp]' browser-harness-mcp
uvx --from 'browser-harness[mcp]' browser-harness-mcp
uvx --from 'browser-harness[mcp]' browser-harness-mcp
Host compatibility
What the documentation claims. We have not run a compatibility test.
| Host | Status | Notes |
|---|---|---|
| claude-code | mentioned | |
| claude-desktop | mentioned | |
| codex | mentioned | |
| copilot | mentioned | |
| cursor | mentioned | |
| gemini-cli | mentioned | |
| openclaw | mentioned |
What it tells the agent
The instruction file, verbatim from the audited commit — this is the text the model reads, and the surface the audit's instruction layer examines. Quoted here so you can judge it without cloning anything.
---
name: browser-harness
description: "Control a real browser via CDP: clicking, typing, navigation, logged-in sessions, JS-rendered or bot-protected pages. Not for plain HTTP fetches of public content - use curl for those."
---
# browser-harness
Direct browser control via CDP. For task-specific edits, use `agent-workspace/agent_helpers.py`. For setup, install, or connection problems, read https://github.com/browser-use/browser-harness/blob/main/install.md.
## When Not to Use
A basic fetch of public information needs no browser. If a plain HTTP request can read it — a public page, an API, docs — use `curl` or your fetch tool, and leave the browser alone. Use browser-harness when the task needs interaction (click, type, navigate), the user's logged-in session, JS rendering, or a bot-protected page. If a direct fetch fails or returns a shell page, then escalate to the browser.
Domain skills are off by default. Set `BH_DOMAIN_SKILLS=1` to enable them; see the bottom section.
**If `BH_DOMAIN_SKILLS=1` and the task is site-specific, read every file in the matching `$BH_AGENT_WORKSPACE/domain-skills/<site>/` directory before inventing an approach.**
## Usage
```bash
browser-harness <<'PY'
print(page_info())
PY
```
- Invoke as `browser-harness`. Use heredocs for multi-line commands.
- Helpers are pre-imported. `run.py` calls `ensure_daemon()` before `exec`.
- First navigation for a task is `new_tab(url)`, not `goto_url(url)`. The daemon
preserves the attached tab across separate CLI invocations, so do not call
`new_tab()` again in every script.
- Keep one working tab per task/site. Before opening another, inspect
`current_tab()` and `list_tabs()` and use `switch_tab()` to reuse a matching
tab. Do not leave duplicate tabs on the same URL or close tabs you did not
create.
- At task completion, close tabs created for the task that are no longer needed.
Keep a tab open if the user needs to see it, it is needed for a known follow-up,
or closing it could discard unsaved work or other important state.
- `new_tab()` and `switch_tab()` attach and move the horse marker without
changing Chrome's visible tab. Screenshots and normal CDP input work in the
background. Never call `activate_tab(target)` automatically: it brings Chrome
to the foreground. Call it only when the user explicitly asks to see or
visibly switch to that tab. Do not pair `switch_tab()` with `activate_tab()`.
- A local daemon is a connection to the whole Chrome instance, not to one site,
task, card, or agent. Omit `BU_NAME` and reuse the default daemon for normal
sequential local work across websites, tabs, screenshots, and Codex turns.
Do not invent per-job names such as `gmail1375` or `slack1371`: every new
local daemon opens another browser-level CDP connection and Chrome may show
another Allow prompt.
- Set `BH_TAB_MARKER=0` before starting the daemon to leave page titles unchanged.
The horse marker remains enabled by default.
- A timeout or page that pauses while hidden is not permission to foreground
Chrome. Keep using background CDP operations. For a focus-gated page,
temporarily call `cdp("Emulation.setFocusEmulationEnabled", enabled=True)`,
perform and verify the operation, then disable it in a `finally` block. If
background control still cannot work, report that limitation instead of
activating the tab. Do not invent a `Runtime.evaluate` scroll replacement or
a cross-frame JS walker.
- The normal local flow attaches to the running Chrome/Chromium CDP endpoint. No browser ids or local profile selection.
## Local Chrome
The default daemon can keep many tabs and visit many sites; browser-harness has
no per-site, screenshot, or result-count limit that requires a new daemon.
Chrome memory and page complexity are the practical limits. Reuse matching tabs
with `list_tabs()` and `switch_tab()`.
One daemon has one mutable attached/current tab. Many agents can share it when
their browser operations are serialized: treat local Chrome as one shared
browser lane while non-browser work continues in parallel. Sequential tab
switching, input, and screenshot capture are safe. Do not create another local
daemon merely because several agents exist.
Two agents that switch tabs and act simultaneously can race, causing one to act
on or capture the other's tab. For truly simultaneous interactive work, use
separate remote browsers when Browser Use Cloud authentication is already
available. Otherwise serialize browser operations through the default local
daemon. A named local daemon is a last resort when simultaneous isolation is
required, remote auth is unavailable or unsuitable, and the extra Chrome
approval prompt is acceptable. It creates another controller and dedicated tab
in the same local Chrome profile, not another Chrome profile or process.
If the default daemon becomes stale, use its built-in reattachment/recovery
first. A command timeout, truncated output, site change, closed tab, or new task
is not a reason to create another daemon. Run `browser-harness --doctor` and
restart or replace the default daemon only when it is actually dead or cannot
recover.
If the daemon cannot connect, run diagnostics:
```bash
browser-harness --doctor
```
If Chrome is not running at all, the harness launches it automatically and retries.
If Chrome is running but remote debugging is not enabled, the harness opens:
```text
chrome://inspect/#remote-debugging
```
On macOS, when local Chrome asks for remote-debugging permission, keep the
original browser command running and call `mac-approve` in another shell/tool
call. Preserve the exact daemon name: if the waiting command used
`BU_NAME=r7k2`, run:
```text
BU_NAME=r7k2 browser-harness mac-approve
```
For the default daemon, omit the `BU_NAME` prefix. The original command resumes
when the helper returns `ready`; do not rerun it. If the helper reports
`accessibility-required`, ask the user once to grant the app launching
browser-harness (for exampleTrust audit
BLOCKgrade F · trust 47/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | WARN |
| L1 | Static analysis of the code | FAIL |
| L2 | Instruction surface (what it tells the agent) | FAIL |
| L3 | Class-specific surface | PASS |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- none-observed
- Network
- declared (11 observation(s))
- Shell
- declared (1 observation(s))
- Dependencies
- pinned
- Secrets in source
- found
Findings (25)
exec(code, globals())
t = t.replace("","").replace("","").replace("","")10. **Unicode zero-width space in some titles.** `\u200b` (zero-width space) appears at the start of certain titles (e.g. "Our Life: Beginnings & Always"). Strip with `.replace('\u200b', '').strip()`skills/browser-harness/SKILL.md
src/browser_harness/SKILL.md
return json.loads(urllib.request.urlopen(f"http://127.0.0.1:{port}/json/version", timeout=1).read())["webSocketDebuggerUrl"]with urllib.request.urlopen(f"http://127.0.0.1:{probe_port}/json/version", timeout=1) as r:with urllib.request.urlopen(f"http://127.0.0.1:{port}/json/version", timeout=0.3) as response:API_KEY = "1MOZgmNFxvmljaQR1X9KAij9Mo4xAY3u"
API_KEY = "1MOZgmNFxvmljaQR1X9KAij9Mo4xAY3u"
API_KEY = "1MOZgmNFxvmljaQR1X9KAij9Mo4xAY3u"
API_KEY = "1MOZgmNFxvmljaQR1X9KAij9Mo4xAY3u"
API_KEY = "1MOZgmNFxvmljaQR1X9KAij9Mo4xAY3u"
BU_NAME=qbo BU_CDP_URL=http://127.0.0.1:9223 browser-harness <<'PY'
- **Way 2 (isolated profile, no popups):** launch Chrome with `--remote-debugging-port=9222 --user-data-dir=<non-default path>`, then set `BU_CDP_URL=http://127.0.0.1:9222`. Best for unattended automa
open(path, "wb").write(base64.b64decode(r["data"]))
data = base64.b64decode(shot["data"])
2. Open Test mode and its API keys page. Do not infer account identity from the URL alone.
`curl -fsSL https://browser-use.com/profile.sh | sh`.
curl -fsSL https://browser-use.com/profile.sh | sh
POST `https://my.recruitwithatlas.com/graphql` using the tab's own cookies:
`https://github.com/{owner}/{repo}` — user-triggered actions on the repo header (Star, Unstar, Watch, Unwatch) are HTML forms that POST back to GitHub with the session's CSRF token already rendered inHLTB runs a token-gated POST endpoint at `/api/find`. You must first fetch a session token from `/api/find/init`, then include it in the search request. Both steps are plain HTTP — no browser required
- **Token system**: Every search requires a fresh token from `/api/find/init`. Token encodes `timestamp::IP|UA|hpKey|hmacHash`. The server validates that the UA used to fetch the token matches the UA
- **Required headers on search POST**: `Origin: https://howlongtobeat.com` AND `Referer: https://howlongtobeat.com/` — missing either causes HTTP 403 or 404. `x-auth-token`, `x-hp-key`, `x-hp-val` are
Gates applied: no_behavioural_pass.
850b2014fd27full audit observations/trust-audit/skill/browser-use__browser.json · Report an issue / request a re-scanAudit history
Every audit this skill has had.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-20 | 850b2014fd27 | BLOCK | F | 47 | first audit |
Questions
What does the browser — browser-use/browser-harness skill do?
Let Claude Code, Codex or Cursor use your real Chrome, logged in as you. One prompt to set up. Open source, MIT.
Is browser — browser-use/browser-harness safe to install?
No — not without reading the findings first. The audit graded it F (47/100) and found 3 critical or high issues in the source. Each one is listed on this page with the file and line it is on.
What can browser — browser-use/browser-harness access on my machine?
The audit observed that it reaches the network and runs shell commands. Each of those is consistent with what it says it does. Secrets in the source: found — see the findings.
Which assistants does browser — browser-use/browser-harness work with?
Its documentation mentions claude-code, claude-desktop, codex, copilot, cursor, gemini-cli and openclaw. That is what the text claims, not a compatibility test we ran.
How current is this page?
The grade is for one exact copy of the source (850b2014fd27), read on 2026-10-20. The repository is watched, and a new audit runs when it changes — this is the first audit.
Add this badge to your README
Shows this page's audit result and links back to it. It updates when the audit does.
[](https://modelsatlas.com/skills/browser-use/browser/)
Not your listing, or something out of date? Claim or update your listing.