---
name: browser
description: "Drives the device's existing Chromium over the Chrome DevTools Protocol for both retrieval and interactive automation. browser-render reads a JS-rendered page's DOM (the JS-rendering leg of WebFetch / url-get / browser-render retrieval). The browser-navigate family keeps one page alive across calls for click/fill/snapshot automation, and browser-pdf-save / browser-screenshot turn the rendered page into PDF or PNG files on device. Use it for client-rendered SPAs, form automation, and generating A4 PDFs."
tools:
  - name: browser-render
    publicAllowlist: false
    adminAllowlist: true
    riskClass: read
  - name: browser-navigate
    publicAllowlist: false
    adminAllowlist: true
    riskClass: read
  - name: browser-snapshot
    publicAllowlist: false
    adminAllowlist: true
    riskClass: read
  - name: browser-click
    publicAllowlist: false
    adminAllowlist: true
    riskClass: external
  - name: browser-fill
    publicAllowlist: false
    adminAllowlist: true
    riskClass: external
  - name: browser-fill-form
    publicAllowlist: false
    adminAllowlist: true
    riskClass: external
  - name: browser-type
    publicAllowlist: false
    adminAllowlist: true
    riskClass: external
  - name: browser-press-key
    publicAllowlist: false
    adminAllowlist: true
    riskClass: external
  - name: browser-hover
    publicAllowlist: false
    adminAllowlist: true
    riskClass: read
  - name: browser-select-option
    publicAllowlist: false
    adminAllowlist: true
    riskClass: external
  - name: browser-wait-for
    publicAllowlist: false
    adminAllowlist: true
    riskClass: read
  - name: browser-handle-dialog
    publicAllowlist: false
    adminAllowlist: true
    riskClass: external
  - name: browser-evaluate
    publicAllowlist: false
    adminAllowlist: true
    riskClass: exec
  - name: browser-console-messages
    publicAllowlist: false
    adminAllowlist: true
    riskClass: read
  - name: browser-tabs
    publicAllowlist: false
    adminAllowlist: true
    riskClass: read
  - name: browser-pdf-save
    publicAllowlist: false
    adminAllowlist: true
    riskClass: write_local
  - name: browser-screenshot
    publicAllowlist: false
    adminAllowlist: true
    riskClass: write_local
  - name: browser-resize
    publicAllowlist: false
    adminAllowlist: true
    riskClass: read
always: true
embed: false
metadata: {"platform":{}}
mcp:
  command: node
  args:
    - ${PLATFORM_ROOT}/lib/mcp-spawn-tee/dist/index.js
    - ${PLATFORM_ROOT}/plugins/browser/mcp/dist/index.js
  env:
    MCP_SPAWN_TEE_NAME: browser
    LOG_DIR: ${LOG_DIR}
    PLATFORM_ROOT: ${PLATFORM_ROOT}
    CDP_PORT: ${CDP_PORT}
mcp-manifest: auto
---

# Browser

Drives the device's Chromium over the Chrome DevTools Protocol for three things: reading JS-rendered pages, automating a page (click/fill/snapshot), and turning a page into a PDF or PNG file on device.

## Why it exists

`WebFetch` summarises and refuses verbatim output; `url-get` returns the verbatim source but does not execute JavaScript. Client-rendered pages come back from both as an empty shell. `browser-render` runs the page's JavaScript and reads the resulting DOM. The automation and output tools exist because the specialists previously named a Playwright tool surface (interaction + `browser_pdf_save`) that never actually shipped on a Pi — these tools provide the same capabilities backed by the on-device CDP connection instead.

## How it works

The device already runs a per-brand Chromium for the VNC surface, launched by `platform/scripts/vnc.sh` with `--remote-debugging-port=${CDP_PORT}`. Every tool attaches to that running browser over the Chrome DevTools Protocol — it never launches its own browser, so nothing extra ships on device and there is no on-demand download. The brand's CDP port reaches this server through the `CDP_PORT` spawn placeholder.

`browser-render` is **stateless**: each call opens a fresh background target, navigates, reads the DOM, and closes it. The automation tools share **one persistent page**: `browser-navigate` creates (or reuses) a single live target, and the action tools drive it across calls. If that page dies (operator closes the tab, crash, browser restart) the action tools return `session-lost` and the agent calls `browser-navigate` again — only navigate re-attaches, so a dead page is never silently replaced mid-action. `browser-tabs` opens/switches/closes pages.

## Tools

Retrieval (stateless):
- `browser-render` — `url`, optional `loadTimeoutMs`. Returns rendered HTML + visible text.

Automation (shared persistent page; selectors come from `browser-snapshot`):
- `browser-navigate` — open a URL and keep the page alive. Entry point for automation.
- `browser-snapshot` — accessibility snapshot: each interactive/landmark node as role, name, and a unique CSS selector to use with click/fill/hover.
- `browser-click`, `browser-fill`, `browser-fill-form`, `browser-hover`, `browser-select-option` — act on a CSS selector.
- `browser-type`, `browser-press-key` — real keyboard input (`Input.*`).
- `browser-wait-for` — wait for a selector or text to appear.
- `browser-handle-dialog` — pre-arm how the next JS dialog(s) are answered (CDP dialogs block, so policy must be set before the triggering action).
- `browser-evaluate` — run a JS expression in the page and return its value.
- `browser-console-messages` — drain buffered console output + uncaught errors.
- `browser-tabs` — list / new / select / close pages.

Output (to a file on device):
- `browser-pdf-save` — `Page.printToPDF` of the current page; the on-device replacement for the old Playwright `browser_pdf_save`.
- `browser-screenshot` — PNG of the page or one element (CSS selector clip), e.g. glassmorphism print-fallback images.
- `browser-resize` — set the viewport size.

All tools are admin-allowlisted; none are exposed to public chat.

## Observability

A successful render emits `[browser-render] url=… rendered=true domBytes=…` on the server's stderr (captured by the mcp-spawn-tee log). Every other tool returns a structured outcome: `ok`, or one of `cdp-unreachable`, `session-lost`, `selector-not-found`, `option-not-found`, `wait-timeout`, `navigate-failed`, `load-timeout`, `evaluate-failed`, `command-timeout`, `pdf-failed`, `screenshot-failed`, `unsupported-key`. On a device with no running Chromium (e.g. a laptop native-display install with no VNC browser) the tools return `cdp-unreachable` rather than hanging. In virtual-display mode a Chromium that dies while the VNC display stays up is self-healing: the brand's standing CDP-liveness check (run each `rss-sampler` cycle via `vnc.sh cdp-liveness`) relaunches it and logs `[vnc] op=cdp-liveness … reachable=<bool>` plus `[vnc] op=chromium-respawn … outcome=<ready|failed>` to the service journal, so `cdp-unreachable` is transient (recovered within the check interval, bounded against hot-looping an unlaunchable browser) rather than dead-until-restart. Native-display installs launch Chromium on-demand, so a `cdp-unreachable` there clears on the next browser call.
