@raultov/browser-control-mcp 0.3.6-beta.6 → 0.5.0-beta.8

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 CHANGED
@@ -10,6 +10,209 @@ from a copy of its code: entries describe the differences from that release.
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [0.5.0-beta.8] - 2026-10-31
14
+
15
+ Public **BETA**. Closes the rest of the Playwright MCP comparison: the new
16
+ `network` tool group mocks requests (`mock_route`, `list_routes`,
17
+ `remove_route`) and takes a tab offline (`set_offline`), `save_pdf` prints
18
+ a tab to PDF, and `--secrets` keeps known values out of what the agent
19
+ reads. Debugging a paused page works better: `evaluate_js` answers at once
20
+ on Chrome, a pause names its script, and the input tools fail at once
21
+ naming the pause instead of reporting a click or a text that has not
22
+ happened (a behaviour change: they used to "succeed").
23
+
24
+ ### Added
25
+
26
+ - `--secrets <file>` (dotenv): every tool result and error shows the
27
+ file's values as `<secret:NAME>` (as is or escaped in JSON), and
28
+ `fill_input` / `fill_form` type the value of a `<secret:NAME>` they are
29
+ given, so the agent can fill a password without reading it. A
30
+ convenience, not a security boundary: images are not masked and a page
31
+ can transform a value. Values never reach the logs. (#14)
32
+ - `save_pdf`, in the `files` tool group: prints the tab to PDF
33
+ (`landscape`, `print_background`, `scale`, `page_ranges`) and writes it to
34
+ `path`, an absolute `.pdf` under the upload roots, answering `{path,
35
+ bytes, pages}`; without `path` the PDF comes back as an embedded
36
+ `application/pdf` resource (at most 5 MB). Chrome `Page.printToPDF`,
37
+ Firefox `browsingContext.print`. (#13)
38
+ - `mock_route`, `list_routes`, `remove_route` and `set_offline`, in the new
39
+ `network` tool group (`--tool-groups network`). A route answers the tab's
40
+ requests whose URL matches a glob with a mocked status, body, content
41
+ type and headers, without reaching the network; the latest matching
42
+ route wins and `list_routes` counts its hits. Chrome uses the `Fetch`
43
+ domain (not while `enable_proxy_auth` is active), Firefox a
44
+ `beforeRequestSent` network intercept. `set_offline` takes the tab
45
+ offline or back online (Chrome still serves its HTTP cache). (#12)
46
+
47
+ ### Fixed
48
+
49
+ - The input tools (`click_element`, `hover`, `fill_input`, `fill_form`,
50
+ `press_key`) fail at once, naming the pause, when the page is already
51
+ paused in the debugger, and send nothing: the page handles input events
52
+ only once it resumes. Chrome reported a click or a key as done, and
53
+ both browsers blamed the field ("rejected the text") or, on Firefox, a
54
+ covering `html` element. (#17)
55
+ - On Chrome `evaluate_js` answers at once while the page is paused in the
56
+ debugger: it waited 10 s and failed with "no result after 10 s", even
57
+ for `1 + 1`, because Chrome cannot await an evaluation while no
58
+ microtask runs. It now evaluates without awaiting, reads a settled promise from
59
+ its state, and fails at once on a pending one, saying to `resume` or use
60
+ `evaluate_on_call_frame`. (#16)
61
+ - On Chrome a pause's location names its script: `resume`, `step_over`,
62
+ the `{paused, location}` of `click_element` and `press_key`, and the
63
+ "The page paused in the debugger at ..." message of an interrupted call
64
+ gave `url: null`, because Chrome leaves the deprecated
65
+ `callFrames[].url` empty. The URL now comes from the tab's parsed
66
+ scripts, and the location also carries `script_id` (what
67
+ `set_breakpoint` takes). (#10)
68
+
69
+ ### Changed
70
+
71
+ - The package description (crates.io, npm, Homebrew `desc`) is 79
72
+ characters instead of 213, within Homebrew's 80, so `brew style` no
73
+ longer flags the formula's `desc` line as too long.
74
+
75
+ ## [0.4.0-beta.7] - 2026-10-31
76
+
77
+ Public **BETA**. Closes the gaps a comparison with Playwright MCP showed:
78
+ `snapshot` and `find_in_snapshot` outline the page with refs every
79
+ selector accepts, selectors reach into shadow roots and iframes with `>>>`,
80
+ and new tools wait for content (`wait_for`), fill forms (`fill_form`),
81
+ upload files, press keys, hover, read and change cookies and web storage,
82
+ and emulate viewports. A click into a breakpoint or a dialog answers at once.
83
+ Some behaviour changes: the `raw`, `perf`, `storage`, `files` and
84
+ `emulation` tools are listed only with the new `--tool-groups` (start the
85
+ server with `--tool-groups raw,perf` or `all` to keep `send_cdp_command`,
86
+ `get_custom_events` and the performance tools), tool descriptions are about
87
+ half as long, and `click_element` succeeds (noting it) when the click opens
88
+ a dialog or hits a breakpoint.
89
+
90
+ ### Added
91
+
92
+ - `--tool-groups` lists opt-in tool groups, comma separated: `storage`
93
+ (`cookies`, `web_storage`), `files` (`upload_file`), `raw`
94
+ (`send_cdp_command`, `send_bidi_command`, `send_rdp_request`,
95
+ `get_custom_events`), `perf` (`get_performance_metrics`,
96
+ `profile_page_performance`), `emulation` (`emulate`), or `all`. A tool of
97
+ a group that is off fails naming the flag. The tool list stays fixed for
98
+ the server's life (MCP 2026-07-28 forbids it to change as a side effect of
99
+ a call), so there is no tool to switch groups on during a session.
100
+
101
+ - `emulate`: the tab's viewport (`width`, `height`, `device_scale_factor`,
102
+ and on Chrome `mobile` with touch) and, on Chrome, CSS media features
103
+ (`color_scheme`, `reduced_motion`, `forced_colors`, `media: print`);
104
+ `reset` removes them. It answers what the page now sees, measured in it
105
+ (size, pixel ratio, media queries). Firefox emulates the viewport and
106
+ pixel ratio through `browsingContext.setViewport`; its WebDriver BiDi has
107
+ no media or touch emulation yet, so those options fail there naming
108
+ them. Responsive and dark-mode checks needed raw protocol commands. (#9)
109
+
110
+ - `cookies` and `web_storage` (Chrome and Firefox). `cookies` gets, sets
111
+ and deletes cookies: those the page's URL (or `url`) would be sent, or
112
+ those of a `domain`, optionally by `name` (Chrome `Network.*Cookie*`,
113
+ Firefox BiDi `storage.*` in the tab's partition, with the same filters
114
+ and replies). `web_storage` gets, sets, deletes and clears the page's
115
+ `localStorage` or `sessionStorage`. Both hide values (often session
116
+ tokens), giving their length, unless `show_values: true`. Cookies could
117
+ only be imported from your Chrome before. (#8)
118
+
119
+ - `press_key` and `hover` (Chrome and Firefox), and `button`,
120
+ `click_count` and `modifiers` for `click_element`. `press_key` presses
121
+ one key (a character, Enter, Tab, Escape, Backspace, Delete, the arrows,
122
+ Home, End, PageUp, PageDown, Insert, Space, F1 to F12) with Control,
123
+ Shift, Alt or Meta held, in `selector` (focused first) or the focused
124
+ element, as a user (Chrome `Input.dispatchKeyEvent`, Firefox key
125
+ actions); a shortcut types nothing, and a dialog or debugger pause the key
126
+ causes is noted like for a click. `hover` moves the mouse over an element
127
+ after the same visibility checks as `click_element`. A right click opens
128
+ context menus and `click_count: 2` double-clicks. These needed raw
129
+ protocol commands or synthetic JavaScript events, which many pages
130
+ ignore. (#7)
131
+
132
+ - `upload_file` (Chrome and Firefox): sets the files of an
133
+ `<input type=file>` as a user picking them (Chrome
134
+ `DOM.setFileInputFiles`, Firefox `input.setFiles`); the page gets `input`
135
+ and `change`. Only files under the upload roots are accepted: the
136
+ server's working directory and the system temp directory, or the
137
+ directories of the new repeatable `--upload-root` flag, checked once `..`
138
+ and symlinks are resolved, so the agent cannot hand a page any file of
139
+ the machine. Uploads needed three raw CDP commands, on Chrome only. (#6)
140
+
141
+ - `fill_form` (Chrome and Firefox): fills several fields in one call, in
142
+ order. Text fields, textareas, contenteditable elements and `<select>`s
143
+ go through `fill_input`; a checkbox or radio takes `true` or `false` and
144
+ is clicked only when its state differs. It answers
145
+ `Filled N fields.` with each field's outcome (`filled`, `selected`,
146
+ `checked`, `unchecked`, `unchanged`), never the values, and stops at the
147
+ first field that fails, naming it and what was done before. A login form
148
+ took one call per field. (#5)
149
+
150
+ - `snapshot` and `find_in_snapshot` (Chrome and Firefox): an outline of the
151
+ page with one line per element with a role (`button "Sign in" [ref=e7]`,
152
+ `textbox "Username" [ref=e3]`, `heading "Cart" [level=2] [ref=e1]`...),
153
+ its name, states and value, indented under its parent, through open
154
+ shadow roots and same-origin iframes, hidden elements left out; and a
155
+ search over that outline that returns only the matching lines under their
156
+ ancestors (a 1500-row page: 143 characters to find a button instead of a
157
+ 50 000-character snapshot). The `selector` of `click_element`,
158
+ `fill_input`, `scroll` and `wait_for` accepts the refs, so agents no
159
+ longer write CSS from raw HTML. Computed in the page with the same
160
+ JavaScript on both browsers (one outline, refs that resolve the same
161
+ way); refs are kept per element while it is in the page and end when the
162
+ page navigates; password values are never shown. (#4)
163
+
164
+ - Selectors of `click_element`, `fill_input`, `scroll` and `wait_for` reach
165
+ into open shadow roots and same-origin iframes with `>>>`
166
+ (`my-widget >>> button`, `#frame >>> #email`, chainable), on both
167
+ browsers. `inspect_dom` showed those elements but no tool could act on
168
+ them. A click inside iframes is checked for overlays in each document and
169
+ lands at the right point of the top window; a part that is neither an
170
+ open shadow host nor a same-origin iframe fails naming it. `fill_input`
171
+ also recognises fields of another frame (they failed as "not an input").
172
+ (#3)
173
+
174
+ - `wait_for` (Chrome and Firefox): waits until the page's visible text
175
+ contains `text` or no longer contains `text_gone`, the first element
176
+ matching `selector` is `visible`, `hidden`, `attached` or `detached`
177
+ (`state`), and/or the tab's URL contains `url`, checking every 100 ms up
178
+ to `timeout_ms` (default 5000, at most 30000). It answers
179
+ `Waited N ms for ...` with `{elapsed_ms, url}`, or fails naming what is
180
+ still missing and where the page is. Text includes open shadow roots and
181
+ same-origin iframes; a check the page did not answer (a navigation) is
182
+ retried; a JavaScript dialog or a debugger pause ends the wait at once.
183
+ Agents no longer have to write polling promises in `evaluate_js`. (#2)
184
+
185
+ ### Changed
186
+
187
+ - The `raw`, `perf`, `storage`, `files` and `emulation` tools are no longer
188
+ listed by default: 33 tools are (plus `enable_proxy_auth` and the WebMCP
189
+ tools behind their flags). Start the server with `--tool-groups raw,perf`
190
+ (or `all`) to keep `send_cdp_command`, `get_custom_events` and the
191
+ performance tools.
192
+ - Tool and argument descriptions are about half as long (the tool list of
193
+ every tool went from 72 986 to 35 568 characters, about 18 000 to 9 000
194
+ tokens; the 33 default tools take 23 555), and `instance_id` / `tab_id` no longer say "Chrome".
195
+
196
+ - `click_element` succeeds when the click opens a JavaScript dialog or its
197
+ handler pauses in the debugger: the click happened, so the reply says so
198
+ (`... The page opened a JavaScript confirm dialog ("Delete?"), which
199
+ blocks it: call handle_dialog ...`), followed by `{"dialog": {...}}` or
200
+ `{"paused": true, "location": ...}`. It was an error, which invited
201
+ agents to click again. Calls whose answer is missing (`evaluate_js`) or
202
+ that may have stopped half way (`fill_input`) still fail. (#11)
203
+
204
+ ### Fixed
205
+
206
+ - A call whose own code hits a breakpoint (`click_element` on a button whose
207
+ handler has one, `fill_input`, `scroll`, `evaluate_js`, `navigate`,
208
+ `reload`) fails at once on both browsers with where the page paused and
209
+ how to go on (`evaluate_on_call_frame`, `resume`, `step_over`). It waited
210
+ for the 10 s command timeout and failed with `Input.dispatchMouseEvent`
211
+ (Chrome) or `input.performActions` (Firefox) "timed out". On Chrome a
212
+ pause must last 200 ms to count, so a page's own `debugger;` the server
213
+ resumes does not fail the call. A Chrome click interrupted on the press
214
+ still sends the release, so the button is not left held. (#1)
215
+
13
216
  ## [0.3.6-beta.6] - 2026-10-29
14
217
 
15
218
  Public **BETA**. `fill_input` types the same way on both browsers,
package/README.md CHANGED
@@ -11,7 +11,7 @@ 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.3.6-beta.6).** This version contains known bugs and differences
14
+ > **Status: BETA (0.5.0-beta.8).** 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
@@ -29,6 +29,26 @@ life: start the server with `--browser firefox`, or pass
29
29
  `"browser": "firefox"` to `open_instance`. If Chrome is not installed but Firefox
30
30
  is, the launch error says so and tells the agent to ask you before switching.
31
31
 
32
+ ## Tool groups
33
+
34
+ Every tool listed costs the agent context on every turn, so niche and risky
35
+ tools are off by default and listed only with `--tool-groups`:
36
+
37
+ | Group | Tools | Why it is opt-in |
38
+ | --- | --- | --- |
39
+ | `storage` | `cookies`, `web_storage` | reads and changes session tokens |
40
+ | `files` | `upload_file`, `save_pdf` | reads and writes files of the machine |
41
+ | `raw` | `send_cdp_command`, `send_bidi_command`, `send_rdp_request`, `get_custom_events` | anything the protocol allows |
42
+ | `perf` | `get_performance_metrics`, `profile_page_performance` | niche |
43
+ | `emulation` | `emulate` | niche |
44
+ | `network` | `mock_route`, `list_routes`, `remove_route`, `set_offline` | changes what the page receives |
45
+
46
+ `--tool-groups all` lists every group. The page, input, observation and
47
+ debugger tools are always listed (33 tools), plus `enable_proxy_auth` with
48
+ `--proxy-server` and the WebMCP tools with `--enable-webmcp`. A tool of a
49
+ group that is off fails naming the flag. The list is fixed for the server's
50
+ life: it never changes during a session, so clients can cache it.
51
+
32
52
  ## Feature matrix
33
53
 
34
54
  ✓ supported, ✗ not available (the tool answers a capability error naming the
@@ -49,17 +69,32 @@ see the note.
49
69
  | `navigate` | ✓ | ✓ | |
50
70
  | `reload` | ✓ | ✓ | |
51
71
  | `scroll` | ✓ | ✓ | reports the window positions before and after |
72
+ | `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) |
73
+ | `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 |
52
74
  | `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) |
75
+ | `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 |
76
+ | `find_in_snapshot` | ✓ | ✓ | `text` (case-insensitive) or `regex` over the whole outline; matching lines under their ancestors, at most 20 |
77
+ | `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` |
53
78
  | `capture_screenshot` | ✓ | ✓ | `format` `png`, `jpeg` (or `jpg`) or `webp`; `full_page` at most 16 384 px per side (a text note says when cut) |
54
- | `click_element` | ✓ | ✓ | scrolls the element into view; fails without clicking when it is hidden or covered |
79
+ | `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}`) |
55
80
  | `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 |
81
+ | `press_key` | ✓ | ✓ | one key (a character, Enter, Tab, Escape, arrows, F1–F12...) with Control / Shift / Alt / Meta held, in `selector` (focused first) or the focused element; a shortcut types nothing |
82
+ | `hover` | ✓ | ✓ | moves the mouse over the element (scrolled into view; fails when hidden or covered) |
83
+ | `fill_form` | ✓ | ✓ | several fields in one call: text and `<select>` as `fill_input`, a checkbox or radio takes `true`/`false` and is clicked only when its state differs; stops at the first failing field naming what was done; never echoes values |
84
+ | `upload_file` | ✓ | ✓ | `files` group; sets the files of an `<input type=file>` (Chrome `DOM.setFileInputFiles`, Firefox `input.setFiles`); files must be under the upload roots (`--upload-root`, default the working and temp directories) |
56
85
  | `handle_dialog` | ✓ | ✓ | accepts or dismisses the open `alert` / `confirm` / `prompt` / `beforeunload` dialog |
57
86
  | `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`) |
87
+ | `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) |
88
+ | `web_storage` | ✓ | ✓ | `storage` group; `get` / `set` / `delete` / `clear` on the page's `localStorage` or `sessionStorage`; values hidden unless `show_values: true` |
58
89
  | `get_console_logs` | ✓ | ✓ | a message keeps at most 10 000 characters; Firefox: page errors, CSS warnings and platform messages need the debugger port |
59
90
  | `get_network_logs` | ✓ | ✓ | a failed request has `error`, a binary body `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); Firefox: WebSocket frames need the debugger port |
60
- | `get_custom_events` | ✓ | partial | `{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` |
61
- | `get_performance_metrics` | ✓ | partial | Firefox: `Nodes`, `Frames`, `Resources`, `TransferSize` from the Web Performance APIs (no Performance domain), plus a note |
62
- | `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` |
91
+ | `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) |
92
+ | `list_routes` | ✓ | ✓ | `network` group; the tab's routes with the `hits` each answered |
93
+ | `remove_route` | ✓ | ✓ | `network` group; one route by id, or all |
94
+ | `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 |
95
+ | `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` |
96
+ | `get_performance_metrics` | ✓ | partial | `perf` group; Firefox: `Nodes`, `Frames`, `Resources`, `TransferSize` from the Web Performance APIs (no Performance domain), plus a note |
97
+ | `profile_page_performance` | ✓ | partial | `perf` group; Firefox: FCP, LCP, DCL and Load from the Web Performance APIs; no trace, so `total_blocking_time_ms` and `top_long_tasks` are `null` |
63
98
  | `set_breakpoint` | ✓ | partial | Firefox: by URL only (no `script_id` / `script_hash`) |
64
99
  | `remove_breakpoint` | ✓ | ✓ | |
65
100
  | `resume` | ✓ | ✓ | |
@@ -68,9 +103,9 @@ see the note.
68
103
  | `search_scripts` | ✓ | ✓ | a long line's preview is 100 characters on each side of the match |
69
104
  | `pause_on_load` | ✓ | ✗ | TODO on Firefox, see [Limitations and TODO](#limitations-and-todo) |
70
105
  | `enable_proxy_auth` | ✓ | partial | needs `--proxy-server`; a request the proxy rejects twice is cancelled; untested on Firefox against a real proxy |
71
- | `send_cdp_command` | ✓ | ✗ | raw Chrome DevTools Protocol; refuses `Network`/`Page`/`Runtime`/`Log`/`Debugger.disable` and `Browser.close`; `Fetch.enable` comes with a note |
72
- | `send_bidi_command` | ✗ | ✓ | raw WebDriver BiDi; refuses `session.new`, `session.end`, `browser.close` |
73
- | `send_rdp_request` | ✗ | ✓ | raw Firefox RDP request to an actor (`thread`, `console`, `root` shortcuts) |
106
+ | `send_cdp_command` | ✓ | ✗ | `raw` group; raw Chrome DevTools Protocol; refuses `Network`/`Page`/`Runtime`/`Log`/`Debugger.disable` and `Browser.close`; `Fetch.enable` comes with a note |
107
+ | `send_bidi_command` | ✗ | ✓ | `raw` group; raw WebDriver BiDi; refuses `session.new`, `session.end`, `browser.close` |
108
+ | `send_rdp_request` | ✗ | ✓ | `raw` group; raw Firefox RDP request to an actor (`thread`, `console`, `root` shortcuts) |
74
109
  | `webmcp_list_tools` | ✓ | ✗ | needs `--enable-webmcp`; WebMCP is a Chrome testing feature; every annotation Chrome reports is listed (`readOnly`, `consequential`, `untrustedContent`, `debugging`, `autosubmit`, newer ones as they are) |
75
110
  | `webmcp_invoke_tool` | ✓ | ✗ | needs `--enable-webmcp`; the output of an `untrustedContent` tool comes with a note to treat it as data |
76
111
  | `webmcp_get_invocation` | ✓ | ✗ | needs `--enable-webmcp` |
@@ -305,6 +340,18 @@ claude mcp add browser-control-mcp -- npx -y @raultov/browser-control-mcp
305
340
  A `value` over 100 000 characters is not returned whole: a string keeps
306
341
  its first 100 000, an object or array is measured in the page and only
307
342
  described; the reply then carries `"truncated": true`.
343
+ - **Acting on elements.** `snapshot` outlines the page, one line per
344
+ element with a role, e.g. `textbox "Username" [ref=e3]`,
345
+ `button "Sign in" [ref=e7]`, `iframe [ref=e9]` with the frame's elements
346
+ under it; `find_in_snapshot` returns only the lines that match, under
347
+ their ancestors. The `selector` of `click_element`, `fill_input`,
348
+ `scroll` and `wait_for` takes such a ref, a CSS selector, or
349
+ `host >>> inner` to reach into an open shadow root or a same-origin
350
+ iframe (`#checkout >>> #card-number`, chainable). An element keeps its ref
351
+ in later snapshots while it is in the page; refs end when the page
352
+ navigates (a stale ref fails saying so). Roles and names are computed in
353
+ the page, an approximation of the browser's accessibility tree that is
354
+ the same on both browsers; password values never appear.
308
355
  - **Per-tab state.** Console logs, network traffic, breakpoints and custom
309
356
  events are kept per tab, in bounded buffers. A tab's console buffer (last
310
357
  1000 messages) is cleared when the tab moves to another site (registrable
@@ -338,6 +385,9 @@ claude mcp add browser-control-mcp -- npx -y @raultov/browser-control-mcp
338
385
  | `--enable-automation` | off | shows the automation infobar | ignored; `list_instances` shows a warning |
339
386
  | `--user-profile` | off | your default profile (no `--user-data-dir`) | only with `--firefox-port` (attach to your running Firefox) |
340
387
  | `--allow-cookie-import` | off | exposes `copy_cookies` | capability error |
388
+ | `--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 |
389
+ | `--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 |
390
+ | `--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 |
341
391
  | `--enable-webmcp` | off | WebMCP switches and `webmcp_*` tools | capability error |
342
392
  | `--proxy-server <URL>` | none | proxy for every instance; exposes `enable_proxy_auth` | `http://` and `socks5://` proxies |
343
393
  | `--proxy-username <USER>` | none | answers proxy (407) challenges | same (untested) |
@@ -390,9 +440,21 @@ Chrome and Firefox take the same calls:
390
440
  2. `search_scripts` with a needle to find the script URL and line.
391
441
  3. `set_breakpoint` with `url` and `line_number` (0-based).
392
442
  4. Trigger the code (`click_element`, `evaluate_js` with a `setTimeout`, a
393
- reload...). The page pauses.
443
+ reload...). The page pauses. A `click_element` whose handler hits the
444
+ breakpoint answers at once, as a success: the click happened, and the
445
+ reply adds `The page paused in the debugger at <url> line N, column M
446
+ (0-based)` (the URL when the browser gives it) and
447
+ `{"paused": true, "location": ...}`. Any other call the pause leaves
448
+ unfinished (`evaluate_js`, `fill_input`, `navigate`...) fails at once
449
+ with that location.
394
450
  5. `evaluate_on_call_frame` reads locals of the paused frame; `step_over`
395
- moves one statement; `resume` continues.
451
+ moves one statement; `resume` continues. While paused, the input tools
452
+ (`click_element`, `hover`, `fill_input`, `fill_form`, `press_key`) fail
453
+ at once naming the pause and do nothing: the page would handle their
454
+ events only after `resume`. `evaluate_js` still answers
455
+ while paused, in the page's global scope. A promise it returns cannot
456
+ always settle meanwhile: Chrome reads a settled one and fails at once on
457
+ a pending one; Firefox settles it when its microtasks can run.
396
458
  6. `remove_breakpoint` with the id `set_breakpoint` returned.
397
459
 
398
460
  Both browsers answer the same replies, with 0-based lines everywhere:
@@ -401,7 +463,7 @@ Both browsers answer the same replies, with 0-based lines everywhere:
401
463
  | --- | --- |
402
464
  | `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 |
403
465
  | `remove_breakpoint` | `{"removed": "bp-N"}`; an unknown id fails listing the tab's ids |
404
- | `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) |
466
+ | `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) |
405
467
  | `evaluate_on_call_frame` | the `evaluate_js` shape; an exception fails with `Evaluation threw: <what was thrown>` |
406
468
 
407
469
  Breakpoint ids are per tab and MCP-level (`bp-1`, `bp-2`...), like tab ids.
@@ -503,6 +565,25 @@ incompatible:
503
565
 
504
566
  - `--allow-cookie-import` copies your real Chrome cookies into an isolated
505
567
  profile; the agent must ask you before using `copy_cookies`.
568
+ - `cookies` and `web_storage` hide values (often session tokens) unless
569
+ `show_values: true`; values never reach the logs.
570
+ - `upload_file` hands the page a file's contents: it only reads files under
571
+ the upload roots (the server's working directory and the system temp
572
+ directory, or the `--upload-root` directories), resolved so `..` and
573
+ symlinks cannot leave them. Paths are never logged.
574
+ - `--secrets <file>` keeps known values (test passwords, API keys) out of
575
+ what the agent reads: every tool result and error (text, embedded text,
576
+ structured content) shows them as `<secret:NAME>`, also when escaped
577
+ inside JSON, and `fill_input` / `fill_form` type the value of a
578
+ `<secret:NAME>` they are given, so the agent can log in without seeing
579
+ the password. It is a convenience, not a security boundary: the page can
580
+ transform a value (encode it, split it) past the masking, images
581
+ (screenshots, PDFs) are not masked, and the server stops at start-up if
582
+ the file has a malformed line, a value shorter than 4 characters or a
583
+ name defined twice (the error names the line, never a value). Values
584
+ never reach the logs.
585
+ - `save_pdf` writes only `.pdf` files, under the same upload roots and
586
+ resolved the same way; it overwrites an existing `.pdf` there.
506
587
  - The proxy password is never logged and is given only to proxy (407)
507
588
  challenges, never to a site's own authentication.
508
589
  - The Firefox debugger port needs `devtools.chrome.enabled`: any local
@@ -19,7 +19,7 @@
19
19
  "hasInstallScript": true,
20
20
  "license": "MIT",
21
21
  "name": "@raultov/browser-control-mcp",
22
- "version": "0.3.6-beta.6"
22
+ "version": "0.5.0-beta.8"
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.3.6-beta.6"
51
+ "version": "0.5.0-beta.8"
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.6-beta.6"
3
+ "https://github.com/raultov/browser-control-mcp/releases/download/v0.5.0-beta.8"
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 at runtime: breakpoints, call frames and stepping over CDP and WebDriver BiDi/RDP, plus navigation, DOM, input, screenshots, console and network capture.",
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.3.6-beta.6",
95
+ "version": "0.5.0-beta.8",
96
96
  "volta": {
97
97
  "node": "18.14.1",
98
98
  "npm": "9.5.0"