glm-acp-agent
v1.1.2
Published
ACP agent in TypeScript that uses the Zhipu AI GLM series (specifically GLM-5.1 and GLM-4.7) as the reasoning core
Maintainers
Readme
glm-acp-agent
An Agent Client Protocol (ACP) agent written in TypeScript that uses the Z.AI / Zhipu AI GLM model family (GLM-5.1, GLM-4.7, GLM-4.6, …) as its reasoning core.
The agent connects to any ACP-compatible IDE or client over stdio, streams responses back in real time, and can call a rich set of tools to interact with the user's file system, terminal, and the web.
Coding Plan Only
glm-acp-agent is intentionally built for the Z.AI GLM Coding Plan. It is not a general-purpose Z.AI Open Platform API client.
By default, model calls use the Coding Plan endpoint:
https://api.z.ai/api/coding/paas/v4The same Coding Plan API key is used for the agent's GLM model calls and supported Coding Plan tools. General Z.AI API/resource-package billing surfaces, such as direct /api/paas/v4 Tool API calls, are intentionally out of scope for this ACP agent.
Built-in web tools use Coding Plan-compatible MCP endpoints, not the general /api/paas/v4 Tool API. If you need general Z.AI API billing, separate resource packages, or non-Coding Plan endpoints, use a different provider configuration or fork this agent for that purpose.
Features
- Full ACP compliance – implements
initialize,authenticate,session/new,session/set_mode,session/prompt,session/cancel,session/close,session/list,session/load,session/fork,session/resume, andsession/set_model - Streaming – assistant text and reasoning tokens are forwarded as incremental ACP chunks
- Tool calling – agentic loop with up to 20 turns of GLM function calling
- Thinking mode – GLM's
reasoning_contenttokens are surfaced asagent_thought_chunkblocks so the client can show the model's chain of thought - Per-session model switching –
session/set_modellets clients change the active GLM model mid-conversation;session/newreturns the curatedavailableModelslist - Image input via Coding Plan Vision MCP –
promptCapabilities.imageis advertised; pasted ACP image blocks are routed through Z.AI Vision MCP (@z_ai/mcp-server) and the resulting analysis is fed into the main coding model. Direct chat-image (e.g.glm-4v-plus) calls are intentionally not used. - Session persistence – conversations are written to
~/.local/state/glm-acp-agent/sessions/and can be reloaded viasession/load, branched viasession/fork, or resumed without replay viasession/resume - Six built-in tools (see below)
- Capability-aware – every tool is gated on the client capabilities advertised at
initializetime; tools the client can't run return a clear error to the model instead of crashing - Permissioned writes / commands – every
write_fileandrun_commandcall asks the user viasession/request_permissionbefore doing anything - Protocol-correct stop reasons – maps model and runtime conditions to ACP
end_turn,max_tokens,max_turn_requests,refusal, andcancelled - Protocol-correct tool statuses –
pending→in_progress→completed/failed - Token usage reporting – aggregated usage is returned on the
session/promptresponse
Architecture
ACP Client (IDE plugin, CLI, …)
│ stdio (ndjson)
▼
GlmAcpAgent ← ACP protocol layer (src/protocol/)
│
├─ GlmClient ← Z.AI / Zhipu AI Coding Plan Chat Completions (src/llm/)
│
├─ ToolExecutor ← executes tool calls (src/tools/)
│ ├─ read_file / write_file → ACP client (fs.*)
│ ├─ list_files / run_command → ACP client (terminal)
│ ├─ web_search / web_reader → Z.AI Coding Plan Web MCP (HTTP)
│ └─ image_analysis → Z.AI Coding Plan Vision MCP (stdio)
│
└─ VisionMcpClient ← spawns `npx @z_ai/mcp-server` on demandThe agent process needs network access to api.z.ai for chat completions and Web MCP, plus npx available on PATH so it can launch @z_ai/mcp-server for vision. Filesystem and shell operations remain delegated to the ACP client; the agent itself only writes to the OS temp directory when it needs to materialize a pasted image for Vision MCP, and those temp files are removed once the prompt completes.
Available Tools
| Tool | Runs on | Requires client capability | Description |
|------|---------|----------------------------|-------------|
| read_file | ACP client | fs.readTextFile | Read the text content of a file |
| write_file | ACP client | fs.writeTextFile | Write or overwrite a text file (asks for permission) |
| list_files | ACP client | terminal | List a directory via ls -la (POSIX shell required) |
| run_command | ACP client | terminal | Run an arbitrary shell command via sh -c (asks for permission) |
| web_search | Agent (Z.AI Coding Plan MCP) | – | Search the web — returns titles, URLs, and summaries |
| web_reader | Agent (Z.AI Coding Plan MCP) | – | Fetch and parse a web page (markdown or plain text) |
| image_analysis | Agent (Z.AI Vision MCP, stdio) | – | Analyze a local image path or remote URL using @z_ai/mcp-server |
Prerequisites
- Node.js 20 or later (native
fetchand Web Streams required) - npm 9 or later
- A Z.AI API key — obtain one at https://z.ai/manage-apikey/apikey-list
Installation
git clone https://github.com/stefandevo/glm-acp-agent.git
cd glm-acp-agent
npm install
npm run buildConfiguration
The agent reads its configuration from environment variables, plus an optional credentials file written by glm-acp-agent --setup.
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| Z_AI_API_KEY | One of env / --setup | — | API key for the Z.AI / Zhipu AI service. If unset, the credentials file is consulted. |
| ACP_GLM_MODEL | No | glm-5.1 | Default GLM model for new sessions |
| ACP_GLM_AVAILABLE_MODELS | No | built-in list | Comma-separated list of model ids advertised in session/set_model |
| ACP_GLM_BASE_URL | No | https://api.z.ai/api/coding/paas/v4 | Override the API base URL |
| ACP_GLM_MAX_TOKENS | No | 8192 | Cap on max_tokens for each completion |
| ACP_GLM_THINKING | No | auto-detected | Force thinking mode true / false |
| ACP_GLM_SESSION_DIR | No | $XDG_STATE_HOME/glm-acp-agent/sessions | Where session JSON files are persisted |
| ACP_GLM_DEBUG | No | — | Set to true or 1 to enable verbose debug logging to stderr (shows model selection, API key resolution, tool calls, and usage stats) |
| XDG_CONFIG_HOME | No | ~/.config | Where the credentials file is read/written |
One-time setup
If you'd rather not pass Z_AI_API_KEY through your ACP client's environment block, run the interactive setup once and the agent will read the key from disk on subsequent launches:
# From the project directory (after npm run build)
node dist/index.js --setup
# Or, if you installed globally
glm-acp-agent --setupThe key is written to $XDG_CONFIG_HOME/glm-acp-agent/credentials.json (default: ~/.config/glm-acp-agent/credentials.json) with 0600 permissions. The Z_AI_API_KEY environment variable, when set, always wins over the file.
Supported models
The agent advertises only the models on the current Z.AI Coding Plan allowlist:
| Model | Notes |
|-------|-------|
| glm-5.1 | Default. Long-horizon coding model; thinking mode auto-enabled |
| glm-5-turbo | Faster Coding Plan reasoning model |
| glm-4.7 | 200K-context reasoning model |
| glm-4.5-air | Lightweight, lower-latency model |
ACP_GLM_AVAILABLE_MODELS still lets you advertise custom IDs, but custom IDs sit outside the supported Coding Plan list — the Coding Plan endpoint will reject any model code Z.AI hasn't whitelisted (business code 1211).
Vision (glm-4v-plus etc.) is not advertised as a chat model: vision on the Coding Plan flows through the Vision MCP section below instead.
When the model name matches glm-4.5, glm-4.6, glm-4.7, or glm-5.x, the agent enables Z.AI's thinking: { type: "enabled" } extension and forwards reasoning tokens to the client as agent_thought_chunk blocks. Override with ACP_GLM_THINKING=false if you want plain completions only.
Vision MCP
Pasted ACP image blocks are not sent to the chat-completions endpoint. Instead, the agent boots @z_ai/mcp-server over stdio (via npx -y @z_ai/mcp-server@latest) and calls its image_analysis tool. The text result is spliced into the user message as <image_analysis index="N">…</image_analysis> so the regular Coding Plan model can reason about it.
Prerequisites:
npxonPATH(Node 18+ / npm 9+).- The same
Z_AI_API_KEYused for chat completions; the agent forwards it to the MCP server asZ_AI_API_KEYplusZ_AI_MODE=ZAI.
The model can also call image_analysis explicitly with { image_source: "/path/or/url", prompt?: "…" }. Vision failures (missing npx, MCP startup, quota) are surfaced as actionable errors but never abort the prompt; an inline <image_analysis_error> annotation is used so the conversation can continue.
Running
Standalone (stdio)
export Z_AI_API_KEY=your_key_here
node dist/index.jsThe agent speaks the ACP newline-delimited JSON protocol over stdin/stdout. You can connect any ACP-compatible client to it.
As a global CLI
npm install -g .
export Z_AI_API_KEY=your_key_here
glm-acp-agentDevelopment mode (watch)
export Z_AI_API_KEY=your_key_here
npm run dev # tsc --watchConnecting to an ACP Client
Zed (recommended for local testing)
Zed is currently the most polished editor for trying out ACP agents locally — it spawns the agent process over stdio and surfaces it in the agent panel. This is the fastest way to iterate on glm-acp-agent before publishing it to the ACP registry or to npm.
1. Prerequisites
- A recent build of Zed that supports the
agent_serverssetting - Node.js 20 or later on your
PATH(node --version) - A Z.AI API key — create one at https://z.ai/manage-apikey/apikey-list
2. Build the agent
git clone https://github.com/stefandevo/glm-acp-agent.git
cd glm-acp-agent
npm install
npm run buildTake note of the absolute path to the freshly built entry point — Zed needs it (no ~, no $HOME shortcuts):
echo "$(pwd)/dist/index.js"3. Wire it into Zed
Open (or create) ~/.config/zed/settings.json and add an agent_servers entry. Pick one of the two API-key strategies below.
Option A — inline env block (simplest):
{
"agent_servers": {
"glm": {
"command": "node",
"args": ["/absolute/path/to/glm-acp-agent/dist/index.js"],
"env": { "Z_AI_API_KEY": "sk-…" }
}
}
}Option B — credentials file (no key in your editor settings):
Run the interactive setup once. From the project directory:
node dist/index.js --setup…or, if you npm install -g .'d the package:
glm-acp-agent --setupThe key is written to ~/.config/glm-acp-agent/credentials.json with 0600 permissions. Then drop the env block from the Zed entry — the agent will read the file on launch:
{
"agent_servers": {
"glm": {
"command": "node",
"args": ["/absolute/path/to/glm-acp-agent/dist/index.js"]
}
}
}If Z_AI_API_KEY is set in the environment and a credentials file exists, the environment variable wins.
4. Verify it works
- Save
settings.json. Zed reloads settings automatically. - Open the agent panel (use the command palette:
agent panel: toggle focus). - In the agent picker, select glm — Zed labels external agents by their
agent_serverskey. - Start a new thread and send a small prompt that exercises a tool, e.g.
Read package.json and tell me the project name. - You should see streaming text, a
read_filetool call awaiting permission, and (with a thinking-capable model likeglm-5.1) reasoning surfaced as a separate thought block.
5. Iterating on the agent
After editing the source, rebuild:
npm run buildZed spawns a fresh agent process per thread, so the easiest way to pick up changes is to start a new thread with the glm agent. If you see stale behavior, fully quit and reopen Zed — that guarantees no in-flight process is reused.
For a tighter inner loop, run npm run dev in a terminal so dist/ rebuilds on every save; you only need to start a new Zed thread to test the latest code.
6. Troubleshooting
- The "glm" agent doesn't appear in the picker. —
settings.jsonlikely has a JSON parse error, or your Zed build is older thanagent_serverssupport. Open the Zed log via the command palette (zed: open log) and look for settings errors. Error: Cannot find module '/.../dist/index.js'— you skippednpm run build, or the path inargsis wrong. It must be absolute and point at a file that exists.No API key found.— neitherZ_AI_API_KEYnor~/.config/glm-acp-agent/credentials.jsonis set. Use Option A or Option B in step 3.HTTP 401: Invalid API key— your key is wrong, expired, or for the wrong region. Rotate it on https://z.ai/manage-apikey/apikey-list.client does not advertise the … capability— Zed didn't expose that capability to the agent (e.g.terminalforrun_command/list_files). Ask the model to use a different tool, or upgrade Zed.
Neovim / VS Code / JetBrains / any ACP client
Any client that supports configuring an ACP agent via a command + args invocation works the same way: point it at node /absolute/path/to/glm-acp-agent/dist/index.js and supply Z_AI_API_KEY in the environment.
Authentication
The agent advertises two authentication methods at initialize time:
- An
agent-default method — the agent will read the API key itself, either from theZ_AI_API_KEYenvironment variable or from the credentials file written byglm-acp-agent --setup. - An
env_varmethod (experimental SDK extension) describing theZ_AI_API_KEYvariable so capable clients can prompt the user and inject it.
ACP clients that support the auth-methods proposal will use whichever method they recognise; clients that don't handle auth methods should set Z_AI_API_KEY themselves before launching the agent (or run glm-acp-agent --setup once and let the agent read it from disk).
Project Structure
src/
├── index.ts # Entry point – starts stdio connection or --setup flow
├── setup.ts # Interactive credential setup (`--setup`)
├── llm/
│ ├── glm-client.ts # OpenAI-compatible client for Z.AI / Zhipu AI
│ └── credentials.ts # API-key resolution (env var > credentials.json)
├── protocol/
│ ├── connection.ts # Sets up the ACP stdio connection
│ ├── agent.ts # GlmAcpAgent – ACP protocol implementation
│ └── session-store.ts # On-disk persistence for load/fork/resume
├── tools/
│ ├── definitions.ts # Tool JSON schemas (function-calling format)
│ └── executor.ts # ToolExecutor – dispatches tool calls
└── tests/
├── agent.test.ts # Protocol-level tests for GlmAcpAgent
├── credentials.test.ts # Credential resolution and --setup persistence
├── executor.test.ts # Tests for ToolExecutor
├── glm-client.test.ts # Tests for streaming / tool-call assembly
└── integration.test.ts # End-to-end tests over the real ACP ndjson transportBuilding & Testing
npm run build # one-shot TypeScript compilation → dist/
npm run dev # watch mode
npm test # build + run unit tests with the node:test runnerThe test suite covers:
- ACP protocol-version negotiation, capability advertising, and auth method shape
- Session lifecycle (
new/list/close, filter bycwd) - Prompt loop streaming, tool-call assembly, max-turn cap, cancellation, stop-reason mapping (
stop→end_turn,length→max_tokens,content_filter→refusal,tool_callsexhausted →max_turn_requests) - Content-block conversion (
text,resource_link, embeddedresource) - Token usage reporting on the
PromptResponse - Tool call lifecycle (
pending→in_progress→completed/failed) - Capability gating (tools refuse cleanly when the client did not advertise the required capability)
- Permission flows for
write_fileandrun_command(allow / reject / cancel) - Shell-quoted argument handling for
list_filesandrun_command - GLM streaming: text deltas,
reasoning_contentdeltas, multi-chunk tool-call assembly, and trailing usage - Image preprocessing through a mocked Vision MCP client and graceful degradation on Vision MCP failures
Troubleshooting
No API key found.— either setZ_AI_API_KEYin the environment, or runnode dist/index.js --setup(orglm-acp-agent --setupif installed globally) once to store the key on disk.HTTP 401: Invalid API key— your key is wrong or expired; rotate it on https://z.ai/manage-apikey/apikey-list.- The agent says "client does not advertise the … capability". — your ACP client doesn't expose that capability (e.g. terminal). Ask the model to use a different tool, or upgrade the client.
- Tools never get to run. — make sure the client is sending the
clientCapabilitiesfield ininitialize; the agent uses it to decide which tools to expose to the model.
License
Apache 2.0 – see LICENSE.
