@raultov/browser-control-mcp 0.1.1-beta.1 → 0.2.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,90 @@ from a copy of its code: entries describe the differences from that release.
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [0.2.0-beta.1] - 2026-10-13
14
+
15
+ Public **BETA**. Chrome and Firefox now answer the same replies; several are
16
+ incompatible with 0.1.x (marked **Breaking**; see the README's "Migration
17
+ from 0.1.x to 0.2.0").
18
+
19
+ ### Changed
20
+
21
+ - **Breaking:** `navigate` and `reload` answer the same shape on Chrome and
22
+ Firefox once the page fired `load` (at most 30 s): `{url, tab_id, browser,
23
+ notes?}`, `url` being the final URL after redirects and `browser` the
24
+ protocol's ids (Chrome used to answer the raw `Page.navigate` reply without
25
+ waiting, and `{}` on reload). `open_tab` reports the final URL too, and on
26
+ Firefox it now creates the tab blank and navigates once it is registered,
27
+ as on Chrome, so the page's first events are captured.
28
+ - **Breaking:** a network error fails `navigate` and `open_tab` on both
29
+ browsers with `Navigation failed: <browser's code> (<url>)` (Chrome used to
30
+ report success with `errorText` inside, or a tab on `chrome-error://`); from
31
+ `open_tab` the error names the tab, which stays open.
32
+ - **Breaking:** `evaluate_js` answers `{value, type, subtype?, description?}`
33
+ on both browsers (Chrome used to wrap the value in the CDP `RemoteObject`,
34
+ Firefox to return the bare value). Values without a JSON form (functions,
35
+ DOM nodes, symbols, `NaN`, bigints, maps...) have `value: null` and a
36
+ `description`; on Chrome they no longer fail with "Object couldn't be
37
+ returned by value" or answer `{}` (array-likes such as `NodeList` are
38
+ described, e.g. `NodeList(6)`, as on Firefox). A thrown exception fails
39
+ with the same wording on both: what was thrown plus its 1-based line and
40
+ column.
41
+ - **Breaking:** the debugger tools answer the same shapes on both browsers,
42
+ with 0-based lines everywhere: `set_breakpoint` returns an MCP id
43
+ (`bp-N`, also on Chrome instead of the CDP id) with the bound location and
44
+ `resolved`; `remove_breakpoint` takes that id and answers `{removed}`;
45
+ `step_over` and `resume` answer `{paused, location}` (Firefox's step
46
+ location was 1-based; Chrome reported none); `evaluate_on_call_frame`
47
+ answers the `evaluate_js` shape, and an exception in the frame is an error
48
+ on Chrome too (`Evaluation threw: ...`; it used to be a success carrying
49
+ `exceptionDetails`). `step_over` without a pause fails with "the debugger
50
+ is not paused" on both.
51
+ - **Breaking:** `get_console_logs` answers `{messages, total, truncated,
52
+ notes?}` instead of a bare array, returns at most `limit` messages (new
53
+ argument, default 200, the most recent ones), and `clear` removes only the
54
+ returned messages (it used to empty the whole buffer, even messages a
55
+ `level_filter` had hidden).
56
+
57
+ ### Added
58
+
59
+ - Partial `get_performance_metrics` and `profile_page_performance` on
60
+ Firefox, from the standard Web Performance APIs: `Nodes`, `Frames`,
61
+ `Resources` and `TransferSize`; FCP, LCP, DCL and Load (optionally after a
62
+ reload, cache bypassed with `disable_cache`). What Firefox cannot give (JS
63
+ heap, layout counts, long tasks, total blocking time) is `null` or listed
64
+ in a note, never a silent empty value.
65
+ - Firefox WebSocket frames in `get_network_logs`, and the console messages
66
+ WebDriver BiDi does not report (script errors with file and line, cookie
67
+ and security warnings, CSS warnings, platform messages) in
68
+ `get_console_logs`, from the RDP watcher of each tab's debugger, in the
69
+ same shapes as Chrome. Each tab's debugger is attached when the session
70
+ starts, when `open_tab` creates the tab, or at the first log call on a tab
71
+ the page opened, matched by its position in BiDi's and RDP's tab lists
72
+ (checked against their URLs), without bringing it to the front. Needs the
73
+ Firefox debugger port (default for a launched Firefox). Messages both feeds report are kept once; console
74
+ `source` labels say what a Firefox message is (`javascript`, `css`,
75
+ `security`, `network`, `other`).
76
+ - `--window-size <W>x<H>` (default `1920x1080`): launched Chrome and Firefox
77
+ get the same window size, headless too (they used to pick 921x648 and
78
+ 1280x887). Attached browsers keep their window.
79
+ - A tab's console buffer is cleared when the tab moves to another site
80
+ (registrable domain, via the `psl` crate's embedded Public Suffix List)
81
+ and kept across subdomains of one site, reloads and same-page
82
+ navigations; the next `get_console_logs` says what was cleared.
83
+
84
+ - Lifecycle crates bumped: `cdp-browser-lite` 0.4.2 and `bidi-browser-lite`
85
+ 0.1.4 (`bidi-lite` 0.1.2). Their mocks now record wire parameters, so the
86
+ Firefox page, input, network and debugger tools and the Chrome domain
87
+ setup are covered by hermetic tests instead of only by the E2E smoke.
88
+
89
+ ### Fixed
90
+
91
+ - Chrome `get_performance_metrics` returned `{}`: every tab now enables the
92
+ Performance domain when it is registered, the tool enables it on a session
93
+ that missed it, and an empty metric list is an error instead of `{}`.
94
+ - The "Chrome not found, use Firefox?" hint no longer ends the library's
95
+ sentence with two periods.
96
+
13
97
  ## [0.1.1-beta.1] - 2026-10-12
14
98
 
15
99
  Public **BETA**: known bugs and Chrome/Firefox differences are listed in the
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.1.1-beta.1).** This version contains known bugs and differences
14
+ > **Status: BETA (0.2.0-beta.1).** 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
@@ -54,11 +54,11 @@ see the note.
54
54
  | `click_element` | ✓ | ✓ | |
