Skip to content

ACP

Run zidane as an Agent Client Protocol agent in Zed and other editors, or embed the server.

Editors run zidane through the Agent Client Protocol (ACP) v1. They start zidane-acp and talk to it over stdio. Your own process can embed the same server from zidane/acp.

Install#

Terminal
npm install -g zidane
zidane-acp --help

From a checkout: bun install, then bun run acp --help. stdout carries only JSON-RPC frames. Logs go to stderr.

Connect from Zed#

Add a custom agent to Zed's settings.json:

{
  "agent_servers": {
    "Zidane": {
      "type": "custom",
      "command": "zidane-acp"
    }
  }
}

If Zed cannot find zidane-acp on PATH, use an absolute path. With no provider or model flags, zidane-acp:

  • offers a model for every provider with stored credentials
  • exposes Build and Plan modes
  • saves sessions as JSON files in acp-sessions/ in zidane's data folder (files and folders), so Zed reopens threads after a restart
  • gives sessions file read and write, edit, shell, directory listing, sub-agents, web_search and fetch_url

For a local or OpenAI-compatible provider:

settings.json
{
  "agent_servers": {
    "Zidane Local": {
      "type": "custom",
      "command": "zidane-acp",
      "args": ["--provider", "local", "--base-url", "http://localhost:8000/v1", "--model", "my-model"],
      "env": { "LOCAL_LLM_API_KEY": "no-key" }
    }
  }
}

Credentials#

