ultrabrowse
# ultrabrowse
**Ultra-fast browsing for Claude Code, Codex, Cursor and any MCP client.**
Hand your agent a goal ("find the yearly price of the Pro plan", "search for X and open the first
result") and a real Chrome on your machine does the clicking, driven by TypeSafe's **Jev** (a small
action-selection model that returns one indexed choice per call, not text) at about **300 ms per
decision**. Your big model makes one tool call and gets the result. It never spends tokens or seconds on
the intermediate pages.
Independent open-source project; not affiliated with TypeSafe (makers of Jev) or browser-use. The
engine is the [jev-ultrafast](https://github.com/browser-use/jev-ultrafast) policy rebuilt in plain
JavaScript over raw Chrome DevTools Protocol. No Playwright, no Python, no native modules.
| | per step | 10-step task | what your model sees |
| --- | --- | --- | --- |
| Model driving a browser tool click by click | seconds | 30–90 s | every page, every step |
| `ultrabrowse browse` | ~0.3 s | 3–10 s | one result |
## Install
Needs **Node 22+** and **Google Chrome** (Chromium, Brave or Edge also work). Two API keys:
- `TYPESAFE_API_KEY` picks every click. Get one at https://typesafe.ai
- `TEXT_MODEL_API_KEY` is an OpenAI-compatible key used whenever text has to be typed and for `extract`.
Default provider is OpenRouter (https://openrouter.ai/keys); any `/chat/completions` endpoint works via
`TEXT_MODEL_BASE_URL`.
Pass them like any other MCP server, in the `env` block. If you prefer a file, put them in
`~/.ultrabrowse/.env` (template: [.env.example](.env.example)); the env block always takes precedence.
### Claude Code
```bash
claude mcp add ultrabrowse -e TYPESAFE_API_KEY=… -e TEXT_MODEL_API_KEY=… -- npx -y ultrabrowse@latest
```
Or install the plugin, which adds the server **plus a skill that teaches Claude to delegate whole web
tasks** instead of clicking step by step (keys then go in `~/.ultrabrowse/.env`):
```
/plugin marketplace add narekgevorgyan/ultrabrowse
/plugin install ultrabrowse@ultrabrowse
```
### Codex CLI
```bash
codex mcp add ultrabrowse --env TYPESAFE_API_KEY=… --env TEXT_MODEL_API_KEY=… -- npx -y ultrabrowse@latest
```
or in `~/.codex/config.toml` (raise the tool timeout: Codex defaults to 60 s and a `browse` can take longer):
```toml
[mcp_servers.ultrabrowse]
command = "npx"
args = ["-y", "ultrabrowse@latest"]
tool_timeout_sec = 300
[mcp_servers.ultrabrowse.env]
TYPESAFE_API_KEY = "…"
TEXT_MODEL_API_KEY = "…"
```
Add to `AGENTS.md`: *"For any web task, call the `ultrabrowse` MCP `browse` tool with a goal instead of driving a browser click by click."*
### Cursor, Windsurf, Claude Desktop
```json
{
"mcpServers": {
"ultrabrowse": {
"command": "npx",
"args": ["-y", "ultrabrowse@latest"],
"env": { "TYPESAFE_API_KEY": "…", "TEXT_MODEL_API_KEY": "…" }
}
}
}
```
### VS Code (`.vscode/mcp.json`)
```json
{
"servers": {
"ultrabrowse": {
"command": "npx",
"args": ["-y", "ultrabrowse@latest"],
"env": { "TYPESAFE_API_KEY": "…", "TEXT_MODEL_API_KEY": "…" }
}
}
}
```
### From a clone
The bundle `dist/cli.cjs` is committed, so a clone needs no `npm install`:
```bash
git clone https://github.com/narekgevorgyan/ultrabrowse
claude mcp add ultrabrowse -- node "$PWD/ultrabrowse/dist/cli.cjs"
node ultrabrowse/dist/cli.cjs doctor # checks keys and Chrome (opens a Chrome window; add --no-launch to skip)
```
## Tools
| Tool | What it does |
| --- | --- |
| `browse(url?, goal)` | The one to use. Opens the URL (or continues in the current tab when `url` is omitted) and completes the goal end to end. Returns status (`done` / `blocked` / `stuck` / `budget` / `timeout`), the steps, and the final page: url, title, text, numbered elements. Consent walls (including ones inside cross-origin iframes) are dismissed before the first decision; JS alerts/confirms are accepted automatically and listed in `notes`; controls below the fold are indexed and scrolled to when chosen; a request that got redirected elsewhere before any action comes back `blocked` with `reason: "redirected"` and `requested_url`. When a run does not end `done`, it first tries once to clear overlays and retry, then returns with a screenshot attached, a `reason` when known (`consent_overlay`, `redirected`) and a hint on how to take over. |
| `snapshot(url?, full?)` | No-model read in ~0.5 s: on-screen text (plus same-origin iframe text) and numbered elements, one line each (`[12] link "Title"`). `full: true` returns the whole document's text (up to 40k chars). Reports a covering cross-origin iframe as `overlay`. Needs no keys. |
| `extract(url?, goal?, schema)` | Browse (optional), then return JSON shaped like `schema` from the page. Tolerates models that wrap JSON in prose or code fences, with one retry. |
| `browse_many(tasks[], concurrency)` | Goal-driven runs across many sites in parallel tabs. |
| `click(index)`, `type_text(index, text, submit)`, `press_key(key)`, `scroll`, `back` | Manual control over the numbered elements, for taking over when a `browse` stops. |
| `screenshot()`, `close()`, `close_all()` | JPEG of the current tab; close this server's tab; close every page in ultrabrowse's Chrome (tabs left by other processes too). Tabs are also closed when the MCP client disconnects. |
## Chrome
By default ultrabrowse launches a dedicated Chrome with its own profile under
`~/.ultrabrowse/chrome-profile` and reuses it across calls, restarts and clients: Claude Code and
Codex (or several sessions) running at once all share the one Chrome; whoever starts first launches it
and the others join it. The profile **persists**
(cookies and logins survive), so treat it like a browser profile; `rm -rf ~/.ultrabrowse/chrome-profile`
resets it. Set `ULTRABROWSE_HEADLESS=1` to hide the window.
To browse **as yourself** (logged-in sessions, internal tools), start a Chrome with remote debugging and
point ultrabrowse at it:
```bash
# macOS example; a separate profile dir so it doesn't fight your everyday Chrome
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --remote-debugging-port=9222 --user-data-dir="$HOME/.ultrabrowse/my-chrome"
```
```
ULTRABROWSE_CDP_URL=http://127.0.0.1:9222
```
## What leaves your machine
- **api.typesafe.ai**, on every step: the current page's URL, title, visible text, the labels and values of
interactive elements, your goal, and the last 10 actions. No cookies, no screenshots, no password fields.
- **openrouter.ai** (or your `TEXT_MODEL_BASE_URL`), only when text has to be typed or you call `extract`:
the goal, the schema, and up to 14 000 characters of page text.
- Nothing else. No telemetry, no update checks.
## Safety
- Model output is only ever an index into elements the page snapshot observed. It cannot produce a
selector, a URL, or code that runs.
- The snapshot skips `password`, `file` and `hidden` inputs, so ultrabrowse **cannot type a password**.
- Every click is hit-tested at execution time (visible, not covered, still the same element).
- Bot challenges (Cloudflare and friends) are reported as `challenge: true`, never solved.
- Runs are bounded by an action budget, wall time, and no-progress detection.
## Develop
```bash
npm install
npm run build # regenerates the inlined snapshot and bundles to dist/cli.cjs
node scripts/smoke.js dist # live end-to-end test over stdio (needs keys + Chrome)
claude plugin validate . # checks the plugin manifests
```
`src/snapshot.js` is the in-page atomic snapshot (from jev-ultrafast, plus React-clickable divs);
`src/engine.js` the bounded loop; `src/browser.js` the CDP tab; `src/model.js` the TypeSafe and
text-model client; `src/server.js` the tools.
MIT.
TDQS
Scored across 11 tools
Most tools are clearly distinct: click, type_text, press_key, scroll, back, close, snapshot, screenshot, and extract each have a specific role. Some overlap exists between browse, browse_many, and extract with a goal, since all can perform autonomous browsing, but their outputs and parallelism distinguish them.
Tool names mostly follow an imperative command style with snake_case for multi-word names like type_text, press_key, and browse_many. Minor deviations like snapshot, screenshot, and back are noun/adverb-style rather than verb_ noun, but the overall pattern is readable and predictable.
11 tools is well-scoped for a browser automation server. Each tool covers a distinct aspect of browsing, reading, interacting, or tab management without excessive redundancy or an overwhelming surface.
The core browser lifecycle is well covered: navigation via browse/back, reading via snapshot/screenshot/extract, interaction via click/type_text/press_key/scroll, and tab handling via close. Minor gaps like reload or forward navigation are absent, but the high-level browse tool can work around many of those cases.