caveman-browse
Read web pages as compressed accessibility trees instead of raw HTML.
caveman-browse is a local Chrome driver for agents. It reads Chrome's accessibility tree, turns it into compact
UID-addressed text, and stores raw tree bytes behind a recovery handle.
- Transport
- MCP over stdio, with a direct CLI helper.
- Browser path
- Chrome DevTools Protocol.
- Snapshots
- Compressed accessibility text, not raw HTML.
- Basis
inferred- Licence
- BSL 1.1.
Build from source#
caveman-browse is not published on npm today.
git clone https://github.com/JuliusBrussee/caveman
go build -o ./bin/caveman-browse ./public/browse/cmd/caveman-browseRun with no arguments to serve MCP over stdio:
./bin/caveman-browseMCP tools#
| Tool | Purpose |
|---|---|
browser_snapshot | Navigate when a URL is supplied, wait, then return a compact accessibility view. |
browser_act | Click, type, select, scroll, or wait using a UID from the latest snapshot. |
browser_eval | Evaluate a JavaScript expression in the page. |
browser_recover | Return raw accessibility bytes, or a query-selected view of them. |
Configure an MCP host with the absolute binary path:
{
"mcpServers": {
"caveman-browse": {
"command": "/absolute/path/to/bin/caveman-browse",
"args": []
}
}
}Take a useful snapshot#
Large pages should include the task as a query:
browser_snapshot({
url: "https://example.com/settings",
query: "save notification settings"
})The query keeps matching accessible nodes and their ancestors. CCR retains full raw tree. This usually costs less than taking a full snapshot and recovering later.
interactive: true keeps UID-bearing nodes and their ancestors only. It is useful for dense control surfaces but
hides most page text. Query focus remains the better default when the agent must read.
Snapshot output is text:
[a1] button "Save"
[a2] checkbox "Email alerts"
caveman before=... view=... after=... ratio=... basis=inferred handle=ccr_...Only actionable or unknown custom roles receive UID tokens. The trailing line counts the exact result delivered to
the agent. view isolates compact tree cost; after includes the accounting line.
Act, then resnapshot#
browser_act({ action: "click", uid: "a1" })
browser_act({ action: "type", uid: "a3", text: "alerts@example.com" })
browser_act({ action: "select", uid: "a4", option: "Daily" })
browser_act({ action: "scroll", uid: "a5" })
browser_act({ action: "wait" })UIDs belong to the latest successful snapshot. Unknown or stale UIDs fail explicitly.
Click, type, select, and scroll report that dispatch succeeded, but they do not prove application state settled.
Take another focused snapshot to verify the result. wait is a fixed short delay, not a condition-based page
assertion.
Direct CLI#
caveman-browse snapshot https://example.com "save settings"
caveman-browse snapshot -i https://example.com
caveman-browse act <uid> click
caveman-browse eval "document.title"
caveman-browse recover <handle>
caveman-browse closeDirect commands share one detached, isolated Chrome until close.
Navigation and limits#
Navigation accepts HTTP, HTTPS, about:blank, and bounded data:text/html URLs. Local files and privileged browser
schemes fail with cave_browser_url_denied.
Snapshot wait is limited to 30 seconds. Query text is limited to 4 KiB, and a data URL to 1 MiB. A snapshot that cannot produce a smaller recovery-backed UID view fails and keeps the previous UID map rather than dumping raw AX JSON into model context.
Browser selection#
Environment variables select runtime behaviour:
CAVEMAN_BROWSE_CHROME explicit Chrome executable
CAVEMAN_BROWSE_CDP attach to an existing CDP endpoint
CAVEMAN_BROWSE_USER_DATA_DIR profile directory
CAVEMAN_BROWSE_HEADFUL=1 show the browser window
CAVEMAN_BROWSE_EPHEMERAL=1 use in-memory recoveryDefault mode is headless. The recovery store otherwise uses the shared CCR path.
browser_eval runs caller-supplied JavaScript in the active page. Treat it as code execution in that browser
session and use it only on pages you intend to control.