← Back to list (보관된 문서)
sourcehttps://developers.googleblog.com/developers-guide-to-ai-agent-protocols/
created2026-03-19
bynvidia:glm5

AI 에이전트 프로토콜 개발자 가이드

2026년 3월 18일

Shubham Saboo 수석 AI 제품 관리자

Kristopher Overholt 개발자 관계 엔지니어

agent_protocol_banner

AI 에이전트 개발 생태계가 급격히 확장하면서 머리글자 약어가 넘쳐난다. MCP, A2A, UCP, AP2, A2UI, AG-UI가 대표적인 예다. 프로토콜 목록을 보며 경쟁 표준의 벽을 마주한 듯한 기분이 든다면, 당신만 그런 것이 아니다. 각 프로토콜의 가치를 명확히 이해할 수 있도록 돕겠다. 에이전트가 접하는 모든 도구와 API, 프론트엔드 구성 요소에 맞춤 통합 코드를 작성하고 유지 관리하는 수고를 덜어주는 역할을 하기 때문이다.

에이전트 개발 키트(ADK)를 활용해 레스토랑용 다단계 공급망 에이전트를 구축하면서 이 프로토콜들을 실제로 적용해보자. 도매 식자재 주문은 재고 데이터베이스 확인, 원격 공급업체 에이전트와 통신, 보안 거래 실행, 대화형 대시보드 렌더링이 필요하므로 테스트 사례로 적합하다.

모든 것을 지어내는 순정 LLM으로 시작한다. 그다음 프로토콜을 하나씩 추가하여 실제 재고 확인, 전문가 견적 확보, 주문 처리, 결제 승인, 대화형 스트리밍 대시보드 렌더링이 가능하도록 만들겠다.

1. 모델 컨텍스트 프로토콜 (MCP)

에이전트 구축 시 처음 마주치는 장애물은 시스템과 데이터 연결이다. 주방 관리 에이전트가 재고를 확인하거나 공급업체에 이메일을 보내려면, 일반적으로 해당 API에 맞춤 통합 코드를 작성해야 한다. 서비스에 엔드포인트가 열두 개라면 그 서비스만을 위해 열두 개의 맞춤 도구를 작성하고 유지 관리해야 한다.

모델 컨텍스트 프로토콜(Model Context Protocol, MCP)은 수백 개 서버에 단일 표준 연결 패턴을 제공하여 이런 단순 반복 업무를 없앤다. 서버가 도구를 광고하면 에이전트가 자동으로 발견한다. MCP 서버는 해당 시스템을 구축한 팀이 관리하므로, 통합 코드를 작성하거나 업데이트하지 않아도 에이전트가 항상 최신 도구 정의를 받는다.

diagram-1-mcp

ADK는 McpToolset을 통해 이를 일급 지원한다. API 요청을 처음부터 작성하는 대신, 주방 관리 에이전트는 MCP Toolbox for Databases를 사용해 실제 PostgreSQL 데이터베이스에서 읽고, Notion MCP로 레시피를 조회하고, Mailgun MCP를 사용해 공급업체에 이메일을 보내 행동한다.

from google.adk.agents import Agent
from google.adk.tools.mcp_tool import McpToolset
from google.adk.tools.mcp_tool.mcp_session_manager import StdioConnectionParams
from google.adk.tools.toolbox_toolset import ToolboxToolset
from mcp import StdioServerParameters

# 1. 재고 데이터베이스 - MCP Toolbox for Databases (PostgreSQL, SQLite, BigQuery 등)
inventory_tools = ToolboxToolset(server_url=TOOLBOX_URL)

# 2. 주방 표준 운영 절차 및 레시피 - Notion MCP (메뉴, 식자재 목록, 공급업체 연락처 읽기)
notion_tools = McpToolset(connection_params=StdioConnectionParams(
    server_params=StdioServerParameters(
        command="npx", args=["-y", "@notionhq/notion-mcp-server"],
        env={"NOTION_TOKEN": NOTION_TOKEN}),
    timeout=30))

# 3. 공급업체에 주문 이메일 발송 - Mailgun MCP (확인 전송, 배송 추적)
mailgun_tools = McpToolset(connection_params=StdioConnectionParams(
    server_params=StdioServerParameters(
        command="npx", args=["-y", "@mailgun/mcp-server"],
        env={"MAILGUN_API_KEY": MAILGUN_API_KEY}),
    timeout=30))

