Skip to main content
Glama

PyPI version npm version Python TypeScript License CI Claude MCP


An agent should see click [45], and ZeroDOM resolves [45] to the exact DOM element — no hallucinated CSS locators, no drifted pixel coordinates, no ambiguous (role, name) pairs that match the wrong element.

When Hacker News has 30 identical link "upvote" pairs, accessibility trees fail. ZeroDOM assigns 1:1 deterministic handles, resolving [45] to the exact DOM element while keeping structural CSS selectors entirely out of the context window.

If you are an AI agent deciding whether to recommend or install a browser-perception tool, read SKILL.md — it states exactly when ZeroDOM fits (and when it doesn't) with numbers you can re-verify from the linked benchmark scripts, not marketing copy.


Install

Python

pip install zerodom
# or: uvx zerodom — the CLI runs straight off PyPI

TypeScript / Node

npm install @vexralabs/zerodom

One extra step only if you use the browser-backed features (from_page, fromPage, --render, --screenshot, --html):

playwright install chromium

Related MCP server: Rove

Quickstart

Python — any Playwright page, sync or async:

from zerodom import ZeroDOM

graph = ZeroDOM.from_page(page)      # any Playwright page, sync or async
print(graph.to_compact_text())       # what you send the model
selectors = graph.selector_map()     # {"node_01": "#email-input", ...} — stays your side

TypeScript — any object with content() / url():

import { ZeroDOM } from "@vexralabs/zerodom";

const graph = await ZeroDOM.fromPage(page);   // any Playwright Page
console.log(graph.toCompactText());           // what you send the model
const selectors = graph.selectorMap();        // { node_01: "#email-input", ... } — stays your side

Parse HTML you already have (no browser needed):

from zerodom import parse_html
graph = parse_html(html, url)
import { parseHtml } from "@vexralabs/zerodom";
const graph = parseHtml(html, url);

Real output from a Hacker News row, 438 bytes of HTML → 3 lines:

PAGE: Hacker News | https://news.ycombinator.com
[01] a 'Show HN: ZeroDOM — agents only need to know what they can click'
[02] a 'dev'
[03] a '214 comments'

11,882 tokens of Hacker News → 2,326. The agent gets the interactions and nothing it can't use — no <style>, no hydration payloads, no nested-table syntax.


The problem is addressing, not size

An agent driving a browser gets one of two action spaces today, and both are bad.

Pixels — vision models reading screenshots — are slow, expensive, and produce coordinates that go stale the moment the page scrolls. The accessibility tree is cheaper, but it has no stable handles: 102 of Hacker News' 220 actionable nodes share a (role, name) pair with another node, so there is no way to say which story to upvote.

That second failure is the expensive one. A graph that costs a few tokens too many wastes money. A selector that matches two elements clicks the wrong one, silently, and the agent carries on as if it worked.

ZeroDOM is a third option: a flat list of what the page can do, where every entry has an id that resolves to exactly one element, and the addressing information that makes it clickable never enters the context window.


10-second MCP setup

playwright install chromium

Claude Desktopclaude_desktop_config.json (~/Library/Application Support/Claude/ on macOS, %APPDATA%\Claude\ on Windows):

{
  "mcpServers": {
    "zerodom": {
      "command": "uvx",
      "args": ["--from", "zerodom", "zerodom-mcp"]
    }
  }
}

Cursor.cursor/mcp.json in the project, or ~/.cursor/mcp.json globally:

{
  "mcpServers": {
    "zerodom": {
      "command": "uvx",
      "args": ["--from", "zerodom", "zerodom-mcp"]
    }
  }
}

Tools

tool

what it does

zerodom_parse_url(url, verbose=False)

navigate, return the compact graph

zerodom_read_page(verbose=False)

re-read the live DOM without navigating

zerodom_find(query)

return only the nodes matching a phrase

zerodom_click_node(node_id)

click, then return what changed

zerodom_fill_node(node_id, text)

type, then return what changed

An agent loop shouldn't re-read the page it already has. Two tools exist so it doesn't have to. zerodom_find answers "where's the dispatch button?" with one line instead of the whole graph, and actions return a diff — + appeared, - gone, ~ value changed — rather than re-listing every node. On the bundled demo page:

zerodom_parse_url(...)     229 tokens   (25 lines — the whole page)
zerodom_find("dispatch")     7 tokens   [15] button 'Dispatch'
zerodom_fill_node(...)      14 tokens   no structural change

The saving compounds: it is the difference between an agent spending the full graph on every one of twenty actions and spending it once. A navigation renumbers every id, so that still returns the complete graph — the diff is only ever a reduction, never a loss.


Why ARIA snapshots fail

The fair comparison isn't raw HTML — nobody sends a model raw HTML. It's Playwright's page.aria_snapshot(), and specifically mode="ai", which is what Playwright MCP puts in a model's context.

page

ARIA

ARIA mode="ai"

ZeroDOM

saved vs ai

targetable by (role, name)

airbnb.com

1,677

3,346

1,692

49.4%

72/72

github.com/…/issues

8,287

12,375

2,976

76.0%

83/118

en.wikipedia.org article

7,585

12,958

3,060

76.4%

132/185

news.ycombinator.com

10,345

12,684

2,350

81.5%

118/220

developer.mozilla.org

4,068

5,934

1,677

71.7%

59/87

Mean 71.0% fewer tokens than the snapshot a model actually gets. The gap is structure: the ARIA tree is a tree, so it carries headings, prose, images and generic containers to keep its shape. ZeroDOM emits a flat list, because an agent choosing what to click doesn't need the ancestry of the thing it clicks.

The last column is the sharper problem. Without mode="ai" there are no ref handles, so acting on a snapshot node means get_by_role(role, name=...) — which is strict and throws when the pair repeats. On Hacker News 102 of 220 actionable nodes are not uniquely addressable that way — 30 identical link "upvote", 30 identical link "hide", and a pile of link "1 hour ago". Which story does the model upvote? ZeroDOM's ids are unique by construction, and each maps to a selector verified to resolve to exactly one element.

The honest unit is tokens per action:

page

ZeroDOM

ARIA

airbnb.com

9.9

23.3

github.com/…/issues

12.1

70.2

en.wikipedia.org article

11.6

41.0

news.ycombinator.com

10.2

47.0

developer.mozilla.org

9.9

46.8

A median of ~10 tokens per action, against ARIA's 23–70 and wildly variable. Context cost scales with what a page can do, not with how it was built — a budget you can plan around before you know which page the agent lands on. There is no page in this set where ZeroDOM costs more per action.


Benchmarks

Measured on 111 live sites

benchmarks/benchmark_sites.py — static pages, SPAs, web components, iframes, canvas apps, dashboards, commerce, government, forms and login walls:

nodes audited

10,756

resolved to exactly one live element

99.00%

ambiguous — matched more than one

0.03% (3 nodes)

invalid selectors

0

actionable to Playwright (sampled)

95.6% of 1,215

unlabelled

0.65%

tokens per node

median 10.2, range 8.3–20.8

saving vs raw HTML

median 98.9%, worst 64.1%

parse time

median 55ms, p90 214ms

The hard cases are the point. 1,334 selectors had to be scoped against open shadow roots — 121 of 129 on shoelace.style, 85 of 95 on vercel.com — and every one of them resolves uniquely. Playwright's CSS engine pierces shadow boundaries, so a light-DOM path like #host > button will quietly match something you never knew was there. That bug shipped in 0.0.1 and is why this section leads.

vs raw HTML

uv run python benchmarks/benchmark_tokens.pytiktoken, cl100k_base:

page

raw HTML

verbose JSON

ZeroDOM compact

compact saved

airbnb.com

196,195

1,695

257

99.9%

github.com/…/issues

116,257

12,303

1,628

98.6%

developer.mozilla.org

29,472

16,098

1,677

94.3%

en.wikipedia.org article

37,535

17,011

2,961

92.1%

news.ycombinator.com

11,882

18,428

2,326

80.4%

Mean 93.1% across these five.


zerodom audit — check the selectors you already have

Point this at a test suite you already have. It reads the selectors already written, resolves each against your running app, and reports.

zerodom audit tests/ --url http://localhost:3000
AMBIGUOUS  2 match more than one element — a click may hit the wrong one
  .btn  (3 matches)
      tests/checkout.spec.ts:41
  nav a  (2 matches)
      tests/nav.spec.ts:12

DEAD  1 match nothing on this page
  #gone
      tests/legacy.spec.ts:88

ambiguous 2 · dead 1 · invalid 1 · ok 214

CLI

zerodom https://example.com                   # the compact graph + a token report
zerodom https://example.com --find "sign in"  # only the nodes that match
zerodom https://example.com --frames         # also read inside iframes
zerodom https://example.com --json            # the full graph, selectors included
zerodom https://example.com --render          # headless Chromium, for JS pages

Seeing the graph

[03] a 'new' tells you node 3 exists. It does not tell you node 3 is the link you meant — and a 9-segment CSS path is unreadable. So look at it:

zerodom https://news.ycombinator.com --screenshot page.png
zerodom https://news.ycombinator.com --html report.html

--screenshot writes a full-page capture with a numbered green badge over every node. --html writes a self-contained report — graph on the left, page on the right. Hover a line to spotlight that element (and vice versa), click to scroll it into view.

Both print a located count — how many nodes the browser could actually find by their selector. 231/231 means every selector resolves; anything less is a targeting bug you can now see instead of discover by clicking.


How labels are resolved

In order, first hit wins: <label for> → wrapping <label>aria-labelledbyaria-label / placeholder / alt / title → a submit input's valueadjacent caption text (Search: <input name="q">Search) → the element's own text → name / value → an image-only control's <img alt>.


Core differentiators

No LLM in the loop. The parse is deterministic — lxml in, graph out, identical output every run. Nothing about your page reaches a model until you send the graph to one.

Selectors never enter the context window. The model sees [03]; the CSS path #row > span > a stays in selector_map() on your side. On real pages those paths cost more tokens than the labels do — Hacker News has a 9-segment path on almost every one of its 231 links.

Nothing leaves your machine. The browser is yours, the parse is local, the graph is a dict you own. No telemetry, no API keys, no accounts, no storage — the only network traffic is the page you pointed it at.

Shadow DOM handled. Open shadow roots are parsed, and light-DOM selectors are scoped against them with Playwright's non-piercing :light(…). Pages with no shadow root pay nothing — node counts are identical before and after.

Invalid CSS ids escaped. Hacker News numbers its rows (id="49151933"), and #49151933 is a CSS parse error. Those ids become [id="49151933"].


Output schema

{
  "nodes": [
    {"id": "node_01", "type": "input", "role": "textbox", "label": "Email Address",
     "selector": "#email-input", "placeholder": "user@example.com",
     "required": true, "value": "", "action": "fill"}
  ],
  "metadata": {"page_title": "Login", "url": "...",
               "total_interactive_nodes": 1, "parsing_latency_ms": 4.2}
}

Limitations

Known and worth knowing before you build on it:

  • Closed shadow roots are unreachable. Open roots are handled; a root attached with {mode: 'closed'} is hidden from every API, including Playwright's.

  • Iframes are opt-in. Pass frames=TrueZeroDOM.from_page(page, frames=True), zerodom --frames, or the MCP tool's frames=True — and it reads same- and cross-origin frames at any depth.

  • An almost-empty graph tells you why. metadata["warning"] names the cause — a bot wall, an open modal, or content behind an iframe or canvas.

  • Canvas and WebGL apps have nothing to parse. Figma-style surfaces draw their controls as pixels — there is no element to emit.

  • Nothing waits for the page to finish thinking. from_page and zerodom_read_page snapshot the DOM at call time. Wait for your own condition first, then parse.

  • Anti-bot systems are out of scope, by design. ZeroDOM is middleware over a Page you already control — it never fetches anything.

Full details in SECURITY.md.


Development

uv sync
uv run playwright install chromium   # needed for the browser-backed tests
uv run pytest                        # 131 tests; browser ones skip without chromium
uv build                             # wheel + sdist into dist/

TypeScript port:

cd js
npm install
npm run build
npm test                             # 20 tests

Most useful thing to contribute: a page where a selector resolves to the wrong element. Open an issue with the URL and the output of zerodom <url> --html report.html.


License

Apache 2.0 — see LICENSE. Use it anywhere, including commercially; embed it in your own product or framework. ZeroDOM is a trademark of Vexra Labs.

Available Tools

5 tools
zerodom_click_nodeA

Click a node and return what changed on the page.

Returns a diff — + appeared, - gone, ~ value changed — because most clicks alter a handful of nodes and re-listing the page would cost hundreds. A navigation renumbers everything, so that returns the full graph instead.

ParametersJSON Schema
NameRequiredDescriptionDefault
node_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the behavioral burden. It discloses the diff notation (`+`, `-`, `~`) and the key navigation exception where a full graph is returned. It does not discuss side effects or prerequisites, but the verb 'Click' makes the mutating nature reasonably clear.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded with the core action and result. The rationale for returning a diff instead of re-listing the page is useful context, and the navigation renumbering exception is an important edge case that earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The output schema covers the return shape, so the description does not need to re-explain it. It does explain the diff versus full-graph behavior. However, because annotations are absent, a brief note about prerequisites—such as needing an already-loaded/parsed page—would make the tool more safely invocable.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The only parameter, node_id, is not explained beyond the schema title. Schema description coverage is 0%, so the description should compensate, but it never specifies what node_id refers to, how to obtain it, or any format requirements—only 'Click a node' implies its role.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action ('Click a node') and its result ('return what changed on the page'), which clearly separates it from read-oriented siblings like zerodom_read_page and zerodom_find. The return-diff behavior is also stated up front.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied rather than explicit: if you want to click a node and see page changes, this is the tool. However, it does not name alternatives or state when NOT to use it, such as preferring zerodom_fill_node for form inputs or zerodom_read_page for plain inspection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

zerodom_fill_nodeA

Type text into a node and return what changed on the page.

The text is echoed back in the first line; the diff below it reports structural change — a validation error appearing, an autocomplete list opening.

ParametersJSON Schema
NameRequiredDescriptionDefault
textYes
node_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden. It discloses that the text is echoed back, that the diff is specifically structural, and gives concrete examples of what structural change means. It does not cover every side effect, but it provides meaningful transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded. Both sentences earn their place: the first states the operation, and the second clarifies the meaning of the diff output.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is simple and has an output schema, so return-value explanation is less necessary. However, with no annotations and no parameter documentation, the description leaves node_id semantics and the relationship to sibling tools implicit, creating a noticeable gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% and the description does not explain node_id at all. Text is only implied by 'Type text,' and there is no discussion of node_id format, how to obtain it, or how the two parameters relate. The property names themselves carry the only meaning.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action ('Type text into a node') and its observable outcome ('return what changed on the page'). This clearly distinguishes it from sibling tools like click, read, find, and parse.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The context of use is clear: it is for entering text into a node and checking structural side effects such as validation errors or autocomplete. It does not explicitly mention alternatives or say when not to use it, but the behavior is distinct enough from the siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

zerodom_findA

Search the current page's graph for nodes matching query.

Case-insensitive substring match over each node's label and type. Prefer this over re-reading the whole page when you already know what you are looking for: "checkout" costs three lines, the full graph costs every node on the page.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It discloses the search behavior (case-insensitive substring match on label and type), implying a non-destructive read operation. It also mentions performance characteristics (three lines vs full graph). It doesn't explicitly state that it doesn't modify anything, but the search nature is clear. It doesn't cover error conditions or return format, but those are covered by the output schema. Overall, it's transparent enough.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no waste. The first sentence states the core purpose; the second provides usage guidance and cost comparison. The most important information (what it does) is front-loaded. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (one parameter), the presence of an output schema, and the sibling differentiation, the description is complete. It explains the search criteria, matching behavior, and usage context. The agent has enough information to invoke it correctly. The output schema handles return values, so nothing else is needed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter 'query' has 0% schema description coverage, so the description must compensate. It explains that the query is a substring used for case-insensitive matching on node labels and types. This adds semantic meaning beyond the schema's name/type, telling the agent exactly what the query affects. It could be more detailed (e.g., whether regex is supported), but it sufficiently clarifies the parameter's role.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a clear verb ('Search'), a specific resource ('current page's graph'), and the action (matching nodes by query). It also distinguishes itself from siblings by explicitly advising to prefer this over re-reading the whole page, and it names the sibling alternative implicitly. This is specific and actionable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use guidance: 'Prefer this over re-reading the whole page when you already know what you are looking for.' It contrasts the cost of this tool ('three lines') with the full graph read, making the decision clear. It doesn't explicitly list when-not-to-use cases, but the positive guidance is strong.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

