Claude Code에게 디렉팅 가르치기: Gemini의 Interactions API와 MCP를 기반으로 구축된 상태 유지(Stateful)
요약
Google의 Gemini Omni Flash 모델과 MCP를 활용하여 Claude Code에서 상태 유지(Stateful) 비디오 편집이 가능하도록 구현한 기술 사례를 소개합니다. 사용자는 대화형 인터페이스를 통해 이전 영상의 맥락을 유지하며 비디오를 수정하거나 스타일을 변경할 수 있습니다.
핵심 포인트
- Gemini Omni Flash의 Interactions API를 활용한 상태 유지 비디오 편집
- Claude Code를 위한 FastMCP 서버 기반의 스킬 패키징
- 텍스트, 이미지, 비디오를 활용한 5가지 멀티모달 생성 방식 지원
- 연속적인 프롬프트를 통한 비디오 스타일 및 내용의 점진적 수정 가능
요약 (TL;DR): omni-skill-claude는 Google의 gemini-omni-flash-preview 모델 (Omni Flash)을 아주 작은 FastMCP 서버로 래핑(wrap)하여 Claude Code 스킬로 패키징합니다. Claude Code에 "눈 속을 달리는 여우의 영상을 생성해줘"라고 입력하면, 그냥... 실행합니다. 그다음 "눈이 내리는 밤으로 만들어줘"라고 말하면, 전체 장면을 다시 프롬프트로 입력할 필요 없이 동일한 영상을 편집합니다. 또한 정지 영상을 애니메이션으로 만들거나, 두 키프레임(keyframe) 사이를 보간(interpolate)하거나, 이미 가지고 있는 영상의 스타일을 변경할 수도 있습니다. 그리고 결과물이 마음에 들면 터미널을 떠나지 않고도 YouTube에 업로드할 수 있습니다.
배경: 왜 또 다른 비디오 도구가 필요한가?
대부분의 비디오 생성 워크플로우는 상태 비저장(stateless) 방식입니다. 프롬프트를 보내면 프레임을 돌려받고, 모델은 즉시 모든 것을 잊어버립니다. 결과를 수정하고 싶나요? 그러면 전체 장면을 다시 묘사하고 캐릭터, 조명, 카메라 워크가 이번 과정에서도 유지되기를 기도해야 합니다. (해설: 유지되지 않습니다.)
Google의 Omni Flash — gemini-omni-flash-preview — 는 다른 접근 방식을 취합니다. 이는 Google의 Gemini "Omni" 라인업에 포함된 비디오 생성 모델로, 빠르고 고충실도(high-fidelity)의 클립을 위해 구축되었으며 — 핵심 기능은 — 상태 유지(stateful) Interactions API에 연결되어 있다는 점입니다. 이를 통해 모델이 서버 측에서 시각적 컨텍스트(visual context)를 유지하는 동안 여러 턴에 걸쳐 비디오를 반복적으로 수정할 수 있습니다.
Omni Flash가 실제로 하는 일
"Omni"라는 부분은 단순한 브랜딩 수식어가 아닙니다. 이 모델은 진정한 의미의 혼합 멀티모달(multimodal) 입력을 수용합니다. 단일 요청의 input은 일반 문자열이거나, 다음과 같이 타입이 지정된 파트(parts)의 리스트일 수 있습니다: text 파트, base64로 인코딩된 image 파트, 그리고 Gemini File API를 통해 업로드한 비디오를 가리키는 document 파트입니다. 모델은 사용자가 전달하는 무엇이든 하나의 클립으로 구성합니다. 이 단일 메커니즘은 비디오를 만드는 다섯 가지의 뚜렷한 방식을 모두 커버합니다:
- Text → video (텍스트 → 비디오). 프롬프트를 입력하면
.mp4파일이 출력됩니다. 생성 시점에 가로형16:9또는 세로형9:16을 선택할 수 있습니다. - 이미지 1장 + 모션 프롬프트 → animation (애니메이션). 정지 영상에 생동감을 불어넣습니다 (예: "그룹이 웃으며 카메라를 향해 손을 흔듭니다").
- 이미지 2장 + 트랜지션 프롬프트 → keyframe interpolation (키프레임 보간). 모델이 프레임 A에서 프레임 B 사이의 중간 영상을 만들어냅니다 (예: "일출부터 일몰까지의 부드러운 타임랩스").
- 참조 이미지 + 장면 프롬프트 → subject-consistent generation (피사체 일관성 생성). 참조 사진 속의 인물이나 사물이 프롬프트의 지시에 따라 생성된 장면 속에 등장합니다.
- 업로드된 비디오 + 편집 프롬프트 → restyling (스타일 재구성). 모델이 생성하지 않은 기존 영상을 다시 렌더링합니다 (예: "픽사 애니메이션 스타일로 만들어줘").
그리고 이 다섯 가지 방식 위에 상태 유지(stateful) 계층이 놓여 있습니다. store=True로 호출된 모든 요청은 **interaction ID (상호작용 ID)**를 반환하며, 이후 모든 결과물은 점진적인 편집 프롬프트를 통해 차례대로 정교화할 수 있습니다. 모델이 사용자가 다시 설명하게 만드는 대신 저장된 시각적 컨텍스트 (visual context)를 검색하기 때문에 동일한 캐릭터, 동일한 조명, 동일한 카메라 언어를 유지할 수 있습니다.
시작하기 전에 알아두어야 할 세 가지 실질적인 사항이 있습니다: 생성은 동기식(synchronous)이며 느립니다 (비디오가 준비될 때까지 호출이 차단됩니다), 생성당 비용이 발생하며, 출력 파일의 크기가 빠르게 커집니다. 약 4MB를 넘어가면 인라인 base64 방식 대신 File-API 전달 방식을 사용하는 것이 좋습니다. 아래의 서버와 스킬은 이러한 현실적인 문제들을 대신 처리하기 위해 존재합니다.
이 리포지토리(repo)는 이 모든 것을 Claude Code에 결합하여, 여러분의 코딩 에이전트가 세션의 자연스러운 일부로서 비디오를 생성하고 반복적으로 정교화할 수 있도록 합니다. 이 리포지토리는 두 가지 구성 요소로 제공됩니다:
- 정확히 8개의 도구(tools)를 노출하는 Model Context Protocol (MCP) 서버 (
omni-video-agent,server.py에 포함된 단일 파일 FastMCP 앱). - Claude에게 해당 도구들을 언제 그리고 어떻게 잘 사용할지 가르치는 Claude Code 스킬 (
omni-video).
Interactions API: 기억력이 있는 비디오
Interactions API는 Gemini의 상태 유지(stateful) 엔드포인트입니다. 핵심 루프는 다음과 같습니다:
- 프롬프트와
store=True옵션을 포함하여client.interactions.create(...)를 호출합니다. - 응답에는 Google 서버에 유지되는 해당 턴의 시각적 컨텍스트 핸들인 **
interaction_id**가 포함됩니다. - 다음 호출 시
previous_interaction_id를 전달하면, 모델은 장면, 캐릭터, 조명 및 스타일의 연속성을 유지하며 _기존 비디오_를 편집합니다.
따라서 다음과 같은 방식(상태 비저장(stateless) 방식의 고충) 대신:
"골든 아워에 신선한 눈 속을 달리는 붉은 여우의 트래킹 샷, 자작나무, 낮은 태양, 얕은 피사체 심도, 그리고 이제는 또한 폭설이 내리는 밤"
...이렇게 작성하면 됩니다:
"폭설이 내리는 밤으로 만들어줘."
그게 전부입니다. 저장된 컨텍스트가 나머지를 처리합니다.
서버가 대신 처리해 주는 몇 가지 실무적인 세부 사항은 다음과 같습니다:
- 매 턴마다 새로운 interaction ID가 반환됩니다. 가장 최신 ID를 체이닝(chain)해야 합니다. 오래된 ID로 편집하면 세션이 이전 상태에서 조용히 분기(fork)됩니다 (이를 수동으로 구현할 경우 매우 미묘하고 짜증 나는 버그가 됩니다).
- 종횡비(Aspect ratio)는 생성 시점에 결정되며 (
16:9가로형 또는9:16세로형), 상태 유지(stateful) 편집 시 _상속_됩니다. 따라서 편집 도구는 종횡비를 별도로 받지 않도록 의도적으로 설계되었습니다. - 전송 모드(Delivery modes):
inline(기본값 — 비디오가 base64로 반환되며 짧은 클립에 적합함) 또는uri—uri방식은 출력이 Google File API에 저장되며, 서버는 준비될 때까지 폴링(poll)한 후 다운로드합니다. 비디오는 용량이 빠르게 커지므로, 약 4MB를 넘어가면uri방식을 사용하는 것이 직접 겪게 될 페이로드 제한(payload-limit) 오류를 방지하는 길입니다.
MCP란 무엇인가, 1분 요약
**Model Context Protocol (MCP)**는 AI 어시스턴트를 도구 및 데이터와 연결하기 위한 개방형 표준입니다. MCP 이전에는 모델에 특정 서비스에 대한 액세스 권한을 주려면 각 어시스턴트마다 맞춤형 통합 코드를 작성해야 했습니다. 즉, N개의 어시스턴트 × M개의 서비스만큼 모두가 동일한 배관 작업을 새로 만들어야 했습니다. MCP는 이를 통합합니다. 도구 제작자가 타입이 지정된 도구를 노출하는 하나의 MCP 서버를 작성하면, MCP를 지원하는 모든 클라이언트(Claude Code, Claude Desktop 및 점점 늘어나는 다른 클라이언트들)가 클라이언트별 접착 코드(glue code) 없이도 해당 도구를 발견하고 호출할 수 있습니다.
MCP 서버는 일반적으로 stdio를 통해 JSON-RPC를 사용하는 작은 로컬 프로세스입니다. 클라이언트가 이를 실행하고 "어떤 도구들을 가지고 있나요?"라고 물으면, 그 이후부터 모델은 해당 도구들을 함수처럼 호출할 수 있습니다.
omni-video-agent 서버는 정확히 8개의 도구를 노출합니다:
| 도구 (Tool) | 기능 |
|---|---|
generate_video | 텍스트 → 비디오. 로컬에 .mp4로 저장하며, 경로와 interaction ID를 반환합니다. |
| ... |
에러는 프로토콜 에러가 아닌 🔴 ... 텍스트 문자열로 반환되므로, 에이전트가 이를 읽고 대응할 수 있습니다.
8가지 함수 호출 상세 설명
전체 인터페이스에 걸쳐 두 가지 관례가 적용됩니다. 모든 비디오 도구는 delivery 파라미터를 받습니다. 'inline' (기본값; 비디오가 응답에 base64로 포함됨) 또는 'uri' (출력이 Google File API에 저장되며, 서버가 ACTIVE 상태가 될 때까지 폴링한 후 다운로드함 — 약 4MB 이상의 모든 작업에 사용하세요) 중 하나를 선택합니다. 그리고 모든 비디오 도구는 저장된 로컬 경로와 다음 편집으로 체이닝(chain)할 수 있는 interaction ID를 포함하는 텍스트 보고서를 반환합니다. 비디오는 각 도구별 접두사(prefix)와 함께 <prefix>_<unix-timestamp>.mp4 형식으로 디스크에 저장됩니다.
generate_video — 텍스트 → 비디오
generate_video(prompt: str, aspect_ratio: str = "16:9", delivery: str = "inline") -> str
시작점입니다. aspect_ratio는 '16:9' (가로형) 또는 '9:16' (세로형)입니다. 상태 유지(stateful) 편집이 이를 상속받기 때문에, 이 값을 허용하는 유일한 도구입니다. 다른 값을 입력하면 모델의 기본값으로 조용히 대체됩니다. 내부적으로는 store=True 옵션이 포함된 단일 client.interactions.create(...) 호출로 이루어지므로, 결과물을 즉시 편집할 수 있습니다. gen_*.mp4로 저장됩니다.
edit_video — 상태 유지(stateful) 편집
edit_video(previous_interaction_id: str, edit_prompt: str, delivery: str = "inline") -> str
전체 아키텍처의 중심이 되는 도구입니다. 최신 (latest) 턴의 interaction ID를 전달하고 변경 사항만 기술하세요. 나머지 정보는 저장된 컨텍스트 (context)가 유지합니다. 각 호출은 새로운 (new) ID를 반환하며, 다음 호출 시 해당 ID를 체이닝 (chain)해야 합니다. 오래된 (stale) ID로 편집할 경우 세션이 이전 상태에서 조용히 분기(fork)되기 때문입니다. 의도적으로 aspect_ratio 파라미터는 포함하지 않았습니다. 이는 상속됩니다. edit_*.mp4로 저장됩니다.
animate_image — 정지 이미지 → 움직임
animate_image(image_path: str, motion_prompt: str, delivery: str = "inline") -> str
로컬 이미지(png/jpg/jpeg/webp — 확장자에서 MIME 타입을 추론하며, 그 외의 형식은 png로 전송됨)를 읽어 base64로 인코딩한 후, [image, text]를 멀티모달 (multimodal) 입력으로 전송합니다. animated_*.mp4로 저장됩니다.
interpolate_images — 두 개의 키프레임 → 그 사이의 푸티지 (footage)
interpolate_images(start_image_path: str, end_image_path: str, prompt: str, delivery: str = "inline") -> str
animate_image와 동일한 인코딩 방식을 사용하지만, 입력값은 [start_image, end_image, text]이며 프롬프트는 전환 과정(예: "일출부터 일몰까지의 부드러운 타임랩스")을 설명합니다. interpolation_*.mp4로 저장됩니다.
generate_with_subjects — 참조 이미지를 활용한 디렉팅 생성
generate_with_subjects(subject_image_paths: list[str], prompt: str, delivery: str = "inline") -> str
리스트 내의 모든 경로는 이미지 파트로 구성되며, 장면 프롬프트 (scene prompt)가 마지막에 위치합니다. 모델은 해당 피사체 (subjects)들이 등장하는 비디오를 생성합니다. subject_*.mp4로 저장됩니다.
edit_user_video — 기존 푸티지 스타일 재구성
edit_user_video(video_path: str, edit_prompt: str, delivery: str = "inline") -> str
입력 시 Gemini File API를 사용하는 유일한 도구입니다. 로컬 비디오를 업로드하고 처리가 완료될 때까지 폴링 (polling)하며 (최대 5분), 완료되면 URI로 참조된 업로드된 비디오와 편집 지침을 포함한 [document, text]를 전송합니다. user_edit_*.mp4로 저장됩니다.
upload_to_youtube — 최종 편집본 게시
upload_to_youtube(video_path: str, title: str, description: str,
category_id: str = "22", privacy_status: str = "private") -> str
YouTube Data API v3를 사용합니다. 일회성 OAuth 설정이 필요합니다 (server의 작업 디렉토리에 client_secrets.json 파일이 있어야 하며, 첫 실행 시 브라우저가 열리고 token.pickle이 캐시됩니다). category_id의 기본값은 '22' (인물 및 블로그)입니다. privacy_status는 'private', 'public', 또는 'unlisted' 중 하나를 선택할 수 있으며, 기본값은 'private'로 설정되어 있어 실수로 영상이 공개되는 것을 방지합니다. 에러 발생 시 🔴 ... 대신 ❌ ...를 사용하며, 추가 종속성(dependencies)은 선택 사항입니다. 종속성이 누락된 경우 도구가 정확한 pip install 명령어를 보고합니다.
get_help — 내장 매뉴얼
get_help() -> str
매개변수가 없습니다. 전체 도구 카탈로그(tool catalog), 전달 모드(delivery-mode) 안내, 그리고 시네마틱 프롬프팅 가이드(cinematic prompting guide)를 반환합니다. 이를 통해 에이전트(또는 궁금한 사용자)가 세션을 떠나지 않고도 방향을 잡을 수 있습니다.
그렇다면 Claude Code의 _skill(기술)_이란 무엇인가?
MCP가 손(Claude가 물리적으로 호출할 수 있는 도구들)이라면, **skill (기술)**은 _근육 기억 (muscle memory)_입니다. 즉, Claude의 컨텍스트(context)에 로드되어 워크플로우를 가르쳐주는 마크다운 파일(SKILL.md)과 번들된 리소스들의 집합입니다. 어떤 도구를 어떤 순서로, 어떤 제약 조건 하에 사용해야 하는지를 학습시킵니다.
omni-video의 경우, 이 skill은 다음과 같은 사항들을 인코딩합니다:
- 시네마틱하게 (cinematically) 프롬프트 작성: 장면 배치(scene layout), 피사체의 동작(subject action), 카메라 움직임(tracking shot, slow zoom), 조명, 스타일 등을 포함합니다. 모호한 표현보다는 구체적인 비트(beats)를 사용하세요.
- 편집 프롬프트는 **점진적 (incremental)**으로 유지: 장면 전체가 아닌, 변경 사항만을 설명하세요.
- 항상 최신 (latest) interaction ID를 체이닝(chain)하세요.
- 길이가 길거나 움직임이 많은 경우에는
delivery='uri'방식을 선호하세요. - 비디오 생성은 준비될 때까지 차단(block)되며 비용이 발생합니다. 관련 편집 작업은 하나의 잘 정의된 프롬프트로 배치(batch) 처리하고, YouTube에
public으로 업로드하기 전에는 반드시 확인을 요청하세요.
또한 이 skill은 MCP 서버 자체(mcp/server.py), 요구 사항(requirements), 설치 스크립트, 그리고 Interactions API 비디오 가이드를 함께 묶어 제공하므로 자기 완결적(self-contained)입니다. 즉, skill을 설치하면 서버를 구축하는 데 필요한 모든 것을 갖추게 됩니다.
설치 방법: "그냥 바로 작동했으면 좋겠어요" 버전
세 가지가 필요합니다: Python 3.10+, Claude Code, 그리고 Gemini API key (Google AI Studio에서 무료로 발급 가능)입니다. 아래 경로 중 _하나_를 선택하세요.
경로 A: 플러그인 마켓플레이스 (가장 적은 키 입력)
Claude Code 내부에서 다음과 같이 입력합니다:
/plugin marketplace add xbill9/omni-skill-claude
/plugin install omni-video@omni-skill-claude
이 방식은 skill을 설치함과 동시에 MCP 서버를 자동으로 등록합니다. 플러그인 매니페스트(manifest)에는 API key가 포함되어 있지 않으며(당연히 그래야 합니다!), 서버는 환경 변수에서 GEMINI_API_KEY를 읽어옵니다. 따라서 Claude Code를 실행하기 전에 해당 변수가 export 되어 있는지 확인하세요.
경로 B: 클론(Clone) 및 부트스트랩 (이 리포지토리 사용)
# 1. 코드 가져오기
git clone https://github.com/xbill9/omni-skill-claude.git
cd omni-skill-claude
...
정말로 이게 전부입니다. 무언가 잘못된 것 같다면 init.sh를 다시 실행해도 안전합니다.
경로 C: 본인의 프로젝트에 설치하기
리포지토리를 클론한 상태에서 다음을 실행합니다:
make init TARGET=/path/to/your/project
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기