본문으로 건너뛰기

© 2026 Molayo

Qiita헤드라인2026. 06. 29. 20:16

【#11】 OpenClaw 해독하기 — 외부로 열린 세 개의 문, 미디어와 MCP / ACP

요약

OpenClaw의 미디어 생성·이해 구조와 MCP/ACP 프로토콜을 분석합니다. 플러그인 불가지론적 설계를 통해 이미지, 영상, 음성 등 다양한 미디어 프로바이더를 계약(contract) 기반으로 유연하게 통합하는 방식을 다룹니다.

핵심 포인트

  • Capability 계약을 통한 플러그인 불가지론적 설계 적용
  • 매니페스트 우선(manifest-first) 방식의 프로바이더 해결
  • 미디어 생성 및 이해를 위한 표준화된 인터페이스 구조
  • 실패 시 모델 후보를 순차 시도하는 폴백(fallback) 메커니즘

본 기사의 코드 참조는 OpenClaw main 브랜치의 cee2aca409 (version 2026.6.10) 시점입니다. 행 번호는 업데이트에 따라 어긋날 수 있습니다.

연재 「OpenClaw 해독하기」

지금까지 OpenClaw의 주요 내부 구조를 한 차례 살펴보았습니다. #11에서는 지금까지 다루지 않았지만 중요한 주변 영역——미디어 생성·이해와 외부와 연결되는 두 가지 프로토콜인 MCP / ACP——를 src/media-*, src/mcp, src/acp를 통해 해독합니다. 공통 테마는 역시 #04에서 보았던 capability 계약에 의한 plugin-agnostic (플러그인 불가지론적) 설계입니다.

이미지 생성 프로바이더의 계약(src/image-generation/types.ts:131)은 심플합니다.

export type ImageGenerationProvider = {
  id: string;
  aliases?: string[];
  ...
}

메타데이터(id / label / models)와 런타임 훅(isConfigured / generateImage)으로 구성됩니다. 동영상·음악·TTS도 동일한 형태의 계약을 가지며, 등록은 #04에서 보았던 범용 핸들러를 경유합니다(src/plugins/types.ts:2718 부근).

registerSpeechProvider: (provider: SpeechProviderPlugin) => void;
registerMediaUnderstandingProvider: (provider) => void;
registerImageGenerationProvider: (provider) => void;
...

capability의 해결은 manifest-first (매니페스트 우선) 방식입니다(src/plugins/capability-provider-runtime.ts:29). imageGenerationProviders / videoGenerationProviders / speechProviders … 와 같은 레지스트리 키가 있으며, 코어는 매니페스트의 contract (계약)를 보고 프로바이더 ID를 해결할 뿐입니다. 특정 프로바이더 이름의 하드코딩은 없습니다.

type CapabilityProviderRegistryKey =
  | "embeddingProviders" | "speechProviders" | "mediaUnderstandingProviders"
  | "imageGenerationProviders" | "videoGenerationProviders" | "musicGenerationProviders"
  ...

생성 결과는 버퍼(buffer)로서 runtime을 흐르며, 필요에 따라 data URL로 정규화됩니다(src/image-generation/image-assets.ts:103toImageDataUrl, 매직 바이트로 MIME을 판정). 폴백(fallback)도 내장되어 있어, runtime.ts가 프로바이더/모델 후보를 순차적으로 시도하며 실패를 FallbackAttempt[]에 기록합니다. 이는 #06의 모델 페일오버(failover)와 동일한 사상이 미디어에도 관통되어 있습니다.

역방향인 수신 미디어의 「이해」 또한 capability입니다(src/media-understanding/types.ts:247).

export type MediaUnderstandingProvider = {
  id: string;
  capabilities?: MediaUnderstandingCapability[]; // "image" | "audio" | "video"
  ...
}

한 프로바이더가 여러 capability(이미지·음성·동영상)를 선언할 수 있는 것이 특징입니다. OpenAI 호환 음성 받아쓰기(transcription)는, 정규화된 음성을 멀티파트 폼(multipart form)으로 만들어 HTTP 엔드포인트로 보내고, 텍스트를 검증하여 반환하는 정직한 구현입니다(src/media-understanding/openai-compatible-audio.ts:27). 이해 결과는 수신 메시지의 컨텍스트에 적용됩니다(src/media-understanding/apply.ts).

)、이미지→음성→동영상의 순서로 실행하며, outputs (전사/설명)와 decisions (어떤 프로바이더가 어떻게 판단했는지)를 나누어 기록합니다.

즉, '사용자가 보낸 사진이나 음성 메모'를 에이전트가 읽을 수 있는 텍스트 컨텍스트로 변환하는 계층입니다. 이것이 #05의 채널 수신 플로우(buildChannelInboundEventContext)와 연결되어 멀티모달(Multimodal) 입력이 성립됩니다.

MCP (Model Context Protocol)에 대해, OpenClaw는 서버와 클라이언트 양쪽 모두입니다.

서버로서: OpenClaw 자신의 기능을 MCP 툴(Tool)로서 외부에 공개합니다(src/mcp/channel-tools.ts:33).

