Commit Graph
629 Commits
Author SHA1 Message Date
zeekay aa6aa95656 hanzo-mcp: search, ranked code spans from a local index
One call answers a question or a failing test's output with the spans
of the tree most likely to hold the cause, as path:start-end and their
lines, so an agent stops chaining grep and read.

Python is chunked by ast, other text in overlapping 40-line windows.
BM25 over code tokens is fused by reciprocal rank with the spans a
traceback's path:line frames name; when the query names a test file
the index holds, the code under test ranks first. Every search stats
the tree and re-chunks only what moved.

A dense stage (Qwen3-Embedding form, vectors cached by text hash) is
on only when HANZO_SEARCH_MODEL names a model: on the 42 dev tasks it
lifts span MRR .385 to .508, but a CPU query embedding takes seconds.

`python -m hanzo_search.server --root DIR` serves the tool alone over
stdio with the standard library, outside hanzo_mcp, whose import is
seconds long.

The `hanzo` mode lists it, so the default surface carries it; another
mode, `tool disable search` or HANZO_MCP_ENABLED_TOOLS decide it as they
decide every other tool.
2026-10-12 08:51:36 -07:00
zeekay 7374804405 hanzo-mcp 0.15.21: the active mode is the whole manifest, and a flood of output has a ceiling
The default surface was 34 tools, 61 KB, ~15,300 tokens of schema in every
prompt of every agent. The mode system already names what a session should
carry, but only the tools register_all_tools enumerates obeyed it: zsh,
curl, playwright and the helpers (ps, npx, uvx, jq, wget, cdp, open) stayed
on in every mode. A mode now decides every discovered tool, the `hanzo`
mode lists what a coding agent calls every few steps (18 tools, 32 KB,
~8,000 tokens), and the rest is a name away: HANZO_MCP_ENABLED_TOOLS=a,b
or =all, `tool enable`, or `mode`.

hanzo-tools-shell 0.6.6: run_shell kept every byte a foreground command
wrote until the command ended. `yes` for thirty seconds is gigabytes. It
keeps the first 16 MiB per stream and drains the rest.

The tests' session-wide patch of asyncio.create_subprocess_exec handed
every shell command a stream that never reached EOF; the first test to run
one read it in a loop until the machine was out of memory (22 GB, killed).
The patch is now opt-in and its process has exited.

bench/latency.py measures the cold start, the tools/list bill and the common
calls through a real MCP client over stdio. Interleaved rounds, wall and CPU
seconds, p50 over 6 cold starts:

                         cold wall  cold CPU  tools  bytes   ~tokens
  installed 0.15.18      9.34 s     10.46 s   30     52,601  13,150
  main 0.15.20           1.52 s      1.13 s   34     61,287  15,321
  this                   0.96 s      1.06 s   18     32,222   8,055

