@raultov/browser-control-mcp 0.4.0-beta.7 → 0.6.0-beta.9
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/CHANGELOG.md +130 -0
- package/README.md +97 -11
- package/npm-shrinkwrap.json +2 -2
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,136 @@ from a copy of its code: entries describe the differences from that release.
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [0.6.0-beta.9] - 2026-10-31
|
|
14
|
+
|
|
15
|
+
Public **BETA**. Spends fewer tokens and answers what the agent needs, after
|
|
16
|
+
a side-by-side study with Playwright MCP and chrome-devtools-mcp (now in the
|
|
17
|
+
README): `get_network_logs` and `get_console_logs` answer one line per entry
|
|
18
|
+
(details on demand), `snapshot` no longer repeats table text, input actions
|
|
19
|
+
wait for the page to settle, and `profile_page_performance` explains its
|
|
20
|
+
numbers with `insights`. Behaviour changes: the two log tools answer text
|
|
21
|
+
instead of JSON unless `include_details: true`, layout tables leave only
|
|
22
|
+
their content in `snapshot`, and input actions take ~50 ms more when the
|
|
23
|
+
page does not react.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- `profile_page_performance` takes `insights: true`: what explains the
|
|
28
|
+
numbers, read in the page with the Web Performance APIs on both browsers:
|
|
29
|
+
the document's TTFB, server time and compression, the LCP element with
|
|
30
|
+
its breakdown (TTFB, load delay, load, render delay), layout-shift
|
|
31
|
+
culprits, render-blocking requests, the slowest requests and the scripts
|
|
32
|
+
of long animation frames (at most 5 each), and `findings` from the web.dev
|
|
33
|
+
thresholds (LCP over 2.5 s, CLS over 0.1, a slow server, an uncompressed
|
|
34
|
+
document, render-blocking requests). Firefox reports no layout shifts,
|
|
35
|
+
render-blocking status or long frames: those are `null`, with notes. The
|
|
36
|
+
reply grows from ~60 to ~600-700 tokens on Wikipedia. (#22)
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- `click_element`, `hover`, `fill_input`, `fill_form` and `press_key` wait
|
|
41
|
+
for the page to settle before answering: until the DOM stays unchanged for
|
|
42
|
+
50 ms (style changes aside) and the requests the action started finished,
|
|
43
|
+
at most 1 s, on both browsers. They answered in 1-2 ms, before the page
|
|
44
|
+
reacted, so a read right after a click that fetches data saw the old page.
|
|
45
|
+
An action nothing reacts to now takes ~50 ms and keeps its reply; one the
|
|
46
|
+
page reacts to adds how (`The page reacted (the DOM changed, 1 request(s))
|
|
47
|
+
and settled after 134 ms.`), or that it was still changing after 1 s. Not
|
|
48
|
+
done when the action left a dialog open or the page paused. (#21)
|
|
49
|
+
- `snapshot` and `find_in_snapshot` no longer print a table's text two or
|
|
50
|
+
three times. A table without headers (`th`, `thead`, `caption`,
|
|
51
|
+
`summary`) and without a role is layout: its `table`, `row` and `cell`
|
|
52
|
+
lines are left out and only their content stays, as in Chrome's
|
|
53
|
+
accessibility tree. A cell is named by its content only when that content
|
|
54
|
+
is plain text (folded into the name, up to 100 characters); a cell with
|
|
55
|
+
links or controls keeps them as children, unnamed. Hacker News went from
|
|
56
|
+
~8 500 to ~4 500 tokens. (#20)
|
|
57
|
+
- `get_console_logs` answers one line per message by default: `[level]
|
|
58
|
+
text @ url:line` (lines from 1), repeats in a row folded (`[x3]`),
|
|
59
|
+
uncaught errors marked (`Uncaught ...`) with up to 5 stack lines
|
|
60
|
+
indented; `include_details: true` keeps the previous JSON. A page with
|
|
61
|
+
six messages went from ~550 to ~150 tokens. (#19)
|
|
62
|
+
- `get_network_logs` answers one line per request by default (`id METHOD
|
|
63
|
+
url => status [type]`, oldest first), then one per WebSocket frame with a
|
|
64
|
+
payload preview: ~100 tokens for a page of four requests instead of ~2 250.
|
|
65
|
+
The new `request_id` shows one request's headers and body;
|
|
66
|
+
`include_details: true` (now off by default) keeps the previous JSON with
|
|
67
|
+
every request's details. (#18)
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
|
|
71
|
+
- `get_console_logs` previews objects and arrays as the console shows
|
|
72
|
+
them (`hello {n: 1, s: 'x'} [1, 2, 3]`): Chrome printed `Object`, Firefox
|
|
73
|
+
`Object(3) Array(3)`. On Chrome a `console.*` call keeps its location
|
|
74
|
+
(`url`, `line_number`), which was always empty, and `undefined`
|
|
75
|
+
arguments are printed. (#19)
|
|
76
|
+
- `get_network_logs` no longer lists the browser's own requests: a freshly
|
|
77
|
+
launched Chrome logged its new-tab page (`chrome://resources/...`, `data:`
|
|
78
|
+
images) before the first navigation. `chrome://`, `devtools://`, `data:`,
|
|
79
|
+
`about:` and extension URLs are not logged. (#18)
|
|
80
|
+
|
|
81
|
+
## [0.5.0-beta.8] - 2026-10-31
|
|
82
|
+
|
|
83
|
+
Public **BETA**. Closes the rest of the Playwright MCP comparison: the new
|
|
84
|
+
`network` tool group mocks requests (`mock_route`, `list_routes`,
|
|
85
|
+
`remove_route`) and takes a tab offline (`set_offline`), `save_pdf` prints
|
|
86
|
+
a tab to PDF, and `--secrets` keeps known values out of what the agent
|
|
87
|
+
reads. Debugging a paused page works better: `evaluate_js` answers at once
|
|
88
|
+
on Chrome, a pause names its script, and the input tools fail at once
|
|
89
|
+
naming the pause instead of reporting a click or a text that has not
|
|
90
|
+
happened (a behaviour change: they used to "succeed").
|
|
91
|
+
|
|
92
|
+
### Added
|
|
93
|
+
|
|
94
|
+
- `--secrets <file>` (dotenv): every tool result and error shows the
|
|
95
|
+
file's values as `<secret:NAME>` (as is or escaped in JSON), and
|
|
96
|
+
`fill_input` / `fill_form` type the value of a `<secret:NAME>` they are
|
|
97
|
+
given, so the agent can fill a password without reading it. A
|
|
98
|
+
convenience, not a security boundary: images are not masked and a page
|
|
99
|
+
can transform a value. Values never reach the logs. (#14)
|
|
100
|
+
- `save_pdf`, in the `files` tool group: prints the tab to PDF
|
|
101
|
+
(`landscape`, `print_background`, `scale`, `page_ranges`) and writes it to
|
|
102
|
+
`path`, an absolute `.pdf` under the upload roots, answering `{path,
|
|
103
|
+
bytes, pages}`; without `path` the PDF comes back as an embedded
|
|
104
|
+
`application/pdf` resource (at most 5 MB). Chrome `Page.printToPDF`,
|
|
105
|
+
Firefox `browsingContext.print`. (#13)
|
|
106
|
+
- `mock_route`, `list_routes`, `remove_route` and `set_offline`, in the new
|
|
107
|
+
`network` tool group (`--tool-groups network`). A route answers the tab's
|
|
108
|
+
requests whose URL matches a glob with a mocked status, body, content
|
|
109
|
+
type and headers, without reaching the network; the latest matching
|
|
110
|
+
route wins and `list_routes` counts its hits. Chrome uses the `Fetch`
|
|
111
|
+
domain (not while `enable_proxy_auth` is active), Firefox a
|
|
112
|
+
`beforeRequestSent` network intercept. `set_offline` takes the tab
|
|
113
|
+
offline or back online (Chrome still serves its HTTP cache). (#12)
|
|
114
|
+
|
|
115
|
+
### Fixed
|
|
116
|
+
|
|
117
|
+
- The input tools (`click_element`, `hover`, `fill_input`, `fill_form`,
|
|
118
|
+
`press_key`) fail at once, naming the pause, when the page is already
|
|
119
|
+
paused in the debugger, and send nothing: the page handles input events
|
|
120
|
+
only once it resumes. Chrome reported a click or a key as done, and
|
|
121
|
+
both browsers blamed the field ("rejected the text") or, on Firefox, a
|
|
122
|
+
covering `html` element. (#17)
|
|
123
|
+
- On Chrome `evaluate_js` answers at once while the page is paused in the
|
|
124
|
+
debugger: it waited 10 s and failed with "no result after 10 s", even
|
|
125
|
+
for `1 + 1`, because Chrome cannot await an evaluation while no
|
|
126
|
+
microtask runs. It now evaluates without awaiting, reads a settled promise from
|
|
127
|
+
its state, and fails at once on a pending one, saying to `resume` or use
|
|
128
|
+
`evaluate_on_call_frame`. (#16)
|
|
129
|
+
- On Chrome a pause's location names its script: `resume`, `step_over`,
|
|
130
|
+
the `{paused, location}` of `click_element` and `press_key`, and the
|
|
131
|
+
"The page paused in the debugger at ..." message of an interrupted call
|
|
132
|
+
gave `url: null`, because Chrome leaves the deprecated
|
|
133
|
+
`callFrames[].url` empty. The URL now comes from the tab's parsed
|
|
134
|
+
scripts, and the location also carries `script_id` (what
|
|
135
|
+
`set_breakpoint` takes). (#10)
|
|
136
|
+
|
|
137
|
+
### Changed
|
|
138
|
+
|
|
139
|
+
- The package description (crates.io, npm, Homebrew `desc`) is 79
|
|
140
|
+
characters instead of 213, within Homebrew's 80, so `brew style` no
|
|
141
|
+
longer flags the formula's `desc` line as too long.
|
|
142
|
+
|
|
13
143
|
## [0.4.0-beta.7] - 2026-10-31
|
|
14
144
|
|
|
15
145
|
Public **BETA**. Closes the gaps a comparison with Playwright MCP showed:
|
package/README.md
CHANGED
|
@@ -11,12 +11,62 @@ It is the successor of
|
|
|
11
11
|
from a copy of its code) and complements Playwright MCP or
|
|
12
12
|
chrome-devtools-mcp with runtime JavaScript debugging on both browsers.
|
|
13
13
|
|
|
14
|
-
> **Status: BETA (0.
|
|
14
|
+
> **Status: BETA (0.6.0-beta.9).** This version contains known bugs and differences
|
|
15
15
|
> between Chrome and Firefox (see [Limitations and TODO](#limitations-and-todo))
|
|
16
16
|
> and parts that have not been verified (see [Untested](#untested)). Tool
|
|
17
17
|
> replies may change in incompatible ways before 1.0. Please report problems
|
|
18
18
|
> in the [issues](https://github.com/raultov/browser-control-mcp/issues).
|
|
19
19
|
|
|
20
|
+
💬 Questions, ideas or feedback: join us on [Discord](https://discord.gg/TunAHQZ8K).
|
|
21
|
+
|
|
22
|
+
## Comparison with Playwright MCP and chrome-devtools-mcp
|
|
23
|
+
|
|
24
|
+
Measured on 2026-10-31 with browser-control-mcp built from the current
|
|
25
|
+
sources, `@playwright/mcp` 0.0.83 and `chrome-devtools-mcp` 1.12.0, driven by
|
|
26
|
+
the same stdio client through the same tasks on Chrome 156 (headless,
|
|
27
|
+
Linux). Every outcome was checked in the page, not taken from the reply.
|
|
28
|
+
Tokens are estimated as characters / 4 for every server.
|
|
29
|
+
|
|
30
|
+
**What each one can do**
|
|
31
|
+
|
|
32
|
+
| | browser-control-mcp | Playwright MCP | chrome-devtools-mcp |
|
|
33
|
+
| --- | --- | --- | --- |
|
|
34
|
+
| JavaScript debugger (breakpoints, call frames, stepping, script search) | **✓ Chrome and Firefox** (pause on load: Chrome) | ✗ | ✗ |
|
|
35
|
+
| Firefox | **✓ next to Chrome, same tools** | ✓ one browser per server | ✗ |
|
|
36
|
+
| Several browsers and instances at once | **✓** | ✗ | ✗ |
|
|
37
|
+
| Selectors | CSS, snapshot refs, `host >>> inner` into shadow roots and iframes | CSS, snapshot refs | snapshot refs only |
|
|
38
|
+
| Snapshot of a long page | **bounded at 50 000 characters**, `find_in_snapshot` searches the rest | unbounded | unbounded |
|
|
39
|
+
| Waits for the page to react after an action | ✓ ~50 ms when nothing reacts | ✓ ~500 ms | ✓ ~200 ms |
|
|
40
|
+
| Clicking a covered or hidden element | **fails at once, naming the overlay** | fails after a 5 s timeout | reports success (the overlay gets the click) |
|
|
41
|
+
| Mock requests, offline mode | ✓ | ✓ | offline only |
|
|
42
|
+
| PDF | ✓ file or inline | ✓ file | ✗ |
|
|
43
|
+
| Cookies and web storage | ✓ values hidden unless asked | ✓ values shown | ✗ |
|
|
44
|
+
| Secrets masked in output (`--secrets`) | ✓ | ✓ | ✗ |
|
|
45
|
+
| Performance | vitals + insights (LCP breakdown, layout shifts, render-blocking, long scripts) | trace file | trace + insights, Lighthouse |
|
|
46
|
+
| Heap snapshots, CSS styles, screencast | ✗ | video, tracing | ✓ |
|
|
47
|
+
| Generates Playwright test code | ✗ | ✓ | ✗ |
|
|
48
|
+
| Telemetry | **none** | none found | usage statistics on by default; traced URLs sent to CrUX |
|
|
49
|
+
| Single native binary (no Node.js) | **✓** | ✗ | ✗ |
|
|
50
|
+
|
|
51
|
+
**Measured**
|
|
52
|
+
|
|
53
|
+
| Task | browser-control-mcp | Playwright MCP | chrome-devtools-mcp |
|
|
54
|
+
| --- | --- | --- | --- |
|
|
55
|
+
| Tool list in context, default tools | 6 040 tokens | 5 330 tokens | 7 770 tokens |
|
|
56
|
+
| Tool list in context, every opt-in tool on | 9 500 tokens | 12 040 tokens | not measured (optional categories) |
|
|
57
|
+
| Snapshot of Hacker News | **4 588 tokens** | 12 356 tokens | 6 869 tokens |
|
|
58
|
+
| Snapshot of Wikipedia "Web browser" | **12 643 tokens** (cut, searchable) | 34 349 tokens | 27 656 tokens |
|
|
59
|
+
| Snapshot of MDN `<input>` | **12 625 tokens** (cut, searchable) | 158 077 tokens | 76 654 tokens |
|
|
60
|
+
| Log in step by step (8 calls) | **298 tokens, 1.8 s** | 521 tokens, 2.4 s | 364 tokens, 2.6 s |
|
|
61
|
+
| Log in with `fill_form` (5 calls) | **321 tokens, 1.6 s** | 428 tokens, 1.9 s | 352 tokens, 2.0 s |
|
|
62
|
+
| Click, median | 52 ms | 531 ms | 234 ms |
|
|
63
|
+
| Evaluate JavaScript, median | **0 ms** | 504 ms | 204 ms |
|
|
64
|
+
| Navigate (local page), median | **13 ms** | 31 ms | 119 ms |
|
|
65
|
+
| Full-page screenshot of a 31 700 px page | cut at 16 384 px, with a note | 3.3 MB image in the reply | saved to a file |
|
|
66
|
+
| Navigation to an unknown host | error | error | success with the error in the text |
|
|
67
|
+
| Navigate, then read the console (log, warning, errors) | 249 tokens | 191 tokens | 117 tokens |
|
|
68
|
+
| Navigate, then read the network log (3 requests) | 168 tokens | 48 tokens | 54 tokens |
|
|
69
|
+
|
|
20
70
|
## Supported browsers
|
|
21
71
|
|
|
22
72
|
| Browser | Protocol | Validated with |
|
|
@@ -37,10 +87,11 @@ tools are off by default and listed only with `--tool-groups`:
|
|
|
37
87
|
| Group | Tools | Why it is opt-in |
|
|
38
88
|
| --- | --- | --- |
|
|
39
89
|
| `storage` | `cookies`, `web_storage` | reads and changes session tokens |
|
|
40
|
-
| `files` | `upload_file` |
|
|
90
|
+
| `files` | `upload_file`, `save_pdf` | reads and writes files of the machine |
|
|
41
91
|
| `raw` | `send_cdp_command`, `send_bidi_command`, `send_rdp_request`, `get_custom_events` | anything the protocol allows |
|
|
42
92
|
| `perf` | `get_performance_metrics`, `profile_page_performance` | niche |
|
|
43
93
|
| `emulation` | `emulate` | niche |
|
|
94
|
+
| `network` | `mock_route`, `list_routes`, `remove_route`, `set_offline` | changes what the page receives |
|
|
44
95
|
|
|
45
96
|
`--tool-groups all` lists every group. The page, input, observation and
|
|
46
97
|
debugger tools are always listed (33 tools), plus `enable_proxy_auth` with
|
|
@@ -71,8 +122,9 @@ see the note.
|
|
|
71
122
|
| `emulate` | ✓ | partial | `emulation` group; `width`/`height`, `device_scale_factor`, `mobile` (touch), `color_scheme`, `reduced_motion`, `forced_colors`, `media` (`print`), `reset`; answers what the page now sees; Firefox: viewport and device pixel ratio only (BiDi has no media or touch emulation yet; those options fail naming them) |
|
|
72
123
|
| `wait_for` | ✓ | ✓ | waits (checking every 100 ms, at most 30 s) until a visible text appears or is gone, a selector is `visible` / `hidden` / `attached` / `detached`, or the URL contains a text; a dialog or debugger pause ends the wait at once |
|
|
73
124
|
| `inspect_dom` | ✓ | ✓ | includes open shadow roots and same-origin iframes (cross-origin ones are noted); at most 100 000 characters without `query` (cut in the page) |
|
|
74
|
-
| `snapshot` | ✓ | ✓ | outline of the page: role, name, states, value and a ref (`e12`) per element, through open shadow roots and same-origin iframes; at most 50 000 characters (`selector` and `depth` narrow it); the same JavaScript on both browsers |
|
|
125
|
+
| `snapshot` | ✓ | ✓ | outline of the page: role, name, states, value and a ref (`e12`) per element, through open shadow roots and same-origin iframes; tables without headers (`th`, `thead`, `caption`) are layout and leave only their content, and a cell is named by its text only when it holds nothing else; at most 50 000 characters (`selector` and `depth` narrow it); the same JavaScript on both browsers |
|
|
75
126
|
| `find_in_snapshot` | ✓ | ✓ | `text` (case-insensitive) or `regex` over the whole outline; matching lines under their ancestors, at most 20 |
|
|
127
|
+
| `save_pdf` | ✓ | ✓ | `files` group; prints the tab as the browser's print does (`landscape`, `print_background`, `scale` 0.1–2, `page_ranges` like `1-3, 5`); with `path` (an absolute `.pdf` under the upload roots) writes the file and answers `{path, bytes, pages}`, else returns the PDF as an embedded `application/pdf` resource (at most 5 MB); Chrome `Page.printToPDF`, Firefox `browsingContext.print` |
|
|
76
128
|
| `capture_screenshot` | ✓ | ✓ | `format` `png`, `jpeg` (or `jpg`) or `webp`; `full_page` at most 16 384 px per side (a text note says when cut) |
|
|
77
129
|
| `click_element` | ✓ | ✓ | scrolls the element into view; `button` (left/right/middle), `click_count` (2 = double click) and `modifiers`; fails without clicking when it is hidden or covered; `host >>> inner` selectors reach open shadow roots and same-origin iframes (also in `fill_input`, `scroll`, `wait_for`); a dialog or debugger pause the click causes is noted in a successful reply (`{dialog}` or `{paused, location}`) |
|
|
78
130
|
| `fill_input` | ✓ | ✓ | picks `<select>` options; fails without typing on elements that cannot take the text; types key by key on both (key handlers run; newlines and tabs inserted as text), about 1 ms per character; `change` on leaving the field, or at the end when the window has no focus |
|
|
@@ -84,11 +136,15 @@ see the note.
|
|
|
84
136
|
| `evaluate_js` | ✓ | ✓ | `value` at most 100 000 characters (`truncated: true`); Chrome stops a script after 8 s, Firefox cannot (a looping tab is closed with `close_tab`) |
|
|
85
137
|
| `cookies` | ✓ | ✓ | `storage` group; `get` / `set` / `delete`: the cookies sent to the page (or `url`) or of a `domain`; values hidden unless `show_values: true` (Chrome `Network.*Cookie*`, Firefox BiDi `storage.*` in the tab's partition) |
|
|
86
138
|
| `web_storage` | ✓ | ✓ | `storage` group; `get` / `set` / `delete` / `clear` on the page's `localStorage` or `sessionStorage`; values hidden unless `show_values: true` |
|
|
87
|
-
| `get_console_logs` | ✓ | ✓ | a message keeps at most 10 000 characters; Firefox: page errors, CSS warnings and platform messages need the debugger port |
|
|
88
|
-
| `get_network_logs` | ✓ | ✓ |
|
|
139
|
+
| `get_console_logs` | ✓ | ✓ | one line per message, oldest first: `[level] text @ url:line` (lines from 1), objects and arrays previewed as the console shows them (`{n: 1}`, `[1, 2, 3]`), repeats in a row folded (`[x3]`), uncaught errors marked with up to 5 stack lines indented; `include_details: true` gives the JSON with every field; a message keeps at most 10 000 characters; Firefox: page errors, CSS warnings and platform messages need the debugger port |
|
|
140
|
+
| `get_network_logs` | ✓ | ✓ | one line per request, oldest first (`id METHOD url => status [type]`, or `failed: <reason>` / `pending`), then one per WebSocket frame (payload preview); `request_id` shows one request's headers and body, `include_details: true` every request's as JSON (a binary body has `response_body_encoding: "base64"`; bodies at most 64 000 characters, none over 2 MB; a WebSocket payload keeps 64 000, `payload_len` is the whole); the browser's own requests (`chrome://`, `devtools://`, `data:`, `about:`, extensions) are not logged; Firefox: WebSocket frames need the debugger port |
|
|
141
|
+
| `mock_route` | ✓ | ✓ | `network` group; requests whose URL matches a glob (`*`, `?`, `\` escapes) get a mocked `status`, `body` (text, at most 1 MB), `content_type` and `headers` without reaching the network; the latest matching route wins; at most 50 routes per tab, kept until `remove_route` or the tab closes (Chrome `Fetch`, not while `enable_proxy_auth` is active; Firefox a `network` intercept) |
|
|
142
|
+
| `list_routes` | ✓ | ✓ | `network` group; the tab's routes with the `hits` each answered |
|
|
143
|
+
| `remove_route` | ✓ | ✓ | `network` group; one route by id, or all |
|
|
144
|
+
| `set_offline` | ✓ | ✓ | `network` group; requests fail as without a network and `navigator.onLine` is false, until set back (Chrome `Network.emulateNetworkConditions`, Firefox `emulation.setNetworkConditions`); mocked routes still answer; Chrome still serves what its HTTP cache holds, as an offline browser does, Firefox fails every request |
|
|
89
145
|
| `get_custom_events` | ✓ | partial | `raw` group; `{events, total, truncated}`, the newest `limit` events; `clear` removes the returned ones; params over 64 000 characters of JSON are replaced by `{truncated, characters}`; Firefox: events of the modules subscribed with `send_bidi_command` |
|
|
90
146
|
| `get_performance_metrics` | ✓ | partial | `perf` group; Firefox: `Nodes`, `Frames`, `Resources`, `TransferSize` from the Web Performance APIs (no Performance domain), plus a note |
|
|
91
|
-
| `profile_page_performance` | ✓ | partial | `perf` group;
|
|
147
|
+
| `profile_page_performance` | ✓ | partial | `perf` group; `insights: true` adds what explains the numbers, read with the Web Performance APIs (same JavaScript on both): the document's TTFB, server time and compression, the LCP element and its breakdown (TTFB, load delay, load, render delay), layout-shift culprits, render-blocking requests, the slowest requests and the scripts of long animation frames (at most 5 each), plus `findings` from the web.dev thresholds; Firefox: FCP, LCP, DCL and Load; no trace, so `total_blocking_time_ms` and `top_long_tasks` are `null`, and its insights have no layout shifts, render-blocking requests or long frames (`null`, with notes) |
|
|
92
148
|
| `set_breakpoint` | ✓ | partial | Firefox: by URL only (no `script_id` / `script_hash`) |
|
|
93
149
|
| `remove_breakpoint` | ✓ | ✓ | |
|
|
94
150
|
| `resume` | ✓ | ✓ | |
|
|
@@ -105,6 +161,16 @@ see the note.
|
|
|
105
161
|
| `webmcp_get_invocation` | ✓ | ✗ | needs `--enable-webmcp` |
|
|
106
162
|
| `webmcp_list_invocations` | ✓ | ✗ | needs `--enable-webmcp` |
|
|
107
163
|
|
|
164
|
+
After `click_element`, `hover`, `fill_input`, `fill_form` and `press_key`
|
|
165
|
+
the server waits for the page to settle before answering: until its DOM
|
|
166
|
+
stays unchanged for 50 ms (style changes, often animations, aside) and the
|
|
167
|
+
requests the action started finished, at most 1 s, on both browsers. An
|
|
168
|
+
action nothing reacts to costs those 50 ms and keeps its reply; otherwise
|
|
169
|
+
the reply adds `The page reacted (the DOM changed, 1 request(s)) and settled
|
|
170
|
+
after 134 ms.`, or says the page was still changing after 1 s. A click that
|
|
171
|
+
navigates waits for the new page's requests. Use `wait_for` for anything
|
|
172
|
+
slower (a timer, a long request).
|
|
173
|
+
|
|
108
174
|
The JavaScript debugger tools on Firefox need its debugger (RDP) port: on by
|
|
109
175
|
default for a launched Firefox, `--firefox-rdp-port` for an attached one. The
|
|
110
176
|
same port gives Firefox's WebSocket frames and the console messages BiDi does
|
|
@@ -379,8 +445,9 @@ claude mcp add browser-control-mcp -- npx -y @raultov/browser-control-mcp
|
|
|
379
445
|
| `--enable-automation` | off | shows the automation infobar | ignored; `list_instances` shows a warning |
|
|
380
446
|
| `--user-profile` | off | your default profile (no `--user-data-dir`) | only with `--firefox-port` (attach to your running Firefox) |
|
|
381
447
|
| `--allow-cookie-import` | off | exposes `copy_cookies` | capability error |
|
|
382
|
-
| `--
|
|
383
|
-
| `--
|
|
448
|
+
| `--secrets <FILE>` | none | dotenv file (`NAME=value`, at least 4 characters per value) whose values every tool result and error shows as `<secret:NAME>`; `fill_input` and `fill_form` type the value of a `<secret:NAME>` (see [Security notes](#security-notes)) | same |
|
|
449
|
+
| `--tool-groups <GROUPS>` | none | lists the opt-in tool groups, comma separated: `storage`, `files`, `raw`, `perf`, `emulation`, `network` or `all` (see [Tool groups](#tool-groups)) | same |
|
|
450
|
+
| `--upload-root <DIR>` | working + temp dir | directory `upload_file` may read files from and `save_pdf` may write PDFs to (repeatable; replaces the defaults) | same |
|
|
384
451
|
| `--enable-webmcp` | off | WebMCP switches and `webmcp_*` tools | capability error |
|
|
385
452
|
| `--proxy-server <URL>` | none | proxy for every instance; exposes `enable_proxy_auth` | `http://` and `socks5://` proxies |
|
|
386
453
|
| `--proxy-username <USER>` | none | answers proxy (407) challenges | same (untested) |
|
|
@@ -441,7 +508,13 @@ Chrome and Firefox take the same calls:
|
|
|
441
508
|
unfinished (`evaluate_js`, `fill_input`, `navigate`...) fails at once
|
|
442
509
|
with that location.
|
|
443
510
|
5. `evaluate_on_call_frame` reads locals of the paused frame; `step_over`
|
|
444
|
-
moves one statement; `resume` continues.
|
|
511
|
+
moves one statement; `resume` continues. While paused, the input tools
|
|
512
|
+
(`click_element`, `hover`, `fill_input`, `fill_form`, `press_key`) fail
|
|
513
|
+
at once naming the pause and do nothing: the page would handle their
|
|
514
|
+
events only after `resume`. `evaluate_js` still answers
|
|
515
|
+
while paused, in the page's global scope. A promise it returns cannot
|
|
516
|
+
always settle meanwhile: Chrome reads a settled one and fails at once on
|
|
517
|
+
a pending one; Firefox settles it when its microtasks can run.
|
|
445
518
|
6. `remove_breakpoint` with the id `set_breakpoint` returned.
|
|
446
519
|
|
|
447
520
|
Both browsers answer the same replies, with 0-based lines everywhere:
|
|
@@ -450,7 +523,7 @@ Both browsers answer the same replies, with 0-based lines everywhere:
|
|
|
450
523
|
| --- | --- |
|
|
451
524
|
| `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 |
|
|
452
525
|
| `remove_breakpoint` | `{"removed": "bp-N"}`; an unknown id fails listing the tab's ids |
|
|
453
|
-
| `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) |
|
|
526
|
+
| `step_over`, `resume` | `{"paused", "location": {"url", "script_id"?, "line_number", "column_number"} \| null}`: where the thread stopped next (`script_id` on Chrome, for `set_breakpoint`) (`step_over` waits up to 2 s; `resume` reports a pause only if one follows within 250 ms) |
|
|
454
527
|
| `evaluate_on_call_frame` | the `evaluate_js` shape; an exception fails with `Evaluation threw: <what was thrown>` |
|
|
455
528
|
|
|
456
529
|
Breakpoint ids are per tab and MCP-level (`bp-1`, `bp-2`...), like tab ids.
|
|
@@ -534,7 +607,7 @@ incompatible:
|
|
|
534
607
|
- Firefox debugger: tabs are mapped to the debugger through Firefox's Remote
|
|
535
608
|
Agent (verified on Firefox 157 only); a Firefox without it answers the
|
|
536
609
|
debugger tools with an error instead of guessing a tab.
|
|
537
|
-
- `get_network_logs` with
|
|
610
|
+
- `get_network_logs` with `include_details: true` has no total size limit (each
|
|
538
611
|
Firefox body is capped at 2 MB): use `url_filter` on busy pages.
|
|
539
612
|
- The Docker image is amd64 only.
|
|
540
613
|
|
|
@@ -558,6 +631,19 @@ incompatible:
|
|
|
558
631
|
the upload roots (the server's working directory and the system temp
|
|
559
632
|
directory, or the `--upload-root` directories), resolved so `..` and
|
|
560
633
|
symlinks cannot leave them. Paths are never logged.
|
|
634
|
+
- `--secrets <file>` keeps known values (test passwords, API keys) out of
|
|
635
|
+
what the agent reads: every tool result and error (text, embedded text,
|
|
636
|
+
structured content) shows them as `<secret:NAME>`, also when escaped
|
|
637
|
+
inside JSON, and `fill_input` / `fill_form` type the value of a
|
|
638
|
+
`<secret:NAME>` they are given, so the agent can log in without seeing
|
|
639
|
+
the password. It is a convenience, not a security boundary: the page can
|
|
640
|
+
transform a value (encode it, split it) past the masking, images
|
|
641
|
+
(screenshots, PDFs) are not masked, and the server stops at start-up if
|
|
642
|
+
the file has a malformed line, a value shorter than 4 characters or a
|
|
643
|
+
name defined twice (the error names the line, never a value). Values
|
|
644
|
+
never reach the logs.
|
|
645
|
+
- `save_pdf` writes only `.pdf` files, under the same upload roots and
|
|
646
|
+
resolved the same way; it overwrites an existing `.pdf` there.
|
|
561
647
|
- The proxy password is never logged and is given only to proxy (407)
|
|
562
648
|
challenges, never to a site's own authentication.
|
|
563
649
|
- The Firefox debugger port needs `devtools.chrome.enabled`: any local
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"hasInstallScript": true,
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"name": "@raultov/browser-control-mcp",
|
|
22
|
-
"version": "0.
|
|
22
|
+
"version": "0.6.0-beta.9"
|
|
23
23
|
},
|
|
24
24
|
"node_modules/detect-libc": {
|
|
25
25
|
"engines": {
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"requires": true,
|
|
51
|
-
"version": "0.
|
|
51
|
+
"version": "0.6.0-beta.9"
|
|
52
52
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"artifactDownloadUrls": [
|
|
3
|
-
"https://github.com/raultov/browser-control-mcp/releases/download/v0.
|
|
3
|
+
"https://github.com/raultov/browser-control-mcp/releases/download/v0.6.0-beta.9"
|
|
4
4
|
],
|
|
5
5
|
"author": "raultov <raultov@protonmail.com>",
|
|
6
6
|
"bin": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"detect-libc": "^2.1.2"
|
|
11
11
|
},
|
|
12
|
-
"description": "MCP server to control and debug Chrome and Firefox
|
|
12
|
+
"description": "MCP server to control and debug Chrome and Firefox, with JavaScript breakpoints",
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"prettier": "^3.8.3"
|
|
15
15
|
},
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"zipExt": ".tar.xz"
|
|
93
93
|
}
|
|
94
94
|
},
|
|
95
|
-
"version": "0.
|
|
95
|
+
"version": "0.6.0-beta.9",
|
|
96
96
|
"volta": {
|
|
97
97
|
"node": "18.14.1",
|
|
98
98
|
"npm": "9.5.0"
|