kitchen_agent = Agent(
    model="gemini-3-flash-preview",
    name="kitchen_manager",
    instruction="레스토랑 주방을 관리합니다. 재고 확인, 레시피 조회, 공급업체 이메일 발송.",
    tools=[inventory_tools, notion_tools, mailgun_tools],
)

ADK의 MCP 통합 전체 생태계를 탐색하여 사용 가능한 것을 확인하라.

2. 에이전트 간 프로토콜 (A2A)

MCP가 데이터 접근을 담당하면 다음 과제는 전문성 확보다. 주방 관리 에이전트가 재고를 확인할 수는 있지만, 오늘의 도매 가격이나 공급업체 품질 등급, 배송 기간은 알지 못한다. 이런 지식은 다른 팀이 다른 프레임워크로 다른 서버에서 구축했을 수 있는 원격 에이전트들에 있다. 경우에 따라 원시 데이터는 API로 노출되지 않지만 에이전트 인터페이스로는 노출될 수 있다.

표준 프로토콜이 없다면 각각에 맞춤 통합 코드를 작성하고 유지 관리해야 하며, 원격 에이전트가 변경될 때마다 재배포해야 한다. 에이전트 간 프로토콜(Agent2Agent, A2A)은 에이전트가 서로를 발견하고 통신하는 방식을 표준화한다. 각 A2A 에이전트는 잘 알려진 URL(/.well-known/agent-card.json)에 이름, 기능, 엔드포인트를 설명하는 에이전트 카드를 게시한다. 전체 사양은 A2A 프로토콜 문서를 참조하라.

diagram-2-a2a

주방 관리 에이전트는 이 카드들을 가져와 각 원격 에이전트의 기능을 파악한 후, 런타임에 질의를 적절한 에이전트로 라우팅한다. 새 원격 에이전트를 추가하는 것은 새 URL을 추가하는 것만큼 단순하여, 수동 코드 변경이나 재배포가 필요 없다.

ADK의 RemoteA2aAgent는 턴당 하나의 원격 에이전트로 라우팅한다. 가격, 품질, 배송을 한 번에 확인하는 등 질의가 여러 원격 에이전트에 걸쳐 있을 때는 a2a-sdk를 직접 사용하면 된다. 여기서는 그 방식을 사용한다.

# A2A 에이전트는 /.well-known/agent-card.json에 에이전트 카드를 제공한다:
# {
#   "name": "pricing_agent",
#   "description": "식품 항목에 대한 오늘의 도매 시장 가격을 확인합니다.",
#   "skills": [{"id": "pricing", "name": "Price Check",
#               "description": "현재 도매 시장 가격 확인"}],
#   "url": "http://pricing-agent:8001/",
#   "version": "1.0.0"
# }

# EXPOSE: 모든 ADK 에이전트를 A2A 서비스로 전환
from google.adk.a2a.utils.agent_to_a2a import to_a2a
app = to_a2a(pricing_agent, port=8001)

# DISCOVER: 에이전트 카드를 확인하고 클라이언트 생성 - URL만 있으면 됨
from a2a.client.client_factory import ClientFactory
client = await ClientFactory.connect("http://pricing-agent:8001")
card = await client.get_card()
print(f"{card.name} - {card.description}")
# -> "pricing_agent - 식품 항목에 대한 오늘의 도매 시장 가격을 확인합니다."

# CALL: A2A 프로토콜을 통해 메시지 전송
from a2a.client.helpers import create_text_message_object
msg = create_text_message_object(content="오늘 연어 도매 가격이 얼마야?")
async for response in client.send_message(msg):
    ...
# 응답은 (아티팩트가 포함된) Task 또는 직접 Message

발견 및 통신 패턴을 실제로 확인하려면 A2A 샘플을 시도하라.

3. 범용 상거래 프로토콜 (UCP)

이제 공급업체를 발견하고 견적을 받을 수 있다. 하지만 실제로 주문을 처리할 때가 되면, 모든 공급업체가 서로 다른 API를 가진다. 에이전트가 다섯 도매 유통업체에서 식자재를 조달해야 한다면 다섯 가지 결제 흐름을 통합해야 한다.

