Run Z.ai’s 7,300-star coding agent yourself: a hands-on guide to ZCode
ZCode (7,300+ stars, Apache-2.0) is Z.ai’s open-source coding agent harness: terminal TUI, web UI, a plugin marketplace, MCP servers, and deterministic guardrail hooks. Build the agent CLI from source and put hooks around your agent’s tool calls — in about half an hour.

Every coding agent you rent lives in someone else's cloud. ZCode is the opposite bet: an Apache-2.0 harness from Z.ai that you clone, build, and run on your own machine. It ships a terminal TUI, a browser UI, an Electron desktop app, a plugin marketplace, first-class MCP support, and — the part that matters most — deterministic hooks that can approve, block, or rewrite your agent's tool calls before they execute. Since its public release on September 20, 2026 it has collected over 7,300 stars and 2,200 forks. This tutorial builds it from source and puts a real guardrail around the agent's shell.
1. Why this one
Most open agent harnesses give you a chat loop and stop there. ZCode gives you the whole workbench: the Agent CLI and runtime are plain TypeScript you can read and fork, plugins contribute skills, custom slash commands, and MCP servers, and the hook system intercepts seven lifecycle events — SessionStart, UserPromptSubmit, PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, and Stop. A hook is a plain process: JSON in on stdin, JSON out on stdout, and exit code 2 means an explicit block. That is the escape hatch every other harness makes you beg for: policy enforced in code, not in a system prompt the model can talk its way around.
2. What you'll need
- Git, Node.js 24.14.0, and pnpm 10.33.2 — the repo's
mise.tomlis the source of truth, and the builds assume these exact versions. - A model account the harness can authenticate against. The CLI proxies
/api/v1/oauth/tokento Z.ai's product service; pointZCODE_BUILTIN_PROVIDER_CONFIG_FILEat your own provider config file if you run one. - A machine you are comfortable building a large TypeScript monorepo on. This is a build-from-source harness — there is no public one-line installer yet.
3. Step 1 — Clone and bootstrap
git clone https://github.com/zai-org/ZCode.git
cd ZCode
pnpm bootstrap
pnpm bootstrap installs workspace dependencies, prepares the desktop local runtime assets, and runs build:bootstrap. It skips remote-workspace resource preparation, which is only needed if you are connecting the agent to remote projects over SSH or WSL — for local work the default is fine.
4. Step 2 — Build the agent CLI
The agent CLI and runtime source live in apps/zcode-cli/ as ordinary directories in the same repo — no submodules to initialize. Build it and check the entry point:
pnpm --filter @zcode/cli... build
node apps/zcode-cli/packages/cli/dist/zcode.cjs --help
This produces dist/zcode.cjs, the full agent with its terminal UI, without any of the packaging layers below. It is the fastest way to confirm the build works on your machine.
5. Step 3 — Build the full distribution
The real artifact is the unified zcode launcher: no arguments opens the TUI, --web starts the browser UI, and anything else goes to the agent CLI. Assemble it:
pnpm build:zcode --base-url https://YOUR-HOST/zcode/
--base-url must point at a place you can host the release directory (the README uses downloads.example.com as a placeholder — it is not a real download). The build emits dist/zcode/releases/<version>/zcode-<version>.tar.gz plus latest.json and install.sh. To skip hosting entirely, extract the tarball and run it directly:
zcode_version=$(node -p "require('./dist/zcode/latest.json').version")
mkdir -p dist/zcode/debug
tar -xzf "dist/zcode/releases/$zcode_version/zcode-$zcode_version.tar.gz" \
-C dist/zcode/debug
# terminal UI:
node dist/zcode/debug/zcode/bin/zcode.mjs
# web UI, pointed at your project, no browser auto-open:
node dist/zcode/debug/zcode/bin/zcode.mjs --web \
--workspace "$PWD" --port 3030 --no-open
The official installer flow downloads the tarball from your base URL, drops the runtime in ~/.zcode/runtime, and links zcode into ~/.local/bin (overridable with ZCODE_DIST_HOME and ZCODE_DIST_BIN_DIR).
6. Step 4 — Pick your interface: TUI or web
All three entry points share one backend:
zcode # terminal UI
zcode --web # browser UI, workspace = cwd
zcode --web --workspace /path/to/project --port 3030 --no-open
The web server binds to 127.0.0.1 and picks a free port unless you say otherwise. Expose it on your LAN with --host 0.0.0.0 — at that point a token is generated by default and printed with the URL; pass your own with --token or disable auth entirely with --no-token. For scripted setups, ZCODE_SERVER_AUTH_TOKEN sets the same token as an environment variable.

