Install
$ agentstack add skill-devotts-fable-it-chrome-cdp-control ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Chrome CDP Control
Manual, step-by-step control of the user's real Chrome browser via Playwright over Chrome DevTools Protocol. Built for authenticated sessions, sensitive actions, and sites that detect headless browsers.
Operating principle
This skill is a manual loop, not an automation framework. One action, one screenshot, one decision. Never batch. Never loop unattended. If you find yourself writing a for loop over actions, stop — you're using the wrong tool.
0. Preflight (run at the start of every session)
Before any action, verify CDP is reachable:
curl -s http://localhost:9222/json/version
If this fails, stop and tell the user:
> Chrome isn't running with remote debugging. Relaunch it with: > ``bash > "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \ > --remote-debugging-port=9222 \ > --user-data-dir="$HOME/.chrome-automation" \ > --no-first-run & > ``
Do not try to start Chrome yourself — the user launches it manually so they control which profile is exposed.
Also confirm Playwright is installed:
python3 -c "import playwright" 2>&1 || pip3 install playwright
1. The core loop
Every interaction follows this sequence — no exceptions:
- Screenshot with
mcp__computer-use__screenshotto see current state - Decide what single action moves the task forward
- Execute one Playwright action via `python3 About to: Post the following tweet to the user's account on X:
> > "Testing the new SEMrush connector — domain resolution working end to end." > > Confirm to proceed?
No exceptions. Even if the user said "yes" to a similar action earlier in the conversation, re-confirm for each new write.
7. Failure protocol
If a screenshot after an action does not show the expected state:
- Take one more screenshot — sometimes the page is mid-render
- Try one alternative selector (e.g., role instead of text)
- If still failing: STOP. Do not retry a third time. Report:
- What you tried to do
- What you see on screen now
- What you expected to see
- Two or three options for how to proceed
Common failure causes — check for these before retrying:
- CAPTCHA or bot challenge appeared → STOP, tell the user, do not attempt to solve
- Login wall / session expired → STOP, ask the user to log in manually
- Modal / cookie banner blocking the target → handle the modal first (with user's approval if it's destructive)
- Element re-rendered with new attributes → re-snapshot the DOM
- Wrong tab → list tabs, verify
Never loop "click → fail → click → fail" more than twice.
8. Clipboard pasting (macOS)
For long text or text with special characters, prefer clipboard over keyboard.type:
mcp__computer-use__write_clipboardwith the text- Focus the target element via Playwright:
await page.locator('[contenteditable]').focus() - Trigger paste:
await page.keyboard.press("Meta+v") - Screenshot to verify
Caveat: OS-level focus must be on Chrome. If the user is in another app, the paste lands in the wrong window. When in doubt, ask the user to confirm Chrome is the foreground window.
9. Action log
For any session that touches a logged-in account, append a one-line audit entry per action:
echo "$(date -u +%FT%TZ) [chrome-cdp] $ACTION_DESCRIPTION" >> ~/.chrome-automation-actions.log
Cheap insurance. Helps debugging and gives the user a paper trail of what was done on their behalf.
10. Canonical action template
Use this exact wrapper for every Python action. Fill in the === ACTION === block.
python3 << 'PYEOF'
import asyncio
from playwright.async_api import async_playwright
async def go():
pw = await async_playwright().start()
browser = await pw.chromium.connect_over_cdp('http://localhost:9222')
ctx = browser.contexts[0]
# Find target tab by URL substring (NEVER pages[0] blindly)
page = next((p for p in ctx.pages if "TARGET_URL_FRAGMENT" in p.url), None)
if page is None:
page = await ctx.new_page()
await page.goto("https://TARGET_URL", wait_until="domcontentloaded", timeout=30000)
# === ACTION ===
# Exactly ONE of: navigate, click, type, evaluate, read
# Example:
await page.get_by_role("button", name="Post").click()
# ==============
# Verify with an explicit wait, not a sleep
await page.wait_for_load_state("networkidle", timeout=10000)
print("OK:", page.url)
await pw.stop()
asyncio.run(go())
PYEOF
JS inside page.evaluate(): use double quotes for JS strings, since the outer wrapper is triple-double-quoted heredoc:
await page.evaluate('document.querySelector("[contenteditable]").focus()')
11. Hard rules — never violate
- ❌ Never run a
forloop over actions - ❌ Never run more than ~20 actions in a session without checking in with the user
- ❌ Never solve CAPTCHAs or bot challenges
- ❌ Never close tabs the user didn't authorize
- ❌ Never retry a failed action more than twice
- ❌ Never skip the destructive action gate, even for "small" writes
- ❌ Never assume
pages[0]is the right tab - ❌ Never trust auto-generated CSS class names
- ✅ Always screenshot before and after every action
- ✅ Always preflight CDP at session start
- ✅ Always re-resolve page and elements (no state persists)
- ✅ Always log writes to
~/.chrome-automation-actions.log
12. When NOT to use this skill
- Fetching public, unauthenticated content → use
web_fetch - Scraping hundreds of pages → use a standalone headless Playwright script outside this loop
- Anything the target service exposes via API → use the API (faster, safer, auditable)
- Anything that doesn't need the user's logged-in session
If you're not sure whether this skill applies, ask the user before connecting to CDP.
Authored by DevOtts._
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: DevOtts
- Source: DevOtts/fable-it
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.