| name | acp-dev-guide |
| description | ACP (Agent Client Protocol) 开发指南。提供协议规范、SDK 接口、Agent 兼容性等完整参考,指导开发者实现 ACP Agent 或 Client。触发方式:用户提及 "/acp-dev"、"ACP 开发"、"实现 ACP Agent"、"ACP SDK"、"Agent Client Protocol" 等关键词时激活。 |
| license | MIT |
| allowed-tools | Shell, Read, Write, Glob, Grep, WebFetch |
ACP (Agent Client Protocol) 开发指南
1. 协议概述
ACP 是一个标准化协议,定义了 Client(代码编辑器/IDE)与 Agent(AI 编码助手)之间的通信方式。类似于 LSP 对语言服务器的标准化作用,ACP 让任何兼容的 Agent 都能接入任何兼容的 Client。
协议特性:
- 基于 JSON-RPC 2.0
- 传输层:stdio(主要)、Streamable HTTP(草案)
- 消息类型:Method(请求-响应)、Notification(单向通知)
- 所有文件路径必须是绝对路径,行号从 1 开始
角色定义:
| 角色 | 职责 | 示例 |
|---|
| Client | 管理环境、处理用户交互、控制资源访问 | Zed、JetBrains IDE、VS Code、Neovim、AgentDispatch ClientNode |
| Agent | 使用 AI 自主修改代码,通常作为 Client 的子进程运行 | Claude Agent、Gemini CLI、Goose、自定义 Agent |
2. 官方文档与资源
2.1 核心文档
2.2 生态系统
3. SDK 获取与安装
3.1 TypeScript SDK
npm install @agentclientprotocol/sdk
核心类:
| 类 | 用途 |
|---|
AgentSideConnection | 构建 Agent — 提供 Agent 侧的 ACP 连接,实现 Client 接口 |
ClientSideConnection | 构建 Client — 提供 Client 侧的 ACP 连接,实现 Agent 接口 |
TerminalHandle | 终端操作句柄(由 createTerminal 返回) |
ndJsonStream | 将 stdin/stdout 包装为 NDJSON 双向流 |
3.2 Python SDK
pip install agent-client-protocol
uv add agent-client-protocol
核心类/函数:
| 类/函数 | 用途 |
|---|
Agent (基类) | 构建 Agent — 子类化并实现 prompt() 等方法 |
Client (接口) | 构建 Client — 实现 request_permission()、session_update() |
run_agent() | 启动 Agent 并监听 stdio |
spawn_agent_process() | 作为 Client 启动 Agent 子进程 |
spawn_client_process() | 作为 Agent 启动 Client 子进程 |
text_block() | 构建文本内容块 |
start_tool_call() | 构建 tool call 开始通知 |
update_tool_call() | 构建 tool call 更新通知 |
3.3 其他 SDK
4. 协议方法完整参考
4.1 Agent 实现的方法(Client → Agent)
必选方法
| 方法 | 说明 |
|---|
initialize | 协商协议版本和能力 |
session/new | 创建新会话 |
session/prompt | 发送用户消息 |
可选方法
| 方法 | 前提条件 | 说明 |
|---|
session/load | Agent 声明 loadSession 能力 | 恢复已有会话 |
session/set_mode | — | 切换 Agent 工作模式 |
authenticate | Agent 声明 authMethods | 认证 |
通知(Client → Agent,无响应)
| 通知 | 说明 |
|---|
session/cancel | 取消当前 prompt turn |
4.2 Client 实现的方法(Agent → Client)
必选方法
| 方法 | 说明 |
|---|
session/request_permission | 请求用户授权 tool call |
可选方法(取决于 Client 声明的能力)
| 方法 | 前提条件 | 说明 |
|---|
fs/read_text_file | clientCapabilities.fs.readTextFile | 读取文件内容 |
fs/write_text_file | clientCapabilities.fs.writeTextFile | 写入文件内容 |
terminal/create | clientCapabilities.terminal | 创建终端 |
terminal/output | clientCapabilities.terminal | 获取终端输出 |
terminal/release | clientCapabilities.terminal | 释放终端 |
terminal/wait_for_exit | clientCapabilities.terminal | 等待终端命令退出 |
terminal/kill | clientCapabilities.terminal | 杀死终端进程 |
通知(Agent → Client,无响应)
| 通知 | 说明 |
|---|
session/update | 会话更新(消息块、tool call、计划、命令列表、模式变更) |
4.3 Capability 协商
Client Capabilities(Client 在 initialize 请求中声明):
interface ClientCapabilities {
fs?: {
readTextFile?: boolean;
writeTextFile?: boolean;
};
terminal?: boolean;
}
Agent Capabilities(Agent 在 initialize 响应中声明):
interface AgentCapabilities {
loadSession?: boolean;
promptCapabilities?: {
image?: boolean;
audio?: boolean;
embeddedContext?: boolean;
};
mcp?: {
http?: boolean;
sse?: boolean;
};
}
5. 消息流完整生命周期
5.1 初始化
Client → Agent: initialize { protocolVersion, clientCapabilities, clientInfo }
Agent → Client: initialize response { protocolVersion, agentCapabilities, agentInfo, authMethods }
5.2 创建会话
Client → Agent: session/new { cwd, mcpServers }
Agent → Client: response { sessionId }
5.3 Prompt Turn 循环
Client → Agent: session/prompt { sessionId, prompt: ContentBlock[] }
┌─ Agent 处理循环 ─────────────────────────────────┐
│ │
│ Agent → Client: session/update (plan) │
│ Agent → Client: session/update (agent_message) │
│ │
│ if tool call: │
│ Agent → Client: session/update (tool_call) │
│ Agent → Client: request_permission (可选) │
│ Client → Agent: permission response │
│ Agent → Client: session/update (in_progress) │
│ Agent → Client: session/update (completed) │
│ → 将结果送回 LLM,继续循环 │
│ │
│ if cancelled: │
│ Client → Agent: session/cancel │
│ Agent → Client: prompt response (cancelled) │
│ │
└────────────────────────────────────────────────────┘
Agent → Client: session/prompt response { stopReason }
StopReason 类型:
| 值 | 含义 |
|---|
end_turn | LLM 正常完成 |
max_tokens | 达到 token 上限 |
max_model_requests | 达到模型请求次数上限 |
refused | Agent 拒绝继续 |
cancelled | Client 取消 |
5.4 session/update 通知类型
| sessionUpdate 值 | 含义 |
|---|
agent_message_chunk | Agent 消息文本块(流式) |
user_message_chunk | 用户消息块(会话恢复时) |
tool_call | 新的 tool call |
tool_call_update | tool call 状态更新 |
plan | Agent 执行计划 |
commands_update | 可用 slash commands 更新 |
mode_change | Agent 模式变更 |
6. 实现 ACP Agent(TypeScript)
6.1 最小 Agent
import { AgentSideConnection, ndJsonStream, PROTOCOL_VERSION } from '@agentclientprotocol/sdk';
import type { Agent, StopReason } from '@agentclientprotocol/sdk';
const stream = ndJsonStream(process.stdin, process.stdout);
const connection = new AgentSideConnection(
(conn): Agent => ({
initialize: async (params) => ({
protocolVersion: PROTOCOL_VERSION,
agentCapabilities: {},
agentInfo: { name: 'my-agent', version: '1.0.0' },
authMethods: [],
}),
newSession: async (params) => ({
sessionId: `sess_${Date.now()}`,
}),
prompt: async (params) => {
const text = params.prompt
.filter((b): b is { type: 'text'; text: string } => b.type === 'text')
.map((b) => b.text)
.join('\n');
await conn.sessionUpdate({
sessionId: params.sessionId,
update: {
sessionUpdate: 'agent_message_chunk',
content: { type: 'text', text: `Echo: ${text}` },
},
});
return { stopReason: 'end_turn' as StopReason };
},
}),
stream,
);
await connection.closed;
6.2 使用 Client 能力(读写文件、终端)
prompt: async (params) => {
const file = await conn.readTextFile({
sessionId: params.sessionId,
path: '/absolute/path/to/file.ts',
});
await conn.writeTextFile({
sessionId: params.sessionId,
path: '/absolute/path/to/output.ts',
content: '// generated code',
});
const terminal = await conn.createTerminal({
sessionId: params.sessionId,
command: 'npm',
args: ['test'],
cwd: '/project/root',
});
const output = await terminal.waitForExit();
const permission = await conn.requestPermission({
sessionId: params.sessionId,
toolCall: { toolCallId: 'call_001' },
options: [
{ optionId: 'allow', name: 'Allow', kind: 'allow_once' },
{ optionId: 'reject', name: 'Reject', kind: 'reject_once' },
],
});
return { stopReason: 'end_turn' };
}
7. 实现 ACP Agent(Python)
7.1 最小 Agent
import asyncio
from acp import Agent, PromptResponse, run_agent
class EchoAgent(Agent):
async def initialize(self, protocol_version, client_capabilities, **kw):
return {
"protocolVersion": 1,
"agentCapabilities": {},
"agentInfo": {"name": "echo-agent", "version": "1.0.0"},
"authMethods": [],
}
async def new_session(self, cwd, mcp_servers=None, **kw):
return {"sessionId": f"sess_{id(self)}"}
async def prompt(self, prompt, session_id, **kw) -> PromptResponse:
text = " ".join(b["text"] for b in prompt if b.get("type") == "text")
await self.connection.session_update(
session_id=session_id,
update={
"sessionUpdate": "agent_message_chunk",
"content": {"type": "text", "text": f"Echo: {text}"},
},
)
return PromptResponse(stop_reason="end_turn")
if __name__ == "__main__":
asyncio.run(run_agent(EchoAgent()))
7.2 作为 Client 启动 Agent
import asyncio
from acp import spawn_agent_process, text_block
from acp.interfaces import Client
class MyClient(Client):
async def request_permission(self, options, session_id, tool_call, **kw):
return {"outcome": {"outcome": "selected", "optionId": options[0]["optionId"]}}
async def session_update(self, session_id, update, **kw):
print(f"[{session_id}] {update}")
async def main():
async with spawn_agent_process(
MyClient(), "gemini", "--experimental-acp"
) as (conn, proc):
await conn.initialize(protocol_version=1)
session = await conn.new_session(cwd="/my/project", mcp_servers=[])
result = await conn.prompt(
session_id=session.session_id,
prompt=[text_block("Explain this codebase")],
)
print(f"Stop reason: {result.stop_reason}")
asyncio.run(main())
8. 实现 ACP Client(TypeScript)
AgentDispatch 的 ClientNode 就是一个 ACP Client 的实现。核心步骤:
import { ClientSideConnection, ndJsonStream, PROTOCOL_VERSION } from '@agentclientprotocol/sdk';
import { spawn } from 'node:child_process';
const proc = spawn('gemini', ['--experimental-acp'], {
stdio: ['pipe', 'pipe', 'pipe'],
cwd: '/project/root',
});
const stream = ndJsonStream(proc.stdout, proc.stdin);
const connection = new ClientSideConnection(
(_agent) => ({
requestPermission: async (params) => ({
outcome: { outcome: 'selected', optionId: params.options[0].optionId },
}),
readTextFile: async (params) => ({
content: await fs.readFile(params.path, 'utf-8'),
}),
writeTextFile: async (params) => {
await fs.writeFile(params.path, params.content, 'utf-8');
return null;
},
sessionUpdate: async (params) => {
console.log('Update:', JSON.stringify(params.update));
},
}),
stream,
);
const initResult = await connection.initialize({
protocolVersion: PROTOCOL_VERSION,
clientCapabilities: {
fs: { readTextFile: true, writeTextFile: true },
terminal: true,
},
clientInfo: { name: 'my-client', version: '1.0.0' },
});
const session = await connection.newSession({
cwd: '/project/root',
mcpServers: [],
});
const result = await connection.prompt({
sessionId: session.sessionId,
prompt: [{ type: 'text', text: 'Review the code in src/' }],
});
9. 常用 Agent 的 ACP 支持形态
核心规律:几乎所有 Agent CLI 的默认模式是交互式终端,需要特定子命令/标志才能进入 ACP stdio 模式。
9.1 分类总览
| 类型 | 特征 | 代表 |
|---|
| 子命令风格 | command acp 启动 ACP 模式 | Goose、OpenCode、Kiro、Kimi、OpenHands |
| 标志风格 | command --acp 启动 ACP 模式 | Cline、Augment(auggie)、Qwen Code(qwen)、Gemini(--experimental-acp) |
| 独立适配器 | 适配器二进制本身就是 ACP 模式 | claude-agent-acp、codex-acp、vibe-acp、pi-acp |
9.2 完整命令参考
9.3 常见错误
{ "command": "gemini" }
{ "command": "cline" }
{ "command": "gemini", "args": ["--experimental-acp"] }
{ "command": "cline", "args": ["--acp"] }
10. 调试与测试
10.1 手动测试 ACP Agent
通过管道直接向 Agent 发送 JSON-RPC 消息:
echo '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":1,"clientCapabilities":{"fs":{"readTextFile":true,"writeTextFile":true},"terminal":true},"clientInfo":{"name":"test","version":"0.1.0"}}}' | gemini --experimental-acp
10.2 使用 acpx CLI 测试
acpx 是一个通用 ACP CLI 客户端:
npx acpx gemini --experimental-acp
10.3 在 Zed 中验证
在 ~/.config/zed/settings.json 中添加自定义 Agent:
{
"agent_servers": {
"My Agent": {
"type": "custom",
"command": "node",
"args": ["./dist/my-agent.js"]
}
}
}
11. 协议 JSON Schema
完整的协议 JSON Schema 可从以下地址获取:
TypeScript SDK 中的类型定义直接从 Schema 生成,可作为实现参考。
12. 开发检查清单
实现新的 ACP Agent 或 Client 前,确认以下事项:
| 检查项 | 说明 |
|---|
| stdio 通信 | 通过 stdin/stdout 使用 NDJSON 格式的 JSON-RPC 2.0 |
| stderr 仅用于日志 | stdout 不能混入非 JSON-RPC 内容 |
initialize 握手 | 必须实现版本协商和能力交换 |
session/new | 必须返回唯一 sessionId |
session/prompt | 必须处理 prompt 并返回 StopReason |
session/cancel | 必须优雅处理取消,返回 cancelled stop reason |
| 能力检查 | 调用 Client 方法前必须检查对应 capability |
| 绝对路径 | 所有文件路径必须是绝对路径 |
| 行号从 1 开始 | 不是 0-based |