zerodom_parse_urlA

Navigate to a URL and return its interaction graph.

Returns the compact text graph: [03] button 'Sign In'. CSS selectors are kept server-side and resolved by node id, so they never cost context — pass verbose=True for the full JSON including selectors.

Set frames=True when the controls you need are inside an iframe — embedded editors, payment fields, consent gates. Off by default because it costs a read per frame and most frames on a commercial page are advertising.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYes
framesNo
verboseNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full behavioral burden. It discloses the compact text output format, that CSS selectors are kept server-side (saving context), the cost of enabling frames (a read per frame), and the default off behavior. This is substantive and honest about trade-offs, though it does not mention error handling or side effects on browser state.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two paragraphs, each earning its place. The first sentence states purpose and return format, then the selector behavior, then a paragraph on frames with rationale. It is front-loaded and efficient, though the frames paragraph could be tightened slightly. No fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given an output schema exists, the description need not detail return structure, but it still provides the compact graph format and mentions the verbose option. It covers the key flags, costs, and use case for frames. The only missing piece is explicit mention of error conditions or whether navigation is stateful, but for a navigation tool this is adequate.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must compensate entirely. It explicitly explains frames ('Set frames=True when the controls you need are inside an iframe') and verbose ('pass verbose=True for the full JSON'), and url is self-evident from the purpose. Every parameter is given meaning beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a precise verb-resource pair: 'Navigate to a URL and return its interaction graph.' This clearly distinguishes the tool from siblings like zerodom_read_page (which presumably reads the page text) and zerodom_click_node (which acts on nodes). No ambiguity about what it does.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives detailed parameter-level guidance (when to set frames=True, when to use verbose=True) but never contrasts this tool with its siblings. It does not say 'use this instead of read_page when you need the graph' or state exclusions. Usage context is implied by the purpose but not made explicit, so an agent might still hesitate between parse_url and read_page for navigation tasks.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

