튜토리얼

AI Agent는 다른 Agent를 어떻게 찾는가: Agent Registry, A2A Agent Card, JSON Capability Discovery

발견이 실패하면 먼저 어느 홉인지 묻는다. 카탈로그 검색인가, 명함 가져오기인가, 도메인은 쥐고 있는데 GET 경로가 틀린 것인가.

지난글 Agent Card 필드 튜토리얼은 1.0 필수표를 펼쳤다. 오늘은 필드를 다시 채우지 않고, Registry가 카탈로그인 이유도 반복하지 않는다. 오케스트레이터의 다음 질문은 이것이다. 「반품을 분류할 동료가 필요하다」에서 호출 가능한 Card까지 어떻게 가는가. 공식 답은 A2A Agent Discovery에 있다. 전략은 셋, Card는 하나. 명세는 큐레이션 카탈로그 조회 API를 정하지 않는다. 같은 페이지 Considerations를 보라. Google Cloud 카탈로그는 구현 중 하나다. 모양은 Registry JSON schemas와 맞춰 본다. 이 글은 홉을 따라간다. 가로 위임과 아래로 도구를 부르는 층은 A2A vs MCP에 있다. 오늘은 반복하지 않는다.

발견은 프로토콜이 아니다. 찾아지는 것은 Card

A2A가 표준화하는 것은 자기 서술이지 전화번호부가 아니다. 원격 Agent는 능력을 JSON 명함에 쓴다. 클라이언트는 그 카드로 맞는지, 어떻게 붙는지, 어떤 Task를 보낼지 판단한다. 방법은 환경에 따라 바뀐다. 공개망, 기업 카탈로그, 개발기 하드코딩. 세 길 모두 같은 Card에 닿을 수 있다. 「발견」을 또 하나의 대화 프로토콜이라고 부르면 리뷰가 먼저 빗나간다. 프로토콜은 여전히 A2A다. 바뀌는 것은 well-known이나 카탈로그로 들어가는 문이다.

공식 발견 페이지의 「Role of the Agent Card」는 아직 0.3 말투로 최상위 url을 언급한다. 새 카드는 1.0이다. 엔드포인트는 supportedInterfaces에 있다. 변경점은 v1.0 변경을 보라. 발견 홉이 옛 필드를 읽어도 그것을 1.0 주계약으로 삼지 마라. 필드 쓰는 법은 지난글에 있다. 오늘 묻는 것은 이 카드가 어느 홉으로 손에 왔는지, 온 뒤 먼저 어떤 키를 확인할지다.

발견 성공은 tools/call 허가와 다르다. 맞은편은 여전히 불투명한 Agent다. skill을 고르고 인터페이스를 고른 뒤 보내는 것은 Task다. 검색 적중을 함수표로 보면 여러 턴의 확인이 arguments를 깨뜨린다. MCP 홉에서 인자를 어떻게 확인하는지는 사이트 안 Schema 글. 오늘은 「누구를 찾았고, 왜 그 일을 맡길 수 있다고 보는가」에서 멈춘다.

전략 이미 아는 것 다음 홉
Well-known URI도메인 또는 호스트GET /.well-known/agent-card.json
큐레이션 카탈로그스킬 키워드 / 태그카탈로그를 조회한 뒤 Card 또는 참조를 가져온다
직접 설정URL 또는 카드 전체검색을 건너뛰고 카드를 읽는다

공식 세 전략: 길을 고른 뒤 걷는다

Well-known은 공개 Agent, 또는 호스트명을 통제할 수 있는 발견에 맞다. 경로는 RFC 8615를 따른다. https://{agent-server-domain}/.well-known/agent-card.json. 클라이언트는 도메인을 알거나 유도할 수 있다. HTTP GET을 보내고 JSON을 받는다. 구현은 단순하고 자동화하기 쉽다. 명함에 민감한 스킬이나 사내 URL이 있으면 그 GET 자체에 인증이 필요하다. 사내 엔드포인트를 공개망에 그대로 걸지 마라.

