Skip to main content
Glama
README.md
<p align="center">
  <img src="assets/banner.svg" alt="gepettobrowser — a stealth Chrome your coding agent can see inside" width="100%">
</p>

<p align="center">
  <a href="https://www.npmjs.com/package/gepettobrowser"><img src="https://img.shields.io/npm/v/gepettobrowser?color=2dd4bf" alt="npm"></a>
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-AGPL--3.0-2dd4bf" alt="License: AGPL-3.0"></a>
  <a href="https://github.com/Zzatiz/gepetto-browser/actions/workflows/ci.yml"><img src="https://github.com/Zzatiz/gepetto-browser/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  <img src="https://img.shields.io/badge/node-%E2%89%A518-3b4b5a" alt="Node 18+">
  <img src="https://img.shields.io/badge/MCP-14%20tools-f5b041" alt="MCP: 14 tools">
  <img src="https://img.shields.io/badge/types-included-3b4b5a" alt="TypeScript types included">
</p>

**gepettobrowser** is a stealth Chrome for scrapers and coding agents. It launches a browser
with a coherent, real-looking fingerprint, then records what each page does under the hood — its
API calls, console errors and code — so an agent (or you) can drive it step by step and see
exactly what every click caused. Use it from code, the `gepetto` CLI, or any MCP client.

- **Stealth that holds up** — one consistent identity across the page, Web Workers, WebGL and
  Client Hints; WebRTC leak protection; proxies with GeoIP-matched timezones; Cloudflare Turnstile.
- **See inside the page** — important requests only (documents, API/XHR/fetch, GraphQL, WebSockets)
  with JSON response shapes; trackers, beacons and assets filtered out. Every action reports the
  requests and errors it triggered.
- **Share one browser** — you, Claude Code, Cursor, Codex or a script can all attach to the same
  session; open it in a visible window to co-browse.
- **Compact by design** — text output an LLM reads in a few hundred tokens; full data stays on disk.

**AI agents:** point your agent at [`AGENTS.md`](AGENTS.md) — it's the complete usage guide
(CLI, MCP tools, output formats, recipes). A ready-made Claude Code skill lives in [`skill/`](skill/SKILL.md).

## Install

```sh
npm install -g gepettobrowser     # the `gepetto` CLI + MCP server
npm install gepettobrowser        # as a library in your project
```

The MCP SDK and the optional AI plugin's SDK come along as optional dependencies. Point it at an
installed Chrome for best results: `export GEPETTO_EXECUTABLE_PATH=/usr/bin/google-chrome-stable`.

## Quick start

**CLI** — sessions start in the background and stay alive between commands:

```sh
gepetto open https://example.com     # → page digest; interactive elements get refs (e1, e2…)
gepetto act click e3                 # → what happened, the requests it fired, fresh digest
gepetto net                          # important requests on this page
gepetto close
```

**MCP** — register once, then ask your agent to use the `gepetto_*` tools:

```sh
claude mcp add -s user gepetto -- gepetto mcp
```

<details>
<summary>Claude Desktop · Cursor · Codex config</summary>

GUI apps don't inherit your shell's `PATH`, so use absolute paths (`command -v npx`):

```jsonc
// claude_desktop_config.json, ~/.cursor/mcp.json
{ "mcpServers": { "gepetto": {
    "command": "/absolute/path/to/npx",
    "args": ["-y", "gepettobrowser", "mcp"],
    "env": { "GEPETTO_EXECUTABLE_PATH": "/usr/bin/google-chrome-stable" } } } }
```

```toml
# ~/.codex/config.toml
[mcp_servers.gepetto]
command = "/absolute/path/to/npx"
args = ["-y", "gepettobrowser", "mcp"]
```
</details>

**Library:**

```js
const { BrowserController } = require('gepettobrowser/control');

const ctl = new BrowserController({ headless: true, capture: true });
const page = await ctl.open('https://shop.example');           // digest with element refs
const res  = await ctl.act({ type: 'click', ref: 'e4' });       // outcome + activity + digest
console.log(res.activity.network.entries, ctl.console().entries);
await ctl.close();
```

