본문으로 건너뛰기

Cursor와 함께 사용하는 Agent Client Protocol (ACP)

Agent Client Protocol with Cursor

Agent Client Protocol(ACP)은 편집기 클라이언트와 코딩 에이전트가 공유된 JSON-RPC 기반 인터페이스를 통해 서로 대화할 수 있게 하는 개방형 표준입니다. 모든 편집기가 모든 에이전트에 자체 브리지를 구축하는 대신, 클라이언트와 에이전트 모두 ACP를 사용하고 그 짝은 상호 교체 가능해집니다. Cursor는 cursor-agent를 통해 그 대화의 에이전트 측으로 작동할 수 있으며, 이는 ACP 호환 클라이언트가 Cursor의 에이전트를 구동할 수 있음을 의미합니다. 이 가이드는 프로토콜, 세션 수명 주기, ACP를 통해 Cursor를 노출하는 방법, 실제 메시지 흐름, 자체 클라이언트 구축 방법, 그리고 현재의 격차를 설명합니다.

Agent Client Protocol이란 무엇인가​

ACP는 편집기 클라이언트가 코딩 에이전트와 대화하는 방식을 정의하는 개방형 표준입니다. 이 프로토콜은 Zed에 의해 대중화되었으며, Zed는 이를 사용하여 편집기가 다양한 에이전트를 구동하게 합니다. 하지만 의도적으로 편집기 독립적이고 에이전트 독립적입니다.

핵심 설계 선택은 단순합니다:

  • 전송은 stdio를 통한 JSON-RPC입니다. 클라이언트는 에이전트를 하위 프로세스로 생성하고 stdin과 stdout에서 줄바꿈으로 구분된 JSON-RPC 메시지를 교환합니다.
  • 양쪽 모두 요청과 알림을 보낼 수 있습니다. 클라이언트는 에이전트에게 세션을 시작하고 프롬프트를 보내도록 요청하고, 에이전트는 도구 실행 권한을 클라이언트에 요청하고 진행 상황을 스트리밍합니다.
  • 세션은 명시적입니다. 작업은 newSession으로 생성된 세션 안에서 이루어지며, 이는 작업 디렉터리와 옵션을 담습니다.

전송이 순수 stdio이고 메시지가 순수 JSON이므로 ACP는 프로세스를 생성할 수 있는 모든 곳에서 작동합니다: 로컬 편집기, 터미널 도구, 헤드리스 CI 러너.

ACP는 MCP가 아닙니다

MCP는 에이전트를 외부 도구 및 데이터 소스에 연결합니다. ACP는 편집기 클라이언트를 에이전트에 연결합니다. 서로 다른 문제를 해결하며 흔히 함께 사용됩니다: ACP 에이전트 자체가 MCP 클라이언트일 수 있습니다.

ACP 아키텍처​

아키텍처에는 세 부분이 있습니다: 클라이언트, 에이전트, 그 사이의 전송. 클라이언트는 UI와 권한을 소유하고, 에이전트는 모델과 도구를 소유합니다.

계층역할예시
클라이언트대화를 렌더링하고, 사용자 입력을 수집하고, 권한 요청에 응답Zed, 사용자 정의 편집기, 헤드리스 스크립트
에이전트모델을 실행하고, 도구를 실행하고, 업데이트를 스트리밍cursor-agent acp, 기타 ACP 에이전트
전송JSON-RPC 메시지를 양방향으로 이동stdio를 통한 줄바꿈 구분 JSON

세션 수명 주기는 고정된 순서입니다. 한 번 이해하면 메시지 흐름이 대본처럼 읽힙니다.

initialize -> client and agent agree on protocol version and capabilities
newSession -> client asks the agent to create a session with a cwd and options
prompt -> client sends user input into the session
session/update -> agent streams progress, tool calls, and messages back (notification)
permission request-> agent asks the client to approve a tool action
cancel -> client aborts the current turn

에이전트는 클라이언트에 먼저 묻지 않고 보호된 리소스에 대해 행동하지 않습니다. 그 권한 왕복이 UI를 자율 에이전트의 제어 아래 유지하는 것입니다.

ACP가 중요한 이유​

ACP는 편집기를 에이전트에서 분리하기 때문에 중요합니다. 그 가치는 세 곳에서 나타납니다.

편집기 이식성​

Cursor와 다른 클라이언트가 모두 ACP를 사용하면 맞춤 통합 없이 어느 쪽에서든 같은 에이전트를 사용할 수 있습니다. 에이전트 구성, 모델 선택 및 규칙은 편집기가 아니라 에이전트와 함께 이동합니다.

