concepts
How it works
Brain, medium, hands — the agent decides, the bridge carries the message, a real Chrome does the reaching. And why that beats headless.
Pluckor is three parts in one loop: the agent (brain), the bridge (medium), and a real Chrome (hands).
agent ── MCP/stdio ──▶ plk mcp (proxy)
│ WebSocket (control · :9235)
▼
plk daemon ──▶ Chrome for Testing + Pluckor extension
│ │
│ WebSocket (:9234) │ content script (reads, no CDP)
└────────────────────▶ │ chrome.debugger (run_js/click/type)
▼
the live page
The agent calls a tool → the proxy forwards it to the daemon → the daemon drives the extension → the extension acts on the page and sends the result back the same way. Everything binds to 127.0.0.1; the browser is never exposed off-box.
Reads vs. interactions
This split is the heart of Pluckor’s stealth:
- Reads —
navigate,get_html,get_markdown,wait_for_selector,snapshot,extract,extract_links,wait_for_response,wait_for_network_idle,wait_for_human,capture_console,select_option,go_back,go_forward,reload,get_cookies,set_cookie,get_local_storage,set_local_storage,open_tab,list_tabs,close_tab,capture_requests(metadata) — run throughchrome.tabs,chrome.webRequest, and a content script. No CDP. Nonavigator.webdriver. There is essentially no automation fingerprint on a read. - Interactions —
run_js,click,type,press_key,hover,wait_for_function,scroll(gesture) — attachchrome.debugger(CDP) only while they run, then detach. Clicks and typing are trusted (isTrusted = true), so JS handlers accept them. screenshotcaptures the page as an image and spans both — its default viewport andscroll(stitched) modes stay no-CDP;fullPageand element capture use CDP.
Prefer reads; escalate to interactions only when you must. On the most aggressively-monitored targets, favor get_html + parse over run_js.
Why Chrome for Testing, not your everyday Chrome
- Chrome 137+ removed the
--load-extensionflag in branded Chrome, so the bridge can’t auto-load an unpacked extension into the Chrome in your dock. Chrome for Testing is the same Chromium engine with the flag intact. - It’s fingerprint-identical to normal Chrome in headful mode — same user agent, same rendering stack. Detectability comes from how a browser is driven (CDP, automation flags, a throwaway profile), not from the binary. Pluckor launches Chrome for Testing with no automation flags and drives reads from inside the browser.
Why not headless?
Headless works technically — but the user agent becomes HeadlessChrome/…, which is the single clearest bot-detection tell. Running headless to beat Cloudflare or DataDome is self-defeating. The differentiated thing is precisely the local, headful, real browser — a window that opens on your machine, with your profile and your residential IP.
Everything is local
The daemon runs a single browser on fixed local ports (:9234, :9235), both bound to 127.0.0.1. Concurrent agent sessions share the one browser rather than each launching their own. Tabs are isolated per connection — each proxy (each plk mcp lane) gets its own default tab, so multiple agents share the one warm browser without colliding, and open_tab lets a single agent drive several tabs at once. See Multiple tabs. For agents doing unrelated work that shouldn’t share logins at all, set PLUCKOR_INSTANCE to run a fully separate browser — its own profile, logins, and ports. Nothing is reachable from another machine. The control channel can also be deliberately exposed to drive a containerized or remote browser from an agent running elsewhere — see Deployment.
Recovering the daemon
Because the daemon is long-lived and shared, it can outlast an upgrade or wedge. A dropped control socket reconnects on its own; a stale or outdated daemon is replaced with a single restart — from the CLI or the restart tool. See Recovering a stuck browser.