55
55
  | `fill_input` | ✓ | ✓ | |
56
56
  | `evaluate_js` | ✓ | ✓ | |
57
- | `get_console_logs` | ✓ | ✓ | |
58
- | `get_network_logs` | ✓ | partial | Firefox: HTTP requests and response bodies, no WebSocket frames |
57
+ | `get_console_logs` | ✓ | ✓ | Firefox: page errors, CSS warnings and platform messages need the debugger port |
58
+ | `get_network_logs` | ✓ | ✓ | Firefox: WebSocket frames need the debugger port |
59
59
  | `get_custom_events` | ✓ | partial | Firefox: events of the modules subscribed with `send_bidi_command` |
60
- | `get_performance_metrics` | ✓ | ✗ | Firefox has no equivalent of CDP's Performance domain |
61
- | `profile_page_performance` | ✓ | ✗ | Firefox has no equivalent of CDP's Tracing domain |
60
+ | `get_performance_metrics` | ✓ | partial | Firefox: `Nodes`, `Frames`, `Resources`, `TransferSize` from the Web Performance APIs (no Performance domain), plus a note |
61
+ | `profile_page_performance` | ✓ | partial | Firefox: FCP, LCP, DCL and Load from the Web Performance APIs; no trace, so `total_blocking_time_ms` and `top_long_tasks` are `null` |
62
62
  | `set_breakpoint` | ✓ | partial | Firefox: by URL only (no `script_id` / `script_hash`) |
63
63
  | `remove_breakpoint` | ✓ | ✓ | |
64
64
  | `resume` | ✓ | ✓ | |
@@ -76,7 +76,18 @@ see the note.
76
76
  | `webmcp_list_invocations` | ✓ | ✗ | needs `--enable-webmcp` |
77
77
 
78
78
  The JavaScript debugger tools on Firefox need its debugger (RDP) port: on by
79
- default for a launched Firefox, `--firefox-rdp-port` for an attached one.
79
+ default for a launched Firefox, `--firefox-rdp-port` for an attached one. The
80
+ same port gives Firefox's WebSocket frames and the console messages BiDi does
81
+ not report (script errors with their file and line, cookie and security
82
+ warnings, CSS warnings, platform messages), in the same shapes as Chrome.
83
+ Each tab's debugger watches them: the session's first tabs are attached when
84
+ the session starts and `open_tab` attaches its tab before loading the URL; a
85
+ tab the page opened is attached by the first `get_console_logs` or
86
+ `get_network_logs` on it. Tabs are matched by their position in BiDi's and
87
+ RDP's tab lists (checked against their URLs), never by activating them; a
88
+ tab that cannot be matched that way (tabs opening or closing at that moment)
89
+ is attached by its first debugger tool, which brings it to the front. Frames
90
+ are captured from the moment a tab is attached.
80
91
 
