mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-26 11:49:36 +00:00
Each hanzo-mcp now hosts a ZAP server directly on the lowest free port from [9999..9995], using a POSIX flock for cross-MCP arbitration. The browser extension discovers and connects to this server without any node-side bridge in the critical path. - New hanzo_tools/browser/zap_server.py: wire format, server, lifecycle, client registry, leases, cluster-visibility config registry. - _extension_command tries ZAP first (sub-1ms median round-trip on loopback), falls back to legacy HTTP bridge on :9224 only when no ZAP-connected client matches. Pinnable via BROWSER_TRANSPORT. - New tool actions: list_mcp_instances, claim_browser, release_browser for sub-agent coordination across multiple parallel MCPs. - Legacy CDP HTTP bridge no longer auto-starts (opt in with HANZO_CDP_BRIDGE_ENABLED=1). It remains available for non-ZAP MCP clients. Tests: 30 new unit tests cover wire format, port arbitration, multi-MCP coexistence, lease semantics, and cluster registry. Bench shows 0.35ms median round-trip on local loopback. Total tests: 4 baseline -> 35 passing (30 zap_server + 1 bench + 4 imports).
188 lines
5.7 KiB
Markdown
188 lines
5.7 KiB
Markdown
# LLM.md - Hanzo Python SDK
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
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, uvx
|
|
- `hanzo-tools-browser` - Playwright automation
|
|
- `hanzo-tools-fs` - read, write, edit, tree, find, search, ast
|
|
- `hanzo-tools-memory` - Unified memory tool
|
|
- `hanzo-tools-reasoning` - think, critic
|
|
- `hanzo-tools-agent` - CLI agent runner, iching, review
|
|
- `hanzo-tools-api` - Generic REST API via OpenAPI
|
|
- `hanzo-tools-lsp` - Language server protocol
|
|
- `hanzo-tools-refactor` - AST-based refactoring
|
|
- `hanzo-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:**
|
|
```toml
|
|
[project.entry-points."hanzo.tools"]
|
|
shell = "hanzo_tools.shell:TOOLS"
|
|
```
|
|
|
|
## Key Patterns
|
|
|
|
### Tool Registration
|
|
```python
|
|
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)
|
|
```python
|
|
# 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)
|
|
|
|
Two-process architecture (since 0.5.0): `hanzo-tools-browser` hosts the
|
|
ZAP server directly inside the MCP process. Browser extensions discover
|
|
it on the lowest free port from `[9999, 9998, 9997, 9996, 9995]` (POSIX
|
|
flock ensures multi-MCP coexistence under one ext).
|
|
|
|
```python
|
|
from hanzo_tools.browser import (
|
|
BrowserTool, # the MCP tool
|
|
ZapServer, # raw server (for advanced use)
|
|
get_or_start_server, # bootstrap + return singleton
|
|
get_server, # return current singleton or None
|
|
)
|
|
```
|
|
|
|
Key files:
|
|
- `hanzo_tools/browser/zap_server.py` — wire format, server, leases,
|
|
cluster registry (`~/.hanzo/extension/config.json:mcp_instances`).
|
|
- `hanzo_tools/browser/browser_tool.py` — `_extension_command` tries
|
|
ZAP first, falls back to legacy HTTP bridge on `:9224`. New actions
|
|
`list_mcp_instances`, `claim_browser`, `release_browser`.
|
|
- `hanzo_tools/browser/cdp_bridge_server.py` — legacy node-bridge
|
|
replacement (kept for non-ZAP callers).
|
|
|
|
Env vars:
|
|
- `BROWSER_TRANSPORT=zap|http|auto` — pin transport (default auto).
|
|
- `HANZO_ZAP_DISABLED=1` — don't auto-start the ZAP server.
|
|
- `HANZO_ZAP_PORTS=9999,9998` — override the candidate port list.
|
|
- `HANZO_AGENT_LABEL=...` — attach to the cluster registry entry.
|
|
- `HANZO_CDP_BRIDGE_ENABLED=1` — opt back into the legacy HTTP bridge.
|
|
|
|
Tests live in `pkg/hanzo-tools-browser/tests/test_zap_server.py` (30
|
|
cases: wire format, lifecycle, RPC, leases, multi-MCP). Latency bench
|
|
in `test_zap_bench.py`. The full suite runs with:
|
|
|
|
```bash
|
|
cd pkg/hanzo-tools-browser
|
|
uv venv .venv --python 3.12
|
|
.venv/bin/python -m pip install -e .
|
|
.venv/bin/python -m pytest tests/ -v
|
|
```
|
|
|
|
## API Tool
|
|
|
|
Generic REST API tool with OpenAPI specs.
|
|
|
|
```python
|
|
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` |
|
|
|
|
```python
|
|
# 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
|
|
|
|
```bash
|
|
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)
|