Skip to main content

onyxweb-server

Serves onyxweb's browser to agents over MCP and to programs over HTTP.

Requires Python 3.11+ and the browsers from onyxweb --install, which fetches both the headless shell and full Chrome. The default engine is the full Chrome; engine="shell" is lighter and faster, and many sites block it. Running in Docker, or as BBOT does, needs sandbox=False: see "Docker and BBOT" in the onyxweb README.

HTTP

uv tool install "onyxweb-server[http]"       # or: pip install "onyxweb-server[http]"
onyxweb-server http --port 8000              # binds 127.0.0.1
Route Returns
POST /fetch the whole page as RenderResult.snapshot() JSON; RenderResult.load rebuilds it
POST /screenshot the image itself, as image/png, image/jpeg or image/webp
POST /fetch_all {"snapshot": {...}, "image": "<base64>", "format": "png"}: the page and its image from one visit
POST /batch NDJSON in the order given, one line per URL: {"url", "snapshot"}, or {"url", "error"} for a URL that failed
GET /health {"status": "ok", "engines": {"full": true}, "stats": {...}}: each engine built so far and whether its Chrome is alive, plus counters (requests, failures by kind, retries, restarts, pages held, egress refusals)

Every body is a JSON object sent with Content-Type: application/json. A route accepts the fields below and refuses any other by name:

Route Fields
/fetch url, engine, wait_ms, timeout_ms, wait_until, headers, block_urls, bypass_anti_bot
/screenshot url, engine, wait_ms, timeout_ms, wait_until, headers, full_page, format, quality, viewport
/fetch_all the /fetch fields, plus full_page, format and quality
/batch urls in place of url, and the other /fetch fields, applied to every URL

Send Accept-Encoding: zstd to get JSON compressed (about 9x on a 7 MB page). A batch is compressed as it is written, and an image never is. scripts, post_load_scripts and actions are refused with a 400, because the server never runs caller-supplied JavaScript, and so is a private, loopback or link-local address. The server holds nothing between requests.

A failure is {"error": {"kind": ..., "message": ..., "url": ...}} with a status from the kind:

Status Kinds
400 invalid_url, invalid_config, post_load_script, invalid_request (a refused option or URL), refused_field
401 unauthorized
413 too_large
422 invalid_request (a body that is not a JSON object, or a missing or unknown field)
502 cdp, io
503 chrome_exited, queue_timeout, launch_failed, chrome_not_found
504 navigation_timeout, timeout
500 internal, and any kind not listed

One bad URL never fails a batch. Its line carries the same error object, with the same kind, and the status stays 200. A batch that is itself refused, with 0 URLs or more than MAX_BATCH, is a 400.

Set ONYXWEB_SERVER_TOKEN to require Authorization: Bearer <token> on every route but /health. Without a token, onyxweb-server http listens on loopback only and exits 1 for any other --host. The server has no other authentication, so put a reverse proxy in front before exposing it.

Limits and egress

Both front-ends share one core, so they refuse the same things. ONYXWEB_SERVER_* variables set the limits:

Variable Default Sets
MAX_PAGES 50 pages held per session
MAX_STORE_BYTES 268435456 bytes of html held; the least recently used page goes first
MAX_PAGE_BYTES 20971520 largest page or image one call may return
MAX_BATCH 50 URLs in one batch
MAX_WAIT_MS 30000 longest settle after the page loads
MAX_TIMEOUT_MS 60000 longest navigation budget
QUEUE_MS 10000 longest a request waits for a free tab, then queue_timeout
CONCURRENCY 4 tabs per engine
EGRESS 1 0 turns the egress proxy off

A caller may set only engine, wait_ms, timeout_ms, wait_until, extra headers, block_urls and bypass_anti_bot, each within a ceiling. A Chrome that dies mid-call is replaced and the call retried once.