ACP has no way for an editor to pass provider keys. zidane-acp looks in this order:

  1. Environment variables (<PROVIDER>_API_KEY, or the client's env block).
  2. zidane's credentials file, written when you sign in from the terminal app. OAuth tokens refresh from it. A variable already set wins.
  3. The legacy OAuth store, ~/.credentials.json.

Sign in once with the terminal app to keep keys out of settings.json. --no-credential-store uses environment variables only. ZIDANE_CREDENTIALS_PATH moves the credentials file (files and folders).

Models and modes#

Pick the models#

FlagUse
--models claude-sonnet-4-5,claude-opus-4-7Several models on one --provider (shared credentials)
--model-option anthropic:claude-sonnet-4-5 --model-option openrouter:google/gemini-2.5-pro:GeminiModels from several providers, repeatable, with an optional label. Credentials resolve on first use.

The first entry is the default (or --model, if listed). Switch models between turns. zidane refuses a switch during a prompt.

Modes#

ModeTools
Build (default)All
PlanRead, search and fetch tools only. Mutating calls are also blocked.

Both modes send the same tool list, so a switch keeps the prompt cache. A gate refuses the calls Plan does not allow, and the next prompt carries a note that tells the model about the mode.

Model options#

zidane turns the model catalog into ACP session config options, applied from the next prompt. Reasoning models get a Thinking selector with the effort levels. Model flags such as Anthropic's fast mode get On/Off toggles.

What zidane-acp supports#

AreaSupport
Sessionssession/new, session/load, session/resume, session/list, session/delete, session/close, session/cancel
Load or resumesession/load replays the history as message updates, without the notes zidane added for the model. session/resume reattaches without a replay.
Extra foldersadditionalDirectories on new and resumed sessions, saved with the session and returned by session/list
Prompt contentImages and audio when the model accepts them; embedded resources always
MCP servers from the editorstdio, HTTP and SSE, added to the ones from --mcp
Context meterA usage_update after each turn: tokens in context, the model's window and the cost so far
Client toolsTools the editor declares under _meta.zidane.clientTools in session/new, session/load or session/resume (details on GitHub)
The runstopReason
You cancelled itcancelled
It hit its turn limitmax_turn_requests
The last turn ran out of output tokensmax_tokens
A content filter or provider error ended itrefusal
Anything elseend_turn

Files and terminals in the editor#

When the editor offers these at initialize, zidane runs the tool through the editor's buffers and terminals:

Editor capabilityTool it backs
fs.readTextFileread_file
fs.writeTextFilewrite_file
terminalshell

Through an editor terminal, shell cannot run in the background. The agent gets an error and reruns the command in the foreground.

Permissions#

shell, write_file, edit, multi_edit and mutating client tools ask through ACP's session/request_permission:

OptionDoes
Allow onceRuns this call
Always allowRuns it, and runs the same call (same tool, same input) without asking for the rest of the session
Reject onceRefuses this call
Always rejectRefuses the same call for the rest of the session

The preset is basic, plus web_search and fetch_url. zidane-acp advertises no slash commands.

Other ACP clients#

Any ACP v1 client can launch zidane-acp [options]. It takes the zidane run flags plus three of its own. The prompt and output flags (--prompt, --json, --stream-json, --format, --json-schema) do nothing.

zidane-acp flags
FlagDefaultDescription
-h, --helpNonePrint help and exit.
-p, --prompt <text>NonePrompt to run.
-m, --model <id>NoneModel id.
-t, --presetbasicPreset name.One of basic.
-s, --system <text>NoneSystem prompt override.
--thinking <level>offThinking (reasoning effort) level.One of off, minimal, low, medium, high, xhigh, max, adaptive.
--provideranthropicModel provider.One of anthropic, openai, openrouter, cerebras, xai, google, google-vertex, arcee, baseten, neuralwatt, local, openai-compat.
--base-url <url>NoneBase URL for local/openai-compat.
--api-key-env <NAME>NoneEnv var with the local/openai-compat API key.
--headers-env <NAME>NoneEnv var with JSON headers for local/openai-compat.
--header 'Name: value'NoneExtra local/openai-compat header. Repeatable.
--visionNoneThe local/openai-compat model reads images.
--image-in-tool-resultNoneThe local/openai-compat model reads images in tool results.
--codemodeNoneAdd the codemode tool to a --json or --stream-json run.
--temperature <f>NoneSampling temperature for local/openai-compat.
--seed <n>NoneSampling seed for local/openai-compat.
-c, --contextprocessExecution context.One of process, docker, e2b, daytona.
--image <image>NoneDocker image for --context docker.
--cwd <path>NoneTool execution working directory.
--env KEY=VALUENoneSet an env var in the context (repeatable).
--pass-env VARNAMENoneForward a shell env var into the context (repeatable).
--pregame <file>Nonee2b/daytona: upload + run this setup script once the sandbox is ready, before prompting (shebang picks the interpreter; a non-zero exit aborts the run).
--sandbox <id>Nonee2b/daytona: connect to an existing sandbox by id instead of creating one (left running on exit).
--sandbox-on-destroykille2b/daytona: what to do with a created sandbox at the end of the run (pause preserves state for reconnect via --sandbox; leave relies on provider TTL).One of kill, pause, leave.
--mcp <json>NoneMCP config as JSON, or a path to a JSON file.
--session-db <path><data dir>/acp-sessionsSession store directory.
--session-id <id>NoneSession id to load or create.
--durabilityNoneWith --session-db: checkpoint runs; on resumes an interrupted run first (resume-ask never does here).One of on, resume-ask, off.
--json-schema <JSON>NoneSchema for result.output (implies --json).
--schema <file.json>NoneRead the output schema from a file.
--jsonNoneWait for the run, then print only the result JSON.
--stream-jsonNonePrint each turn as JSONL, then the result JSON.
--formatzidaneJSON shape. provider prints raw provider messages.One of zidane, provider.
--models <id,id,...>NoneAdvertise a model selector (all on --provider).
--model-option provider:model[:label]NoneAdd a cross-provider selectable model (repeatable).
--no-credential-storeNoneIgnore ~/.zidane credentials; use only env.

--cwd applies when the client sends no session folder. Ctrl+C closes the sessions and exits 130; SIGTERM exits 143.

Vim and Neovim#

editors/vim is a Vim 9 and Neovim 0.9+ plugin on zidane-acp. It opens a panel over any window. Its tools let the agent inspect your setup, read :help and run commands you approve.

vim
Plug 'Tahul/zidane', { 'rtp': 'editors/vim' }

Leaderz or :Zidane [prompt] opens it and sends your visual selection as context. See :help zidane.

Embed the server#

TypeScript
import { runAcpStdioServer } from 'zidane/acp'
import { anthropic } from 'zidane/providers'

const handle = runAcpStdioServer({
  provider: anthropic(),
  permission: { enabled: true },
})

process.once('SIGTERM', () => {
  void handle.close().finally(() => process.exit(143))
})
OptionDoes
commandsSlash commands to advertise with available_commands_update when a session starts
logoutA handler for the logout method. Setting it advertises the logout capability.
useClientFileSystem, useClientTerminalSet false to keep the local read_file, write_file and shell tools (both default to true)
permissionTurns on permission requests. toolMatcher picks which calls ask
freezeSystemPromptSet false to send the freshly built system prompt on every prompt (default true)

ACP sessions use a 1-hour prompt cache (behavior.cacheTtl: '1h'), because editor sessions go at a human pace. Set behavior.cacheTtl on the server options, agentOptions or the preset to change it.

Each session's system prompt is frozen on its first prompt and sent unchanged afterwards. Changes to skills, searchable tools or MCP instructions reach the model as notes. When the base prompt changed (for example a restart with a new system), the next prompt records the new one, and the provider reads the whole conversation again once.

zidane/acp also exports createAcpServer, JSON-RPC and content-mapping helpers, and ACP types for a custom transport.

Troubleshooting#

  1. Run zidane-acp --help to confirm the binary starts.
  2. Make sure that the editor uses the same command path and environment.
  3. In Zed, open the ACP logs from the Command Palette.
  4. Print nothing to stdout from a wrapper script. Clients expect only protocol frames.
  5. Set ZIDANE_DEBUG=1 to log prompt-cache breaks and prompt re-captures to stderr.

Reference#