## See what the page does

![act, then watch the requests and errors it caused](assets/demo-act.png)

One click on *Add to cart* shows the `POST /api/cart` it fired, the shape of the response, and the
exception the page threw. Of the ten requests so far, only the four that matter are listed; the rest
(images, the script, the stylesheet, an analytics beacon, a tracker) are counted under `hidden`.

![inspect one request and the page's code](assets/demo-inspect.png)

`gepetto net <id>` gives a request's headers, body and a ready-to-run **curl** (with the session's
cookies). `gepetto code` lists every script and stylesheet, the detected stack, and embedded state
like `__NEXT_DATA__`; `code search "<text or /regex/>"` searches the live DOM plus every captured
script and API response.

Everything is also stored on disk, deduplicated and greppable:

```
~/.gepetto/sessions/<id>/
  network.ndjson   one line per request      bodies/   API/HTML/JS/CSS bodies (content-addressed)
  console.ndjson   errors + exceptions       dom/      raw + condensed DOM snapshots
```

## Share one browser

![one session, many drivers](assets/how-it-works.svg)

The process that starts a session publishes it on `127.0.0.1` behind a random token; anything else
attaches by session id. Run `gepetto live https://myapp.local --headed`, click around, and your agent
sees exactly what your clicks triggered — or let the agent drive the window while you watch.

Sandboxed shell agents such as Codex can use the CLI without MCP: start the session in a normal
terminal (`gepetto live <url> --detach --session codex`), then attach with `--session codex`. The
sandbox needs localhost access (`sandbox_workspace_write.network_access = true` in Codex).

## Stealth

![bot.sannysoft.com: 31 of 31 checks passed](assets/stealth.png)

A fingerprint is one coherent, optionally seeded identity (`fingerprint: true`, a seed, or your own
profile). It covers `navigator.*`, WebGL vendor/renderer (a real GPU, never SwiftShader), canvas and
audio noise, screen metrics, plugins, permissions, UA + Client Hints + `userAgentData` — identically
in the main thread, Web Workers and SharedWorkers — plus WebRTC leak blocking, GeoIP timezone
alignment for proxies, and a Cloudflare Turnstile clicker.

## Reference

<details>
<summary><b>CLI commands</b></summary>

```
gepetto fetch  <url>                       HTTP-first fetch (no browser) → text
gepetto browse <url> [--screenshot FILE]   one-shot stealth browser → visible text
gepetto view   [url] [--os windows|mac|iphone]   visible window for manual use
gepetto mcp                                run the MCP server on stdio

gepetto live [url] [--session ID] [--headed] [--proxy URL] [--detach] [--idle MIN]
gepetto open <url> [--find TEXT]           (auto-starts a background session)
gepetto digest [--find TEXT]
gepetto act click|hover <ref> | type <ref> "<text>" [--submit] | select <ref> "<option>"
            key <Key> | scroll [down|up] | navigate <url> | back | forward | reload | wait <ms> | tab <n>
gepetto net [<id>] [--since N] [--match RE] [--errors] [--all] [--kinds api,ws] [--scope session]
gepetto console [--level error|warn|log|all] [--since N]
gepetto code [summary | dom [--selector S] | get <s3|c1|#42|dom|url> | search "<text|/re/>"]
gepetto read [--html] [--eval "<js>"]  ·  gepetto shot [file]  ·  gepetto extract --fields '<json>'
gepetto sessions [--prune]  ·  gepetto close
common flags: --session ID · --json
```
</details>

<details>
<summary><b>MCP tools (14)</b></summary>

