{"skill":{"slug":"chrome-cdp-remote","displayName":"Remote Chrome CDP","summary":"Control Chrome/Chromium via CDP (Chrome DevTools Protocol) — open tabs, navigate URLs, take screenshots, execute JS. Supports local and remote machines via S...","description":"---\nname: chrome-cdp-remote\ndescription: Control Chrome/Chromium via CDP (Chrome DevTools Protocol) — open tabs, navigate URLs, take screenshots, execute JS. Supports local and remote machines via SSH tunnel.\ntags: [browser, automation, cdp, chrome, screenshots, devtools, remote]\n---\n\n# Chrome CDP Remote Control\n\nUse when: automating browser tasks programmatically — navigate pages, capture screenshots, run searches, execute JavaScript — without a full Playwright/Puppeteer dependency.\n\n---\n\n## Setup: Launch Chrome with CDP enabled\n\n### Linux\n```bash\ngoogle-chrome \\\n  --remote-debugging-port=9222 \\\n  --remote-allow-origins=* \\\n  --user-data-dir=~/.config/chrome-cdp\n```\n\n### macOS\n```bash\n/Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome \\\n  --remote-debugging-port=9222 \\\n  --remote-allow-origins=* \\\n  --user-data-dir=\"$HOME/.config/chrome-cdp\"\n```\n\nNote: Use a dedicated `--user-data-dir` (e.g. `~/.config/chrome-cdp`) — Chrome 126+ blocks CDP on the default profile by design. Log in with your Google account once to enable sync.\n\nNote: On macOS, omit `--user-data-dir` only if you don't need sync — it gives you your real profile with extensions and custom shortcuts.\n\n### Verify CDP is running\n```bash\ncurl http://localhost:9222/json/version\n```\nShould return browser version and a `webSocketDebuggerUrl`.\n\n### Kill and relaunch\n```bash\npkill -f 'chrome.*remote-debugging-port=9222'\nsleep 2\n# relaunch with flags above\n```\n\n---\n\n## Setup: Remote machine via SSH tunnel (recommended)\n\nForward the remote machine's CDP port to localhost:\n```bash\nssh -L 9223:localhost:9222 user@remote-host -N &\n```\nThen use `PORT = 9223` in your scripts. Works great over Tailscale.\n\n---\n\n## macOS: Raycast Script Command\n\nSave as `~/.config/raycast/scripts/chrome-cdp.sh` and `chmod +x`:\n\n```bash\n#!/bin/bash\n# @raycast.schemaVersion 1\n# @raycast.title Chrome CDP\n# @raycast.mode silent\n# @raycast.icon 🌐\n# @raycast.description Launch Chrome with CDP enabled\n\npkill -f \"Google Chrome.*remote-debugging-port\" 2>/dev/null\nsleep 1\n\n/Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome \\\n  --remote-debugging-port=9222 \\\n  --remote-allow-origins=* \\\n  --user-data-dir=\"$HOME/.config/chrome-cdp\" &\n\necho \"Chrome CDP started on port 9222\"\n```\n\n---\n\n## Python: WebSocket control pattern\n\nInstall dependency (once):\n```bash\npip install websocket-client\n```\n\n```python\nimport json, urllib.request, websocket, time, base64\n\nPORT = 9222  # use 9223 if connecting via SSH tunnel\n\ndef send_recv(ws, method, params, msg_id):\n    \"\"\"Send a CDP command and wait for its response, draining intermediate events.\"\"\"\n    ws.send(json.dumps({\"id\": msg_id, \"method\": method, \"params\": params}))\n    for _ in range(30):\n        msg = json.loads(ws.recv())\n        if msg.get(\"id\") == msg_id:\n            return msg\n    return None\n\n# Open a new tab\nreq = urllib.request.Request(f\"http://localhost:{PORT}/json/new\", method=\"PUT\")\ntab = json.loads(urllib.request.urlopen(req).read())\n\n# Connect via WebSocket — origin header is required\nws = websocket.WebSocket()\nws.connect(tab[\"webSocketDebuggerUrl\"], origin=f\"http://localhost:{PORT}\")\nws.settimeout(15)\n\nmsg_id = 1\nsend_recv(ws, \"Page.enable\", {}, msg_id); msg_id += 1\n\n# Navigate to a URL\nsend_recv(ws, \"Page.navigate\", {\"url\": \"https://example.com\"}, msg_id); msg_id += 1\ntime.sleep(5)  # wait for page load\n\n# Take a screenshot\n# Use fromSurface + scale=2 on Retina/HiDPI displays (e.g. MacBook Pro)\nresult = send_recv(ws, \"Page.captureScreenshot\", {\n    \"format\": \"jpeg\",\n    \"quality\": 70,\n    \"fromSurface\": True,\n    \"scale\": 2  # remove on non-Retina displays\n}, msg_id); msg_id += 1\n\nimg = base64.b64decode(result[\"result\"][\"data\"])\nwith open(\"/tmp/cdp_screenshot.jpg\", \"wb\") as f:\n    f.write(img)\n\nws.close()\nprint(\"Screenshot saved.\")\n```\n\n---\n\n## Custom search engine shortcuts (bangs)\n\nIf Chrome is launched with a real user profile, custom search engine shortcuts are available.\n\nConfigure in Chrome → Settings → Search engine → Manage search engines.\n\nExample — add Perplexity as `!ppx`:\n- Shortcut: `!ppx`\n- URL: `https://www.perplexity.ai/search?q=%s`\n\nTrigger via address bar: type `!ppx` then Tab, then your query.\n\nWhen controlling via CDP from a different machine, use the full URL directly:\n```\nhttps://www.perplexity.ai/search?q=YOUR+QUERY\nhttps://www.google.com/search?q=YOUR+QUERY\n```\n\n---\n\n## CDP patterns\n\n- Never connect to `ws://localhost:9222/devtools/browser` directly — always fetch `http://localhost:9222/json/list` and use `webSocketDebuggerUrl` from the target tab\n- `Runtime.enable` and `Page.enable` must be called before any `Runtime.evaluate` or `Page.navigate`\n- `Network.responseReceived` doesn't include body — call `Network.getResponseBody` with requestId after response completes\n- Block URLs before navigate: call `Network.setBlockedURLs` before `Page.navigate`, not after\n\n---\n\n## Pitfalls\n\n- `--remote-allow-origins=*` is required — omitting it causes `403 Forbidden` on WebSocket handshake\n- Chrome 126+ blocks CDP on the default profile — always use a dedicated `--user-data-dir`\n- Always filter `ws.recv()` by message ID — CDP sends intermediate events between your command and its response\n- SSH tunnel holds the port after Chrome dies — kill the tunnel before relaunching (`kill %1` or `pkill ssh`)\n- macOS: omit `--user-data-dir` only if you want your real profile with bangs/extensions (and don't mind Chrome not starting if already open)\n- Chromium-based alternatives (Helium, Brave, etc.) support CDP but may behave inconsistently — Chrome is most reliable\n","tags":{"latest":"1.0.0"},"stats":{"comments":0,"downloads":645,"installsAllTime":24,"installsCurrent":0,"stars":1,"versions":1},"createdAt":1774039821270,"updatedAt":1779078591505},"latestVersion":{"version":"1.0.0","createdAt":1774039821270,"changelog":"Initial release — enables remote and local browser automation via Chrome DevTools Protocol (CDP).\n\n- Control Chrome/Chromium: open tabs, navigate URLs, take screenshots, and execute JavaScript via simple commands.\n- Supports connections to both local and remote machines (with SSH port forwarding).\n- Includes detailed setup instructions for Linux and macOS, including optional Raycast integration.\n- Provides Python example for WebSocket-based CDP control and automation patterns.\n- Notes on search engine shortcut support, best practices, and common CDP pitfalls.","license":"MIT-0"},"metadata":null,"owner":{"handle":"noestelar","userId":"s173x2bdr73z23k4xqx181h35s83g3v9","displayName":"Noé Rivera","image":"https://avatars.githubusercontent.com/u/95829890?v=4"},"moderation":null}