- Drop backslash from f-string expression in markdown/html.py
- That syntax needs 3.12+ (PEP 701) and broke import on 3.10/3.11
- Add Python matrix 3.10/3.12/3.14/3.15 to the Gitea testing workflow
- Pass project_version() to MCPServer; serverInfo.version was empty
- Cover the reported version with an in-memory MCP client test
- Swap deprecated click.get_text_stream for sys.stdin.isatty()
- Add explicit encoding to native host shim and manifest writes
- Add explicit encoding to the registry lock file open
- Add explicit encoding to every read_text/write_text call in tests
- Bump version to 0.16.10 in pyproject.toml and extension/manifest.json
- Move .python-version to 3.15 and refresh uv.lock
Extension code is unchanged; only the manifest version string moved.
Verified with 778 tests on 3.10 through 3.15, plus a strict run using -X warn_default_encoding with EncodingWarning and DeprecationWarning as errors.
- Add browser_cli/url_match.py with a canonical URL key for tab matching
- Canonicalize scheme, host, default ports, www., trailing slash, query order
- Support reuse modes none, exact, similar and origin via normalize_mode()
- Pick the closest match when several tabs qualify, preferring identical URLs
- Skip the reload when the reused tab already shows the requested page
- Expose nav.reusable_tab() so callers can look up a tab without side effects
- Accept reuse/reuse_domain/reuse_title in tabs.open(), not just nav.open()
- Add --reuse-exact to nav open and nav open-wait; --reuse is now fuzzy
- Default MCP tabs_open to reuse="similar" and return a reused flag
- Tell MCP agents in the server instructions to reuse before opening
- Document the reuse modes in README for MCP, CLI and Python API
- Cover url_match, SDK reuse paths and MCP reuse behaviour with new tests
- Bump package and extension version to 0.16.9
- Refresh uv.lock for the updated dependency set
- Add server-side search filtering to the MCP tabs_list tool.
- Let MCP DOM query, click, and type tools operate on an explicit tab_id.
- Use tab-scoped DOM eval fallbacks when the SDK active-tab helpers are not enough.
- Cover tab filtering and tab-targeted DOM operations in MCP tests.
- Bump package and extension versions to 0.16.8 and refresh the lockfile.
- Add --tab option to all extract commands and page info
- Thread tab_id through the SDK extract and page namespaces
- Route page.info with a tab_id to tabs.status for cross-tab metadata
- Accept tab_id in the MCP page_info, extract_text, extract_markdown tools
- Forward tabId in the extension page.info and extract.html handlers
- Keep the active tab as default when no tab is given
- Cover tab-scoped reads in API, CLI, and MCP tests
- Bump package and extension version to 0.16.7
- Refresh uv.lock with current dependency versions
Requiring an explicit tab_id forced every navigate or close through a
preceding tabs_list call, which costs an MCP client a full round trip just to
learn the ID the browser already considers current.
navigate and tabs_close now resolve the active tab when tab_id is omitted,
matching the screenshot tool. Resolution is explicit rather than forwarding
None into the SDK, so the acting tool knows which tab it touched; tabs_close
reports it, since closing the wrong tab is not recoverable.
This stays in the MCP layer: the SDK and CLI signatures are unchanged.
Expose a conservative browser-cli tool surface to MCP hosts without
duplicating browser-control logic: every tool is a thin adapter over the
existing Python SDK, and each call builds a fresh BrowserCLI so the real
browser stays the only owner of tab and navigation state.
The MCP process resolves BROWSER_CLI_PROFILE, BROWSER_CLI_REMOTE, and
BROWSER_CLI_KEY explicitly instead of passing None down, so a pinned server
targets one browser rather than fanning listing calls out across every
connected browser. Explicit tool arguments still win.
Tool names keep a browser_ prefix for hosts that expose raw MCP names, while
BROWSER_CLI_MCP_TOOL_PREFIX lets hosts that already namespace by server drop
it and avoid names like browser_cli_testing_browser_tabs_list.
JavaScript evaluation, raw commands, storage writes, and session import stay
unexposed, and Streamable HTTP refuses non-loopback binds because the endpoint
has no authentication of its own; remote browsers go through browser-cli's
authenticated remote transport instead.
MCP stays an optional extra, so normal installs are unaffected.