모든 호환 클라이언트에서 Cursor의 에이전트 재사용​

각 편집기에서 에이전트 동작을 재구현하는 대신, 모든 ACP 클라이언트를 cursor-agent로 지정하여 그 클라이언트의 UI 안에서 Cursor의 추론, 도구 사용 및 모델 선택을 얻을 수 있습니다. 이는 Cursor가 아닌 편집기로 표준화했지만 Cursor의 에이전트 동작을 원하는 팀에 특히 유용합니다.

헤드리스 자동화​

전송이 stdio이므로 편집기 없이 스크립트나 CI 작업에서 에이전트를 구동할 수 있습니다. 이는 파이프라인에서 스크립트화된 반복 가능한 에이전트 실행을 가능하게 합니다.

# A headless run is just a client that writes JSON-RPC to the agent's stdin
cat initialize.json newSession.json prompt.json | cursor-agent acp
헤드리스 실행에도 권한이 필요합니다

스크립트에서 에이전트를 구동할 때 도구 호출에 "approve"를 클릭할 사람이 없습니다. 권한 모드를 미리 결정하고, 민감한 자격 증명이 있는 환경에서는 절대 자동 실행을 기본으로 하지 마세요.

cursor-agent로 ACP를 통해 Cursor 노출하기​

cursor-agent는 연결의 ACP 에이전트 측으로 작동할 수 있는 CLI입니다. Cursor CLI를 설치하는 것과 같은 방식으로 설치하세요.

# Install via npm
npm install -g cursor-agent

# Or install via the Cursor CLI script
curl https://cursor.com/install -fsS | bash

# Confirm it is on your PATH
cursor-agent --version

ACP 모드에서 실행하기​

ACP 진입점은 하위 명령입니다. 실행하면 프로세스가 stdio에서 JSON-RPC를 사용하고 클라이언트가 연결되기를 기다립니다.

# Start the agent as an ACP server, talking JSON-RPC over stdio
cursor-agent acp

# Useful flags
cursor-agent acp --cwd /path/to/project # set the working directory
cursor-agent acp --model claude-sonnet-4-5 # pin a model for the session
cursor-agent acp --verbose # log messages for debugging

인증​

에이전트는 프롬프트를 제공하기 전에 자격 증명이 필요합니다. 한 번 로그인하면 자격 증명이 로컬에 저장됩니다.

# Interactive login, opens a browser flow
cursor-agent login

# Check current auth status
cursor-agent status

# Log out and clear stored credentials
cursor-agent logout

에이전트가 시작되지만 모든 프롬프트가 실패하면 거의 항상 인증 누락 또는 만료가 원인입니다. 다른 것을 디버깅하기 전에 cursor-agent status를 실행하세요.

세션 구성하기​

세션은 작업 디렉터리와 해당 대화에 대해 에이전트의 행동 방식을 결정하는 옵션으로 생성됩니다.

모델 선택​

모델은 시작 시 고정하거나 세션별로 선택할 수 있습니다. 세션별로 전달하면 단일 에이전트 프로세스가 서로 다른 모델 선호를 가진 여러 클라이언트를 지원할 수 있습니다.

{
"method": "session/new",
"params": {
"cwd": "/home/dev/project",
"model": "claude-sonnet-4-5",
"mcpServers": []
}
}

컨텍스트 창 사용량 보고​

일부 클라이언트는 컨텍스트 창이 얼마나 찼는지 표시합니다. 에이전트는 클라이언트가 지원하는 경우 세션 업데이트의 일부로 토큰 사용량을 보고합니다.

{
"method": "session/update",
"params": {
"sessionId": "sess_01HXYZ",
"update": {
"type": "usage",
"inputTokens": 12480,
"outputTokens": 2310,
"contextLimit": 200000
}
}
}

권한 모드와 작업 디렉터리​

권한 모드는 에이전트가 도구를 실행하기 전에 묻는지 여부를 결정합니다. 대화형 클라이언트에서는 기본이 묻는 것입니다. 헤드리스 자동화에서는 의도적으로 선택합니다.

  • 도구별 요청: 에이전트가 권한 요청을 보내고 기다립니다. 가장 안전하며 대화형 편집기에 가장 좋습니다.
  • 자동 실행: 에이전트가 묻지 않고 작업 디렉터리 내에서 실행합니다. 빠르며 샌드박스에서만 적절합니다.
  • 읽기 전용: 에이전트가 읽고 검색할 수 있지만 절대 쓰지 않습니다. 검토 전용 세션에 좋습니다.

