Skip to content

Browser MCP 1.30.1

Choose a tag to compare

@github-actions github-actions released this 07 Oct 09:54
· 349 commits to main since this release
a03a7b5

Install: npx @agent360/browser-mcp install - or load the attached zip unpacked in chrome://extensions.

Pairing is withdrawn, and a new install could get a bridge that never connected. This release
is those two, and the faults found on the way.

Pairing (1.30.0) is taken back. 1.30.0 shipped an optional pairing key so one Chrome profile
takes commands from one server. It did not work - and when we fixed that, a review measured that
it still kept nobody out:

  • The extension sent its key in the handshake to any server listening on a port in its range, and
    accepted the same key back as proof. A program on your machine could pair itself by repeating it.
  • With only one profile paired, that profile could lock onto the other profile's server and refuse
    every call it made.
  • The 1.30.0 extension could never read its own key (an offscreen document only has
    chrome.runtime), so setting BROWSER_MCP_TOKEN - as the popup told you to - locked you out.

A safety feature that keeps nobody out is worse than none. It is gone from the popup, the extension
sends and requires no key (also not one 1.30.0 stored), and the server ignores BROWSER_MCP_TOKEN
and says so when it starts. It will come back redesigned. The bridge is local and
unauthenticated:
any program running as you on the same machine can connect to it, as before 1.30.0.

Kept from that work, because it holds on its own: a connection can no longer answer a command it
was never sent. Replies were matched on the command id alone, and ids count from 1, so any socket
could guess one and return a forged result. Each pending command is now bound to the connection
it went to.

A new install could get a half-dead bridge, "Not connected" for good. Three callers could
build the bridge at the same time, and on a fresh install the install event closed it while
another caller was still building it. The bridge then existed, reported the right ports, and
every call to the server hung. It hit exactly the person installing today; existing installs
never saw it. The bridge is now built one at a time, and a fresh install no longer closes it.
"Reconnect" and extension updates close and rebuild it in the same queue, so they cannot close a
bridge halfway through loading. Known limit: if Chrome ever leaves a build hanging, both the
heartbeat and "Reconnect" wait behind it, and recovery is not guaranteed - an attached debugger
keeps the extension's background worker alive. Reloading the extension in chrome://extensions
clears it.

Four characters could stop the server. The text null is valid JSON; the next line read
msg.type, and the process died. Any program on the machine could send it without saying hello.
Only objects are treated as messages now, on both sides of the bridge.

One hanging probe could stop the extension finding servers until Chrome restarted. The
scan lock was released in finally, which never runs if a probe never settles - and Chrome can
freeze an offscreen document mid-probe. The lock is now released after 15 seconds (counted from
when the document runs again, if Chrome froze it), and a port with a call still in the air is not
probed again, so the valve cannot stack connections until Chrome's per-host limit is used up. Known
limit: a port whose old call never settles stays skipped until that call ends.

eget_vindue can place its window. vindue_x, vindue_y, vindue_bredde and
vindue_hoejde position the new window, and fokuser asks Chrome to give it focus. Chrome often
refuses. When it does give focus, it is exclusive: whatever the person types goes to that window,
even on another display - use it only on a machine nobody is typing on. A position that is not a
number now refuses to open the window instead of letting Chrome put it in front of you (text such
as "-1920" is read as a number), and focus without a position warns.

eget_vindue reported what it was asked for. It answered fokuseret: true when Chrome had
refused focus, and gave back the coordinates we requested rather than where the window landed.
It is the one feature whose job is to keep a test run off the user's screen, so the echo read as
permission to continue. The reply now carries the measured position, what was asked for, whether
they match, and a warning when they do not.

Also: a controlled <select> goes through the prototype's value setter, as five other paths
in the same file already did. See the retraction on /learn/tools-that-lie - this was a
precaution, not a fix for a bug we could reproduce in React.

Node: the package now asks for Node 20 or newer - the versions it is tested on (20, 22, 24).
It said 18, which was never tested.

Credited: @DorianChn moved the two select suites onto one shared page model (#28), so a
change to what the extension reads from the page lands in both tests instead of one.