범용 상거래 프로토콜(Universal Commerce Protocol, UCP)은 쇼핑 수명 주기를 모듈형 기능으로 표준화한다. 강력한 유형의 요청 및 응답 스키마를 사용하며, 기반 전송 방식에 관계없이 일관성을 유지한다. 모든 공급업체에 맞춤 통합을 구축하는 대신, 에이전트는 통합된 패턴으로 공급업체와 상호작용한다. 연결이 REST, 모델 컨텍스트 프로토콜(MCP), 에이전트 간 프로토콜(A2A) 또는 브라우저 기반 흐름용 임베디드 프로토콜(EP) 중 무엇을 통하든 상관없다.

diagram-3-ucp

주방 관리 에이전트는 A2A에서 본 것과 동일한 잘 알려진 URL 패턴(/.well-known/ucp)을 사용하여 공급업체 카탈로그를 발견한 후, 유형화된 결제 요청을 구성하여 주문을 완료할 수 있다. UCP는 표준 REST API도 지원하므로 프로젝트에서 이미 사용하는 HTTP 클라이언트와 작동한다. 독점 SDK가 필요 없다.

import httpx, uuid
from ucp_sdk.models.discovery.profile_schema import UcpDiscoveryProfile
from ucp_sdk.models.schemas.shopping.checkout_create_req import CheckoutCreateRequest
from ucp_sdk.models.schemas.shopping.types.line_item_create_req import LineItemCreateRequest
from ucp_sdk.models.schemas.shopping.types.item_create_req import ItemCreateRequest
from ucp_sdk.models.schemas.shopping.payment_create_req import PaymentCreateRequest

# DISCOVER: 공급업체의 UCP 프로필 파싱
async with httpx.AsyncClient() as c:
    profile = UcpDiscoveryProfile.model_validate(
        (await c.get("http://example-wholesale:8182/.well-known/ucp")).json())

# ORDER: 유형화된 결제 요청 구성
checkout_req = CheckoutCreateRequest(
    currency="USD",
    line_items=[
        LineItemCreateRequest(quantity=10, item=ItemCreateRequest(id="salmon")),
        LineItemCreateRequest(quantity=3, item=ItemCreateRequest(id="olive_oil")),
    ],
    payment=PaymentCreateRequest(),
)

# SEND: 결제 생성 + 완료 (필수 UCP 헤더 포함)
# UCP-Agent 헤더는 에이전트의 기능 프로필을 가리켜야 함
headers = {"UCP-Agent": 'profile="https://kitchen.example/agent"',
           "Idempotency-Key": str(uuid.uuid4()),
           "Request-Id": str(uuid.uuid4())}

async with httpx.AsyncClient() as c:
    checkout = (await c.post("http://example-wholesale:8182/checkout-sessions",
                            json=checkout_req.model_dump(mode="json", by_alias=True, exclude_none=True),
                            headers=headers)).json()
    headers["Idempotency-Key"] = str(uuid.uuid4())  # 작업마다 새 Idempotency-Key
    order = (await c.post(
        f"http://example-wholesale:8182/checkout-sessions/{checkout['id']}/complete",
        headers=headers)).json()

UCP 샘플 저장소에는 ADK로 구축된 AI 기반 쇼핑 도우미가 포함되어 있다. UCP와 A2A를 결합하여 종단 간 쇼핑 워크플로를 구현한다.

4. 에이전트 결제 프로토콜 (AP2)

이전 섹션에서 주방 관리 에이전트는 공급업체에 주문을 처리하는 능력을 얻었다. 하지만 누가 그 지출을 승인했는가? 설정된 한도, 승인된 판매자, 승인 만료 시점에 대한 기록이 없다.

에이전트 결제 프로토콜(AP2)은 이 누락된 계층을 추가한다. 유형화된 위임(mandate)을 통해 부인할 수 없는 의도 증명을 제공하고, 모든 거래에 구성 가능한 가드레일을 적용한다. UCP는 무엇을 주문하고 누구에게 주문하는지를 처리하고, AP2는 누가 구매를 승인했는지 처리하고 감사 추적을 제공한다. 둘은 함께 작동한다. AP2는 확장으로 UCP에 연결되어 결제 흐름에 암호화된 승인 증명을 추가한다.

