chromiumfish_mcp
# chromiumfish_mcp
`chromiumfish_mcp` is an independent Model Context Protocol (MCP) server for [ChromiumFish](https://github.com/arman-bd/chromiumfish). It lets Claude Code, Claude Desktop, Cursor, and other MCP clients drive a ChromiumFish browser through structured tools.
It uses the official ChromiumFish npm package and ships no Chromium source or binaries — on the first tool call that needs a page, the upstream SDK downloads and caches the matching browser build. See **[docs/USAGE.md](docs/USAGE.md)** for detailed tool usage.
## Requirements
- Node.js 20 or later.
- An OS and architecture supported by ChromiumFish.
- Network access for the initial browser download. If no prebuilt asset matches your platform, build ChromiumFish locally and point to it with `--chrome-path` or `CHROME_BIN`.
## Install
```bash
npm install --global github:LowOrbitLab/chromiumfish_mcp
chromiumfish_mcp --persona-seed alice
```
Or run without installing: `npx --yes github:LowOrbitLab/chromiumfish_mcp --persona-seed alice`.
## Configure
Add the server to your MCP client config:
```json
{
"mcpServers": {
"chromiumfish": {
"command": "chromiumfish_mcp",
"args": ["--persona-seed", "alice"]
}
}
}
```
To run from GitHub instead of a global install, set `"command": "npx"` and prepend `"--yes", "github:LowOrbitLab/chromiumfish_mcp"` to `args`. On Windows, use `npx.cmd` if your client cannot resolve `npx`.
## Tools
| Tool | Purpose |
|------|---------|
| `list_pages`, `open_page`, `select_page`, `close_page` | Manage pages by stable `pageId` |
| `navigate`, `navigate_back`, `navigate_forward`, `reload` | Navigate and use page history |
| `snapshot` | List visible interactive elements with `e1`/`e2` references |
| `get_text`, `take_screenshot` | Retrieve page or frame content; `take_screenshot` crops to one element with `target` |
| `click`, `hover`, `type_text`, `select_option`, `set_checked`, `press_key`, `scroll`, `wait_for` | Interact with the page |
| `drag` | Drag onto another element or by a pixel offset, with a human-like path |
| `handle_dialog` | Arm the answer for the next `alert`/`confirm`/`prompt`/`beforeunload` |
| `upload_file` | Attach local files to a file input — requires `--upload-dir` |
| `list_downloads` | Report what was downloaded and where it landed — requires `--download-dir` |
| `click_at` | Click absolute coordinates (for widgets `snapshot` cannot see) |
| `list_frames` | List frames/iframes with stable IDs |
| `find_challenge`, `solve_challenge` | Detect and clear interstitial / framed challenges; the result says whether one was observed, interacted with, and verified |
| `evaluate` | Run arbitrary JavaScript — requires `--allow-eval` |
| `run_task` | Native ChromiumFish agent — requires `--allow-native-agent` |
A JavaScript dialog is answered the instant it opens, because a page left waiting on one freezes and no later call could finish. Unarmed the answer is dismiss — which is what Playwright already did silently — so a click that only raised `confirm("Delete?")` did not delete, and the action result now says so under `dialogs`. Call `handle_dialog` to arm the next answer, then repeat the action. The four navigation tools also report the HTTP `status`, since a 404 or a 500 loads like any other page.
Every navigation and interaction tool reports the resulting `url`, `title`, `navigated`, and any `newPages`, so a client only needs a follow-up `snapshot` when the page actually changed; `returnSnapshot: true` returns the action result and a fresh snapshot in one call. An action that navigates waits for the new document before reporting, so those fields describe the page it arrived on; one whose navigation is still in flight after ten seconds reports `navigationPending: true` rather than describing the page being left as though nothing happened, and one whose navigation was cancelled outright — blocked by `--allowed-host`, an unresolvable host — reports `navigationFailed` with the browser's own reason instead of waiting out that bound for a page that is never coming. Reference numbers are never reused, so one held across a snapshot fails with an error instead of acting on a different element; `snapshot` also reports ARIA roles, so `role=button[name="Submit"]` works in any `target` field as a re-render-proof alternative. The server ships MCP `instructions` describing this workflow, reference lifetime, and the challenge sequence.
同一会话的页面操作、页面切换和快照按调用顺序串行执行,避免并发输入混入同一控件。排队期间取消的 MCP 调用不会在稍后执行。元素点击会检查遮挡和可用状态;无法点击时返回错误。
`returnSnapshot: true` 的快照若失败,结果保留动作的成功状态并返回 `snapshotError`。此时只重试读取,不能因为快照失败而重复点击、提交或上传。
Snapshot references, frame-aware interaction, waiting, and the cross-origin challenge workflow are covered in **[docs/USAGE.md](docs/USAGE.md)**.
启用 `--allowed-host` 后,顶层重定向的每一跳都会在浏览器响应头阶段校验,未授权地址不会收到重定向请求。该模式同时禁用 Service Worker,避免其绕过导航拦截;普通子框架和静态资源仍可访问其他主机。下载文件通过排他创建原子分配名称,并发同名下载不会互相覆盖。
## Command-line options
```text
--persona-seed VALUE Use a stable browser fingerprint persona
--chrome-path PATH Use a local ChromiumFish executable
--browser-version VERSION Select an upstream ChromiumFish build version (rejected with --chrome-path)
--headed Show the browser window
--window-size WIDTHxHEIGHT Set the browser window size
--timezone ZONE Use an IANA time zone, auto, or system (default: auto)
--proxy URL Route browser traffic through a proxy
--allowed-host HOST Allow top-level navigation to a bare host and its subdomains; repeatable
--max-text-chars N Set the hard limit for text and snapshot output
--upload-dir PATH Allow upload_file to read this directory; repeatable
--download-dir PATH Save downloads here; without it they are discarded
--allow-eval Enable arbitrary JavaScript execution
--allow-native-agent Enable the native ChromiumFish browser agent
```
Proxy credentials can be embedded in the proxy URL, but are then exposed in the client config. Never commit config files containing proxy passwords, cookies, or API keys.
## Security
- stdio only — do not expose the Chromium DevTools endpoint to the public internet.
- `evaluate` and the native agent are disabled by default; enable them only in trusted environments.
- `--allowed-host example.com` restricts top-level HTTP/HTTPS navigation (redirects, links, form posts, popups) to a host and its subdomains. Give it a bare host name: matching is on the host alone, so a scheme, path, port, leading dot, or wildcard is rejected at startup rather than quietly matching nothing. Third-party subframes and page assets remain reachable — it is a navigation guard, not a network egress filter. A blocked navigation is reported as `navigationFailed` with reason `net::ERR_BLOCKED_BY_CLIENT`, rather than as a click that appeared to do nothing. Enabling it also turns on request interception, which disables the browser's HTTP cache for the session: expect slower asset-heavy pages, and note that a browser re-fetching everything on a repeat visit is itself a signal to sites that profile behavior.
- `upload_file` sends host files to whatever origin the page posts to, so it is unregistered until `--upload-dir` names at least one directory, and every path must resolve inside one. Both the path and the roots are resolved through symlinks first, so a link planted inside a root cannot reach outside it. Scope the roots to the files a task actually needs — a page can influence which file the model picks.
- Downloads are written only when `--download-dir` names a directory; without it the transfer still runs (so the page's own flow is unchanged) but nothing is kept, and the action result says `discarded`. The file name comes from the remote server's `Content-Disposition`, so it is reduced to a bare base name and the resolved path is checked against the root before anything is written.
- A page can raise `confirm()` to guard a destructive action. Unarmed dialogs are dismissed, so that guard holds by default; `handle_dialog` with `accept: true` deliberately answers the next one. Keep human confirmation in front of it for anything irreversible.
- Clients can click and type with real side effects. Keep human confirmation for purchases, publishing, deletion, and permission changes.
- `solve_challenge` returns `ok: true` whenever the page is not blocked, including on pages that were never challenged. Read `challengeObserved`, `interactionPerformed`, and `clearanceVerified` before recording or reporting that a challenge was cleared — `ok` alone will overstate what happened.
- Each process runs an independent browser context; this is not a shared multi-tenant service.
## Development
```bash
npm ci
npm test
node dist/index.js --help
```
The suite uses an in-memory MCP transport to verify tool discovery, dangerous-tool opt-in, and results. It does not download or launch a browser.
`npm run test:integration` 运行本地 HTTP 页面上的真实浏览器回归,覆盖重定向、遮挡、并发操作和下载。浏览器可通过 `CHROME_BIN` 指定,或先执行 `npx playwright-core install chromium`。CI 同时执行单元测试和这些浏览器回归。
## License
MIT. ChromiumFish code and trademarks belong to their respective contributors; see [NOTICE](NOTICE) for attribution. This is an independent wrapper, not an official ChromiumFish release.
TDQS
Scored across 24 tools
Each tool has a clearly distinct purpose, from basic interactions like click and hover to specialized actions like solve_challenge and drag. The slight overlap between click and click_at is well-documented and justified by use cases.
Tool names mostly follow a consistent verb_noun pattern (e.g., open_page, type_text), though a few single-word verbs (click, hover, scroll) deviate slightly. The pattern is predictable and readable.
24 tools is a comprehensive set for browser automation, covering navigation, input, waiting, frames, screenshots, and challenge handling. It is slightly above the ideal range but each tool serves a distinct purpose.
The toolset covers most common browser interactions including all major navigation and input actions, frame handling, and even challenge detection/solving. Minor gaps like file upload or clipboard operations exist but are not essential for core automation.