Summary
The extension's background service worker (MV3) gets killed by the browser after a period of inactivity. When the user opens the popup, it shows "Disconnect" (read from storage), but the service worker hasn't actually re-established the WebSocket to ws://localhost:9009. All MCP tool calls fail with "No connection to browser extension" despite the UI indicating a connection.
Root cause
The background service worker in background.js polls every 1s:
const e = setInterval(async () => {
let n = await pd.getValue() // selectedTabId from storage
if (n && !await xC(n) && (pd.setValue(null), n = null), t || !n) return
const r = RC.defaultWsPort // 9009
t = new WebSocket("ws://localhost:" + r)
...
}, 1000)
When the service worker is killed (browser idle / memory pressure), the setInterval stops. The selectedTabId persists in storage, so on next popup open the UI reads it and shows "Disconnect", but the service worker hasn't started yet — and even when it does start, the polling interval may not fire before the MCP client sends tools/list.
Steps to reproduce
- Install Browser MCP extension v1.3.4 in Helium/Chrome
- Connect to a tab — works fine
- Leave the browser idle for a few minutes (or force-kill the service worker via
chrome://extensions)
- Open the popup — shows "Disconnect" (stale)
- Try any MCP tool — fails with "No connection to browser extension"
- Clicking "Disconnect" then "Connect" again fixes it (restarts the service worker)
Environment
- Extension: v1.3.4
- Server: @browsermcp/mcp@0.1.3
- Browser: Helium (Chromium-based, macOS)
- Client: OpenCode desktop app
Suggested fix
- Service worker keepalive: Use
chrome.runtime.connect() or a lightweight port to keep the service worker alive while an MCP client is connected.
- Stale state detection: On popup open, verify the WebSocket is actually open before showing "Disconnect". If the WS is closed, show "Connect" instead.
- Lazy reconnect: When the service worker starts (or the popup opens), immediately attempt to reconnect the WebSocket rather than waiting for the next 1s poll interval.
Summary
The extension's background service worker (MV3) gets killed by the browser after a period of inactivity. When the user opens the popup, it shows "Disconnect" (read from storage), but the service worker hasn't actually re-established the WebSocket to
ws://localhost:9009. All MCP tool calls fail with "No connection to browser extension" despite the UI indicating a connection.Root cause
The background service worker in
background.jspolls every 1s:When the service worker is killed (browser idle / memory pressure), the
setIntervalstops. TheselectedTabIdpersists in storage, so on next popup open the UI reads it and shows "Disconnect", but the service worker hasn't started yet — and even when it does start, the polling interval may not fire before the MCP client sendstools/list.Steps to reproduce
chrome://extensions)Environment
Suggested fix
chrome.runtime.connect()or a lightweight port to keep the service worker alive while an MCP client is connected.