참고
ACP 지원은 GitHub Copilot 명령 줄 인터페이스 (CLI) 안에 포함되어 있으며 공개 미리 보기 변경될 수 있습니다.
개요
ACP(에이전트 클라이언트 프로토콜)는 클라이언트(예: 코드 편집기 및 IDE)와 에이전트(예: 코파일럿 CLI)간의 통신을 표준화하는 프로토콜입니다. 이 프로토콜에 대한 자세한 내용은 공식 소개를 참조하세요.
사용 사례
- IDE 통합: 모든 편집기 또는 개발 환경에 지원을 빌드 Copilot 합니다.
- CI/CD 파이프라인: 자동화된 워크플로에서 에이전트 코딩 작업을 오케스트레이션합니다.
- 사용자 지정 프런트 엔드: 특정 개발자 워크플로에 대한 특수 인터페이스를 만듭니다.
- 다중 에이전트 시스템: 표준 프로토콜을 사용하여 다른 AI 에이전트와 조정 Copilot 합니다.
ACP 서버 시작
명령 옵션을 --acp``copilot 사용하여 CLI의 ACP 서버를 시작합니다. 또는 --stdio 옵션을 사용하여 전송 모드를 --port 지정할 수 있습니다. 전송 모드가 지정되지 않은 경우 서버는 기본적으로 stdio 모드로 설정됩니다.
모든 세션에 적용되는 옵션
ACP session/new 요청을 사용하면 클라이언트가 사용할 작업 디렉터리 및 MCP 서버와 같은 몇 가지 세션 매개 변수만 설정할 수 있습니다. 도구 필터링 또는 추론 설정은 수행하지 않습니다. 이러한 옵션을 구성하려면 서버를 시작할 때 해당 옵션을 전달합니다. 서버는 값을 저장하고 연결하는 모든 클라이언트에 대해 값을 만들거나 로드하는 모든 세션에 대한 초기 구성으로 적용합니다. 연결하는 클라이언트는 이 값들을 선택하지 않으며, 서버를 시작하는 쪽에서 선택합니다.
| 서버 옵션 | 허용되는 값 | 모든 세션에 미치는 영향 |
|---|---|---|
--available-tools=TOOL ... | 따옴표로 묶인, 쉼표로 구분된 도구 이름 목록 | 세션은 나열된 도구만 사용할 수 있습니다. |
--excluded-tools=TOOL ... | 따옴표로 묶이고 쉼표로 구분된 도구 이름 목록 | 나열된 도구가 세션에서 제거됩니다. |
--effort=LEVEL, --reasoning-effort=LEVEL | ||
low, medium, high, xhigh 또는 max | 세션의 초기 추론 작업을 설정합니다. |
예를 들어, 이 명령은 모든 세션이 최대 수준의 추론을 사용하고 bash 및 view 도구만 노출하는 서버를 시작합니다.
copilot --acp --port 3000 --effort=max --available-tools="bash,view"
연결된 클라이언트가 해당 서버에 대해 열리는 모든 세션은 해당 설정을 상속합니다. 서버가 시작될 때 값이 고정되므로 클라이언트는 세션 session/new별로 변경할 수 없습니다.
stdio 모드
stdio 모드는 ACP 서버를 시작할 때 기본적으로 유추됩니다. 또한 중의성 해소를 위해 --stdio 옵션을 사용할 수 있습니다.
copilot --acp --stdio
TCP 모드
옵션과 --port 함께 --acp 옵션이 제공되면 서버가 TCP 모드에서 시작됩니다.
copilot --acp --port 3000
stdio와 TCP 중에서 선택
두 전송 모드 모두 줄 바꿈으로 구분된 JSON(NDJSON)으로 인코딩된 동일한 ACP 메시지를 전달합니다. 클라이언트가 서버에 연결하는 방법과 서버의 수명 주기를 관리하는 방법만 다릅니다. 두 모드는 서로 배타적입니다. --stdio와 --port를 모두 전달하면 거부됩니다.
| Aspect | stdio 모드 | TCP 모드 |
|---|---|---|
| 클라이언트 연결 방법 | 클라이언트는 자식 프로세스로 시작하고 copilot --acp 프로세스의 표준 입력 및 출력을 통해 메시지를 교환합니다. | 서버는 클라이언트가 네트워크 소켓을 통해 연결하는 TCP 수신기를 엽니다. 기본적으로 루프백 주소 127.0.0.1에 바인딩됩니다. |
| 클라이언트 수 | 단일 클라이언트- 서버를 생성하고 파이프를 소유하는 프로세스입니다. | 수신기는 각각 자체 에이전트 연결로 처리되는 소켓 연결을 허용합니다. |
| 수명 주기 | 부모 프로세스에 연결됩니다. 입력 스트림이 닫히면(부모 프로세스가 종료되거나 파이프를 닫기 때문에) 서버가 자동으로 종료됩니다. | 단일 클라이언트와 독립적입니다. 서버는 중지될 때까지 포트에서 계속 수신 대기합니다(예: Ctrl+C). |
| 표준 출력 | 로그 또는 다른 텍스트에 사용할 수 없도록 NDJSON 프로토콜 스트림용으로 예약됩니다. | 프로토콜 트래픽이 소켓을 통해 이동하므로 다른 용도로는 무료입니다. |
각 모드를 사용하는 경우:
- 편집기, IDE 또는 스크립트가 코파일럿 CLI를 하위 프로세스로 직접 생성하는 경우 stdio 모드를 사용합니다. 이 설정은 프로세스가 시작될 때 transport 연결이 자동으로 설정되고 종료될 때 해제되므로, IDE 통합을 위한 기본값이자 권장 설정입니다.
- 클라이언트가 파이프 대신 소켓을 통해 서버에 연결해야 하는 경우(예: 별도의 프로세스 또는 컨테이너에서) 또는 알려진 포트의 수명이 긴 서버에 연결할 때 TCP 모드 를 사용합니다.
예: ACP 서버와 통합
다음 예제는 Copilot의 ACP 서버와 상호 작용하여 GitHub Copilot 명령 줄 인터페이스 (CLI)를 사용하는 클라이언트 애플리케이션입니다. STdio 모드에서 ACP 서버를 시작하고, 세션을 열고, 프롬프트를 입력하라는 메시지를 보내고, 스트리밍된 응답을 출력합니다.
ACP 서버와 프로그래밍 방식으로 상호 작용하기 위한 라이브러리 에코시스템이 증가하고 있습니다. 이 예제에서는 ACP TypeScript 라이브러리를 사용합니다.
이 예제를 실행하려면 다음 종속성이 필요합니다.
- Node.js 버전 18 이상.
- GitHub Copilot 명령 줄 인터페이스 (CLI), 설치 및 인증되었습니다.
@agentclientprotocol/sdkACP TypeScript 라이브러리를 제공하는 패키지입니다.npm install @agentclientprotocol/sdk을 실행하여 설치합니다.
import * as acp from "@agentclientprotocol/sdk";
import { spawn } from "node:child_process";
import { Readable, Writable } from "node:stream";
import * as readline from "node:readline/promises";
async function main() {
const executable = process.env.COPILOT_CLI_PATH ?? "copilot";
// ACP uses standard input/output (stdin/stdout) for transport; we pipe these for the NDJSON stream.
const copilotProcess = spawn(executable, ["--acp", "--stdio"], {
stdio: ["pipe", "pipe", "inherit"],
});
if (!copilotProcess.stdin || !copilotProcess.stdout) {
throw new Error("Failed to start Copilot ACP process with piped stdio.");
}
// Create ACP streams (NDJSON over stdio)
const output = Writable.toWeb(copilotProcess.stdin) as WritableStream<Uint8Array>;
const input = Readable.toWeb(copilotProcess.stdout) as ReadableStream<Uint8Array>;
const stream = acp.ndJsonStream(output, input);
const client: acp.Client = {
async requestPermission(params) {
// This example should not trigger tool calls; if it does, refuse.
return { outcome: { outcome: "cancelled" } };
},
async sessionUpdate(params) {
const update = params.update;
if (update.sessionUpdate === "agent_message_chunk" && update.content.type === "text") {
process.stdout.write(update.content.text);
}
},
};
const connection = new acp.ClientSideConnection((_agent) => client, stream);
await connection.initialize({
protocolVersion: acp.PROTOCOL_VERSION,
clientCapabilities: {},
});
const sessionResult = await connection.newSession({
cwd: process.cwd(),
mcpServers: [],
});
process.stdout.write("Session started!\n");
// Ask the user to enter a prompt instead of using a hard-coded one.
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const promptText = await rl.question("Enter a prompt: ");
rl.close();
const promptResult = await connection.prompt({
sessionId: sessionResult.sessionId,
prompt: [{ type: "text", text: promptText }],
});
process.stdout.write("\n");
if (promptResult.stopReason !== "end_turn") {
process.stderr.write(`Prompt finished with stopReason=${promptResult.stopReason}\n`);
}
// Best-effort cleanup
copilotProcess.stdin.end();
copilotProcess.kill("SIGTERM");
await new Promise<void>((resolve) => {
copilotProcess.once("exit", () => resolve());
setTimeout(() => resolve(), 2000);
});
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
import * as acp from "@agentclientprotocol/sdk";
import { spawn } from "node:child_process";
import { Readable, Writable } from "node:stream";
import * as readline from "node:readline/promises";
async function main() {
const executable = process.env.COPILOT_CLI_PATH ?? "copilot";
// ACP uses standard input/output (stdin/stdout) for transport; we pipe these for the NDJSON stream.
const copilotProcess = spawn(executable, ["--acp", "--stdio"], {
stdio: ["pipe", "pipe", "inherit"],
});
if (!copilotProcess.stdin || !copilotProcess.stdout) {
throw new Error("Failed to start Copilot ACP process with piped stdio.");
}
// Create ACP streams (NDJSON over stdio)
const output = Writable.toWeb(copilotProcess.stdin) as WritableStream<Uint8Array>;
const input = Readable.toWeb(copilotProcess.stdout) as ReadableStream<Uint8Array>;
const stream = acp.ndJsonStream(output, input);
const client: acp.Client = {
async requestPermission(params) {
// This example should not trigger tool calls; if it does, refuse.
return { outcome: { outcome: "cancelled" } };
},
async sessionUpdate(params) {
const update = params.update;
if (update.sessionUpdate === "agent_message_chunk" && update.content.type === "text") {
process.stdout.write(update.content.text);
}
},
};
const connection = new acp.ClientSideConnection((_agent) => client, stream);
await connection.initialize({
protocolVersion: acp.PROTOCOL_VERSION,
clientCapabilities: {},
});
const sessionResult = await connection.newSession({
cwd: process.cwd(),
mcpServers: [],
});
process.stdout.write("Session started!\n");
// Ask the user to enter a prompt instead of using a hard-coded one.
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const promptText = await rl.question("Enter a prompt: ");
rl.close();
const promptResult = await connection.prompt({
sessionId: sessionResult.sessionId,
prompt: [{ type: "text", text: promptText }],
});
process.stdout.write("\n");
if (promptResult.stopReason !== "end_turn") {
process.stderr.write(`Prompt finished with stopReason=${promptResult.stopReason}\n`);
}
// Best-effort cleanup
copilotProcess.stdin.end();
copilotProcess.kill("SIGTERM");
await new Promise<void>((resolve) => {
copilotProcess.once("exit", () => resolve());
setTimeout(() => resolve(), 2000);
});
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
예제를 실행하려면 다음을 수행합니다.
-
위의 코드를 .라는
acp-client.ts파일에 저장합니다. -
별도의 빌드 단계 없이 TypeScript를 직접 실행하는 파일을
npx tsx실행합니다.npx tsx acp-client.ts
슬래시 명령 사용
GitHub Copilot 명령 줄 인터페이스 (CLI)'의 기본 제공 슬래시 명령은 ACP를 통해 실행할 수 있습니다. 하나를 호출하려면 텍스트가 명령이고 단일 텍스트 콘텐츠 블록으로 전달되는 일반 프롬프트로 보냅니다. 예를 들면 다음과 같습니다 /context``/session info. 서버는 명령을 인식하고 이를 직접 실행합니다. 즉, /usage 또는 /context와 같은 정보성 명령은 모델을 호출하지 않고 해당 출력을 반환하는 반면, /plan 또는 /review와 같은 동작 명령은 해당 에이전트 작업을 시작합니다. 어느 쪽이든 명령 텍스트는 질문으로 모델에 전송되지 않습니다.
사용 가능한 명령 검색
서버는 표준 ACP available_commands_update 세션 알림을 통해 지원하는 명령을 보급합니다. 세션이 만들어지거나 로드된 후, 그리고 설정이 변경될 때마다 다시 전송됩니다(예: 기술 로드가 완료될 때). 이 보급 목록은 ACP를 통해 실행할 수 있는 신뢰할 수 있는 항상 최신 명령 집합이며 클라이언트는 일반적으로 명령 메뉴에 표시합니다.
표시된 목록에는 다음 항목이 포함됩니다:
- 기본 제공 명령어(예:
/compact,/context,/usage,/env,/model,/mcp,/plan,/review,/research,/session및/rename) - 활성화되어 사용자가 호출할 수 있는 기술(
/SKILL-NAME명령으로 표시됨)
클라이언트가 직접 등록한 명령은 해당 클라이언트에 다시 알려지지 않습니다.
클라이언트에서 목록에 액세스
목록이 요청에 대한 응답이 아닌 알림으로 도착하므로 요청 시 가져올 방법이 없습니다. 클라이언트는 session/update 알림을 처리하고 유형이 available_commands_update인 업데이트에 응답하여 이에 액세스합니다. 각 항목에는 name(선행 슬래시 없음), description, 그리고 명령의 인수를 설명하는 선택적 input.hint가 있습니다. 설정이 변경될 때마다 알림이 다시 전송되므로 각 알림은 캐시한 목록을 완전히 대체합니다.
다음 sessionUpdate 핸들러는 앞서 제시한 예의 client 객체를 확장하여 공개된 명령을 캡처합니다.
// Track the latest advertised commands for the session.
let availableCommands: acp.AvailableCommand[] = [];
const client: acp.Client = {
async sessionUpdate(params) {
const update = params.update;
if (update.sessionUpdate === "available_commands_update") {
// This notification is a full snapshot—replace any cached list.
availableCommands = update.availableCommands;
for (const command of availableCommands) {
// command.name has no leading slash; invoke it by sending "/<name>" as a prompt.
console.log(`/${command.name} — ${command.description}`);
}
return;
}
// ...handle other updates, such as agent_message_chunk
},
// ...other client methods, such as requestPermission
};
// Track the latest advertised commands for the session.
let availableCommands: acp.AvailableCommand[] = [];
const client: acp.Client = {
async sessionUpdate(params) {
const update = params.update;
if (update.sessionUpdate === "available_commands_update") {
// This notification is a full snapshot—replace any cached list.
availableCommands = update.availableCommands;
for (const command of availableCommands) {
// command.name has no leading slash; invoke it by sending "/<name>" as a prompt.
console.log(`/${command.name} — ${command.description}`);
}
return;
}
// ...handle other updates, such as agent_message_chunk
},
// ...other client methods, such as requestPermission
};
안내된 명령 중 하나를 실행하려면 슬래시 명령 사용에 설명된 대로 단일 텍스트 콘텐츠 블록에 해당 이름을 프롬프트로 보내세요(예: { type: "text", text: "/context" }).
ACP를 통해 사용할 수 없는 명령
대화형 터미널 인터페이스에 의존하는 슬래시 명령은 ACP 서버에서 처리되지 않습니다. 여기에는 선택기, 대화 상자 또는 전체 화면 보기(예: /diff, /resume, /theme, /settings``/login, /help``/tasks및 /undo)를 여는 명령이 포함됩니다. 일반적으로 명령이 목록에 표시되지 available_commands_update 않으면 ACP를 통해 실행되지 않습니다. 서버는 텍스트를 일반 프롬프트로 처리하고 실행하는 대신 모델에 전달합니다.
ACP 클라이언트에는 대화형 선택기가 없으므로 일반적으로 하위 메뉴가 열리는 기본 제공 명령은 해당 옵션을 텍스트로 반환합니다. 직접적인 결과를 얻으려면 하위 명령을 명시적으로 지정하세요. 예를 들어 /session 또는 /mcp만 단독으로 사용하는 대신 /session info 또는 /mcp list를 사용하세요.
슬래시 명령의 전체 목록은 코파일럿 CLI을 참조하세요.