Files
python-sdk/examples/tools/__main__.py
T
zeekayandHanzo Dev 3a62c22256 regen the cloud client from the resynced spec (1f9b03b) and repoint every flow
The document grew: cloud's woven spec was merged and won, taking hanzo.yaml from
1132 paths / 1519 operations / 779 schemas to 1737 / 2452 / 1798.
pkg/hanzoai/cloud/ is now 263 api + 2031 model modules, all importing.
`generate.py python --check` reports [python] clean.

The resync also RENAMED nearly every operationId to cloud_<method>_<path>, which
renames every generated method: billing_billingBalance became
cloud_get_v1_billing_balance, cloud_AgentsController.Create became
cloud_post_v1_agents. Examples written against the old names stop resolving —
exactly what the examples gate exists to catch. It went red; this is the fix.

Every flow was re-probed against api.hanzo.ai unauthenticated and with a bogus
key, because a spec says what SHOULD be served and only a request says what IS.
All eleven operations answer 401/403 — routed and identity-gated. Two moved:

  store  to the PROVISIONING plane (POST /v1/kv, GET|DELETE /v1/kv/{name}). The
         per-key data plane the spec also describes is mounted nowhere: GET
         /v1/kv/keys/{key} 404s, PUT and DELETE 405, kv.hanzo.ai 404s the whole
         prefix. A round-trip on keys could not run.
  tools  to GET /v1/tools, the catalog. A live JSON-RPC door at POST /v1/mcp
         answers tools/list with 730 tools but is absent from hanzo.yaml, so the
         generator emits no method and an example would have to bypass the SDK
         to reach it. Of the declared MCP routes /v1/automations/mcp returns 405.

hello stays on bot_authMe: /v1/ai/account answers 200 with
type="anonymous-user" to a request with NO Authorization header, so a hello
built on it certifies a key that would 401 everywhere else.

All six run and report the server's own refusal for a bogus key:
  hello  403 no validated principal      store  403 X-Org-Id required
  chat   401 API key validation failed   agent  403 X-Org-Id required
  money  401 sign in to view billing     tools  403 a validated principal required

money goes through the generated *_without_preload_content variant, because its
two operations are declared with a `default` response and no content so the
typed methods return None though the server sends JSON — a spec gap, and not a
small one: 696 of 2425 operations model no response body. That raw variant does
NOT raise on 4xx (the check lives in the typed deserialization these lack), so
the example checks status itself; without it a 401 body printed as the balance.

3.1.5 -> 3.1.6. 3.1.5 is live on PyPI, so this is a real patch above it.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-31 09:08:30 -07:00

37 lines
1.3 KiB
Python

"""tools — list the tools this key can reach.
``GET /v1/tools`` (operationId ``cloud_get_v1_tools``), the catalog behind the
MCP surface: each entry is a tool name, its description and its input schema.
A note on the MCP door, because it is easy to pick the wrong one. There is a
live JSON-RPC endpoint at ``POST /v1/mcp`` that answers ``tools/list`` with the
same catalog (730 tools at the time of writing) — but it is NOT in hanzo.yaml,
so the generator emits no method for it and an example would have to bypass the
SDK to call it, which defeats the point of an SDK example. Of the MCP routes
that ARE declared, ``/v1/automations/mcp`` returns 405 at api.hanzo.ai. So this
catalog read is the one that is both generated and served. When ``/v1/mcp`` is
added to the spec, this flow should move to it.
python -m examples.tools
"""
from hanzoai.cloud import ToolsApi
from examples.client import client, run
def main() -> None:
with client() as api:
catalog = ToolsApi(api).cloud_get_v1_tools()
tools = catalog.tools or []
print(f"{len(tools)} tools")
for tool in tools[:20]:
print(f" {tool.name} — {tool.description or '(no description)'}")
if len(tools) > 20:
print(f" … and {len(tools) - 20} more")
if __name__ == "__main__":
run(main)