The installed environment's 9 s is its hanzoai 8.5.156, whose package init
imports the whole generated cloud client; hanzo-mcp >= 0.15.20 requires
hanzoai >= 8.5.624, which loads it lazily. Per-call latency is spawn-bound
(zsh 17.6 ms, git status 33 ms, fs.read 2.5 ms at p50) and moves with the
machine's load; no call path changed.
2026-10-12 08:47:57 -07:00
zeekayandHanzo Dev cfad648f7f client: regenerate from hanzo-inc/cloud@ce4f33a2bf (8.5.719)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-12 08:42:45 +00:00
zeekayandHanzo Dev 496288eced client: regenerate from hanzo-inc/cloud@b9a5b7ab54 (8.5.717)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-10 20:22:45 +00:00
zeekayandHanzo Dev 0bb26a5c5c client: regenerate from hanzo-inc/cloud@1d19c5f527 (8.5.714)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-10 11:43:59 +00:00
zeekayandHanzo Dev ddab78c22f client: regenerate from hanzo-inc/cloud@95d9f810dd (8.5.704)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-09 23:48:10 +00:00
zeekayandHanzo Dev 527ba6ae03 client: regenerate from hanzo-inc/cloud@ffdcf5c42d (8.5.699)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-09 19:09:02 +00:00
zeekayandHanzo Dev 4ffcf2bb1a client: regenerate from hanzo-inc/cloud@1e531e42c6 (8.5.655)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-05 16:15:26 +00:00
zeekayandHanzo Dev 40850a752c client: regenerate from hanzo-inc/cloud@e693e1249c (8.5.653)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-05 15:33:21 +00:00
zeekayandHanzo Dev 924fba7a5e client: regenerate from hanzo-inc/cloud@8c559683df (8.5.646)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-05 09:34:03 +00:00
zeekayandHanzo Dev 18df31c7e1 client: regenerate from hanzo-inc/cloud@963ad356aa (8.5.640)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-05 06:13:52 +00:00
zeekay 54e7f4fa2b feat(hanzo-mcp): repo joins the default surface, on llm limits and typed plan refusals 2026-10-04 16:54:10 -07:00
zeekay e315b45181 feat(hanzo-tools-vcs): repo lists Hanzo git repositories and syncs one now 2026-10-04 16:53:26 -07:00
zeekay 34082fa2ca feat(hanzo-tools-llm): llm limits, searched models, served and paid-by, typed plan refusals 2026-10-04 16:50:55 -07:00
zeekay 10a9562e1d feat(hanzoai): a usage refusal names its spent window and reads the /v1 envelope 2026-10-04 16:46:07 -07:00
zeekay d5daeda70e feat(hanzo-tools): a cloud refusal carries its body and headers, send returns headers 2026-10-04 16:40:55 -07:00
zeekay 4240afb736 feat(hanzoai): typed usage-limit errors and read_usage 2026-10-04 16:37:35 -07:00
zeekay 259f4d2fa4 fix(client): regenerate 8.5.623 with the current python driver 2026-10-04 16:23:43 -07:00
zooqueenandHanzo Dev 5c0cc67e39 Put llm and kai_decide on the default hanzo-mcp surface
Every mode disables a registered tool it does not list, and llm was
listed nowhere but HEAVY_TOOLS, written when it pulled a 100 MB client.
It is an HTTP call to api.hanzo.ai now, so llm and kai_decide join the
essential set every personality carries, and the llm package maps to the
two tools it ships.

hanzo-mcp 0.15.19, on hanzo-tools 0.3.8 and hanzo-tools-llm 0.2.1.

Co-authored-by: Hanzo Dev <[email protected]>
2026-10-04 13:55:32 -07:00
zooqueenandHanzo Dev 034e1a6caf Route llm through api.hanzo.ai with Enso, and add kai_decide
The llm tool called upstream providers through the litellm fork, read
provider keys, and never reached the MCP surface: its register() was a
no-op and it skipped BaseTool's constructor. Kai had no Python tool.

hanzo-tools-llm now carries two HIP-0300 tools over HanzoCloud:
- llm (default action query): POST /v1/chat/completions with model
  enso-auto unless named, "auto" for Enso's pick, max_cost and
  max_latency_ms as X-Max-Cost and X-Max-Latency-Ms, returning id and the
  model that served; models: GET /v1/models, one row per model with the
  catalog's per-million prices, narrowed by family; feedback: POST
  /v1/ai/feedback with request_id, signal and a 1-3 rating.
- kai_decide (default action decide): POST /v1/decisions with the
  TypeScript tool's schema and shape checks.

The consensus, llm_manage and provider modules wrapped the upstream
client and were never registered, so they go, with hanzo-consensus and
the [full] extra. hanzo-tools-llm 0.2.1.

