Files
browser-cli/browser_cli/mcp/serialization.py
T
daniel156161 bd2a18baba Add optional stateless MCP server for the real browser
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.
2026-08-09 20:37:47 +02:00

20 lines
699 B
Python

"""Convert browser-cli SDK models into MCP structured-output values."""
from __future__ import annotations
from dataclasses import fields, is_dataclass
from typing import Any
def structured(value: Any) -> Any:
"""Return JSON-compatible data without private SDK binding fields."""
if is_dataclass(value) and not isinstance(value, type):
return {
field.name: structured(getattr(value, field.name))
for field in fields(value)
if not field.name.startswith("_")
}
if isinstance(value, dict):
return {str(key): structured(item) for key, item in value.items()}
if isinstance(value, (list, tuple, set)):
return [structured(item) for item in value]
return value