HIP-0300 names llm among the optional tools and Python and Rust carry it,
but the TypeScript server had none, so an agent on @hanzo/mcp could ask
Kai a typed question and could not reach a model.
llm routes by action over api.hanzo.ai:
- query: POST /v1/chat/completions, model enso-auto unless named; "auto"
lets Enso pick. max_cost and max_latency_ms ride as X-Max-Cost and
X-Max-Latency-Ms. It returns the model that served (X-Routed-Model, else
the body's model) and the completion id feedback takes.
- models: GET /v1/models, each model's family, class and per-million
price as the catalog states it; family narrows to enso or kai.
- feedback: POST /v1/ai/feedback with request_id, signal and a 1-3
rating; an envelope that says error is an error.
Co-authored-by: Hanzo Dev <[email protected]>
exec gains run(): an argv with no shell, waited on up to a deadline, then
left running in exec's process table. npx and uvx use it with Python's argv
and a two-minute deadline; jq passes its filter as one argv word.
@hanzo/mcp 2.4.14
read returns numbered lines and the full sha256 hash, write only creates,
apply_patch takes old_text and new_text under base_hash, list walks with
depth, pattern and cursor, patch takes the Rust grammar, and search_text
takes a pattern and glob. rg now runs without a shell, so a pattern is never
a shell word.
@hanzo/mcp 2.4.13
src/zap.ts is a consumer seat on the unix socket of whichever process the
lock elected: zapd has no JS or wasm build, so this runtime joins as
mcp/hanzo-<pid> and never stands for router. browser and cdp carry Python's
action names, parameters and extension methods; headless Playwright covers
the core, navigation and tabs when no browser is registered. The test runs
PyPI zapd as the router in another process, with a fake extension.
@hanzo/mcp 2.4.12
Every suite had stopped executing. ts-jest peers on `typescript >=4.3 <7`
and this repo builds against 7, so all twenty files died inside ConfigSet
with "Cannot read properties of undefined (reading 'fileExists')" — twenty
failures, zero tests, and nothing to read them by because the push gate
deliberately runs typecheck and build rather than jest.
swc transforms instead: it strips types rather than asking the compiler
to, so it has no TypeScript version to disagree with, and `tsc --noEmit`
already answers the question ts-jest was answering twice.
Running as real ESM then exposed what the CommonJS fallback had hidden.
jest.mock does not hoist there, so a static import binds the real module
and the suite fails on `mockReturnValue is not a function` — an absent
mock reported as a broken one. The search, autogui and ui suites now
register through unstable_mockModule and import after it. One of them had
never mocked fs at all: it cast fs.promises to a mocked type and called
mockImplementation on the real module.
384 tests, twenty suites, all passing.
The seven resources this offered had drifted from the fleet it names: paas is
platform, storage is s3, auth folded into iam, and iam alone carries 97
operations against the handful the hand-written tool reached. It also dialled
four hosts for one API.
The names come from cloud's typed operations now, so this client cannot come to
disagree with the API about what exists. The withholding rule is asked of cloud
rather than restated here — a client deriving its set from the raw catalog offers
what the fleet refuses.
The default surface is unchanged at 22 tools; the fleet sits behind hanzo, whose
resource enum is derived rather than listed.
The tool surface reaches a browser through zapd's native host, so the server
side of the localhost WebSocket transport has no counterpart. Removing it
takes `ws` out of the dependencies with it.
2.4.7.
`serve --transport http` answered "not yet implemented" and exited. It now
serves the streamable http transport through the SDK's StreamableHTTPServer
Transport: a fresh server and transport per request, stateless — no session
header, each POST answered on its own.
The request handlers are registered once, in registerHandlers, and both the
stdio and http paths call it. The tool surface a client sees is the same on
either transport.
An agent could already keep a todo list (`tasks`), but only on its own disk.
Nothing here reached the board a human looks at, so "what is this agent doing"
had no answer outside the agent. These four tools are the cloud /v1/tracker
surface — the ONE work-item primitive — as MCP tools.
They are deliberately NOT called tasks_*. Three planes are easy to braid and
cloud apps/tracker/contract.go is law about it: a tracker Issue is a work item,
hanzoai/tasks is durable async execution, and the local `tasks` tool is a
private file at ~/.hanzo/todos.json. Naming these tasks_* would have collided
with that third one and pointed callers at the plane nobody can see.
Linking a run needed no new field. An agent's run is a SESSION — "mission" is
not modeled anywhere in the fleet — and the tracker contract already defines
extRef as "a link INTO another plane", so `session` writes the anchor
`session:<id>` and hanzo.app renders that session's live status on the row.
tracker_create defaults source to agent, because the board's ?source=agent
filter is only true if agents say so.
Cloud's own refusal reaches the caller verbatim — "unknown status filter" is
worth more to an agent than a generic failure — and the org is never sent: it
is minted server-side from the bearer, and a tool that sends one is a tool
asking to be trusted about tenancy.
NOTE: the jest suite cannot RUN on main right now — TypeScript 7.0.2 is
installed and ts-jest 29.4.6 cannot read its compiler API, so all 18 suites
fail to load. That break predates this commit and is orthogonal to it (verified
on a clean origin/main checkout). These tools were verified twice: 20 jest tests
green under the TS 5 toolchain, and 11 runtime checks through the shipped
esbuild bundle under main's current dependency state.
Co-authored-by: Hanzo Dev <[email protected]>
TypeScript 7 is the native Go compiler; the npm package is a shim that
resolves a platform-specific native binary.
Safe here because the build does not emit through tsc — esbuild bundles
both entrypoints, so tsc is typecheck-only and the published artifact is
byte-for-byte unaffected by the compiler change. Repos that emit with
tsc are deliberately NOT being bumped in this pass.
Verified: tsc --noEmit reports 0 errors (instant), pnpm build exits 0 and
produces dist/index.js + dist/cli.js as before.
Co-authored-by: Hanzo Dev <[email protected]>
This repo committed BOTH package-lock.json and pnpm-lock.yaml — two descriptions of
one dependency tree, neither reconciled against the other. CI then ran
`npm ci || npm install`, which is the shape that lets that persist: when the lockfile
stops matching package.json, the fallback silently resolves a different tree and the
run still goes green. A lockfile nobody fails on is not a lockfile.
Now there is one. pnpm-lock.yaml is tracked, package-lock.json is deleted and
ignored along with every other manager's, corepack takes the pnpm version from
`packageManager`, and Docker and CI both install with --frozen-lockfile so a drifted
tree stops the build instead of being papered over.
The `pnpm.onlyBuiltDependencies` field in package.json was dead — pnpm 11 reads
settings from pnpm-workspace.yaml and prints a warning that it ignored it. Its five
entries moved there verbatim, documented, so the build-script denial is actually in
effect rather than merely written down.
esbuild goes ^0.27.4 -> ^0.28.1, the version console already builds with. 0.27.4's
install script overwrites its own bin/esbuild with a raw ELF, and pnpm's shim reads
a missing shebang as "run this with node", so `pnpm build` died on
`SyntaxError: Invalid or unexpected token` at the first byte. Same minor series, and
it is what makes the build work under pnpm at all.
Verified: frozen-lockfile install clean, `pnpm build` emits a 617KB bundle, 354
tests passing.
Co-authored-by: Hanzo Dev <[email protected]>
CI:
- Drop GitHub-hosted ubuntu/macOS/Windows runners per org policy
(NO GITHUB BUILDERS). All jobs now run on [self-hosted, amd-medium]
or [self-hosted, arm-medium] — native amd64 + arm64 in parallel,
no QEMU.
- 6 jobs: build/test × {amd64, arm64}, integration × {amd64, arm64},
e2e-against-published × {amd64, arm64}, security audit.
Integration test (scripts/smoke-mcp.mjs, also 'npm run smoke'):
- Replaced the initialize-only probe with a 6-step real-tool roundtrip
on one stdio session: initialize → tools/list → tools/call fs.read
on a real on-disk probe → tools/call fs.list → resources/list →
resources/read. Validates serverInfo, tool catalog membership, and
payload bodies. Local run: 6/6 pass in <1s.
This is the actual contract Claude Desktop / Code / Cursor use. If
this passes on amd64 + arm64 native, the package works.
Catches the class of bug that shipped in 2.4.1 (silent stdio hang because
'serve' was being routed to the wrong binary).
- .github/workflows/ci.yml: matrix on ubuntu / macOS / Windows × Node 20/22.
Adds a 'smoke' job that runs scripts/smoke-mcp.mjs (spawn cli.js, send
initialize, require serverInfo.name === hanzo-mcp within 20 s) on every
supported OS, and an 'e2e-npx' job that does the same against the
*published* npm package via the exact npx invocation Claude Desktop /
Claude Code generate. This is what was missing — local builds were fine,
but the published-bin routing was broken.
- scripts/smoke-mcp.mjs: cross-platform MCP-protocol smoke test, also
exposed as 'npm run smoke'.
- src/cli.ts: always-on startup banner now includes node version + platform
so support logs are useful by default. HANZO_MCP_DEBUG=1 adds cwd,
resolved CLI path, argv, and PATH prefix — surfaces 'wrong binary
shadowing hanzo-mcp' bugs immediately.
- README.md: Troubleshooting section with stdio-handshake test command,
HANZO_MCP_DEBUG setup, log paths for macOS / Windows, and the rationale
for the '--package=' arg form.
Two coordinated fixes for the Claude Desktop integration:
1. package.json: add 'mcp' bin alias pointing to dist/cli.js
so 'npx @hanzo/mcp ...' auto-resolves the binary by name
match (npx strips scope: '@hanzo/mcp' → 'mcp').
2. cli.ts: install-* generators now emit args:
['-y', '--package=@hanzo/mcp', 'hanzo-mcp', 'serve']
instead of:
['-y', '@hanzo/mcp', 'serve']
The broken form treats 'serve' as a separate package and runs
the unrelated 'serve' (static-file server) instead of hanzo-mcp,
which is why some users saw the MCP server 'hang' silently.
Affects all 8 install targets: Claude Desktop, Claude Code, Codex,
Cursor, Windsurf, VS Code, JetBrains, --all.
Bump 2.4.1 → 2.4.2.
- Add McpMethodHandler type and handleMethod option to ZAP server
- Refactor MCP handlers into reusable functions shared between MCP + ZAP
- CLI now starts ZAP server alongside stdio with full method dispatch
- All MCP methods (tools/*, resources/*, prompts/*) work identically over ZAP
- Fix TS strict mode error in code.ts summarize action
- 337/337 tests pass
Binary protocol server (9-byte header + JSON) on ports 9999-9995.
Extensions connect and discover tools via handshake, call tools via
RPC. Supports N simultaneous browser connections.
- New 'install' command with flags for different applications
- Supports --claude-desktop, --claude-code, --cursor, --vscode
- Add --all flag to install for all supported applications
- Backward compatibility with deprecated install-desktop command
- Improved server.js to default to serve command
- Version bump to 2.0.2
- Fix CLI to properly handle --version flag and serve command
- Made serve command the default command
- Added server.js as entry point wrapper
- Version bumped to 2.0.1 for npm release
- Organized test files into test/ directory
- Add comprehensive MCP_DEMO.md with setup instructions
- Add test scripts for E2E and protocol testing
- Update build configuration for better module compatibility
- Configure Claude Desktop with multiple MCP server options
## Core Implementation
- Unified search engine with strategy pattern for extensible search types
- Full OpenAI MCP specification compliance for search and fetch tools
- Multi-modal search strategies: Symbol, AST, Vector, Text, and File search
- Automatic strategy selection based on query characteristics
## Security & Remote Access
- Secure ngrok tunnel integration with automatic detection
- Multi-method authentication (Bearer token, API keys, JWT)
- Post-quantum TLS configuration support
- Rate limiting and CORS protection
- Access logging and monitoring
- Only exposes files when ngrok credentials are present
## Testing & CI
- 102 tests with 85% coverage on critical paths
- GitHub Actions CI workflow for automated testing
- Node.js 18.x and 20.x compatibility testing
- Coverage reporting to Codecov
- Security audit checks
## Documentation
- Comprehensive documentation in LLM.md
- Security guide in docs/SECURITY.md
- .npmrc excluded from repository for security
This implementation provides a production-ready search system for AI tools like ChatGPT and Claude Desktop.