newSession의 cwd는 에이전트가 프로젝트로 취급하는 것을 제한합니다. 그 밖의 파일은 건드리지 않아야 합니다.

예제 JSON-RPC 메시지 흐름​

다음은 최소하지만 완전한 대화입니다. 모든 메시지는 전선에서 한 줄의 JSON입니다.

초기화​

클라이언트는 initialize로 시작하고, 에이전트는 기능과 프로토콜 버전으로 응답합니다.

{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": 1,
"clientCapabilities": {
"fs": { "readTextFile": true, "writeTextFile": true },
"terminal": true
}
}
}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": 1,
"agentCapabilities": {
"promptCapabilities": { "image": false },
"loadSession": true
}
}
}

새 세션​

기능이 합의되면 클라이언트가 세션을 만들고 ID를 돌려받습니다.

{
"jsonrpc": "2.0",
"id": 2,
"method": "session/new",
"params": { "cwd": "/home/dev/project", "mcpServers": [] }
}
{
"jsonrpc": "2.0",
"id": 2,
"result": { "sessionId": "sess_01HXYZ" }
}

프롬프트​

클라이언트가 사용자의 턴을 보냅니다. 이는 요청이며 에이전트가 이를 확인하지만 실제 내용은 스트리밍 알림으로 도착한다는 점에 유의하세요.

{
"jsonrpc": "2.0",
"id": 3,
"method": "session/prompt",
"params": {
"sessionId": "sess_01HXYZ",
"content": [{ "type": "text", "text": "Add input validation to the signup route." }]
}
}

스트리밍 업데이트​

에이전트가 작업하면서 ID 없이 session/update 알림을 내보냅니다. 단방향이기 때문입니다.

{
"jsonrpc": "2.0",
"method": "session/update",
"params": {
"sessionId": "sess_01HXYZ",
"update": { "type": "agent_message_chunk", "text": "Reading src/routes/signup.ts" }
}
}

권한 요청​

보호된 작업 전에 에이전트가 클라이언트에 묻습니다. 클라이언트는 표준 JSON-RPC 요청 메커니즘을 통해 응답합니다.

{
"jsonrpc": "2.0",
"id": "perm-1",
"method": "session/request_permission",
"params": {
"sessionId": "sess_01HXYZ",
"toolCall": {
"title": "Write file",
"path": "src/routes/signup.ts"
}
}
}
{
"jsonrpc": "2.0",
"id": "perm-1",
"result": { "outcome": { "outcome": "selected", "optionId": "allow" } }
}

취소​

턴을 중단하려면 클라이언트가 session/cancel을 보냅니다. 에이전트는 현재 작업을 중지하고 취소 중지 사유로 프롬프트 요청을 마칩니다.

{
"jsonrpc": "2.0",
"method": "session/cancel",
"params": { "sessionId": "sess_01HXYZ" }
}

자체 ACP 클라이언트 구축하기​

최소 클라이언트는 에이전트를 생성하고, stdin에 JSON-RPC 줄을 쓰고, stdout에서 줄바꿈으로 구분된 JSON을 파싱합니다. 아래 스니펫은 TypeScript에서의 형태를 보여줍니다.

import { spawn } from "node:child_process";
import { createInterface } from "node:readline";

const agent = spawn("cursor-agent", ["acp", "--cwd", process.cwd()], {
stdio: ["pipe", "pipe", "inherit"],
});

let nextId = 1;

function send(method: string, params: unknown): void {
const message = { jsonrpc: "2.0", id: nextId++, method, params };
agent.stdin.write(JSON.stringify(message) + "\n");
}

// Read newline-delimited JSON-RPC from the agent
const reader = createInterface({ input: agent.stdout });
reader.on("line", (line) => {
if (!line.trim()) return;
const msg = JSON.parse(line);

// Notifications have no id
if (!("id" in msg)) {
if (msg.method === "session/update") {
console.log("update:", JSON.stringify(msg.params.update));
}
return;
}

// A request from the agent needs a reply
if (msg.method === "session/request_permission") {
const reply = {
jsonrpc: "2.0",
id: msg.id,
result: { outcome: { outcome: "selected", optionId: "allow" } },
};
agent.stdin.write(JSON.stringify(reply) + "\n");
return;
}

console.log("response:", JSON.stringify(msg.result ?? msg.error));
});

// Kick off the session
send("initialize", { protocolVersion: 1, clientCapabilities: {} });
send("session/new", { cwd: process.cwd(), mcpServers: [] });

기억해야 할 핵심 규칙: 모든 요청에는 id가 있고 일치하는 응답을 기대하며, 알림에는 절대 id가 없고, 에이전트도 사용자에게 요청을 보낼 수 있으므로 리더가 양방향을 처리해야 합니다.

