LiuHe
# Malong LiuHe(码龙·六合工具)
**LLM-Native Code Operations Toolkit** — Code tooling reinvented for the LLM that has no hands, no eyes, and no memory.
**English** | [简体中文](README.zh-CN.md) | [Docs](https://wulun811.github.io/LiuHe) | [GitHub](https://github.com/wulun811/LiuHe)
[](https://glama.ai/mcp/servers/wulun811/LiuHe)












**Malong LiuHe** is a toolkit built for LLMs rather than humans. It ships two components:
- **`malong/`** — an MCP toolset (44 tools): symbol read/write, indexing, impact analysis, reference tracing, call graphs, dead-code detection, code review, security scanning, atomic batch editing, test orchestration, pipeline verification — served to the LLM over MCP (JSON-RPC over stdio).
- **`malong-parse/`** — a Rust parsing service: a tree-sitter-based multi-language symbol extraction engine (JavaScript/TypeScript/TSX/Python/Go/Rust/C/C++/Java/Bash), talking to the toolset over a Unix socket, with an LRU tree cache and batched parallel extraction.
## Core Idea
Traditional tools (git, sed, IDE) assume a user with hands, eyes, and memory. An LLM has none of these. Malong LiuHe is redesigned around that fact:
| Missing | Design Consequence |
|---------|--------------------|
| **No hands** | Operations must be atomic, undoable, retryable (`edit_transaction` with rollback and undo journal) |
| **No eyes** | Output must be structured and self-explanatory (JSON for direct consumption; errors carry `suggestion`/`next_action`) |
| **No memory** | Every call is self-contained (takes `workspace_dir`); optimistic concurrency with version anchors (`read_symbol` → `write_symbol(base_version)` conflict state machine) |
## Performance (Measured)
| Metric | Value | Note |
|--------|-------|------|
| Small-file read P95 | **1ms** | 50 runs, warmed |
| Small-file write P95 | **7ms** | 30 runs, incl. post-write re-index |
| `repo_map` | **98ms** | down from tens of seconds (SQLite index + Rust parsing) |
| Memory | **RSS 134MB / 26%** | under a real docker `--memory=512m` cgroup limit |
| Concurrency | **128 concurrent / 256 in-flight requests, zero OOM** | 32-way mixed read+write on one hot file, no tearing, DB `integrity_check` PASS |
| Throughput | **~588 calls/s** | single-threaded ceiling — 60–600× real LLM demand |
| Token savings | **↓65.3%** | same task 7673 → 2662 est. (primitives) |
| Call reduction | **↓50.0%** | 6 calls (legacy) → 3 calls (primitives) |
| Tool-description compression | **44 tools ≈ 1.33k tokens** | tiered: core kept, low-freq ≤70 chars, verbose ≤230; detail flows via next_step hints |
| Index throughput | **538 files / 7s** | scoped reindex, measured |
| dry_run fidelity | **47/47** | golden-hash comparison across 3 languages, 100% |
| Concurrent writes to same symbol | **16/16** | exactly one winner + one conflict, zero silent overwrites |
## Language Support
| Language | Symbol Extraction | Reference Extraction |
|----------|:---:|:---:|
| JavaScript / TypeScript / TSX / JSX / MTS / CTS | ✓ | ✓ |
| Python | ✓ | ✓ |
| Go | ✓ | ✓ |
| Rust (impl blocks / trait / enum semantics) | ✓ | ✓ |
| C / C++ (incl. headers) | ✓ | ✓ |
| Java | ✓ | ✓ |
| Bash | ✓ | ✓ |
## Tool Overview
| Category | Tools |
|----------|-------|
| **I/O primitives** | `read_symbol` (symbol read + version), `write_symbol` / `write_symbols` (safe write: conflict state machine + atomic write + undo) |
| **Index & search** | `reindex`, `symbol_search`, `code_search` (keyword + regex intent patterns — NOT semantic search), `repo_map` (98ms file map; skeleton pagination: `page`/`page_size`/`prefix` — truncated output always carries the full top-level skeleton with page numbers)
| **Analysis** | `impact_analysis` (blast radius + risk), `call_chain`, `references`, `dep_graph`, `inspect` (outline + refs + call chain in one), `trace_symbol` (constant tracing + hardcoded copies) |
| **Editing** | `edit_batch`, `edit_transaction` (atomic txn + rollback + cross-session lock), `edit_collision_guard` (write conflict detection), `edit_sandbox` (pre-write validation), `diff_facts` (symbol changes + test sync), `rename_symbol` (atomic cross-file rename), `git_worktree` (isolated branch + verify + merge) |
| **Quality gates** | `code_review`, `security_review` (regex security patterns), `code_quality` (5-dim probe), `style_sniffer`, `guard_patterns`, `naming_consistency`, `exception_guard`, `config_drift`, `dependency_gatekeeper`, `fix_imports`, `sweep_dead_code`, `mock_sync` — all pure regex/AST, zero LLM calls, deterministic |
| **Engineering** | `test_bridge` (run/suggest/discover), `find_tests`, `verify_pipeline` (lint/test/typecheck stages), `debug_runner` (error analysis), `patch_parser` (SEARCH/REPLACE), `tsc_check`, `spec_gen`, `active_todos` |
| **System** | `health`, `gc`, `feedback` |
## Architecture
```
┌─────────────────────────┐ ┌──────────────────────────────┐
│ LLM Client (MCP) │ │ malong-parse (Rust daemon) │
│ malong/ 44 tools │◄───►│ tree-sitter, 10 languages │
│ SQLite index (per ws) │ │ LRU tree cache 50MB / batch │
│ Unix socket client │ │ catch_unwind crash recovery │
└─────────────────────────┘ └──────────────────────────────┘
```
- All parsing is done by the Rust service (zero tree-sitter bindings on the Node side)
- Index is SQLite (WAL mode, multi-process safe), isolated per workspace
- LRU tree cache (50MB / 5min TTL), batched extraction 50 files/batch
- Daemon multi-session safety: circuit breaker + liveness probe + cross-session coordinated restart (O_EXCL lock) — a slow request in one session never stalls others
## Self-Hosting (Dogfooding)
30+ rounds of "Malong reviews Malong": the toolset audits and fixes its **own** code, with dozens of real bugs fixed and locked by tests — including directory-scope filtering gaps, false dead-code reports for registration patterns, lost constant read-sites, and SQL parameterization cleanup. Every round grew the assertion count (now 2240 across 87 JS test files, full chain 0 failures). Latest round: fixed a Windows-only path-form mismatch in batch indexing — the file collector emits forward-slash paths while `join(repo, r.path)` produces backslashes on win32, so every `statMap` lookup missed (all `files.size = 0`, outline `tokens_estimate` stuck at 0, `content_hash` surviving only via a CWD-dependent fallback) — locked by a new collector→indexBatch chain test, measured on Windows 2026-09-04.
## Scan Boundaries (limits acknowledged; committed within)
Deterministic scanners only — regex pattern matching and reference graphs. **No control-flow, data-flow, or cross-module semantic coverage.** 0 findings ≠ clean; high scores ≠ healthy.
- `security_review` scans: injection (eval/Function-ctor, exec/spawn string building, template `${}`, `$()` substitution, SQL concat), XSS, hardcoded secrets, CORS *, timing/insecure compare, dotenv. **Not scanned**: SSRF, XXE, deserialization, auth/logic flaws, control-flow defects.
- `sweep_dead_code` reference scope: import graph + fixed text-ref fallback (.sh/.json/.md etc.); CLI-string references count as alive (miss-over-delete). **Not scanned**: dynamic/reflection wiring.
- Commitment: issues in scope get fixed; covered patterns are test-locked; new rules must stay within committed categories.
## Quick Start
### Standard install (default — recommended)
`npm ci` installs `better-sqlite3` (the full SQLite backend); this is the default recommended path:
```bash
# 1) Get the parsing daemon — download a prebuilt binary from releases/, or build:
cd malong-parse && cargo build --release && cp target/release/malong-parse ~/.local/bin
malong-parse & # start the daemon (Unix socket)
# 2) Install the toolset (npm ci installs the full better-sqlite3 backend)
cd ../malong && npm ci
# 3) Register the MCP server (works with any MCP client, e.g. opencode / Claude Desktop)
# opencode (project-root opencode.json; Windows paths use double backslashes, Linux/macOS forward slashes):
# {
# "mcp": {
# "malong": {
# "type": "local",
# "command": ["node", "--max-old-space-size=512", "N:\\repo\\liuhe\\malong\\mcp-server.js", "--workspace", "N:\\repo\\liuhe"],
# "enabled": true
# }
# }
# }
# Claude Desktop (verified on Windows: %APPDATA%\Claude\claude_desktop_config.json):
# {
# "mcpServers": {
# "malong": { "command": "node", "args": ["/path/to/malong/mcp-server.js"] }
# }
# }
# 4) Ask your LLM: "search for the symbol 'handle' in my workspace"
# DeepSeek Harness (dsh web) — auto session-workspace convenience (extra, optional):
# Run once on the dsh host (Linux/macOS):
# Option A (npm, one line — full backend + platform binary included):
# dsh plugin --profile web add @jieai/dsh-malong-bridge
# pkill -f "dsh web"; dsh web --port 3456 --host 0.0.0.0 --trusted-host <LAN IP>
# Option B (script, points at a checkout of this repo):
# bash malong/dsh/install-dsh.sh # idempotent; edits ~/.dsh/profiles/web/cordis.patch.yml with backup
# The bridge registers all 44 tools as malong__* and auto-fills workspace_dir from the
# current conversation's workspace (no per-call path needed; explicit paths still win).
# Full guide incl. index rules: malong/dsh/DSH-INTEGRATION.md
# Troubleshooting: if EVERY malong__* call hangs until timeout, the bridge's
# mcp-server subprocess is gone. The bridge now auto-restarts it (exponential
# backoff, pending calls fail fast with "restarting — retry shortly"); if that
# keeps failing, restart dsh web: kill <dsh web pid> && dsh web --port 3456 ...
```
> **SQL backend note:** the default is the full `better-sqlite3` backend. If `npm install` fails (offline / no build toolchain / Node < 20), the server automatically degrades to the **vendored sql.js WASM sandbox backend** (`malong/vendor/`, zero-dependency, no network) — the startup log shows which backend is active and the upgrade command. Data files are fully compatible between backends (both are SQLite).
### Zero-build deployment (degraded path: sandbox / offline environments)
For environments with **Node >= 20 and no npm access** (no `npm ci`, no native
compilation), everything runs from the repo files as-is (Linux/Windows — the
prebuilt binaries are `linux-x86_64` and `windows-x86_64`). On this path SQLite
runs on the sql.js sandbox backend (degraded):
```bash
# 0) Clone, then enter the toolset directory:
git clone <repo-url> liuhe && cd liuhe/malong
# 1) Parse daemon — prebuilt binary from releases/ (no cargo needed):
mkdir -p ~/.local/bin
tar -xzf ../releases/malong-liuhe-0.4.7-linux-x86_64.tar.gz
cp malong-parse/target/release/malong-parse ~/.local/bin
malong-parse & # start the daemon (socket: /tmp/malong-parse-$UID.sock)
# 2) Run the toolset — zero npm install:
node --max-old-space-size=4096 mcp-server.js --workspace /path/to/project
# vendored SQLite (sql.js WASM) auto-enabled when better-sqlite3 is unavailable
# 3) Self-check (30s, optional):
node tests/test-db-adapter.js # 22 assertions: sql.js backend + persistence
node tests/test-mcp-server.js # 25 assertions: MCP stdio + daemon round-trip
```
> **Windows users:** use `releases/malong-liuhe-0.4.7-windows-x86_64.tar.gz` (contains `malong-parse.exe`), extract and put it on PATH; the MCP server auto-starts the daemon, so step 1's `malong-parse &` is not required. The `mkdir -p`/`tar -xzf` above are Unix syntax — on Windows use any extractor (`tar -xzf` works on Win10+).
> Note: if the daemon is not running, parse-dependent tools (symbol extraction
> etc.) degrade; the SQLite-backed tools (repo-map / code-index / health-check)
> still work.
- See `THIRD_PARTY_NOTICES.md` for the vendored component.
What the LLM sees back:
```json
{
"results": [
{ "name": "handle", "type": "function", "start_line": 6, "end_line": 12, "file": "app.js" },
{ "name": "handle_login", "type": "function", "start_line": 5, "end_line": 9, "file": "src/auth.py" }
],
"count": 2,
"next_step": "Before modifying found symbols, check blast radius: impact_analysis(...)"
}
```
### Build the Rust parsing service
```bash
cd malong-parse
cargo build --release
cp target/release/malong-parse ~/.local/bin/ # or add to PATH
malong-parse & # daemon (Unix socket: /tmp/malong-parse-$(id -u).sock)
```
Prebuilt binaries for Linux x86_64 are committed under
[`releases/`](releases/) (with `.sha256` checksums) — pick one up instead of building if you prefer.
**Platforms:** Linux and macOS use the Unix socket; on **Windows** the daemon listens on TCP `127.0.0.1:31001` (set `MALONG_PORT` to override) — the MCP server probes and auto-starts `malong-parse.exe` on startup, so no manual launch is needed (if you use the parse-client API directly outside MCP, start it manually). Set `MALONG_SOCKET` to override the Unix socket path.
### Install the toolset
```bash
cd malong
npm ci
npm test # 2240 assertions (daemon must be running)
```
### Start the MCP server
```bash
cd malong
node --max-old-space-size=512 mcp-server.js --workspace /path/to/project
```
Register the MCP server with any MCP-capable LLM client and the tools become available. For **opencode**, register in the project root `opencode.json`:
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"malong": {
"type": "local",
"command": ["node", "--max-old-space-size=512", "malong/mcp-server.js", "--workspace", "."],
"enabled": true
}
}
}
```
> Windows note: relative paths in `--workspace` and `malong/mcp-server.js` resolve against the directory where opencode is launched; pass absolute paths (escape backslashes as `\\` in JSON) if you start it elsewhere. The daemon is auto-started by the MCP server — no need to launch `malong-parse.exe` manually.
### Configuration (environment variables)
All optional — sensible defaults apply when unset.
| Variable | Default | Purpose |
| --- | --- | --- |
| `MALONG_STATE_DIR` | `~/.config/malong` | Where usage / feedback / edit-stats files are written. Override to redirect state (tests, sandboxed hosts). Reads fall back to the legacy `~/.config/opencode/` so pre-0.3.37 data is not lost. |
| `MALONG_SOCKET` | `/tmp/malong-parse-$(id -u).sock` | Unix socket path to the parse daemon (Linux / macOS). |
| `MALONG_PORT` | `31001` | TCP port for the parse daemon (Windows). |
| `MALONG_PARSE_BIN` | npm platform pkg / `~/.local/bin` | Binary used when the client auto-starts the daemon. Resolution order: this env → `@jieai/malong-parse-<os>-<arch>` npm platform package (installed as an optional dependency of `@jieai/dsh-malong-bridge`) → `~/.local/bin/malong-parse` → `malong-parse/target/release` (dev tree). |
| `MALONG_PARSE_MODE` | rust-service | Parse transport. Only `rust-service` is supported (`builtin` / `shadow` are rejected). |
| `MALONG_WS_GC_DAYS` | `14` | Days a workspace index cache may sit untouched before `health cleanup` prunes it; `0` disables. |
### Suppressing security false positives
`security_review` is deliberately conservative, so benign patterns (a hash used as a cache key, `process.exit` in a CLI entry) can trip it. Two explicit, auditable suppression mechanisms exist — neither ever auto-suppresses injection rules.
**Inline** — append `malong-ignore` to a line to suppress all findings on it, or `malong-ignore[eval,exec-cmd]` for specific rules (a reason after `:` is encouraged):
```js
const id = crypto.createHash('md5').update(p).digest('hex') // malong-ignore: cache-dir name, not security
```
**Config** — add a `securityIgnore` array to `.ai-patterns.json` (the same file `guard_patterns` reads). Each entry takes `files` (`*` / `**` globs) and/or `rules`; omit one side to match all:
```json
{ "securityIgnore": [ { "files": ["**/mcp-server.js"], "rules": ["process-exit"] } ] }
```
Suppressed findings are dropped from the score but counted in the summary's `suppressed` field, so nothing is hidden silently. Injection rules (eval / exec / SQL / spawn) are only ever suppressed by an explicit marker or entry you authored — never heuristically.
### Journal auto-cleanup
Every safe write (`write_symbol` / `write_symbols` / `edit_batch`) leaves an undo journal under `.malong/journal/`. Terminal transactions (`committed` / `rolled_back` / `abandoned` / `failed`) older than a TTL (default **24h**, configurable) are pruned automatically — throttled to at most one scan per hour per workspace, so it costs nothing. In-flight (`created` / `staged`) and `needs_review` (external change awaiting human review) journals are **never** auto-deleted. This only ever touches the tool's own rollback backups — never your source files.
### Using with Claude Code
Verified end-to-end with Claude Code 2.x. The MCP layer is standard JSON-RPC over
stdio, so any MCP client (Claude Code, codex, opencode, Claude Desktop…) connects
the same way.
**1) Prerequisites** — Node >= 20 and the parsing daemon running (see the
*Zero-build deployment* or *Build the Rust parsing service* sections above).
**2) Install Claude Code**
```bash
npm install -g @anthropic-ai/claude-code
```
**3) Point Claude Code at an Anthropic-compatible provider**
Claude Code needs an LLM backend. Any Anthropic-compatible endpoint works — set the
variables below (or manage them with [`cc-switch`](https://www.npmjs.com/package/@cc-switch/cli)):
```bash
export ANTHROPIC_BASE_URL="https://your-provider/anthropic" # any Anthropic-compatible endpoint
export ANTHROPIC_AUTH_TOKEN="your-api-key" # never commit this
export ANTHROPIC_MODEL="your-model" # a model your provider serves
```
> With [`cc-switch`](https://www.npmjs.com/package/@cc-switch/cli): `npm i -g @cc-switch/cli`,
> `cc-switch new my-provider` (fill in the same `ANTHROPIC_*` keys under `env`),
> `cc-switch switch my-provider`, then `eval $(cc-switch export)`.
**4) Register the MCP server**
```bash
claude mcp add liuhe -- node /path/to/malong/mcp-server.js --workspace /path/to/project
claude mcp list # → "liuhe … ✔ Connected"
```
**5) Use it** — start `claude` in your project and ask, or run headless:
```bash
claude -p "index the workspace with reindex, then find createDb with symbol_search" \
--allowedTools "mcp__liuhe__reindex" "mcp__liuhe__symbol_search"
```
Tools are exposed as `mcp__liuhe__<tool>` (e.g. `mcp__liuhe__health`,
`mcp__liuhe__symbol_search`, `mcp__liuhe__repo_map`). Run `reindex` (optionally
`blocking=true`) once per workspace first, so symbol search / impact analysis have
an index to query.
### Using with codex
Verified end-to-end with codex (same JSON-RPC-over-stdio MCP layer).
> **Version note:** recent codex releases force the OpenAI *Responses* API
> (`wire_api = "responses"`). Use a version that still supports `wire_api = "chat"`
> together with MCP — verified with **0.50.0**:
> `npm install -g @openai/codex@0.50.0`.
**1) Prerequisites** — Node >= 20 and the parsing daemon running (see above).
**2) Configure `~/.codex/config.toml`** — point codex at any OpenAI-compatible
provider (example below: [OpenCode Zen](https://opencode.ai)) and register the MCP
server:
```toml
model = "deepseek-v4-flash"
model_provider = "opencode-zen"
[model_providers.opencode-zen]
name = "OpenCode Zen"
base_url = "https://opencode.ai/zen/go/v1" # any OpenAI-compatible endpoint
wire_api = "chat"
env_key = "OPENCODE_ZEN_API_KEY" # codex reads the API key from this env var
[mcp_servers.liuhe]
command = "node"
args = ["/path/to/malong/mcp-server.js", "--workspace", "/path/to/project"]
```
**3) Run** — export the API key, then start codex:
```bash
export OPENCODE_ZEN_API_KEY="your-api-key" # never commit this
codex exec "index the workspace with reindex, then find createDb with symbol_search"
```
Tools are exposed as `liuhe.<tool>` (e.g. `liuhe.health`, `liuhe.symbol_search`,
`liuhe.repo_map`). Run `reindex` once per workspace first so symbol search has an
index to query.
## Testing
- Rust: `cargo test` (92 assertions: per-language extraction + protocol framing/decoding + cache LFU + server dispatch/priority queue + batch_extract + deep-nesting guards + hello handshake)
- JS: 86 test files, 2231 assertions. `npm test` runs the full chain (primitives / embedded / mvp-batch / tool-registry / repo-map / handler-smoke / patch-parser / file-collector / code-search / health-check / db-adapter dual-backend / write-runtime / host-config / mcp-server / security-review-rules / journal-prune / gatekeeper-golden / debug-runner / edit-collision-guard / fix-imports / symbol-search / naming-consistency / call-chain / edit-transaction-ext / batch-edit-write / verify-pipeline / dep-graph-project / code-quality / tsc-spec / batch-edit-tocou / workflow-closure / variable-refs / output-budget / read-symbols-batch / feedback-list / test-bridge-run / mock-syncer-truncation / r54-p0 / r54-p1 / r54-p2 / r8 / r9 / crash-injection / r10 / dogfood-r12); `test-dogfood-r14…r30` (end-to-end against a real daemon) run separately
- Total: 2231 assertions across 86 JS test files (plus cargo tests)
- One-shot: `./scripts/ci.sh` (self-contained: cargo test + npm test + dogfood; reuses or auto-starts the daemon)
## License
[MIT](LICENSE)
## Changelog
See [CHANGELOG.md](CHANGELOG.md) for the full release history.
TDQS
Scored across 44 tools
Numerous tools have heavily overlapping responsibilities: multiple edit paths (write_symbol, write_symbols, edit_batch, edit_transaction, sandbox_validate, edit_collision_guard), overlapping search tools (symbol_search, code_search, references), and redundant inspection tools (inspect explicitly replaces read_outline/references/call_chain but they still exist). While descriptions attempt to differentiate them, the boundaries are often subtle, making tool selection error-prone.
The vast majority of tool names follow a clear verb_noun pattern in snake_case (e.g., find_tests, fix_imports, rename_symbol, read_symbol, write_symbol). A few outliers like 'gc', 'health', and 'inspect' break the pattern but are still intuitively named and do not introduce mixing of styles.
44 tools is far beyond the typical 3-15 well-scoped range and even exceeds the 25-tool threshold for 'too many'. The server attempts to cover an extremely broad domain, but the high count primarily reflects tool proliferation rather than genuinely distinct capabilities, overwhelming agents.
The toolset covers the full lifecycle of code development: indexing, searching, reading, editing, refactoring, testing, security review, quality analysis, and dependency management. Obvious gaps are few (e.g., no standalone git history/commit tool beyond git_worktree), and most workflows have a dedicated path. The redundancy slightly undermines usability but not coverage.