Manuk is an MCP server that lets a model operate websites through a structured accessibility view of each page (role, accessible name, state, hit-test box and a stable handle per element) rather than screenshots or selectors. It has two backends behind the same tools: Chrome for Testing (the one to use) and an independent browser engine written in Rust (deterministic and forkable, with no Chrome dependency, but behind on real sites; see the scoreboard).
Important
Pre-release. Nothing is tagged or published for this launch; build from source. The engine's status as a browser is under Status.
./scripts/mozjs-fetch.sh # once: pinned SpiderMonkey prebuilt (a clean-clone build needs it)
cargo auditable build --release -p manuk-mcp # or: cargo build --release -p manuk-mcp
claude mcp add manuk -- manuk-mcp --backend chrome--backend chrome drives the pinned Chrome for Testing (under ~/.local/chrome-cft/<pin>/, or MANUK_CHROME) with
its sandbox on. Without the flag, manuk-mcp runs the in-process engine. Thin stdio clients:
python/manuk_client.py (standard library) and ts/manuk.ts (Node
built-ins). Tool-by-backend behaviour: docs/sprint/BACKENDS.md. Security policy and SBOM:
SECURITY.md.
WebMCP. Tools a page registers through document.modelContext.registerTool, declarative toolname forms, and one
synthesized fill-form tool per <form> appear as webmcp_<name> MCP tools with list_changed; a tool whose
description holds paint-hidden text is withheld. Against Chrome's own registry on 6 demo pages the in-process engine
matched tool names on 4 and names plus schemas on 1 (W3.20.b, gate not met; see docs/sprint/JOURNAL.md).
The task suite and the bars were fixed in docs/sprint/SUCCESS.md before any result. Rows:
docs/sprint/SCOREBOARD.tsv. The table is generated by
python3 -I scripts/readme-check.py --emit, and the same script fails if it differs from the TSV (rule: the latest
row per arm, task and run).
chrome_ver=145.0.7632.116, driving model Haiku 5.5, pass count per run (3 runs), from docs/sprint/SCOREBOARD.tsv:
| arm | own 40 tasks, runs 1/2/3 | external 10 tasks, runs 1/2/3 |
|---|---|---|
| Playwright MCP + Chrome | 39 / 40 / 38 of 40 | 8 / 7 / 7 of 10 |
manuk-mcp --backend chrome |
40 / 40 / 40 of 40 | 7 / 8 / 7 of 10 |
| manuk-mcp (in-process engine) | 39 / 39 / 38 of 40 | 7 / 5 / 7 of 10 |
Verdict (docs/sprint/VERDICT.md): Bar 1 met, Bar 2 not met, which SUCCESS.md reads as
"a useful agent tool, but the engine is not the reason". Bar 2 failed on every branch of the decisive-win test: the
hidden-instruction rate is 0 for the in-process engine and also ~0 for Playwright; on tokens the in-process engine
uses more than the best Playwright arm (727 vs 517 in+out per task, W4.B4); the rollback branch measured 0 flips
(0 of 44 failed tasks on the REAL deterministic subset). The Chrome backend therefore ships as the product, and the
in-process engine as the deterministic, forkable research backend. On the REAL deterministic subset (47 tasks) the
in-process engine passes 10-11 tasks, the Chrome backend 15-18 and Playwright 23-25 (W4.A6); the engine cannot yet
drive those forms. On all 102 REAL tasks, with the LLM-judged evals scored by an owner-provided judge
(openai/gpt-oss-120b, validated against positive, negative and agreement controls, W3.18.a.a.b), the tasks passed in
at least 2 of 3 runs are: Playwright 52, the Chrome backend 35, the in-process engine 32 (runs 46/49/52, 35/36/40 and
35/31/30; docs/sprint/w318/real-judge-report.tsv).
Measured against the pinned Chrome for Testing (chrome_ver=145.0.7632.116), 3 runs per row, local fixture pages so the
numbers are not shared-cache artefacts. Method, raw rows and what is not claimed:
docs/sprint/FOOTPRINT.md.
| axis | in-process engine | Chrome | reading |
|---|---|---|---|
| memory, 1 page (PSS) | 31 MB | 399-407 MB | 0.08× |
| memory per page, 50 pages | 12.4 MB (one process each), 4.9 MB (forks of one checkpoint) | 20.1 MB (tabs in one browser) | 0.62×, 0.24× |
| new page from a checkpoint | 36-43 ms (fork, replay-based) |
65-70 ms (new context and navigate) | — |
| cold start to first observation | 55-57 ms | 305-314 ms | 0.18× |
| real sites, per page (5 paired URLs) | — | — | PSS 0.30-0.31×; wall clock slower (1.66-1.96× this run, not settled) |
The memory gap narrows as pages accumulate, because Chrome tabs share most of their cost. On live sites the engine is
smaller, but its single-page load time is behind Chrome's, mostly in subresource fetch
(docs/sprint/PERF.md).
Software agents that operate websites today either interpret screenshots or depend on DOM selectors that change with each deployment. WebMCP offers a semantic tool surface only on sites that adopt it, and WebDriver BiDi provides standard actuation but no accessibility surface. Because Manuk parses the DOM and solves layout itself, it derives, without executing page JavaScript, a semantic view of any page: each element's role, accessible name, interaction state, hit-test box and a stable address. An agent can perceive a page as structured data, act through standard WebDriver BiDi input that dispatches real DOM events, and verify the outcome, on sites that have made no provision for agents.
The same engine core serves three front ends: a desktop browser (shell), a headless agent browser (agent) and the
WebAssembly demonstration (demo).
Measured for the v1.2.0 release on a corpus of real sites rendered by both Manuk and Chromium from identical
snapshots. 76 of 265 corpus sites were reachable by the reference Chromium (the remainder are blocked by bot
protection for the reference itself) and all 76 are scored. "Full" counts a site that hangs the driver as zero;
"measured-only" reports quality where a score was obtained. Method and per-site data:
docs/RELEASE-v1.2.0.md.
| axis | measures | measured-only | full | exit threshold |
|---|---|---|---|---|
| M1 render | node placement relative to Chromium (shape ≥ 0.75), no overlap or overflow | coverage 89.9%, shape 72.1% | — | ≥ 95% of in-scope sites |
| M2 drive | perceive → address → act on page targets | 78.9% (n = 60) | 62.3% | ≥ 95% render ∧ function |
| a11y | accessibility tree role/name/state agreement with Chromium (F1) | 82.0% (n = 65) | 70.1% | ≥ 90% of nodes |
Web Platform Tests. About 496,000 WPT subtests pass in the project's internal harness (tests/wpt), tracked as a
monotonic ratchet across releases. Results have not been submitted to wpt.fyi, and the figure is an internal
measurement, not a published conformance claim. JavaScript conformance is provided by SpiderMonkey; Test262 results
are not separately reported.
Release history: v1.0.0 (2026-08-21, notes), v1.1.0
(notes), v1.2.0 (notes). Live, generated project state:
STATUS.md.
| component | crate | implementation | upstream (license) |
|---|---|---|---|
| Networking | engine/net |
reused | hyper, rustls, tokio (MIT / Apache-2.0) |
| HTML parsing | engine/html |
reused | html5ever (MIT / Apache-2.0) |
| DOM | engine/dom |
own | — |
| Style | engine/css |
reused, embedded unmodified | Stylo, the cascade used by Servo and Firefox (MPL-2.0) |
| Layout | engine/layout |
own (block, inline, float, table, positioning); flexbox and grid via Taffy | Taffy (MIT) |
| Text | engine/text |
reused | parley, swash (MIT / Apache-2.0) |
| JavaScript | engine/js |
reused, bound through a JsRuntime trait |
SpiderMonkey via mozjs (MPL-2.0) |
| Media | engine/media |
own demux; decode via upstream | symphonia, openh264 |
| Accessibility | engine/a11y |
own | — |
| Paint | engine/paint |
own display list; rasterization via tiny-skia | tiny-skia (BSD-3-Clause) |
| Compositor | engine/compositor |
own | — |
| Pipeline | engine/page |
own | — |
| Automation | bidi |
own WebDriver BiDi endpoint | — |
| Agent | agent |
own | — |
SpiderMonkey and Stylo are configured and bound, never patched internally. The JIT, garbage collector and sandbox are
upstream code. All page content runs in a single process today; one OS process per tab is a decided design scheduled
for the security phase. The breakdown of each crate, and the boundary with upstream code, is in
docs/loop/HORIZON.md and docs/SPEC.md.
Manuk implements WebDriver BiDi, the W3C cross-browser automation protocol. It does not implement the Chrome DevTools Protocol, which is specific to Chromium and not a standard.
| BiDi module | implemented commands and events | state |
|---|---|---|
session |
new, end, status, subscribe, unsubscribe |
implemented |
browsingContext |
create, close, getTree, navigate, traverseHistory, setViewport, captureScreenshot; events contextCreated, contextDestroyed, navigationStarted, domContentLoaded, load |
implemented |
input |
performActions |
implemented |
script |
evaluate, callFunction |
partial |
network, storage, log, emulation, permissions |
— | not implemented |
Client libraries (Puppeteer, Selenium, Playwright's BiDi channel) have not yet been tested against this endpoint.
Requirements: a Rust toolchain; SpiderMonkey's build prerequisites for the spidermonkey feature. Linux x86_64 is the
verified platform; macOS and Windows build in CI but are not verified on hardware.
# Render a page to PNG, headless (no GPU or display required)
cargo run -p manuk-shell --no-default-features -- render https://example.com/ -o out.png --width 800
# Desktop browser window (winit + wgpu)
cargo run -p manuk-shell -- browse https://example.com/
# Headless agent browser (configure a model endpoint in .env, or use a local llama-server)
cp .env.example .env
cargo run -p manuk-agent --bin agent-run -- "What is this page's main heading?" https://example.com/
# JavaScript through SpiderMonkey and the live Stylo cascade. First fetch the pinned SpiderMonkey prebuilt:
# the script checks its sha256 against the pin and, when `gh` is authenticated, runs `gh attestation verify`.
./scripts/mozjs-fetch.sh
cargo test -p manuk-js --features spidermonkey
cargo test -p manuk-css --features stylo
# Tests and the internal WPT harness
cargo test --workspace
cargo run -p manuk-wpt- Rendering: block, inline, flex, grid, table, float and positioned layout; stacking contexts; overflow clipping;
backgrounds, gradients, shadows, transforms; the full Stylo cascade including
@layer, container queries and:has(). - JavaScript platform: DOM, CSSOM and events;
fetchand XHR; ES modules with import maps; custom elements and shadow DOM; mutation, intersection and resize observers; Web Animations; View Transitions; the Navigation API; IndexedDB; Cache API; Web and Service Workers (same thread); Content Security Policy;crypto.subtle. - Media:
<video>playback of H.264 and AAC in MP4; Media Source Extensions partial. - Browser shell: forms and uploads, tabs with hibernation, history, bookmarks, find in page, zoom, RFC 6265 cookies with partitioned storage, session restore, an encrypted credential store with origin-scoped autofill, streaming downloads.
- Agent surface: accessibility tree (role, accessible name, state, focus, geometry) computed without page JavaScript; occlusion-aware hit testing; actions that dispatch real DOM events; a provider-neutral inference interface; page text delivered to models only inside an untrusted-content boundary.
- MCP tools:
navigate,observe(full, viewport or diff; a tree-shaped snapshot with list, row, card and dialog context and frame-merged handles),extract(query)returning only matching passages,find,actwith an effect diff and a dispatch-time hit-test check,typewith real key events,scroll, compositefill_form(fields[])andact(steps[])that stop at the first dialog or navigation,upload,key,drag,dialog, gated downloads,screenshot,storage_state, andhandoff(the agent pauses, a person acts, the agent resumes with a diff of what changed). Per-backend behaviour is indocs/sprint/BACKENDS.md. - Engine-only agent features: every act and navigate reports when the page has settled, read from the engine's own
fetch and timer queues rather than a network-idle guess;
checkpoint,restoreandfork(replay-based, including a cookie and storage snapshot); action receipts listing the storage writes each act made;--zygote-after-loadforks sessions from an already-loaded page (64-way fork verified on a page with a 3.9 MB DOM);--deterministicruns with a logical clock and seeded randomness. Retry-from-checkpoint and best-of-3 forking help on flaky forms, but the same layer over Chrome matches them (W4.C2, W4.C3). - Security defaults (agent mode): an ephemeral in-memory cookie and storage partition unless
--profileis given; nofile:loads from a web document; loopback, RFC 1918 and link-local addresses refused after DNS resolution, on fetch, WebSocket and proxy dials; a per-task origin allowlist enforced on every network channel; values read on one origin refused toward another (including encoded copies); secrets typed from the OS keyring through named handles and redacted from results; text a person cannot see is withheld from the model; SpiderMonkey's optimizing JIT tiers off (baseline tiers kept) and a Landlock filesystem ruleset on the headless binaries. The Chrome backend runs with Chrome's sandbox on;--unsafe-no-sandboxis the only way to turn it off. Each layer is pinned by a gate shown to fail without it (SECURITY.md). - Agent grants:
manuk-mcptakes--read-originsand--write-origins(origin sets for navigation, reads, typing and POSTs), and--confirm=submit,download,navigate-new-origin(actions that needconfirm=true). Credential and payment fields are never typed by the agent (HANDOFF), and out-of-set origins are REFUSED on both the in-process and Chrome backends (the Chrome blocked-iframe socket residual is listed in the gaps). Flags, verdicts and open gaps: docs/sprint/BACKENDS.md.
Gaps on the Phase-0 path: placement fidelity (mean parent-relative shape about 0.69); roughly one in five in-scope
sites not yet reaching a stable, scorable DOM; rich editing (contenteditable, Selection) and IME composition;
WebAuthn; the M2 function certificate; adoption of a platform accessibility bridge. SpiderMonkey faults inside its
own frames cannot be contained in-process until process-per-tab isolation lands. The WebAssembly demo has no
JavaScript because SpiderMonkey does not target WebAssembly.
Out of scope by decision: Encrypted Media Extensions and DRM, WebRTC, WebGPU and heavy WebGL applications, HTTP/3. Manuk does not attempt fingerprint evasion or bot-detection circumvention.
Manuk is developed largely by an autonomous AI coding loop operating under human direction. Each change must pass a
single all-or-nothing verification wall (scripts/verify.sh): build, WPT-derived and purpose-built correctness gates
(about 570), box-geometry probes within ±3 px of headless Chromium, real-site fidelity, JavaScript conformance and
performance floors. A capability counts only once a gate has been shown to fail without it, and any measured
regression is reverted. Work is prioritized by a differential oracle that renders 265 real sites in Manuk and Chromium
and clusters divergences by root cause. The method is documented in docs/loop/ and
CONSTITUTION.MD. Responsibility for every change rests with the repository owner.
External contributions are not being accepted during Phase 0. Vulnerability reporting and the supply-chain notes are
in SECURITY.md; do not report vulnerabilities in public issues.
Mozilla Public License 2.0, as declared in the workspace manifest. Vendored and linked components remain under their own licenses, as listed in Architecture.