Metadata-Version: 2.5
Name: servo-agent
Version: 0.2.0
Summary: An agent-controllable browser over the Servo engine's built-in WebDriver.
Project-URL: Homepage, https://github.com/parker-brown-family/servo-agent
Project-URL: Repository, https://github.com/parker-brown-family/servo-agent
Project-URL: Documentation, https://github.com/parker-brown-family/servo-agent/tree/main/docs
Project-URL: Changelog, https://github.com/parker-brown-family/servo-agent/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/parker-brown-family/servo-agent/issues
Project-URL: Demo, https://servo-agent.brownfamilysports.com
Author: servo-agent contributors
License: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Keywords: agent,browser,llm,mcp,scraping,servo,webdriver
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.10
Requires-Dist: beautifulsoup4>=4.12
Requires-Dist: markdownify>=0.11.6
Requires-Dist: mcp<2,>=1.15
Requires-Dist: requests>=2.31
Requires-Dist: trafilatura!=2.2.0,>=2.1
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Description-Content-Type: text/markdown

# servo-agent

**An agent-controllable browser, built on the [Servo](https://servo.org) engine.**

[![ci](https://github.com/parker-brown-family/servo-agent/actions/workflows/ci.yml/badge.svg)](https://github.com/parker-brown-family/servo-agent/actions/workflows/ci.yml)
[![license](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
[![python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](pyproject.toml)
[![live demo](https://img.shields.io/badge/demo-live-ff8c00.svg)](https://servo-agent.brownfamilysports.com)

> **Status:** pre-1.0 / alpha — the API may change. Feedback and contributions welcome.

![servo-agent — a robot giving life to a web page](https://raw.githubusercontent.com/parker-brown-family/servo-agent/main/web/assets/hero.png)

<sub>Live product page → **[servo-agent.brownfamilysports.com](https://servo-agent.brownfamilysports.com)** — the [`/pc`](https://servo-agent.brownfamilysports.com/pc) kiosk ("Easy as 1-2-3" animation) + the [`/info`](https://servo-agent.brownfamilysports.com/info) page (use cases, quickstart).</sub>

`servo-agent` turns a Servo build into a browser an LLM agent can drive — and,
crucially, _read_. It wraps Servo's built-in **W3C WebDriver** server (a control
surface LLMs already understand) and adds the one thing agents actually need:
`read_page`, which distills the **post-render DOM** into clean markdown, with
scripts, navigation chrome and citation markup removed and links kept absolute.

It runs headless, manages the engine lifecycle for you, and ships as an **MCP
server** so you can drop it straight into Claude Code, Codex, or any MCP client.

```text
            ┌──────────────┐   W3C WebDriver    ┌────────────────┐
  agent ──▶ │  servo-agent │ ─────────────────▶ │   servoshell   │ ──▶ the web
  (MCP)     │  (this repo) │ ◀───────────────── │  (Servo engine)│
            └──────────────┘   DOM → markdown    └────────────────┘
```

## Why

Most "agent browsing" stacks puppeteer Chromium over CDP: heavyweight, hard to
instrument, and easy to bot-detect. Servo is a **memory-safe, embeddable engine
you can own end to end**. Because it already speaks WebDriver, an agent is
immediately on familiar ground — and because you control the engine, you can
extend it at the source (see the [roadmap](docs/roadmap.md)).

The payoff is `read_page`: an agent reads distilled content, not tag soup.

Measured on 2026-10-21 (reproducible — `uv run python bench/read_page_bench.py`):

| Page | Rendered HTML | Distilled markdown | Reduction | `read_page` returns |
|---|---|---|---|---|
| `playwright.dev` docs (Docusaurus SPA) | 134 KB | 5 KB | **27×** | 5 KB |
| Wikipedia, "Web browser" | 597 KB | 36 KB | **16×** | 12 KB (capped) |
| RFC 2616 on `rfc-editor.org` | 2 MB | 452 KB | 4× | 12 KB (capped) |
| Hacker News front page | 34 KB | 17 KB (links kept) | 2× | 12 KB (capped) |

Pages built around navigation and boilerplate shrink the most; a page that is
mostly text (the RFC) or mostly links (Hacker News) shrinks the least. Separately,
`read_page` returns at most 12,000 characters unless you raise `max_chars`, so one
call never floods an agent's context.

## Install

`servo-agent` is not on PyPI yet. Install it from this repository:

```bash
uv tool install git+https://github.com/parker-brown-family/servo-agent
```

`pipx install git+https://github.com/parker-brown-family/servo-agent` works too.
To work from a clone instead, run `uv sync` in it and put `uv run` in front of the
commands below.

You also need a built `servoshell`, Servo's browser binary. Put it on `PATH`, set
`$SERVOSHELL`, or check out [Servo](https://github.com/servo/servo) next to this
repository and build it:

```bash
./mach build -d --media-stack dummy  # -> target/debug/servoshell
```

`servo-agent` looks for it in this order: `$SERVOSHELL`, then `PATH`, then a built
`servo/` checkout beside this repository, or beside any directory above the
installed package (so a project that installs servo-agent, such as
[servo-reader](https://github.com/parker-brown-family/servo-reader), finds a
`servo/` checkout beside itself).

Every tool works with stock Servo except `read_native`, which needs the
`/servo/agent/read` endpoint from the `t3/native-read` branch of
[our Servo fork](https://github.com/parker-brown-family/servo/tree/t3/native-read).
On other builds `read_native` answers `{"available": false}` and `read_page`
does the reading.

## Quick start

```bash
# Prove the whole stack end-to-end (spawns its own headless engine):
servo-agent selftest https://news.ycombinator.com

# Run as an MCP server (stdio):
servo-agent serve
```

Library use:

```python
from servo_agent import ServoBrowser, distill

with ServoBrowser(headless=True) as b:
    b.navigate("https://example.com")
    print(distill(b.read_html(), b.current_url()))   # clean markdown
    rows = b.extract_table("table#stats")            # structured data
    b.screenshot("shot.png")
```

## Tools (MCP)

| Tool | Purpose |
|---|---|
| `open_url` | Navigate; returns title/url once the new document has committed (`settle` to wait out SPA hydration). |
| `read_page` | Post-render DOM → clean markdown (the value-add). |
| `read_native` | In-engine read: `{url, title, text, headings, links}` with no JS round-trip (needs the fork's endpoint, see Install). |
| `find` / `wait_for_selector` | Query / wait for elements by CSS selector. |
| `wait_for_load` | Wait for readyState=complete **and** a quiescent DOM (post-load hydration). |
| `get_errors` | Best-effort JS error capture (uncaught / rejection / failed resource). |
| `click` / `type_text` / `fill_form` | Interaction. |
| `scroll` | Scroll to bottom/top, a selector, or a pixel offset. |
| `extract_links` / `extract_table` | Structured extraction → JSON. |
| `eval_js` | Run JavaScript, get JSON back. |
| `screenshot` / `status` | Capture (optional `width`/`height`/`full_page`) / introspect. |

## Wire into an MCP client

**Claude Code**

```bash
claude mcp add servo-agent -s user -- servo-agent serve
```

From a clone, run it through uv instead:

```bash
claude mcp add servo-agent -s user -- \
  uv run --project /path/to/servo-agent servo-agent serve
```

**Codex** (`~/.codex/config.toml`)

```toml
[mcp_servers.servo-agent]
command = "uv"
args = ["run", "--project", "/path/to/servo-agent", "servo-agent", "serve"]
```

> MCP tools are injected at client startup — restart after adding/changing the
> server. Validate harness edits without a restart via `servo-agent selftest`.

## Observability

Every tool call is recorded to a structured JSONL log — the product feedback loop,
built for agents to consume (not a human dashboard). It captures what only the
server can see: distill extractor + compression ratio, fallbacks, errors, engine
respawns, latency.

```bash
servo-agent events                 # rolled-up digest (rates, latency, distill ratios)
servo-agent events --tail 50       # raw recent events (JSONL)
servo-agent events --tool read_page --errors
```

Log at `$SERVO_AGENT_LOG` (default `~/.servo-agent/events.jsonl`); set
`SERVO_AGENT_LOG=off` to disable. Details in [docs/observability.md](docs/observability.md).

## Known limits

- **Pages Servo never finishes loading.** Many real pages stay at
  `readyState: interactive` in Servo because a web font or analytics beacon never
  completes (servo.org itself does). `open_url` returns once such a page's DOM has
  been still for two seconds and reports `interactive`; the content is readable.
- **Screenshots of those pages time out** after Servo's 10-second limit
  ([#3](https://github.com/parker-brown-family/servo-agent/issues/3)).
- **One session, no saved profile:** no cookies across runs, no tabs, no
  downloads. Login-gated flows still need a fuller browser.
- **`click` → `back()` → `reload()`** in the Python API hits a Servo WebDriver bug
  and answers 500 ([#5](https://github.com/parker-brown-family/servo-agent/issues/5)).
  Each call works on its own.

## Use cases

Worked, runnable examples live in [`examples/`](examples/) and the integration
adapters in [`integrations/`](integrations/):

- **Research source-reader** — distilled markdown for multi-source research.
- **Site QA / pre-deploy checker** — render, screenshot, assert content & links.
- **Page watcher** — poll a page, extract a value, alert on change.
- **Bot-walled scrape fallback** — a JS-rendering engine you own.
- **Structured data extraction** — `extract_table` into a pipeline.
- **Universal "read the live web"** primitive for any agent task.

See [docs/use-cases.md](docs/use-cases.md) for the full write-up.

## Develop

```bash
uv sync --extra dev
uv run pytest            # unit tests always run; integration auto-skips w/o a binary
uv run ruff check .
```

## Contributing

Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) for dev setup
and the workflow. On your first pull request you sign the [CLA](CLA.md) once, by
posting the comment the CLA bot asks for. Please be kind
([Code of Conduct](CODE_OF_CONDUCT.md)) and report vulnerabilities privately
([SECURITY.md](SECURITY.md)). Changes are logged in [CHANGELOG.md](CHANGELOG.md).

## License

Apache-2.0 — see [LICENSE](LICENSE) and [NOTICE](NOTICE). Servo itself is a
separate project under MPL-2.0; `servo-agent` talks to it over WebDriver and does
not vendor its code.
