34 Commits
Author SHA1 Message Date
zeekay 25c6f62cff feat(rust): hanzo-shell reads exec's command and says allow, ask or deny
A pure-Rust classifier for the `Classify` seam. brush-parser (MIT) gives the bash AST
and rustpython-parser (MIT) the Python one; every simple command, redirection,
substitution and nested script (bash -c, here-documents, a script the command wrote and
runs, python -c, ssh host '...') is read in the order the shell runs them, and the policy
over paths decides what each costs. rust/shell is its own crate with a wasm-bindgen build,
so kai-gateway can replace safety.ts with it.

exec: an ask is refused until the call carries approve: true (never lifts a deny); the
interpreter must be a shell the classifier reads; the env is read beside the command;
the git tool asks the same classifier and runs with core.fsmonitor=false; ui install no
longer hands the component name to sh -c.

On 408,231 Bash commands: safety.ts flags 20.69% (15.27% unparsed); this answers
allow 88.2 / ask 9.6 / deny 2.2 (roots set), cannot read 0.22%, p99 0.6 ms.
2026-10-14 16:32:13 -07:00
zeekay fe2e8c2a02 feat(rust): hanzo-trigram reads tgrep's index, and a limit bounds the work
A search with a limit now runs in two passes: every file is weighed
(matching lines counted, definitions as 2^32) without holding a hit,
then hits are read from the heaviest files until the limit is met. The
answer is the head of the unlimited list, checked for limits from 0 to
the whole list over text, symbol, context, ties and documents. Before,
every hit of every file was built, sorted and deduplicated before the
limit applied: a pattern matching 1.28 million lines of a 112 MB tree
took 15.6 s where it now takes 0.14 s, and rg's --max-count had bounded
that case before this crate replaced it.

When there is a tgrep index at or above the searched directory the
search asks it which files can match. Candidates are matched against
current bytes, and files whose content or name changed at or after the
build are read from disk whether or not the index lists them, so the
answer is the scan's. Index::Skip scans, Index::Only refuses to; the
tests ask every query all three ways on the same tree, before and after
edits, creations, deletions and renames, and fail if the freshness walk
is disabled. hanzo-mcp index [DIR] builds one.