큐레이션 카탈로그는 기업이나 마켓에 맞다. 중간 서비스가 Card를 모으고, 클라이언트는 skills, tags, provider, capabilities로 조회하며, 카탈로그는 맞는 카드나 참조를 돌려준다. 얻는 것은 거버넌스와 능력 검색이다. 대가는 카탈로그를 키우는 일이다. A2A는 카탈로그 API를 정하지 않는다. Google Agent Registry, 커뮤니티 registry, 자체 카탈로그는 조회 모양을 각자 쓴다. 「범용 discover JSON」 한 장으로 세 업체를 검증하지 마라.

직접 설정은 밀결합, 사설 Agent, 개발기에 맞다. 카드 URL은 환경 변수, 설정 파일, 전용 API에 둔다. 관계가 정적일 때 가장 싸다. Card가 움직이면 클라이언트도 따라가야 한다. 운영에서 「개발기 하드코딩」을 유일한 발견면으로 삼는 것은 2025년 주소록이다. 세 길은 공존할 수 있다. 카탈로그는 누구를 찾고, well-known은 카탈로그가 죽어도 명함을 읽으며, 설정은 기동 시 씨앗 호스트를 남긴다.

호스트명이 있을 때: GET well-known

가장 깨끗한 홉은 세 걸음이다. (1) 도메인을 얻는다. 예: returns.agents.example.com. (2) GET https://returns.agents.example.com/.well-known/agent-card.json. (3) 응답은 1.0 Schema를 통과하는 Card다. 경로를 틀리거나 HTTP를 내부 별칭으로 바꾸거나 인증서 호스트명이 어긋나면 장애 표에는 「발견 실패」라고 적힌다. Card는 멀쩡한데 GET이 닿지 않은 것이다.

명세는 Card 엔드포인트에 캐시 헤더를 권한다. Cache-Control: max-age=…로 중간층과 클라이언트는 매번 전문을 치지 않는다. ETag는 version이거나 내용 해시여도 된다. 만료 뒤에는 조건 요청(If-None-Match)을 보내고 매번 무조건 GET하지 마라. 서버가 헤더를 안 주면 클라이언트는 짧은 기본값을 둘 수 있다. 다만 skills가 바뀔 수 있는 카드를 영구 캐시하지 마라.

공개 카드는 오케스트레이터가 고를 분량이면 충분하다. 민감한 스킬과 사내 두 번째 엔드포인트는 인증 후 확장 카드에 둔다. capabilities.extendedAgentCard가 참일 때만 두 번째를 가져온다. 발견 홉은 미인증 상태에서 확장 카드가 있다고 가정하지 마라. 카탈로그가 신원에 따라 다른 Card를 주는 것과, well-known 한 장이 모두에게 같은 것은 공개 모델이 다르다. 리뷰에서는 문장을 나눠 써라.

카탈로그가 있을 때: tags를 찾고 Card를 가져온다

호스트명 없이 「반품을 분류할 동료를 찾아라」라는 문장만 있을 때는 큐레이션 카탈로그를 걷는다. 조회가 먹는 것은 Card의 skills[].tags와 스킬 이름이지, 슬라이드의 토폴로지가 아니다. Google 프로젝트 안 오케스트레이터, Gemini Enterprise, Agent Gateway는 스킬 키워드로 등록 항목을 찾는다. 제품 동작이지 A2A 표준 RPC가 아니다. 커뮤니티와 다른 클라우드는 각자 search를 가진다. 점검 데이터가 단언할 것은 「조회 → 적중 목록 → 각 행에 cardUrl 또는 내장 Card」다. 한 업체의 경로를 명세로 올리지 마라.

