English · 中文
All notable changes to byob will be documented here.
browser_evalnow passes through CDPexceptionDetails(line/col/stack) in error envelopes so callers can debug the actual page-side throw instead of seeing a generic "Page threw during eval".browser_screenshot— bridge nowmkdir -ps the parent directory of a user-suppliedsavePathbefore writing. Previously failed withENOENTwhen the target directory didn't already exist.browser_screenshot— reportedwidth/heightnow match the actual captured image: viewport size whenfullPage:false, full scroll dimensions whenfullPage:true. Previously always reporteddocumentElement.scrollHeight, which mismatched the PNG whenfullPage:false.browser_screenshot— over-size error hint now mentionsbrowser_emulate_deviceas an additional escape valve alongsidefullPage:falseandformat:jpeg.- URL guard —
chrome-extension://is no longer blocked. byob is itself an extension and CDP attaches to extension pages just fine; wallet/tooling extensions (Rabby, MetaMask, etc.) are common inspection targets. - URL guard —
BYOB_ALLOW_FILE/BYOB_ALLOW_AUTH_DOMAINStoggles are now actually wired up. Background SW hydrates the in-memory cache fromchrome.storage.localat boot and listens tostorage.onChanged. Set via the byob SW console:chrome.storage.local.set({ BYOB_ALLOW_FILE: true }). Previously theenvFlagstub silently returnedfalsewhile theurl_forbiddenhint kept telling users to set env vars that did nothing.
browser_emulate_device— 4 desktop viewport presets so callers don't needcustomfor common sizes:desktop-1366(1366×768),desktop-1440(1440×900 @2×),desktop-1080p(1920×1080),desktop-4k(2560×1440 @2×).desktop(no suffix) still resets all overrides.
browser_intercept_start+browser_intercept_stop— Stateful request interception via CDPFetchdomain. Rules array withurlPattern(glob) orurlRegex, optionalmethodsfilter, and one of 5 actions:block/fulfill/modify(request) /modifyResponse/passthrough.modifyResponsesupportsbodyReplace(whole-body) orbodyRegex(in-place regex substitution) — text-like Content-Types only. Mirrorsrecord-network's stateful lifecycle (start returnsinterceptId; stop returns hit stats).browser_drag— Mouse drag from one point to another overdurationMswith linear interpolation instepssubsteps.from/toeach accept a CSS selector OR{x, y}page coordinates. Triggers mouse events; HTML5dragstart/drag/dragendare NOT fired. Supports iframe (framePath).browser_emulate_device— Emulate viewport / DPR / touch / User-Agent via CDPEmulation.*Override. Presets:iphone-17-pro-max,iphone-17,ipad-pro,pixel-9-pro,galaxy-s25-ultra,desktop(resets). Orcustom: { width, height, deviceScaleFactor, mobile, userAgent? }. Effect persists until reset or tab close.
intercept_not_found—intercept_stopinterceptId is unknown or already drained.
browser_set_cookies— Write a cookie viachrome.cookies.set(counterpart tobrowser_get_cookies). Honors CHIPS partition keys and lowercasesameSiteenum.browser_print_pdf— Save the current page as a PDF using CDPPage.printToPDFwith streamingIO.read. Default save dir is~/.byob/pdfs/. Returns a file PATH (not data). Supports A4/Letter/Legal, landscape, page ranges, uniform margins, 120-second timeout.browser_get_storage— ReadlocalStorage/sessionStoragefor the page's origin. Supports iframe (framePath). Truncates over 1MB by default, dropping sessionStorage first then trimming localStorage keys lexicographically.browser_get_performance— Page Web Vitals (LCP/CLS/INP/FCP/TTFB) plus navigation timing (DCL, load, DNS, TCP, transfer size). Default 3000ms sampling window; INP requires real user interaction.browser_upload_file— Upload local files to<input type="file">via CDPDOM.setFileInputFiles. Bridge validates absolute paths andfs.accessbefore forwarding to Chrome. Auto-firesinput+changeevents. Supports iframe (framePath).
not_a_file_input—upload_fileselector is not<input type="file">.file_not_found—upload_filepath is missing, non-readable, or non-absolute.
browser_scroll— scroll to top/bottom, scroll a selector into view, or scroll to an absolute Y coordinate. Returns finalscrollY+pageHeight.browser_press_key— send a single keyboard event (Enter, Escape, Tab, F5, ArrowDown, etc.) with optional Alt/Control/Shift/Meta modifiers.browser_select— choose an<option>in a native<select>by value, label, or index. Dispatchesinput+changeevents so SPA frameworks pick it up.browser_close_tab— close a browser tab by tabId.browser_go_back/browser_go_forward— walk the tab's history one step in either direction. Returnsno_historywhen the stack is empty.browser_hover— move the mouse over a selector via real CDP mouse events. Triggers tooltips and:hoverdropdown menus.browser_get_html— return outerHTML (or innerHTML) of an element (or the whole document). Truncated tomaxBytes(default 256 KB, max 8 MB) on a UTF-8 boundary.
option_not_found—<select>has no matching<option>for the given value/label/index (used bybrowser_select).no_history— the tab's history stack has nothing to go back/forward to (used bybrowser_go_back/browser_go_forward).
- 8 new schema unit tests covering required-field, XOR refinements, and default values for each new Input schema.
browser_get_console_logs— snapshot a tab'sconsole.log/warn/error- uncaught exceptions via CDP
Runtime.consoleAPICalled+Runtime.exceptionThrown.
- uncaught exceptions via CDP
browser_read_markdown— page → clean markdown via Mozilla Readability + turndown. Conversion runs server-side in the bridge (jsdom) so the page itself never sees Readability's DOM.browser_extract_table—<table>→ JSON. Two output shapes (rowsfor raw arrays,objectskeyed by header text) and anthOfTypeselector hint per table.browser_start_record_network/browser_stop_record_network— paired tools for HTTP + WebSocket capture. Output as JSON or HAR 1.2 (DevTools- compatible). Backed by a CDPNetwork.*accumulator with URL-pattern filtering and SW-eviction defence.
framePath: string[]parameter on 9 tools —browser_read / click / type / eval / wait_for / download_images / get_console_logs / read_markdown / extract_table. Walks nested iframes by CSS selector at each level.- Cross-origin iframes (OOPIFs) work via CDP
Target.setAutoAttach({ flatten: true })— the extension transparently follows the auto-attached child sessions. - Page-level coordinate translation for clicks inside nested iframes
(frame-coords helper composes per-frame
getBoundingClientRect). - 4 new error codes:
frame_not_found,frame_navigation_during_op,frame_attach_failed,frame_eval_blocked— each carriesframePathIndex+reasonso the LLM knows exactly which hop failed.
- End-to-end cancellation — mcp-client
Ctrl+C→ bridgePOST /cancel→ Native Messaging cancel frame → handlerAbortSignal→ CDP detach. PendingbridgePost/bridgeGetpromises reject with the newABORTEDerror code instead of hanging until the 10-min cap. - Per-call
requestIdis now plumbed through every layer, and every handler accepts anAbortSignal.
browser_evalretries viachrome.scripting.executeScriptwhen CDP attach fails (e.g. DevTools is open on the tab). The fallback runs in the page world, returns the same shape, and sets_meta.fallbackUsed: trueso callers can tell.
- Dual detector —
chrome.alarmsperiodic tick +chrome.idlestate change. On wake, in-flight recordings are aborted and every CDP session is detached so the next call starts clean. - New
ABORTED_DUE_TO_WAKEerror code distinguishes wake-aborts from userCtrl+C.
- 70+ new unit tests across schema, frame-resolver, har-converter, url-pattern, and handler abort plumbing (was sparse before).
nthOfTypeselector hint added tobrowser_extract_tableoutput for reliable click-by-table follow-ups.
browser_extract_tableimplicit-thead detection — when the first<tr>is all<th>Chrome auto-wraps it in a<tbody>; we now treat that as the header row instead of returning empty results.- bridge GET routes accept
?_requestId=query — regression introduced by the cancel-chain plumbing (B.2) that brokebrowser_list_tabsbriefly post-merge.
- 10 MCP tools end-to-end:
browser_read,browser_screenshot,browser_click,browser_type,browser_get_cookies,browser_navigate,browser_wait_for,browser_list_tabs,browser_switch_tab,browser_eval. - Native Messaging round-trip — bun-workspaces TS monorepo with three
packages (
@byob/sharedschemas,@byob/bridgeNative Messaging host,@byob/mcp-serverstdio MCP server) plus a WXT-built MV3 extension. - Per-user RSA key —
byob installauto-generates~/.byob/extension-key.pemon first run;wxt.config.tsreads it dynamically. Two byob installs on different machines get two different extension IDs. - One-command setup —
byob installdoes it all: key gen → extension build → NM manifest write → next-step instructions. - Multi-bridge support — one
byob-bridgeprocess per Chrome profile, all registered in~/.byob/bridges.jsonwith PID liveness check. - Management CLI —
byob doctor / install / bridges / logs / uninstall.
- CDP attach 3× retry with linear backoff (covers DevTools-toggle race).
- Discarded-tab revival — if Chrome GC'd a reused tab, reload + waitForLoad before attaching.
- Special-URL pre-check — catches
chrome:///devtools:///about://active tabs before CDP attach throws an opaque error; reported asurl_forbiddenwith an actionable hint. - chrome.power.requestKeepAwake('display') wraps long-running operations via a refcounted helper so concurrent calls don't fight each other.
- beforeunload guard — installed during
browser_readso SPAs can't yank the DOM out from under the scroll loop. - SPA priming + scrollHeight stability — first-paint scroll + tracked scrollHeight stability fixes the "X.com / FB / new-Reddit returns zero chunks on round 1" failure mode.
- bridgePost / bridgeGet timeouts — undici Agent with explicit 10-min
cap + clean
bridge_not_runningenvelope when bridge is unreachable. - NM-protocol envelope strip — bridge no longer leaks
type/requestIdfields into the HTTP response. - Handler
typefield collision fix — dispatcher now spreads payload before NM-protocol fields so handlers can never shadowtype:'result'(caught whenEvalOutput.typecollided and stalled the pending-request map). - Cookie sameSite enum matches Chrome's lowercase
chrome.cookiesAPI rather than CDP's capitalizedNetwork.getCookiesform. - Focused-window tab placement — new background tabs land in the user's current window (not a stray new one).
browser_evalis hidden by default. SetBYOB_ALLOW_EVAL=1on the MCP server's environment to expose it.- URL blacklist — chrome:/chrome-extension:/about:/devtools:/view-source:/ file: protocols and major auth hostnames blocked by default.
- Eval audit log — every call appended to
~/.byob/eval-audit.log. - Eval rate-limit — 5 calls per minute per tab (extension-side).
- Eval Chrome notification — every call surfaces a system notification.
- Socket file mode 0600,
~/.byob/directory mode 0700,umask(0o077)enforced in bridge process.
- Cancel/Abort propagation (mcp-client cancel → bridge → CDP detach)
- CDP fallback to
chrome.scripting.executeScript browser_download_imagesseparate tool (loopback HTTP for large payloads)- Wake / sleep detection (1s tick, gap > 5s)
- Cross-frame iframe operation (
Page.getFrameTree+ executionContextId) - Long-operation streaming progress (MCP
setStatus) - Container tree structured output
- Session-handle incremental chunk collection
- Chrome Web Store packaging