with one click
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
// 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
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | browsing |
| description | 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 |
| allowed-tools | mcp__chrome__use_browser |
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."
Use this when:
Use Playwright MCP when:
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 messagesFiles are saved to the session directory with sequential prefixes (001-navigate, 002-click, etc.). You must check these before using extract or screenshot actions.
Single MCP tool with action-based interface. Chrome auto-starts on first use.
Parameters:
action (required): Operation to performtab_index (optional): Tab to operate on (default: 0)selector (optional): CSS selector for element operationspayload (optional): Action-specific datatimeout (optional): Timeout in ms for await operations (default: 5000)navigate: Navigate to URL
payload: URL string{action: "navigate", payload: "https://example.com"}await_element: Wait for element to appear
selector: CSS selectortimeout: Max wait time in ms{action: "await_element", selector: ".loaded", timeout: 10000}await_text: Wait for text to appear
payload: Text to wait for{action: "await_text", payload: "Welcome"}click: Click element
selector: CSS selector{action: "click", selector: "button.submit"}type: Text input
selector: Optional — clicks to focus firstpayload: Text to type (\t=Tab, \n=Enter){action: "type", selector: "#email", payload: "user@example.com"}double_click: Double-click element (fires dblclick event)
selector: CSS selector{action: "double_click", selector: ".item"}right_click: Right-click element (fires contextmenu event)
selector: CSS selector{action: "right_click", selector: ".row"}select: Select dropdown option
selector: CSS selectorpayload: Option value(s){action: "select", selector: "select[name=state]", payload: "CA"}keyboard_press: Press special keys (Tab, Enter, Escape, Arrow keys, F1-F12)
payload: Key namemodifiers: Optional {shift, ctrl, alt, meta}{action: "keyboard_press", payload: "Tab"}These use CDP Input.dispatchMouseEvent, bypassing synthetic event restrictions.
hover: Move mouse over element (CSS :hover, tooltips, menus)
selector: CSS selector{action: "hover", selector: ".menu-trigger"}drag_drop: Drag element to target (native drag-and-drop via CDP)
selector: Source elementpayload: Target selector or JSON coordinates {"x":N,"y":N}{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){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{action: "scroll", payload: "down"}selector: File input elementpayload: File path or JSON {"files":["/path/a.pdf","/path/b.jpg"]}{action: "file_upload", selector: "#upload", payload: "/tmp/doc.pdf"}extract: Get page content
payload: Format ('markdown'|'text'|'html')selector: Optional - limit to element{action: "extract", payload: "markdown"}{action: "extract", payload: "text", selector: "h1"}attr: Get element attribute
selector: CSS selectorpayload: Attribute name{action: "attr", selector: "a.download", payload: "href"}eval: Execute JavaScript
payload: JavaScript code{action: "eval", payload: "document.title"}payload: Filenameselector: Optional - screenshot specific element{action: "screenshot", payload: "/tmp/chart.png", selector: ".chart"}list_tabs: List all open tabs
{action: "list_tabs"}new_tab: Create new tab
{action: "new_tab"}close_tab: Close tab
tab_index: Tab to close{action: "close_tab", tab_index: 2}show_browser: Make browser window visible (headed mode)
{action: "show_browser"}hide_browser: Switch to headless mode (invisible browser)
{action: "hide_browser"}browser_mode: Check current browser mode, port, and profile
{action: "browser_mode"}{"headless": true|false, "mode": "headless"|"headed", "running": true|false, "port": 9222, "profile": "name", "profileDir": "/path"}set_profile: Change Chrome profile (must kill Chrome first)
{action: "set_profile", "payload": "browser-user"}get_profile: Get current profile name and directory
{action: "get_profile"}{"profile": "name", "profileDir": "/path"}Default behavior: Chrome starts in headless mode with "superpowers-chrome" profile on a dynamically allocated port (range 9222-12111). Override with CHROME_WS_PORT env var or MCP --port=N flag.
Critical caveats when toggling modes:
When to use headed mode:
When to stay in headless mode (default):
Profile management: Profiles store persistent browser data (cookies, localStorage, extensions, auth sessions).
Profile locations:
~/Library/Caches/superpowers/browser-profiles/{name}/~/.cache/superpowers/browser-profiles/{name}/%LOCALAPPDATA%/superpowers/browser-profiles/{name}/When to use separate profiles:
Profile data persists across:
To use a different profile:
await chromeLib.killChrome(){action: "set_profile", "payload": "my-profile"}Navigate and extract:
{action: "navigate", payload: "https://example.com"}
{action: "await_element", selector: "h1"}
{action: "extract", payload: "text", selector: "h1"}
{action: "navigate", payload: "https://example.com/login"}
{action: "await_element", selector: "input[name=email]"}
{action: "type", selector: "input[name=email]", payload: "user@example.com"}
{action: "type", selector: "input[name=password]", payload: "pass123"}
{action: "keyboard_press", payload: "Enter"}
{action: "await_text", payload: "Welcome"}
Uses keyboard_press to submit the form.
{action: "list_tabs"}
{action: "click", tab_index: 2, selector: "a.email"}
{action: "await_element", tab_index: 2, selector: ".content"}
{action: "extract", tab_index: 2, payload: "text", selector: ".amount"}
{action: "navigate", payload: "https://example.com"}
{action: "type", selector: "input[name=q]", payload: "query"}
{action: "click", selector: "button.search"}
{action: "await_element", selector: ".results"}
{action: "extract", payload: "text", selector: ".result-title"}
{action: "navigate", payload: "https://example.com"}
{action: "await_element", selector: "a.download"}
{action: "attr", selector: "a.download", payload: "href"}
{action: "eval", payload: "document.querySelectorAll('a').length"}
{action: "eval", payload: "Array.from(document.querySelectorAll('a')).map(a => a.href)"}
Use eval to resize the browser window for testing responsive layouts:
{action: "eval", payload: "window.resizeTo(375, 812); 'Resized to mobile'"}
{action: "eval", payload: "window.resizeTo(768, 1024); 'Resized to tablet'"}
{action: "eval", payload: "window.resizeTo(1920, 1080); 'Resized to desktop'"}
Note: This resizes the window, not device emulation. It won't change:
For most responsive testing, window resize is sufficient.
Use eval to clear cookies accessible to JavaScript:
{action: "eval", payload: "document.cookie.split(';').forEach(c => { document.cookie = c.trim().split('=')[0] + '=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/'; }); 'Cookies cleared'"}
Note: This clears cookies accessible to JavaScript. It won't clear:
For most logout/reset scenarios, this is sufficient.
{action: "scroll", payload: "down"}
{action: "scroll", payload: "up"}
{action: "scroll", selector: ".container", payload: "{\"deltaX\":0,\"deltaY\":500}"}
Uses real mouse wheel events (vs eval + scrollTo which bot detectors flag).
Always wait before interaction: Don't click or fill immediately after navigate - pages need time to load.
// BAD - might fail if page slow
{action: "navigate", payload: "https://example.com"}
{action: "click", selector: "button"} // May fail!
// GOOD - wait first
{action: "navigate", payload: "https://example.com"}
{action: "await_element", selector: "button"}
{action: "click", selector: "button"}
Use specific selectors: Avoid generic selectors that match multiple elements.
// BAD - matches first button
{action: "click", selector: "button"}
// GOOD - specific
{action: "click", selector: "button[type=submit]"}
{action: "click", selector: "#login-button"}
Submit forms:
Use keyboard_press with Enter after type, or append \n to the payload.
{action: "type", selector: "#search", payload: "query"}
{action: "keyboard_press", payload: "Enter"}
Check content first: Extract page content to verify selectors before building workflow.
{action: "extract", payload: "html"}
Element not found:
await_element before interactionextract action using 'html' formatTimeout errors:
{timeout: 30000} for slow pagesTab index out of range:
list_tabs to get current indiceseval returns [object Object]:
JSON.stringify() for complex objects: {action: "eval", payload: "JSON.stringify({name: 'test'})"}{action: "eval", payload: "JSON.stringify(await yourAsyncFunction())"}When building test automation, you have two approaches:
Best for: Single-step tests, direct Claude control during conversation
{"action": "navigate", "payload": "https://app.com"}
{"action": "click", "selector": "#test-button"}
{"action": "eval", "payload": "JSON.stringify({passed: document.querySelector('.success') !== null})"}
Best for: Multi-step test suites, standalone automation scripts
Key insight: chrome-ws is the reference implementation showing proper Chrome DevTools Protocol usage. When use_browser doesn't work as expected, examine how chrome-ws handles the same operation.
# Example: Automated form testing
./chrome-ws navigate 0 "https://app.com/form"
./chrome-ws fill 0 "#email" "test@example.com"
./chrome-ws click 0 "button[type=submit]"
./chrome-ws wait-text 0 "Success"
For command-line usage outside Claude Code, see COMMANDLINE-USAGE.md.
For detailed examples, see EXAMPLES.md.
Full CDP documentation: https://chromedevtools.github.io/devtools-protocol/