orchords-web-pilot
Official<p align="center">
<img src="https://raw.githubusercontent.com/ORCHORDS/docs/main/assets/1080x360.jpg" width="1080" alt="ORCHORDS — BUILD DIFFERENT.">
</p>
# Orchords Web Pilot
[](https://github.com/ORCHORDS/OrchordsBrowserPilot/actions/workflows/main-verification.yml)
[](https://github.com/ORCHORDS/OrchordsBrowserPilot/actions/workflows/daily-build.yml)
[](https://github.com/ORCHORDS/OrchordsBrowserPilot/actions/workflows/codeql.yml)
[](https://github.com/ORCHORDS/OrchordsBrowserPilot/actions/workflows/dependency-audit.yml)
[](LICENSE)
> ⭐ 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](mailto: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](https://modelcontextprotocol.io) 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.
## Start here
| If you need… | Start with |
| --- | --- |
| Install and first run | [Install](#install) |
| Wire it into a desktop agent | [stdio (default)](#stdio-default--desktop-agents) |
| Run it as a hosted service | [Streamable HTTP](#streamable-http-hosted--shared) |
| Use a remote browser grid | [Local or remote browser](#local-or-remote-browser) |
| Available tools and parameters | [Tools](#tools) · [Configuration](#configuration) |
| Security guidance | [Security](#security) · [SECURITY.md](./SECURITY.md) |
| Contributing process | [Contributing](#contributing) |
## Capabilities
- 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_ENDPOINT` to 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
## Install
```bash
npm install -g orchords-web-pilot
```
Or run it straight from a checkout:
```bash
git clone https://github.com/ORCHORDS/OrchordsBrowserPilot.git
cd OrchordsBrowserPilot
npm install
npm run build
npm start
```
## Compatibility
- **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` (or `npx playwright install --with-deps chromium` on a fresh Linux runner).
- **Verification policy:** every `main` push 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 stable `playwright@latest` to 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.
## Usage
### stdio (default — desktop agents)
Add to your MCP client config (Claude Desktop, ZCode, etc.):
```json
{
"mcpServers": {
"orchords-web-pilot": {
"command": "orchords-web-pilot",
"env": {
"PILOT_HEADLESS": "true"
}
}
}
}
```
### Streamable HTTP (hosted / shared)
```bash
PILOT_TRANSPORT=http \
PILOT_HTTP_HOST=0.0.0.0 \
PILOT_HTTP_PORT=8788 \
PILOT_HTTP_PATH=/mcp \
npm start
```
Then point your client at `http://<host>:8788/mcp`.
### Local or remote browser
By default Web Pilot launches a local Chromium via Playwright. To use a remote
browser (Browserless, hosted Chrome, your own grid), set:
```bash
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.
## Tools
| 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. |
### Snapshot refs
`browser_snapshot` returns an accessibility tree in which every interactive node carries a `ref` token, e.g.:
```yaml
- 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_URL` and `PILOT_CAPTCHA_SOLVER_TOKEN` and 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.
### HTTP transport security
The HTTP endpoint is hardened by default ([#43](https://github.com/ORCHORDS/OrchordsBrowserPilot/issues/43)):
- **Origin validation** — any request carrying an `Origin` header must match the allowlist (loopback variants by default) or it is rejected with `403`. This blocks malicious websites from driving your local browser via `fetch` against `localhost:8788`, including sandboxed `null` origins.
- **Host validation** — `Host` must 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 unless `PILOT_HTTP_ALLOW_PUBLIC_BIND=true` is 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`.
## Configuration
All config is via environment variables. See [`.env.example`](./.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. |
## Development
```bash
npm install
npm run dev # tsx watch
npm run lint
npm test
```
## Security
Web 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](./SECURITY.md).
## Documentation boundary
Product-specific engineering guidance lives in this repository. Company-wide public engineering, security, governance, and operational documentation is maintained in [`ORCHORDS/docs`](https://github.com/ORCHORDS/docs).
## Contributing
Issues and PRs welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md).
## Brand
**ORCHORDS — BUILD DIFFERENT.**
## License
Licensed under the [Apache License 2.0](LICENSE).
TDQS
Scored across 15 tools
Each browser_ tool serves a distinct, well-defined action—navigation, snapshot, click, type, fill, press, hover, drag, select, screenshot, evaluate, wait, console, network, and captcha solving. Even the text-entry tools (type vs. fill) are clearly differentiated by their descriptions, eliminating ambiguity.
All tools consistently use the 'browser_' prefix followed by a descriptive verb (or verb+noun) in lowercase snake_case, such as browser_navigate, browser_screenshot, browser_captcha_solve. This uniform pattern makes the tool names predictable and easy to learn.
With 15 tools, the set is appropriately scoped for a comprehensive browser automation server. Each tool covers a specific aspect of web interaction without being redundant or overwhelming, staying within the ideal range for a focused purpose.
The tool surface covers the full lifecycle of browser interactions: navigation, inspection, element actions, waiting, screenshotting, JavaScript execution, and special handling like captchas. Minor gaps like tab/multi-page management or dialog handling exist, but they are non-essential for most web-pilot tasks.