Measured on 112 MB / 9,026 files, cores 0-4: rg 43-55 ms, scan 75-93,
indexed 28-50. Without a watcher the index costs a metadata walk.
2026-10-14 14:41:07 -07:00
zeekay aad64666db fix(rust): a file's definitions lead its hits, so a limit cannot cut them off 2026-10-14 13:46:41 -07:00
zeekay b232128e54 docs(rust): native search, the exec verdict, and the lean tool surface 2026-10-14 13:41:39 -07:00
zeekay e3067ebc8e feat(rust): llm limits, catalog search, served and paid-by, plan refusals 2026-10-04 16:28:31 -07:00
zeekay bd67f73a4c feat(llm): limits, catalog search, served and paid-by, plan refusals 2026-10-04 16:15:00 -07:00
zeekay 0e11347985 Add npx, uvx and jq to the TypeScript surface
Hanzo CI/CD / test (linux-amd64) (push) Skipped
Hanzo CI/CD / test (linux-arm64) (push) Skipped
Hanzo CI/CD / test-advisory (push) Skipped
publish / publish (push) Failing after 51s
Hanzo CI/CD / test-amd64 (push) Successful in 2m25s
Hanzo CI/CD / cicd (push) Successful in 3m55s
CI/CD / cicd (push) Successful in 6m24s
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
2026-09-30 15:17:34 -07:00
zeekay 46b081c99e Give the TypeScript fs tool Python's contract
Hanzo CI/CD / test (linux-amd64) (push) Skipped
Hanzo CI/CD / test (linux-arm64) (push) Skipped
Hanzo CI/CD / test-advisory (push) Skipped
Hanzo CI/CD / cicd (push) Successful in 1m52s
CI/CD / cicd (push) Successful in 3m40s
Hanzo CI/CD / test-amd64 (push) Successful in 1m45s
publish / publish (push) Failing after 2m50s
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
2026-09-30 15:06:58 -07:00
zeekay 03b7fd3edf Give the Rust fs tool Python's contract
Hanzo CI/CD / test (linux-amd64) (push) Skipped
Hanzo CI/CD / test (linux-arm64) (push) Skipped
Hanzo CI/CD / test-advisory (push) Skipped
Hanzo CI/CD / test-amd64 (push) Successful in 1m46s
publish / publish (push) Failing after 1m10s
Hanzo CI/CD / cicd (push) Successful in 1m53s
CI/CD / cicd (push) Successful in 3m43s
uri (path accepted), the unified envelope, sha256 hashes, write that only
creates, apply_patch guarded by the hash a read returned, list with depth,
pattern and cursor, search_text, and patch taking input. The search alias
tool goes: fs action=search_text is the one search.
2026-09-30 15:03:49 -07:00
zeekay 6dd2293590 Serve the extension's native messaging host from the Rust binary
Hanzo CI/CD / test (linux-amd64) (push) Skipped
Hanzo CI/CD / test (linux-arm64) (push) Skipped
Hanzo CI/CD / test-advisory (push) Skipped
publish / publish (push) Failing after 1m15s
Hanzo CI/CD / test-amd64 (push) Successful in 1m56s
Hanzo CI/CD / cicd (push) Successful in 2m33s
CI/CD / cicd (push) Successful in 4m33s
Run as hanzo-zap-host, hanzo-mcp relays ai.hanzo.zap messages to the ZAP
router's socket, as Python's native_host.py does, so a machine with only the
Rust server still lets Chrome and Firefox join without a door or pairing. Each
start registers it with the installed browsers, leaving any manifest whose
program exists alone.
2026-09-30 14:59:17 -07:00
zeekay b0d75abea9 Record parity with Python hanzo-mcp and the ZAP browser path in LLM.md
Hanzo CI/CD / test (linux-amd64) (push) Skipped
Hanzo CI/CD / test (linux-arm64) (push) Skipped
Hanzo CI/CD / test-advisory (push) Skipped
Hanzo CI/CD / test-amd64 (push) Successful in 2m16s
publish / publish (push) Failing after 1m12s
Hanzo CI/CD / cicd (push) Successful in 1m48s
CI/CD / cicd (push) Successful in 4m11s
2026-09-30 14:54:55 -07:00
zeekay 8d13859eb0 The kai tools steer a yes/no to a choice whose labels name the outcomes, which Kai answers better than yes and no. 2026-09-28 15:13:12 -07:00
zeekay 0c324b7c18 Publish, CI and release run from .github/workflows, the only directory the hanzoai org runners read. 2026-09-28 14:46:56 -07:00
zeekay e1f6a85d42 The kai tools cap no label or level count, since /v1/decisions caps none. 2026-09-28 14:46:36 -07:00
zeekay e33c38f0a2 The kai tools follow the frozen decision contract: instructions optional, 1 to 100 questions, 2 to 255 labels, 1 to 10 levels, and a noul's confidence. 2026-09-28 14:41:35 -07:00
zeekay 30fafe2616 kai_decide, kai_choice, kai_score and kai_noul ask Kai typed questions on /v1/decisions, in @hanzo/mcp 2.4.11 2026-09-28 14:31:22 -07:00
zeekay 48a91758a9 Merge the hand-written cloud surface's last changes
The two lines had diverged on what this client offers: a hand-written tool
naming seven resources against four hosts, or the surface cloud generates
from its own typed operations. The generated one wins, and not by
preference — cloud's gen-mcp-catalog writes src/tools/catalog.json into
this repo, so the fleet already treats it as the destination.

hanzo-cloud.ts stays deleted and the Rust tool keeps reading the catalog.
The iam commits improved a `call` helper that dialled a host per service;
the catalog-driven path takes every address from the document and answers
through the error envelope, so what those commits were correcting is gone
rather than reverted. The hero card's font stack came across untouched.

384 JS tests, 51 Rust tests, typecheck clean.
2026-09-01 09:48:02 -07:00
zeekay 914af987d1 Let cloud write the catalog, and drop the second generator 2026-08-31 23:42:26 -07:00
zeekay 0a5f63f352 Read the same catalog from Rust
The runtimes are said to mirror one another, and this one listed ten services
against the fleet's 114. Two of the ten had been left behind, and the commerce
base was api.hanzo.ai/api/v1 — an /api/ prefix nothing serves.

Nine routers over four hosts become one dispatch to the single endpoint, and the
service list becomes a read of the file the TypeScript runtime already embeds.

The aliases are removed rather than repointed: three of the four named the old
surface, so they mapped a caller off the API. Spelling is still normalised.
2026-08-30 16:05:35 -07:00
zeekay bb0ba82020 Generate the cloud surface instead of writing it
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.
2026-08-30 15:57:28 -07:00
zeekayandHanzo Dev 08159273e4 iam: address identity by path under /v1/iam
Users, organizations, roles, applications, providers, permissions,
invitations, tokens, sessions and audit rows are addressed as
/v1/iam/{plural} for a list and /v1/iam/{plural}/{owner}/{name} for a row,
in both the Rust `hanzo` router and the TypeScript iam/auth tools. `id`
splits into its owner and name path segments and a malformed one is refused
rather than sent. Deleting a user is a DELETE on the row; updating one reads
the row and PUTs {"user": merged}.