| Tool | Does |
|------|------|
| `gepetto_fetch` / `gepetto_browse` | One-shot HTTP fetch / stealth browser read |
| `gepetto_open` | Open a URL in a persistent session (`headed: true` for a visible window) → digest |
| `gepetto_digest` | Re-read the page; `find` filters all interactive elements |
| `gepetto_act` | click · type · select · hover · key · scroll · navigate · back · forward · reload · wait · tab → outcome + activity + digest |
| `gepetto_network` | Important requests; `since` tails new ones; `id` → headers, body, curl |
| `gepetto_console` | Console errors/warnings and uncaught exceptions |
| `gepetto_code` | `summary` · `dom` · `get` · `search` over the page's code and captured bodies |
| `gepetto_extract` / `gepetto_export` | Selector-map scrape → CSV / XLSX / JSON / NDJSON / TSV |
| `gepetto_screenshot` / `gepetto_read` | PNG · raw text/HTML · evaluate JS |
| `gepetto_sessions` / `gepetto_close` | List / close sessions |

Environment: `GEPETTO_EXECUTABLE_PATH`, `GEPETTO_PROXY`, `GEPETTO_HEADED=1`, `GEPETTO_CAPTURE=0`,
`GEPETTO_IDLE_MINUTES` (default 60), `GEPETTO_HOME` (default `~/.gepetto`), `GEPETTO_KEEP_DAYS`
(default 7), `GEPETTO_LOG_LEVEL`. The server never reads a `proxies.txt` from its working directory.
</details>

<details>
<summary><b>init() options</b></summary>

```js
const { init } = require('gepettobrowser');
const { browser, page } = await init({
  headless: false,            // default false (headed via xvfb on Linux unless disableXvfb)
  executablePath: undefined,  // or GEPETTO_EXECUTABLE_PATH
  screenSize: { width: 1280, height: 720 },
  fingerprint: false,         // true | seed | profile object
  turnstile: false,           // background Cloudflare Turnstile solver
  proxy: null,                // 'http://user:pass@host:port' · object · false (no proxy, ignore proxies.txt)
  proxies: null, proxyRotation: 'roundrobin', geoip: false,
  captcha: false,             // 2Captcha extension — see below
  captchaExtension: undefined, captchaApiKey: undefined,
  blockResources: false, blockAds: false,
  userDataDir: null, cookiesFile: null,
  inputDelay: 0,              // seconds of human mouse movement before click/type
  silent: false, logLevel: 'info', logger: null,
});
await page.gotoResilient('https://example.com'); // retries + challenge wait
```

The page also gets `findAdaptive` (self-healing selectors), `saveCookies` / `loadCookies`,
`realClick` (ghost-cursor), and `exportData`. Types ship in the package.

**2Captcha:** the solver extension isn't bundled (it has no open-source license). Download it from
[rucaptcha/2captcha-solver releases](https://github.com/rucaptcha/2captcha-solver/releases), unzip,
and pass `captchaExtension: '/path/to/folder'` with `captchaApiKey` (or `TWOCAPTCHA_API_KEY`).
</details>

<details>
<summary><b>Optional AI agent and data export</b></summary>

```js
const { aiScrape } = require('gepettobrowser/ai');   // needs @anthropic-ai/sdk + ANTHROPIC_API_KEY
const res = await aiScrape({ url, prompt: 'list the products as {name, price}', export: 'xlsx' });
// res.data · res.usage · res.cost · res.file

const { saveData } = require('gepettobrowser/export'); // zero-dependency CSV / XLSX / JSON / NDJSON
await saveData(rows, { format: 'xlsx', dir: './out', organize: 'domain/date' });
```
</details>

## Development

```sh
npm run test:unit   # browser-free unit tests (what CI runs)
npm test            # headless smoke test
```

See [CONTRIBUTING.md](CONTRIBUTING.md), [SECURITY.md](SECURITY.md) and [CHANGELOG.md](CHANGELOG.md).

## Responsible use

This is a browser automation tool. You're responsible for following the terms of the sites you
visit and the laws that apply to you. Captured sessions contain cookies and response bodies — treat
`~/.gepetto` like a browser profile.

## License

[AGPL-3.0-only](LICENSE) © 2025–2026 redhatzx. The launch layer started as a fork of
[puppeteer-real-browser](https://github.com/zfcsoftware/puppeteer-real-browser) (MIT) — see
[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).