@raultov/browser-control-mcp 0.11.0-beta.17 → 0.11.0-beta.19
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 +165 -0
- package/README.md +33 -20
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,171 @@ from a copy of its code: entries describe the differences from that release.
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [0.11.0-beta.19] - 2026-11-03
|
|
14
|
+
|
|
15
|
+
Public **BETA**. Downloads are saved per instance and reported on both
|
|
16
|
+
browsers: an input action that downloads a file names the saved file,
|
|
17
|
+
`list_downloads` lists them, and `--downloads-dir` keeps them after the
|
|
18
|
+
instance closes. `get_network_logs` shows what it hid: sizes of bodies over
|
|
19
|
+
2 MB, redirect hops, CORS reasons, streams the page closed, and on Firefox
|
|
20
|
+
request types and bodies. Chrome watches dedicated workers (their requests
|
|
21
|
+
and console). Accepting a `beforeunload` waits for the navigation it allowed.
|
|
22
|
+
`snapshot` names tree items and shows descriptions, pressed, current and live
|
|
23
|
+
states; console counters and timers, `(in promise)` rejections and prompt
|
|
24
|
+
defaults behave the same on both browsers; a newline in a textarea is the
|
|
25
|
+
Enter key, `scroll` can turn the mouse wheel, and input actions note file
|
|
26
|
+
choosers and same-document URL changes. Behaviour changes: Chrome intercepts
|
|
27
|
+
file choosers (none opens on screen); `redirects` in `get_network_logs` are
|
|
28
|
+
`{url, status, location}` objects; an unset `same_site` is `null` on
|
|
29
|
+
Firefox too.
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- Downloads are saved and reported on both browsers (#77). A click that
|
|
34
|
+
downloads a file used to say only that the page reacted, and the agent
|
|
35
|
+
could not find the file. Now each instance saves downloads in a directory
|
|
36
|
+
of its own, the input action's reply says
|
|
37
|
+
`The action downloaded data.csv (8 bytes) to <path>.` (or that the file
|
|
38
|
+
is still downloading, or was canceled), and the new `list_downloads` tool
|
|
39
|
+
lists them with their state, sizes and tab. The directory is
|
|
40
|
+
`--downloads-dir <DIR>/<instance id>` (new flag), else a `downloads`
|
|
41
|
+
folder inside the instance's temporary profile (deleted with it), else a
|
|
42
|
+
new folder in the system temp directory (an attached browser, or
|
|
43
|
+
`--user-profile`). A file is never replaced: Chrome saves each download
|
|
44
|
+
under its guid, then the server names it `data.csv`, `data (1).csv`...;
|
|
45
|
+
Firefox numbers them itself (`data(1).csv`).
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- Chrome: `search_scripts` finds an inline script the debugger no longer
|
|
50
|
+
holds. One that had already run could be released while its page sat in
|
|
51
|
+
the back-forward cache, and Chrome does not announce it again when the
|
|
52
|
+
page comes back, so a search for code visible in the page found nothing
|
|
53
|
+
(an intermittent E2E failure). The page's `<script>` elements are now
|
|
54
|
+
searched too: such a match has `scriptId: null`, `lineInScript` and a note
|
|
55
|
+
to reload the page before setting a breakpoint in it. (#62)
|
|
56
|
+
- Input actions miss fewer effects (#81), the same on both browsers: in a
|
|
57
|
+
textarea or contenteditable, `fill_input` types a newline as the Enter key
|
|
58
|
+
(Chrome inserted it as text with no key; Firefox sent a key named `\n`),
|
|
59
|
+
and a contenteditable whose new line is a block no longer reads as
|
|
60
|
+
reformatted. `scroll` with `wheel: true` dispatches a real mouse wheel
|
|
61
|
+
event at an element or the viewport's centre, for a canvas or map that
|
|
62
|
+
listens to `wheel`. An action that opens a file chooser says so (Chrome
|
|
63
|
+
now intercepts file choosers: none opens on screen; `upload_file` sets
|
|
64
|
+
the files), and one that changes the URL without loading a page
|
|
65
|
+
(`history.pushState`, a `#fragment`) names the new URL; on Firefox,
|
|
66
|
+
`list_tabs` follows such URL changes too.
|
|
67
|
+
- Console and dialog details (#80). On Firefox, `console.count` and the
|
|
68
|
+
timers keep their count and time (`counter: 2`, not `counter [x2]`), and
|
|
69
|
+
its own messages (a rejected cookie, a CSP report) no longer read
|
|
70
|
+
`Uncaught`. An unhandled rejection reads `Uncaught (in promise) …` on
|
|
71
|
+
both browsers (Firefox: with its debugger port). Accepting a `prompt`
|
|
72
|
+
without `prompt_text` answers its default, as a user pressing OK does (it
|
|
73
|
+
answered `""`). A cookie without `SameSite` has `same_site: null` on both
|
|
74
|
+
browsers (Firefox said `"Default"`).
|
|
75
|
+
- `snapshot` shows more of what assistive technology reads (#79), the same
|
|
76
|
+
on both browsers: a tree item is named by its own text (`treeitem "src"`,
|
|
77
|
+
not its nested items' text; it had no name), the cells of a `grid` are
|
|
78
|
+
`gridcell`, and elements show `[description="…"]` (`aria-describedby`,
|
|
79
|
+
`aria-description`), `[pressed=…]`, `[current=page]` and `[live=polite]`
|
|
80
|
+
(an `aria-live` element without a role is now listed, as `generic`;
|
|
81
|
+
`alert`, `status` and `log` are live regions).
|
|
82
|
+
- Accepting a `beforeunload` with `handle_dialog` returns once the
|
|
83
|
+
navigation it allowed committed, and the reply names the URL reached
|
|
84
|
+
(both browsers). A `navigate` right after used to race it: on Chrome it
|
|
85
|
+
met the dialog again and the tab stayed blocked; on Firefox it failed with
|
|
86
|
+
`Beforeunload prompt was rejected` while the tab ended on the page the
|
|
87
|
+
dialog let it go to. (#76)
|
|
88
|
+
- Chrome: what a dedicated worker does reaches the tab's logs. Its
|
|
89
|
+
requests were missing from `get_network_logs` and its script stayed
|
|
90
|
+
`pending`; its `console.log` read as `info` (Chrome's `Log` domain has no
|
|
91
|
+
`log` level). Each worker is now attached like a frame of another origin
|
|
92
|
+
(its own session, `Runtime` and `Network`; within the 50 watched targets
|
|
93
|
+
per tab), and the tab's copies of its console messages are dropped. (#75)
|
|
94
|
+
- `get_network_logs` shows what it used to hide (#78):
|
|
95
|
+
- A response body over 2 MB has `response_body_omitted` with its size,
|
|
96
|
+
instead of a bare `null`.
|
|
97
|
+
- A redirected request is still one line, but it says
|
|
98
|
+
`(after N redirects from <first URL>)` and `url_filter` matches any
|
|
99
|
+
hop. Its `redirects` are now `{url, status, location}` objects instead
|
|
100
|
+
of bare URLs.
|
|
101
|
+
- A CORS block names its reason: Chrome
|
|
102
|
+
`net::ERR_FAILED (blocked by CORS: MissingAllowOriginHeader)`. Firefox
|
|
103
|
+
showed the response as `200 OK`; the request now fails with
|
|
104
|
+
`blocked by CORS: <reason>`, read from Firefox's console error.
|
|
105
|
+
- On Chrome, a request the page canceled after its response arrived (an
|
|
106
|
+
`EventSource` it closed) keeps its status, with
|
|
107
|
+
`canceled_by_page: true`, instead of `failed: net::ERR_ABORTED`.
|
|
108
|
+
- Firefox: fetches and XHRs have a `resource_type` (`fetch`,
|
|
109
|
+
`xmlhttprequest`), and request bodies are kept (`request_post_data`, with
|
|
110
|
+
`request_post_data_encoding: "base64"` for a binary one).
|
|
111
|
+
|
|
112
|
+
## [0.11.0-beta.18] - 2026-11-03
|
|
113
|
+
|
|
114
|
+
Public **BETA**. Consistency fixes on both browsers. `webmcp_cancel_invocation`
|
|
115
|
+
cancels a pending WebMCP invocation; `webmcp_invoke_tool` waits 10 s by
|
|
116
|
+
default (`timeout_ms`), takes no `frameId` when one frame has the tool, and
|
|
117
|
+
`webmcp_list_tools` is compact. Tab tools behave the same on Chrome and
|
|
118
|
+
Firefox (`close_tab` defaults to the active tab, one rule for the next active
|
|
119
|
+
tab, `list_tabs` in tab order, Chrome registers its first tab after
|
|
120
|
+
`restart_browser`). Chrome `search_scripts` no longer counts or searches the
|
|
121
|
+
server's snippets or a previous page's scripts; `resume` and the other
|
|
122
|
+
debugger tools word "not paused" the same on both browsers. Chrome `emulate`
|
|
123
|
+
measures the page once a resize landed. Behaviour changes: `webmcp_list_tools`
|
|
124
|
+
answers `{frameId, tools}`; `webmcp_invoke_tool` waits 10 s instead of 30 s.
|
|
125
|
+
|
|
126
|
+
### Added
|
|
127
|
+
|
|
128
|
+
- `webmcp_cancel_invocation` (`--enable-webmcp`, Chrome) cancels a pending
|
|
129
|
+
WebMCP invocation with `WebMCP.cancelInvocation`; one Chrome no longer
|
|
130
|
+
knows (its page navigated away) is marked `Canceled`. (#73)
|
|
131
|
+
|
|
132
|
+
### Fixed
|
|
133
|
+
|
|
134
|
+
- Chrome: `emulate` measures what the page sees once a resize (or a reset)
|
|
135
|
+
landed: the next `resize` event or two animation frames, at most 500 ms.
|
|
136
|
+
Chrome answers the override before the renderer resizes, so the reply
|
|
137
|
+
could hold the previous size on a fresh instance. (#74)
|
|
138
|
+
- Debugger tools no longer leak internals (#66):
|
|
139
|
+
- Chrome: `search_scripts` neither counts nor searches the server's own
|
|
140
|
+
snippets nor the scripts of a page the tab left. Its script list was
|
|
141
|
+
never cleared on navigation: after a change of process Chrome reuses
|
|
142
|
+
script ids, so a stale entry named the server's outline snippet, and a
|
|
143
|
+
page with 5 scripts counted 38. The list is now cleared when the page's
|
|
144
|
+
contexts are, and scripts created over the protocol (no URL and no
|
|
145
|
+
stack) are left out; a page's own `eval` stays.
|
|
146
|
+
- `search_scripts` with `debug` answers JSON (`{scripts, errors}`), not
|
|
147
|
+
Rust's `Debug` format.
|
|
148
|
+
- Chrome `evaluate_on_call_frame` on a page that is not paused fails with
|
|
149
|
+
`the debugger is not paused`, as on Firefox and as `step_over`.
|
|
150
|
+
- `resume` on a page that is not paused answers `The debugger was not
|
|
151
|
+
paused: nothing to resume.` with `{paused: false}` on both browsers
|
|
152
|
+
(Firefox failed with `wrongState`, Chrome answered either way).
|
|
153
|
+
- Firefox RDP failures say `Firefox (RDP) protocol error in <request>`;
|
|
154
|
+
they were labelled BiDi.
|
|
155
|
+
- Tab tools behave the same on both browsers (#69):
|
|
156
|
+
- `close_tab` without `tab_id` closes the active tab, as every other tool
|
|
157
|
+
omitting it acts on it (it failed with `missing field \`tab_id\``).
|
|
158
|
+
- Closing the active tab activates the most recently active tab still
|
|
159
|
+
open, else the newest one (by number: `tab-10` after `tab-9`). Chrome
|
|
160
|
+
picked an arbitrary tab when none had been active, Firefox the highest
|
|
161
|
+
id as text.
|
|
162
|
+
- `list_tabs` lists the tabs by number on both browsers.
|
|
163
|
+
- After `restart_browser`, Chrome registers the new browser's first tab
|
|
164
|
+
(it listed none until the next call), as Firefox does.
|
|
165
|
+
- WebMCP tools are easier to drive (#73):
|
|
166
|
+
- `webmcp_invoke_tool` waits 10 s by default (it blocked 30 s), or
|
|
167
|
+
`timeout_ms` (at most 60 000); the pending reply says how to poll or
|
|
168
|
+
cancel the invocation. `frameId` is optional when one frame registers
|
|
169
|
+
the tool (a clear error names the frames otherwise).
|
|
170
|
+
- A pending invocation whose page navigated away carries a `note` in
|
|
171
|
+
`webmcp_get_invocation` and `webmcp_list_invocations` (it showed
|
|
172
|
+
`Pending` forever).
|
|
173
|
+
- `webmcp_list_invocations` takes `status` in any case.
|
|
174
|
+
- `webmcp_list_tools` is compact JSON: `{frameId, tools}` with the frame
|
|
175
|
+
once when shared, no empty `required` and no false annotations (on
|
|
176
|
+
knot.kz, 789 tokens instead of ~1700).
|
|
177
|
+
|
|
13
178
|
## [0.11.0-beta.17] - 2026-11-02
|
|
14
179
|
|
|
15
180
|
Public **BETA**. Fixes found probing the debugger, tab and raw protocol tools
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ It is the successor of
|
|
|
13
13
|
from a copy of its code) and complements Playwright MCP or
|
|
14
14
|
chrome-devtools-mcp with runtime JavaScript debugging on both browsers.
|
|
15
15
|
|
|
16
|
-
> **Status: BETA (0.11.0-beta.
|
|
16
|
+
> **Status: BETA (0.11.0-beta.19).** This version contains known bugs and differences
|
|
17
17
|
> between Chrome and Firefox (see [Limitations and TODO](#limitations-and-todo))
|
|
18
18
|
> and parts that have not been verified (see [Untested](#untested)). Tool
|
|
19
19
|
> replies may change in incompatible ways before 1.0. Please report problems
|
|
@@ -117,19 +117,20 @@ see the note.
|
|
|
117
117
|
| `open_instance` | ✓ | partial | `features`, `copy_cookies` and `source_profile` are Chrome only |
|
|
118
118
|
| `close_instance` | ✓ | ✓ | |
|
|
119
119
|
| `list_instances` | ✓ | ✓ | each instance reports its `browser` and `capabilities` |
|
|
120
|
-
| `restart_browser` | ✓ | ✓ | was `restart_chrome`; refused on a Chrome already running on the port (attached, not launched by the server) |
|
|
120
|
+
| `restart_browser` | ✓ | ✓ | was `restart_chrome`; refused on a Chrome already running on the port (attached, not launched by the server); the new browser's first page is registered as a tab |
|
|
121
121
|
| `stop_browser` | ✓ | ✓ | was `stop_chrome`; on an attached Chrome or Firefox it only disconnects (Firefox: ends the session), the browser keeps running and the reply says so |
|
|
122
122
|
| `open_tab` | ✓ | ✓ | the new tab becomes the active one, in front |
|
|
123
|
-
| `close_tab` | ✓ | ✓ | the
|
|
124
|
-
| `list_tabs` | ✓ | ✓ | |
|
|
123
|
+
| `close_tab` | ✓ | ✓ | `tab_id` defaults to the active tab; the most recently active open tab (else the newest) becomes active and is brought to the front |
|
|
124
|
+
| `list_tabs` | ✓ | ✓ | tabs in the order they were opened (`tab-2` before `tab-10`) |
|
|
125
|
+
| `list_downloads` | ✓ | ✓ | the files the instance's pages downloaded, oldest first (`id`, `url`, `file_name`, `path` once complete, `state` `in_progress` / `completed` / `canceled`, sizes, `tab_id`), and the `directory` they are saved in; an input action that starts a download says where the file went. Chrome: saved under its guid (`Browser.setDownloadBehavior` `allowAndName`), then given its name without replacing a file (`data (1).csv`); Firefox: `browser.setDownloadBehavior`, numbered by Firefox (`data(1).csv`), sizes read from the saved file |
|
|
125
126
|
| `switch_tab` | ✓ | ✓ | |
|
|
126
127
|
| `navigate` | ✓ | ✓ | `history: "back"` or `"forward"` instead of `url` moves one entry in the tab's history and waits for the page to load (also when it comes from the back-forward cache, which fires no `load`); at either end it fails without moving; a URL the browser saves instead of opening (a download: `Content-Disposition: attachment`, or a type such as CSV) fails saying so, and the tab stays where it was; a page behind HTTP authentication (401) fails at once naming its scheme and realm and how to log in (credentials in the URL, `http://user:password@host/path`, which the browser keeps for the session); a page that pauses at a breakpoint or opens a dialog while loading fails at once naming it (see `resume`, `handle_dialog`) |
|
|
127
128
|
| `reload` | ✓ | ✓ | a pause, a dialog or HTTP authentication while loading fails at once, as with `navigate` |
|
|
128
|
-
| `scroll` | ✓ | ✓ | reports the window positions before and after |
|
|
129
|
+
| `scroll` | ✓ | ✓ | reports the window positions before and after; `wheel: true` dispatches a real mouse wheel event instead (`x`/`y` pixels, default 100 down) at `selector` or the viewport's centre, for a canvas or map listening to `wheel` (Chrome `Input.dispatchMouseEvent` `mouseWheel`, Firefox a BiDi `wheel` action) |
|
|
129
130
|
| `emulate` | ✓ | partial | `emulation` group; `width`/`height`, `device_scale_factor`, `mobile` (touch), `color_scheme`, `reduced_motion`, `forced_colors`, `media` (`print`), `user_agent`, `locale`, `timezone` (checked by the page's `Intl` first), `geolocation` (the page is granted the permission to read it), `cpu_throttling` (1–20×), `network_throttling` (`slow-3g`, `fast-3g`, `4g`, `none`), `reset`; each call adds to the earlier ones; answers what the page now sees (with the position it reads and the active throttling); Firefox: no `mobile`, media or throttling options (BiDi has no media, touch or throttling emulation yet; they fail naming them) |
|
|
130
131
|
| `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 |
|
|
131
132
|
| `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) |
|
|
132
|
-
| `snapshot` | ✓ | ✓ | outline of the page: role, name, states, value and a ref (`e12`) per element, through open shadow roots and same-origin iframes, and on Chrome closed shadow roots too (pages up to 20 000 elements; their elements are reached by ref, not by `>>>`); frames of another origin are read too (at most 20, 5 deep), their iframe marked `[frame=f2]` and their refs prefixed (`f2e5`), and `selector: "f2"` outlines one; 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; comboboxes, spinbuttons and sliders built with ARIA show their value (the field's text, `aria-valuetext` or `aria-valuenow`, the option a button-like combobox shows), progress bars and meters theirs (`40/100`), and a `<details>` summary `[expanded=true|false]`; a field the page rejects (`aria-invalid`) or the browser does (`:user-invalid`, once the user changed it or tried to submit) is `[invalid]`, with `[error="..."]` from its `aria-errormessage`, `aria-describedby` or the browser's validation message; a native `<select>` lists its options under its combobox (`[selected]`, `[disabled]`, optgroups as groups; no refs: `fill_input` picks one), the first 25 and how many more, all of them for `find_in_snapshot`; what the page made inert is left out: `inert` subtrees and, while a modal `<dialog>` is open (marked `[modal]`), everything outside it; an element that is only clickable (a handler or a pointer cursor), draggable (`draggable="true"`, marked `[draggable]`) or a drop zone (`ondrop`/`ondragover`) is one named line with a ref for `drag`, unless it holds links, buttons or fields, which are listed under it; at most 50 000 characters (`selector` and `depth` narrow it); the same JavaScript on both browsers |
|
|
133
|
+
| `snapshot` | ✓ | ✓ | outline of the page: role, name, states, value and a ref (`e12`) per element, through open shadow roots and same-origin iframes, and on Chrome closed shadow roots too (pages up to 20 000 elements; their elements are reached by ref, not by `>>>`); frames of another origin are read too (at most 20, 5 deep), their iframe marked `[frame=f2]` and their refs prefixed (`f2e5`), and `selector: "f2"` outlines one; 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; comboboxes, spinbuttons and sliders built with ARIA show their value (the field's text, `aria-valuetext` or `aria-valuenow`, the option a button-like combobox shows), progress bars and meters theirs (`40/100`), and a `<details>` summary `[expanded=true|false]`; a tree item is named by its own text (not its nested items'), the cells of a `grid` are `gridcell`, and elements show `[description="…"]` (`aria-describedby`, `aria-description`), `[pressed=true|false|mixed]`, `[current=page|…]` and `[live=polite|assertive]` (an `aria-live` element without a role is `generic`; `alert`, `status` and `log` are live regions); a field the page rejects (`aria-invalid`) or the browser does (`:user-invalid`, once the user changed it or tried to submit) is `[invalid]`, with `[error="..."]` from its `aria-errormessage`, `aria-describedby` or the browser's validation message; a native `<select>` lists its options under its combobox (`[selected]`, `[disabled]`, optgroups as groups; no refs: `fill_input` picks one), the first 25 and how many more, all of them for `find_in_snapshot`; what the page made inert is left out: `inert` subtrees and, while a modal `<dialog>` is open (marked `[modal]`), everything outside it; an element that is only clickable (a handler or a pointer cursor), draggable (`draggable="true"`, marked `[draggable]`) or a drop zone (`ondrop`/`ondragover`) is one named line with a ref for `drag`, unless it holds links, buttons or fields, which are listed under it; at most 50 000 characters (`selector` and `depth` narrow it); the same JavaScript on both browsers |
|
|
133
134
|
| `find_in_snapshot` | ✓ | ✓ | `text` (case-insensitive) or `regex` over the whole outline; matching lines under their ancestors, at most 20 |
|
|
134
135
|
| `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` |
|
|
135
136
|
| `capture_screenshot` | ✓ | ✓ | `format` `png`, `jpeg` (or `jpg`) or `webp`; `full_page` at most 16 384 px per side (a text note says when cut); `selector` captures one element (CSS, `host >>> inner` or a snapshot ref), scrolled into view, whole even when taller than the viewport; on Chrome a tab in the background (hidden) is refused at once, naming `switch_tab` |
|
|
@@ -140,12 +141,12 @@ see the note.
|
|
|
140
141
|
| `drag` | ✓ | ✓ | presses on `from` (checked like `click_element`), moves in steps to the centre of `to` (which must then be in the viewport) and releases: sortable lists, boards, sliders; HTML5 drag and drop is real on Chrome and simulated with script events on Firefox (its WebDriver input starts a native drag but never drops it), and the reply says so |
|
|
141
142
|
| `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 |
|
|
142
143
|
| `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) |
|
|
143
|
-
| `handle_dialog` | ✓ | ✓ | accepts or dismisses the open `alert` / `confirm` / `prompt` / `beforeunload` dialog |
|
|
144
|
+
| `handle_dialog` | ✓ | ✓ | accepts or dismisses the open `alert` / `confirm` / `prompt` / `beforeunload` dialog; accepting a `beforeunload` waits (at most 5 s) for the navigation it allowed and names the URL reached; accepting a `prompt` without `prompt_text` answers its default, as a user pressing OK does |
|
|
144
145
|
| `evaluate_js` | ✓ | ✓ | `await` at the top level (`const r = await fetch(u); r.json()`), as in a console; `value` at most 100 000 characters (`truncated: true`); symbols and functions are described; Chrome stops a script after 8 s, Firefox cannot (a looping tab is closed with `close_tab`) |
|
|
145
|
-
| `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) |
|
|
146
|
+
| `cookies` | ✓ | ✓ | `storage` group; `get` / `set` / `delete`: the cookies sent to the page (or `url`) or of a `domain`; values hidden unless `show_values: true`; a cookie without `SameSite` has `same_site: null` (Chrome `Network.*Cookie*`, Firefox BiDi `storage.*` in the tab's partition) |
|
|
146
147
|
| `web_storage` | ✓ | ✓ | `storage` group; `get` / `set` / `delete` / `clear` on the page's `localStorage` or `sessionStorage`; values hidden unless `show_values: true` |
|
|
147
|
-
| `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 |
|
|
148
|
-
| `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
|
|
148
|
+
| `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 (an unhandled rejection `Uncaught (in promise) …`; Firefox: with its debugger port), `console.count` and the timers with their count and time, and Firefox's own messages (a rejected cookie, a CSP report) not marked uncaught; `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 |
|
|
149
|
+
| `get_network_logs` | ✓ | ✓ | the tab's requests, its frames' and its dedicated workers'; one line per request, oldest first (`id METHOD url => status [type]`, or `failed: <reason>` / `pending`; a redirected request says `(after N redirects from <first URL>)`, and `url_filter` matches any hop; a CORS block says `blocked by CORS: <reason>`; a request the page canceled after its response, such as a closed `EventSource`, keeps its status with `canceled by the page`), 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; a body over 2 MB is not fetched and `response_body_omitted` gives its size; `redirects` lists each hop with its `status` and `location`; a WebSocket payload keeps 64 000, `payload_len` is the whole); the browser's own requests (`chrome://`, `devtools://`, `data:`, `about:`, extensions) are not logged; a note counts the responses with no body because they are still loading or the page never read them (an open stream; on Chrome, a `fetch` whose body the page did not consume); Firefox: WebSocket frames need the debugger port, a fetch or XHR has the type `fetch` / `xmlhttprequest` and a beacon none (Firefox gives no type for it) |
|
|
149
150
|
| `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) |
|
|
150
151
|
| `list_routes` | ✓ | ✓ | `network` group; the tab's routes with the `hits` each answered |
|
|
151
152
|
| `remove_route` | ✓ | ✓ | `network` group; one route by id, or all |
|
|
@@ -156,19 +157,20 @@ see the note.
|
|
|
156
157
|
| `audit_page` | ✓ | ✓ | `perf` group; accessibility (images without alt, fields, buttons and links without a name, `html lang`, heading order, duplicate ids, broken `aria-labelledby`/`aria-describedby`, `tabindex` > 0, targets under 24×24 px too close to another, text contrast under WCAG AA), SEO (title, meta description, viewport, vague link text, uncrawlable links, canonical, `noindex`) and best practices (doctype and quirks mode, HTTPS, mixed content, distorted images, console errors); the same script on both browsers; each failing check lists its first 10 elements with a snapshot ref, a selector, its start tag and a detail (the contrast ratio, the size...), then the ids that passed or did not apply; `categories` narrows it; not checked: text over background images or gradients, iframes, closed shadow roots, response headers |
|
|
157
158
|
| `set_breakpoint` | ✓ | partial | Firefox: by URL only (no `script_id` / `script_hash`) |
|
|
158
159
|
| `remove_breakpoint` | ✓ | ✓ | |
|
|
159
|
-
| `resume` | ✓ | ✓ | |
|
|
160
|
+
| `resume` | ✓ | ✓ | on a page that is not paused: `The debugger was not paused: nothing to resume.` with `{paused: false}` |
|
|
160
161
|
| `step_over` | ✓ | ✓ | |
|
|
161
162
|
| `evaluate_on_call_frame` | ✓ | ✓ | |
|
|
162
|
-
| `search_scripts` | ✓ | ✓ | a long line's preview is 100 characters on each side of the match; inline scripts and modules included; frames of another origin too (Chrome ids such as `2.17`) |
|
|
163
|
+
| `search_scripts` | ✓ | ✓ | a long line's preview is 100 characters on each side of the match; inline scripts and modules included, the server's own evaluations left out (Chrome: an inline script the debugger no longer holds, released while its page sat in the back-forward cache, is found in the page's `<script>` elements, with `scriptId: null`, `lineInScript` and a note to reload before setting a breakpoint in it); `debug` answers `{scripts, errors}` as JSON; frames of another origin too (Chrome ids such as `2.17`) |
|
|
163
164
|
| `pause_on_load` | ✓ | ✗ | TODO on Firefox, see [Limitations and TODO](#limitations-and-todo) |
|
|
164
165
|
| `enable_proxy_auth` | ✓ | partial | needs `--proxy-server`; a request the proxy rejects twice is cancelled; untested on Firefox against a real proxy |
|
|
165
166
|
| `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 |
|
|
166
167
|
| `send_bidi_command` | ✗ | ✓ | `raw` group; raw WebDriver BiDi; refuses `session.new`, `session.end`, `browser.close` |
|
|
167
168
|
| `send_rdp_request` | ✗ | ✓ | `raw` group; raw Firefox RDP request to an actor (`thread`, `console`, `root` shortcuts); the event packets that actor sends afterwards go to `get_custom_events` (at most 16 actors watched) |
|
|
168
|
-
| `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) |
|
|
169
|
-
| `webmcp_invoke_tool` | ✓ | ✗ | needs `--enable-webmcp`; a tool answering in MCP shape (`{content, isError}`) gives its own text and image blocks, and its `isError` makes the reply an error; other output comes as JSON; the output of an `untrustedContent` tool comes with a note to treat it as data |
|
|
170
|
-
| `webmcp_get_invocation` | ✓ | ✗ | needs `--enable-webmcp` |
|
|
171
|
-
| `webmcp_list_invocations` | ✓ | ✗ | needs `--enable-webmcp` |
|
|
169
|
+
| `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); compact JSON `{frameId, tools}` (`frameId` once when the tools share it), without empty `required` or false annotations |
|
|
170
|
+
| `webmcp_invoke_tool` | ✓ | ✗ | needs `--enable-webmcp`; `frameId` optional when one frame registers the tool; waits `timeout_ms` (default 10 000, at most 60 000) for the answer, then says how to poll or cancel it; a tool answering in MCP shape (`{content, isError}`) gives its own text and image blocks, and its `isError` makes the reply an error; other output comes as JSON; the output of an `untrustedContent` tool comes with a note to treat it as data |
|
|
171
|
+
| `webmcp_get_invocation` | ✓ | ✗ | needs `--enable-webmcp`; a pending invocation whose page navigated away carries a `note` |
|
|
172
|
+
| `webmcp_list_invocations` | ✓ | ✗ | needs `--enable-webmcp`; `status` in any case |
|
|
173
|
+
| `webmcp_cancel_invocation` | ✓ | ✗ | needs `--enable-webmcp`; cancels a pending invocation (`WebMCP.cancelInvocation`); one Chrome no longer knows (its page is gone) is marked `Canceled` |
|
|
172
174
|
|
|
173
175
|
After `click_element`, `hover`, `drag`, `fill_input`, `fill_form` and
|
|
174
176
|
`press_key` the server waits for the page to settle before answering: until its DOM
|
|
@@ -180,7 +182,12 @@ after 134 ms.`, or says the page was still changing after 1 s. A click that
|
|
|
180
182
|
navigates waits for the new page's requests. Use `wait_for` for anything
|
|
181
183
|
slower (a timer, a long request). An action that opened tabs (a
|
|
182
184
|
`target=_blank` link, `window.open`) says so: `The action opened tab tab-3
|
|
183
|
-
(https://…): call switch_tab with its tab_id to act in it.`
|
|
185
|
+
(https://…): call switch_tab with its tab_id to act in it.` So does one
|
|
186
|
+
that downloaded a file (`The action downloaded data.csv (8 bytes) to …`),
|
|
187
|
+
opened a file chooser (`set its files with upload_file`; Chrome intercepts
|
|
188
|
+
file choosers, so none opens on screen) or changed the URL without loading
|
|
189
|
+
a page (`history.pushState`, a `#fragment`). In a textarea or
|
|
190
|
+
contenteditable, `fill_input` types a newline as the Enter key.
|
|
184
191
|
|
|
185
192
|
The JavaScript debugger tools on Firefox need its debugger (RDP) port: on by
|
|
186
193
|
default for a launched Firefox, `--firefox-rdp-port` for an attached one. The
|
|
@@ -396,7 +403,7 @@ claude mcp add browser-control-mcp -- npx -y @raultov/browser-control-mcp
|
|
|
396
403
|
pages (an error status, 400 and up, also adds a note; an error page with
|
|
397
404
|
an empty body is reported the same way on both browsers). A URL the
|
|
398
405
|
browser downloads fails with `Navigation failed: the URL is a
|
|
399
|
-
download
|
|
406
|
+
download...`; the file is still saved (see `list_downloads`). `open_tab` answers `{tab_id, target_id, url, label}` with the
|
|
400
407
|
final URL. A network error (unknown host, refused connection...) fails with
|
|
401
408
|
`Navigation failed: <browser's code> (<url>)`, e.g.
|
|
402
409
|
`net::ERR_NAME_NOT_RESOLVED` on Chrome and `NS_ERROR_UNKNOWN_HOST` on
|
|
@@ -470,6 +477,7 @@ claude mcp add browser-control-mcp -- npx -y @raultov/browser-control-mcp
|
|
|
470
477
|
| `--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 |
|
|
471
478
|
| `--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 |
|
|
472
479
|
| `--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 |
|
|
480
|
+
| `--downloads-dir <DIR>` | inside the instance's profile | each instance saves downloads in `<DIR>/<instance id>` (kept after the instance closes); without it, in a `downloads` folder of the instance's temporary profile (deleted with it), or a new folder in the system temp directory for an attached browser or `--user-profile` | same; snap Firefox can only write to non-hidden folders of `$HOME` (not `~/.local/...`): it cancels the download otherwise |
|
|
473
481
|
| `--enable-webmcp` | off | WebMCP switches and `webmcp_*` tools | capability error |
|
|
474
482
|
| `--proxy-server <URL>` | none | proxy for every instance; exposes `enable_proxy_auth` | `http://` and `socks5://` proxies |
|
|
475
483
|
| `--proxy-username <USER>` | none | answers proxy (407) challenges | same (untested) |
|
|
@@ -617,8 +625,9 @@ incompatible:
|
|
|
617
625
|
- Firefox WebSocket frames and page errors need the Firefox debugger port
|
|
618
626
|
(WebDriver BiDi has no WebSocket frame events); binary frames are reported
|
|
619
627
|
as Firefox gives them, not base64 as on Chrome.
|
|
620
|
-
- `webmcp_list_tools`, `webmcp_invoke_tool`, `webmcp_get_invocation
|
|
621
|
-
`webmcp_list_invocations` are Chrome only
|
|
628
|
+
- `webmcp_list_tools`, `webmcp_invoke_tool`, `webmcp_get_invocation`,
|
|
629
|
+
`webmcp_list_invocations` and `webmcp_cancel_invocation` are Chrome only
|
|
630
|
+
(a Chrome testing feature).
|
|
622
631
|
- `send_cdp_command` is Chrome only; `send_bidi_command` and
|
|
623
632
|
`send_rdp_request` are Firefox only.
|
|
624
633
|
- Cookie import (`--allow-cookie-import`) and launch presets (`features`) are
|
|
@@ -661,6 +670,10 @@ incompatible:
|
|
|
661
670
|
profile; the agent must ask you before using `copy_cookies`.
|
|
662
671
|
- `cookies` and `web_storage` hide values (often session tokens) unless
|
|
663
672
|
`show_values: true`; values never reach the logs.
|
|
673
|
+
- Downloads are written only to the instance's downloads directory (see
|
|
674
|
+
`--downloads-dir`): a suggested name keeps only its file name, an
|
|
675
|
+
instance label only letters, digits, `-` and `_`, and an existing file is
|
|
676
|
+
never replaced.
|
|
664
677
|
- `upload_file` hands the page a file's contents: it only reads files under
|
|
665
678
|
the upload roots (the server's working directory and the system temp
|
|
666
679
|
directory, or the `--upload-root` directories), resolved so `..` and
|
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.11.0-beta.
|
|
22
|
+
"version": "0.11.0-beta.19"
|
|
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.11.0-beta.
|
|
51
|
+
"version": "0.11.0-beta.19"
|
|
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.11.0-beta.
|
|
3
|
+
"https://github.com/raultov/browser-control-mcp/releases/download/v0.11.0-beta.19"
|
|
4
4
|
],
|
|
5
5
|
"author": "raultov <raultov@protonmail.com>",
|
|
6
6
|
"bin": {
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"zipExt": ".tar.xz"
|
|
93
93
|
}
|
|
94
94
|
},
|
|
95
|
-
"version": "0.11.0-beta.
|
|
95
|
+
"version": "0.11.0-beta.19",
|
|
96
96
|
"volta": {
|
|
97
97
|
"node": "18.14.1",
|
|
98
98
|
"npm": "9.5.0"
|