Phase 13은 모든 조각을 따로따로 가르쳤습니다. 이 종합 프로젝트(capstone)는 그 조각들을 하나의 프로덕션(production) 형태 시스템으로 엮어 냅니다. 도구(tools) + 리소스(resources) + 프롬프트(prompts) + 태스크(tasks) + UI를 모두 갖춘 MCP 서버, 경계(edge)에 배치된 OAuth 2.1, 역할 기반 접근 제어(Role-Based Access Control; RBAC) 게이트웨이(gateway), 다중 서버(multi-server) 클라이언트, A2A 하위 에이전트 호출(sub-agent call), 컬렉터(collector)로 들어가는 OpenTelemetry(OTel) 추적, CI(Continuous Integration) 단계의 도구 오염 탐지(tool-poisoning detection), 그리고 AGENTS.md + SKILL.md 번들(bundle)을 한꺼번에 다룹니다. 끝나면 모든 아키텍처(architecture) 선택을 스스로 방어할 수 있게 됩니다.
유형: Build
언어: Python (표준 라이브러리, 끝에서 끝까지(end-to-end) 이어지는 생태계 하네스(ecosystem harness))
선수 학습: Phase 13 · 01부터 22까지
소요 시간: 약 120분
학습 목표
- 도구, 리소스, 프롬프트, 그리고
ui:// 앱(app)이 결합된 태스크를 노출하는 MCP 서버를 구성합니다.
- 역할 기반 접근 제어(RBAC)와 핀(pinned)된 해시(hash)를 강제하는 OAuth 2.1 게이트웨이를 서버 앞단에 배치합니다.
- OTel GenAI 속성(attribute)으로 처음부터 끝까지 추적되는 다중 서버 클라이언트를 작성합니다.
- 작업(workload)의 일부를 A2A 하위 에이전트에 위임하고 불투명성(opacity)이 그대로 유지되는지 확인합니다.
- 다른 에이전트가 이 시스템을 그대로 구동할 수 있도록 전체 스택(stack)을
AGENTS.md + SKILL.md로 패키징(packaging)합니다.
문제
"리서치와 보고(research and report)" 시스템을 출시한다고 가정합니다.
- 사용자 요청: "에이전트 프로토콜(agent protocol)에 관한 2026년 arXiv 논문 중 인용 수가 가장 많은 세 편을 요약해 줘."
- 시스템이 하는 일: MCP로 arXiv를 검색하고, A2A로 전문 작성 에이전트(writer agent)에 논문 요약을 위임하고, 결과를 모은 뒤, MCP Apps의
ui:// 리소스로 상호작용 가능한 보고서를 렌더링(rendering)하며, 모든 단계를 OTel에 기록합니다.
Phase 13에서 다룬 모든 프리미티브(primitive)가 한 자리에 등장합니다. 장난감 같은 예시가 아닙니다. 2026년에 Anthropic이 출시한 Claude Research 제품, OpenAI가 Apps SDK와 함께 공개한 GPTs, 그리고 여러 서드파티(third party) 회사가 배포한 실제 프로덕션 리서치 어시스턴트(research-assistant) 시스템이 모두 정확히 이 형태를 갖추고 있습니다.
개념
아키텍처(Architecture)
[user] -> [client] -> [gateway (OAuth 2.1 + RBAC)] -> [research MCP server]
|
+- MCP tool: arxiv_search (pure)
+- MCP resource: notes://recent
+- MCP prompt: /research_topic
+- MCP task: generate_report (long)
+- MCP Apps UI: ui://report/current
+- A2A call: writer-agent (tasks/send)
|
+- OTel GenAI spans
추적 계층(Trace hierarchy)
agent.invoke_agent
├── llm.chat (kick off)
├── mcp.call -> tools/call arxiv_search
├── mcp.call -> resources/read notes://recent
├── mcp.call -> prompts/get research_topic
├── a2a.tasks/send -> writer-agent
│ └── task transitions (opaque internals)
├── mcp.call -> tools/call generate_report (task-augmented)
│ └── tasks/status polling
│ └── tasks/result (completed, returns ui:// resource)
└── llm.chat (final synthesis)
전체 흐름이 하나의 추적 ID(trace id)로 묶여 있습니다. 모든 스팬(span)은 올바른 gen_ai.* 속성을 갖추고 있어야 합니다.
보안 자세(Security posture)
- 리소스 인디케이터(resource indicator)로 청중(audience)을 게이트웨이에 고정하는 OAuth 2.1 + PKCE를 사용합니다.
- 게이트웨이가 업스트림(upstream) 자격 증명을 보관하므로 사용자는 그 값을 직접 보지 않습니다.
- RBAC 정책:
alice는 research:read와 research:write 두 가지 권한 범위(scope)를 가지므로 모든 도구를 호출할 수 있습니다. bob은 research:read만 가지고 있어 generate_report는 호출할 수 없습니다.
- 핀된 설명 매니페스트(pinned description manifest): 도구의 해시 값이 바뀐 서버는 명단에서 제외합니다.
- 둘의 법칙(Rule of Two) 감사: 신뢰할 수 없는 입력(untrusted input), 민감한 데이터(sensitive data), 결과를 되돌릴 수 없는 행동(consequential action) 세 가지를 한꺼번에 결합하는 도구가 없는지 점검합니다.
렌더링(Rendering)
마지막 generate_report 태스크는 콘텐츠 블록(content block)과 함께 ui://report/current 리소스를 반환합니다. 클라이언트의 호스트(host)인 Claude Desktop 등은 샌드박스 iframe(sandbox iframe) 안에서 상호작용 대시보드(interactive dashboard)를 렌더링합니다. 대시보드에는 정렬된 논문 목록, 인용 수(citation count), 그리고 사용자가 특정 논문을 클릭했을 때 host.callTool('summarize_paper', {arxiv_id})를 호출하는 버튼이 들어 있습니다.
패키징(Packaging)
전체 산출물은 다음과 같은 형태로 배포됩니다.
research-system/
AGENTS.md # project conventions
skills/
run-research/
SKILL.md # the top-level workflow
servers/
research-mcp/ # the MCP server
pyproject.toml
src/
agents/
writer/ # the A2A agent
gateway/
config.yaml # RBAC + pinned manifest
사용자는 docker compose up 명령으로 배포합니다. Claude Code, Cursor, Codex, opencode 사용자는 run-research 스킬(skill)을 호출해 이 시스템을 그대로 구동할 수 있습니다.
각 Phase 13 강의가 종합 프로젝트에 기여한 것
| Lesson | 종합 프로젝트가 사용하는 것 |
|---|
| 01-05 | 도구 인터페이스, 제공자 이식성(provider portability), 병렬 호출(parallel calls), 스키마(schemas), 린팅(linting) |
| 06-10 | MCP 프리미티브, 서버, 클라이언트, 전송(transports), 리소스 + 프롬프트 |
| 11-14 | 샘플링(sampling), 루트(roots) + 사용자 정보 요청(elicitation), 비동기 태스크(async tasks), ui:// 앱 |
| 15-17 | 도구 오염(tool poisoning), OAuth 2.1, 게이트웨이 + 레지스트리(registry) |
| 18 | 프로덕션 인증(DCR, JWKS 회전(rotation), 청중 고정 토큰(audience-pinned tokens)) |
| 19 | A2A 하위 에이전트 위임 |
| 20 | OTel GenAI 추적 |
| 21 | LLM 라우팅 게이트웨이(routing gateway) 계층 |
| 22 | SKILL.md + AGENTS.md 패키징 |
사용해 보기
code/main.py는 앞선 강의에서 만든 패턴(pattern)들을 하나의 실행 가능한 데모로 엮어 냅니다. 코드 전부가 표준 라이브러리만 사용하고 모든 호출이 동일 프로세스 안에서 일어나므로, 처음부터 끝까지 직접 읽으며 흐름을 따라갈 수 있습니다. 데모는 리서치와 보고 시나리오의 전체 흐름을 그대로 재현합니다. 게이트웨이와 핸드셰이크(handshake)를 마치고, OAuth 2.1 흐름을 시뮬레이션하며, tools/list를 병합하고, generate_report를 태스크 형태로 실행한 뒤, 작성 에이전트(writer)에 A2A 호출(call)을 보내고, ui:// 리소스를 반환하고, OTel 스팬을 내보냅니다.
특히 살펴볼 지점은 다음과 같습니다.
- 모든 단계(hop)를 가로지르는 하나의 추적 ID.
- 게이트웨이 정책이 두 번째 사용자의 쓰기 시도를 차단하는 모습.
- 태스크 수명 주기(lifecycle)가
working에서 completed로 전환되면서 텍스트와 ui:// 콘텐츠를 함께 반환하는 흐름.
- A2A 호출의 내부 상태가 오케스트레이터(orchestrator)에게 그대로 불투명하게 유지되는 점.
- 다른 에이전트가 이 워크플로(workflow)를 재현할 때
AGENTS.md와 SKILL.md만 있으면 충분하다는 점.
산출물 만들기
이 강의는 outputs/skill-ecosystem-blueprint.md를 만듭니다. 리서치, 요약, 자동화 같은 제품 요구(product need)가 주어졌을 때, 이 스킬은 전체 아키텍처를 한 장으로 정리해 냅니다. 어떤 MCP 프리미티브를 쓸지, 어떤 게이트웨이 제어(gateway control)를 둘지, 어떤 A2A 호출이 필요한지, 어떤 텔레메트리(telemetry)와 패키징을 적용할지를 모두 함께 제시합니다.
연습문제
-
(쉬움) code/main.py를 실행해 봅니다. 하나의 추적 ID와 스팬이 어떻게 중첩되는지 확인하고, 이 데모가 Phase 13의 프리미티브 중 몇 개를 실제로 건드리는지 세어 봅니다.
-
(중간) 데모를 확장해 두 번째 백엔드(backend) MCP 서버, 예를 들어 bibliography를 추가하고, 게이트웨이가 그 서버의 도구를 같은 네임스페이스(namespace)로 병합하는지 확인합니다.
-
(중간) 가짜 A2A 작성 에이전트(writer agent)를 서브프로세스(subprocess)에서 실제로 실행되는 에이전트로 교체합니다. Lesson 19의 하네스를 활용합니다.
-
(어려움) 오케스트레이터와 LLM 사이의 라우팅 게이트웨이에 개인 식별 정보(Personally Identifiable Information; PII) 마스킹 단계를 끼워 넣습니다. 사용자 질의(query) 안의 이메일 주소가 가려지는지(scrub) 확인합니다.
-
(어려움) 이 시스템을 유지보수할 팀원을 위해 AGENTS.md를 작성합니다. 5분 안에 다 읽을 수 있어야 하고, Cursor나 Codex에서 종합 프로젝트를 그대로 구동하는 데 필요한 모든 정보를 담아야 합니다.
핵심 용어
| 용어 | 흔한 설명 | 실제 의미 |
|---|
| 종합 프로젝트(Capstone) | "Phase 13 통합 데모" | 모든 프리미티브를 한꺼번에 사용하는 end-to-end 시스템 |
| 리서치와 보고(Research and report) | "그 시나리오" | 검색, 요약, 렌더링으로 이어지는 패턴 |
| 생태계(Ecosystem) | "모든 조각의 결합" | 서버 + 클라이언트 + 게이트웨이 + 하위 에이전트 + 텔레메트리 + 패키지 |
| 추적 계층(Trace hierarchy) | "Single trace id" | 모든 단계의 스팬이 같은 추적을 공유하고, 스팬 ID로 부모-자식 관계를 표현함 |
| 게이트웨이가 발급한 토큰(Gateway-issued token) | "전이적 인증(transitive auth)" | 클라이언트는 게이트웨이가 발급한 토큰만 보고, 업스트림 자격 증명은 게이트웨이가 보관함 |
| 병합된 네임스페이스(Merged namespace) | "모든 도구가 하나의 flat list에" | 게이트웨이에서 다중 서버 병합을 수행하고 이름 충돌에는 접두어(prefix)를 붙임 |
| 불투명 경계(Opacity boundary) | "A2A 호출은 내부를 숨김" | 하위 에이전트의 추론 과정은 오케스트레이터에게 보이지 않음 |
| 세 계층 스택(Three-layer stack) | "AGENTS.md + SKILL.md + MCP" | 프로젝트 컨텍스트 + 워크플로 + 도구 |
| 심층 방어(Defense-in-depth) | "여러 보안 계층" | 핀된 해시, OAuth, RBAC, 둘의 법칙, 감사 로그(audit log)를 함께 둠 |
| 스펙 준수 매트릭스(Spec compliance matrix) | "우리가 spec 요구사항 중 무엇을 ship하는가" | 산출물(deliverable)을 2025-11-25 사양 요구사항에 매핑한 점검표 |
더 읽을거리