Agent Communication Protocol (ACP)¶
이 문서는 Continuum Router의 ACP 구현을 설명합니다. 이 구현으로 라우터는 JSON-RPC 2.0 프로토콜을 통해 IDE 클라이언트와 코딩 어시스턴트를 위한 에이전트 백엔드로 동작할 수 있습니다.
개요¶
ACP(Agent Communication Protocol)를 사용하면 IDE 클라이언트(에디터, 터미널, 코딩 어시스턴트)가 Continuum Router를 LLM 에이전트로 삼아 표준 JSON-RPC 2.0 전송 위에서 통신할 수 있습니다. ACP가 활성화되면 라우터는 다음 두 역할을 동시에 수행합니다:
- OpenAI 호환 요청을 위한 HTTP API 게이트웨이 (기존 기능)
- ACP를 통한 IDE 통합용 stdio 기반 에이전트 백엔드
┌─────────────────────────────┐
│ IDE 클라이언트 (에디터) │
└──────────────┬──────────────┘
│
JSON-RPC 2.0 (NDJSON)
│
┌──────────────▼──────────────┐
│ Continuum Router (ACP) │
│ ┌────────────────────────┐ │
│ │ AcpMethodRouter │ │
│ │ SessionStore │ │
│ │ PermissionPolicy │ │
│ │ ToolExecutor │ │
│ │ McpConnectionManager │ │
│ └────────────────────────┘ │
└──────────────┬──────────────┘
│
┌──────────┴──────────┐
▼ ▼
┌────────┐ ┌───────────┐
│ OpenAI │ ... │ Anthropic │
└────────┘ └───────────┘
프로토콜 스택¶
ACP는 JSON-RPC 2.0을 와이어 프로토콜로 사용하고, stdin/stdout을 통해 줄바꿈으로 구분된 JSON(NDJSON) 형식으로 통신합니다. 각 줄이 하나의 완전한 JSON-RPC 메시지입니다.
메시지 유형¶
| 유형 | 설명 |
|---|---|
| Request | id가 있는 메서드 호출이며, 응답을 기대합니다 |
| Notification | id가 없는 메서드 호출이며, 응답을 기대하지 않습니다 |
| Response | 요청에 대한 결과 또는 오류 |
표준 오류 코드¶
| 코드 | 의미 |
|---|---|
-32700 |
파싱 오류 (형식이 잘못된 JSON) |
-32600 |
잘못된 요청 |
-32601 |
메서드를 찾을 수 없음 |
-32602 |
잘못된 매개변수 |
-32603 |
내부 오류 |
-32002 |
서버가 초기화되지 않음 |
-32001 |
애플리케이션 오류 (세션을 찾을 수 없음 등) |
프로토콜 라이프사이클¶
1. 초기화 핸드셰이크¶
클라이언트는 다른 어떤 메서드보다 먼저 반드시 initialize를 호출해야 합니다. 초기화가 완료될 때까지 다른 모든 메서드는 -32002로 거부됩니다.
요청:
{
"jsonrpc": "2.0",
"method": "initialize",
"params": {
"protocolVersion": 1,
"clientCapabilities": {
"readTextFile": true,
"writeTextFile": true,
"terminal": false
},
"clientInfo": {
"name": "MyEditor",
"version": "1.0.0"
}
},
"id": 1
}
응답:
{
"jsonrpc": "2.0",
"result": {
"protocolVersion": 1,
"agentCapabilities": {
"loadSession": true,
"image": false,
"audio": false,
"embeddedContext": false,
"mcp": true,
"mcpCapabilities": { "acp": true }
},
"agentInfo": {
"name": "Continuum Router",
"version": "1.3.0-beta.1"
},
"authMethods": []
},
"id": 1
}
2. 인증 (선택 사항)¶
authenticate 메서드는 모든 인증 시도를 수락하는 스텁(stub)이며, 라우터의 API 키 시스템과는 연결되어 있지 않습니다. 접근 제어 용도로 이 메서드에 의존하지 마세요.
{
"jsonrpc": "2.0",
"method": "authenticate",
"params": {
"method": "api_key",
"token": "your-api-key"
},
"id": 2
}
3. 세션 관리¶
세션 생성¶
응답:
{
"jsonrpc": "2.0",
"result": {
"sessionId": "550e8400-e29b-41d4-a716-446655440000",
"createdAt": "2026-03-08T12:00:00Z",
"updatedAt": "2026-03-08T12:00:00Z",
"mode": "code",
"historyLength": 0
},
"id": 3
}
기존 세션 로드¶
loadSession 에이전트 기능이 필요합니다.
{
"jsonrpc": "2.0",
"method": "session/load",
"params": { "sessionId": "550e8400-e29b-41d4-a716-446655440000" },
"id": 4
}
세션 모드 변경¶
응답:
진행 중인 작업 취소¶
세션의 취소 토큰을 발동시킵니다. 새 토큰이 다시 설치되므로 세션은 계속 사용할 수 있습니다.
4. 프롬프트 전송¶
LLM에 프롬프트를 보내고 session/update 알림으로 응답을 받습니다.
요청:
{
"jsonrpc": "2.0",
"method": "session/prompt",
"params": {
"sessionId": "550e8400-...",
"content": [
{ "type": "text", "text": "Explain the architecture of this project" }
],
"model": "gpt-4o",
"stream": true,
"temperature": 0.7,
"maxTokens": 4096
},
"id": 7
}
스트리밍 알림은 id가 없는 JSON-RPC 알림으로 전송됩니다:
{
"jsonrpc": "2.0",
"method": "session/update",
"params": {
"sessionId": "550e8400-...",
"content": [{ "type": "text", "text": "The architecture..." }]
}
}
최종 응답:
종료 사유¶
| 값 | 설명 |
|---|---|
end_turn |
모델이 응답을 자연스럽게 완료함 |
max_tokens |
토큰 한도에서 응답이 잘림 |
refusal |
모델이 응답 생성을 거부함 |
cancelled |
session/cancel로 작업이 취소됨 |
콘텐츠 블록 유형¶
| 유형 | 필드 | 설명 |
|---|---|---|
text |
text |
일반 텍스트 콘텐츠 |
image |
data, mediaType |
Base64로 인코딩된 이미지 데이터 |
resource |
uri, mediaType (선택 사항) |
외부 리소스 참조 |
도구 호출 보고¶
LLM이 도구 호출을 생성하면, 에이전트는 도구 호출 상태 업데이트를 담은 session/update 알림으로 이를 클라이언트에 보고합니다.
도구 종류¶
| 종류 | 설명 | 기본 권한 |
|---|---|---|
read |
파일 읽기 또는 디렉터리 목록 조회 | 자동 허용 |
edit |
파일 쓰기 또는 수정 | 항상 확인 |
delete |
파일 또는 디렉터리 삭제 | 항상 확인 |
move |
파일 이름 변경 또는 이동 | 항상 확인 |
search |
파일 내용 검색 | 자동 허용 |
execute |
셸 명령 실행 | 항상 확인 |
think |
내부 추론 | 자동 허용 |
fetch |
HTTP 요청 | 확인 |
other |
분류되지 않은 작업 | 확인 |
도구 호출 라이프사이클¶
┌─────────┐
│ Pending │
└────┬────┘
│
분류 + 권한 확인
│
┌────▼────┐
┌─────────│ Ask │─────────┐
│ └─────────┘ │
allow/always reject/always
│ │
┌────▼──────┐ ┌──────▼──┐
│InProgress │ │ Failed │
└────┬──────┘ └─────────┘
│
도구 실행
│
┌────▼──────┐
│ Completed │
└───────────┘
권한 위임¶
권한 시스템은 클라이언트가 어떤 도구 작업을 허용할지 제어할 수 있게 합니다:
권한 요청 알림:
{
"jsonrpc": "2.0",
"method": "session/request_permission",
"params": {
"sessionId": "...",
"toolCallId": "tc-1",
"title": "Edit File",
"kind": "edit",
"locations": [{ "uri": "file:///src/main.rs", "startLine": 42 }]
}
}
클라이언트 응답 옵션:
allow_once: 이 작업 한 번만 허용allow_always: 세션 동안 이 종류의 모든 작업 허용reject_once: 이 작업 한 번만 거부reject_always: 세션 동안 이 종류의 모든 작업 거부
캐시된 always_* 결정은 세션이 유지되는 동안 지속됩니다.
MCP-over-ACP 브리지¶
MCP 브리지는 기존 MCP(Model Context Protocol) 서버를 ACP 채널을 통해 터널링할 수 있게 합니다. 덕분에 IDE 클라이언트는 별도의 연결을 관리하지 않고도 MCP 서버에 접근할 수 있습니다.
MCP 서버에 연결¶
응답:
메시지 라우팅¶
{
"jsonrpc": "2.0",
"method": "mcp/message",
"params": {
"connectionId": "uuid-...",
"message": {
"jsonrpc": "2.0",
"method": "tools/list",
"id": 1
}
},
"id": 11
}
연결 해제¶
{
"jsonrpc": "2.0",
"method": "mcp/disconnect",
"params": { "connectionId": "uuid-..." },
"id": 12
}
서버 ID 검증 규칙¶
- 최대 128자
- ASCII 영숫자, 하이픈, 밑줄, 점만 사용 가능
- 이중 밑줄 사용 불가 (도구 네임스페이스용으로 예약)
- 영숫자로 시작하고 끝나야 함
프로세스 라이프사이클¶
mcp/connect가 stdio 파이프를 연결한 MCP 서버 프로세스를 스폰합니다- 백그라운드 태스크가 stdin/stdout을 양방향으로 브리지합니다 (메시지 버퍼 64개)
- stderr는 라우터의
tracing::warn로그로 전달됩니다 mcp/disconnect는 SIGTERM을 보내고 5초간 기다린 뒤, 필요하면 SIGKILL을 보냅니다
기능 협상¶
초기화 과정에서 클라이언트와 에이전트는 서로의 기능(capability) 선언을 교환하며, 협상된 결과에 따라 해당 세션에서 사용할 수 있는 기능이 결정됩니다.
클라이언트 기능¶
| 기능 | 설명 |
|---|---|
readTextFile |
클라이언트가 로컬 파일시스템에서 파일 읽기를 지원합니다 |
writeTextFile |
클라이언트가 로컬 파일시스템에 파일 쓰기를 지원합니다 |
terminal |
클라이언트가 터미널 명령 실행을 지원합니다 |
에이전트 기능¶
| 기능 | 설명 |
|---|---|
loadSession |
에이전트가 이전 세션 재개를 지원합니다 |
image |
에이전트가 이미지 콘텐츠 블록을 지원합니다 |
audio |
에이전트가 오디오 콘텐츠 블록을 지원합니다 |
embeddedContext |
에이전트가 임베디드 컨텍스트 블록을 지원합니다 |
mcp |
에이전트가 MCP-over-ACP 브리지를 지원합니다 |
에이전트 레지스트리 메타데이터¶
라우터는 에이전트 레지스트리 디스커버리에 쓰이는 agent.json 메타데이터 문서를 생성하며, GET /admin/acp/agent.json으로 조회할 수 있습니다.
출력 예제:
{
"name": "continuum-router",
"displayName": "Continuum Router",
"version": "1.3.0-beta.1",
"description": "Local LLM inference agent with multi-backend routing",
"transport": [
{ "type": "stdio" }
],
"capabilities": {
"loadSession": true,
"mcp": true
}
}
Admin API 엔드포인트¶
| 엔드포인트 | 설명 |
|---|---|
GET /admin/acp/status |
ACP 서버 상태 (활성화 여부, 전송, 기능) |
GET /admin/acp/sessions |
활성 ACP 세션 목록 |
GET /admin/acp/agent.json |
동적 에이전트 레지스트리 메타데이터 |
설정¶
ACP는 하위 호환성을 위해 기본적으로 비활성화되어 있습니다. 활성화하려면 config.yaml에 다음을 추가하세요:
acp:
enabled: true
transport:
stdio:
enabled: true
agent:
name: "Continuum Router"
version: "1.0.0"
description: "Local LLM inference agent"
capabilities:
load_session: true
image: false
audio: false
embedded_context: false
mcp: true
# ACP 세션의 기본 모델 (선택 사항)
default_model: "gpt-4o"
# 시스템 프롬프트 주입 (선택 사항)
system_prompt: "You are a helpful coding assistant."
# 코딩 에이전트 모드 활성화
coding_agent_mode: true
permissions:
# 기본 정책: ask_always, allow_read 또는 allow_all
default_policy: ask_always
auto_allow:
- read
- search
- think
always_ask:
- edit
- delete
- execute
sessions:
max_concurrent: 10
idle_timeout: "1h"
storage: "memory"
mcp:
max_connections_per_session: 5
allowed_servers: [] # 비어 있으면 설정된 모든 서버 허용
server_spawn_timeout: "10s"
설정 레퍼런스¶
최상위 옵션¶
| 옵션 | 유형 | 기본값 | 설명 |
|---|---|---|---|
enabled |
bool | false |
ACP 서브시스템 활성화/비활성화 |
default_model |
string | 없음 | ACP 세션의 모델 선택 오버라이드 |
system_prompt |
string | 없음 | 모든 ACP 요청에 시스템 프롬프트 주입 |
coding_agent_mode |
bool | false |
코딩 에이전트 시스템 프롬프트 활성화 |
전송 옵션¶
| 옵션 | 유형 | 기본값 | 설명 |
|---|---|---|---|
transport.stdio.enabled |
bool | true |
stdio 전송 활성화 |
권한 옵션¶
| 옵션 | 유형 | 기본값 | 설명 |
|---|---|---|---|
permissions.default_policy |
enum | ask_always |
기본 정책: ask_always, allow_read, allow_all |
permissions.auto_allow |
list | [read, search, think] |
확인 없이 자동 허용되는 도구 종류 |
permissions.always_ask |
list | [edit, delete, execute] |
항상 권한 확인이 필요한 도구 종류 |
세션 옵션¶
| 옵션 | 유형 | 기본값 | 설명 |
|---|---|---|---|
sessions.max_concurrent |
int | 10 |
최대 동시 세션 수 |
sessions.idle_timeout |
string | "1h" |
정리 전 유휴 타임아웃 |
sessions.storage |
string | "memory" |
저장소 백엔드: memory 또는 file |
sessions.storage_path |
string | 없음 | 파일 기반 저장소 경로 |
MCP 브리지 옵션¶
| 옵션 | 유형 | 기본값 | 설명 |
|---|---|---|---|
mcp.max_connections_per_session |
int | 5 |
세션당 최대 MCP 연결 수 |
mcp.allowed_servers |
list | [] |
허용할 서버 ID (비어 있으면 모두 허용) |
mcp.server_spawn_timeout |
string | "10s" |
MCP 서버 프로세스 스폰 타임아웃 |
모듈 구조¶
src/acp/
├── mod.rs # 모듈 루트
├── jsonrpc.rs # JSON-RPC 2.0 프로토콜 타입
├── types.rs # ACP 콘텐츠 블록과 메시지
├── error.rs # 오류 타입
├── capabilities.rs # 기능 협상
├── config.rs # 설정 구조체
├── config_validate.rs # 설정 검증
├── metadata.rs # 에이전트 레지스트리 메타데이터 (agent.json)
├── transport/
│ ├── mod.rs # AcpTransport 트레이트
│ └── stdio.rs # stdin/stdout 위의 NDJSON
├── session.rs # 세션 라이프사이클과 저장소
├── router.rs # 메서드 디스패치 라우터
├── handlers/
│ ├── initialize.rs # 프로토콜 초기화
│ ├── authenticate.rs # 인증 (스텁)
│ ├── session.rs # session/* 메서드
│ ├── prompt.rs # session/prompt 핸들러
│ ├── prompt_stream.rs # SSE 스트리밍 프로세서
│ └── translate.rs # ACP → OpenAI 형식 변환
├── tools/
│ ├── mod.rs # 도구 호출 타입
│ ├── classifier.rs # 도구 종류 분류
│ ├── permission.rs # 권한 정책 엔진
│ ├── executor.rs # 도구 실행 오케스트레이터
│ └── client_methods.rs # 클라이언트 측 메서드 (fs/*, terminal/*)
├── mcp_bridge/
│ ├── mod.rs # MCP 기능
│ ├── handlers.rs # connect/message/disconnect 핸들러
│ ├── connection.rs # 연결 관리자
│ ├── process.rs # MCP 서버 프로세스 스폰
│ └── schema_translate.rs # MCP ↔ ACP 스키마 변환
└── admin.rs # Admin API 엔드포인트