원시 전선을 로깅하세요

클라이언트 통합이 오작동하면 로직을 건드리기 전에 양방향의 모든 줄을 로깅하세요. ACP 버그는 보통 누락된 id, 잃어버린 줄바꿈 또는 결코 전송되지 않은 응답이며, 원시 전선이 세 가지를 모두 분명하게 만들어 줍니다.

한계와 현재 격차​

ACP는 아직 어리고 모든 Cursor 기능이 이를 통해 노출되지는 않습니다. 그 위에 구축하기 전에 무엇을 포기하는지 알아두세요.

시스템 프롬프트 사용자 정의 없음​

ACP 세션 옵션은 현재 클라이언트가 IDE 내부의 .cursor/rules처럼 에이전트의 시스템 프롬프트를 교체하거나 확장하게 하지 않습니다. 프로젝트에 구성된 규칙은 여전히 에이전트가 적용하지만, 클라이언트가 자체 규칙을 주입할 수는 없습니다.

불완전한 컨텍스트 창 보고​

사용량 보고가 존재하지만 완전하거나 실시간임이 보장되지 않습니다. 정확한 컨텍스트 미터를 원하는 클라이언트는 ACP 사용량 업데이트에만 의존하지 않아야 합니다.

IDE 대비 기능 동등성 차이​

여러 IDE 기능이 ACP를 통해 사용할 수 없거나 축소됩니다:

  • 클라이언트가 구현하지 않는 한 클라이언트 내부에서 제안된 편집의 시각적 diff 검토가 없습니다.
  • 백그라운드 에이전트와 클라우드 에이전트는 이 경로를 통해 구동되지 않습니다.
  • 일부 도구 및 MCP 동작이 IDE의 Agent 모드와 다릅니다.
  • Tab 완성은 별도 시스템이며 ACP의 일부가 전혀 아닙니다.

문제 해결​

에이전트를 찾을 수 없음​

클라이언트가 cursor-agent를 생성하고 ENOENT를 받으면 바이너리가 생성하는 프로세스의 PATH에 없는 것입니다. 생성 호출에 절대 경로를 사용하고, GUI 클라이언트가 터미널과 다른 PATH를 상속할 수 있음을 기억하세요.

# Find the absolute path and use it in the client config
which cursor-agent

인증 오류​

모든 프롬프트가 인증 오류로 실패하면 에이전트에 유효한 자격 증명이 없는 것입니다. cursor-agent status를 실행한 다음 cursor-agent login을 실행하고, 클라이언트가 로그인한 사용자나 홈 디렉터리와 다른 환경에서 실행되고 있지 않은지 확인하세요.

스트리밍 업데이트 없음​

프롬프트 요청은 반환되지만 업데이트가 결코 도착하지 않으면 클라이언트가 알림을 올바르게 파싱하지 않을 가능성이 큽니다. 알림에는 id가 없으므로 응답만 처리하는 리더는 이를 버립니다. 줄 리더가 줄바꿈에서 내보내고 파서가 id 없는 메시지를 처리하는지 확인하세요.

클라이언트가 권한 프롬프트를 결코 받지 못함​

에이전트가 묻지 않고 도구를 실행하면 세션이 자동 실행 권한 모드로 생성되었거나 클라이언트가 initialize 중에 권한 요청을 처리할 기능을 광고하지 않은 것입니다. 세션 옵션과 보낸 기능을 모두 확인하세요.

증상원인해결
생성 시 ENOENT에이전트 바이너리가 PATH에 없음생성 호출에 절대 경로 사용
모든 프롬프트 실패인증 누락 또는 만료cursor-agent status 실행 후 cursor-agent login
session/update 메시지 없음리더가 id 있는 응답만 처리id 없는 알림 처리
권한 프롬프트 없음자동 실행 모드 또는 클라이언트 기능 누락도구별 요청 설정 및 기능 광고
프롬프트가 영원히 멈춤요청에 대한 응답이 결코 전송되지 않음모든 에이전트 요청에 일치하는 id로 응답
응답 파싱 오류줄바꿈 없이 연결된 메시지줄당 정확히 하나의 JSON 객체 쓰기 및 읽기

ACP는 편집기-에이전트 관계를 계약으로 바꾸고, 그 계약이 프로토콜을 사용하는 어디에서든 Cursor의 에이전트를 사용할 수 있게 만드는 것입니다. 위의 메시지 흐름으로 시작하고, 전선을 로깅하면 통합은 재작성이 아니라 적은 양의 배관 작업이 됩니다.