@raultov/browser-control-mcp 0.0.0-stage → 0.2.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitignore +2 -0
- package/CHANGELOG.md +299 -0
- package/LICENSE +21 -0
- package/README.md +510 -2
- package/binary-install.js +348 -0
- package/binary.js +124 -0
- package/install.js +4 -0
- package/npm-shrinkwrap.json +52 -0
- package/package.json +97 -3
- package/run-browser-control-mcp.js +4 -0
package/README.md
CHANGED
|
@@ -1,3 +1,511 @@
|
|
|
1
|
-
#
|
|
1
|
+
# browser-control-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Browser Control MCP** lets an AI agent control **and debug** real browsers
|
|
4
|
+
at runtime: breakpoints, pause on load, call frames, evaluation in a paused
|
|
5
|
+
frame, stepping and script search on **Chrome (CDP)** and **Firefox
|
|
6
|
+
(WebDriver BiDi + Firefox RDP)**, plus navigation, DOM, input, screenshots,
|
|
7
|
+
console and network capture.
|
|
8
|
+
|
|
9
|
+
It is the successor of
|
|
10
|
+
[chrome-debug-mcp](https://github.com/raultov/chrome-debug-mcp) (it starts
|
|
11
|
+
from a copy of its code) and complements Playwright MCP or
|
|
12
|
+
chrome-devtools-mcp with runtime JavaScript debugging on both browsers.
|
|
13
|
+
|
|
14
|
+
> **Status: BETA (0.2.0-beta.1).** This version contains known bugs and differences
|
|
15
|
+
> between Chrome and Firefox (see [Limitations and TODO](#limitations-and-todo))
|
|
16
|
+
> and parts that have not been verified (see [Untested](#untested)). Tool
|
|
17
|
+
> replies may change in incompatible ways before 1.0. Please report problems
|
|
18
|
+
> in the [issues](https://github.com/raultov/browser-control-mcp/issues).
|
|
19
|
+
|
|
20
|
+
## Supported browsers
|
|
21
|
+
|
|
22
|
+
| Browser | Protocol | Validated with |
|
|
23
|
+
| --- | --- | --- |
|
|
24
|
+
| Chrome / Chromium | Chrome DevTools Protocol | Google Chrome 155.0.8059.39 (Linux) |
|
|
25
|
+
| Firefox | WebDriver BiDi, plus the Firefox Remote Debugging Protocol (RDP) for the JavaScript debugger | Firefox 157.0.1 (Linux, snap) |
|
|
26
|
+
|
|
27
|
+
The default browser is Chrome. Every instance runs one browser for its whole
|
|
28
|
+
life: start the server with `--browser firefox`, or pass
|
|
29
|
+
`"browser": "firefox"` to `open_instance`. If Chrome is not installed but Firefox
|
|
30
|
+
is, the launch error says so and tells the agent to ask you before switching.
|
|
31
|
+
|
|
32
|
+
## Feature matrix
|
|
33
|
+
|
|
34
|
+
✓ supported, ✗ not available (the tool answers a capability error naming the
|
|
35
|
+
tool, the browser and the reason; nothing is sent to the browser), partial:
|
|
36
|
+
see the note.
|
|
37
|
+
|
|
38
|
+
| Tool | Chrome | Firefox | Notes |
|
|
39
|
+
| --- | --- | --- | --- |
|
|
40
|
+
| `open_instance` | ✓ | partial | `features`, `copy_cookies` and `source_profile` are Chrome only |
|
|
41
|
+
| `close_instance` | ✓ | ✓ | |
|
|
42
|
+
| `list_instances` | ✓ | ✓ | each instance reports its `browser` and `capabilities` |
|
|
43
|
+
| `restart_browser` | ✓ | ✓ | was `restart_chrome` |
|
|
44
|
+
| `stop_browser` | ✓ | ✓ | was `stop_chrome`; on an attached Firefox it ends the session, the browser keeps running |
|
|
45
|
+
| `open_tab` | ✓ | ✓ | |
|
|
46
|
+
| `close_tab` | ✓ | ✓ | |
|
|
47
|
+
| `list_tabs` | ✓ | ✓ | |
|
|
48
|
+
| `switch_tab` | ✓ | ✓ | |
|
|
49
|
+
| `navigate` | ✓ | ✓ | |
|
|
50
|
+
| `reload` | ✓ | ✓ | |
|
|
51
|
+
| `scroll` | ✓ | ✓ | |
|
|
52
|
+
| `inspect_dom` | ✓ | ✓ | |
|
|
53
|
+
| `capture_screenshot` | ✓ | ✓ | |
|
|
54
|
+
| `click_element` | ✓ | ✓ | |
|
|
55
|
+
| `fill_input` | ✓ | ✓ | |
|
|
56
|
+
| `evaluate_js` | ✓ | ✓ | |
|
|
57
|
+
| `get_console_logs` | ✓ | ✓ | Firefox: page errors, CSS warnings and platform messages need the debugger port |
|
|
58
|
+
| `get_network_logs` | ✓ | ✓ | Firefox: WebSocket frames need the debugger port |
|
|
59
|
+
| `get_custom_events` | ✓ | partial | Firefox: events of the modules subscribed with `send_bidi_command` |
|
|
60
|
+
| `get_performance_metrics` | ✓ | partial | Firefox: `Nodes`, `Frames`, `Resources`, `TransferSize` from the Web Performance APIs (no Performance domain), plus a note |
|
|
61
|
+
| `profile_page_performance` | ✓ | partial | Firefox: FCP, LCP, DCL and Load from the Web Performance APIs; no trace, so `total_blocking_time_ms` and `top_long_tasks` are `null` |
|
|
62
|
+
| `set_breakpoint` | ✓ | partial | Firefox: by URL only (no `script_id` / `script_hash`) |
|
|
63
|
+
| `remove_breakpoint` | ✓ | ✓ | |
|
|
64
|
+
| `resume` | ✓ | ✓ | |
|
|
65
|
+
| `step_over` | ✓ | ✓ | |
|
|
66
|
+
| `evaluate_on_call_frame` | ✓ | ✓ | |
|
|
67
|
+
| `search_scripts` | ✓ | ✓ | |
|
|
68
|
+
| `pause_on_load` | ✓ | ✗ | TODO on Firefox, see [Limitations and TODO](#limitations-and-todo) |
|
|
69
|
+
| `enable_proxy_auth` | ✓ | partial | needs `--proxy-server`; untested on Firefox against a real proxy |
|
|
70
|
+
| `send_cdp_command` | ✓ | ✗ | raw Chrome DevTools Protocol |
|
|
71
|
+
| `send_bidi_command` | ✗ | ✓ | raw WebDriver BiDi; refuses `session.new`, `session.end`, `browser.close` |
|
|
72
|
+
| `send_rdp_request` | ✗ | ✓ | raw Firefox RDP request to an actor (`thread`, `console`, `root` shortcuts) |
|
|
73
|
+
| `webmcp_list_tools` | ✓ | ✗ | needs `--enable-webmcp`; WebMCP is a Chrome testing feature |
|
|
74
|
+
| `webmcp_invoke_tool` | ✓ | ✗ | needs `--enable-webmcp` |
|
|
75
|
+
| `webmcp_get_invocation` | ✓ | ✗ | needs `--enable-webmcp` |
|
|
76
|
+
| `webmcp_list_invocations` | ✓ | ✗ | needs `--enable-webmcp` |
|
|
77
|
+
|
|
78
|
+
The JavaScript debugger tools on Firefox need its debugger (RDP) port: on by
|
|
79
|
+
default for a launched Firefox, `--firefox-rdp-port` for an attached one. The
|
|
80
|
+
same port gives Firefox's WebSocket frames and the console messages BiDi does
|
|
81
|
+
not report (script errors with their file and line, cookie and security
|
|
82
|
+
warnings, CSS warnings, platform messages), in the same shapes as Chrome.
|
|
83
|
+
Each tab's debugger watches them: the session's first tabs are attached when
|
|
84
|
+
the session starts and `open_tab` attaches its tab before loading the URL; a
|
|
85
|
+
tab the page opened is attached by the first `get_console_logs` or
|
|
86
|
+
`get_network_logs` on it. Tabs are matched by their position in BiDi's and
|
|
87
|
+
RDP's tab lists (checked against their URLs), never by activating them; a
|
|
88
|
+
tab that cannot be matched that way (tabs opening or closing at that moment)
|
|
89
|
+
is attached by its first debugger tool, which brings it to the front. Frames
|
|
90
|
+
are captured from the moment a tab is attached.
|
|
91
|
+
|
|
92
|
+
## 📦 Installation
|
|
93
|
+
|
|
94
|
+
Every method installs the **same pre-compiled binaries** published on each
|
|
95
|
+
[GitHub Release](https://github.com/raultov/browser-control-mcp/releases); a Rust toolchain is only needed for the Cargo and
|
|
96
|
+
source routes. The npm package is a small wrapper whose `postinstall` fetches
|
|
97
|
+
the pre-compiled binary for your platform from GitHub Releases: no compiling,
|
|
98
|
+
no Rust toolchain. Install Chrome and/or Firefox yourself: the server launches
|
|
99
|
+
them, it does not ship them (except the Docker image).
|
|
100
|
+
|
|
101
|
+
> ⚠️ **Always use the scoped npm name `@raultov/browser-control-mcp`.** The
|
|
102
|
+
> unscoped `browser-control-mcp` on npm is a different, unrelated project.
|
|
103
|
+
|
|
104
|
+
Prefer zero effort? The one-prompt route below lets your AI agent do all of
|
|
105
|
+
it. Otherwise, methods are grouped by platform and ordered by popularity.
|
|
106
|
+
|
|
107
|
+
### 🤖 One-Prompt Install (any platform)
|
|
108
|
+
|
|
109
|
+
Paste this prompt into any AI coding agent's chat (Claude Code, opencode,
|
|
110
|
+
Cursor, Codex CLI, Copilot, ...) and let it install the server and register
|
|
111
|
+
it with every MCP-capable client it detects on your machine:
|
|
112
|
+
|
|
113
|
+
```text
|
|
114
|
+
Install and set up the browser-control-mcp MCP server on this machine. Do not launch Chrome or Firefox yourself: the server launches and manages them.
|
|
115
|
+
|
|
116
|
+
1. Install the server binary with the first method that works on this OS:
|
|
117
|
+
- Homebrew available (macOS/Linux): brew install raultov/tap/browser-control-mcp
|
|
118
|
+
- Node.js >= 14 available (any OS): npm install -g @raultov/browser-control-mcp
|
|
119
|
+
- macOS/Linux fallback: curl --proto '=https' --tlsv1.2 -LsSf https://github.com/raultov/browser-control-mcp/releases/latest/download/browser-control-mcp-installer.sh | sh
|
|
120
|
+
- Windows fallback: download and run the .msi from https://github.com/raultov/browser-control-mcp/releases/latest
|
|
121
|
+
Verify with: browser-control-mcp --version (add the install dir to PATH if it is not found).
|
|
122
|
+
|
|
123
|
+
2. Register the MCP server with every AI client you detect on this machine:
|
|
124
|
+
- Claude Code: claude mcp add --scope user browser-control-mcp browser-control-mcp
|
|
125
|
+
- opencode: merge into ~/.config/opencode/opencode.json —
|
|
126
|
+
"mcp": { "browser-control-mcp": { "type": "local", "command": ["browser-control-mcp"] } }
|
|
127
|
+
- Codex CLI: append to ~/.codex/config.toml —
|
|
128
|
+
[mcp_servers.browser-control-mcp]
|
|
129
|
+
command = "browser-control-mcp"
|
|
130
|
+
- Any other MCP-capable client (Cursor, Windsurf, VS Code, ...): add it with its own MCP settings, command "browser-control-mcp" with no arguments.
|
|
131
|
+
|
|
132
|
+
3. Report what you installed and which clients you configured, and remind me to restart each client so the MCP server loads. Do not enable --user-profile or --allow-cookie-import unless I ask for them.
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### macOS
|
|
136
|
+
|
|
137
|
+
**1. Homebrew**, the natural option on macOS:
|
|
138
|
+
```bash
|
|
139
|
+
brew install raultov/tap/browser-control-mcp
|
|
140
|
+
```
|
|
141
|
+
The formula fetches the pre-compiled binary (Apple Silicon and Intel) from
|
|
142
|
+
GitHub Releases, and `brew upgrade` keeps you current.
|
|
143
|
+
|
|
144
|
+
**2. npm / npx**, the MCP-client convention (requires Node.js >= 14):
|
|
145
|
+
```bash
|
|
146
|
+
npm install -g @raultov/browser-control-mcp # installs the `browser-control-mcp` command
|
|
147
|
+
npx -y @raultov/browser-control-mcp # ...or just run it once, nothing to install
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**3. Shell installer**, a one-liner that installs to `~/.cargo/bin`:
|
|
151
|
+
```bash
|
|
152
|
+
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/raultov/browser-control-mcp/releases/latest/download/browser-control-mcp-installer.sh | sh
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**4. Cargo**, if you already have Rust:
|
|
156
|
+
```bash
|
|
157
|
+
cargo install browser-control-mcp # compiles from crates.io
|
|
158
|
+
cargo binstall browser-control-mcp # with cargo-binstall: fetches the pre-compiled binary, no compile
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**5. Manual download**: the `aarch64-apple-darwin` or `x86_64-apple-darwin`
|
|
162
|
+
`.tar.xz` archive from the [Releases](https://github.com/raultov/browser-control-mcp/releases) page.
|
|
163
|
+
|
|
164
|
+
### Linux
|
|
165
|
+
|
|
166
|
+
**1. npm / npx** (requires Node.js >= 14):
|
|
167
|
+
```bash
|
|
168
|
+
npm install -g @raultov/browser-control-mcp # installs the `browser-control-mcp` command
|
|
169
|
+
npx -y @raultov/browser-control-mcp # ...or just run it once, nothing to install
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**2. Shell installer**, a one-liner that installs to `~/.cargo/bin`:
|
|
173
|
+
```bash
|
|
174
|
+
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/raultov/browser-control-mcp/releases/latest/download/browser-control-mcp-installer.sh | sh
|
|
175
|
+
```
|
|
176
|
+
The Linux binaries require glibc >= 2.35 (Ubuntu 22.04+, Debian 12+, Fedora 36+).
|
|
177
|
+
|
|
178
|
+
**3. Homebrew (Linuxbrew)**, if you already use
|
|
179
|
+
[Homebrew on Linux](https://docs.brew.sh/Homebrew-on-Linux):
|
|
180
|
+
```bash
|
|
181
|
+
brew install raultov/tap/browser-control-mcp
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**4. Cargo**, if you already have Rust:
|
|
185
|
+
```bash
|
|
186
|
+
cargo install browser-control-mcp # compiles from crates.io
|
|
187
|
+
cargo binstall browser-control-mcp # with cargo-binstall: fetches the pre-compiled binary, no compile
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**5. Manual download**: the `x86_64-unknown-linux-gnu` or
|
|
191
|
+
`aarch64-unknown-linux-gnu` `.tar.xz` archive from the [Releases](https://github.com/raultov/browser-control-mcp/releases) page.
|
|
192
|
+
|
|
193
|
+
### Windows
|
|
194
|
+
|
|
195
|
+
**1. npm / npx** (requires Node.js >= 14):
|
|
196
|
+
```powershell
|
|
197
|
+
npm install -g @raultov/browser-control-mcp # installs the `browser-control-mcp` command
|
|
198
|
+
npx -y @raultov/browser-control-mcp # ...or just run it once, nothing to install
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**2. MSI installer**, the native Windows experience: download
|
|
202
|
+
`browser-control-mcp-x86_64-pc-windows-msvc.msi` from the [Releases](https://github.com/raultov/browser-control-mcp/releases)
|
|
203
|
+
page and double-click it.
|
|
204
|
+
|
|
205
|
+
**3. PowerShell installer**, a one-liner:
|
|
206
|
+
```powershell
|
|
207
|
+
powershell -ExecutionPolicy Bypass -c "irm https://github.com/raultov/browser-control-mcp/releases/latest/download/browser-control-mcp-installer.ps1 | iex"
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**4. Cargo**, if you already have Rust:
|
|
211
|
+
```powershell
|
|
212
|
+
cargo install browser-control-mcp # compiles from crates.io
|
|
213
|
+
cargo binstall browser-control-mcp # with cargo-binstall: fetches the pre-compiled binary, no compile
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**5. Manual download**: the `x86_64-pc-windows-msvc` `.zip` archive from the
|
|
217
|
+
[Releases](https://github.com/raultov/browser-control-mcp/releases) page.
|
|
218
|
+
|
|
219
|
+
### Docker (amd64)
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
docker build -t browser-control-mcp .
|
|
223
|
+
docker run -i --rm browser-control-mcp --headless # Chrome and Firefox inside
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### From source
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
git clone https://github.com/raultov/browser-control-mcp && cd browser-control-mcp
|
|
230
|
+
make install # release build copied to ~/.cargo/bin (or cargo install --path . --locked)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Browser discovery
|
|
234
|
+
|
|
235
|
+
`CHROME_PATH`, then `google-chrome`, `chromium` and the usual install paths;
|
|
236
|
+
`--firefox-path`, then `FIREFOX_PATH`, then `firefox` and the usual install
|
|
237
|
+
paths.
|
|
238
|
+
|
|
239
|
+
## MCP client configuration
|
|
240
|
+
|
|
241
|
+
Universal JSON:
|
|
242
|
+
|
|
243
|
+
```json
|
|
244
|
+
{
|
|
245
|
+
"mcpServers": {
|
|
246
|
+
"browser-control-mcp": { "command": "browser-control-mcp", "args": [] },
|
|
247
|
+
"browser-control-firefox": {
|
|
248
|
+
"command": "browser-control-mcp",
|
|
249
|
+
"args": ["--browser", "firefox"]
|
|
250
|
+
},
|
|
251
|
+
"browser-control-docker": {
|
|
252
|
+
"command": "docker",
|
|
253
|
+
"args": ["run", "-i", "--rm", "browser-control-mcp", "--headless"]
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Claude Code:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
claude mcp add browser-control-mcp browser-control-mcp
|
|
263
|
+
claude mcp add browser-control-firefox -- browser-control-mcp --browser firefox
|
|
264
|
+
claude mcp add browser-control-mcp -- npx -y @raultov/browser-control-mcp
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Usage concepts
|
|
268
|
+
|
|
269
|
+
- **Instances.** A `default` instance always exists and uses the `--browser`
|
|
270
|
+
flag. `open_instance` with a `label` starts another one (its id is the
|
|
271
|
+
label), with `browser` to pick Chrome or Firefox. Browsers are launched
|
|
272
|
+
lazily, on the first tool that needs them, with an ephemeral profile.
|
|
273
|
+
- **Tabs.** `open_tab`, `list_tabs`, `switch_tab`, `close_tab`. Every tool
|
|
274
|
+
takes `instance_id` and `tab_id`; without them it targets the default
|
|
275
|
+
instance and its active tab.
|
|
276
|
+
- **Same replies on both browsers.** `navigate` and `reload` answer once the
|
|
277
|
+
page fired `load` (at most 30 s, else with a note): a first line
|
|
278
|
+
(`Navigated to <final URL>.` / `Page reloaded.`), then
|
|
279
|
+
`{"url": "<final URL, after redirects>", "tab_id": "tab-N", "browser": {...}}`,
|
|
280
|
+
where `browser` holds the protocol's ids (CDP `frameId`/`loaderId`, BiDi
|
|
281
|
+
`navigation`). `open_tab` answers `{tab_id, target_id, url, label}` with the
|
|
282
|
+
final URL. A network error (unknown host, refused connection...) fails with
|
|
283
|
+
`Navigation failed: <browser's code> (<url>)`, e.g.
|
|
284
|
+
`net::ERR_NAME_NOT_RESOLVED` on Chrome and `NS_ERROR_UNKNOWN_HOST` on
|
|
285
|
+
Firefox; from `open_tab` the error names the tab, which stays open.
|
|
286
|
+
`evaluate_js` answers `{"value", "type", "subtype"?, "description"?}`:
|
|
287
|
+
`type` is JavaScript's `typeof`, `subtype` refines objects (`array`,
|
|
288
|
+
`null`, `node`, `date`, `map`...), `value` is the JSON form and is `null`
|
|
289
|
+
when there is none (functions, DOM nodes, symbols, `NaN`, bigints,
|
|
290
|
+
maps...), `description` then says what it was (Chrome describes functions
|
|
291
|
+
by their source, Firefox only as `function`). A thrown exception or a
|
|
292
|
+
rejected promise fails with what was thrown and its 1-based line and
|
|
293
|
+
column, e.g. `ReferenceError: x is not defined (line 1, column 1)`.
|
|
294
|
+
- **Per-tab state.** Console logs, network traffic, breakpoints and custom
|
|
295
|
+
events are kept per tab, in bounded buffers. A tab's console buffer (last
|
|
296
|
+
1000 messages) is cleared when the tab moves to another site (registrable
|
|
297
|
+
domain from the Public Suffix List: `www.knot.kz` and `docs.knot.kz` are
|
|
298
|
+
one site, `a.github.io` and `b.github.io` two) and kept across reloads and
|
|
299
|
+
same-page navigations. `get_console_logs` answers
|
|
300
|
+
`{"messages": [...], "total", "truncated", "notes"?}`: at most `limit`
|
|
301
|
+
(default 200) of the most recent matching messages, oldest first; `clear`
|
|
302
|
+
removes only the returned ones; a note says when a site change cleared
|
|
303
|
+
messages.
|
|
304
|
+
- **Firefox allows a single automation session.** Each Firefox instance holds
|
|
305
|
+
one WebDriver BiDi session shared by its tabs; `close_instance`,
|
|
306
|
+
`stop_browser` and every server shutdown path end it, so the next run can
|
|
307
|
+
attach again.
|
|
308
|
+
|
|
309
|
+
## CLI flags
|
|
310
|
+
|
|
311
|
+
| Flag | Default | Chrome | Firefox |
|
|
312
|
+
| --- | --- | --- | --- |
|
|
313
|
+
| `--browser <chrome\|firefox>` | `chrome` | browser of the default instance | browser of the default instance |
|
|
314
|
+
| `--local` | off | navigation limited to local addresses | same |
|
|
315
|
+
| `--headless` | off | headless Chrome | headless Firefox |
|
|
316
|
+
| `--window-size <W>x<H>` | `1920x1080` | window of launched Chrome (`--window-size`) | window of launched Firefox (`--width`/`--height`) |
|
|
317
|
+
| `--host <HOST>` | `127.0.0.1` | debugging host | debugging host |
|
|
318
|
+
| `--port <PORT>` | `9222` | debugging port of the default instance | ignored (use `--firefox-port`) |
|
|
319
|
+
| `--max-instances <N>` | `8` | instance cap | shared with Chrome |
|
|
320
|
+
| `--enable-automation` | off | shows the automation infobar | ignored; `list_instances` shows a warning |
|
|
321
|
+
| `--user-profile` | off | your default profile (no `--user-data-dir`) | only with `--firefox-port` (attach to your running Firefox) |
|
|
322
|
+
| `--allow-cookie-import` | off | exposes `copy_cookies` | capability error |
|
|
323
|
+
| `--enable-webmcp` | off | WebMCP switches and `webmcp_*` tools | capability error |
|
|
324
|
+
| `--proxy-server <URL>` | none | proxy for every instance; exposes `enable_proxy_auth` | `http://` and `socks5://` proxies |
|
|
325
|
+
| `--proxy-username <USER>` | none | answers proxy (407) challenges | same (untested) |
|
|
326
|
+
| `--proxy-password <PASS>` | none | never logged | same |
|
|
327
|
+
| `--firefox-path <PATH>` | discovered | — | Firefox binary to launch |
|
|
328
|
+
| `--firefox-port <PORT>` | none (launch) | — | attach to a running Firefox's BiDi port |
|
|
329
|
+
| `--firefox-rdp-port <PORT>` | none | — | debugger port of the attached Firefox (needs `--firefox-port`) |
|
|
330
|
+
| `--no-firefox-debugger` | off | — | launch Firefox without its debugger port (no debugger tools) |
|
|
331
|
+
|
|
332
|
+
`--proxy` and `--webmcp` are aliases. To attach to your own Firefox, start it
|
|
333
|
+
with `--remote-debugging-port <port>` (and `--start-debugger-server ws:<rdp>`
|
|
334
|
+
plus `devtools.debugger.remote-enabled` and `devtools.chrome.enabled` for the
|
|
335
|
+
debugger), then pass `--browser firefox --firefox-port <port>
|
|
336
|
+
--firefox-rdp-port <rdp>`.
|
|
337
|
+
|
|
338
|
+
## Architecture
|
|
339
|
+
|
|
340
|
+
```
|
|
341
|
+
MCP client (agent)
|
|
342
|
+
│ JSON-RPC over stdio (tools/list, tools/call)
|
|
343
|
+
▼
|
|
344
|
+
browser-control-mcp ── tool table: flag gate → capability check → per-browser handler
|
|
345
|
+
│ │
|
|
346
|
+
│ Chrome instance │ Firefox instance
|
|
347
|
+
▼ ▼
|
|
348
|
+
cdp-browser-lite / cdp-lite bidi-browser-lite / bidi-lite
|
|
349
|
+
(launch, ports, profiles, (launch, prefs, one BiDi session,
|
|
350
|
+
CDP over WebSocket) RDP client for the debugger)
|
|
351
|
+
│ CDP commands/events │ BiDi commands/events │ RDP packets
|
|
352
|
+
▼ ▼ ▼
|
|
353
|
+
Chrome Firefox ◄───────────────────┘
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
A tool call resolves the instance (`instance_id`, default `default`) and the
|
|
357
|
+
tab (`tab_id`, default active), checks the instance can serve it, then runs
|
|
358
|
+
the implementation for that instance's browser. Events are pumped into
|
|
359
|
+
per-tab state: on Chrome, one listener per CDP domain and tab; on Firefox,
|
|
360
|
+
one subscription per session, routed by browsing context id.
|
|
361
|
+
|
|
362
|
+
## Debugging JavaScript
|
|
363
|
+
|
|
364
|
+
Chrome and Firefox take the same calls:
|
|
365
|
+
|
|
366
|
+
1. `navigate` to the page (or `open_tab` + `switch_tab`).
|
|
367
|
+
2. `search_scripts` with a needle to find the script URL and line.
|
|
368
|
+
3. `set_breakpoint` with `url` and `line_number` (0-based).
|
|
369
|
+
4. Trigger the code (`click_element`, `evaluate_js` with a `setTimeout`, a
|
|
370
|
+
reload...). The page pauses.
|
|
371
|
+
5. `evaluate_on_call_frame` reads locals of the paused frame; `step_over`
|
|
372
|
+
moves one statement; `resume` continues.
|
|
373
|
+
6. `remove_breakpoint` with the id `set_breakpoint` returned.
|
|
374
|
+
|
|
375
|
+
Both browsers answer the same replies, with 0-based lines everywhere:
|
|
376
|
+
|
|
377
|
+
| Tool | Reply |
|
|
378
|
+
| --- | --- |
|
|
379
|
+
| `set_breakpoint` | `{"breakpoint_id": "bp-N", "url", "line_number", "column_number", "resolved"}`: where the browser bound it; `resolved` is false until a script with that URL loads |
|
|
380
|
+
| `remove_breakpoint` | `{"removed": "bp-N"}`; an unknown id fails listing the tab's ids |
|
|
381
|
+
| `step_over`, `resume` | `{"paused", "location": {"url", "line_number", "column_number"} \| null}`: where the thread stopped next (`step_over` waits up to 2 s; `resume` reports a pause only if one follows within 250 ms) |
|
|
382
|
+
| `evaluate_on_call_frame` | the `evaluate_js` shape; an exception fails with `Evaluation threw: <what was thrown>` |
|
|
383
|
+
|
|
384
|
+
Breakpoint ids are per tab and MCP-level (`bp-1`, `bp-2`...), like tab ids.
|
|
385
|
+
|
|
386
|
+
Chrome also offers `pause_on_load` (pause on the first statement of the next
|
|
387
|
+
load). Firefox notes: breakpoints by URL only; the column is snapped to the
|
|
388
|
+
nearest breakable position automatically; the debugger needs the RDP port.
|
|
389
|
+
|
|
390
|
+
## Migration from chrome-debug-mcp
|
|
391
|
+
|
|
392
|
+
| chrome-debug-mcp 1.5.6 | browser-control-mcp |
|
|
393
|
+
| --- | --- |
|
|
394
|
+
| binary / crate `chrome-debug-mcp` | `browser-control-mcp` |
|
|
395
|
+
| npm `@raultov/chrome-debug-mcp` | `@raultov/browser-control-mcp` |
|
|
396
|
+
| tool `restart_chrome` | `restart_browser` |
|
|
397
|
+
| tool `stop_chrome` | `stop_browser` |
|
|
398
|
+
| replies printed as Rust `Debug` (`WsResponse { .. }`) | prefix + pretty JSON of the reply |
|
|
399
|
+
| `get_network_logs` returned credential headers | `<redacted>` unless `include_sensitive_headers: true` |
|
|
400
|
+
| `enable_proxy_auth` answered every auth challenge | answers proxy (407) challenges only |
|
|
401
|
+
|
|
402
|
+
All command-line flags are kept; `--browser`, `--firefox-path`,
|
|
403
|
+
`--firefox-port`, `--firefox-rdp-port` and `--no-firefox-debugger` are new.
|
|
404
|
+
In client configurations replace the command `chrome-debug-mcp` with
|
|
405
|
+
`browser-control-mcp` (or the npm package name). The old tool names answer an
|
|
406
|
+
error that names the new ones.
|
|
407
|
+
|
|
408
|
+
### Migration from 0.1.x to 0.2.0
|
|
409
|
+
|
|
410
|
+
0.2.0 makes the replies the same on Chrome and Firefox; these changes are
|
|
411
|
+
incompatible:
|
|
412
|
+
|
|
413
|
+
| Tool | 0.1.x | 0.2.0 |
|
|
414
|
+
| --- | --- | --- |
|
|
415
|
+
| `navigate`, `reload` | Chrome: raw `Page.navigate` reply (`frameId`, `loaderId`, `errorText`) without waiting, `{}` on reload; Firefox: `{navigation, url}` | `{url, tab_id, browser, notes?}` after `load`; `url` is the final URL; protocol ids under `browser` |
|
|
416
|
+
| `navigate`, `open_tab` to an unreachable host | Chrome: success (`errorText` inside, or a `chrome-error://` tab) | error `Navigation failed: <code> (<url>)`; `open_tab` names the tab, which stays open |
|
|
417
|
+
| `evaluate_js` | Chrome: `{"result": {type, value, ...}}`; Firefox: the bare value | `{value, type, subtype?, description?}` |
|
|
418
|
+
| `set_breakpoint` | Chrome: CDP id (`1:3:0:http://…`) and `locations`; Firefox: `{breakpointId, url, lineNumber, columnNumber}` | `{breakpoint_id: "bp-N", url, line_number, column_number, resolved}` |
|
|
419
|
+
| `remove_breakpoint` | Chrome: CDP id, reply `{}` | `bp-N` id, reply `{removed}` |
|
|
420
|
+
| `step_over`, `resume` | Chrome: text only; Firefox `step_over`: `{paused, location: {line (1-based), column}}` | `{paused, location: {url, line_number, column_number} \| null}`, 0-based |
|
|
421
|
+
| `evaluate_on_call_frame` | Chrome: raw `RemoteObject` (exceptions as success); Firefox: bare value | the `evaluate_js` shape; exceptions are errors (`Evaluation threw: ...`) |
|
|
422
|
+
| `get_console_logs` | a JSON array; `clear` emptied the whole buffer | `{messages, total, truncated, notes?}`, at most `limit` (200) messages; `clear` removes the returned ones; the buffer is cleared on cross-site navigation |
|
|
423
|
+
|
|
424
|
+
## Limitations and TODO
|
|
425
|
+
|
|
426
|
+
- **TODO** Default window size from the screen size: launched browsers use
|
|
427
|
+
`--window-size` (default 1920x1080 on both); reading the screen size needs
|
|
428
|
+
native APIs per OS, left out of the server.
|
|
429
|
+
- **TODO** `pause_on_load` on Firefox: neither a preload `debugger;`
|
|
430
|
+
statement nor an RDP interrupt pauses a reloading page (spike verdict).
|
|
431
|
+
- **TODO** `rust-mcp-sdk` 2.0: the server stays on 1.1.0 (MCP protocol
|
|
432
|
+
2025-11-25). 2.0 moves to the stateless MCP protocol (2026-07-28), which
|
|
433
|
+
today's MCP clients probably do not speak yet; upgrade once the clients do.
|
|
434
|
+
- `get_performance_metrics` and `profile_page_performance` are partial on
|
|
435
|
+
Firefox: it has no Performance or Tracing domain, so they read what the
|
|
436
|
+
Web Performance APIs give and say what is missing (JS heap, layout and
|
|
437
|
+
style counts, long tasks, total blocking time).
|
|
438
|
+
- Firefox WebSocket frames and page errors need the Firefox debugger port
|
|
439
|
+
(WebDriver BiDi has no WebSocket frame events); binary frames are reported
|
|
440
|
+
as Firefox gives them, not base64 as on Chrome.
|
|
441
|
+
- `webmcp_list_tools`, `webmcp_invoke_tool`, `webmcp_get_invocation` and
|
|
442
|
+
`webmcp_list_invocations` are Chrome only (a Chrome testing feature).
|
|
443
|
+
- `send_cdp_command` is Chrome only; `send_bidi_command` and
|
|
444
|
+
`send_rdp_request` are Firefox only.
|
|
445
|
+
- Cookie import (`--allow-cookie-import`) and launch presets (`features`) are
|
|
446
|
+
Chrome only.
|
|
447
|
+
- Firefox breakpoints are set by URL only.
|
|
448
|
+
- `--local` checks the URLs the server navigates to and the known navigation
|
|
449
|
+
methods of the raw tools; JavaScript running in the page can still navigate
|
|
450
|
+
anywhere.
|
|
451
|
+
- Firefox: after a lost connection a launched Firefox gets a new session on
|
|
452
|
+
the next tool call; an attached Firefox keeps its orphan session, so
|
|
453
|
+
restart that Firefox.
|
|
454
|
+
- Firefox debugger: a tab is matched to its debugger by its position in
|
|
455
|
+
BiDi's and RDP's tab lists, checked against the tabs' web URLs; tabs
|
|
456
|
+
showing no web page (`about:blank`, error pages) cannot be checked, so a
|
|
457
|
+
tab opening or closing elsewhere at that very moment could still bind the
|
|
458
|
+
debugger to the wrong page. When the lists differ the debugger attaches by
|
|
459
|
+
activating the tab, as before.
|
|
460
|
+
- `get_network_logs` with response bodies has no total size limit (each
|
|
461
|
+
Firefox body is capped at 2 MB): use `url_filter` on busy pages.
|
|
462
|
+
- The Docker image is amd64 only.
|
|
463
|
+
|
|
464
|
+
## Untested
|
|
465
|
+
|
|
466
|
+
- Proxy authentication on Firefox against a real proxy.
|
|
467
|
+
- macOS and Windows with real browsers (CI builds and unit-tests them; the
|
|
468
|
+
E2E smoke ran on Linux only).
|
|
469
|
+
- Firefox ESR.
|
|
470
|
+
- The Docker image on arm64.
|
|
471
|
+
- The Windows clippy cross-check (`make clippy-windows`) on a machine with
|
|
472
|
+
mingw.
|
|
473
|
+
|
|
474
|
+
## Security notes
|
|
475
|
+
|
|
476
|
+
- `--allow-cookie-import` copies your real Chrome cookies into an isolated
|
|
477
|
+
profile; the agent must ask you before using `copy_cookies`.
|
|
478
|
+
- The proxy password is never logged and is given only to proxy (407)
|
|
479
|
+
challenges, never to a site's own authentication.
|
|
480
|
+
- The Firefox debugger port needs `devtools.chrome.enabled`: any local
|
|
481
|
+
process that reaches that port gets chrome (privileged) access to the
|
|
482
|
+
browser. Use `--no-firefox-debugger` when you do not need the debugger.
|
|
483
|
+
- Credential headers (`Authorization`, `Cookie`, `Set-Cookie`, ...) are
|
|
484
|
+
redacted in `get_network_logs` unless `include_sensitive_headers: true`.
|
|
485
|
+
- Logs carry tool names, ids, protocol method names and sizes, never
|
|
486
|
+
payloads, cookies or URLs with queries.
|
|
487
|
+
|
|
488
|
+
## Development
|
|
489
|
+
|
|
490
|
+
```sh
|
|
491
|
+
make check # fmt, clippy -D warnings, tests, docs, duplication, packaging, version
|
|
492
|
+
make e2e # E2E smoke against real, visible Chrome and Firefox (Linux, macOS)
|
|
493
|
+
make check-all # both
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
`make check` is hermetic: it never starts a browser or touches the network.
|
|
497
|
+
`make e2e` runs `tests/run_all_e2e.sh`: the launched-mode smoke for both
|
|
498
|
+
browsers, then the attach mode against browsers started by `tests/chrome.sh`
|
|
499
|
+
and `tests/firefox.sh`. See [AGENTS.md](AGENTS.md) for the conventions.
|
|
500
|
+
|
|
501
|
+
## Releasing
|
|
502
|
+
|
|
503
|
+
For the maintainer: bump the version in `Cargo.toml` and `package.json`,
|
|
504
|
+
close the `[Unreleased]` section of `CHANGELOG.md`, run `make check-all`,
|
|
505
|
+
tag `vX.Y.Z` and push the tag (the release workflow builds the installers and
|
|
506
|
+
publishes npm and Homebrew; it needs the `NPM_TOKEN` and
|
|
507
|
+
`HOMEBREW_TAP_TOKEN` secrets), then `cargo publish` by hand.
|
|
508
|
+
|
|
509
|
+
## License
|
|
510
|
+
|
|
511
|
+
MIT, see [LICENSE](LICENSE).
|