ACP: 에디터와 코딩 에이전트를 잇는 표준 프로토콜

요즘 goose의 모델 제공자(provider) 설정 화면을 열면 낯선 항목이 하나 보이는데요.
“Claude Code ACP”를 골라 보면 “ACP adapter not found”라는 경고와 함께 @agentclientprotocol/claude-agent-acp를 설치하라는 안내가 뜹니다.

Zed의 AI 기능을 쓰다 보면 에이전트 패널에서 클로드 코드를 띄울 때도 같은 이름을 만나게 되고요.
ACP는 이렇게 에디터나 앱이 외부 코딩 에이전트(coding agent)를 불러다 쓸 때 오가는 대화를 표준으로 정해 둔 프로토콜입니다. 이번 글에서는 ACP가 무엇이고 MCP와는 어떻게 다른지, 실제로 어떤 메시지가 오가는지 직접 캡처해서 살펴보겠습니다. 그리고 클로드 코드, Codex 같은 주요 에이전트를 ACP로 설치해서 Zed와 goose에 연결하는 방법까지 정리해 보겠습니다. 🔌
ACP란 무엇일까?
ACP(Agent Client Protocol)는 코드 에디터 같은 클라이언트(client)와 코딩 에이전트 사이의 통신 방식을 표준화한 개방형 프로토콜입니다. 2025년 8월 Zed Industries가 처음 공개했고, Google이 Gemini CLI를 첫 번째 외부 에이전트로 붙이면서 프로토콜 설계를 검증했어요. 같은 해 10월에는 JetBrains가 합류해 IntelliJ IDEA 같은 자사 IDE에 ACP를 들여오기로 했습니다.
ACP가 풀려는 문제는 언어 서버 프로토콜(Language Server Protocol, 이하 LSP)이 풀었던 문제와 닮았습니다. LSP가 나오기 전에는 에디터마다 언어별 자동 완성 플러그인을 따로 만들어야 했죠. 에디터가 M개, 언어가 N개면 통합 작업도 M×N개가 필요했는데, LSP가 이 사이에 공통 규약을 두면서 M+N개로 줄였습니다.
코딩 에이전트도 똑같은 상황을 겪고 있었어요. 클로드 코드, Codex, Gemini CLI, OpenCode 같은 에이전트가 쏟아지는데, 에디터가 이걸 하나씩 붙이려면 에이전트마다 전용 연동 코드를 짜야 했습니다. 반대로 에이전트 쪽도 VS Code, JetBrains, Neovim마다 확장 프로그램을 따로 만들어야 했고요. ACP는 에이전트가 ACP만 지원하면 ACP를 지원하는 어떤 클라이언트에서든 돌아가게 만듭니다.
지금은 Zed와 JetBrains 말고도 Neovim, Emacs, VS Code 확장, Obsidian 플러그인, 터미널 UI, 모바일 앱까지 수십 가지 클라이언트가 ACP를 지원하고 있어요.
MCP와는 무엇이 다를까?
이름이 비슷해서 MCP(Model Context Protocol)와 자주 헷갈리는데, 두 프로토콜은 서로 다른 구간을 담당합니다. MCP는 에이전트가 바깥의 도구와 데이터에 접근하는 방법을 정합니다. 반면 ACP는 사람이 보는 화면, 즉 클라이언트와 에이전트가 대화하는 방법을 정해요.
flowchart TB
accTitle: ACP와 MCP가 담당하는 구간
accDescr: 사용자가 쓰는 에디터나 앱은 ACP로 코딩 에이전트와 통신하고, 코딩 에이전트는 MCP로 GitHub, 데이터베이스 같은 외부 도구 서버와 통신한다.
user(["사용자"]) --> client["클라이언트<br/>Zed, JetBrains, goose"]
client <-- "ACP" --> agent["코딩 에이전트<br/>클로드 코드, Codex"]
agent <-- "MCP" --> tools["MCP 서버<br/>GitHub, DB, 브라우저"]
두 프로토콜은 경쟁하지 않고 맞물려 돌아갑니다.
뒤에서 보겠지만 ACP로 세션을 만들 때 클라이언트가 mcpServers 목록을 에이전트에게 넘길 수 있어요.
에디터에 등록해 둔 MCP 서버를 에이전트가 그대로 이어받아 쓰는 구조입니다.
참고로 IBM이 만든 Agent Communication Protocol도 약자가 ACP인데, 이건 에이전트끼리 통신하는 프로토콜로 이후 A2A 프로토콜에 흡수됐습니다. 이 글에서 말하는 ACP는 Zed가 시작한 Agent Client Protocol이에요.
메시지 흐름 살펴보기
ACP의 통신 방식은 단순합니다. 클라이언트가 에이전트를 하위 프로세스(subprocess)로 띄우고, 표준 입출력(stdio)으로 JSON-RPC 2.0 메시지를 한 줄씩 주고받아요. LSP와 거의 같은 방식이라 에디터 입장에서는 익숙한 구조입니다.
한 번의 대화는 대략 다음 순서로 흘러갑니다.
sequenceDiagram
accTitle: ACP 세션의 메시지 흐름
accDescr: 클라이언트가 initialize로 기능을 협상하고 session/new로 세션을 만든 뒤 session/prompt로 요청을 보낸다. 에이전트는 session/update 알림으로 진행 상황을 스트리밍하고, 필요하면 session/request_permission으로 권한을 묻고, 마지막에 stopReason으로 응답한다.
participant C as 클라이언트
participant A as 에이전트
C->>A: initialize
A-->>C: 지원 기능, 에이전트 정보
C->>A: session/new (cwd, mcpServers)
A-->>C: sessionId
C->>A: session/prompt
A--)C: session/update (메시지 조각, 도구 호출)
A->>C: session/request_permission
C-->>A: 허용 또는 거부
A--)C: session/update (도구 결과)
A-->>C: stopReason: end_turn
클로드 코드 ACP 어댑터(adapter)를 직접 띄워서 오가는 메시지를 캡처해 봤습니다.
먼저 클라이언트가 initialize로 자신이 지원하는 기능을 알리면, 에이전트가 자기 정보와 지원 기능으로 답합니다.
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": 1,
"clientCapabilities": {
"fs": { "readTextFile": false, "writeTextFile": false },
"terminal": false,
},
},
}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": 1,
"agentCapabilities": {
"promptCapabilities": { "image": true, "embeddedContext": true },
"mcpCapabilities": { "http": true, "sse": true },
"loadSession": true,
},
"agentInfo": {
"name": "@agentclientprotocol/claude-agent-acp",
"title": "Claude Agent",
"version": "0.81.2",
},
},
}
이 과정을 기능 협상(capability negotiation)이라고 부르는데요.
클라이언트가 fs.readTextFile을 true로 알리면 에이전트는 파일을 직접 읽지 않고 에디터에게 fs/read_text_file로 대신 읽어 달라고 요청할 수 있습니다.
이렇게 하면 아직 저장하지 않은 에디터 버퍼의 내용까지 에이전트가 볼 수 있어요.
이번 실험에서는 false로 두었기 때문에 에이전트가 자기 도구로 파일을 직접 다룹니다.
다음으로 session/new로 작업 디렉터리를 지정해 세션을 만들고, session/prompt로 요청을 보냅니다.
{
"jsonrpc": "2.0",
"id": 4,
"method": "session/prompt",
"params": {
"sessionId": "c7a74855-...",
"prompt": [
{
"type": "text",
"text": "greeting.txt 파일의 hello를 안녕하세요로 바꿔 줘.",
},
],
},
}
그러면 에이전트가 session/update 알림(notification)을 쏟아내기 시작합니다.
응답 텍스트는 agent_message_chunk로 조금씩 흘러오고, 도구를 쓸 때는 tool_call과 tool_call_update가 따라와요.
파일을 고치기 직전에는 에이전트가 거꾸로 클라이언트에게 session/request_permission 요청을 보냅니다.
{
"jsonrpc": "2.0",
"id": 0,
"method": "session/request_permission",
"params": {
"toolCall": {
"title": "Edit greeting.txt",
"kind": "edit",
"content": [
{
"type": "diff",
"path": ".../greeting.txt",
"oldText": "hello",
"newText": "안녕하세요",
},
],
},
"options": [
{ "optionId": "allow-once", "name": "Yes", "kind": "allow_once" },
{
"optionId": "allow-with-updates",
"name": "Yes, allow all edits during this session",
"kind": "allow_always",
},
{ "optionId": "reject", "name": "No", "kind": "reject_once" },
],
},
}
여기서 눈여겨볼 부분은 content에 담긴 diff입니다.
에이전트가 무엇을 바꿀지 구조화된 형태로 보내기 때문에 에디터는 이걸 받아서 자기만의 diff 화면으로 보여 줄 수 있어요.
터미널 출력을 긁어서 해석하던 방식과 비교하면 에디터 화면이 훨씬 풍부해지는 이유가 여기 있습니다.
클라이언트가 allow-once로 답하면 에이전트가 파일을 고치고, 마지막으로 처음 session/prompt 요청에 stopReason을 담아 응답하면서 한 턴(turn)이 끝납니다.
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"stopReason": "end_turn",
"usage": { "inputTokens": 6, "outputTokens": 799, "totalTokens": 104088 },
},
}
Bun으로 ACP 클라이언트 만들어 보기
메시지 구조를 알았으니 아주 작은 클라이언트를 직접 만들어 보겠습니다.
공식 TypeScript SDK(@agentclientprotocol/sdk)를 쓰면 더 편하지만, 프로토콜이 얼마나 단순한지 보여주려고 JSON-RPC를 손으로 주고받아 볼게요.
// 1. 어댑터를 하위 프로세스로 띄운다
const agent = Bun.spawn(["bunx", "@agentclientprotocol/claude-agent-acp"], {
stdin: "pipe",
stdout: "pipe",
});
let nextId = 1;
const pending = new Map<number, (result: any) => void>();
function send(message: object) {
const line = JSON.stringify({ jsonrpc: "2.0", ...message });
agent.stdin.write(line + "\n");
}
function request(method: string, params: object): Promise<any> {
const id = nextId++;
send({ id, method, params });
return new Promise((resolve) => pending.set(id, resolve));
}
// 2. 에이전트가 보내는 메시지를 한 줄씩 읽는다
async function listen() {
let buffer = "";
for await (const chunk of agent.stdout.pipeThrough(new TextDecoderStream())) {
buffer += chunk;
const lines = buffer.split("\n");
buffer = lines.pop()!;
for (const line of lines.filter(Boolean)) {
const message = JSON.parse(line);
if (message.method === "session/update") {
const { update } = message.params;
if (update.sessionUpdate === "agent_message_chunk") {
process.stdout.write(update.content.text);
} else if (update.sessionUpdate === "tool_call") {
console.log(`\n[도구 호출] ${update.title}`);
}
} else if (message.method === "session/request_permission") {
// 3. 권한 요청에는 첫 번째 선택지(한 번 허용)로 답한다
const option = message.params.options[0];
console.log(
`\n[권한 요청] ${message.params.toolCall.title} → ${option.name}`,
);
send({
id: message.id,
result: {
outcome: { outcome: "selected", optionId: option.optionId },
},
});
} else if (message.id !== undefined) {
pending.get(message.id)?.(message.result);
}
}
}
}
listen();
const init = await request("initialize", {
protocolVersion: 1,
clientCapabilities: {
fs: { readTextFile: false, writeTextFile: false },
terminal: false,
},
});
console.log(`연결됨: ${init.agentInfo.title} ${init.agentInfo.version}`);
const { sessionId } = await request("session/new", {
cwd: process.cwd(),
mcpServers: [],
});
await request("session/set_mode", { sessionId, modeId: "default" });
const { stopReason } = await request("session/prompt", {
sessionId,
prompt: [
{ type: "text", text: "greeting.txt 파일의 hello를 안녕하세요로 바꿔 줘." },
],
});
console.log(`\n종료 이유: ${stopReason}`);
agent.kill();
한 가지 짚고 넘어갈 점은 메시지가 양방향이라는 겁니다.
클라이언트만 요청하는 게 아니라 에이전트도 session/request_permission처럼 클라이언트에게 요청을 보내요.
그래서 listen()은 들어온 메시지에 method가 있으면 요청이나 알림으로, id만 있으면 내가 보낸 요청의 응답으로 구분해 처리합니다.
session/set_mode로 모드를 default(Manual)로 바꾼 것도 이유가 있습니다.
어댑터의 기본 모드는 auto라서 권한 판단을 클로드가 알아서 하는데, 권한 요청 흐름을 보려고 매번 묻는 모드로 바꿨어요.
클로드 코드에 로그인한 상태에서 실행하면 다음과 같이 나옵니다.
echo hello > greeting.txt
bun acp-client.ts
연결됨: Claude Agent 0.81.2
[도구 호출] Read File
[도구 호출] Edit
[권한 요청] Edit greeting.txt → Yes
`greeting.txt`에 있던 `hello`를 `안녕하세요`로 바꿨습니다.
종료 이유: end_turn
에이전트가 파일을 먼저 읽고, 고치기 전에 권한을 물은 다음 작업을 마쳤습니다. 50줄 남짓한 코드로 클로드 코드를 내 프로그램 안에서 부린 셈이에요. Zed나 JetBrains도 원리는 같고, 이 메시지들을 예쁜 화면으로 그려줄 뿐입니다.
어댑터 방식과 네이티브 방식
에이전트가 ACP를 지원하는 방식은 두 가지로 나뉩니다.
우선 에이전트 자체가 ACP를 말하는 네이티브(native) 방식이 있어요. Gemini CLI, GitHub Copilot CLI, OpenCode, goose가 여기에 속하고, 실행 파일에 플래그나 하위 명령을 붙이면 ACP 서버로 동작합니다.
그리고 에이전트가 ACP를 모르기 때문에 중간에 통역을 두는 어댑터 방식이 있습니다. 클로드 코드와 Codex가 대표적이에요. 어댑터가 ACP 메시지를 받아서 클로드 코드나 Codex가 알아듣는 호출로 바꾸고, 결과를 다시 ACP 메시지로 돌려줍니다.
각 에이전트를 ACP 모드로 띄우는 명령은 다음과 같습니다.
ACP 레지스트리에 등록된 값을 기준으로 정리했고, 모두 initialize 핸드셰이크가 되는지 직접 확인했어요.
| 에이전트 | 방식 | ACP 실행 명령 | 인증 방법 |
|---|---|---|---|
| 클로드 코드 | 어댑터 | claude-agent-acp | 클로드 코드 로그인 |
| Codex | 어댑터 | codex-acp | ChatGPT 로그인, API 키 |
| Gemini CLI | 네이티브 | gemini --acp | Google 로그인, API 키, Vertex AI |
| GitHub Copilot CLI | 네이티브 | copilot --acp | Copilot 로그인 |
| OpenCode | 네이티브 | opencode acp | OpenCode 로그인 |
| goose | 네이티브 | goose acp | goose에 설정한 제공자 |
네이티브 방식의 에이전트는 이미 설치해 두었다면 따로 할 일이 없습니다. 설치가 필요한 건 어댑터 방식의 두 에이전트인데, 차례로 살펴볼게요.
클로드 코드를 ACP로 쓰기
클로드 코드용 어댑터는 @agentclientprotocol/claude-agent-acp 패키지입니다.
클로드 코드 CLI를 감싸는 대신 Anthropic의 Claude Agent SDK로 구현되어 있어서, 클로드 코드와 같은 엔진과 설정을 그대로 씁니다.
전역으로 설치하면 claude-agent-acp 명령이 생깁니다.
bun add -g @agentclientprotocol/claude-agent-acp
npm을 쓴다면 다음과 같이 설치합니다.
npm install -g @agentclientprotocol/claude-agent-acp
인증은 따로 하지 않아도 됩니다.
어댑터가 클로드 코드의 로그인 정보를 그대로 가져다 쓰기 때문에, 터미널에서 claude를 한 번 실행해 로그인해 두었다면 Pro나 Max 구독으로 바로 동작해요.
API 키로 쓰고 싶다면 ANTHROPIC_API_KEY 환경 변수를 지정하면 됩니다.
CLAUDE.md, 스킬, 사용자 설정의 훅(hook)처럼 클로드 코드에 쌓아 둔 설정도 그대로 따라옵니다.
세션을 만들면 에이전트가 available_commands_update로 사용할 수 있는 슬래시 커맨드 목록을 보내 주는데, 제가 설치해 둔 스킬이 전부 들어 있었어요.
세션 응답에는 모드 목록도 함께 옵니다.
default(Manual), acceptEdits, plan, auto, bypassPermissions로, 클로드 코드의 권한 모드가 ACP 세션 모드로 그대로 노출된 거예요.
클라이언트는 session/set_mode나 session/set_config_option으로 모드와 모델을 바꿔요.
패키지 이름이 몇 번 바뀌었다는 점도 알아 두면 좋습니다.
처음에는 @zed-industries/claude-code-acp였다가 @zed-industries/claude-agent-acp를 거쳐 지금의 @agentclientprotocol/claude-agent-acp가 됐어요.
예전 이름의 패키지는 npm에서 사용 중단(deprecated) 처리되어 더 이상 업데이트되지 않으니, 옛날 글을 보고 설치했다면 새 이름으로 갈아타야 합니다.
Codex를 ACP로 쓰기
Codex용 어댑터는 @agentclientprotocol/codex-acp입니다.
패키지 안에 호환되는 Codex CLI가 함께 들어 있어서 Codex를 따로 설치하지 않아도 돼요.
bun add -g @agentclientprotocol/codex-acp
npm을 쓴다면 다음과 같이 설치합니다.
npm install -g @agentclientprotocol/codex-acp
인증 방법은 initialize 응답의 authMethods에 chat-gpt와 api-key 두 가지로 나옵니다.
ChatGPT Plus나 Pro 구독이 있다면 클라이언트에서 ChatGPT 로그인을 고르면 되고, API 키로 쓰려면 CODEX_API_KEY나 OPENAI_API_KEY를 지정하면 됩니다.
OPENAI_API_KEY=sk-... codex-acp
이미 설치해 둔 Codex 실행 파일을 쓰고 싶다면 CODEX_PATH로 경로를 넘기고, 처음 시작할 모드를 정하려면 INITIAL_AGENT_MODE에 read-only, workspace-write, agent, agent-full-access 중 하나를 넣으면 돼요.
agentclientprotocol 조직과 레지스트리
ACP는 Zed가 시작했지만 지금은 GitHub의 agentclientprotocol 조직에서 관리합니다.
처음에는 zed-industries 아래에 있던 저장소들이 이 조직으로 옮겨졌고, 어댑터 패키지 이름이 바뀐 것도 이 이전 때문이에요.
조직에 있는 주요 저장소를 역할별로 묶어 보면 이렇습니다.
먼저 agent-client-protocol 저장소에 프로토콜 명세(specification)와 JSON 스키마가 있습니다.
새 기능은 RFD(Request for Dialog) 문서로 제안되고 논의를 거쳐 명세에 들어가요.
그리고 에이전트나 클라이언트를 직접 만들 때 쓰는 SDK가 언어별로 있습니다.
typescript-sdk, python-sdk, rust-sdk, kotlin-sdk, java-sdk가 있고, 앞에서 손으로 짠 JSON-RPC 처리를 이 SDK들이 대신해 줍니다.
또 앞에서 설치한 claude-agent-acp와 codex-acp 어댑터도 이 조직에 있어요.
레지스트리를 보면 클로드 어댑터의 저자로 Anthropic, Zed Industries, JetBrains가 함께 올라가 있습니다.
마지막으로 registry 저장소는 ACP 에이전트의 목록입니다.
에이전트마다 agent.json 파일이 하나씩 있고, 여기에 설치 방법과 실행 명령이 적혀 있어요.
클로드 어댑터의 항목은 이렇게 생겼습니다.
{
"id": "claude-acp",
"name": "Claude Agent",
"version": "0.81.2",
"description": "ACP wrapper for Anthropic's Claude",
"repository": "https://github.com/agentclientprotocol/claude-agent-acp",
"authors": ["Anthropic", "Zed Industries", "JetBrains"],
"distribution": {
"npx": {
"package": "@agentclientprotocol/claude-agent-acp@0.81.2"
}
}
}
distribution에는 npm 패키지(npx), PyPI 패키지, 플랫폼별 바이너리 중 하나가 들어갑니다.
goose나 OpenCode처럼 바이너리로 배포하는 에이전트는 운영체제와 CPU별 다운로드 주소, 체크섬, 실행 인자(args: ["acp"])가 함께 적혀 있어요.
버전은 한 시간마다 도는 작업이 자동으로 올려 주고, 사용자 인증을 지원하는 에이전트만 CI 검증을 거쳐 등록됩니다.
Zed와 JetBrains IDE는 이 레지스트리를 읽어서 에이전트 목록을 보여 줍니다. 사용자가 목록에서 에이전트를 고르면 에디터가 알아서 내려받고 실행하는 구조예요.
Zed에서 ACP 에이전트 연결하기
Zed에서는 레지스트리에 있는 에이전트라면 에이전트 패널의 + 버튼에서 바로 골라 쓸 수 있습니다.
클로드 코드를 고르면 Zed가 어댑터를 알아서 설치하고 업데이트까지 해 줘요.
설정 파일에는 "type": "registry"로 기록되는데, 필요하면 여기에 환경 변수를 덧붙이면 됩니다.
{
"agent_servers": {
"claude-acp": {
"type": "registry",
"env": {
"CLAUDE_CODE_EXECUTABLE": "/usr/local/bin/claude"
}
}
}
}
레지스트리에 없는 에이전트나 로컬에 설치한 실행 파일을 쓰고 싶다면 "type": "custom"으로 명령을 직접 지정합니다.
예를 들어 goose를 Zed의 에이전트로 붙이려면 이렇게 적어요.
{
"agent_servers": {
"goose": {
"type": "custom",
"command": "goose",
"args": ["acp"]
}
}
}
앞의 표에 있는 명령이면 무엇이든 같은 방식으로 등록하면 됩니다. Zed에서 클로드 코드를 쓸 때 선택 영역을 넘기는 방법이나 단축키 설정은 Zed의 AI 기능에서 자세히 다뤘어요.
goose에서 클로드 코드 구독 쓰기
goose는 ACP의 양쪽에 모두 설 수 있다는 점이 재밌습니다.
앞에서처럼 goose acp로 띄우면 Zed 같은 클라이언트에 붙는 에이전트가 되고, 반대로 goose가 클라이언트가 되어 다른 에이전트를 불러다 쓸 수도 있어요.
처음에 본 “Claude Code ACP” 제공자가 바로 후자입니다.
goose는 이렇게 ACP로 불러 쓰는 에이전트를 ACP 제공자(ACP provider)라고 부르는데요. 토큰 단위로 과금되는 API 키 대신 이미 결제 중인 클로드 코드, ChatGPT, Amp 구독으로 goose를 쓰게 해 줍니다. 예전에 CLI를 직접 호출하던 방식의 제공자를 대체하는 기능이에요.
클로드 코드를 제공자로 쓰려면 먼저 앞에서처럼 claude-agent-acp 어댑터를 전역으로 설치하고 claude 로그인을 확인합니다.
그다음 goose의 제공자를 claude-acp로 지정해요.
export GOOSE_PROVIDER=claude-acp
export GOOSE_MODEL=sonnet
goose session
GOOSE_MODEL 값은 default(Opus), sonnet, haiku 중에서 고릅니다.
환경 변수 대신 goose configure에서 대화형으로 골라도 되고, 데스크톱 앱이라면 설정 화면에서 “Claude Code ACP”를 선택한 뒤 “Check again”을 누르면 어댑터를 다시 찾습니다.
goose의 권한 모드(GOOSE_MODE)는 클로드 코드의 모드로 이렇게 옮겨집니다.
auto:bypassPermissions로 옮겨지며 권한 확인을 건너뜀smart-approve:acceptEdits로 옮겨지며 파일 수정만 자동 승인approve:default로 옮겨지며 모든 작업마다 확인chat:plan으로 옮겨지며 계획만 세우고 실행하지 않음
goose에 등록한 확장(extension)은 앞에서 본 session/new의 mcpServers로 클로드 코드에 전달됩니다.
goose의 확장이 곧 MCP 서버이니, goose에서 쓰던 도구를 클로드 코드가 그대로 이어받는 거예요.
다만 세션 포크(fork)나 이어 하기(resume)는 아직 지원하지 않고, goose의 세션 ID와 ACP 세션 ID가 달라 기록을 맞춰 보기 어렵다는 한계가 있습니다.
Codex도 방법은 같습니다.
codex-acp를 설치하고 codex로 로그인한 뒤 GOOSE_PROVIDER=codex-acp, GOOSE_MODEL=current로 지정하면 됩니다.
마치며
ACP는 에디터와 코딩 에이전트 사이의 대화를 JSON-RPC 몇 가지 메서드로 표준화한 프로토콜입니다. MCP가 에이전트와 도구 사이를 잇는다면, ACP는 사람이 보는 화면과 에이전트 사이를 잇는 셈이에요. 클로드 코드와 Codex는 어댑터로, Gemini CLI나 OpenCode, goose는 자체 지원으로 ACP를 말하기 때문에 한 번 익혀 두면 에디터와 에이전트를 자유롭게 조합할 수 있습니다.
직접 에이전트나 클라이언트를 만들어 보고 싶다면 이 글의 50줄짜리 클라이언트에서 출발해 공식 SDK로 넘어가 보세요.
fs/read_text_file이나 terminal/create처럼 이 글에서 꺼 둔 클라이언트 기능을 켜 보면 에디터가 에이전트에게 무엇을 더 해 줄 수 있는지 감이 올 거예요.
더 자세한 내용은 ACP 공식 문서를 참고하세요.
This work is licensed under CC BY 4.0