Co-authored-by: Hanzo Dev <[email protected]>
2026-10-04 13:55:32 -07:00
zooqueenandHanzo Dev e0897353ce Let HanzoCloud carry request headers, and a tool name its default action
Enso reads its routing bounds from X-Max-Cost and X-Max-Latency-Ms on
/v1/chat/completions, and HanzoCloud could send no header but its own, so
no tool could bound a routed request. post() and the shared request path
now take extra headers that ride beside the bearer.

BaseTool runs `help` for a call that names no action. A tool with one
obvious act (llm's query, kai_decide's decide) now declares
DEFAULT_ACTION, which the call, the advertised schema's default and its
required list all read, so it answers the same no-action call the
TypeScript and Rust runtimes do. Every other tool keeps `help`.

hanzo-tools 0.3.8.

Co-authored-by: Hanzo Dev <[email protected]>
2026-10-04 13:55:31 -07:00
zeekayandHanzo Dev 10f2ac7769 client: regenerate from hanzo-inc/cloud@97eb976f07 (8.5.617)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-04 09:22:49 +00:00
zeekayandHanzo Dev dc0ca692a7 client: regenerate from hanzo-inc/cloud@60477ef158 (8.5.607)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-04 08:26:01 +00:00
zeekayandHanzo Dev 454e3bfcd6 client: regenerate from hanzo-inc/cloud@45a5d493c1 (8.5.604)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-04 04:17:37 +00:00
zeekayandHanzo Dev babd327276 client: regenerate from hanzo-inc/cloud@1e6d2a97de (8.5.583)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-03 23:19:03 +00:00
zeekayandHanzo Dev 64ef70f33a client: regenerate from hanzo-inc/cloud@8edb6f1601 (8.5.578)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-03 17:19:13 +00:00
zeekayandHanzo Dev be5f1dff52 client: regenerate from hanzo-inc/cloud@470b2430b9 (8.5.550)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-03 09:46:59 +00:00
zeekayandHanzo Dev 608ba2fd52 client: regenerate from hanzo-inc/cloud@1a819b53e3 (8.5.547)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-03 05:59:55 +00:00
zeekayandHanzo Dev 2dd8c6f865 client: regenerate from hanzo-inc/cloud@d62601149e (8.5.539)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-03 00:41:54 +00:00
zeekayandHanzo Dev e6d3482d1a client: regenerate from hanzo-inc/cloud@12a3344d6d (8.5.533)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-02 21:44:16 +00:00
zeekayandHanzo Dev 010e046347 client: regenerate from hanzo-inc/cloud@5075921e62 (8.5.528)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-02 18:48:48 +00:00
zeekayandHanzo Dev 4b762d3a59 client: regenerate from hanzo-inc/cloud@9b7cbb92dc (8.5.511)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-02 02:25:10 +00:00
zeekayandHanzo Dev f6cc56fbc0 client: regenerate from hanzo-inc/cloud@fb00f045f9 (8.5.509)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-02 01:44:08 +00:00
zeekayandHanzo Dev 0d006d27b4 client: regenerate from hanzo-inc/cloud@b852e740ce (8.5.506)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-01 23:54:56 +00:00
zeekayandHanzo Dev c23f20cbaf client: regenerate from hanzo-inc/cloud@9b7f663b28 (8.5.505)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-01 23:08:56 +00:00
zeekayandHanzo Dev d019bb67d4 client: regenerate from hanzo-inc/cloud@0a52a53e66 (8.5.500)
Co-authored-by: Hanzo Dev <[email protected]>
2026-10-01 19:39:01 +00:00
zeekay 32ce9a8595 Answer with the connected browser's error, never a Playwright stand-in
In auto mode any extension error fell through to headless Playwright, so
a slow page in the user's own browser came back as Playwright's missing
executable. A connected browser's failure is now the answer; Playwright
stands in only when no browser is on the router. hanzo-tools-browser
0.5.16.
2026-09-30 21:49:37 -07:00
zeekay 4c0e40351c hanzo-kai reads /v1/models pricing as per-token prompt and completion strings plus per-million rates under keys that name the unit. 2026-09-30 14:56:35 -07:00
zeekay cac19547ff hanzo-tools-browser 0.5.15 2026-09-30 14:54:29 -07:00
zeekay fedeec8d70 Regenerate the cloud client from v8.5.491
The release lane regenerates pkg/hanzoai/cloud from the served document
and refuses to publish when the committed client differs; it had drifted
since v8.5.458, so every python-sdk tag, hanzo-mcp and
hanzo-tools-browser included, stopped at that check. The lock now names
v8.5.491 and the hash api.hanzo.ai serves.
2026-09-30 14:54:13 -07:00
zeekay 58801c6ccc Register the native host with Firefox as well
Firefox reads its host manifests from ~/.mozilla/native-messaging-hosts
(Mozilla/NativeMessagingHosts on macOS) and names the extension by its
gecko id in allowed_extensions, not an origin. With the extension now
asking for nativeMessaging, Firefox joins the router without a pairing
code, as Chromium does. hanzo-tools-browser 0.5.14.
2026-09-30 12:32:10 -07:00
z e629d5e539 A browser joins the ZAP router through native messaging
hanzo-zap-host is the Chrome native messaging host ai.hanzo.zap: it relays
router envelopes between the extension's stdio and the router's unix socket
and stands for the router itself. hanzo-mcp registers it with every installed
Chromium-family browser on start. hanzo-tools-browser 0.5.13.
2026-09-30 10:17:05 -07:00
zeekay 2b672025db client: regenerate from hanzo-inc/[email protected] (8.5.164)
The served document moved past the one this projection was generated
from, so the client lane's check failed every release. uv.lock follows
hanzo-tools-browser onto zapd.
2026-09-30 07:13:42 -07:00
zeekay ad606c7611 The browser tool takes a seat on the embedded ZAP router
hanzo-mcp said HELLO as consumer:hanzo-mcp/<pid> to a separate zapd
daemon, which the router that replaced it refuses as bad_id. Every
process that speaks ZAP now embeds the router (zapd 1.1 on PyPI) and the
kernel lock elects one; hanzo-mcp stands for it at startup and takes the
seat mcp/hanzo-<pid>. A browser is a browser/<host>/<name> node, found
by engine or name and reached with Node.call, and the extension's
command codec is unchanged.