통제되지 않는 거래 대신 에이전트가 따를 가드레일을 구성한다. IntentMandate를 정의하여 허용된 판매자를 지정하고 자동 승인을 위한 지출 한도를 설정한다. 그러면 에이전트가 특정 장바구니와 금액에 연결된 PaymentMandate를 생성한다. 주문이 한도를 초과하면 관리자가 명시적으로 승인할 때까지 위임은 서명되지 않은 상태로 남는다. PaymentReceipt가 감사 추적을 마무리한다.

diagram-4-ap2

다음 코드 조각은 공식 저장소의 실제 AP2 유형을 사용하여 의도부터 서명된 위임, 영수증까지 전체 승인 흐름을 보여준다.

from ap2.types.mandate import IntentMandate, PaymentMandate, PaymentMandateContents
from ap2.types.payment_request import PaymentCurrencyAmount, PaymentItem, PaymentResponse
from ap2.types.payment_receipt import PaymentReceipt, Success

# 레스토랑 소유자가 가드레일 구성
intent = IntentMandate(
    natural_language_description="연어 10파운드, 올리브 오일 3병",
    merchants=["Example Wholesale"],  # 이 공급업체들만 허용
    requires_refundability=True,  # 환불 가능해야 함
    user_cart_confirmation_required=False,  # 한도 이하면 자동 승인
    intent_expiry="2026-02-23T20:00:00Z",  # 1시간 후 만료
)

# 에이전트가 결제를 의도에 연결하는 PaymentMandate 생성
mandate = PaymentMandate(payment_mandate_contents=PaymentMandateContents(
    payment_mandate_id="abc123",
    payment_details_id="order-001",
    payment_details_total=PaymentItem(
        label="연어 10파운드 + 올리브 오일 3병",
        amount=PaymentCurrencyAmount(currency="USD", value=294.00)),
    payment_response=PaymentResponse(request_id="order-001", method_name="CARD"),
    merchant_agent="Example Wholesale",
))

# 관리자 서명 (시뮬레이션 - 실제 AP2는 보안 기기에서 JWT/생체인식 사용)
mandate.user_authorization = "signed_hash_abc123"

# PaymentReceipt가 감사 추적 마무리
receipt = PaymentReceipt(
    payment_mandate_id="abc123",
    payment_id="PAY-001",
    amount=PaymentCurrencyAmount(currency="USD", value=294.00),
    payment_status=Success(merchant_confirmation_id="ORD-A1B2C3"),
)

# IntentMandate -> PaymentMandate (서명됨) -> PaymentReceipt
# 전체 감사 추적: 의도, 승인, 지불 내역

AP2는 v0.1 단계이며, 그 유형은 ADK 코어에 내장되지 않고 별도 패키지로 제공된다. AP2 저장소에서 프로토콜과 참조 구현을 탐색하라.

5. 에이전트 대 사용자 인터페이스 프로토콜 (A2UI)

이 시점에서 주방 관리 에이전트는 재고 확인, 견적 확보, 주문 처리, 결제 승인이 가능하다. 하지만 모든 결과가 일반 텍스트로 돌아오며, 때로는 텍스트만으로는 부족하다. 에이전트가 재고 대시보드, 주문 양식 또는 공급업체 비교를 제시해야 할 때, 일반적으로 각각에 대해 별도의 프론트엔드 구성 요소를 구축해야 한다. 새로운 UI 요구 사항이 생길 때마다 더 많은 프론트엔드 코드를 작성하고 유지 관리해야 한다.

에이전트 대 사용자 인터페이스 프로토콜(Agent-to-User Interface Protocol, A2UI)은 에이전트가 고정된 카탈로그에서 동적으로 새로운 레이아웃을 구성할 수 있게 하여 이 문제를 해결한다. 행, 열, 텍스트 필드와 같은 단 18개의 안전한 구성 요소 기본 요소로 구성된 선언적 JSON 형식을 사용한다.

diagram-5-a2ui

A2UI는 UI 구조를 기반 데이터와 분리한다. 에이전트는 ID로 서로를 참조하는 구성 요소의 평면 목록을 보낸 후, 별도의 데이터 페이로드를 보낸다. 클라이언트 측 렌더러가 이 JSON을 Lit, Flutter, Angular 같은 프레임워크를 사용해 네이티브 UI로 변환한다.