카탈로그가 참조를 주면 다음 홉은 여전히 well-known이거나 카탈로그가 준 Card URL이다. 내장 Card를 주면 그래도 1.0 Schema로 한 번 더 확인한다. 등록 성공은 필드 적법과 다르다. 호스트만 있고 skills가 없는 NO_SPEC 항목은 검색면이 비어 있다. #5에서 이미 썼다. 오늘 남는 말은 하나다. 빈 인덱스에 키워드 홉을 쳐도 프로토콜이 고장난 것은 아니다.

카탈로그가 죽어도 발견이 자동으로 끝나지 않는다. 호스트명을 이미 쥐고 있으면 클라이언트는 여전히 well-known을 GET해야 한다. 카탈로그를 유일한 진실로 두면 발견면이 단일 장애점이 된다. 씨앗 설정에 안정 호스트를 하나둘 남기고, 카탈로그가 돌아온 뒤 키워드 검색을 재개하는 편이 「카탈로그 500이면 멈춘다」보다 명세의 세 길 공존에 가깝다.

적중 이후: skills를 맞추고 인터페이스를 고른 뒤 Task

검색 적중은 「그일 수도 있다」만 뜻한다. 오케스트레이터는 여전히 skills[]를 본다. id가 위임하려는 종류의 일인지, tags가 질의어에 진짜 맞는지, description의 경계를 받아들일지. MCP inputSchema로 skill을 씌우지 마라. 1.0의 힌트는 examples와 MIME이다. skill을 잘못 고른 뒤 Task를 보내면 실패는 위임에서 나고 발견에서 나지 않는다. 다만 점검 데이터는 「적중 ≠ 선정」을 두 단계로 써야 한다.

선정한 뒤 supportedInterfaces를 읽는다. 첫 항목이 우선이다. 클라이언트는 말할 수 있는 바인딩을 고른다. JSONRPC, GRPC, HTTP+JSON. 공통 바인딩이 없으면 발견은 성공하고 통화는 실패한다. 0.3 최상위 url을 1.0 주 엔드포인트로 쓰지 마라. 능력 플래그도 본다. streaming이 거짓인데 스트림을 구독하면 명세는 능력 오류를 요구한다. 조용히 unary로 바꾸지 마라.

아래는 점검용 데이터이지 어느 카탈로그의 공식 API가 아니다. 질의, 적중, 우선 인터페이스를 한 JSON으로 접어 저장소 Card와 나란히 diff하기 쉽게 했다. 다음 동사는 message/send다. 검증 점검표와 서명은 더 뒤의 글로 남긴다.

{
  "kind": "discovery-trace",
  "note": "CI/review fixture — not an official A2A or Google Registry API",
  "query": {
    "tags": ["returns", "classify"]
  },
  "strategy": "curated-registry",
  "hits": [
    {
      "name": "Returns Specialist",
      "cardUrl": "https://returns.agents.example.com/.well-known/agent-card.json",
      "matchedTags": ["returns", "classify"],
      "skillId": "classify-return"
    }
  ],
  "selected": {
    "skillId": "classify-return",
    "preferredInterface": {
      "url": "https://agents.example.com/returns/a2a",
      "protocolBinding": "JSONRPC",
      "protocolVersion": "1.0"
    },
    "next": "message/send"
  }
}
이 홉 입력 단언할 것
카탈로그 검색tags 또는 스킬 이름적중 목록이 비어 있지 않고 참조를 가진다
well-known 가져오기호스트명 또는 cardUrlJSON이 parse되고 1.0 Schema를 통과한다
선정 / 인터페이스 선택완전한 1.0 Cardskill id가 맞고 클라이언트가 그 인터페이스를 말한다

캐시, 낡은 인덱스, 점검용 데이터

Card는 자주 움직이지 않는다. 스킬을 더하거나 인증을 바꿀 때 움직인다. 발견면은 그래도 낡는다. 카탈로그 인덱스가 늦고, well-known은 이미 새 version을 주며, 검색은 옛 tags를 가리킨다. 점검 데이터는 적어도 두 벌이다. 카탈로그 적중 스냅샷과 방금 GET한 Card. 키워드가 안 맞으면 먼저 tags와 인덱스 지연을 보고, 그다음에 1.0 클라이언트가 0.3 카드를 읽었는지 본다.

