is_authenticated() was `load_token() is not None` — a string-presence check that returned True for the literal "fake.not.a.real.jwt". Every permission decision downstream of it was therefore a lie. This makes the client side of Hanzo auth actually work, end to end. Builds on the /v1/iam prefix already on main; the endpoint paths were necessary but not sufficient. hanzo_iam.tokens — the ONE credential judge. Verifies the signature against the issuer's published JWKS plus exp/iss/aud, and fails CLOSED: an unreachable JWKS is not a pass, because a client that cannot check a signature does not know the token is good. Reason codes distinguish "expired" from "offline". `alg: none` is absent from the accepted list by construction. An opaque API key cannot be judged offline, so it is confirmed against userinfo rather than assumed valid for being a non-empty string. hanzo_iam.oauth — a real login: authorization code + PKCE (S256) over a loopback redirect, with a deadline. Not the device grant, which would be the better CLI UX: iam implements RFC 8628 fully and advertises it, but no PUBLIC client is registered, so POST /v1/iam/oauth/device answers 401 invalid_client for hanzo-cli, hanzo-app, hanzo-cloud and hanzo-console alike. Registering one public app is the whole server-side fix; shipping a device path that always 401s would be a lie in code. The password grant is out for the same reason (401 invalid_client without a secret), so `--no-browser` now prints the URL instead of prompting for a password it cannot use. The loopback listener binds a REGISTERED redirect_uri. iam compares redirect_uri by exact string and does not apply RFC 8252 §7.3 port-agnostic loopback matching, so a CLI cannot pick a free ephemeral port — the previous code bound 8399 (cli) and 8398 (bot), neither registered, and /authorize refused both with a bare 400 before the user saw a login page. It listens on 127.0.0.1 and ::1 because the registered URIs spell "localhost", and sets SO_REUSEADDR because the previous callback sits in TIME_WAIT for ~60s and would otherwise EADDRINUSE a second login on a port nothing is using. hanzo_iam.store — keyring first, else an atomic 0600 file. The old path did write_text() then chmod(0600), so with umask 022 a bearer token sat at 0644 between the two calls. Now it is created private and renamed into place. The PaaS session cache goes through the same writer. Two definitions of IAMConfig existed; models.py's was the one every client imported, so the endpoint properties main had just fixed on config.py's were dead code. One now, in config.py, plus jwks_uri and device_endpoint. The bot and CLI login copies are gone; both call the one flow. whoami and `hanzo bot login` no longer print claims from an unverified decode — that rendered an attacker-chosen identity as fact. The Team tool refuses instead of pretending: api.hanzo.ai/team is the marketing SPA (200 text/html), so raise_for_status() passed and .json() blew up. There is no Team API; HANZO_TEAM_URL stays as the seam for when one ships. PaaS token exchange reports its real diagnosis: POST /v1/auth/login 404s while /v1/org and /v1/user 401, so the API is up and gated but the exchange route is not deployed at that edge. No client-side workaround exists, so it says so. Tests fail against the old behaviour, which is the point: 5 session tests fail against the shipped is_authenticated and 4 store tests fail against write-then-chmod. 134 pass alongside main's KMS suite. Co-authored-by: Hanzo Dev <[email protected]>
Hanzo Python SDK
The flagship Python SDK for the Open AI Cloud — models, agents, tools, memory, and MCP in one install.
This is the most complete Hanzo SDK — a uv workspace of 60+ composable packages
covering the full AI surface: the typed cloud client, an agent framework, the
Model Context Protocol server and tools, persistent memory + RAG, distributed
compute, and a batteries-included CLI. If you build AI in Python, start here.
Install
pip install hanzo # flagship: CLI + agents + MCP + client
Or install exactly what you need:
pip install hanzoai # just the typed cloud API client
pip install "hanzo[all]" # everything, including optional extras
Quickstart
from hanzoai import ApiClient, Configuration, AiOpenAICompatibleApi
from hanzoai import AiChatCompletionRequest, AiChatMessage
config = Configuration(host="https://api.hanzo.ai", access_token="sk-...")
with ApiClient(config) as client:
ai = AiOpenAICompatibleApi(client)
resp = ai.ai_create_chat_completion(
AiChatCompletionRequest(
model="zen-coder",
messages=[AiChatMessage(role="user", content="Ship it.")],
)
)
print(resp.choices[0].message.content)
Every route is https://api.hanzo.ai/v1/<service>/*. Models come from the Zen
family (our own models) plus any provider you connect — one typed client, no proxy
in the middle.
Packages
The workspace splits cleanly by concern. The headline packages:
| Package | Purpose |
|---|---|
hanzoai |
Typed cloud API client (generated from the Hanzo OpenAPI surface). |
hanzo |
The hanzo CLI + runtime that ties everything together. |
hanzo-mcp |
Model Context Protocol server — discovers tools via entry points. |
hanzo-agents / hanzo-agent |
Agent framework — build and orchestrate agents and swarms. |
hanzo-network |
Distributed AI compute and node orchestration. |
hanzo-memory |
Persistent memory + RAG (SQLite, optional vector backends). |
hanzo-tools-* |
60+ single-concern tool packages (shell, browser, fs, code, vector, iam, …), each exposing a TOOLS list. |
python-sdk/
└── pkg/
├── hanzoai/ # typed cloud client (OpenAPI-generated)
├── hanzo/ # CLI + runtime meta package
├── hanzo-mcp/ # MCP server (entry-point tool discovery)
├── hanzo-agents/ # agent framework
├── hanzo-network/ # distributed compute
├── hanzo-memory/ # memory + RAG
└── hanzo-tools-*/ # composable tool packages
CLI
pip install hanzo gives you the hanzo command:
hanzo chat # chat with the Zen models
hanzo node # start / manage a local compute node
hanzo mcp # run the MCP server for your editor or agent
hanzo agent # build and run agents
hanzo run # run a workflow
hanzo cloud # manage cloud resources
hanzo search # AI-powered search
Run hanzo --help for the full command tree.
Model Context Protocol (hanzo-mcp)
hanzo-mcp hosts the MCP server and discovers tools through
[project.entry-points."hanzo.tools"], so any installed hanzo-tools-* package
lights up automatically.
from hanzo_mcp import create_mcp_server
server = create_mcp_server()
server.register_tool(my_tool)
server.start()
Agents (hanzo-agents)
from hanzo_agents import Agent, Swarm
agent = Agent(
name="researcher",
model="zen-coder",
instructions="You are a research assistant.",
)
swarm = Swarm([agent])
result = await swarm.run("Research quantum computing.")
Network (hanzo-network)
from hanzo_network import LocalComputeNode, DistributedNetwork
node = LocalComputeNode(node_id="node-001")
network = DistributedNetwork()
network.register_node(node)
Memory (hanzo-memory)
Persistent memory and RAG backed by SQLite, with optional vector search
(sqlite-vec, lancedb, kuzu). Global state lives in ~/.hanzo/; per-project
state in .hanzo/.
from hanzo_memory import MemoryService
memory = MemoryService()
await memory.store("key", "value")
result = await memory.retrieve("key")
Development
This is a uv workspace.
git clone https://github.com/hanzoai/python-sdk.git
cd python-sdk
uv sync --all-packages # install the whole workspace
uv run pytest tests/ -v # run tests
make lint # ruff lint
make format # ruff format
make type-check # mypy / pyright
Per-package work:
uv run pytest pkg/hanzo-mcp -v
cd pkg/hanzo && uv build
Configuration
HANZO_API_KEY=your-api-key
HANZO_BASE_URL=https://api.hanzo.ai
HANZO_LOG_LEVEL=INFO
Or ~/.hanzo/config.yaml:
api:
key: your-api-key
base_url: https://api.hanzo.ai
logging:
level: INFO
Security
- Transport is TLS 1.3+. Secrets belong in a KMS, never in source or plaintext.
- SOC 2 audit in progress; HIPAA BAA available.
Report vulnerabilities to [email protected]. See SECURITY.md.
Contributing
Contributions welcome — see CONTRIBUTING.md. Use type hints,
add tests for new behavior, and run make lint before opening a PR.
License
Apache License 2.0 — see LICENSE.
Support
- Docs: docs.hanzo.ai
- Issues: github.com/hanzoai/python-sdk/issues
- Email: [email protected]
Hanzo — the Open AI Cloud
Open source · every language · on-chain settlement. hanzo.ai · docs.hanzo.ai
SDKs in every language — Python (flagship) · TypeScript · Go · Rust · C++ · Swift · Kotlin · umbrella