servo-agent
An agent-controllable browser, built on the Servo engine.
Status: pre-1.0 / alpha — the API may change. Feedback and contributions welcome.
Live product page → servo-agent.brownfamilysports.com — the /pc kiosk ("Easy as 1-2-3" animation) + the /info page (use cases, quickstart).
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.
┌──────────────┐ 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).
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:
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 next to this
repository and build it:
./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, 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.
On other builds read_native answers {"available": false} and read_page
does the reading.
Quick start
# 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:
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
claude mcp add servo-agent -s user -- servo-agent serve
From a clone, run it through uv instead:
claude mcp add servo-agent -s user -- \
uv run --project /path/to/servo-agent servo-agent serve
Codex (~/.codex/config.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.
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.
Known limits
- Pages Servo never finishes loading. Many real pages stay at
readyState: interactivein Servo because a web font or analytics beacon never completes (servo.org itself does).open_urlreturns once such a page's DOM has been still for two seconds and reportsinteractive; the content is readable. - Screenshots of those pages time out after Servo's 10-second limit (#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). Each call works on its own.
Use cases
Worked, runnable examples live in examples/ and the integration
adapters in 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_tableinto a pipeline. - Universal "read the live web" primitive for any agent task.
See docs/use-cases.md for the full write-up.
Develop
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 for dev setup and the workflow. On your first pull request you sign the CLA once, by posting the comment the CLA bot asks for. Please be kind (Code of Conduct) and report vulnerabilities privately (SECURITY.md). Changes are logged in CHANGELOG.md.
License
Apache-2.0 — see LICENSE and NOTICE. Servo itself is a
separate project under MPL-2.0; servo-agent talks to it over WebDriver and does
not vendor its code.
Metadata
Release files for servo-agent 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| servo_agent-0.2.0.tar.gz | 199.8 kB | With provenance | Details for servo_agent-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| servo_agent-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for servo_agent-0.2.0-py3-none-any.whl |
Total release size: 237.3 kB
Release files / servo_agent-0.2.0.tar.gz
| Download URL | servo_agent-0.2.0.tar.gz |
|---|---|
| Size | 199.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
11ef7bc1ffee637c5d13148ed367b6c7c0c25aeb57279ba56450b0333a6fa758
|
|
BLAKE2b-256 checksum How to use checksums |
4be2bf230241aaf1e281e0543550b898e30a7a2e62d6923211b66f55232b9f45
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logRelease files / servo_agent-0.2.0-py3-none-any.whl
| Download URL | servo_agent-0.2.0-py3-none-any.whl |
|---|---|
| Size | 37.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e248607a69c18ad9a2846037d4b7b4aa5fd40f891bb5bee6f828e285eab18d44
|
|
BLAKE2b-256 checksum How to use checksums |
711aeb36e3eb1e089a4313ad492c7958fc23a5b8ae1b2d3cb7cdf1ea12b8276a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logSecurity for servo-agent 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for servo_agent-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.