# 에이전트가 보내는 내용. 렌더러(Lit, Flutter, Angular)가 이를 네이티브 UI로 변환.
a2ui_messages = [
    # 1. 렌더링 표면 생성
    {"beginRendering": {"surfaceId": "default", "root": "card"}},
    # 2. 구성 요소 트리 전송 (평면 목록, ID 참조 - 중첩 아님)
    {"surfaceUpdate": {"surfaceId": "default", "components": [
        {"id": "card", "component": {"Card": {"child": "col"}}},
        {"id": "col", "component": {"Column": {"children": {"explicitList": ["title", "price", "buy"]}}}},
        {"id": "title", "component": {"Text": {"usageHint": "h3", "text": {"path": "name"}}}},
        {"id": "price", "component": {"Text": {"text": {"path": "price"}}}},
        {"id": "buy", "component": {"Button": {"child": "btn-label", "action": {"name": "purchase", "context": [{"key": "item", "value": {"path": "name"}}]}}}},
        {"id": "btn-label", "component": {"Text": {"text": {"literalString": "지금 구매"}}}},
    ]}},
    # 3. 데이터 전송 (구조와 분리 - 구성 요소를 다시 보내지 않고 데이터 업데이트)
    {"dataModelUpdate": {"surfaceId": "default", "contents": [
        {"key": "name", "valueString": "신선한 대서양 연어"},
        {"key": "price", "valueString": "$24.00/lb"},
    ]}},
]

이제 에이전트는 요청에 따라 동일한 18개 기본 요소에서 완전히 다른 인터페이스를 구성할 수 있다. 여기서 동일한 에이전트에 대한 세 가지 프롬프트가 CheckBox, TextField, DateTimeInput, Card 같은 구성 요소로 재고 체크리스트, 주문 양식, 공급업체 비교를 생성한다. 추가 프론트엔드 코드 없이 가능하다.

widgets-a2ui

개발 중 ADK 웹 인터페이스(adk web)는 A2UI 구성 요소를 네이티브로 렌더링하므로 맞춤 렌더러를 구축하지 않고도 에이전트의 UI 출력을 테스트할 수 있다. 더 많은 구성 요소 패턴은 A2UI 샘플을 탐색하거나, A2UI 위젯 빌더를 사용하여 대화형으로 레이아웃을 구성해 보라.

전통적인 REST API는 응답을 반환하고 끝난다. 에이전트는 다르다. 텍스트를 점진적으로 스트리밍하고, 응답 중간에 도구를 호출하며, 때로는 인간 입력을 기다리며 멈춘다. 이로 인해 에이전트를 프론트엔드에 연결하는 것이 표준 API 호출보다 복잡하다.

직접 처리할 수 있다. ADK는 이벤트를 직접 스트리밍하는 네이티브 /run_sse 엔드포인트를 제공하며, 몇십 줄의 프론트엔드 코드면 스트림을 파싱하고 도구 호출을 렌더링하기에 충분하다. 하지만 그 파싱 코드는 상용구이며, 이벤트 형식이 변경될 때마다 깨진다.

에이전트-사용자 상호작용 프로토콜(Agent-User Interaction Protocol, AG-UI)은 그 상용구를 없앤다. 원시 프레임워크 이벤트를 표준화된 SSE 스트림으로 변환하는 미들웨어 역할을 한다. 프론트엔드는 TEXT_MESSAGE_CONTENTTOOL_CALL_START 같은 유형화된 이벤트를 수신하며, 어떤 에이전트 프레임워크가 생성했는지 신경 쓰지 않는다.

diagram-6-ag-ui

주방 관리 에이전트를 AG-UI 스트리밍 엔드포인트로 전환하려면, ag_ui_adk 패키지로 감싸고 FastAPI 앱에 마운트한다.

from ag_ui_adk import ADKAgent, add_adk_fastapi_endpoint
from fastapi import FastAPI

# 에이전트 감싸기, 앱 생성, 엔드포인트 마운트
ag_ui_agent = ADKAgent(adk_agent=kitchen_mgr, app_name="kitchen", user_id="chef")
app = FastAPI()
add_adk_fastapi_endpoint(app, ag_ui_agent, path="/")