`owner` is scope: it goes on the wire only when the caller names one, and the
server resolves the rest from the credential. /v1/iam/applications is the one
list that requires it, and asks.

A non-2xx answer is now an error carrying its status and reason — RFC 9457
`detail`, else the envelope `msg`, else `title` — so KMS, PaaS and IAM
refusals surface instead of reading as data. Tests cover the row address, the
id split, and a problem+json refusal against a local listener.

Identity questions belong to the iam service alone, so paas no longer
duplicates users/orgs/roles. Actions with no address behind them (enforce,
system_info, health, mfa status) are gone.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-24 03:26:38 -07:00
zooqueen 197ceef32b LLM.md: where @hanzo/mcp publishes from, and how
This repo is the only publisher of the package; the copy vendored in
hanzoai/extension is a build input and stays private. Records the flat KMS read
and that NPM_TOKEN sits at the org root, so the next reader does not rediscover
it against a 404.
2026-08-19 21:31:19 -07:00
zooqueenandHanzo Dev b3c34fa7c3 feat(lsp): repo answers from /v1/code/lsp, file stays local
One tool, two planes, picked by data the caller already has: `file` names the
file, `repo` (a git.hanzo.ai slug, optional `rev`) says which world it lives
in. Without `repo` nothing changes — the same language server on the same
tree. With it, the question goes to the indexed corpus behind /v1/code/lsp,
which reaches across a repo's dependencies without checking anything out.

Actions map onto ops: locate carries a relation (definition|reference|type|
implementation) because "where is X" is one question with four answers, not
four routes; hover, symbols, diagnostics and complete stand on their own. The
body is {repo, rev?, path, line, character, relation?} in LSP's own frame —
0-based line, UTF-16 character — shifted from the tool's 1-based `line` at the
same boundary the local plane shifts it.

Credentials and base URL come from the one HanzoApi client every cloud-backed
tool already composes; there is no second client and no second lsp tool. The
actions a plane cannot serve say so up front rather than spawning a server or
calling out — rename/code_action/organize_imports/status need a working tree,
type/implementation/symbols are the index's to answer.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-06 03:14:53 -07:00
zooqueenandHanzo Dev d0e89b9bf7 feat(rust): research — the /v1/ask answer engine as one tool
Deep research is a MODE of the one door (POST /v1/ask, mode=research), not a
second endpoint and not a client-side loop: the plan → search → read → rank →
synthesize → cite pass runs server-side where it is bounded and billed. The
tool sends `{q,mode}` with `Accept: text/event-stream` and folds the stream
into one cited answer plus the sources behind it.

HanzoApi gains `events()` — POST over SSE — so streaming lives with the other
credentials-and-base-URL knowledge instead of beside a tool, and `frames()`
decodes a body into its `data:` payloads. Both are the shared `@hanzo/ai`
SearchEvent contract: data-only JSON that self-describes via `type`, with the
terminal `[DONE]` a marker rather than an event. Bearer attachment collapses
into one `auth()` now that two request paths need it.

