Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault

No arguments

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
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. Nodes sharing a repeated-list-item ancestor (<article>/<li>/<tr>, e.g. a feed or Hacker-News-style table) are grouped under @card "title": whenever a card holds 2+ controls — twenty identical button 'Upvote' lines are meaningless without knowing which story each belongs to.

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.

Set viewport_only=True on long feed/infinite-scroll pages (a social feed, a video site's homepage) where most of the graph is scrolled off-screen and you only need what's currently visible — this can cut node count by more than half on pages like that. On by default for interactive agents; costs a getBoundingClientRect() per element. Sticks for the rest of this session (every click/fill re-read honors it too) until the next zerodom_parse_url call changes it — same lifetime as frames. When nodes are being skipped, the graph's first line says how many; scroll and re-read to see them.

Set check_occlusion=True when clicks keep failing with Playwright's "element intercepts pointer events" — a modal backdrop, an open dropdown, or a cookie banner is covering nodes that are still in the DOM and still listed. This filters them out at parse time instead of at click time, same sticky-for-the-session lifetime as viewport_only. Off by default: the cost of an elementFromPoint() hit-test per node on a large page is unmeasured, so it isn't imposed on every caller by default.

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. Honors whatever frames/viewport_only/check_occlusion zerodom_parse_url last set.

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.

zerodom_hidden_fieldsA

List hidden <input type="hidden"> fields on the current page.

Hidden inputs are deliberately NOT graph nodes — an agent acting on a page has no reason to click them, so listing them as nodes would be noise. But they carry the payload a form submission needs (CSRF tokens, per-view state like a draft id or a sort order), which is exactly what an agent re-submitting a POST after a stale parse is missing. Returns each field's name, value and CSS selector, so the values can be fed straight into a zerodom_fill_node call if the target form expects them.

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.

A same-page click reporting "no structural change" shortly after navigation is ambiguous — could be a real no-op, could be a server- rendered control (Next.js/Remix/Nuxt) whose framework hasn't finished attaching its handler yet. Flagged, not retried automatically: a false retry risks a real double-submit on a control that did fire. Suppressed when the click triggered a network request even without a DOM change yet — an in-flight fetch/GraphQL mutation (auth actions routinely take 800ms-2s to resolve) is itself evidence the handler did fire, just hasn't finished.

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.

zerodom_hoverA

Hover over a node and return what changed on the page.

Reveals hover-triggered menus and tooltips — content that a click alone would never surface, and that isn't in the graph until this fires.

zerodom_press_keyA

Press a key while a node is focused and return what changed.

key uses Playwright's key names ("Enter", "Escape", "Tab", "ArrowDown", ...). For "press Enter to submit" forms, "Escape to close a modal", and keyboard-only widgets a click/fill can't drive.

zerodom_upload_fileA

Set a file input's value to a local file path and return what changed.

path is resolved on the machine driving the browser — the same trust boundary as zerodom_eval_js, not a new one.

zerodom_dragA

Drag source onto target and return what changed.

Drag-to-reorder lists, drag-and-drop upload zones, sliders — anything a click/fill pair can't express because the gesture itself is the input.

zerodom_scrollA

Scroll the page and return what's newly visible.

direction: "up" or "down". amount: pixels, roughly one screenful is 800. Dispatches a real wheel event at the viewport center rather than window.scrollBy, so it scrolls whatever scrollable container is actually under the cursor — a nested feed/sidebar, not just the document body, matching what a real scroll gesture would do.

zerodom_new_tabA

Open a new tab and make it the active one — every other tool (read, click, fill, scroll) then acts on it until you zerodom_switch_tab away.

In an attached (real-browser) session the new tab lands in the same "zerodom" tab group as every other tab this session touches.

zerodom_list_tabsA

List every open tab, marking the active one with *.

zerodom_switch_tabA

Make another open tab active and return its interaction graph.

Node ids are per-tab, so re-read (this returns the graph already) before clicking or filling anything on the tab you just switched to.

zerodom_close_tabA

Close a tab — the active one by default. Refuses to close the last tab.

zerodom_screenshotA

Full-page screenshot of the active tab, saved to disk; returns the path.

report.py has a screenshot path already, but it's wired to the CLI's own throwaway sync browser (playwright_wrapper.py's sync/async split), not this attached async session — this is that same capability for here. Pass path to choose where it's saved; omitted, a temp file is used.

zerodom_set_viewportA

Resize the active tab's viewport for responsive-design testing.

Not the real browser window — that's zerodom_resize_window. This is Emulation.setDeviceMetricsOverride: the page's own rendered layout at a given width, without touching the chrome around it. Use this one for "how does this render at width X"; use zerodom_resize_window for an actually different-sized window on screen.

zerodom_resize_windowA

Resize the real browser window (not just the page's viewport).

chrome.debugger's CDP surface has no browser-level window-management grant, but chrome.windows.update is a plain extension API, entirely unrelated to chrome.debugger — so this genuinely works despite that. Sent as Browser.setWindowBounds (a real CDP method name Playwright's own driver will actually transmit — a made-up method name gets rejected client-side before reaching the relay at all) and repurposed server-side; see docs/DECISIONS.md D16. Only meaningful for an attached (real-browser) session; a launched headless session has no window to resize.

On a tiling window manager (i3/sway/Hyprland/bspwm-style setups), this call succeeds but the window won't visibly move or resize — on Wayland compositors specifically this isn't a WM being uncooperative, it's the protocol itself: clients are deliberately not allowed to force their own geometry. Works normally on a floating window. Confirmed live on Hyprland.

zerodom_get_stylesA

Computed styles and box-model dimensions for a node — for design/CSS review, not just interaction.

Runs getComputedStyle() in the real page over the same chrome.debugger connection everything else here uses (also reachable ad hoc via zerodom_eval_js; this is the purpose-built version with a curated property list instead of getComputedStyle()'s full ~300-property dump).

zerodom_get_cookiesA

List cookies visible to the active tab's origin.

Reads via CDP's Network domain (Playwright's context.cookies()), which sees httpOnly cookies too — unlike a content script's document.cookie, which httpOnly exists specifically to hide them from.

zerodom_get_storageA

List localStorage and sessionStorage keys the active tab's page holds.

Session state that never touches the network — an issued draft id, a collapsed sidebar preference, a half-typed form — lives here. Same trust boundary as zerodom_eval_js (page context), but purpose-built and read-only, so it returns keys + values without the power of the eval tool. Values that look like a JWT (three base64url segments) get their claims (sub/role/exp/iss/aud) decoded into a second line.

zerodom_eval_jsA

Run arbitrary JavaScript in the active tab's real page context and return the result — Runtime.evaluate over the same chrome.debugger connection everything else here uses, the same power as typing into DevTools' own console.

zerodom_network_logA

Requests/responses the active tab has made since it opened (or since this was last called with clear=True) — method or status, and URL.

Passive visibility only, capped at the most recent 200 entries — not interception or modification of traffic (that needs page.route(), a bigger, stateful feature; zerodom_eval_js can already override window.fetch/XMLHttpRequest from the page side for ad hoc cases).

zerodom_set_scopeA

Constrain the hunt to authorized targets — enforced in code, not on trust.

hosts: comma-separated in-scope host globs (app.example.com,*.example.com). Navigation, replay and clicks to any other host are then refused. deny: a regex of destructive URLs/labels to refuse (default covers logout/delete/ remove/deactivate/revoke) so an unattended agent can't take an irreversible action. max_rps: throttle to at most N requests/second (program rate limits).

An operator can instead lock scope before the agent starts by setting the ZERODOM_SCOPE env var to a YAML file; a locked scope can't be widened here.

zerodom_add_identityA

Register a second identity (e.g. user B) for cross-tenant IDOR testing.

storage_state is a Playwright storage_state JSON file (cookies + origins) — capture one per account. header is an optional extra request header ('Authorization: Bearer …') for token-auth APIs, repeatable via comma isn't supported; call again to add more. The current logged-in session is always available as identity 'live' without registering.

Once two identities exist, zerodom_compare_identities(url) fetches the same URL as each and flags a byte-identical response — the cross-tenant IDOR tell.

zerodom_replayA

Replay an HTTP request under a chosen identity — Burp Repeater for the agent.

Sends method url (with optional body and one extra header 'K: V') through as_identity (default the live logged-in session). Returns the response status, size and a body preview. Use it to probe an endpoint, tamper with a request, or check an object reference — then change the id/body and replay again. Pair with zerodom_compare_identities for the A-vs-B diff.

zerodom_compare_identitiesA

Fetch one URL as each identity and diff — the cross-tenant IDOR check.

Sends method url through every identity in identities (default: the live session plus every registered one) and reports each response's status and size. A byte-identical response under two identities on a per-user resource is a cross-tenant IDOR. Runs on the real, rendered session, so it works where a plain HTTP fetch would hit a WAF or a login wall.

zerodom_statusA

Diagnose the current connection: relay reachability, whether a browser session is attached yet, which tab is active, and the tail of the relay's own log — the single-call version of the manual WebSocket-probing and log- tailing this project's own debugging needed before this tool existed.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

A4/5.0

Scored across 28 tools

Disambiguation5/5

Each tool targets a distinct resource or action, and near-overlapping pairs are explicitly disambiguated in their descriptions (parse_url vs. read_page, set_viewport vs. resize_window, get_cookies/get_storage vs. eval_js). An agent can reliably select the right tool without guessing.

Naming Consistency4/5

All tool names share the zerodom_ prefix and snake_case convention, which makes them predictable. However, the verb style is mixed—get_/list_/read_/parse_/set_/click_/fill_—and a few noun-form names like zerodom_screenshot, zerodom_status, zerodom_network_log, and zerodom_hidden_fields break the otherwise action-first pattern.

Tool Count3/5

28 tools is above the typical well-scoped range and makes the surface feel heavy, though the server covers a broad domain spanning browser automation, DOM interaction, tab management, network visibility, and security testing. It is not bloated to the point of being unusable, but the count is borderline.

Completeness4/5

The toolset covers the core browser-automation lifecycle well: navigation, page reading, interaction, tabs, state inspection, screenshots, network visibility, and identity-based replay. Minor gaps like explicit page reload/back/forward, cookie/storage mutation, or a dedicated wait-for-condition helper are workarounds rather than dead ends.

Maintenance

ActivityMaintained
ResponsivenessUnresponsive