A2A 프로토콜 — Agent Card·message/send·Task
이 장에서 처음 나오는 말6개
A2AAgent2Agent Protocol- 서로 다른 framework로 만든 Agent끼리, 또는 client가 Agent를 부를 때 쓰는 프로토콜이다. Google이 2025년 4월 제안했고 지금은 Agentic AI Foundation 소속이다.
Agent Card- Agent가 이름·endpoint·지원 기능·skill·인증 방식을 적어 잘 알려진 URL에 두는 JSON 문서다. client는 이것부터 읽는다.
Message- 한 턴의 발화다. role(user 또는 agent)과 parts(text·file·data)로 구성된다.
Task- Agent가 Message를 받아 만드는 작업 단위다. id·상태·artifacts·history를 갖고, 끝날 때까지 여러 Message가 오갈 수 있다.
Artifact- Task가 만들어 낸 결과물이다. 답변 text, 생성 파일, 구조화 데이터가 parts로 담긴다.
opaque agent- A2A의 전제다. 부르는 쪽은 Agent의 내부 model·tool·memory를 모르고 Message와 Task만 본다.
MCP 장이 Agent와 tool 사이의 wire를 읽었다면, 이 장은 client와
Agent 사이의 wire를 읽는다. 이 덱의 백엔드가 kagent controller의 8083에 보내는 것,
10장의 adapter가 검증하는 Agent Card, 12장 비교 실습이
호출하는 message/send가 모두 이 프로토콜이다. 읽고 나면 Task가 왜 Message가 아닌지, working에서
멈춘 응답을 어떻게 이어 받는지, 그리고 kagent 0.9.x의 method 이름이 왜 최신 스펙과 다른지 답할 수 있다.
MCP와 나란히 놓으면
섹션 제목: “MCP와 나란히 놓으면”두 프로토콜은 같은 JSON-RPC 2.0 위에 있고 둘 다 HTTP로 나른다. 다른 것은 상대가 누구인가다.
| MCP | A2A | |
|---|---|---|
| 부르는 대상 | tool·resource·prompt를 제공하는 server | 자율적으로 일하는 Agent |
| 호출 단위 | tools/call 한 번에 결과 한 번 | Message를 보내면 Task가 생기고 여러 턴이 오갈 수 있음 |
| 상대의 내부 | schema로 드러난 함수 | opaque. model·tool·memory를 모름 |
| 발견 | initialize 뒤 tools/list | 잘 알려진 URL의 Agent Card |
| 긴 작업 | progress notification, tasks(실험) | Task 상태 전이 + SSE stream + push notification |
| 전송 | stdio·Streamable HTTP | HTTP만. JSON-RPC 외에 gRPC·REST binding도 정의 |
한 Agent가 tool은 MCP로 쓰고 다른 Agent는 A2A로 부르는 것이 공식 그림이다. 이 덱의 kagent Agent가 정확히 그 모양이라 두 프로토콜을 같은 층으로 보면 kagent 실습 5장이 경고하듯 연결 방향과 권한 주체가 뒤섞인다.
Agent Card — 부르기 전에 읽는 문서
섹션 제목: “Agent Card — 부르기 전에 읽는 문서”client는 endpoint URL을 미리 알아도 Agent Card부터 읽는다. 스펙이 권하는 위치는
https://{host}/.well-known/agent-card.json이다(0.3.0부터. 0.2.x는 agent.json이었고 kagent 0.9.9는
아직 agent.json으로 서빙한다 — 실습 5장이 그 경로다).
{ "protocolVersion": "0.3.0", "name": "lab-reader", "description": "Read-only Kubernetes inspector", "url": "http://kagent-controller.kagent.svc:8083/api/a2a/kagent/lab-reader/", "version": "1.0.0", "preferredTransport": "JSONRPC", "capabilities": { "streaming": true, "pushNotifications": false }, "defaultInputModes": ["text/plain"], "defaultOutputModes": ["text/plain"], "securitySchemes": { "bearer": { "type": "http", "scheme": "bearer" } }, "security": [{ "bearer": [] }], "skills": [{ "id": "inspect-kubernetes-resources", "name": "Inspect resources", "description": "List and describe resources in allowed namespaces" }]}| 필드 | client가 하는 일 |
|---|---|
url·preferredTransport·additionalInterfaces | 어디로, 어떤 binding(JSONRPC·GRPC·HTTP+JSON)으로 보낼지 결정 |
capabilities.streaming·pushNotifications | message/stream을 써도 되는지, webhook 등록이 되는지 |
securitySchemes·security | 어떤 인증 header를 붙여야 하는지. OpenAPI security scheme 형식 |
skills | 사람과 라우팅 로직이 읽는 능력 설명. 실행 권한이 아니다 |
defaultInputModes·defaultOutputModes | 주고받을 MIME type |
포트는 Card의 url에 적힌 것이 전부다. 스펙은 포트를 정하지 않고, kagent BYO Agent가 8080에서
듣는 것과 controller가 8083으로 route를 내주는 것은 kagent의 선택이다. 인증이 필요한 Agent는 인증 뒤에
더 자세한 Card를 주는 agent/getAuthenticatedExtendedCard도 제공할 수 있다.
Message·Task·Artifact — 세 객체만 알면 된다
섹션 제목: “Message·Task·Artifact — 세 객체만 알면 된다”| 객체 | 핵심 필드 | 뜻 |
|---|---|---|
Message | role(user·agent), parts[], messageId, taskId?, contextId? | 한 턴의 발화. 첫 Message에는 taskId가 없고 Agent가 만들어 준다 |
Part | kind: text·file·data | text는 text, file은 file.uri 또는 file.bytes, data는 임의 JSON |
Task | id, contextId, status.state, status.message?, artifacts[], history[] | 작업 단위. contextId로 여러 Task를 한 대화 맥락에 묶는다 |
Artifact | artifactId, parts[], name? | Task의 산출물. 답변도 artifact의 text part다 |
Task가 Message와 별개인 이유는 작업이 한 턴에 끝나지 않기 때문이다. Agent가 추가 정보를 물으면
Task는 input-required로 멈추고 client는 같은 taskId로 다음 Message를 보낸다. 이 덱의
5장이 말한 session 소유권이 A2A에서는 contextId와
taskId를 누가 발급·보관하느냐의 문제로 나타난다.
Task 상태 전이
섹션 제목: “Task 상태 전이”completed·failed·canceled·rejected는 종료 상태라 그 뒤로 Message를 보내면 새 Task가 필요하다.
input-required·auth-required는 중단이지 종료가 아니다. client가 상태를 보고 다음 행동을 고르는
것이 A2A client 구현의 핵심이고, 12장 실습 코드가 result.status.state만 뽑아 보는 이유다.
메서드와 실제 입출력
섹션 제목: “메서드와 실제 입출력”0.3.0의 JSON-RPC method는 일곱 개다.
| method | 하는 일 |
|---|---|
message/send | Message를 보내고 Task(또는 짧은 답이면 Message)를 받는다. 응답이 올 때까지 기다림 |
message/stream | 같은 요청을 SSE로. 상태·artifact event가 흘러오고 종료 상태에서 끝난다 |
tasks/get | Task id로 현재 상태·artifacts·history를 조회 |
tasks/cancel | 진행 중인 Task 취소 요청 |
tasks/pushNotificationConfig/set | 긴 Task의 상태 변화를 받을 webhook 등록 |
tasks/resubscribe | 끊긴 stream을 Task id로 다시 구독 |
agent/getAuthenticatedExtendedCard | 인증된 client용 확장 Agent Card |
message/send — 가장 흔한 호출이다. 12장 실습이 보내는 것과 같다.
{"jsonrpc":"2.0","id":"req-1","method":"message/send","params":{ "message":{"role":"user","messageId":"m-1", "parts":[{"kind":"text","text":"kagent 네임스페이스의 Service 수를 tool로 확인해 줘"}]}}}{"jsonrpc":"2.0","id":"req-1","result":{ "kind":"task","id":"t-42","contextId":"c-7", "status":{"state":"completed","timestamp":"2026-09-23T09:00:12Z"}, "artifacts":[{"artifactId":"a-1","parts":[{"kind":"text","text":"Service는 3개다: kagent-controller, kagent-tools, kagent-ui"}]}], "history":[{"role":"user","messageId":"m-1","parts":[{"kind":"text","text":"kagent 네임스페이스의 …"}]}]}}message/stream — 같은 params를 보내되 응답이 text/event-stream이다. 각 data는 완전한 JSON-RPC
response 하나이고 result.kind로 종류를 구분한다. final: true인 status-update가 오면 stream이 끝난다.
data: {"jsonrpc":"2.0","id":"req-2","result":{"kind":"task","id":"t-43","contextId":"c-7","status":{"state":"submitted"}}}
data: {"jsonrpc":"2.0","id":"req-2","result":{"kind":"status-update","taskId":"t-43","contextId":"c-7","status":{"state":"working"},"final":false}}
data: {"jsonrpc":"2.0","id":"req-2","result":{"kind":"artifact-update","taskId":"t-43","contextId":"c-7","artifact":{"artifactId":"a-1","parts":[{"kind":"text","text":"Service는 3개다: …"}]}}}
data: {"jsonrpc":"2.0","id":"req-2","result":{"kind":"status-update","taskId":"t-43","contextId":"c-7","status":{"state":"completed"},"final":true}}긴 작업을 따라가는 방법은 셋이고 Agent Card의 capabilities가 어느 것이 가능한지 말해 준다.
| 방법 | 언제 |
|---|---|
message/send 뒤 tasks/get polling | streaming 미지원 Agent, 또는 backend가 연결을 오래 못 잡을 때 |
message/stream SSE | 사용자 화면에 진행을 보여줄 때. Next.js 채팅 포팅이 이 경로 |
| push notification webhook | 분 단위 이상 걸리고 client가 떠 있지 않아도 될 때 |
인증은 프로토콜 밖에 있다
섹션 제목: “인증은 프로토콜 밖에 있다”A2A는 OAuth 흐름을 정의하지 않는다. Agent Card의 securitySchemes가 OpenAPI와 같은 형식으로 “bearer
token을 Authorization header에 넣어라” 같은 요구를 말하고, token을 어떻게 얻는지는 배포 환경의 일이다.
그래서 이 덱에서 Grant 검사는 A2A 호출 앞에 있다 — 포털 backend가 5장의 invocation 권한을
판정한 뒤 controller 8083으로 message/send를 보내고, 사용자가 controller에 직접 붙어 Grant를 우회하지
못하게 network에서 막는다. kagent v0.9의 oauth2-proxy 기반 인증도 dashboard 입구의 것이지 A2A route의
per-Agent authorization이 아니다.
1.0에서 바뀐 이름
섹션 제목: “1.0에서 바뀐 이름”A2A는 2025년 7월 30일 0.3.0, 2026년 3월 12일 1.0.0을 냈다. 1.0은 JSON-RPC·gRPC·HTTP+JSON 세 binding을 대등하게 두면서 이름을 통일했다. kagent 0.9.x와 이 덱의 실습 코드는 0.2·0.3 형식이다.
| 항목 | 0.2·0.3 | 1.0 |
|---|---|---|
| 메서드 | message/send, message/stream, tasks/get, tasks/cancel | SendMessage, SendStreamingMessage, GetTask, CancelTask, ListTasks, SubscribeToTask |
| role | user·agent | ROLE_USER·ROLE_AGENT |
| 상태 | working·input-required | TASK_STATE_WORKING·TASK_STATE_INPUT_REQUIRED |
| 객체 구분 | kind: "task" 같은 discriminator | 제거. binding별 구조로 구분 |
| binding 선언 | preferredTransport·additionalInterfaces | supportedInterfaces[].protocolBinding |
새 SDK로 client를 만들 때는 Agent Card의 protocolVersion을 먼저 읽고 어느 이름을 쓸지 정한다. 0.x server에
SendMessage를 보내면 method not found다.
- A2A는 client와 Agent 사이의 계약이다. MCP와 같은 JSON-RPC 2.0이지만 상대가 opaque한 Agent이고 호출 단위가 Task다.
- Agent Card(
/.well-known/agent-card.json, kagent 0.9.9는agent.json)를 먼저 읽어 endpoint·binding·인증·능력을 안다. 포트는 Card의url이 전부다. - Message(role·parts)를 보내면 Task(id·status·artifacts)가 돌아온다.
input-required는 중단이라 같은taskId로 이어 보낸다. - 긴 작업은
tasks/getpolling,message/streamSSE, push notification 셋 중 Card가 허용하는 것으로 따라간다. - 인증은 프로토콜 밖이다. 이 덱의 Grant 검사는 A2A 호출 앞에 있다.
- 1.0은 method·role·state 이름을 바꿨다. kagent 0.9.x는 0.x 이름을 쓰므로 Card의
protocolVersion으로 구분한다.
참고 자료
섹션 제목: “참고 자료”- A2A Specification 0.3.0 — Agent Card 필드, method 일곱 개, TaskState, SSE event 구조. kagent 0.9.x가 만나는 형식.
- A2A Specification 1.0 — 세 binding, PascalCase method,
TASK_STATE_*·ROLE_*, Appendix A의 0.x 호환 변경. - A2A GitHub releases — 0.3.0(2025-07-30)·1.0.0(2026-03-12)·1.0.1(2026-05-28) 날짜.
- kagent A2A 예제 — controller
8083의 Agent Card·task route. - kagent BYO Agent — 사용자 image가 지켜야 할 A2A server 계약.