mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-27 04:20:09 +00:00
* fix(browser-mcp): migrate CDP bridge -> zapd consumer; make disk authoritative The MCP runtime kept pulling the buggy published wheel (hanzo-tools-browser 0.5.7) from PyPI instead of the migrated on-disk source, so every restart re-broke the `cdp` tool. Reinstate `cdp` as a zapd-native tool, finish the bridge->zapd migration (delete dead bridge/server modules + their tests), bump to 0.5.8 and pin hanzo-mcp >=0.5.8, add the package to the uv workspace/sources so disk is authoritative. 9/9 browser tests pass. Pairs with the hanzoai/extension bridge->zapd migration (separate, still uncommitted). The ~/.claude.json MCP invocation was also pointed at the local editable source (machine config, not in this repo). (cherry picked from commit 11b4d4ea8a5570d6fb0e51079fa445dba3713f71) * fix(browser): drop dead lifecycle.py (in-process zap_server/cdp_bridge are gone) lifecycle.py only wrapped the removed in-process ZAP server + CDP bridge; nothing imports it under the zapd-consumer model. One way: transport is zapd_consumer, tools are browser/cdp/playwright. --------- Co-authored-by: hanzo-dev <[email protected]> Co-authored-by: Antje Worring <[email protected]> Co-authored-by: Hanzo Dev <[email protected]>
106 lines
3.6 KiB
Python
106 lines
3.6 KiB
Python
"""Browser automation tools for Hanzo AI — zapd consumer model.
|
|
|
|
[Browser ext] --native host--> [zapd router] --unix sock--> [hanzo-mcp] --stdio--> [Agent]
|
|
|
|
hanzo-mcp hosts NO server. It connects to the one shared local router at
|
|
``~/.zap/run/zapd.sock`` as a *consumer*, lists providers, and routes opaque
|
|
CDP commands to a ``browser:*`` provider (the real Chrome/Firefox extension,
|
|
connected via its native-messaging host). No in-process server, no mDNS, no
|
|
well-known port pool, no :9224 HTTP bridge, no Playwright fallback in
|
|
native-browser mode. ``zapd`` is a separate always-on daemon (see ``~/work/zap``).
|
|
|
|
Three peer tools are exposed, all sharing one transport (``zapd_consumer``):
|
|
- ``browser`` — high-level, action-oriented (navigate/click/screenshot/tabs …).
|
|
Auto-routes through the extension over zapd; falls back to
|
|
Playwright only for actions the extension can't serve.
|
|
- ``cdp`` — low-level, method-oriented (send any CDP method by name).
|
|
- ``playwright`` — the same action surface as ``browser`` but pinned to the
|
|
Playwright backend (never touches the extension).
|
|
"""
|
|
|
|
import logging
|
|
from typing import Optional
|
|
|
|
from mcp.server import FastMCP
|
|
|
|
from hanzo_tools.core import BaseTool, ToolRegistry
|
|
from hanzo_tools.browser.browser_tool import (
|
|
PLAYWRIGHT_AVAILABLE,
|
|
BrowserPool,
|
|
BrowserTool,
|
|
get_backend,
|
|
_load_config,
|
|
_save_config,
|
|
browser_tool,
|
|
create_browser_tool,
|
|
launch_browser_server,
|
|
)
|
|
from hanzo_tools.browser.cdp_tool import CdpTool
|
|
from hanzo_tools.browser.playwright_tool import PlaywrightTool
|
|
from hanzo_tools.browser.zapd_consumer import ZapdConsumer, get_consumer
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Tools list for entry point discovery (see pyproject [hanzo.tools]).
|
|
TOOLS = [BrowserTool, CdpTool, PlaywrightTool]
|
|
|
|
__all__ = [
|
|
# Tools (the three peers)
|
|
"BrowserTool",
|
|
"CdpTool",
|
|
"PlaywrightTool",
|
|
# Factory + module-level instance (existing public API)
|
|
"browser_tool",
|
|
"create_browser_tool",
|
|
# Browser pool / Playwright server
|
|
"BrowserPool",
|
|
"launch_browser_server",
|
|
# zapd consumer (the one canonical transport)
|
|
"ZapdConsumer",
|
|
"get_consumer",
|
|
# Availability check
|
|
"PLAYWRIGHT_AVAILABLE",
|
|
# Backend helper
|
|
"get_backend",
|
|
# Registration
|
|
"TOOLS",
|
|
"register_browser_tools",
|
|
"register_tools",
|
|
]
|
|
|
|
|
|
def register_browser_tools(mcp_server: FastMCP, **kwargs) -> list[BaseTool]:
|
|
"""Register the browser tools with the MCP server.
|
|
|
|
hanzo-mcp is a zapd *consumer* — it connects to the shared local router at
|
|
``~/.zap/run/zapd.sock`` on demand and hosts no in-process server. zapd is a
|
|
separate always-on daemon, so there is nothing to start here.
|
|
|
|
Args:
|
|
mcp_server: The FastMCP server instance
|
|
**kwargs: Forwarded to the browser tool (headless, cdp_endpoint, backend)
|
|
|
|
Returns:
|
|
List of registered tools
|
|
"""
|
|
headless = kwargs.get("headless", True)
|
|
cdp_endpoint = kwargs.get("cdp_endpoint")
|
|
backend = kwargs.get("backend")
|
|
|
|
tools: list[BaseTool] = [
|
|
create_browser_tool(headless=headless, cdp_endpoint=cdp_endpoint, backend=backend),
|
|
CdpTool(),
|
|
PlaywrightTool(headless=headless, cdp_endpoint=cdp_endpoint),
|
|
]
|
|
for tool in tools:
|
|
ToolRegistry.register_tool(mcp_server, tool)
|
|
return tools
|
|
|
|
|
|
def register_tools(mcp_server: FastMCP, **kwargs) -> list[BaseTool]:
|
|
"""Register all browser tools with the MCP server.
|
|
|
|
Standard entry point called by the tool discovery system.
|
|
"""
|
|
return register_browser_tools(mcp_server, **kwargs)
|