## Review: [alejandroqh/browser39](https://github.com/alejandroqh/browser39)
**What it is:** A Rust headless browser for AI agents — fetches pages, executes JS, persists sessions/cookies, and returns token-optimized Markdown. Exposed as a CLI, an MCP server (stdio + HTTP), a JSONL watch-mode subprocess, an npm package, and (since v1.8.0) an embeddable Rust library. Apache-2.0, single binary, no Chrome dependency.
### What's good 👍
- **Genuinely lightweight architecture.** It embeds V8 via `deno_core` instead of shelling out to Chrome, so the Linux x64 release binary is 52,507,112 bytes (~52 MB) — matching the README claim — vs. ~280 MB for Playwright/Puppeteer bundles ([README](https://github.com/alejandroqh/browser39), [release assets](https://api.github.com/repos/alejandroqh/browser39/releases)).
- **Thoughtful token-efficiency design.** The core loop — first fetch returns a list of content sections with token estimates, then the agent re-fetches only the section it needs — is a genuinely good answer to "dump the whole page at the LLM." Heading auto-expand, RSS/Atom feed→Markdown rendering, and a 4-bytes-per-token estimator are all implemented ([README](https://github.com/alejandroqh/browser39), [CHANGELOG v1.6.3](https://github.com/alejandroqh/browser39/blob/main/CHANGELOG.md)).
- **Unusually good docs for a solo project.** The README's comparison table is honest (e.g., "no XPath, no `:contains()`"); [docs/selectors.md](https://github.com/alejandroqh/browser39/blob/main/docs/selectors.md) is a genuinely useful cheat sheet that even documents current limitations; the MCP tool descriptions are decision-grade (USE WHEN / NOT WHEN / effect class / common failures with repair moves); the CHANGELOG is detailed ([server.rs source](https://raw.githubusercontent.com/alejandroqh/browser39/main/src/mcp/server.rs)).
- **Code hygiene.** ~486 KB of Rust, clippy-clean (11 `collapsible_if` + 1 `new_without_default` fixed in v1.8.0), a snapshot test locking the MCP tool schema, 12 unit tests for feed parsing, GPG-verified signed release commits ([CHANGELOG](https://github.com/alejandroqh/browser39/blob/main/CHANGELOG.md), [releases](https://github.com/alejandroqh/browser39/releases)). The v1.6.0 migration from `boa_engine` to `deno_core` (real V8, JIT) was a solid engineering call ([CHANGELOG](https://github.com/alejandroqh/browser39/blob/main/CHANGELOG.md)).
- **Good distribution story:** `cargo install`, npm (`@aquintanar/browser39`, 6 versions, 0 deps, published on npm), prebuilt binaries for 6 platforms, and runnable Python/TS/Rust examples ([npm page](https://www.npmjs.com/package/@aquintanar/browser39)).
### What's concerning 👎
1. **An unfixed MCP protocol bug that breaks modern clients.** [Issue #3](https://github.com/alejandroqh/browser39/issues/3) (open since Aug 6, 2026, no response visible): the stdio MCP server **exits with rc=1** when it receives an unknown JSON-RPC method (e.g., `server/discover` sent by Go-SDK ≥ 1.7 clients like Charm Crush) instead of replying `-32601 Method not found`. The reporter's repro is one line: `printf '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{}}\n' | browser39 mcp`. This kills the whole connection for newer clients — a real protocol-compliance defect in the tool's primary integration surface.
2. **The project appears dormant.** Created 2026-04-03, last push 2026-05-20 — over 3 months before the issue above was filed, and the issue has sat unanswered ([GitHub API repo metadata](https://api.github.com/repos/alejandroqh/browser39)).
3. **"Headless browser" overstates it.** It's an HTTP client + HTML parser (`scraper`/`html5ever`) + sandboxed JS DOM shim — there is **no layout/rendering engine and no screenshots**. The docs themselves admit "selectors run against the static parsed HTML, not the live DOM" ([docs/selectors.md](https://github.com/alejandroqh/browser39/blob/main/docs/selectors.md)), and the changelog calls SPA support only "the foundation" ([CHANGELOG](https://github.com/alejandroqh/browser39/blob/main/CHANGELOG.md)). So "handles modern sites" in the README should be read as "handles modern static-ish sites"; heavy client-rendered SPAs are out of scope.
4. **No CI.** There is no `.github/workflows` directory, so tests and clippy run only locally ([file tree](https://github.com/alejandroqh/browser39/tree/main/.github/workflows) — 404).
5. **Benchmark claims are self-reported.** The "196 tokens vs ~14,600 / 75× less" comparison in the README is one Wikipedia test case run by the author, not an independent benchmark; take the exact numbers as marketing.
6. **Tiny adoption:** 87 stars, 10 forks, 0 pull requests, npm 0 dependents, and release download counts in the tens (62 for linux-x64) ([GitHub API](https://api.github.com/repos/alejandroqh/browser39), [releases API](https://api.github.com/repos/alejandroqh/browser39/releases)).
### Bottom line
A well-crafted, focused tool with a genuinely clever token-efficiency design and unusually good documentation — arguably the best-documented small MCP browser project I've seen. But it's a single-maintainer project that's been silent for ~3 months, with an open, reproducible MCP protocol bug that blocks compatibility with newer MCP clients, no CI, and a "browser" label that overpromises relative to its static-HTML + JS-shim reality. **Worth watching (or forking to fix issue #3), but not yet production-ready for teams depending on MCP client interoperability.**
**Sources opened:** [README](https://github.com/alejandroqh/browser39) · [Issue #3](https://github.com/alejandroqh/browser39/issues/3) · [Cargo.toml](https://github.com/alejandroqh/browser39/blob/main/Cargo.toml) · [CHANGELOG.md](https://github.com/alejandroqh/browser39/blob/main/CHANGELOG.md) · [docs/selectors.md](https://github.com/alejandroqh/browser39/blob/main/docs/selectors.md) · [src/mcp/server.rs](https://raw.githubusercontent.com/alejandroqh/browser39/main/src/mcp/server.rs) · [src/core/html_to_md.rs](https://raw.githubusercontent.com/alejandroqh/browser39/main/src/core/html_to_md.rs) · [Releases](https://github.com/alejandroqh/browser39/releases) · [npm package](https://www.npmjs.com/package/@aquintanar/browser39) · [Repo metadata API](https://api.github.com/repos/alejandroqh/browser39) · [Releases API](https://api.github.com/repos/alejandroqh/browser39/releases)
1navigatehttps://github.com/alejandroqh/browser39
2navigatehttps://github.com/alejandroqh/browser39/tree/main/src
3navigatehttps://github.com/alejandroqh/browser39/issues
4navigatehttps://github.com/alejandroqh/browser39/issues/3
5navigatehttps://github.com/alejandroqh/browser39/blob/main/Cargo.toml
6navigatehttps://github.com/alejandroqh/browser39/tree/main/src/core
7navigatehttps://github.com/alejandroqh/browser39/commits/main/
8navigatehttps://github.com/alejandroqh/browser39/blob/main/src/lib.rs
9navigatehttps://github.com/alejandroqh/browser39/blob/main/CHANGELOG.md
10navigatehttps://github.com/alejandroqh/browser39/tree/main/src/mcp
11navigatehttps://github.com/alejandroqh/browser39/releases
12navigatehttps://github.com/alejandroqh/browser39/blob/main/src/mcp/server.rs
13navigatehttps://github.com/alejandroqh/browser39/tree/main/docs
14navigatehttps://raw.githubusercontent.com/alejandroqh/browser39/main/src/mcp/server.rs
15navigatehttps://raw.githubusercontent.com/alejandroqh/browser39/main/src/core/html_to_md.rs
16navigatehttps://api.github.com/repos/alejandroqh/browser39/languages
17navigatehttps://raw.githubusercontent.com/alejandroqh/browser39/main/src/mcp/server.rs
18navigatehttps://www.npmjs.com/package/@aquintanar/browser39
19navigatehttps://raw.githubusercontent.com/alejandroqh/browser39/main/docs/selectors.md
20navigatehttps://github.com/alejandroqh/browser39/tree/main/.github/workflows
21navigatehttps://api.github.com/repos/alejandroqh/browser39
22navigatehttps://api.github.com/repos/alejandroqh/browser39/releases
23navigatehttps://github.com/alejandroqh/browser39/tree/main/examples