81
92
  ## 📦 Installation
82
93
 
@@ -262,8 +273,34 @@ claude mcp add browser-control-mcp -- npx -y @raultov/browser-control-mcp
262
273
  - **Tabs.** `open_tab`, `list_tabs`, `switch_tab`, `close_tab`. Every tool
263
274
  takes `instance_id` and `tab_id`; without them it targets the default
264
275
  instance and its active tab.
276
+ - **Same replies on both browsers.** `navigate` and `reload` answer once the
277
+ page fired `load` (at most 30 s, else with a note): a first line
278
+ (`Navigated to <final URL>.` / `Page reloaded.`), then
279
+ `{"url": "<final URL, after redirects>", "tab_id": "tab-N", "browser": {...}}`,
280
+ where `browser` holds the protocol's ids (CDP `frameId`/`loaderId`, BiDi
281
+ `navigation`). `open_tab` answers `{tab_id, target_id, url, label}` with the
282
+ final URL. A network error (unknown host, refused connection...) fails with
283
+ `Navigation failed: <browser's code> (<url>)`, e.g.
284
+ `net::ERR_NAME_NOT_RESOLVED` on Chrome and `NS_ERROR_UNKNOWN_HOST` on
285
+ Firefox; from `open_tab` the error names the tab, which stays open.
286
+ `evaluate_js` answers `{"value", "type", "subtype"?, "description"?}`:
287
+ `type` is JavaScript's `typeof`, `subtype` refines objects (`array`,
288
+ `null`, `node`, `date`, `map`...), `value` is the JSON form and is `null`
289
+ when there is none (functions, DOM nodes, symbols, `NaN`, bigints,
290
+ maps...), `description` then says what it was (Chrome describes functions
291
+ by their source, Firefox only as `function`). A thrown exception or a
292
+ rejected promise fails with what was thrown and its 1-based line and
293
+ column, e.g. `ReferenceError: x is not defined (line 1, column 1)`.
265
294
  - **Per-tab state.** Console logs, network traffic, breakpoints and custom
266
- events are kept per tab, in bounded buffers.
295
+ events are kept per tab, in bounded buffers. A tab's console buffer (last
296
+ 1000 messages) is cleared when the tab moves to another site (registrable
297
+ domain from the Public Suffix List: `www.knot.kz` and `docs.knot.kz` are
298
+ one site, `a.github.io` and `b.github.io` two) and kept across reloads and
299
+ same-page navigations. `get_console_logs` answers
300
+ `{"messages": [...], "total", "truncated", "notes"?}`: at most `limit`
301
+ (default 200) of the most recent matching messages, oldest first; `clear`
302
+ removes only the returned ones; a note says when a site change cleared
303
+ messages.
267
304
  - **Firefox allows a single automation session.** Each Firefox instance holds
268
305
  one WebDriver BiDi session shared by its tabs; `close_instance`,
269
306
  `stop_browser` and every server shutdown path end it, so the next run can
@@ -276,6 +313,7 @@ claude mcp add browser-control-mcp -- npx -y @raultov/browser-control-mcp
276
313
  | `--browser <chrome\|firefox>` | `chrome` | browser of the default instance | browser of the default instance |
277
314
  | `--local` | off | navigation limited to local addresses | same |
278
315
  | `--headless` | off | headless Chrome | headless Firefox |
316
+ | `--window-size <W>x<H>` | `1920x1080` | window of launched Chrome (`--window-size`) | window of launched Firefox (`--width`/`--height`) |
279
317
  | `--host <HOST>` | `127.0.0.1` | debugging host | debugging host |
280
318
  | `--port <PORT>` | `9222` | debugging port of the default instance | ignored (use `--firefox-port`) |
281
319
  | `--max-instances <N>` | `8` | instance cap | shared with Chrome |
@@ -334,6 +372,17 @@ Chrome and Firefox take the same calls:
334
372
  moves one statement; `resume` continues.
335
373
  6. `remove_breakpoint` with the id `set_breakpoint` returned.
336
374
 
