v0.2.0
BrowserBuddy 0.2.0 inverts the transport. The browser now spawns a native-messaging host that owns a token-protected loopback MCP endpoint, replacing the unauthenticated WebSocket hub of 0.1. The agent's endpoint stays stable across browser service-worker respawns, observation history survives restarts, and a guided connect flow (browserbuddy install-host + browserbuddy client-config) wires Claude Code to your real Chrome or Firefox.
Context
The transport inversion is first of all a security fix. In 0.1 the server
listened on ws://127.0.0.1:8590 with no authentication, so any process running
as the same OS user -- any npm postinstall script, any other agent, anything
that could reach loopback -- could drive the logged-in browser. 0.2 closes that:
the browser spawns the host over native messaging, the host binds an ephemeral
loopback port and mints a bearer token, and the trust boundary is the OS user
rather than "whoever gets to the port first."
It is also a lifetime inversion. The always-on side -- the browser -- now owns
the listening endpoint, and the session-scoped side -- the agent -- dials in.
That is what makes the endpoint stable across service-worker respawns and what
lets observation history survive a restart, neither of which the old
short-lived-server-owns-the-socket arrangement could offer.
Also here: a red-green fix to actor attribution, where a slow cross-origin load
could outlive the agent-attribution window and come back tagged as the user's,
polluting observation, poisoning demonstrations, and potentially waking a
browser_wait_for_user the agent had started itself.
The standalone-product half of the inversion is deliberately deferred to a
later release: moving semantics into the extension, a consent GUI, a
deterministic replay engine, and store distribution are not in 0.2.0.
Breaking
- Setup instructions now describe the transport the extension actually uses. The README walks the native-messaging flow end to end (install dependencies,
install-host, load the extension,client-config), replacing the old stdio registration; the WebSocket carrier is documented as the explicitly-selected alternative.
Features
- Native-messaging transport. The extension can now have the browser spawn a native-messaging host that serves MCP over a loopback HTTP endpoint with a per-launch bearer token, instead of dialling a separately started WebSocket hub.
- Transport documentation.
docs/PROTOCOL.mdnow specifies the native-messaging carrier (framing, size cap, ephemeral port, bearer token, endpoint file, host manifest locations) anddocs/ARCHITECTURE.mdexplains the inverted startup and the measured MV3 lifetime behaviour. - The MCP endpoint now survives browser background restarts. The native host reuses its bearer token and port across respawns, so an MCP client configured once keeps working instead of silently pointing at a dead URL every time the extension's service worker is torn down.
- New
browserbuddy install-hostcommand. Installs the native-messaging host manifest for Chrome or Firefox, so an npm-installed BrowserBuddy can be used with the default native transport without cloning the repo. It targets a profile with--user-data-dir/--homeor finds the platform default, and refuses non-Linux platforms with a clear message instead of installing where no browser looks. - Documented the native carrier's size limits, endpoint stability and install path.
docs/PROTOCOL.mdanddocs/ARCHITECTURE.mdnow specify the per-direction message caps, the 64 MB per-result limit, the endpoint file's pid liveness rule, and how the bearer token and port survive host respawns. - A supported way to attach Claude Code.
browserbuddy client-configprints the exactclaude mcp add --transport httpcommand (and an equivalent config block) for the endpoint the browser-spawned host published, or hard-errors with the full setup procedure when no host is running;--applyregisters it for you.
Fixes
- Large
browser_evalandbrowser_screenshotresults no longer kill the native host. Results above 1 MB used to close the native-messaging channel and take the MCP endpoint down with it; they now round-trip, and a result above 64 MB fails that one call instead of the whole session. - A leftover
mcp-endpoint.jsonfrom a killed host no longer misdirects clients. The endpoint descriptor is validated against the recorded process id, so a stale file reads as "no endpoint" instead of pointing at a port that may now belong to something else. - A missing native host manifest now stays visible, and a normal disconnect stops crying wolf. The extension distinguishes a host that never started (persistent error badge plus install instructions) from a host that exited after running (neutral "native host disconnected" tooltip while it reconnects).
- CLI help no longer points at a port constant that does not exist. The port-in-use message and the
servehelp describe the actual topology: the extension's native transport spawns the host itself, andbrowserbuddy install-hostsets it up. - Nothing is silently dropped. An event whose type this host does not recognise is now stored intact and marked
unknown: trueinbrowser_observeoutput instead of being lost, and an RPC for a method the extension does not implement fails loudly naming the method. - Observation now spans server restarts. Event sequence numbers are persisted and never reused, and the recent-event ring is reloaded from the log at startup, so
browser_observe {sinceSeq}keeps returning exactly what is new even after the browser respawns the host. - The Firefox idle-suspension caveat is now scoped to the carrier it affects. Measured on Firefox 148, the native-messaging transport keeps the same host process serving across three minutes of idle; the once-a-minute reconnect cycling documented before applies only to the WebSocket carrier.
- Page loads the agent caused are no longer recorded as the user's. A slow cross-origin load could outlive the agent-attribution window, so
page_loaded(andtab_activated) came back taggeduser-- which pollutedbrowser_observe, poisoned demonstrations, and could wake abrowser_wait_for_userthe agent had started itself. A load now inherits the actor of the navigation that committed it, and both handlers decide the actor when the event fires rather than after fetching the tab title.