Webhands
БесплатноНе проверенA CLI (and MCP server) that drives a real, persistent browser via Playwright, letting an agent or human control any website from a genuinely logged-in browser s
Описание
A CLI (and MCP server) that drives a real, persistent browser via Playwright, letting an agent or human control any website from a genuinely logged-in browser session.
README
Let your AI agent drive a real, logged-in browser on your own machine.
webhands is a small CLI (it also doubles as an MCP server, thanks to incur) that gives an agent (or you) hands on a real Chromium browser. It reuses a browser YOU logged into yourself, on YOUR machine and YOUR IP, so the agent can read and act on the web apps you already use, the way you would by hand.
The idea in one line: you log in once in a window you can see; after that your agent can open pages, read them, click, type, and run whole sub-flows against that same live, authenticated session.
Under the hood it launches (or attaches to) Chromium with a dedicated profile,
keeps one browser alive behind a long-lived serve process, and exposes page
verbs (goto, snapshot, click, type, eval, script, wait, cookies)
that print clean, structured output an agent can read cheaply. The composable
verbs are the floor; script is the power-user ramp that runs a driver-context
Playwright function against the live page so an agent can batch a whole sub-flow
into ONE call (see the Security note and
docs/adr/0012).
New here? Jump to: Use it via your AI agent (the 30-second start) · Does it deliver? (measured vs Playwright) · Scope and honesty (what it will and will not do).
Use it via your AI agent (start here)
The simplest way to use webhands is to let your coding agent (Claude Code,
Cursor, etc.) run it through plain bash with npx. No MCP wiring, no install
step — the agent just runs npx webhands <verb> commands. The first run of
npx webhands fetches the package automatically.
Give your agent something like: "Use webhands to open Kayak and read me the
live prices for EDI→BOM on 31 Oct." A capable agent will then:
# 1. start & HOLD the browser. serve blocks, so the agent backgrounds it:
nohup npx webhands serve --headed > /tmp/webhands.log 2>&1 &
sleep 12 && cat /tmp/webhands.log # confirm it printed an endpoint + pid
# 2. navigate the live page (separate invocation, same browser):
npx webhands goto 'https://www.kayak.co.uk/flights/EDI-BOM/2026-10-31?sort=price_a'
# 3. let JS results render, then read the page token-cheaply:
npx webhands wait --ms 8000
npx webhands snapshot --token-limit 6000
# 4. always tear down when done:
npx webhands stop
Three things a new user should know up front:
- You log in once, in a window you can see. Run
npx webhands setup-profile(or start withserve --headed) and sign in / clear any cookie or anti-bot prompt yourself. That state is saved to a dedicated profile and reused on later runs. The tool never bypasses logins or solves CAPTCHAs — you do that part. - It acts as the real, logged-in you. Reading pages is low-risk; let the agent do that freely. But anything that spends money, books, posts, or changes account state should be YOUR explicit decision — have the agent surface the link and let you finish checkout. (See Scope and honesty below.)
- Anti-bot sites may need the visible window. Headless runs can hit a
"you look like a bot" page on sites like Kayak. The fix is to run
--headedand clear the challenge yourself once, not to defeat it.
For the full agent playbook (workflow, gotchas, guardrails) AND a complete
per-verb reference, install the bundled skill: npx webhands skills add then look
for use-webhands. A skilled agent drives the whole surface from that skill and
does NOT need to re-dump --help/--llms-full at runtime; those discovery dumps
(npx webhands <verb> --help, npx webhands --llms-full) stay available for
human exploration or an obscure flag.
Output is lean by default. Every verb prints just its structured result; the
old per-result "Suggested command" next-step breadcrumbs are suppressed (an agent
never reads them, so they were pure token overhead). Exploring by hand and want
the breadcrumbs back? Add --cta (alias --hints) to any verb, or set
WEBHANDS_CTA=1 to pin them on for your shell.
How it works (the pipe)
The browser is owned by ONE long-lived serve process; each verb invocation is a
thin client that drives the SAME live page and exits (see
docs/adr/0005). The
typical end-to-end flow:
webhands setup-profile: opens the dedicated profile in a VISIBLE browser so you log in / clear any anti-bot challenge ONCE. State (cookies, login, challenge clearance) persists on disk.webhands serve --headless: launches the one browser against that saved profile and keeps it alive (runs untilstopor Ctrl-C).webhands goto <url>thenwebhands snapshot(andclick/type/eval/wait): separate invocations that all drive the single live page the server holds.webhands stop: tears the session down.
A verb run with no live server prints a clear error telling you to run serve
first; the tool never silently spawns a browser.
Does it deliver? (the capability scoreboard)
Yes, and there is a measured answer, not just a claim. The eval harness runs the SAME real-site goal with two toolkits, a webhands agent and a raw-Playwright-only baseline, and compares them on outcome (did it finish the job?) and token cost. Full numbers, every raw run line, and the honest caveats live in evals/SCOREBOARD.md.
The short version, latest runs first:
| Kind of flow | webhands (best) | raw Playwright | Who wins |
|---|---|---|---|
| Messy / unfamiliar DOM the agent must explore to act | PASS, ~0.7M tokens | PASS, ~1.3M | webhands (both pass; ~1.9x cheaper) |
| Dynamic goal that only resolves from live page state | PASS, ~1.7 to 2.5M | PASS, ~1.6M | tie (both pass) |
| Trivial, one-shot-scriptable sandbox flow | PASS | PASS, cheaper | Playwright (a blind script needs no exploring) |
So the honest reading is: on the flows webhands is built for (messy, changing, explore-then-act, dynamic, anti-bot pages) webhands matches or beats raw Playwright on BOTH outcome and tokens. On a trivial page a human could script in one blind pass, raw Playwright is cheaper, exactly as you'd expect: webhands drives a composable verb surface, so it shines precisely where "write one script and run it blind" breaks down. See the Latest results first summary for the corrected reading (earlier tables show a bigger gap from a cold, unskilled agent and an unfair baseline, both since fixed).
The harness is non-gating and never part of pnpm test.
Where hands change the game (above the verbs-vs-Playwright line)
The scoreboard above compares the raw verb surface against raw Playwright. A hand (a pluggable capability module, see Scope and honesty below) sits ABOVE that comparison and beats it on two separate axes:
- New capability raw Playwright cannot reach at all. A captcha-solving hand
(
iamhuman) is not "cheaper Playwright": it is a capability webhands deliberately does not ship (a provider key + solving logic) plugged in as one verb. Raw Playwright gives an agent a page to poke; it does not give it a captcha solver. So on a captcha-gated flow the honest comparison is not "webhands vs Playwright on tokens," it is "reaches the goal vs does not." - Token collapse on flows Playwright CAN do. Everything the scoreboard shows about narrowing the gap is about an agent driving a flow it must RE-EXPLORE each run. A hand encodes that flow ONCE, so a known sub-flow (log in, run a search, complete a checkout) becomes a single cheap verb call instead of an N-turn explore-decide-act loop the agent re-pays every time. The scoreboard's messy-DOM win already hints at this (the webhands agent wins partly by not re-deriving boilerplate each run); a hand takes it to the limit by authoring the flow into one call.
So: verbs are the floor (makes the flow POSSIBLE), and a hand is both the
ceiling (a NEW capability) and the accelerator (a known flow becomes one cheap
call). Authoring that hand cheaply, straight from a flow the agent just drove
successfully, is an incubating idea (a distill verb,
work/notes/ideas/distill-session-into-hand.md).
Scope and honesty (please read)
This is a personal-use tool. Its whole premise is that you drive a browser you logged into yourself, on your own machine and your own IP, reusing your own authenticated session (see docs/adr/0002). It is deliberately local and single-session by design.
- No login-bypass, no built-in CAPTCHA solver. The human does the one-time
login and clears any anti-bot challenge in the headed
setup-profilestep. webhands ships NO captcha solver and NO provider key, and does not bypass authentication itself. What changed: the verb surface is now rich enough that it no longer STANDS IN THE WAY of a capable agent that brings its OWN key. Such an agent can get past a captcha by poking the page with verbs, both families: the token-harvest family by reading the sitekey with a frame-awarequery,typeing a provider token into the response sink, and firing the callback; the vision/tile family with the coordinatemouse, the element-clippedscreenshot, and the cross-origin frame read. We do not solve it; we no longer stand in the way. The agent supplies its own key and its own logic (or uses a hand, below). webhands is capable, not a solver. - Hands are the ceiling AND the accelerator. A hand is a capability
module (
iamhumantoday, a future buy-on-amazon hand) that closes over the live page and makes the hard thing ONE call. It earns its keep two ways (see Where hands change the game under the scoreboard). First, a NEW capability raw Playwright cannot reach at all: a captcha-solving hand plugs in solving logic + a provider key webhands itself does not ship. Second, TOKEN COLLAPSE on flows an agent could otherwise drive: a known sub-flow (log in, search, checkout) is authored into the hand ONCE, so it becomes a single cheap call instead of an N-turn explore loop the agent re-pays every run. The verb surface is the floor that makes the unaided path POSSIBLE; a hand is the ramp that makes it EASY and CHEAP. (A hand is a trusted in-process peer, loaded only when you name it inhands.json; see docs/adr/0007.) - No fingerprint-spoofing / anti-detect tricks. It leans on being a real
browser/profile/IP rather than spoofing. There is no proxy rotation or
anti-detect build here. (A single, user-chosen SOCKS proxy for traffic/DNS
control is available opt-in via
--proxy; see Optional: route traffic and DNS through a SOCKS proxy below.) - Your own session only. A replayed/stolen cookie does not work anyway (clearance is bound to the browser fingerprint and IP, not just the cookie); the design assumes the session is genuinely yours.
In short: this is for reading and acting on web apps you already have an account on, from your own browser, the way you could by hand.
Optional: stealth launch (opt-in, default OFF)
Standard Playwright drives Chromium over CDP and calls Runtime.enable at
startup. That emits a side-effect a few lines of page JS can detect, and some
anti-bot WAFs (Imperva/Cloudflare/DataDome) use it to serve an "Access Denied"
block page before the page even renders — even on a real residential IP, even
headed. @webhands/core can optionally launch via
Patchright (an
API-compatible Playwright fork that patches exactly these CDP leaks) to remove
that one tell.
This is off by default — vanilla Playwright stays the default. To enable it:
Install the optional dependency (it is NOT pulled in unless you ask for it):
pnpm add patchright # if you do NOT pass --use-system-browser chrome, also fetch its browser: # pnpm exec patchright install chromiumBring the session up with
--stealth. The realistic recipe also drives your installed system browser (--use-system-browser chrome), headed, against a warmed, logged-in profile:# serve consumes these (it is where the browser is launched, ADR-0005): npx webhands serve --headed --stealth --use-system-browser chrome--use-system-browseris independent of--stealth: you can drive real Chrome with or without the Patchright path, and stealth with or without a system browser. Other channel names work too (e.g.msedge).Optional extra hardening.
--no-viewportlets the real browser window drive its own size instead of Playwright's fixed 1280x720 emulated viewport (a known headless tell). It is defaulted ON under--stealth(Patchright's recommended recipe) and is overridable; pass--viewportto keep the fixed viewport even under stealth. webhands deliberately does not overrideuser-agent,locale,timezone, orheaders: a wrong UA is a bigger tell than none.
Programmatic equivalent (the --stealth / --use-system-browser /
--no-viewport flags map onto these transport options; the constructor also
takes extraLaunchArgs and ignoreDefaultArgs escape hatches for additional
hardening flags, none of which touch the OpenTarget seam):
import {PlaywrightLaunchTransport} from '@webhands/core';
const transport = new PlaywrightLaunchTransport(
{}, // profile location (omit for ~/.webhands)
[], // extra hands
{stealth: true, systemBrowser: 'chrome'}, // noViewport defaults to true here
);
// Stealth + headed + a real logged-in profile is the strongest recipe:
const session = await transport.open({
mode: 'launch',
profile: 'default',
headed: true,
});
If stealth is enabled but patchright is not installed, the open throws a typed
MissingStealthDependencyError (the CLI prints pnpm add patchright as the fix).
It never silently falls back to vanilla Playwright, because that would put
the tell back without telling you.
Honest caveat. Stealth addresses ONLY the CDP Runtime.enable automation
tell, and the launch-hardening knobs (--no-viewport, extraLaunchArgs,
ignoreDefaultArgs) reduce but do not eliminate detection. They are
necessary-but-not-sufficient: IP reputation and session/profile
reputation still matter. The realistic recipe is stealth +
systemBrowser: 'chrome' + headed + a warmed, logged-in profile + a residential
IP (see
docs/adr/0002).
Optional: route traffic and DNS through a SOCKS proxy (opt-in, default OFF)
By default webhands connects directly on your own machine and IP. If you want
the browser to egress through a chosen SOCKS proxy (a VPN exit, an SSH/Tor SOCKS
endpoint, a residential proxy), pass --proxy <socks-url> to serve (or
launch). It routes all browser traffic AND DNS through that one proxy:
# socks5h:// tunnels DNS through the proxy too (no DNS leak):
npx webhands serve --headed --proxy socks5h://127.0.0.1:1080
# with credentials:
npx webhands serve --proxy socks5h://user:pass@host:1080
socks5h://means no DNS leak. webhands adds Chromium's--host-resolver-rulescatch-all so even side channels (the DNS prefetcher) cannot leak a raw local DNS query; only the proxy's own host is resolved locally. This is the recommended form.socks5://(orsocks://) allows local DNS. Use it when you deliberately want split DNS. URL loads still resolve at the proxy, but Chromium may issue some local DNS. Override either way with the programmaticproxyNoLeakoption.- A malformed
--proxyvalue fails loudly with a typedInvalidProxyError(it never silently launches unproxied, which would leak the traffic you asked to tunnel).
Programmatic equivalent:
import {PlaywrightLaunchTransport} from '@webhands/core';
const transport = new PlaywrightLaunchTransport(
{}, // profile location
[], // extra hands
{proxy: 'socks5h://127.0.0.1:1080'}, // all traffic + DNS via the proxy, no leak
);
Honest caveat. A proxy changes your IP and DNS path; it does not by itself defeat bot detection, and a proxy/VPN/datacenter IP often reads WORSE than a clean residential one. This is a deliberate, scoped opt-in deviation from the "own IP" default (see docs/adr/0009).
Security note (the serve endpoint runs arbitrary code)
The page verbs execute caller-supplied expressions: eval runs a JS expression
in the page, script runs a driver-context JS function handed the full live
Playwright page (so one call can batch a locate/act/wait/read sub-flow), and a
click/type locator is a raw Playwright locator EXPRESSION the controller
evaluates (see
docs/adr/0004
and
docs/adr/0012).
That is by design for a LOCAL tool driven by its own agent against your own
session, but it means the running serve endpoint is a code-execution surface.
script is the SAME page-script surface as eval (caller JS against your own
session), widened from one page-world expression to a driver-context body + the
page object — NOT a new privilege, and NOT the larger hands.json hand-loading
(npm-dependency) surface: it reads and runs a JS source FILE (a path you pass,
npx webhands script ./flow.js), it loads no
module (see docs/adr/0012).
The same loopback-only rule below covers it.
- Do NOT expose the
serveendpoint to untrusted callers. Keep it bound to localhost (the default); never bind it to a public interface or hand its URL to code you do not trust. Anyone who can call it can run arbitrary JavaScript in your logged-in session (eval,script, and the raw Playwright locators).
Установить Webhands в Claude Desktop, Claude Code, Cursor
unyly install webhandsСтавит в Claude Desktop, Claude Code, Cursor и VS Code — сам разбирается с npx, uvx и сборкой из исходников.
Впервые? Поставь CLI: curl -fsSL https://unyly.org/install | sh
Или настроить вручную
Выполни в терминале:
claude mcp add webhands -- npx -y webhandsFAQ
Webhands MCP бесплатный?
Да, Webhands MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Webhands?
Нет, Webhands работает без API-ключей и переменных окружения.
Webhands — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить Webhands в Claude Desktop, Claude Code или Cursor?
Открой Webhands на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
Playwright
Browser automation, scraping, screenshots
автор: MicrosoftPuppeteer
Browser automation and web scraping.
автор: modelcontextprotocolopentabs-dev/opentabs
Plugin-based MCP server + Chrome extension that gives AI agents access to web applications through the user's authenticated browser session. 100+ plugins with a
автор: opentabs-devrobhunter/agentdeals
1,500+ developer infrastructure deals, free tiers, and startup programs across 54 categories. Search deals, compare vendors, plan stacks, and track pricing chan
автор: robhunterCompare Webhands with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории browse
