The guard allowed anything its few patterns missed whenever Kai's choice said allow, and kai-1.2 reads command risk near chance: on hermes-jev-skills' 118 gate fixtures (MIT) it allowed 12 of the 65 commands that must be denied or asked (rm -rf ~/Documents, chmod -R 777 ~, base64 -d | sh) and asked or denied 33 of the 53 ordinary ones. Now deny and ask are classes matched on the whole line (wipe, pipe_shell, exfiltrate, secret_read, permissions, clobber, disk, metadata, drop_data, claimed_approval; git_remote, git_discard, publish, deploy, service, message, privilege, global_install, sync_delete, persistence, environment, parent_path), and allow needs every part of the line to be inspection, a build or test, local git, or a file change under a relative path or /tmp. Anything else asks. The caller's policy is a floor. No Kai request: its risk questions changed no outcome on the fixtures but one more ask. Frozen and read once on the fixtures: 1 of 65 allowed, 18 of 53 asked. Two classes were fixed after that read and are not held out: the metadata-endpoint fetch (the one allowed command) and a bare `rm -rf /`, which asked where it must deny. With them: 0 of 65 allowed.
@hanzo/mcp
An MCP server for coding agents: files, shell, code, git, HTTP, project context
and UI components, plus tools that call the Hanzo API. It speaks MCP over stdio,
or over streamable HTTP with --transport http.
Register it with an agent
The package declares Node 18 or newer. For Claude Code:
claude mcp add hanzo -- npx -y @hanzo/mcp serve
npx -y @hanzo/mcp install --claude-code runs that command with --scope user,
which registers the server for every project.
claude mcp list then reports hanzo: npx -y @hanzo/mcp serve - ✔ Connected.
With --scope project the entry goes into the project's .mcp.json instead,
and Claude Code asks before it starts a server from that file. Other MCP clients
take the same command in their server config:
{
"mcpServers": {
"hanzo": {
"command": "npx",
"args": ["-y", "@hanzo/mcp", "serve"]
}
}
}
The first start downloads the package; with an empty npm cache that took 80 to 100 seconds.
Tools
serve with no flags offers these, in the groups src/tools/unified/index.ts
defines:
| group | tools |
|---|---|
| core | fs, exec, code, git, fetch, workspace, ui |
| optional | think, memory, hanzo, plan, tasks, mode, gimp |
| code intelligence | code_search, code_context, code_ask, code_index |
| tracker | tracker_boards, tracker_issues, tracker_create, tracker_update |
| kai | kai_decide, kai_choice, kai_score, kai_noul, kai_tool_select, kai_next_action, kai_command_guard, kai_injection_screen, kai_extract, kai_log_triage, kai_recover, kai_agent_step |
The core and optional tools take an action argument that picks the operation;
ui takes method. The code intelligence, tracker and kai tools call
https://api.hanzo.ai with the key in HANZO_API_KEY, or a local Kai instance via
LOCAL_KAI_URL (e.g. http://127.0.0.1:18977).
The kai tools ask Kai, Hanzo's decision model, typed questions about a case
(POST /v1/decisions) and answer with calibrated probabilities: kai_choice
picks a label, kai_score an ordinal level, kai_noul the probability that a
statement holds, and kai_decide asks up to 100 of them in one call.
Beside the general decision primitives, 8 purpose-built native MCP tools provide fast-path zero-inference execution and targeted decision capabilities:
kai_tool_select: Shortlist tools from registered schemas and resolve client naming differences (e.g.bash→Bash).kai_next_action: Recommend an executable next Action (type,target,payload) or calibrately abstain.kai_command_guard: Shell command safety policy enforcement on monotonic latticeallow < ask < deny.kai_injection_screen: Screen untrusted inputs or PR comments for prompt-injection / instruction overrides.kai_log_triage: Classify compiler and test failures and pinpoint exact error spans.kai_recover: Loop breaker and strategy recovery engine from failure fingerprints.kai_extract: Typed discrete entity extraction across structured dimensions.kai_decide: Joint multi-question evaluation in a single model forward pass.
See the Kai Cookbook for 15 runnable recipes and benchmarks across Claude Code, Cursor, Codex, and Hanzo Dev.
instructions is optional on every question and recommended. A kai_choice
call:
{ "state": "I was charged twice for my March invoice. Please refund the duplicate.",
"instructions": "Which team should handle this ticket?",
"criteria": { "billing": "charges, invoices and refunds", "product": "how to use the product" } }
hanzo reaches every Hanzo Cloud subsystem: resource names one, action one
of its operations, args that operation's arguments. With no action it lists
the resource's operations, and resource: "describe" with args: {subsystem, op}
says what one takes. The list is generated from cloud's catalog.
{ "resource": "graph", "action": "graphResolve",
"args": { "entity": "acme/svc/api", "relation": "owner", "as_of": "2026-09-01T00:00:00Z" } }
Flags on serve change the set:
| flag | effect |
|---|---|
--disable-tools plan,tasks |
removes the named tools |
--core-only |
only the core group |
--enable-ui |
adds the ui_* component tools |
--enable-desktop |
adds hanzo_desktop and playwright_control |
--enable-autogui, --enable-orchestration, --enable-ui-registry, --enable-github-ui, --enable-community-cryptuon |
add those tool sets |
--full-surface |
adds the UI, AutoGUI, orchestration, UI registry and GitHub UI sets together |
npx -y @hanzo/mcp serve --help lists every flag. This prints the tool names a
server offers; put any flags after serve:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| npx -y @hanzo/mcp serve 2>/dev/null \
| node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{for(const l of s.split("\n"))if(l.includes("\"id\":2"))console.log(JSON.parse(l).result.tools.map(t=>t.name).join(" "))})'
Over HTTP
npx -y @hanzo/mcp serve --transport http --port 3000
The endpoint is the root path, http://127.0.0.1:3000/. POST JSON-RPC to it
with Accept: application/json, text/event-stream; answers come back as
server-sent events.
When a client shows no tools
Start the server by hand. A working one answers initialize with its name and
version:
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}' \
| npx -y @hanzo/mcp serve
With HANZO_MCP_DEBUG=1 in the server's environment, stderr also shows its
working directory, the resolved binary, its arguments and the start of PATH.
Python
hanzo-mcp on PyPI is a separate server, built from
hanzoai/python-sdk.
Development
pnpm install
pnpm build
pnpm smoke # runs dist/cli.js over stdio: initialize, tools/call, resources
License
MIT