375
+ Both browsers answer the same replies, with 0-based lines everywhere:
376
+
377
+ | Tool | Reply |
378
+ | --- | --- |
379
+ | `set_breakpoint` | `{"breakpoint_id": "bp-N", "url", "line_number", "column_number", "resolved"}`: where the browser bound it; `resolved` is false until a script with that URL loads |
380
+ | `remove_breakpoint` | `{"removed": "bp-N"}`; an unknown id fails listing the tab's ids |
381
+ | `step_over`, `resume` | `{"paused", "location": {"url", "line_number", "column_number"} \| null}`: where the thread stopped next (`step_over` waits up to 2 s; `resume` reports a pause only if one follows within 250 ms) |
382
+ | `evaluate_on_call_frame` | the `evaluate_js` shape; an exception fails with `Evaluation threw: <what was thrown>` |
383
+
384
+ Breakpoint ids are per tab and MCP-level (`bp-1`, `bp-2`...), like tab ids.
385
+
337
386
  Chrome also offers `pause_on_load` (pause on the first statement of the next
338
387
  load). Firefox notes: breakpoints by URL only; the column is snapped to the
339
388
  nearest breakable position automatically; the debugger needs the RDP port.
@@ -356,47 +405,39 @@ In client configurations replace the command `chrome-debug-mcp` with
356
405
  `browser-control-mcp` (or the npm package name). The old tool names answer an
357
406
  error that names the new ones.
358
407
 
408
+ ### Migration from 0.1.x to 0.2.0
409
+
410
+ 0.2.0 makes the replies the same on Chrome and Firefox; these changes are
411
+ incompatible:
412
+
413
+ | Tool | 0.1.x | 0.2.0 |
414
+ | --- | --- | --- |
415
+ | `navigate`, `reload` | Chrome: raw `Page.navigate` reply (`frameId`, `loaderId`, `errorText`) without waiting, `{}` on reload; Firefox: `{navigation, url}` | `{url, tab_id, browser, notes?}` after `load`; `url` is the final URL; protocol ids under `browser` |
416
+ | `navigate`, `open_tab` to an unreachable host | Chrome: success (`errorText` inside, or a `chrome-error://` tab) | error `Navigation failed: <code> (<url>)`; `open_tab` names the tab, which stays open |
417
+ | `evaluate_js` | Chrome: `{"result": {type, value, ...}}`; Firefox: the bare value | `{value, type, subtype?, description?}` |
418
+ | `set_breakpoint` | Chrome: CDP id (`1:3:0:http://…`) and `locations`; Firefox: `{breakpointId, url, lineNumber, columnNumber}` | `{breakpoint_id: "bp-N", url, line_number, column_number, resolved}` |
419
+ | `remove_breakpoint` | Chrome: CDP id, reply `{}` | `bp-N` id, reply `{removed}` |
420
+ | `step_over`, `resume` | Chrome: text only; Firefox `step_over`: `{paused, location: {line (1-based), column}}` | `{paused, location: {url, line_number, column_number} \| null}`, 0-based |
421
+ | `evaluate_on_call_frame` | Chrome: raw `RemoteObject` (exceptions as success); Firefox: bare value | the `evaluate_js` shape; exceptions are errors (`Evaluation threw: ...`) |
422
+ | `get_console_logs` | a JSON array; `clear` emptied the whole buffer | `{messages, total, truncated, notes?}`, at most `limit` (200) messages; `clear` removes the returned ones; the buffer is cleared on cross-site navigation |
423
+
359
424
  ## Limitations and TODO
360
425
 