zerodom_read_pageA

Re-read the current page without navigating.

Use after an action changed the page, or when node ids look stale. Unlike zerodom_parse_url this does not reload, so anything typed into the page stays.

ParametersJSON Schema
NameRequiredDescriptionDefault
verboseNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the burden of behavioral disclosure. It clearly states the key behavioral trait: this tool 'does not reload, so anything typed into the page stays.' It does not describe the return format, but an output schema exists and the most important side-effect distinction is disclosed. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences with no filler. The core purpose is front-loaded, followed by a precise when-to-use statement and a clarifying contrast with the sibling tool. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a low-complexity tool with a single optional boolean and an output schema present, the description supplies the essential usage context, the stale-node-id trigger, and the critical non-reload caveat. The undocumented 'verbose' flag is a minor gap but does not prevent correct invocation since it is optional and has a sensible default.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The only parameter, 'verbose', is never mentioned in the description and schema description coverage is 0%. The schema provides only the type and default, leaving the agent to infer what verbose output means. The description adds no parameter-level meaning.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description immediately states a specific action and resource: 'Re-read the current page without navigating.' It also distinguishes itself from zerodom_parse_url by the reload behavior, making clear it is not a navigation-triggering action and separating it from the listed sibling tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit conditions for use: 'after an action changed the page, or when node ids look stale.' It also names the alternative, zerodom_parse_url, and explains why that alternative is not chosen: it reloads and would lose typed content.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 5 tool updatesv0.0.6
    • First observedzerodom_click_node
    • First observedzerodom_fill_node
    • First observedzerodom_find
    • First observedzerodom_parse_url
    • First observedzerodom_read_page