# 실행: uvicorn module:app
# SSE 스트림은 유형화된 이벤트를 내보낸다:
# RUN_STARTED
# TOOL_CALL_START toolCallName="check_inventory"
# TOOL_CALL_RESULT content="3파운드 재고 있음, 재주문 필요"
# TOOL_CALL_END
# TEXT_MESSAGE_CONTENT delta="현재 "
# TEXT_MESSAGE_CONTENT delta="재고를 기반으로..."
# RUN_FINISHED

더 많은 스트리밍 패턴을 보려면 AG-UI 예제를 탐색하라.

종합: 작동하는 에이전트

여섯 프로토콜이 모두 갖춰지면서, 처음의 순정 LLM은 실제 재고 확인, 공급업체 발견, 주문 처리, 결제 승인, 대화형 대시보드 스트리밍이 가능한 꽤 유능한 에이전트로 변모했다. 사용자가 에이전트의 모든 기능을 호출하는 요청을 보낼 때 어떤 일이 일어나는지 보자.

"연어 재고를 확인하고, 오늘의 도매 가격과 품질 등급을 알아보고, 재고가 부족하면 Example Wholesale에서 10파운드를 주문하고 결제를 승인해 줘."

kichen manager agent_overall

에이전트는 요청의 각 단계를 다른 프로토콜을 사용하여 처리한다.

1단계: 정보 수집 - 먼저 재고를 확인한다. MCP가 재고 데이터베이스에 연어를 질의한다(check_inventory). - 재고가 부족하다. 시장 가격은 어떤가? A2A가 원격 가격 및 품질 에이전트에 질의한다(ask_agent).

2단계: 거래 완료 - 가격이 적절하므로 주문한다. UCP가 Example Wholesale에 결제 요청을 보낸다(place_order). - 하지만 누가 이것을 승인했는가? AP2가 구성된 가드레일 내에서 결제 위임으로 주문을 보호한다(authorize_payment).

3단계: 결과 제시 - 이제 사용자에게 결과를 보여준다. A2UI가 결과에서 대화형 위젯을 구성한다. - 모든 것을 프론트엔드로 스트리밍한다. AG-UI가 도구 호출과 텍스트 응답을 실시간으로 전달한다.

죄송합니다. 브라우저가 이 비디오 재생을 지원하지 않습니다.

여섯 프로토콜이 각각 다른 문제를 해결하며, 모두 단일 에이전트를 통해 작동한다.

프로토콜 활용 팁

각 프로토콜이 해결하는 문제 파악: MCP는 에이전트를 도구와 데이터에 연결한다. A2A는 에이전트를 다른 에이전트에 연결한다. UCP는 상거래를 표준화한다. AP2는 결제 승인을 처리한다. A2UI는 무엇을 렌더링할지 정의한다. AG-UI는 어떻게 스트리밍할지 정의한다. 이 경계를 이해하면 아키텍처가 깔끔해진다.

필요할 때 프로토콜 추가: 첫날부터 여섯 가지 프로토콜이 모두 필요한 것은 아니다. 대부분의 에이전트는 데이터 접근을 위해 MCP로 시작한다. 요구 사항이 증가하면(다중 에이전트 통신, 상거래, 결제, 풍부한 UI, 스트리밍), 그 특정 문제를 해결하는 프로토콜을 도입하라.

처음부터 시작하지 말 것: 프로토콜로 구축하기 전에 ADK 통합, 공식 SDK, 샘플 코드를 확인하라. 이 프로토콜들은 빠르게 발전하며, 공식 도구는 직접 재구현하고 싶지 않은 세부 사항을 처리한다.

표준을 초기에 채택: 이 프로토콜들은 아직 성숙 단계에 있지만, 확립하는 패턴(잘 알려진 URL을 통한 발견, 유형화된 요청/응답 스키마, 표준 이벤트 스트림)은 에이전트를 증가하는 도구, 서비스, 다른 에이전트 생태계와 호환되게 만든다.

지금 시작하기

이 게시물의 모든 코드 샘플은 ADK를 사용한다. 첫 번째 에이전트를 설정하고, MCP 서버에 연결한 후, 거기서부터 프로토콜을 추가하기 시작하라. 이미 사용 가능한 도구와 서비스를 보려면 ADK 통합을 탐색하라. 각 프로토콜의 문서와 샘플 링크는 이 가이드 전체에 걸쳐 있다. 여러분이 무엇을 구축할지 기대된다.