튜토리얼
A2A Agent Card JSON Schema 완전 튜토리얼: 이름·능력·Skills·인터페이스·Endpoint를 어떻게 정의하는가
Card는 오케스트레이터를 위한 계약이지, 자기 모델용 브리프가 아니다. 필드가 틀리면 발견이 먼저 실패한다.
이전 글 Agent Registry는 카탈로그와 소스 JSON을 갈랐다. 오늘은 등록도, 발견 홉도 아니다. 손에 있는 것은 커밋할 agent-card.json. 각 필드가 무엇인지, 무엇이 필수인지, 0.3의 최상위 url을 남겨도 되는지. 답은 슬라이드가 아니라 A2A 명세 §4.4. 0.3에서 1.0으로의 이동은 v1.0 변경. Google 카탈로그는 두 계약을 받는다. Registry JSON schemas를 보라. 새 카드는 1.0. 검증 실전은 나중 글. 오늘은 필드를 펼친다.
이 카드는 누가 읽는가
Agent Card는 A2A 서버가 /.well-known/agent-card.json에 거는 공개 명함이다. 읽는 쪽은 자기 모델이 아니다. 다른 오케스트레이터, 게이트웨이, 조직 안 카탈로그다. 답할 문장은 셋. 당신은 누구인가, 어떻게 연결하는가, 무엇을 맡는다고 주장하는가. 첫째는 name / description / version. 둘째는 supportedInterfaces. 셋째는 skills[]. 런북을 description에 부으면 10KB 상한이 먼저 온다. 발견면은 얇아진다.
1.0은 필수를 고정했다. 명세 표에서 name, description, supportedInterfaces, version, capabilities, defaultInputModes, defaultOutputModes, skills가 Yes다. 하나라도 빠지면 1.0 클라이언트는 합법 카드로 읽지 않아야 한다. 0.3은 최상위 url과 protocolVersion으로 살 수 있었다. 1.0 클라이언트는 이를 무시하고 인터페이스 배열만 읽는다. 두 필드 집합을 섞으면 카탈로그 추출이 조용히 얇아지고, 티켓에는 「찾을 수 없음」만 남는다.
카드는 Plugin Manifest도, MCP tools/list도 아니다. 한 코딩 에이전트가 스킬을 어떻게 기르는지는 Plugin 글. 호출 홉의 인자는 MCP와 JSON Schema. 오늘은 가로 동료가 자신을 어떻게 말하는지뿐이다. 서명, 확장 카드, GetExtendedAgentCard는 인증 이후. 공개 카드가 먼저 스킬을 말한다.
| 필드 | 1.0 필수 | 무엇을 쓰는가 |
|---|---|---|
name / description / version | 예 | 사람용 신원. version은 에이전트 자신의 판 |
supportedInterfaces | 예 | 순서가 있는 엔드포인트. 첫째가 우선 |
capabilities / 기본 MIME / skills | 예 | 능력 플래그, 미디어 타입, 주장한 스킬 |
신원: name, description, version, provider
name은 카탈로그를 훑는 사람용이지 내부 서비스 이름이 아니다. description은 범위와 경계: 무엇을 하고, 무엇을 하지 않는가. 반품 담당은 「반품 신청을 분류한다. 환불 전표를 올리지 않는다」. 오케스트레이터는 README가 아니라 이 문단으로 Task를 보낼지 정한다.
version은 이 에이전트의 릴리스, 예를 들어 1.0.3. 프로토콜 버전은 인터페이스와 같이 supportedInterfaces[].protocolVersion에 간다. 양쪽에 1.0을 쓰면 나중에 구현만 바꿨을 때 어느 쪽이 움직였는지 모른다. provider는 선택이나, 쓰면 쌍이다: organization과 url. documentationUrl과 iconUrl도 선택. 긴 본문은 문서 URL로. 명함에 쌓지 마라.
신원 블록 뒤에서 한 번 멈춘다. 인터페이스가 없으면 호출할 수 없다. skills가 없으면 카탈로그가 찾지 못한다. 세 문장 중 첫째를 짧고 참되게 쓴 다음 엔드포인트를 채운다.
인터페이스: 엔드포인트는 supportedInterfaces
1.0의 주 엔드포인트는 최상위에 없다. supportedInterfaces는 순서 배열이고 첫째가 선호다. 각 항은 url, protocolBinding, protocolVersion이 필수. 운영 url은 절대 HTTPS여야 한다. 공식 핵심 값은 JSONRPC, GRPC, HTTP+JSON. 명세는 확장을 위해 문자열을 열어 둔다. tenant는 선택. 멀티테넌트일 때만 쓴다.
한 에이전트가 세 바인딩을 세 URL에 둘 수 있다. 클라이언트는 배열 순으로 자기가 말하는 첫 번째를 고른다. 완전해 보이려고 셋 다 같은 404를 가리키지 마라. 0.3의 최상위 url과 protocolVersion은 옛 계약. v1.0 변경 페이지는 이를 주 필드로 두지 말라고 한다. 1.0으로 등록하면서 URL을 최상위에 두면 1.0 검증이 실패하거나 주 엔드포인트가 무시된다.
인터페이스 배열은 말하는 방식을 정하지, 할 일을 정하지 않는다. JSONRPC만 있고 skills가 없으면 연결은 되고 검색은 안 된다. skills만 있고 인터페이스가 없으면 검색은 되고 Task는 못 보낸다. 둘 다 있어야 한다.
capabilities와 기본 MIME
1.0에서 capabilities는 필수 객체다. 안의 불리언은 선택: streaming, pushNotifications, extendedAgentCard, 그리고 extensions 배열. 없거나 false면 해당 연산은 에러여야 하고 조용히 재시도하면 안 된다. 0.3의 stateTransitionHistory를 핵심 능력으로 다시 넣지 마라. 4.4.3 표에 더 이상 없다.
defaultInputModes와 defaultOutputModes는 모든 skill에 적용되는 미디어 타입 배열이다. 개별 skill은 inputModes / outputModes로 덮을 수 있다. 텍스트만이면 text/plain. JSON을 내면 application/json을 더한다. 빈 배열은 미선언이고 1.0 검증을 통과하면 안 된다. 파일 확장자나 내부 열거를 쓰지 마라.
extendedAgentCard가 참이면 인증 후 더 두꺼운 두 번째 카드를 가져올 수 있다. 공개 카드는 그래도 자립해야 한다: 스킬, 인터페이스, 기본 MIME. 핵심 skill을 확장 카드에만 숨기면 미인증 카탈로그는 검색면에서 놓친다.
skills[]: id, tags, examples
각 skill은 id, name, description, tags가 필수다. id는 안정적이고 짧은 프로그램용 키. 공백은 피한다. name은 사람용. description은 입출력 경계이지 여전히 인자 스키마가 아니다. tags는 1.0에서 필수 문자열 배열이다. 카탈로그와 오케스트레이터 키워드. Google Registry도 tags를 색인한다. 비거나 생략: parse는 돼도 찾지 못한다.
examples는 선택. 사람용 프롬프트나 장면이지 JSON Schema가 아니다. 1.0은 inputSchema를 skill 계약으로 두지 않는다. 옛 구현은 아직 스키마를 단다. 힌트는 괜찮다. tools/call 계약은 안 된다. 상대는 불투명한 에이전트이고 Task를 보낸다. 사이트 안 0.3 조각이 skill에 inputSchema를 넣은 것은 옛 계약. 새 카드에 베끼지 마라.
내부 함수 이름으로 skill을 자르지 마라. 하나는 오케스트레이터가 위임할 일의 종류다. 반품 담당은 classify-return과 check-window. 「표 읽기」「로그 쓰기」「메일 보내기」를 검색어 셋으로 만들지 마라. 아래는 저장소에 넣을 수 있는 1.0 카드다. 먼저 parse, 그다음 공식 스키마.
{
"name": "Returns Specialist",
"description": "Classifies return requests and checks the return window. Does not post refunds.",
"version": "1.0.3",
"provider": {
"organization": "Example Commerce",
"url": "https://commerce.example.com"
},
"documentationUrl": "https://docs.example.com/returns-agent",
"supportedInterfaces": [
{
"url": "https://agents.example.com/returns/a2a",
"protocolBinding": "JSONRPC",
"protocolVersion": "1.0"
},
{
"url": "https://agents.example.com/returns/a2a/json",
"protocolBinding": "HTTP+JSON",
"protocolVersion": "1.0"
}
],
"capabilities": {
"streaming": true,
"pushNotifications": false,
"extendedAgentCard": false
},
"defaultInputModes": ["text/plain", "application/json"],
"defaultOutputModes": ["text/plain", "application/json"],
"skills": [
{
"id": "classify-return",
"name": "Classify a return",
"description": "Decide whether a request is a return, exchange, or warranty claim.",
"tags": ["returns", "classify", "commerce"],
"examples": ["This jacket arrived damaged. Is it a return or a warranty claim?"]
},
{
"id": "check-window",
"name": "Check return window",
"description": "Say whether the purchase is still inside the return window.",
"tags": ["returns", "policy", "deadline"],
"examples": ["Order 8841 was delivered on 2026-08-02. Can they still return it?"]
}
]
}
| 필드 | 필수 | 자주 깨지는 곳 |
|---|---|---|
id / name / description | 예 | id를 함수 이름에서 복사. description이 런북 |
tags | 예 | 없거나 빈 배열. 카탈로그가 못 찾음 |
examples / 스킬별 MIME | 아니오 | examples를 inputSchema로 취급 |
1.0 카드에 넣지 말 것
음례를 적어도 한 장 둔다. 최상위에 url이 남아 있고, skill에 tags가 없고, MCP 모양 inputSchema가 들어 있다. 1.0 검증은 실패해야 한다. CI가 0.3과 1.0을 「범용 에이전트 검사」로 뭉개면 클라이언트보다 더 지저분하다. Registry는 선언한 버전으로 규칙을 고르고 로드 중에 스키마를 가져오지 않는다.
{
"name": "Returns Specialist",
"description": "Classifies return requests.",
"version": "1.0.3",
"url": "https://agents.example.com/returns/a2a",
"protocolVersion": "1.0",
"supportedInterfaces": [
{
"url": "https://agents.example.com/returns/a2a",
"protocolBinding": "JSONRPC",
"protocolVersion": "1.0"
}
],
"capabilities": {
"streaming": true,
"stateTransitionHistory": true
},
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text/plain"],
"skills": [
{
"id": "classify-return",
"name": "Classify a return",
"description": "Decide the request type.",
"inputSchema": {
"type": "object",
"required": ["orderId"],
"properties": {
"orderId": { "type": "string" }
}
}
}
]
}
securitySchemes와 보안 요구는 선택 층. 공개 카드는 서명 없이 낼 수 있다. signatures는 JWS. 검증 걷는 법은 실전 글. 기억할 것: 서명은 tags를 대체하지 않는다. 진짜 skill 하나 있는 카드가 빈 스킬 배열의 예쁜 카드보다 정직하다. skills는 여전히 필수 배열. 정말 아무것도 없을 때가 아니면 0건으로 내지 마라.
닫힌 Plugin 필드, SKILL.md 경로, MCP tools[].name을 Card에 베끼지 마라. 세 JSON이 모두 능력이라 쓰지만 실패 처리가 다르다. 나쁜 Card는 발견 실패.
브라우저에서 이렇게 하면 된다.JSON 포맷터로 카드가 parse되는지 보고,JSON Schema 검증기로 1.0 supportedInterfaces와 skills[].tags를 보고,JSON Diff로 커밋한 카드와 음례 사이 최상위 url / tags 누락을 잡는다. 데이터는 기기를 떠나지 않는다. 더 읽을 글:Agent Registry 개요, A2A vs MCP. 발견을 걷는 글은 다음.
자주 묻는 질문
호환을 위해 최상위 url을 남겨도 되나
1.0으로 등록한다면 주 필드로 두면 안 된다. 최상위 url을 아직 읽는 클라이언트는 0.3 계약이다. 새 카드는 엔드포인트를 supportedInterfaces에만 둔다. 둘 다 쓰면 두 계약이 반씩 읽는다.
skill은 id만 쓰고 tags는 나중에?
합법 1.0이 아니다. 명세는 tags를 Yes로 표시한다. 카탈로그 검색은 tags를 먹는다. 「나중에」는 지금은 검색되지 않는다는 뜻이다.
description이 짧다. 안내서는 어디에
documentationUrl이나 에이전트 자신의 스킬·문서. 카드에는 크기 상한이 있다. 안내서를 명함에 넣으면 10KB에 먼저 걸리고 발견면은 0이 된다.
skill에 inputSchema를 달아야 하나
1.0 계약으로는 달지 마라. 힌트 모양이 필요하면 examples와 MIME. 결정적 파라미터는 MCP 도구에 두고 Agent Card에는 쓰지 마라.
정리와 다음 단계
2026년 A2A Agent Card는 한 장의 필수 표로 접힌다. 신원 세 칸, 인터페이스 배열, 능력 객체, 기본 MIME, tags가 있는 skills. 오케스트레이터가 읽는 것은 이 필드이지 아키텍처 그림이 아니다.
내는 순서: 합법 1.0 카드. 인터페이스 첫째는 진짜 엔드포인트. 각 skill에 비어 있지 않은 tags. JSONVue에서 parse와 스키마. 카탈로그가 카드를 어떻게 받는지는 이전 글. 홉은 다음 글. 서명과 검증 목록은 더 뒤.