`research` sits beside `web_search` and `web_read` because they are ONE web
capability at three depths — a snippet, a page, a report — so the existing
`ToolsConfig::web_search` flag governs all three and no second flag appears.
A mode this door does not open (`search`, `news`) is refused rather than
silently answered: a wrong answer is worse than an error.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-04 22:50:50 -07:00
zooqueenandHanzo Dev 6182d0d800 feat(tools): tracker_* — the shared board, so an agent can report its own work
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]>
2026-08-04 21:57:27 -07:00
zeekay e8627d0b2e docs: modernize + LLM.md + cross-links (one-way SDK model) 2026-07-24 12:31:52 -07:00
zeekay f8fc0d6217 docs: record the rust release contract
The runner needs ripgrep at test time and cannot run the two display-gated
computer tests. Tag pushes do not start a run today, so the workflow is
dispatched against the tag ref and verified by listing assets.
2026-07-19 00:00:06 -07:00
1e48359e6d feat(tools): wire Hanzo code-intelligence API as MCP tools (#12)
Add code_search / code_context / code_ask / code_index — one MCP tool per
api.hanzo.ai/v1/code endpoint — so @hanzo/dev and any MCP agent gain per-org
code search, budget-packed context, and cited RAG answers.

Auth mirrors hanzo-cloud.ts exactly: the caller's Hanzo bearer from
HANZO_API_KEY (or API_KEY/API_TOKEN/HANZO_TOKEN). The org is minted
server-side from that identity (principal gate) — a client never passes an
org. Output is requested as markdown (?format=md) so the LLM reads a
token-thrifty table; a non-2xx surfaces as a clear isError string. The tools
join the canonical HIP-0300 surface (allUnifiedTools), so tools/list exposes
all four over stdio.

test: hermetic local-server suite (10 tests) asserts method/path/format=md/
Bearer, no-org-leak, and arg + auth failures. Update the two tool-list
registry tests for gimp + the four code tools, and fix a pre-existing
ts-jest rootDir error that blocked every suite importing test/setup.ts.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-10 10:05:08 -07:00
zeekay ac2000708d feat: HIP-0300 unified tool architecture (TS + Rust)
Implement 13 canonical tools organized by axis:
- Core (7): fs, exec, code, git, fetch, workspace, ui
- Optional (6): think, memory, hanzo, plan, tasks, mode

Key changes:
- Add src/tools/unified/ with action-routed tool implementations
- Add Rust native tools: exec, git, fetch, code, hanzo, tasks, workspace, computer
- Refactor tool index to default to unified surface (legacy available via enableLegacy)
- Simplify tests to match new architecture (337 tests, 15 suites pass)
- Update LLM.md with HIP-0300 architecture documentation
- Remove legacy shell_tool.rs and ui_tool/ (replaced by exec_tool.rs and computer_tool/)
- Stop tracking rust/target/.rustc_info.json
2026-03-11 11:37:15 -07:00
zeekay a5ac8a6cd1 chore: update LLM.md, standardize CLAUDE.md symlink convention
- LLM.md is the canonical AI context file (tracked in git)
- CLAUDE.md is a local symlink to LLM.md (gitignored)
- Updated LLM.md content for accuracy and conciseness
2026-02-17 18:13:26 -08:00
zeekay 028c53ed6d chore: sync uncommitted changes 2026-02-13 22:15:53 -08:00
zeekay cc592ebdd0 feat: Unified UI tool with Hanzo-first multi-framework support v2.2.0
BREAKING CHANGE: Consolidated all UI tools into single 'ui' tool with methods

Major Changes:
- Single unified 'ui' tool replaces 13+ separate UI tools
- Hanzo UI as default framework (not shadcn)
- Method-based architecture for all UI operations

Supported Methods:
- list_components - List available components
- get_component - Get component source code
- get_demo - Get component demo/examples
- get_metadata - Get component dependencies
- list_blocks - List UI blocks
- get_block - Get block implementation
- search - Search components
- get_structure - Browse repository structure
- install - Install components
- set_framework - Switch frameworks
- get_framework - Get current framework
- create_composition - Create compositions

Framework Support (Hanzo-first):
- hanzo - Hanzo UI (React) [DEFAULT]
- hanzo-native - Hanzo UI React Native
- hanzo-vue - Hanzo UI Vue
- hanzo-svelte - Hanzo UI Svelte
- shadcn/react - shadcn/ui
- svelte - Svelte (shadcn)
- vue - Vue (shadcn)
- react-native - React Native Reusables

Benefits:
- 93% reduction in MCP tool surface area
- Cleaner AI assistant experience
- Better discoverability
- Easier maintenance
- Hanzo registry priority
2025-09-18 23:01:01 -05:00
zeekay 075661c92e feat: Full shadcn-ui-mcp-server parity with multi-framework support v2.1.0
Major Features:
- Complete GitHub API integration for fetching UI components
- Support for 5 frameworks: Hanzo (default), React, Svelte, Vue, React Native
- 13 new MCP tools for comprehensive UI component management
- Full parity with shadcn-ui-mcp-server functionality

New Tools:
- ui_get_component - Fetch component source code
- ui_get_component_demo - Get component usage examples
- ui_get_component_metadata - Extract dependencies and registry info
- ui_list_components - List all available components
- ui_get_block - Fetch UI blocks (complex compositions)
- ui_list_blocks - List blocks with categories
- ui_get_directory_structure - Browse repository structure
- ui_set_framework - Switch between frameworks
- ui_get_framework - Get current framework
- ui_set_github_token - Configure GitHub authentication
- ui_get_rate_limit - Check API rate limit status

Framework Support:
- Hanzo UI (hanzoai/ui) - Default framework
- React/shadcn (shadcn-ui/ui)
- Svelte (huntabyte/shadcn-svelte)
- Vue (unovue/shadcn-vue)
- React Native (founded-labs/react-native-reusables)

Technical Improvements:
- GitHub API client with rate limiting and caching
- Circuit breaker pattern for resilience
- 15-minute cache TTL for API responses
- Automatic authentication detection
- Support for blocks with multiple files
- Tree view directory browsing
2025-09-18 19:33:00 -05:00
zeekay 7e0448f10e feat: implement unified MCP search with OpenAI spec compliance and CI
## 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.
2025-09-14 18:11:10 -05:00