@raultov/browser-control-mcp 0.11.0-beta.16 → 0.11.0-beta.18

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,154 @@ 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.18] - 2026-11-03
14
+
15
+ Public **BETA**. Consistency fixes on both browsers. `webmcp_cancel_invocation`
16
+ cancels a pending WebMCP invocation; `webmcp_invoke_tool` waits 10 s by
17
+ default (`timeout_ms`), takes no `frameId` when one frame has the tool, and
18
+ `webmcp_list_tools` is compact. Tab tools behave the same on Chrome and
19
+ Firefox (`close_tab` defaults to the active tab, one rule for the next active
20
+ tab, `list_tabs` in tab order, Chrome registers its first tab after
21
+ `restart_browser`). Chrome `search_scripts` no longer counts or searches the
22
+ server's snippets or a previous page's scripts; `resume` and the other
23
+ debugger tools word "not paused" the same on both browsers. Chrome `emulate`
24
+ measures the page once a resize landed. Behaviour changes: `webmcp_list_tools`
25
+ answers `{frameId, tools}`; `webmcp_invoke_tool` waits 10 s instead of 30 s.
26
+
27
+ ### Added
28
+
29
+ - `webmcp_cancel_invocation` (`--enable-webmcp`, Chrome) cancels a pending
30
+ WebMCP invocation with `WebMCP.cancelInvocation`; one Chrome no longer
31
+ knows (its page navigated away) is marked `Canceled`. (#73)
32
+
33
+ ### Fixed
34
+
35
+ - Chrome: `emulate` measures what the page sees once a resize (or a reset)
36
+ landed: the next `resize` event or two animation frames, at most 500 ms.
37
+ Chrome answers the override before the renderer resizes, so the reply
38
+ could hold the previous size on a fresh instance. (#74)
39
+ - Debugger tools no longer leak internals (#66):
40
+ - Chrome: `search_scripts` neither counts nor searches the server's own
41
+ snippets nor the scripts of a page the tab left. Its script list was
42
+ never cleared on navigation: after a change of process Chrome reuses
43
+ script ids, so a stale entry named the server's outline snippet, and a
44
+ page with 5 scripts counted 38. The list is now cleared when the page's
45
+ contexts are, and scripts created over the protocol (no URL and no
46
+ stack) are left out; a page's own `eval` stays.
47
+ - `search_scripts` with `debug` answers JSON (`{scripts, errors}`), not
48
+ Rust's `Debug` format.
49
+ - Chrome `evaluate_on_call_frame` on a page that is not paused fails with
50
+ `the debugger is not paused`, as on Firefox and as `step_over`.
51
+ - `resume` on a page that is not paused answers `The debugger was not
52
+ paused: nothing to resume.` with `{paused: false}` on both browsers
53
+ (Firefox failed with `wrongState`, Chrome answered either way).
54
+ - Firefox RDP failures say `Firefox (RDP) protocol error in <request>`;
55
+ they were labelled BiDi.
56
+ - Tab tools behave the same on both browsers (#69):
57
+ - `close_tab` without `tab_id` closes the active tab, as every other tool
58
+ omitting it acts on it (it failed with `missing field \`tab_id\``).
59
+ - Closing the active tab activates the most recently active tab still
60
+ open, else the newest one (by number: `tab-10` after `tab-9`). Chrome
61
+ picked an arbitrary tab when none had been active, Firefox the highest
62
+ id as text.
63
+ - `list_tabs` lists the tabs by number on both browsers.
64
+ - After `restart_browser`, Chrome registers the new browser's first tab
65
+ (it listed none until the next call), as Firefox does.
66
+ - WebMCP tools are easier to drive (#73):
67
+ - `webmcp_invoke_tool` waits 10 s by default (it blocked 30 s), or
68
+ `timeout_ms` (at most 60 000); the pending reply says how to poll or
69
+ cancel the invocation. `frameId` is optional when one frame registers
70
+ the tool (a clear error names the frames otherwise).
71
+ - A pending invocation whose page navigated away carries a `note` in
72
+ `webmcp_get_invocation` and `webmcp_list_invocations` (it showed
73
+ `Pending` forever).
74
+ - `webmcp_list_invocations` takes `status` in any case.
75
+ - `webmcp_list_tools` is compact JSON: `{frameId, tools}` with the frame
76
+ once when shared, no empty `required` and no false annotations (on
77
+ knot.kz, 789 tokens instead of ~1700).
78
+
79
+ ## [0.11.0-beta.17] - 2026-11-02
80
+
81
+ Public **BETA**. Fixes found probing the debugger, tab and raw protocol tools
82
+ on both browsers. The browser's front tab now stays on the active tab (pages
83
+ in background tabs were hidden, with `requestAnimationFrame` stopped and
84
+ Chrome screenshots timing out); a click whose handler pauses or opens a
85
+ dialog after an `await` is noted at once; Chrome `snapshot`,
86
+ `find_in_snapshot` and `emulate` answer on a paused page; Firefox
87
+ `search_scripts` sees inline scripts and modules, `step_over` and `resume`
88
+ report a breakpoint hit again in a loop, and `get_custom_events` keeps every
89
+ subscribed event and RDP event packets; Chrome `navigate` to a hash route
90
+ reports the new URL; `webmcp_invoke_tool` passes a page tool's error and
91
+ blocks through. Behaviour changes: `open_tab` makes the new tab active.
92
+
93
+ ### Changed
94
+
95
+ - `open_tab` makes the new tab the active one (the tools that omit `tab_id`
96
+ act on it), as the browser shows it, Playwright MCP's `browser_tabs` and
97
+ chrome-devtools-mcp's `new_page` do. Before, the previous tab stayed
98
+ active while the browser showed the new one. (#68)
99
+
100
+ ### Fixed
101
+
102
+ - Firefox: `profile_page_performance` with `action: "reload"` and
103
+ `disable_cache: true` no longer fails with `Argument "ignoreCache" is
104
+ not supported yet`: it bypasses the cache with `network.setCacheBehavior`
105
+ (restored on every exit path). A reload Firefox aborts with
106
+ `NS_BINDING_ABORTED` right after navigating to a page that mounts frames
107
+ during hydration (as MDN does) is retried once after 50 ms instead of
108
+ failing. `forced_reflows.total_ms` no longer serializes `-0.0` when there
109
+ are none. (#58)
110
+ - Firefox: `search_scripts` finds every script of the page, not only the
111
+ first one Firefox announced: inline scripts, modules and scripts loaded
112
+ later were missing (`Total cached scripts: 1` on a page with five). Each
113
+ thread is now asked for its `sources`, merged with the announced ones;
114
+ pause locations and breakable columns use the same list. (#62)
115
+ - The browser's front tab is kept on the active tab. After a click that
116
+ opened a tab (`target=_blank`, a popup, `window.open`), after
117
+ `close_tab` and after a page or the user closed a tab, the active tab was
118
+ left in the background: its page was `hidden`, `requestAnimationFrame`
119
+ stopped (Chrome) or ran at about 1 s (Firefox), and on Chrome an element
120
+ screenshot hung 10 s before a raw CDP timeout. On Chrome,
121
+ `capture_screenshot` of a tab still in the background now fails at once,
122
+ naming `switch_tab`. On Chrome, a click whose reply came before the new
123
+ tab was registered no longer misses the "opened tab" note: it waits (at
124
+ most 2 s) for the pages Chrome announced. (#68)
125
+ - Chrome: `snapshot`, `find_in_snapshot` and `emulate` answer at once on a
126
+ page paused in the debugger, instead of failing after 10 s with
127
+ `CDP Error: Command Runtime.evaluate timed out after 10s`: their
128
+ synchronous snippets are no longer awaited (Chrome runs no microtask while
129
+ paused). `emulate` with `geolocation` on a paused page says the position
130
+ is read once the page resumes. (#64)
131
+ - An input action (`click_element`, `press_key`, `fill_form`, `drag`,
132
+ `hover`) whose handler pauses in the debugger or opens a dialog after an
133
+ async step (an `await`, a timer, a fetch) no longer waits 2 s and answers
134
+ a plain success: the watch of the page settling ends at once and the
135
+ reply notes `{paused, location}` or `{dialog}`, as for a handler that
136
+ blocks right away. Both browsers. (#65)
137
+ - Chrome: `navigate` to another fragment of the current page (a hash route
138
+ such as `#/contact`) answers the new URL; it often answered the previous
139
+ one, read from a history entry Chrome had not updated yet. The URL now
140
+ comes from the page's `Page.navigatedWithinDocument`. (#70)
141
+ - Firefox: `step_over` and `resume` that stop at the same place again (a
142
+ breakpoint in a loop, hit on the next iteration) report the pause with
143
+ its location, instead of waiting 2 s and answering `paused: false` while
144
+ the page was paused. (#63)
145
+ - `webmcp_invoke_tool` maps a page tool's MCP-shaped result
146
+ (`{content: [...], isError}`) to its own reply: the page's text and image
147
+ blocks as they are, and `isError: true` as a tool error. Before, a failed
148
+ page tool answered as a success with its error buried in the text, and
149
+ its output came as JSON escaped inside a string. (#72)
150
+ - Firefox: `get_custom_events` gets every event the agent subscribed to
151
+ with `send_bidi_command`, also those the server handles itself
152
+ (`browsingContext.load`, `contextCreated`, `log.entryAdded`,
153
+ `network.*`...), which were silently dropped. A tab opening or closing,
154
+ and an event without a context (`script.realmDestroyed`), go to the
155
+ active tab. The event packets an actor sends after a `send_rdp_request`
156
+ to it (`evaluationResult` after `evaluateJSAsync`, `pageError` after
157
+ `startListeners`) are kept too, as `rdp.<type>` (at most 16 actors
158
+ watched). Firefox itself sends `script.realmCreated` only for a new
159
+ tab's first document. (#71)
160
+
13
161
  ## [0.11.0-beta.16] - 2026-11-02
14
162
 
15
163
  Public **BETA**. `audit_page` (`perf` group) audits the rendered page for
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).** This version contains known bugs and differences
16
+ > **Status: BETA (0.11.0-beta.18).** 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,11 +117,11 @@ 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
- | `open_tab` | ✓ | ✓ | |
123
- | `close_tab` | ✓ | ✓ | |
124
- | `list_tabs` | ✓ | ✓ | |
122
+ | `open_tab` | ✓ | ✓ | the new tab becomes the active one, in front |
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
125
  | `switch_tab` | ✓ | ✓ | |
126
126
  | `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
127
  | `reload` | ✓ | ✓ | a pause, a dialog or HTTP authentication while loading fails at once, as with `navigate` |
@@ -132,8 +132,8 @@ see the note.
132
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
133
  | `find_in_snapshot` | ✓ | ✓ | `text` (case-insensitive) or `regex` over the whole outline; matching lines under their ancestors, at most 20 |
134
134
  | `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
- | `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 |
136
- | `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, covered (its own label on top does not count: styled checkboxes and switches are clicked through it; a covered element first gets the mouse, so controls shown only on hover are revealed and clicked) or a disabled control, whose handler would never run; an element a CSS selector does not find yet, or a disabled control, is checked again for up to 2 s, as pages add elements and enable buttons a moment after the previous action (a ref that names nothing, a hidden or a covered element fail at once); `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}`) |
135
+ | `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` |
136
+ | `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, covered (its own label on top does not count: styled checkboxes and switches are clicked through it; a covered element first gets the mouse, so controls shown only on hover are revealed and clicked) or a disabled control, whose handler would never run; an element a CSS selector does not find yet, or a disabled control, is checked again for up to 2 s, as pages add elements and enable buttons a moment after the previous action (a ref that names nothing, a hidden or a covered element fail at once); `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}`), also when its handler blocks the page after an `await` or a timer, while the page is watched to settle |
137
137
  | `fill_input` | ✓ | ✓ | picks `<select>` options (on a `<select multiple>`, exactly the ones named one per line; an unknown one changes nothing); sets `date`, `time`, `datetime-local`, `month`, `week`, `range` and `color` inputs to the text in their own format (`2026-10-31`, `13:45`, `72`, `#ff0000`), firing `input` and `change`, noting a value the input adjusted (a range snaps to its step) and failing, without changing it, on a malformed one; fails without typing on elements that cannot take the text; `append: true` types after the content instead of replacing it (a number or email field, which has no caret, is retyped whole); an editor of its own (an element with an `EditContext` or a focusable `textbox` role, or the hidden input of a code editor such as Monaco or Ace) gets the keys a user presses: a click on the editor when its input has no visible box, Control+A (Command+A on macOS) or Control+End, then the text, and the reply says its content could not be checked; in a contenteditable element (including rich text editors such as Slate, Lexical, ProseMirror or CodeMirror 6) it selects the content (a range, then Control+A as a user would) and types over it, so the editor records the edit; 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 |
138
138
  | `press_key` | ✓ | ✓ | one key (a character, Enter, Tab, Escape, arrows, F1–F12...) with Control / Shift / Alt / Meta held (in `modifiers`, or written before the key as `Control+a`, `Shift+Tab` or `ControlOrMeta+c`: Meta on macOS, Control elsewhere), in `selector` (focused first) or the focused element; a shortcut types nothing |
139
139
  | `hover` | ✓ | ✓ | moves the mouse over the element (scrolled into view; fails when hidden or covered) |
@@ -150,25 +150,26 @@ see the note.
150
150
  | `list_routes` | ✓ | ✓ | `network` group; the tab's routes with the `hits` each answered |
151
151
  | `remove_route` | ✓ | ✓ | `network` group; one route by id, or all |
152
152
  | `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 |
153
- | `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` |
153
+ | `get_custom_events` | ✓ | ✓ | `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` (also those the server handles, such as `browsingContext.load`; a tab opening or closing, or an event without a context, goes to the active tab) and the event packets of actors addressed with `send_rdp_request` (`rdp.<type>`, e.g. `rdp.evaluationResult`) |
154
154
  | `get_performance_metrics` | ✓ | partial | `perf` group; Chrome's metrics sorted by name, with the live `Documents`, `Nodes` and `JSEventListeners` of `Memory.getDOMCounters`; `collect_garbage: true` collects garbage first (`HeapProfiler.collectGarbage`), so reads after repeating an action show what it leaked; Firefox: `Nodes`, `Frames`, `Resources`, `TransferSize` from the Web Performance APIs (no Performance domain) and no garbage collection (WebDriver BiDi has none), plus notes |
155
155
  | `profile_page_performance` | ✓ | partial | `perf` group; `save_path` (Chrome only) writes the raw trace (`.json` or `.json.gz`, under the upload roots) for Chrome DevTools or Perfetto; `insights: true` adds what explains the numbers, read with the Web Performance APIs and the DOM (same JavaScript on both): the document's TTFB, server time and compression, the LCP element, its breakdown (TTFB, load delay, load, render delay) and image discovery (`lazy_loaded`, `fetch_priority`), Interaction to Next Paint (`inp`: duration, event, target and input/processing/presentation breakdown), layout-shift culprits, render-blocking requests, the slowest requests, the scripts of long animation frames, DOM size (`nodes`, `max_depth`, `widest_element`, `max_children`), oversized or unsized images, and third-party origins by duration and bytes (at most 5 each), plus `findings` from the web.dev and Lighthouse thresholds; on Chrome, `forced_reflows` from the trace (`Layout`/`UpdateLayoutTree` forced by a script, grouped by call site, a finding over 30 ms) and, with `action: "reload"`, `unused_code` (`unused_js_bytes`, `unused_css_bytes` and the 5 files with most unused bytes, from the `Profiler` precise coverage and `CSS` rule usage of the reload; a finding over 50 KB; without a reload it is `null`: code run before the recording would count as unused); Firefox: FCP, LCP, DCL and Load; no trace (`save_path` fails), so `total_blocking_time_ms` and `top_long_tasks` are `null`, and its insights have no layout shifts, render-blocking requests, long frames, forced reflows or unused code (`null`, with notes) |
156
156
  | `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
157
  | `set_breakpoint` | ✓ | partial | Firefox: by URL only (no `script_id` / `script_hash`) |
158
158
  | `remove_breakpoint` | ✓ | ✓ | |
159
- | `resume` | ✓ | ✓ | |
159
+ | `resume` | ✓ | ✓ | on a page that is not paused: `The debugger was not paused: nothing to resume.` with `{paused: false}` |
160
160
  | `step_over` | ✓ | ✓ | |
161
161
  | `evaluate_on_call_frame` | ✓ | ✓ | |
162
- | `search_scripts` | ✓ | ✓ | a long line's preview is 100 characters on each side of the match; frames of another origin too (Chrome ids such as `2.17`) |
162
+ | `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; `debug` answers `{scripts, errors}` as JSON; frames of another origin too (Chrome ids such as `2.17`) |
163
163
  | `pause_on_load` | ✓ | ✗ | TODO on Firefox, see [Limitations and TODO](#limitations-and-todo) |
164
164
  | `enable_proxy_auth` | ✓ | partial | needs `--proxy-server`; a request the proxy rejects twice is cancelled; untested on Firefox against a real proxy |
165
165
  | `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
166
  | `send_bidi_command` | ✗ | ✓ | `raw` group; raw WebDriver BiDi; refuses `session.new`, `session.end`, `browser.close` |
167
- | `send_rdp_request` | ✗ | ✓ | `raw` group; raw Firefox RDP request to an actor (`thread`, `console`, `root` shortcuts) |
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`; 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` |
167
+ | `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); compact JSON `{frameId, tools}` (`frameId` once when the tools share it), without empty `required` or false annotations |
169
+ | `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 |
170
+ | `webmcp_get_invocation` | ✓ | ✗ | needs `--enable-webmcp`; a pending invocation whose page navigated away carries a `note` |
171
+ | `webmcp_list_invocations` | ✓ | ✗ | needs `--enable-webmcp`; `status` in any case |
172
+ | `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
173
 
173
174
  After `click_element`, `hover`, `drag`, `fill_input`, `fill_form` and
174
175
  `press_key` the server waits for the page to settle before answering: until its DOM
@@ -379,7 +380,12 @@ claude mcp add browser-control-mcp -- npx -y @raultov/browser-control-mcp
379
380
  lazily, on the first tool that needs them, with an ephemeral profile.
380
381
  - **Tabs.** `open_tab`, `list_tabs`, `switch_tab`, `close_tab`. Every tool
381
382
  takes `instance_id` and `tab_id`; without them it targets the default
382
- instance and its active tab. An argument a tool does not have fails the
383
+ instance and its active tab. The active tab is the one the browser shows:
384
+ `open_tab` makes its new tab active, `close_tab` brings the next one to
385
+ the front, and after a page opens or closes a tab (a `target=_blank`
386
+ link, a popup, `window.close()`) the active tab is brought back to the
387
+ front. Background tabs are hidden to their pages (no
388
+ `requestAnimationFrame`, throttled timers). An argument a tool does not have fails the
383
389
  call before anything runs (a `tabId` typo answers "did you mean
384
390
  `tab_id`?"), so a typo never falls back to the active tab.
385
391
  - **Same replies on both browsers.** `navigate` and `reload` answer once the
@@ -513,7 +519,7 @@ one subscription per session, routed by browsing context id.
513
519
 
514
520
  Chrome and Firefox take the same calls:
515
521
 
516
- 1. `navigate` to the page (or `open_tab` + `switch_tab`).
522
+ 1. `navigate` to the page (or `open_tab`).
517
523
  2. `search_scripts` with a needle to find the script URL and line.
518
524
  3. `set_breakpoint` with `url` and `line_number` (0-based).
519
525
  4. Trigger the code (`click_element`, `evaluate_js` with a `setTimeout`, a
@@ -601,6 +607,10 @@ incompatible:
601
607
  - **TODO** `rust-mcp-sdk` 2.0: the server stays on 1.1.0 (MCP protocol
602
608
  2025-11-25). 2.0 moves to the stateless MCP protocol (2026-07-28), which
603
609
  today's MCP clients probably do not speak yet; upgrade once the clients do.
610
+ - On Firefox, a reload with `disable_cache: true` bypasses the cache with
611
+ `network.setCacheBehavior` (Firefox rejects `ignoreCache` on
612
+ `browsingContext.reload`); a reload Firefox aborts with
613
+ `NS_BINDING_ABORTED` is retried once after 50 ms.
604
614
  - `get_performance_metrics` and `profile_page_performance` are partial on
605
615
  Firefox: it has no Performance or Tracing domain, so they read what the
606
616
  Web Performance APIs give and say what is missing (JS heap, layout and
@@ -608,8 +618,9 @@ incompatible:
608
618
  - Firefox WebSocket frames and page errors need the Firefox debugger port
609
619
  (WebDriver BiDi has no WebSocket frame events); binary frames are reported
610
620
  as Firefox gives them, not base64 as on Chrome.
611
- - `webmcp_list_tools`, `webmcp_invoke_tool`, `webmcp_get_invocation` and
612
- `webmcp_list_invocations` are Chrome only (a Chrome testing feature).
621
+ - `webmcp_list_tools`, `webmcp_invoke_tool`, `webmcp_get_invocation`,
622
+ `webmcp_list_invocations` and `webmcp_cancel_invocation` are Chrome only
623
+ (a Chrome testing feature).
613
624
  - `send_cdp_command` is Chrome only; `send_bidi_command` and
614
625
  `send_rdp_request` are Firefox only.
615
626
  - Cookie import (`--allow-cookie-import`) and launch presets (`features`) are
@@ -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.16"
22
+ "version": "0.11.0-beta.18"
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.16"
51
+ "version": "0.11.0-beta.18"
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.16"
3
+ "https://github.com/raultov/browser-control-mcp/releases/download/v0.11.0-beta.18"
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.16",
95
+ "version": "0.11.0-beta.18",
96
96
  "volta": {
97
97
  "node": "18.14.1",
98
98
  "npm": "9.5.0"