Skill v1.0.0
currentAutomated scan100/100version: "1.0.0" name: drisp-browser description: > Drive a real browser against a live or staging website with the Drisp Browser (@drisp/browser-mcp) MCP tools (navigate, snapshot, click, type, find, get_form, screenshot, etc.). Load it whenever the task is to actually open a URL and observe or act on the running page: read live page state, click, type, fill or submit forms, walk flows like login/signup/search/add-to-cart/checkout, upload or attach a file to a form (file inputs, dropzones, native file pickers), reproduce a bug at a given URL, screenshot the live page, or extract reliable selectors from a running page for automation. Trigger even for a single action ("open this page", "click that button on the site"), even when no URL is named but the target is clearly a live page, and on subagent dispatch with browser MCP access — when in doubt, load it. Do NOT load it for writing or styling UI code, fixing or reviewing form/auth/checkout code, authoring test files, or explaining how web tech works — those mention forms, buttons, or pages but need no live browser. user-invocable: true argument-hint: "<url> <goal> — e.g. https://example.com 'Add item to cart'"
Drisp Browser Guide
Use this skill to open live web pages, carry out actions, move through multi-step flows, validate page state, and capture selectors for automation.
Common uses:
- Review a live page or multi-step flow
- Click through navigation, buttons, dialogs, and other actions
- Fill, submit, or inspect forms and validation states
- Upload or attach files through file inputs, dropzones, and native file pickers
- Add products to cart or complete other in-page actions
- Capture reliable Playwright selectors for key elements
Setup
This skill drives the Drisp Browser (@drisp/browser-mcp) MCP server — installing the skill does not install that server. If the mcp__drisp-browser__* tools aren't available, add the server first:
claude mcp add drisp-browser -- npx @drisp/browser-mcp@latest
For Claude Desktop / Cursor / VS Code, register it under the key drisp-browser:
{"mcpServers": {"drisp-browser": {"command": "npx","args": ["@drisp/browser-mcp@latest"]}}}
See the Drisp Browser README for browser modes (user, persistent, isolated) and options.
Why this skill must load before browser MCP calls: without it, browser work reliably produces brittle CSS selectors, narrative prose instead of structured observations, missed state transitions, and shallow page coverage. The selector-capture and state-observation patterns below are what the downstream exploration report, spec generation, and test-writing skills depend on.
Subagent dispatch: when a subagent is dispatched with browser MCP access, this is the first skill it should load — the exploration upstream of spec generation and test writing depends on the patterns below.
Input
Parse the target URL and exploration goal from: $ARGUMENTS
Workflow
- Navigate or recover the right page — use
list_pagesand explicitpage_idwhen session state may be ambiguous - Orient first — read the current state, active region, and visible controls before acting
- Choose the lightest useful tool
- Use page state or
snapshotoutput for quick orientation - Use
findwithlabel,kind, andregionto narrow targets - Use
get_formwhen the task is clearly form-driven - Use
get_elementfor a chosen target, offsets, or selector extraction
- Act one step at a time — click, type, select, scroll, or drag only as needed to advance the task
- Reacquire state after meaningful changes — after navigation, overlays, search expansion, dialog opening, or large DOM updates, refresh your understanding before reusing old
eids - Inspect forms or extract selectors only when relevant — do this when asked for them or when they materially help complete the task
- Report what you did, what happened, and any selectors or form details that matter
Output Format
Always include:
- What you accomplished — the result, finding, or outcome
- Steps taken — pages visited, buttons clicked, forms filled
- Observations — notable page states, messages, and behaviors
- Selectors (when relevant) — Playwright-compatible selectors for key elements
- Form details (when relevant) — only include when they helped drive the task
Operating Heuristics
- Prefer
findover manual scanning when snapshots are trimmed or the page is dense - Filter
findaggressively withkind,label, andregionbefore broad exploration - Expect search UIs to appear as buttons or comboboxes before they expose a text field
- Expect overlays, drawers, and dialogs to mutate the page in place without changing the URL
- Treat
eids as short-lived after large mutations; reacquire targets instead of assuming old ids still work - Trust
get_formas a helper, not as ground truth; busy pages may contain multiple unrelated forms - Use
observations,baseline, anddiffto confirm whether an action actually changed the page - Prefer sequential progress on gated flows; if a control is disabled, look for the prerequisite choice above it
State Snapshot Structure
Every navigation or action returns a <state> snapshot:
<state step="N" title="Page Title" url="https://..."><meta view="1521x752" scroll="0,0" layer="main" /><baseline reason="first|navigation" /><diff type="mutation" added="N" removed="N" /><observations>...</observations><region name="main">...</region></state>
Key Elements
| Element | Purpose | |
|---|---|---|
<meta> | Viewport size, scroll position, active layer | |
<baseline reason="..."> | Fresh snapshot - "first" (initial load) or "navigation" (URL change) | |
<diff type="mutation"> | Incremental update with added/removed counts | |
<observations> | What appeared/disappeared after the action | |
<region> | Semantic page areas with interactive elements |
Observations
After actions (click, type, select), watch for changes:
<observations><appeared when="action">Your Bag is empty</appeared><appeared when="action" role="status"></appeared><disappeared when="action" role="status"></disappeared></observations>
<appeared>: New content visible after action<disappeared>: Content removed after actionroleattribute: Semantic role (status, alert, dialog)
Regions
Page content is organized into semantic regions:
<region name="main"><link id="..." href="...">Link text</link><btn id="...">Button text</btn><!-- trimmed 50 items. Use find with region=main to see all --></region><region name="nav" unchanged="true" count="90" />
Region Types
main- Primary content areanav- Navigation menusheader- Page headerfooter- Page footerform- Form containersaside- Sidebarssearch- Search areas
Optimization Hints
unchanged="true" count="N"- Region didn't change, shows element count<!-- trimmed N items -->- Usefindwithregionfilter to see all
Element Types in Snapshots
| Tag | Element | Key Attributes | |
|---|---|---|---|
<link> | Hyperlink | id, href | |
<btn> | Button | id, val, enabled | |
<rad> | Radio button | id, val, checked, focused | |
<sel> | Dropdown/select | id, expanded, focused | |
<elt> | Input/generic | id, type, val, focused, enabled, selected |
Common Attributes
| Attribute | Meaning | |
|---|---|---|
id | Element ID (eid) - use this to target the element | |
enabled="false" | Element is disabled (common in sequential forms) | |
checked="true" | Radio/checkbox is selected | |
focused="true" | Element has keyboard focus | |
expanded="true" | Dropdown is open | |
selected="true" | Option/tab is selected | |
val | Element value |
Progressive Enablement Pattern
Many sites use progressive enablement: later options stay disabled until earlier choices are made.
<!-- Step 1: Model selection enabled --><rad id="model1" val="pro">iPhone 17 Pro</rad><rad id="color1" enabled="false" val="silver">Silver</rad> <!-- disabled --><!-- After selecting model, colors become enabled --><rad id="model1" checked="true" val="pro">iPhone 17 Pro</rad><rad id="color1" val="silver">Silver</rad> <!-- now enabled -->
Common places this appears:
- Ecommerce product configuration
- Checkout and payment flows
- Onboarding wizards
- Settings pages with dependent options
Strategy: If you see enabled="false", work upward to identify and complete the prerequisite step before continuing.
find Response
<result type="find" page_id="..." snapshot_id="..." count="N"><match eid="abc123"kind="button|link|radio|checkbox|textbox|combobox|heading|image"label="Button text"region="main|nav|header|footer"selector="role=button[name="..."]"visible="true"enabled="true"href="..." /></result>
Filter Parameters
kind: Element type filterlabel: Case-insensitive substring matchregion: Restrict to semantic arealimit: Max results (default 10)include_readable: Include text content (default true)
get_element Response
<node eid="abc123" kind="link" region="main" group="tbody-28"x="147.875" y="11.5" w="97.97" h="16.5"display="inline" zone="top-left">Element label text<selector primary='role=link[name="..."]' /><attrs href="..." /></node>
primary: Best Playwright selector- Position info:
x,y,w,h,zone group: Logical grouping (for tables, lists)
get_form Response
<forms page="page-id"><form id="form-xxx" intent="search|login|signup|checkout" completion="100%"><input eid="748" purpose="search">Search Wikipedia</input><combobox eid="750" purpose="selection" filled="true">EN</combobox><button eid="820" type="submit" primary="true">Search</button><next eid="748" reason="Optional field" /></form></forms>
intent: Form purpose (search, login, checkout, etc.)completion: Percentage fillednext: Suggested next field to fill with reason
list_pages Response
<result type="list_pages" status="success"><pages count="N"><page page_id="page-xxx" url="https://..." title="Page Title" /></pages></result>
Use page_id to target specific browser tabs.
Session Recovery
The browser persists across conversation sessions — tabs from prior sessions remain open. On a new session, there is no "current" page; actions without page_id may target an arbitrary tab.
When encountering a "no page/session" error or resuming from a prior session:
- Call
list_pagesto see all open tabs withpage_id, URL, and title - Identify the target page by URL or title
- Pass
page_idexplicitly to all subsequent calls (snapshot,find,click, etc.) - If the page is not found, navigate fresh — the tab may have been closed
Caveats:
- Stale tab URLs:
list_pagesshows the URL at open time. For SPAs, usesnapshotwithpage_idto see actual current state. - Tab accumulation: The browser accumulates tabs across sessions. Always use
page_idto target the correct one. - Single active work tab assumptions: Do not assume you have multiple useful tabs open. Check
list_pagesinstead of relying on prior turn memory.
Error Responses
<error>Field not found in any form: abc123</error>
Common errors:
- Element ID not found (page may have changed)
- Element not visible/enabled
- Form field not in any form context
- No page/session (see Session Recovery above)
When this happens:
- Re-check the current page state
- Re-run
findorget_formfrom the latest state - Continue only with fresh
eids
Non-DOM Surfaces
Certain browser interactions open surfaces that live outside the page DOM — JavaScript dialogs (alert, confirm, prompt), and OS-native file pickers triggered by <input type="file">. These are exposed as blocking non-DOM surfaces directly in the state response, using the same eid-based model as normal page elements.
What it looks like
When a non-DOM surface is active, the state response gains two extra elements after </state>:
<!-- JavaScript dialog --><non_dom kind="dialog" modal="true" dialog_type="confirm" message="Delete this item?"><ctrl eid="nd-dialog-ok" kind="button" label="Accept" /><ctrl eid="nd-dialog-dismiss" kind="button" label="Dismiss" /></non_dom><dom_blocked reason="dialog" /><!-- File picker (from clicking an input[type=file]) --><non_dom kind="file-picker" modal="true" mode="selectSingle"><ctrl eid="nd-picker-path" kind="input" label="File path" placeholder="Absolute path on browser host..." /><ctrl eid="nd-picker-choose" kind="button" label="Choose" /><ctrl eid="nd-picker-cancel" kind="button" label="Cancel" /></non_dom><dom_blocked reason="file-picker" />
<dom_blocked> means normal DOM interaction is suspended until the surface is resolved.
Synthetic EIDs (nd-*)
Non-DOM controls use a synthetic nd- prefix in the same eid namespace as DOM elements. Pass them directly to click and type — no special tool needed.
| EID | Kind | When present | Action | |
|---|---|---|---|---|
nd-dialog-ok | button | alert / confirm / prompt | Accept (OK / Submit / Stay on Page) | |
nd-dialog-dismiss | button | confirm / prompt / beforeunload | Dismiss (Cancel / Leave) | |
nd-dialog-input | input | prompt dialog only | The text field for prompt response | |
nd-picker-path | input | file picker | Absolute browser-host file path(s) | |
nd-picker-choose | button | file picker | Confirm the typed path and upload | |
nd-picker-cancel | button | file picker | Cancel without uploading |
Dialog workflow
click → <non_dom kind="dialog"> appears# For alert:click(eid="nd-dialog-ok")# For confirm:click(eid="nd-dialog-ok") # Acceptclick(eid="nd-dialog-dismiss") # Dismiss# For prompt:type(eid="nd-dialog-input", text="my answer")click(eid="nd-dialog-ok") # Submit
After resolving a dialog, the page resumes normally and a fresh state is returned.
File upload (file picker) workflow
There is no `upload` tool — it was removed in v4.6.5. Uploads are click-driven:
click(eid="<upload-control-eid>")→ <non_dom kind="file-picker" mode="selectSingle"> appearstype(eid="nd-picker-path", text="/absolute/path/to/file.pdf", clear=true)click(eid="nd-picker-choose")
Click whatever is visible and labelled — "Choose file", "Attach", "Upload", a dropzone, or the input[type=file] itself. A direct file input builds the surface without dispatching a real click; an indirect trigger (styled button, <label for>, dropzone, hidden input) opens the native chooser, which is intercepted and turned into the same surface. Either way you get nd-picker-* controls, so don't hunt for the underlying input.
type on nd-picker-path only stores the value on the surface; nothing reaches the page until nd-picker-choose. Use clear=true so you replace rather than append a previously typed path.
For multi-file pickers (mode="selectMultiple", i.e. the input has multiple), type one absolute path per line:
type(eid="nd-picker-path", text="/path/to/a.pdf\n/path/to/b.pdf")click(eid="nd-picker-choose")
Sending several paths to a selectSingle picker is rejected, not silently truncated.
Cancel with `nd-picker-cancel`, never by clicking elsewhere — cancelling sends an empty file list that releases Chrome's pending chooser.
Path rules
- Absolute paths only; must be an existing regular file
- The path must resolve on the browser host (the machine or container running Chrome), not just on the MCP server — they are assumed to share a filesystem. With remote/containerized Chrome, stage the file into a path that exists inside that container.
- If
UPLOAD_ALLOWED_ROOTSis set (colon-separated absolute dirs), every path must sit inside one of them; copy the file into an allowed root otherwise.
Common upload errors
| Error | Fix | |
|---|---|---|
File path is empty | type into nd-picker-path before choosing | |
Path must be absolute on the browser host | Use a full absolute path | |
File not found | Check the path resolves where Chrome runs; stage it if remote | |
Path is not a regular file | Point at a file, not a directory | |
File ... is outside the configured allowed roots | Copy the file into an allowed root | |
This file picker does not allow multiple files | Send exactly one path | |
No active non-DOM surface | snapshot, then click the upload control again |
After choosing, the surface clears and a fresh state returns. Confirm the upload the way the site reports it — a filename chip, a thumbnail, a newly enabled Submit button — before moving on.
Using find and get_element with non-DOM surfaces
find returns nd-\* controls alongside DOM matches (they appear first). Use them the same way:
find(kind="button") → includes nd-dialog-ok, nd-dialog-dismissget_element(eid="nd-dialog-ok") → shows synthetic control details
snapshot during a dialog
When a JavaScript dialog is blocking the page, calling snapshot returns the pre-dialog page state with the <non_dom> block appended, so you can orient yourself without hanging.
Canvas Interactions
<canvas> elements render pixels, not DOM nodes — standard selectors don't work inside them. Use these tools for canvas-based UIs (drawing apps, games, visualizations):
- `inspect_canvas` — the key tool. Pass a canvas
eidand it auto-detects the rendering library (Fabric.js, Konva, PixiJS, Phaser, Three.js, EaselJS, or raw canvas), queries the scene graph for objects with positions/sizes/labels, and returns an annotated screenshot with coordinate grid overlay and bounding boxes. Supports configurablegrid_spacing(use 10px for precise handle targeting). - `click` with
eid+x/y— click at offset relative to canvas top-left (e.g., select a shape) - `drag` with
eid+ source/target coordinates — drag within canvas (e.g., move objects, scale/rotate handles) - `screenshot` with
eid— capture just the canvas to visually verify state
Workflow: find → get_element (position) → inspect_canvas (discover objects) → click/drag (interact) → re-inspect to verify.
Best Practices
- Use `find` when snapshot shows
<!-- trimmed --> - Track `<baseline>` vs `<diff>` to know if you have full or partial state
- Always pass `page_id` when working across sessions or with multiple tabs
- Reacquire targets after large mutations instead of reusing stale
eids - Keep selector extraction optional unless the task asks for it or automation handoff is part of the outcome
Example Usage
/drisp-browser https://airbnb.com Walk through the search and booking flow for stays in Tokyo/drisp-browser https://apple.com/store Configure an iPhone and add it to the bag, then summarize the steps/drisp-browser https://developer.mozilla.org Find the Fetch API docs and note how the search flow behaves/drisp-browser https://example.com/login Extract the login form selectors and field purposes/drisp-browser https://example.com/profile Upload /Users/me/files/avatar.png to the profile photo field