export function registerChannelMcpTools(server: McpServer, bridge: OpenClawChannelBridge): void {
server.tool("conversations_list", "List OpenClaw channel-backed conversations...",
{ limit, search, channel },
...

대화 목록, 메시지 읽기, 첨부 파일 가져오기, 이벤트 구독, 전송, 승인 응답 등 9가지 툴을 공개합니다. 배후에서는 OpenClawChannelBridge(src/mcp/channel-bridge.ts:68)가 게이트웨이(Gateway) 연결, 이벤트 큐, 승인 추적이라는 상태를 가지며, 얇은 MCP 표면과 분리되어 있습니다. "MCP의 툴 정의는 얇게 유지하고, 상태는 bridge가 가진다"—이는 #03의 '계약과 구현의 분리'와 동일한 구조입니다.

클라이언트로서: 외부 MCP 서버의 툴을 가져와 에이전트 툴로 변환합니다. 반대로 OpenClaw 측의 툴을 MCP로서 배포하는 서버도 2개가 있으며, 플러그인 툴용(src/mcp/plugin-tools-serve.ts)과 내장 툴용(src/mcp/openclaw-tools-serve.ts)으로 나뉩니다. 툴은 AnyAgentTool(src/agents/tools/common.ts:47)이라는 타입 소거된 실행형(Type-erased executable)을 통해 MCP 형식으로 브릿지되며, 스키마 검증, 파라미터 준비, 결과의 새니타이징(Sanitization, 미디어 크기 제한) 단계가 포함됩니다.

VISION.md에서 "기존의 MCP / ACPX / 플러그인 / ClawHub 경로를 명확한 제품상·보안상의 이점 없이 중복시키기만 하는 MCP 구현은 머지(Merge)하지 않는다"라고 못 박아둔 이유는, 이 양면 지원이 이미 충분히 넓기 때문입니다.

ACP (Agent Client Protocol)는 외부 에이전트(Codex 등)와 OpenClaw를 연결하는 프로토콜입니다. packages/acp-core가 타입을 담당하고, src/acp/가 브릿지(Bridge)를 담당합니다.

세션 타입(packages/acp-core/src/types.ts:22)은 다음과 같습니다.

export type AcpSession = {
sessionId: SessionId; sessionKey: string; ledgerSessionId?: string; cwd: string;
createdAt: number; lastTouchedAt: number;
...

브릿지 serveAcpGateway()(src/acp/server.ts:42)는 stdio 상의 ACP (ndJSON의 AgentSideConnection)를 OpenClaw Gateway에 연결합니다.

const gateway = new GatewayClient({
url: bootstrap.url,
clientName: GATEWAY_CLIENT_NAMES.CLI,
...

트랜스레이터(src/acp/translator.ts)가 ACP의 메서드(initialize / new_session / prompt / set_session_mode …)를 Gateway 조작으로 매핑하며, prompt()의 응답을 SessionUpdate

로 스트림합니다. 이벤트 레저 (Event Ledger, src/acp/event-ledger.ts:49)는 프롬프트 업데이트 및 완료 상태를 영속화하며, ACPX 호환 외부 에이전트 (Codex)의 세션 리플레이 (Session Replay)를 지원합니다.

export type AcpEventLedger = {
startSession: (params) => Promise<void>;
recordUserPrompt: (params) => Promise<void>;
...

주목할 점은 루트 AGENTS.md가 Codex 연동에 **하드 게이트 (Hard Gate)**를 부과하고 있다는 것입니다. "Codex 관련 작업은 검증, 코멘트, 승인 전에, acting agent가 sibling인 ../codex 소스를 직접 읽고 정확한 프로토콜/런타임 동작을 확인해야 한다."라는 내용입니다. ACP가 OpenAI Codex와 같은 외부 런타임과 통신하는 이상, 의존 대상의 실제 동작을 추측으로 다루지 않겠다는 규율입니다. 이 연재에서도 ACP의 상세 내용은 여기까지만 다루며, 구현에 대한 정밀 검토는 ../codex를 실제로 읽을 때로 미루겠습니다.

이 장에서 살펴본 3개 영역(미디어, MCP, ACP)은 모두 #04 패턴의 재연이었습니다.

  • **Capability 계약 (Capability Contract)**으로 프로바이더를 선언하고, 코어는 ID로 해결 (하드코딩 없음).
  • Manifest-first 방식으로 발견하고, 런타임은 나중에 실행.
  • **얇은 표면 + 상태를 가진 브릿지 (Thin Surface + Stateful Bridge)**로 프로토콜과 로직을 분리 (MCP bridge / ACP gateway).

"코어는 얇게, 능력은 플러그인으로"라는 #01의 원칙이 입출력, 모델, 미디어, 외부 프로토콜 모두에서 동일한 형태로 결정화되어 있습니다. 이것이 OpenClaw를 거대하면서도 통찰력 있게 유지하는 비결입니다.

  • 미디어 생성/이해는 Capability 계약 (이미지, 영상, 음악, TTS, 전사, vision)로 추상화되며, 폴백 (Fallback)도 내장되어 있습니다. 수신 미디어의 이해는 #05의 채널 수신과 연결되어 멀티모달 (Multimodal) 입력이 성립됩니다.
  • MCP는 서버 겸 클라이언트입니다. 얇은 툴 표면 + 상태를 가진 브릿지입니다.
  • ACP는 외부 에이전트 연동입니다. Codex 연동은 ../codex 실독(Actual Reading)이라는 하드 게이트가 있습니다.
  • 3개 영역 모두 Plugin-agnostic 패턴의 재연입니다.

최종회 #12는 지금까지 읽어온 코드를 뒷받침하는 **빌드, 테스트, 품질 게이트 (Quality Gate)**와 연재 전체를 관통했던 설계 철학의 요약입니다. tsdown, vitest, oxfmt, tsgo, Crabbox, 그리고 "LOC(Lines of Code)를 늘리지 않는다", "정준 경로 (Canonical Path)는 하나다"라는 린 코드 (Lean Code) 사상이 어떻게 122만 행을 파탄 없이 유지하고 있는지 총괄합니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0