{
  "hop": "well-known",
  "method": "GET",
  "url": "https://returns.agents.example.com/.well-known/agent-card.json",
  "requestHeaders": {
    "If-None-Match": "1.0.3"
  },
  "response": {
    "status": 304,
    "etag": "1.0.3",
    "cacheControl": "max-age=3600"
  }
}

명세는 단호하다. 민감 데이터에는 인증이 필요하다. 대역 밖 동적 자격 정보를 권하고, 정적 비밀을 Card에 쓰지 마라. 발견 데이터에 token이나 사내 암호가 있으면 리뷰는 그 자리에서 반려한다. 공개 카드는 가져갈 전제로 쓴다. 확장 카드 캐시는 세션을 따른다. 공개 카드 max-age와 같은 통에 붓지 마라.

Plugin SKILL.md나 MCP tools/list를 발견원으로 삼지 마라. 같은 저장소의 Coding Agent가 스킬을 키우는 일은 상자 이야기다. 다른 팀의 반품 Agent를 찾는 일은 Card 이야기다. 하나의 capability.json에 집어넣으면 실패한 세 홉이 같은 표 한 줄에 떨어진다.

브라우저에서 끝낸다. JSON 포맷으로 점검 데이터와 Card가 parse되는지 보고, JSON Schema 검증으로 적중 후 1.0 Card를 확인하며, JSON Diff로 카탈로그 스냅샷과 방금 가져온 Card를 비교해 tags / 인터페이스 드리프트를 잡는다. 데이터는 이 기기를 떠나지 않는다. 이어서 Agent Card 필드 튜토리얼, Registry 개요. 서명과 검증 점검표는 다음 실전 글.

관련: A2A Agent Card JSON Schema, Google Agent Registry, A2A vs MCP.

자주 묻는 질문

카탈로그가 있으면 well-known은 GET하지 않아도 되나?

해야 한다. 카탈로그는 Card를 소비하지, 대체하지 않는다. 명세는 공개 발견의 표준 경로로 well-known을 적는다. 카탈로그가 죽어도 호스트명을 쥔 클라이언트는 명함을 읽어야 한다.

A2A에 표준 「Agent 검색」 RPC가 있나?

없다. 발견 페이지가 분명히 적는다. 현행 명세는 큐레이션 카탈로그 API를 정하지 않는다. 조회는 각 카탈로그가 스스로 정의한다. 표준인 것은 Card 모양과 well-known 경로다.

찾았으면 tools/call 해도 되나?

안 된다. 적중은 발견 결과다. 맞은편은 불투명한 Agent이고 Task를 보낸다. 확정 인자는 MCP 도구에 남긴다. 발견 홉에 쓰지 마라.

캐시는 얼마나가 적당한가?

먼저 서버의 Cache-Control과 ETag를 따른다. 헤더가 없으면 짧은 기본값과 만료 후 조건 요청. 스킬이나 인증이 바뀌면 version도 움직여야 한다. 클라이언트는 낡은 tags로 운영 트래픽을 돌리면 안 된다.

정리와 다음 단계

2026년에 「다른 Agent를 자동으로 찾는다」는 세 홉으로 접힌다. 전략을 고르고, Card를 얻고, skills와 인터페이스로 위임 여부를 정한다. 카탈로그는 그 문 중 하나이지 프로토콜 자체가 아니다.

출하 순서. 공개 Agent는 먼저 well-known을 제대로 건다. 키워드로 찾으려면 한 업체 카탈로그를 붙이고 조회 데이터를 직접 가진다. 적중 뒤 1.0 Card를 확인한 다음 Task를 보낸다. 필드 쓰는 법은 지난글, 카탈로그가 무엇인지는 #5, 검증과 서명은 더 뒤의 글. 점검 JSON과 저장소 Card는 JSONVue에서 대조한다.