browsing

Use when you need direct browser control - teaches Chrome DevTools Protocol for controlling existing browser sessions, multi-tab management, form automation, and content extraction via use_browser MCP tool

By obra · 401 installs

npx skills add obra/superpowers-chrome --skill browsing

Source repository · Upstream listing

Browsing with Chrome Direct Overview Control Chrome via DevTools Protocol using the use browser MCP tool. Single unified interface with auto starting Chrome. Announce: "I'm using the browsing skill to control Chrome." When to Use Use this when: Controlling authenticated sessions Managing multiple tabs in running browser Playwright MCP unavailable or excessive Use Playwright MCP when: Need fresh browser instances Generating screenshots/PDFs Prefer higher level abstractions Auto Capture Every DOM action (navigate, click, type, select, eval, keyboard press, hover, drag drop, double click, right click, file upload) automatically saves: {prefix}.png — viewport screenshot {prefix}.md — page content as structured markdown {prefix}.html — full rendered DOM {prefix} console.txt — browser console messages Files are saved to the session directory with sequential prefixes (001 navigate, 002 click, etc.). You must check these before using extract or screenshot actions. The use browser Tool Single MCP tool with action based interface. Chrome auto starts on first use. Parameters: action (required): Operation to perform selector (optional): CSS or XPath selector for element operations payload (optional): Action specific data (string or object) timeout (optional): Timeout in ms for await operations (default: 5000) Active tab : Every action operates on the current activeTab . Use switch tab to change it. Actions Reference Navigation navigate : Navigate to URL payload : URL string Example: {action: "navigate", payload: "https://example.com"} await element : Wait for element to appear selector : CSS selector timeout : Max wait time in ms Example: {action: "await element", selector: ".loaded", timeout: 10000} await text : Wait for text to appear payload : Text to wait for Example: {action: "await text", payload: "Welcome"} Interaction click : Click element selector : CSS selector Example: {action: "click", selector: "button.submit"} type : Text input selector : Optional — clicks to focus first payload : Text to type ( \t =Tab, \n =Enter) Example: {action: "type", selector: " email", payload: "user@example.com"} double click : Double click element (fires dblclick event) selector : CSS selector Example: {action: "double click", selector: ".item"} right click : Right click element (fires contextmenu event) selector : CSS selector Example: {action: "right click", selector: ".row"} select : Select dropdown option selector : CSS selector payload : Option value(s) Example: {action: "select", selector: "select[name=state]", payload: "CA"} keyboard press : Press special keys (Tab, Enter, Escape, Arrow keys, F1 F12) payload : Key name (string) or {"key": "Tab", "modifiers": {"shift": true, "ctrl": false, "alt": false, "meta": false}} Example: {action: "keyboard press", payload: "Tab"} Example with modifiers: {action: "keyboard press", payload: {"key": "Tab", "modifiers": {"shift": true}}} Mouse Actions (CDP Level) These use CDP Input.dispatchMouseEvent, bypassing synthetic event restrictions. hover : Move mouse over element (CSS :hover, tooltips, menus) selector : CSS selector Example: {action: "hover", selector: ".menu trigger"} drag drop : Drag element to target (native drag and drop via CDP) selector : Source element payload : Target selector or JSON coordinates {"x":N,"y":N} Example: {action: "drag drop", selector: ".card", payload: ".column 2"} mouse move : Move mouse to coordinates payload : JSON {"x":N,"y":N} (optional: steps , fromX , fromY for smooth movement) Example: {action: "mouse move", payload: "{\"x\":100,\"y\":200}"} scroll : Scroll via mouse wheel events payload : Direction (up/down/left/right) or JSON {"deltaX":N,"deltaY":N} selector : Optional — scroll within element Example: {action: "scroll", payload: "down"} File Upload file upload : Set files on input[type=file] elements (can't be done via JavaScript) selector : File input element payload : File path or JSON {"files":["/path/a.pdf","/path/b.jpg"]} Example: {action: "file upload", selector: " upload", payload: "/tmp/doc.pdf"} Extraction extract : Get page content payload : Format ('markdown' 'text' 'html') selector : Optional limit to element Example: {action: "extract", payload: "markdown"} Example: {action: "extract", payload: "text", selector: "h1"} attr : Get element attribute selector : CSS selector payload : Attribute name Example: {action: "attr", selector: "a.download", payload: "href"} eval : Execute JavaScript payload : JavaScript code Example: {action: "eval", payload: "document.title"} Export screenshot : Capture screenshot of a specific element payload : Filename selector : Optional screenshot specific element Viewport screenshots are auto captured after every DOM action. Use this only when you need a specific element. Example: {action: "screenshot", payload: "/tmp/chart.png", selector: ".chart"} Tab Management list tabs : List all open tabs Example: {action: "list tabs"} new tab : Create new tab Example: {action: "new tab"} close tab : Close the active tab Example: {action: "close tab"} switch tab : Switch the active tab (sticky — stays until changed) payload : Tab index (number), URL substring, or title substring Example: {action: "switch tab", payload: 1} (by index) Example: {action: "switch tab", payload: "example.com"} (by URL substring) Example: {action: "switch tab", payload: "GitHub"} (by title substring) Browser Mode Control show browser : Make browser window visible (headed mode) Example: {action: "show browser"} ⚠️ WARNING : Restarts Chrome, reloads pages via GET, loses POST state hide browser : Switch to headless mode (invisible browser) Example: {action: "hide browser"} ⚠️ WARNING : Restarts Chrome, reloads pages via GET, loses POST state browser mode : Check current browser mode, port, and profile Example: {action: "browser mode"} Returns: {"headless": true false, "mode": "headless" "headed", "running": true false, "port": 9222, "profile": "name", "profileDir": "/path"} Profile Management set profile : Change Chrome profile (must kill Chrome first) Example: {action: "set profile", "payload": "browser user"} ⚠️ WARNING : Chrome must be stopped first Side effect : marks the profile as explicit, opting out of auto disambiguation (see below) get profile : Get current profile name and directory Example: {action: "get profile"} Returns: {"profile": "name", "profileDir": "/path"} Default behavior : Chrome starts in headless mode with "superpowers chrome" profile on a dynamically allocated port (range 9222 12111). Override the port with CHROME WS PORT ; override the profile with CHROME WS PROFILE . Auto disambiguation across parallel MCPs : When two MCP servers start on the same host with the default profile, the first claims superpowers chrome (port 9222) and later ones silently fall through to superpowers chrome 2 (port 9223), superpowers chrome 3 , etc. Each MCP drives its own Chrome with its own profile dir; they don't fight over activeTab . The bridge tracks ownership via a lock file at ~/.cache/superpowers/browser profiles/<profile .mcp.lock ; stale locks (dead PIDs) are reclaimed automatically. To opt out of disambiguation — e.g., to intentionally share Chrome between a long lived chrome ws CLI session and your MCP — set the profile name explicitly: Env var: CHROME WS PROFILE=my profile Or: {action: "set profile", payload: "my profile"} at runtime An explicit profile name still acquires the lock, but on conflict the bridge shares rather than disambiguates — the second process reconnects to the first's Chrome (the original reconnect on restart behavior). Chrome Lifecycle (Recovery) kill chrome : Kill the Chrome process this MCP is driving Example: {action: "kill chrome"} Releases the meta.json; next page action auto restarts Chrome restart chrome : kill chrome + immediate spawn Example: {action: "restart chrome"} Auto restart banner : when the bridge has to spawn a fresh Chrome (because the previous one died or was killed externally — e.g., kill 9 <pid from the shell), the first response after the restart prepends: Treat this as a signal that your prior URL / tab state is gone — re navigate before assuming anything about the current page. Console Logging Capture browser console output for the active tab. Buffer is keyed by the page session's sessionId , so it survives close tab / new tab ordering quirks. Levels: log , info , warn , error . enable console logging : Start capturing Example: {action: "enable console logging"} get console messages : Read captured messages All: {action: "get console messages"} Since timestamp (epoch ms): {action: "get console messages", payload: {since: 1716000000000}} Returns: array of {timestamp, level, text} entries clear console messages : Reset the buffer Example: {action: "clear console messages"} Dialog Handling Native dialogs (JS alert/confirm/prompt, beforeunload, HTTP basic auth, permission prompts, device choosers) pause the page. While a dialog is open, page targeted actions ( extract , click , eval , etc.) return a refusal whose text contains Page is behind a dialog and lists the available dialog:: selectors. When a dialog fires during a navigate (typical for HTTP basic auth), navigate itself throws with the dialog grammar in the message — you don't have to issue a separate page targeted call to discover the dialog. Handle dialogs by clicking/typing a dialog:: selector: {action: "click", selector: "dialog::accept"} — accept JS alert/confirm/prompt, beforeunload, permission grant {action: "click", selector: "dialog::dismiss"} — dismiss / cancel / deny {action: "type", selector: "dialog::prompt", payload: "text"} then accept — respond to JS prompt {action: "type", selector: "dialog::username", payload: "alice"} + {action: "type", selector: "dialog::password", payload: "secret"} + {action: "click", selector: "dialog::accept"} — HTTP basic auth {action: "click", selector: "dialog::device[id=\"<deviceId \"]"} — pick a WebUSB/Bluetooth/Serial/HID device Critical caveats when toggling modes : 1. Chrome must restart Cannot switch headless/headed mode on running Chrome 2. Pages reload via GET All open tabs are reopened with GET requests 3. POST state is lost Form submissions, POST results, and POST based navigation will be lost 4. Session state is lost Any client side state (JavaScript variables, etc.) is cleared 5. Cookies/auth may persist Uses same user data directory, so logged in sessions may survive When to use headed mode : Debugging visual rendering issues Demonstrating browser behavior to user Testing features that only work with visible browser Debugging issues that don't reproduce in headless mode When to stay in headless mode (default): All other cases faster, cleaner, less intrusive Screenshots work perfectly in headless mode Most automation works identically in both modes Profile management : Profiles store persistent browser data (cookies, localStorage, extensions, auth sessions). Profile locations : macOS: ~/Library/Caches/superpowers/browser profiles/{name}/ Linux: ~/.cache/superpowers/browser profiles/{name}/ Windows: %LOCALAPPDATA%/superpowers/browser profiles/{name}/ When to use separate profiles : Default profile ("superpowers chrome") : General a