361
- - **TODO (priority)** Unified `navigate` response: Chrome returns the CDP ids
362
- (`frameId`, `loaderId`) and Firefox `{navigation, url}`; both will return
363
- the final URL in the same shape.
364
- - **TODO (priority)** Unified `evaluate_js` result: Chrome wraps the value in
365
- the CDP `RemoteObject` (`{"result": {"type", "value"}}`) and Firefox
366
- returns the bare value; both will return the same shape.
367
- - **TODO (priority)** Navigation errors: on Chrome, `navigate` and `open_tab`
368
- to an unreachable host report success (the network error is only inside
369
- the reply, or absent); both browsers will fail with the network error.
370
- - **TODO (priority)** Console buffer per navigation: `get_console_logs` will
371
- clear a tab's buffer when it navigates to a different site (kept across
372
- subdomains of the same site) and gain a limit, so one call cannot flood
373
- the client.
374
- - **TODO (priority)** Unified debugger replies: `set_breakpoint`,
375
- `remove_breakpoint` and `step_over` answer differently on Chrome and
376
- Firefox (and Firefox's `step_over` location uses a 1-based line); both
377
- will return the same shape with 0-based lines.
378
- - **TODO (priority)** Known bug: on Chrome `get_performance_metrics` returns
379
- `{}` because the Performance domain is never enabled. Workaround:
380
- `send_cdp_command` with `Performance.enable` first.
381
- - **TODO (priority)** Partial performance on Firefox from the standard Web
382
- Performance APIs (navigation timings, FCP, LCP), stating what Firefox
383
- cannot give (long tasks, CLS, JS heap).
384
- - **TODO (priority)** Same default window size on both browsers (today
385
- Chrome 921x648, Firefox 1280x887): the screen size when known, else
386
- 1920x1080.
387
- - **TODO (priority)** Firefox WebSocket frames and the full console natively
388
- over the Firefox debugger (RDP watcher `websocket`, `error-message` and
389
- `css-message` resources), so both browsers return the same shape without
390
- any page injection.
426
+ - **TODO** Default window size from the screen size: launched browsers use
427
+ `--window-size` (default 1920x1080 on both); reading the screen size needs
428
+ native APIs per OS, left out of the server.
391
429
  - **TODO** `pause_on_load` on Firefox: neither a preload `debugger;`
392
430
  statement nor an RDP interrupt pauses a reloading page (spike verdict).
393
431
  - **TODO** `rust-mcp-sdk` 2.0: the server stays on 1.1.0 (MCP protocol
394
432
  2025-11-25). 2.0 moves to the stateless MCP protocol (2026-07-28), which
395
433
  today's MCP clients probably do not speak yet; upgrade once the clients do.
396
- - `get_performance_metrics` and `profile_page_performance` are Chrome only:
397
- Firefox has no Performance or Tracing domain.
398
- - WebSocket frames in `get_network_logs` are Chrome only: WebDriver BiDi has
399
- no WebSocket frame events.
434
+ - `get_performance_metrics` and `profile_page_performance` are partial on
435
+ Firefox: it has no Performance or Tracing domain, so they read what the
436
+ Web Performance APIs give and say what is missing (JS heap, layout and
437
+ style counts, long tasks, total blocking time).
438
+ - Firefox WebSocket frames and page errors need the Firefox debugger port
439
+ (WebDriver BiDi has no WebSocket frame events); binary frames are reported
440
+ as Firefox gives them, not base64 as on Chrome.
400
441
  - `webmcp_list_tools`, `webmcp_invoke_tool`, `webmcp_get_invocation` and
401
442
  `webmcp_list_invocations` are Chrome only (a Chrome testing feature).
402
443
  - `send_cdp_command` is Chrome only; `send_bidi_command` and
@@ -410,9 +451,12 @@ error that names the new ones.
410
451
  - Firefox: after a lost connection a launched Firefox gets a new session on
411
452
  the next tool call; an attached Firefox keeps its orphan session, so
412
453
  restart that Firefox.
413
- - Firefox debugger: it attaches to the tab Firefox has selected right after
414
- activating the requested one; switching tabs at that very moment can bind
415
- it to the wrong page.
454
+ - Firefox debugger: a tab is matched to its debugger by its position in
455
+ BiDi's and RDP's tab lists, checked against the tabs' web URLs; tabs
456
+ showing no web page (`about:blank`, error pages) cannot be checked, so a
457
+ tab opening or closing elsewhere at that very moment could still bind the
458
+ debugger to the wrong page. When the lists differ the debugger attaches by
459
+ activating the tab, as before.
416
460
  - `get_network_logs` with response bodies has no total size limit (each
417
461
  Firefox body is capped at 2 MB): use `url_filter` on busy pages.
418
462
  - The Docker image is amd64 only.
@@ -19,7 +19,7 @@
19
19
  "hasInstallScript": true,
20
20
  "license": "MIT",
21
21
  "name": "@raultov/browser-control-mcp",
22
- "version": "0.1.1-beta.1"
22
+ "version": "0.2.0-beta.1"
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.1.1-beta.1"
51
+ "version": "0.2.0-beta.1"
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.1.1-beta.1"
3
+ "https://github.com/raultov/browser-control-mcp/releases/download/v0.2.0-beta.1"
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.1.1-beta.1",
95
+ "version": "0.2.0-beta.1",
96
96
  "volta": {
97
97
  "node": "18.14.1",
98
98
  "npm": "9.5.0"