⭐ If you like Orchords Web Pilot or find it useful, please consider starring this repository. It helps more people discover the project.
Interested in sponsoring ORCHORDS? Sponsorships start at US$1,000. Depending on the sponsorship level, sponsors may receive public recognition, logo and website placement, sponsor updates and early previews, roadmap-feedback briefings, priority issue triage, and engineering or integration discussions. Sponsorship does not buy control of the roadmap or guarantee feature implementation. Contact crm@orchords.com.
Independent software studio founded in 2025.
A Model Context Protocol server that gives coding agents a real browser — navigate, observe, interact, and capture proof on any web page through one transport-agnostic surface.
Orchords Web Pilot is a Model Context Protocol server that exposes a Playwright-backed browser session as a set of agent-friendly tools. Point your agent at it and it can navigate, observe, interact, and capture proof on any web page — same model across local and hosted browsers, same surface across stdio and Streamable HTTP transports.
| If you need… | Start with |
|---|---|
| Install and first run | Install |
| Wire it into a desktop agent | stdio (default) |
| Run it as a hosted service | Streamable HTTP |
| Use a remote browser grid | Local or remote browser |
| Available tools and parameters | Tools · Configuration |
| Security guidance | Security · SECURITY.md |
| Contributing process | Contributing |
- Real Chromium session — local via Playwright or remote via any WebSocket endpoint (Browserless, hosted Chrome, your own grid)
- Small, well-typed tool surface — navigate, snapshot, click, type, screenshot, drag, press-key, hover, evaluate JS, capture console + network, fill forms, manage tabs
- One transport for everything — stdio for desktop agents, Streamable HTTP for hosted and multi-user setups
- Auto-detection — set
BROWSER_WS_ENDPOINTto flip to a remote browser; everything else stays the same - Accessibility-first — every interaction accepts an element target from the agent's snapshot, not raw pixel coordinates
npm install -g orchords-web-pilotOr run it straight from a checkout:
git clone https://github.com/ORCHORDS/OrchordsBrowserPilot.git
cd OrchordsBrowserPilot
npm install
npm run build
npm start- Node.js:
>=20.10. - Playwright:
>=1.59.1 <2.0.0. Web Pilot uses the AI-mode accessibility snapshot APIs introduced in the Playwright 1.59 line; startup fails with an actionable error if package-manager overrides force an older or unsupported major version. - Browser binaries: Playwright browser revisions are coupled to the installed Playwright package. After changing Playwright versions, install the matching browser with
npx playwright install chromium(ornpx playwright install --with-deps chromiumon a fresh Linux runner). - Verification policy: every
mainpush exercises the declared minimum Playwright 1.59.1 with a real browser and also installs the packed npm artifact into a clean fixture. The scheduled Daily Build deliberately overrides the lockfile with the current stableplaywright@latestto detect upstream compatibility drift before the supported range is changed.
Web Pilot does not claim support for arbitrary older Playwright installations just because npm can be forced to resolve them. The declared dependency range, runtime guard, package smoke test, and CI compatibility lanes are the support contract.
Add to your MCP client config (Claude Desktop, ZCode, etc.):
{
"mcpServers": {
"orchords-web-pilot": {
"command": "orchords-web-pilot",
"env": {
"PILOT_HEADLESS": "true"
}
}
}
}PILOT_TRANSPORT=http \
PILOT_HTTP_HOST=0.0.0.0 \
PILOT_HTTP_PORT=8788 \
PILOT_HTTP_PATH=/mcp \
npm startThen point your client at http://<host>:8788/mcp.
By default Web Pilot launches a local Chromium via Playwright. To use a remote browser (Browserless, hosted Chrome, your own grid), set:
export BROWSER_WS_ENDPOINT="wss://chrome.browserless.io?token=..."The server auto-detects: if BROWSER_WS_ENDPOINT is set it connects to that;
otherwise it spins up a local browser.
| Tool | What it does |
|---|---|
browser_navigate |
Open a URL. |
browser_snapshot |
Return an accessibility tree (preferred for agent planning). |
browser_click |
Click by ref (from snapshot), selector, or coordinate. |
browser_type |
Type into the focused element. |
browser_fill |
Set an input value directly. |
browser_press |
Press a key (Enter, Tab, Escape, arrow keys…). |
browser_hover |
Hover an element. |
browser_drag |
Drag from one element to another. |
browser_select |
Choose an <option> in a <select>. |
browser_screenshot |
Capture PNG (returns base64 or saves to disk). |
browser_evaluate |
Run a JS expression in the page context. |
browser_console |
Read console messages captured for this session. |
browser_network |
List network requests captured for this session. |
browser_wait |
Wait for text, selector, or a fixed duration. |
browser_captcha_solve |
Plug into an external captcha-solving service. |
browser_snapshot returns an accessibility tree in which every interactive node carries a ref token, e.g.:
- generic [active] [ref=e1]:
- heading "Settings" [level=1] [ref=e2]
- button "Save" [ref=e3]
- textbox "Email" [ref=e4]Pass the token back into any interaction tool: browser_click({ "ref": "e3" }). Refs are resolved to scoped Playwright locators (role + accessible name + occurrence index), so they work even when CSS selectors would be brittle.
Ref lifetime. Refs are valid from the moment browser_snapshot returns until the next navigation (or another snapshot replaces them). After a browser_navigate, all previous refs are invalidated. Using an invalidated ref returns a structured error (Ref 'eN' is no longer valid…) rather than clicking the wrong element — take a fresh browser_snapshot and retry.
Fallback. Every interaction tool also accepts a raw CSS selector (and browser_click accepts x/y coordinates). Prefer refs for agent flows; use selectors only when you already know the DOM.
Sessions. Each MCP session owns its own page, console buffer, and network buffer. On the HTTP transport, sessions are keyed by the Mcp-Session-Id header; on stdio there is a single session for the process lifetime. Console and network diagnostics never leak across sessions.
The captcha-solver tool is a hook: it reads
PILOT_CAPTCHA_SOLVER_URLandPILOT_CAPTCHA_SOLVER_TOKENand forwards the challenge. Wire in your own provider (2Captcha, AntiCaptcha, your own microservice) — the MVP does not ship a default to keep the licensing surface clean.
The HTTP endpoint is hardened by default (#43):
- Origin validation — any request carrying an
Originheader must match the allowlist (loopback variants by default) or it is rejected with403. This blocks malicious websites from driving your local browser viafetchagainstlocalhost:8788, including sandboxednullorigins. - Host validation —
Hostmust match the bound host or the allowlist, defeating DNS-rebinding attacks. - Public-bind refusal — binding to a non-loopback address (including
0.0.0.0) fails at startup unlessPILOT_HTTP_ALLOW_PUBLIC_BIND=trueis set. There is no authentication yet (#42); expose the server only through an authenticating reverse proxy. - Rate limiting — fixed-window per client IP (default 120/min) with
429+Retry-After. - Body cap + timeouts — requests above the body cap get
413; each request has a hard timeout (504). - Security headers —
X-Content-Type-Options: nosniff,Cache-Control: no-store,X-Frame-Options: DENY.
All config is via environment variables. See .env.example.
| Variable | Default | Notes |
|---|---|---|
PILOT_TRANSPORT |
stdio |
stdio or http. |
PILOT_HTTP_HOST |
127.0.0.1 |
HTTP transport only. |
PILOT_HTTP_PORT |
8788 |
HTTP transport only. |
PILOT_HTTP_PATH |
/mcp |
HTTP transport only. |
PILOT_HTTP_ALLOWED_ORIGINS |
loopback | Comma-separated Origin allowlist. |
PILOT_HTTP_ALLOWED_HOSTS |
bind host | Comma-separated Host allowlist. |
PILOT_HTTP_RATE_LIMIT |
120 |
Requests/minute per client IP. |
PILOT_HTTP_MAX_BODY_KB |
1024 |
Max JSON body size. |
PILOT_HTTP_REQUEST_TIMEOUT_SEC |
60 |
Hard per-request timeout. |
PILOT_HTTP_TRUST_PROXY |
false |
Honor X-Forwarded-For (trusted proxies only). |
PILOT_HTTP_ALLOW_PUBLIC_BIND |
false |
Required to bind beyond loopback. |
PILOT_HEADLESS |
true |
Set false to watch the agent work. |
BROWSER_WS_ENDPOINT |
(unset) | Set to use a remote browser. |
PILOT_CAPTCHA_SOLVER_URL |
(unset) | Captcha solver endpoint. |
PILOT_CAPTCHA_SOLVER_TOKEN |
(unset) | Captcha solver bearer token. |
npm install
npm run dev # tsx watch
npm run lint
npm testWeb Pilot is opt-in remote-capable. The HTTP transport binds to 127.0.0.1 by default; change to 0.0.0.0 only behind auth. Never expose it on the public internet without putting a reverse proxy + auth in front.
See SECURITY.md.
Product-specific engineering guidance lives in this repository. Company-wide public engineering, security, governance, and operational documentation is maintained in ORCHORDS/docs.
Issues and PRs welcome. See CONTRIBUTING.md.
ORCHORDS — BUILD DIFFERENT.
Licensed under the Apache License 2.0.
