A2A CLI 소개
A2A CLI는 A2A(Agent2Agent) 프로토콜 (
[A2A 알아보기 1편] A2A 프로토콜: 프레임워크가 서로 달라도 에이전트들끼리 서로 작업을 위임하게 하는 표준 규격)을 따르는 에이전트를 터미널에서 찾고 호출하고 관리하는 공식 명령줄 클라이언트입니다. 배포한 에이전트가 잘 동작하는지 확인하려는 개발자는 보통 짧은 스크립트를 하나 써서 카드를 받아 오고, 메시지를 보내고, 돌아온 JSON을 눈으로 읽습니다. 확인할 에이전트가 늘고 언어와 전송 방식이 갈리면 이 일회용 스크립트도 같이 늘어납니다.
A2A CLI는 그 자리를 하나의 명령 체계로 대체합니다. 에이전트 카드를 읽어 전송 방식을 스스로 고르고, 지정하지 않는 한 태스크가 끝날 때까지 기다리며, 어느 언어로 만든 에이전트를 상대하든 같은 출력 형식을 내놓습니다. 프로토콜에 맞는 JSON을 그대로 내놓는 -o json 옵션과 종료 코드가 함께 있어서 자동화 스크립트나 CI에서도 클라이언트 라이브러리 없이 에이전트를 부를 수 있습니다.
만들어진 배경은 난립한 구현을 정리하는 쪽이었습니다. 언어별 SDK와 커뮤니티가 각자 만든 CLI들이 이미 있었는데 명령 이름과 옵션, 출력 형태, 전송 방식 처리가 서로 달랐고, 이들을 하나의 합의된 표현으로 수렴시킨 결과가 이 저장소입니다. 초기 코드는 A2A Go SDK에 들어 있던 CLI를 가져와 출발했고, 별도의 명세 문서가 명령 분류와 출력 계약, 폴링과 스트리밍 규칙, 종료 코드를 규정합니다. 본 게시물에 실린 출력은 모두 0.2.0 릴리즈 바이너리를 내려받아 직접 실행해 받은 것입니다.
A2A CLI가 기존 방식과 다른 점
프로토콜 명세는 하나지만 그것을 말하는 방법은 세 가지이고, 에이전트마다 지원하는 것이 다릅니다. 직접 스크립트를 쓸 때는 이 차이를 매번 알아야 합니다. 대상 에이전트가 JSON-RPC를 쓰는지 REST를 쓰는지, 스트리밍을 지원하는지, 태스크가 끝날 때까지 몇 초를 기다려야 하는지를 사람이 정해 넣어야 합니다. A2A CLI는 이 결정을 카드에서 읽어 대신하며, 직접 만든 스크립트와 견주면 차이는 다음과 같습니다:
| 항목 | 직접 만든 스크립트 | A2A CLI |
|---|---|---|
| 전송 방식 선택 | 사람이 코드에 고정 | 에이전트 카드의 선호 순서를 읽어 결정 |
| 결과 대기 | 폴링 루프를 직접 구현 | 종료 상태까지 대기가 기본, --async로 끄기 |
| 스트리밍 | 서버가 지원하지 않으면 실패 | --stream, 미지원이면 폴링으로 대체 |
| 출력 | 응답 JSON을 직접 다룸 | 사람이 읽는 텍스트, -o json, -o jsonl |
| 자격 증명 | 스크립트마다 따로 | .env와 환경 변수로 공통 관리 |
자동화에 쓸 때 한 가지는 미리 알아 두어야 합니다. CLI 명세는 종료 코드가 "CLI가 제 일을 했는가"만 보고하고 태스크의 결과는 보고하지 않는다고 규정합니다. 에이전트의 태스크가 FAILED나 REJECTED로 끝나도, 입력이나 인증을 기다리며 멈춰도 CLI는 0으로 끝납니다. 성패를 가르려면 종료 코드가 아니라 출력에 담긴 태스크 상태를 봐야 합니다. 1은 CLI가 작업을 마치지 못한 경우, 2는 인자나 옵션이 잘못된 경우이고, 도달 실패는 3, 인증 실패는 4, 시간 초과는 5가 예약되어 있습니다.
명령 체계는 세 갈래로 나뉩니다. 에이전트를 부르는 클라이언트 명령, 에이전트를 띄우는 서버 명령, 그리고 설정을 확인하는 명령입니다:
A2A CLI 설치
맥과 리눅스에서는 Homebrew로 설치합니다. 저장소를 탭으로 등록하는 명령이 먼저 필요합니다:
brew tap a2aproject/a2a-cli https://github.com/a2aproject/a2a-cli
brew install a2a
윈도우는 WinGet을 씁니다:
winget install a2aproject.a2acli
릴리즈 페이지에서 미리 빌드된 바이너리를 내려받아 압축을 풀고 a2a 실행 파일을 PATH에 두어도 됩니다. 이때는 함께 배포되는 checksums.txt로 무결성을 확인하는 것이 좋습니다. Go 개발 환경이 있다면 소스에서 설치할 수도 있는데, 이 경우 실행 파일 이름이 a2a-cli가 되므로 문서와 맞추려면 a2a로 바꿔 주어야 합니다:
go install github.com/a2aproject/a2a-cli@latest
mv "$(command -v a2a-cli)" "$(dirname "$(command -v a2a-cli)")/a2a"
A2A CLI로 에이전트 호출하기
아래 예시는 A2A Python SDK의 helloworld 예제를 http://127.0.0.1:9999에 띄워 두고 실행한 것입니다. 파이썬으로 만든 에이전트를 Go로 만든 CLI가 호출하는 셈이라, 프로토콜이 언어를 가리지 않는다는 점이 이 조합에서 그대로 드러납니다.
가장 먼저 할 일은 상대가 무엇을 할 수 있는지 확인하는 것입니다. card get은 잘 알려진 경로에서 에이전트 카드를 받아 요약해 보여 줍니다:
$ a2a card get http://127.0.0.1:9999
Name: Hello World Agent
Description: Just a hello world agent
Version: 0.0.1
Interfaces:
JSONRPC http://127.0.0.1:9999
Streaming: true
Skills:
echo_bot Echo Bot
인터페이스 줄에 적힌 JSONRPC가 CLI가 고른 전송 방식입니다. 카드가 여러 인터페이스를 선언하고 있으면 앞쪽에 놓인 것을 우선하며, --transport rest처럼 선호 순서를 직접 지정할 수도 있습니다. 이 옵션은 여러 번 줄 수 있고 적은 순서가 곧 우선순위이며, 카드가 그중 아무것도 제공하지 않으면 카드의 순서로 되돌아갑니다. 카드 조회를 건너뛰고 특정 주소에 곧장 붙으려면 -a 대신 -e로 엔드포인트를 주고 전송 방식을 하나 지정합니다.
프로토콜 버전도 이 자리에서 함께 정해집니다. 인터페이스마다 자기 protocolVersion을 갖고 있어서 전송 방식과 버전이 같이 결정되고, CLI는 요청마다 A2A-Version을 실어 보냅니다. 이 값이 비어 있으면 서버가 0.3으로 해석하기 때문에 명시가 중요하며, 직접 지정하려면 --a2a-version을 씁니다.
메시지 전송은 send입니다. 따로 옵션을 주지 않으면 태스크가 종료 상태에 이를 때까지 기다린 뒤 결과를 정리해 출력합니다:
$ a2a send -a http://127.0.0.1:9999 "Hello, what can you do?"
Task: ba48056c-c4a0-4d8c-a481-23b88e9e7ffb
Context: 50da741a-0ad7-44b6-8fd3-13b469b7360b
Status: completed (2026-09-11T03:08:24Z)
Request is completed!
Artifacts:
[a5b42aa6-c033-4f89-956c-7a69997d8af4] Hello, World! I have received your request (Hello, what can you do?)
History:
[user] Hello, what can you do?
[agent] Processing request...
태스크 식별자와 대화 식별자, 종료 상태, 아티팩트, 대화 기록이 프로토콜의 구조 그대로 나옵니다. --stream을 붙이면 같은 요청이 이벤트 단위로 도착하는 것을 실시간으로 볼 수 있습니다:
$ a2a send -a http://127.0.0.1:9999 --stream "Summarize this"
Task: ccb80803-a5d2-452e-9766-1fd20da30b2e
Context: 1030f3f3-8090-402a-94da-9d90c1e7c4a5
Status: submitted
History:
[user] Summarize this
[status] working: Processing request...
[artifact] Hello, World! I have received your request (Summarize this)
[status] completed: Request is completed!
메시지는 텍스트 한 덩어리로만 보내는 것이 아닙니다. --text-part, --file-part, --data-part를 순서대로 여러 번 붙여 하나의 메시지에 텍스트와 파일과 구조화된 데이터를 섞을 수 있고, 적어 넣은 순서가 그대로 파트 순서가 됩니다. 로컬 경로를 주면 파일이 바이트로 실려 가고, URL을 주면 참조로만 전달되며 CLI가 그 URL을 대신 받아 오지는 않습니다.
오래 걸리는 작업은 --async로 던져 두고 식별자만 받은 뒤, 나중에 task get --wait으로 따라갈 수 있습니다. 이때 --poll-interval로 폴링 간격을, 전역 --timeout으로 전체 대기 예산을 정합니다. 대기 중 태스크가 입력이나 인증을 기다리는 중단 상태에 도달하면 CLI는 종료 상태가 아니어도 먼저 돌아와, 호출한 쪽이 다음 행동을 결정할 수 있게 합니다.
쌓인 태스크는 목록으로 확인합니다:
$ a2a task list -a http://127.0.0.1:9999
ID STATUS CONTEXT
ccb80803-a5d2-452e-9766-1fd20da30b2e completed 1030f3f3-8090-402a-94da-9d90c1e7c4a5
ba48056c-c4a0-4d8c-a481-23b88e9e7ffb completed 50da741a-0ad7-44b6-8fd3-13b469b7360b
9dddbeb7-df6d-4a63-96d3-4b433c4a8a20 completed 3774b431-fee1-4dab-bbe1-09f06d1f5da0
--context로 특정 대화만 추리거나 --status working처럼 상태로 거를 수 있고, 진행 중인 태스크는 task cancel로 취소하거나 task subscribe로 이벤트를 구독할 수 있습니다. 대화를 이어 가려면 send에 --task-id를 주어 같은 태스크에 후속 메시지를 보내고, 새 태스크를 같은 대화 아래 묶으려면 --context-id를 씁니다.
A2A CLI를 서버로 띄우는 세 가지 방식
이 도구가 단순한 클라이언트에 그치지 않는 부분이 server 명령입니다. 세 가지 모드가 있고 셋 다 실제 A2A 서버로 동작합니다.
에코 모드는 받은 메시지를 그대로 돌려주는 에이전트를 즉석에서 띄웁니다. 클라이언트를 개발하는 쪽에서 상대 에이전트가 아직 준비되지 않았을 때 쓸 대상이 생깁니다:
$ a2a server --echo --port 8080
Listening on 127.0.0.1:8080
이렇게 띄운 서버는 자기 에이전트 카드까지 자동으로 만들어 공개하므로, 다른 터미널에서 곧장 조회하고 호출할 수 있습니다:
$ a2a card get http://127.0.0.1:8080
Name: Echo Agent
Description: Echoes the user's message back as a response
Version: 1.0.0
Interfaces:
HTTP+JSON http://127.0.0.1:8080
Streaming: true
프록시 모드는 요청을 다른 에이전트로 넘기면서 오간 내용을 기록합니다. 에이전트끼리 주고받는 실제 메시지를 들여다보거나, 인증이 필요한 업스트림 앞에 자격 증명을 붙여 주는 게이트웨이로 쓸 수 있습니다. 상류 에이전트는 전역 옵션인 -a나 -e로 지정하고, --svc-param으로 추적용 값을 모든 요청에 끼워 넣을 수 있습니다.
실행 모드는 A2A를 전혀 모르는 프로그램을 에이전트로 만들어 줍니다. 메시지의 첫 텍스트 파트가 그 프로그램의 표준 입력으로 들어가고, 표준 출력이 응답 아티팩트가 되며, 종료 코드가 0이면 완료, 0이 아니면 실패로 처리됩니다. 다음은 입력을 대문자로 바꾸는 파이썬 스크립트 하나를 그대로 에이전트로 공개한 예시입니다:
# upper.py
import sys
print(sys.stdin.read().strip().upper())
$ a2a server --exec "python3 -u upper.py" --port 8081 --name "Uppercase Agent"
$ a2a send -a http://127.0.0.1:8081 "hello a2a"
Task: 01a08e70-6141-7347-8e61-3b5bdd5b9354
Context: 01a08e70-6141-73bd-8170-61c601dd69e4
Status: completed (2026-09-11T12:08:44+09:00)
Artifacts:
[01a08e70-61b4-7c29-ba63-edb860b5784e] HELLO A2A
History:
[user] hello a2a
--chunk에 구분자를 주면 표준 출력을 그 구분자로 잘라 나오는 대로 아티팩트 조각으로 흘려 보냅니다. 스크립트는 A2A의 이벤트 모델을 몰라도 되고 그저 줄 단위로 출력하면 되는데, 받는 쪽에는 그것이 스트리밍으로 도착합니다:
$ a2a server --exec "for i in 1 2 3; do echo \$i; sleep 0.5; done" --chunk=$'\n' --port 8082
$ a2a send -a http://127.0.0.1:8082 --stream "go"
Task: 01a08e70-69b0-7105-ac2c-857d36edcafd
Context: 01a08e70-69b0-71fb-9cc8-7b0da6d96f6b
Status: submitted
History:
[user] go
[status] working
[artifact] 1
[artifact+] 2
[artifact+] 3
[status] completed
출력의 [artifact+]는 앞의 아티팩트에 이어 붙는 조각을 표시합니다. 셸 반복문 하나가 프로토콜 수준에서는 세 건의 아티팩트 갱신 이벤트로 바뀐 셈입니다.
서버 모드에는 구버전 대응 장치도 있습니다. --protocol 0.3을 주면 0.3 클라이언트를 받아들이는 호환 계층으로 서비스하고, --card-compat을 주면 에이전트 카드를 0.3과 1.0 두 형식으로 함께 내보내 양쪽 클라이언트가 모두 탐색할 수 있게 합니다.
A2A CLI의 설정과 전송 방식 확장
반복해 쓰는 값은 환경 변수나 .env 파일로 빼 둘 수 있습니다. 변수 이름은 A2ACLI_ 뒤에 긴 옵션 이름을 대문자와 밑줄로 바꿔 붙이는 규칙이라, --agent-card는 A2ACLI_AGENT_CARD가 됩니다. 우선순위는 명시한 옵션, 환경 변수, 작업 디렉토리에서 위로 올라가며 찾은 .env, ~/.config/a2a-cli/.env의 전역 설정, 기본값 순입니다. 지금 어떤 값이 어디에서 왔는지는 config show로 확인할 수 있고, 자격 증명 값은 가려서 출력됩니다.
기본 제공하는 전송 방식은 JSON-RPC와 REST, gRPC 세 가지지만, 여기에 없는 프로토콜도 다시 컴파일하지 않고 붙일 수 있습니다. a2a-transport-<이름> 형태의 실행 파일을 PATH에 두면 CLI가 그것을 로컬 프록시로 띄워 표준 바인딩으로 말을 걸고, 플러그인이 실제 프로토콜로 옮겨 줍니다. 사용하는 쪽에서는 내장 전송 방식과 똑같이 --transport <이름>으로 부르면 되고, 현재 인식된 목록은 a2a transport list로 확인합니다. 직접 고르지 않아도 에이전트 카드가 선언한 protocolBinding이 설치된 플러그인 이름과 맞으면 CLI가 알아서 그 플러그인을 씁니다. 다만 플러그인은 사용자가 직접 설치하는 실행 파일이고 CLI가 플러그인을 샌드박스에 가두지는 않으므로 신뢰할 수 있는 것만 두어야 합니다. CLI가 하는 일은 플러그인을 별도 프로세스로 분리하고 루프백 통신을 세션별 TLS와 실행별 토큰으로 보호하는 데까지입니다.
A2A CLI가 AI 코딩 에이전트에게 제공하는 스킬
저장소에는 사람이 치는 명령 말고도, AI 코딩 에이전트가 이 CLI를 직접 다루게 하는 Agent Skill이 함께 들어 있습니다. 코딩 에이전트가 작업의 일부를 외부 A2A 에이전트에게 넘기려 할 때, 하네스마다 따로 플러그인을 만드는 대신 이 스킬 하나를 설치해 같은 명령 체계를 쓰게 하려는 것입니다. 스킬은 a2a 실행 파일을 대신 설치해 주지는 않으며, 바이너리가 PATH에 있는지 먼저 확인하도록 안내합니다.
스킬이 코딩 에이전트에게 지시하는 내용은 명세 §14에 정리되어 있습니다. 결과를 파싱 가능한 한 덩어리로 받으려면 -o json을, 진행 상황을 점진적으로 받으려면 -o json --stream을 쓰고, 임의의 대기 시간을 넣는 대신 CLI의 완료 대기에 맡기며, 성공 여부는 종료 코드가 아니라 보고된 태스크 상태로 판단하고, CLI가 세션 상태를 갖지 않으므로 --context-id와 --task-id를 매번 명시하라는 것입니다. 명세는 스킬 문서가 전체 명령 목록을 나열하지 말고 a2a --help 같은 실행 시점 조회로 넘겨 항상 로드되는 맥락을 작게 유지하라고도 규정합니다.
이름이 겹치는 점은 주의해야 합니다. 여기서 말하는 Agent Skill은 SKILL.md 파일을 담은 디렉토리를 가리키는 형식이고, A2A 프로토콜이 에이전트 카드에 싣는 AgentSkill 객체와는 별개입니다. 명세도 "불행히도 이름을 공유하는 서로 다른 두 가지" 라고 따로 밝혀 두었습니다.
A2A CLI는 누구에게 맞는가
이미 A2A 에이전트를 배포했거나 붙일 예정이고 그 동작을 사람이 눈으로 확인해야 하는 상황이라면, A2A CLI는 일회용 스크립트를 대체할 자리에 바로 들어갑니다. 특히 클라이언트를 먼저 만들어야 하는데 상대 에이전트가 준비되지 않은 경우, --echo 모드로 대상을 즉석에서 만들 수 있다는 점이 개발 순서를 풀어 줍니다. A2A를 모르는 기존 스크립트를 --exec로 감싸 프로토콜 위에 올려 보는 것도 이 도구 없이는 코드를 써야 하는 일입니다.
다만 지금 바로 운영 경로에 넣을 도구로 보기에는 이릅니다. A2A 프로젝트가 이 저장소를 알파 단계로 명시하고 있고, 최신 릴리즈도 0.2.0입니다. 명세 문서 자체도 버전 0.2에 상태는 Review로 아직 비준 전 단계이며, 비준 전에는 규범 요구사항이 버전 올림 없이 바뀔 수 있으니 구현자는 움직이는 표적으로 다루라고 명세가 스스로 적고 있습니다.
세 단계 기능 계층을 구현 성숙도로 읽지는 않는 편이 좋습니다. 명세는 이 계층이 무엇을 먼저 하고 무엇을 뒤로 미룰지를 가르는 범위 구분이며 "구현에 매기는 등급이 아니다" 라고 밝혀 두었고, 실제로 3단계로 분류된 gRPC 전송은 이미 동작합니다. 반대로 2단계에 속한 task download와 conformance, auth login은 a2a --help의 명령 목록에 아직 없습니다. 개발과 디버깅, 그리고 실패해도 되는 자동화에서 쓰고, 중요한 운영 경로에는 SDK를 직접 쓰는 편이 아직 안전합니다.
A2A CLI의 라이선스
A2A CLI와 함께 공개된 명세 문서는 Apache License 2.0으로 공개되어 있어 개인 및 상업적 목적으로 자유롭게 사용할 수 있습니다.
A2A CLI 전체 명령 참조 문서
A2A CLI GitHub 저장소
더 읽어보기
-
[A2A 알아보기 1편] A2A 프로토콜: 프레임워크가 서로 달라도 에이전트들끼리 서로 작업을 위임하게 하는 표준 규격
-
Sprix SAGE Router: 실행 중인 A2A 에이전트의 실행 경로를 결정하고 호출하는 라우터 (SELF / COLLABORATE / HANDOFF)
-
AI 에이전트 프로토콜 개발자 가이드: MCP부터 A2A, UCP, AP2, A2UI, AG-UI까지 (feat. Google)
이 글은 GPT 모델로 정리한 초안을 바탕으로 한 것으로, 원문의 내용 또는 의도와 다르게 정리된 내용이 있을 수 있습니다. 관심있는 내용이시라면 원문도 함께 참고해주세요! 읽으시면서 어색하거나 잘못된 내용을 발견하시면 댓글로 알려주시기를 부탁드립니다. ![]()
파이토치 한국 사용자 모임
은 이런 글들을 한국어로 정리해 나누고 있습니다. 회원으로 가입하시면 주요 글들을 이메일
로 보내드리고, 텔레그램(Telegram)과 Slack/Discord/Teams/Dooray/GoogleChat 등으로도 새 글 알림을 받으실 수 있습니다. ![]()
아래
쪽에 좋아요
를 눌러주시면 다음 글을 정리하는 데 힘이 됩니다~ ![]()

