fix: restore Python 3.10/3.11 support and report MCP version
Testing / test (3.10) (push) Successful in 48s
Testing / test (3.12) (push) Successful in 47s
Testing / test (3.14) (push) Successful in 1m7s
Testing / remote-protocol-compat (0.15.0) (push) Successful in 6m59s
Testing / remote-protocol-compat (0.16.0) (push) Successful in 6m47s
Testing / test (3.15) (push) Successful in 6m20s

- 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.
This commit is contained in:
2026-10-20 12:41:25 +02:00
parent 7cb034dfc2
commit 69387c1054
19 changed files with 155 additions and 88 deletions
+1 -1
View File
@@ -123,7 +123,7 @@ def cmd_auth_policy(ctx, identifier, keys_file, server_default, safe_only, allow
console.print("[red]Choose exactly one policy mode:[/red] --server-default, --safe, or one/more --allow-* flags")
sys.exit(1)
is_interactive = click.get_text_stream("stdin").isatty()
is_interactive = bool(sys.stdin is not None and sys.stdin.isatty())
current_categories = None
if not identifier:
if not is_interactive:
+2 -2
View File
@@ -38,7 +38,7 @@ def write_native_host_exe(path: Path) -> None:
encoding="utf-8",
)
else:
path.write_text(f'#!{sys.executable}\nfrom browser_cli.native.host import main\nmain()\n')
path.write_text(f'#!{sys.executable}\nfrom browser_cli.native.host import main\nmain()\n', encoding="utf-8")
path.chmod(path.stat().st_mode | 0o111)
def _windows_registry_views():
@@ -156,7 +156,7 @@ def _install_manifest(browser: str, host_exe: Path, manifest: dict) -> list:
try:
directory.mkdir(parents=True, exist_ok=True)
manifest_path = directory / f"{NATIVE_HOST_NAME}.json"
manifest_path.write_text(json.dumps(manifest, indent=2))
manifest_path.write_text(json.dumps(manifest, indent=2), encoding="utf-8")
installed.append(manifest_path)
except Exception as e:
console.print(f"[yellow]Could not write to {directory}: {e}[/yellow]")
+2 -1
View File
@@ -114,7 +114,8 @@ def _inline_text(node):
return f"[{text or href}]({href})" if href else text
if tag == "code":
text = _normalize_inline("".join(_inline_text(child) for child in node.children))
return f"`{text.replace('`', r'\\`')}`" if text else ""
escaped = text.replace("`", r"\\`")
return f"`{escaped}`" if text else ""
if tag in {"strong", "b"}:
text = _normalize_inline("".join(_inline_text(child) for child in node.children))
return f"**{text}**" if text else ""
+2
View File
@@ -18,6 +18,7 @@ from browser_cli.mcp.naming import resolve_tool_prefix, tool_name
from browser_cli.mcp.serialization import structured
from browser_cli.mcp.targets import resolve_tab_id
from browser_cli.url_match import find_reusable_tab, needs_navigation, tab_id as read_tab_id, tab_url
from browser_cli.version_manager import project_version
ClientFactory = Callable[..., BrowserCLI]
@@ -70,6 +71,7 @@ def create_server(*, client_factory: ClientFactory = BrowserCLI, tool_prefix: st
"browser-cli",
description="Control a real running browser through the browser-cli SDK.",
instructions=_SERVER_INSTRUCTIONS,
version=project_version(),
)
@mcp.tool(name=tool_name("tabs_list", prefix))
+1 -1
View File
@@ -15,7 +15,7 @@ def _file_lock(path: Path) -> Iterator[None]:
"""Best-effort cross-process lock for registry read/modify/write updates."""
path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
lock_path = path.with_suffix(path.suffix + ".lock")
with lock_path.open("a+") as lock_file:
with lock_path.open("a+", encoding="utf-8") as lock_file:
if os.name == "nt":
try:
import msvcrt