The browser reaches the network through a proxy the server runs on 127.0.0.1. It resolves every host itself, refuses the request unless every answer is a public address, and connects to the address it checked. So a redirect, a rebinding host, or a page's own script cannot reach a private, loopback or link-local address. A full Chrome opens one connection to www.google.com per tab when it starts. The proxy allows it, since the address is public. A refused navigation is refused like a private URL: a 400 over HTTP, a tool error over MCP. A screenshot of a refused plain-HTTP page shows the proxy's refusal text, because an image carries no status. The proxy adds no authentication of its own: it listens on loopback only, and it can reach only public addresses.

MCP

onyxweb-server mcp lets an agent such as Claude Code fetch a page once, then look, find and read it in pieces, so a large page never floods its context. Pages stay in memory for the session.

uv tool install "onyxweb-server[mcp]"   # or: pip install "onyxweb-server[mcp]"
claude mcp add onyxweb -- onyxweb-server mcp    # register it, then restart the session
Tool Returns
fetch an id, the final URL, status, title and an overview of the page; with screenshot=true, also an image of it from the same visit
batch many URLs at once: an id per page in the order given, and a FAILED line for each URL that failed
screenshot an image of the page, taking full_page, format, quality and viewport
pages every page fetched this session, newest first
overview the count and size of every bucket
query several questions at once: each gets the best-matching passages of the page text; omit id to search all pages
find where an exact string or a regex matches, in the page text and every bucket; omit id to search all pages
read one record's whole content, in pieces of at most 6,000 characters
page_text what the page displays, in pieces of at most 6,000 characters

Fetch a page, then ask everything you need in one query call. Ranking counts word overlap, so it finds passages that use your words and misses ones that only mean the same thing, and it puts navigation, link lists and bare headings below prose (Boilerpipe's link-density and word-count features). Use find for an exact string, a regex or a bucket. Every tool caps its output and names the offset to continue from. Each marks page text as untrusted, and puts that label above the page's title and URL, which the page chose. A page that comes back nearly empty gets a note suggesting engine="full" or a longer wait_ms.

fetch and batch take engine, wait_ms, timeout_ms, wait_until, headers, block_urls and bypass_anti_bot. screenshot takes the same except the last 2. The server checks each against its ONYXWEB_SERVER_* limits, and a value outside one fails with the limit named. Headers show in the conversation, so an agent sends only what the user gave it. An image cannot be cut, so screenshot refuses one over 5 MB and fetch leaves it out with a note. Ask for jpeg or a smaller viewport.

Two things are refused outright, with no setting to turn them on. The tools accept no scripts, post_load_scripts or actions. fetch, batch and screenshot refuse any URL whose host resolves to a private, loopback, link-local or otherwise non-public address, and the egress proxy above stops a redirect or a page's own request from reaching one. It speaks stdio only. ONYXWEB_SERVER_MAX_PAGES (default 50) sets how many pages it keeps.

Release files for onyxweb-server 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for onyxweb-server 0.1.0
File Size Uploaded
onyxweb_server-0.1.0.tar.gz 73.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for onyxweb-server 0.1.0
File Interpreter ABI Platform
onyxweb_server-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 110.6 kB

Release files / onyxweb_server-0.1.0.tar.gz

Download URL onyxweb_server-0.1.0.tar.gz
Size 73.4 kB
Tags Source
SHA-256 checksum
How to use checksums
e129470bf8bf5a01350ccf0ec51ce99be45bf5f9171858d68ba1c4bc404e82b8
BLAKE2b-256 checksum
How to use checksums
6f346b18dc5088bbadf89908fef272281b5e5049aa0084302180bd2e7028c6dd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 22, 2026.

Transparency log

Release files / onyxweb_server-0.1.0-py3-none-any.whl

Download URL onyxweb_server-0.1.0-py3-none-any.whl
Size 37.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a9ae606324e37dd8ea1f20c5c9fb2f56b0b55020b7ca34c9805da57215c4ba19
BLAKE2b-256 checksum
How to use checksums
57b8f4940b9740c78ea6b53b271dad638ca9e571a8f5b375ba54d290d9353d97
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 22, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page