hanzo-mcp pair prints the code the extension's popup takes. The daemon
spawn and socket probing are gone, and so is the zap-proto dependency.
2026-09-30 06:56:52 -07:00
zeekay 379600adc8 client: regenerate from hanzo-inc/cloud@2774987ccc (8.5.164)
The client lane checks the committed client against the document
api.hanzo.ai serves now, not against the ref .spec-lock pins. Cloud
v1.833.257 (2774987c) went live after 8.5.163 was cut and rewrote the
POST /v1/decisions description (cad76e1a0: Jev by OpenRouter's vendor
ids), so the served document moved from 296b1f84 to 783f66a3 and every
push since has failed the check on ai_api.py's docstring. The drift
reproduces identically at e34ab7e1; nothing in this tree caused it.

This is the lane's own move, done by hand: .spec-lock names the served
revision and digest, the generator reruns over those bytes, uv.lock
takes what `uv run` wrote, and the version takes the next patch.
2026-09-29 13:53:29 -07:00
zeekay 1b4cfa4c16 devserver passes browser options through args
The browser tool types only its core fields; everything else rides in
`args`. navigate's state and console's level sat at the top level,
where the schema drops them, so errors opened the app without waiting
for networkidle and read every console message rather than errors.
The test's stand-in browser now has the real tool's shape.