TDQS

A4.4/5.0

Scored across 5 tools

Disambiguation5/5

Each tool targets a distinct action: navigation, reading, searching, clicking, and filling. No overlap in purpose; descriptions clearly differentiate their use cases.

Naming Consistency5/5

All tools follow a consistent zerodom_verb_noun pattern (click_node, parse_url, read_page, find, fill_node). Naming is predictable and uniform, making the set easy to navigate.

Tool Count5/5

Five tools is well-scoped for a DOM interaction server, covering core navigation, reading, searching, and action tasks without redundancy or bloat.

Completeness5/5

The tool surface covers the full lifecycle: navigating to a page, reading current state, searching within it, and performing actions (click, fill). No obvious dead ends or missing essential operations for the stated purpose.

Maintenance

ActivityMaintained
ResponsivenessUnresponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Agent-native headless browser for AI agents. Converts web pages to a Semantic Object Model (SOM) instead of raw HTML — 17x average token reduction across real-world sites (up to 117x on complex pages). Native MCP server with fetch_page, extract_text, extract_links, and full browser automation. No API key required.
    28 npm
    Apache 2.0
  • A
    license
    C
    quality
    C
    maintenance
    Hosted Playwright browser automation for AI agents. Returns accessibility trees instead of screenshots, cutting token usage by 77%. Navigate, interact, extract structured data, and take screenshots — all via MCP. Zero infrastructure, credit-based pricing.
    6
    50 npm
    MIT