Skip to main content
Glama
README.md
# 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

A3.8/5.0

Scored across 11 tools

Disambiguation4/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness4/5

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.