7. Step 5 — Extend it with plugins
Plugins are local bundles that contribute skills (SKILL.md files), custom commands, and MCP servers. The repo ships official plugins, some enabled by default:
zcode plugins list
zcode plugins enable ios-simulator
zcode plugins disable browser-use
Default-on: browser-use, document-skills, skill-creator, and zcode-guide (from the single official marketplace). Discovered but disabled until you opt in: ios-simulator, android-emulator, and restore-legacy-sessions. Plugin state lives under ~/.zcode/cli/plugins/. Your own plugin is a directory with a .zcode-plugin/plugin.json manifest declaring skills, commands, and mcpServers — point plugins.dirs at it in the config and it is enabled.
8. Step 6 — Give the agent your tools via MCP
The main config is ~/.zcode/cli/config.json. MCP servers go under mcp.servers; stdio, http, and sse transports are supported. The filesystem-server example from the project's own docs:
{
"mcp": {
"servers": {
"filesystem": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."],
"timeoutMs": 30000
}
}
}
}
MCP tools register before the first model request and surface as mcp__<server>__<tool>. Inside the CLI, manage the session live with /mcp list, /mcp status, /mcp connect <server>, and /mcp disconnect <server>.
9. Step 7 — Put a guardrail hook around the shell
This is the step that justifies the whole build. Enable hooks in the same config file and block destructive shell commands at the process level, where no prompt injection can reach:
{
"hooks": {
"enabled": true,
"timeoutMs": 60000,
"maxOutputBytes": 32768,
"events": {
"PreToolUse": [
{
"matcher": "^(Bash|Write|Edit)$",
"hooks": [
{ "type": "process", "command": "node",
"args": ["./scripts/pre-tool-hook.mjs"] }
]
}
]
}
}
}
Your hook receives one JSON object on stdin describing the tool call and prints one JSON object to stdout. To deny, exit with code 2 and say why — the contract, verbatim from the docs:
{
"continue": false,
"reason": "Do not run destructive shell commands in this workspace.",
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Blocked by project hook."
}
}
The matcher is a JavaScript regex against the tool name, groups run in config order, and Stop hooks can even demand one more model step with continue: true (repeated continuations are capped to avoid loops). A five-line Node script now enforces what a hundred lines of system prompt never could.

10. What you built
A fully self-hosted coding agent workbench: terminal TUI, browser UI, and the same backend behind both; a plugin layer with skills, commands, and MCP servers; and a process-level hook that deterministically polices the agent's tools. From here the natural extensions are the Electron desktop app (pnpm bundle:desktop -- --os linux --arch x64, target yours), SessionStart hooks that inject your repo's conventions into every session, and packaging the release behind your own --base-url so teammates install with install.sh.
11. Honest limitations
- There is no public one-line installer yet — the download URL in the README is a placeholder, so you build from source or host the release yourself.
- The toolchain is pinned hard: Node.js 24.14.0 and pnpm 10.33.2 via
mise.toml. If your machine's Node is different, usemiseto match before building. - The primary docs are Chinese first (an English README exists), and the project moves fast — v3.14.3 landed September 23, 2026, twelve days after the repo went public. Expect the API surface to shift under you.
- The agent authenticates against Z.ai's product service by default; running it against other models means working through the built-in provider config, which is lightly documented.
- Hooks are powerful and sharp: a bad matcher can block every tool call in the session, and
PostToolUseFailurerecovery logic that loops will burn tokens. Test hooks against a scratch project first.