Claude-Session: https://claude.ai/code/session_01Dc8DXc3fEQCRHUa6XWK4Ku
2026-09-29 13:06:50 -07:00
zeekay 450f65932d browser teaches the snapshot loop in its description and the rest on help
The tool put ~120 actions in a Literal enum and ~60 parameters in its
schema, all of it in every agent's context, and most of it Playwright
locator builders that returned a count. Three hand-kept lists (the enum,
extension_actions, _zap_method_for) disagreed: snapshot, wait and
select_option routed to the extension under names it did not answer.

ACTIONS is now the one table: each action once, its topic, its usage
line, and the extension method that serves it. The schema carries the
core loop (navigate, snapshot, click, fill, type, press, read,
screenshot, evaluate, wait, tabs, help) with an open `action` string;
help prints the rest by topic, and their parameters travel in `args`.

snapshot, click-by-ref and friends go to the extension's page engine
(hanzo.snapshot / hanzo.act / hanzo.read / hanzo.wait / hanzo.navigate,
screenshot annotate=true to hanzo.annotate). A tree or a page answers as
text; a refusal ("Stale ref @e4 … run snapshot again", "covered by
<div#consent>") is the error, not a success wrapping "ERR:", and is
never retried on Playwright, which has no refs to act on.

Aliases and no-ops are gone: select_option (select), get_inner_text and
get_value (get_text), clear (fill), content (get_html), new_page
(new_tab), press_key, mouse_wheel (scroll), list_browsers and
list_mcp_instances (browsers), use_browser, set_default_browser,
claim/release_browser, the locator/get_by_*/first/last/nth/filter/all
builders, is_hidden, frame, main_frame, on and off.

Claude-Session: https://claude.ai/code/session_01Dc8DXc3fEQCRHUa6XWK4Ku
2026-09-29 13:05:21 -07:00
zeekay 82a090d0ba hanzo-tools and hanzo-mcp stay on mcp 1.x
hanzo_tools.core imports mcp.server.FastMCP, which mcp 2 removed, so a
fresh resolve that picks mcp 2.2.0 fails at import. The published
hanzo-mcp 0.15.17 already declares mcp<2; the tree now says the same,
and hanzo-tools, which owns the import, says it too.

Claude-Session: https://claude.ai/code/session_01Dc8DXc3fEQCRHUa6XWK4Ku
2026-09-29 12:41:52 -07:00
zeekay af78255a39 devserver: running dev servers as MCP nodes
An agent editing a web app needs the dev server's own view of it: which
routes exist, and whether the last edit broke the build. Next 16 serves
that over MCP at /_next/mcp, and Vite-based frameworks can with
vite-plugin-mcp, but nothing here found those endpoints.

index finds Next, Vite, Nuxt, Remix, React Router, Astro and SvelteKit
dev servers from each process's argv, cwd and listening ports, and lists
the tools of the MCP each serves. call runs one. errors returns current
build and runtime errors: Next's get_errors, after opening the app in the
browser tool, since Next reports only what a connected session reported;
otherwise Vite's error overlay, console errors and page errors, read
through the same browser tool.

discover() returns the servers as plain records, the shape a devserver
node will take. hanzo-mcp depends on the package so the tool ships.

Claude-Session: https://claude.ai/code/session_01Dc8DXc3fEQCRHUa6XWK4Ku
2026-09-29 12:37:53 -07:00
zeekay 351b1a2b1b mcp_proxy reaches running servers over HTTP and SSE
MCPServerConnection only spawned stdio servers, so an endpoint a process
already serves - Next 16's /_next/mcp, vite-plugin-mcp's /__mcp/sse - had
no client in this repo.

MCPServerConfig now takes a url. connect lists the tools and call_tool
calls one through the SDK's streamable HTTP or SSE client, a session per
request, and returns the same JSON-RPC result the stdio path returns. A
loopback URL skips TLS verification, since a local dev certificate is
self-signed. A failed connect says why in .error.

Claude-Session: https://claude.ai/code/session_01Dc8DXc3fEQCRHUa6XWK4Ku
2026-09-29 12:37:53 -07:00