* 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]>
6.7 KiB
LLM.md - Hanzo Python SDK
Quick Start
uv sync --all-packages
uv run python -c "from hanzoai import __version__; print(__version__)"
Packages
Core (Workspace Members)
| Package | Import | Description |
|---|---|---|
| hanzoai | import hanzoai |
Official Hanzo API client |
| hanzo-mcp | import hanzo_mcp |
MCP server with 39 tools |
| hanzo-memory | import hanzo_memory |
Memory service with SQLite/vector |
| hanzo-repl | import hanzo_repl |
Interactive REPL |
| hanzo-agent | import hanzo_agent |
Agent framework |
Tool Packages (Entry Points)
All tools discovered via [project.entry-points."hanzo.tools"]:
hanzo-tools-shell- zsh, ps, open, npx, uvxhanzo-tools-browser- Playwright automationhanzo-tools-fs- read, write, edit, tree, find, search, asthanzo-tools-memory- Unified memory toolhanzo-tools-reasoning- think, critichanzo-tools-agent- CLI agent runner, iching, reviewhanzo-tools-api- Generic REST API via OpenAPIhanzo-tools-lsp- Language server protocolhanzo-tools-refactor- AST-based refactoringhanzo-tools-llm- LLM calls, consensus
Architecture
hanzo-mcp (thin wrapper)
└── discovers tools via entry points from hanzo-tools-* packages
hanzo-tools-*
└── each exports TOOLS list via entry point
Entry Point Pattern:
[project.entry-points."hanzo.tools"]
shell = "hanzo_tools.shell:TOOLS"
Key Patterns
Tool Registration
class MyTool(BaseTool):
name = "my_tool"
@property
def description(self) -> str:
return "Tool description"
async def call(self, ctx, **params) -> str:
pass
Async (Non-blocking)
# Use asyncio subprocess
proc = await asyncio.create_subprocess_exec(*cmd, stdout=PIPE, stderr=PIPE)
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=30)
# Use aiofiles for file I/O
async with aiofiles.open(path) as f:
content = await f.read()
Auto-backgrounding
Commands auto-background after 45s. Use ps tool to monitor.
Memory System
SQLite-based with optional vector search (sqlite-vec).
~/.hanzo/
├── memory/ # Global markdown files
└── db/
└── global.db # SQLite with FTS5
/project/.hanzo/
├── memory/ # Project memories
└── db/
└── memory.db # Project database
Backends: local (default), sqlite, lancedb (optional), kuzudb (optional)
Browser Tool (hanzo-tools-browser)
hanzo-mcp is a zapd consumer — it hosts NO server. It connects to the
one shared local router ~/.zap/run/zapd.sock (see ~/work/zap), lists
providers, and routes opaque commands to a browser:* provider (the real Chrome/
Firefox extension, connected via the native host). No in-process server, no mDNS,
no 9999-9995, no :9224 HTTP bridge, no BROWSER_TRANSPORT, no Playwright
fallback in native-browser mode. The old zap_server.py/cdp_bridge_server.py
in-process model is removed.
from hanzo_tools.browser.zapd_consumer import ZapdConsumer, get_consumer
c = get_consumer() # connects to ~/.zap/run/zapd.sock
c.resolve_browser("chrome", None) # -> "browser:chrome/<host>/default"
c.route(provider, "Target.getTargets", {}) # opaque command, raw result bytes
Key files:
hanzo_tools/browser/zapd_consumer.py— the ZAP router-envelope codec + consumer (connect / hello / providers.list / route). Mirrorszapd/src/frame.rs.hanzo_tools/browser/browser_tool.py—_extension_command/_check_extensionroute viazapd_consumer.register_browser_toolsno longer starts a server.hanzo_tools/browser/cdp_tool.py— thecdptool, a method-oriented peer ofbrowser. Sends a raw CDP method by name (Target.getTargets,Page.navigate) through the SAMEzapd_consumerroute — the method goes on the wire verbatim, so there is no{"action":"cdp"}envelope for the extension to reject.
Two tools, one transport: browser (high-level verbs) and cdp (raw methods).
Both are in TOOLS and resolve to the same zapd provider.
The router (zapd) is a separate always-on daemon — install/run it from
zap-proto/zapd (curl … | sh or @hanzo/zapd).
MCP must be sourced from disk, not PyPI. The published hanzo-tools-browser
wheel (≤0.5.7) still ships the removed in-process HTTP-bridge model and breaks
cdp with Unknown method: cdp. The Claude Code MCP entry in ~/.claude.json
therefore uses uvx --from pkg/hanzo-mcp --with-editable pkg/hanzo-tools-browser
so the on-disk source (≥0.5.8) is authoritative. --with <python-sdk root> does
NOT work — uv ignores the workspace sources for --from deps and pulls the buggy
wheel from the index. hanzo-mcp pins hanzo-tools-browser>=0.5.8 as a guard.
Router/transport tests live with the router (zap-proto/zapd: cargo test +
tests/e2e.py). The Python consumer + cdp routing are unit-tested in
tests/test_browser_tools.py (zapd mocked) and verified end-to-end against a
live zapd. The old test_zap_server.py/zap_server.py (in-process server,
leases, multi-MCP) and cdp_bridge_server.py are removed.
API Tool
Generic REST API tool with OpenAPI specs.
api(action="list") # List providers
api(action="config", provider="github", api_key="x") # Configure
api(action="call", provider="github", operation="listRepos")
Auto-detects: GITHUB_TOKEN, CLOUDFLARE_API_TOKEN, OPENAI_API_KEY, etc.
Hanzo API Providers
All Hanzo services have unified OpenAPI specs at /Users/z/work/hanzo/openapi/:
| Provider | Service | Base URL | Spec |
|---|---|---|---|
hanzo |
Unified API | api.hanzo.ai | hanzo.yaml |
hanzo-iam |
Identity/Auth | iam.hanzo.ai | iam/openapi.yaml |
hanzo-gateway |
LLM Gateway | gateway.hanzo.ai | gateway/openapi.yaml |
hanzo-commerce |
E-commerce | api.hanzo.ai/v1 | commerce/openapi.yaml |
hanzo-vector |
Vector DB | vector.hanzo.ai | vector/openapi.yaml |
hanzo-cloud |
AI Platform | cloud.hanzo.ai | cloud/openapi.yaml |
hanzo-nexus |
RAG/Knowledge | nexus.hanzo.ai | nexus/openapi.yaml |
# Example: Call IAM API
api(action="spec", provider="hanzo-iam")
api(action="ops", provider="hanzo-iam", search="user")
api(action="call", provider="hanzo-iam", operation="getUser", params='{"id": "admin/user1"}')
Testing
uv run pytest tests/ -v
uv run python -c "from hanzoai import __version__; print(__version__)"
Common Issues
Import error: Run uv sync --all-packages
Missing tool: Check entry point in package's pyproject.toml
Backend not available: Install optional deps (lancedb, kuzu, sqlite-vec)