Claude Code · 플러그인/마켓플레이스와 OMC 팀·브리지
Claude Code · 플러그인/마켓플레이스와 OMC 팀·브리지
한 줄 요약
플러그인은 skill·agent·hook·MCP 같은 낱개 확장을 한 폴더로 묶어 “설치 가능한 한 덩어리”로 만든 포장 형식이고, OMC는 그 위에 여러 AI를 동시에 부리는 공장을 얹은 플러그인이다. 왜 배우나: 내 기능을 남에게 배포·버전관리하는 법과, 한 대가 아니라 여러 AI를 팀으로 굴리는 구조가 어떻게 가능한지 이해하기 위해서다.
그림
플러그인이 설치되어 디스크에 영속화되고, 그 위에 OMC 팀 런타임이 도는 전체 구조다.
flowchart TD
subgraph CORE["코어: 포장과 보관"]
MKT["마켓플레이스 카탈로그<br/>(marketplace.json = 상품 목록)"]
PLG["플러그인 한 덩어리<br/>(plugin.json + skills/agents/hooks/MCP)"]
CACHE["설치 캐시<br/>~/.claude/plugins/cache/버전별"]
REC["영속 장부 두 장<br/>installed_plugins.json<br/>known_marketplaces.json"]
MKT -->|install| PLG
PLG -->|소스 복사| CACHE
CACHE -->|기록| REC
end
subgraph OMC["OMC: 멀티에이전트 공장"]
MCP["MCP 서버 t<br/>(팀 시작 도구)"]
WORKER["워커들<br/>(tmux 패널마다 claude/codex/gemini)"]
DISK["파일시스템 칠판<br/>.omc/state/team/<팀>"]
MCP -->|팀 시작| WORKER
WORKER -->|결과 기록| DISK
DISK -->|상태 읽기| WORKER
end
PLG -.->|OMC도 평범한 플러그인 하나| MCP
쉽게 풀기
1단계 — 낱개를 한 봉투에 (플러그인). skill·agent·hook·MCP·LSP 등은 원래 흩어진 낱개 파일이라 남에게 주려면 일일이 위치를 설명해야 한다. 플러그인은 이걸 한 폴더로 묶고 plugin.json이라는 이름표를 붙인 것이다. 이사 박스에 “주방용품”이라 써 붙이듯, 이름표 하나로 통째로 옮기고 열고 버린다.
2단계 — 박스들의 목록표 (마켓플레이스). marketplace.json은 플러그인 박스들의 카탈로그다. 백화점 안내판처럼 “어디에 무슨 물건이 있는지”만 보여준다. claude plugin install foo@bar 하면 카탈로그를 보고 → 실제 소스(깃허브/로컬/npm)를 받아서 → 보관함(~/.claude/plugins/cache)에 버전별로 복사한다.
flowchart LR U["사용자<br/>install foo@bar"] --> C["카탈로그 조회<br/>marketplace.json"] C --> S["소스 fetch<br/>github/로컬/npm"] S --> K["버전별 캐시 복사<br/>cache/<마켓>/<플러그인>/<버전>"] K --> L["장부 기록<br/>installed+known"]
3단계 — 무엇을 깔았는지 적어 둔다 (영속성). 설치가 끝나면 두 장부에 남긴다. installed_plugins.json(무엇을 어디에) + known_marketplaces.json(어떤 카탈로그를 등록). 덕분에 세션을 껐다 켜거나 재부팅해도 깔아 둔 게 그대로 뜬다. 이게 영속성이다.
3.5단계 — 박스 안의 물건만 실제로 쓰인다. 플러그인 자체는 AI에게 말을 걸지 않는다. 봉투일 뿐이고, 모델이 실제로 소비하는 건 안의 컴포넌트(skill·agent·hook·MCP 도구)다. 봉투는 이것들을 한꺼번에 켜고/끄고/버전 올리기 좋게 묶을 뿐이다. (그래서 플러그인 루트의 CLAUDE.md는 컨텍스트로 안 실린다. 지시를 넣으려면 skill로 만들어야 한다.)
4단계 — 그 위에 공장을 얹는다 (OMC). oh-my-claudecode(OMC)는 겉보기엔 평범한 플러그인이지만 안에 여러 AI를 동시에 부리는 런타임을 넣어 뒀다. t라는 MCP 서버를 띄우고, “팀 시작” 도구를 호출하면 tmux 패널마다 claude/codex/gemini CLI를 워커로 띄운다. 워커들은 서로 대화하지 않는다. 대신 파일시스템이라는 공용 칠판(.omc/state/team/...)에 “할 일 목록”과 “공유 메모리”를 적어 두고, 각자 칠판을 보며 일을 나눠 갖는다.
[!note] 비유로 정리
- 플러그인 = 라벨 붙인 이사 박스 (낱개를 한 단위로 운반)
- 마켓플레이스 = 백화점 안내판 (어디서 받을지 알려주는 목록)
- 영속 장부 = 창고 입출고 대장 (껐다 켜도 기억)
- OMC = 그 박스에 실려 온 작은 공장 (여러 AI가 칠판을 보며 협업) 핵심: 코어는 포장과 보관까지만, 멀티에이전트 협업은 그 위에 별도 레이어로 얹는다.
핵심 정리
plugin.json (.claude-plugin/plugin.json)
name만 필수다. 매니페스트 자체가 선택이라, 없으면 기본 폴더(skills/·agents/·hooks/·.mcp.json)를 자동 발견하고 폴더명을 플러그인명으로 쓴다.
| 필드 | 필수 | 핵심 |
|---|---|---|
name | 식별자. kebab-case, 네임스페이스에 쓰임(plugin-dev:agent-creator) | |
version | semver. 캐시 키 → 올려야 업데이트 배포. 생략 시 git SHA 폴백 | |
| 그 외 | defaultEnabled·userConfig 등 (아래 펼쳐보기) |
[!note]- 펼쳐보기: 전체 필드와 변수 컴포넌트 지정 — “더함 vs 대체” 함정
skills→ 기본skills/에 더한다commands/agents/outputStyles→ 기본 폴더를 대체한다hooks/mcpServers/lspServers→ 경로 또는 인라인 객체 둘 다 가능그 외 선택 필드:
defaultEnabled(기본 true),userConfig(enable 시 사용자에게 물을 값,${user_config.KEY}로 치환),displayName(v2.1.143+),author,homepage,repository,license,keywords,channels,dependencies.변수 세 개 — 어디에 무엇을 저장할지
${CLAUDE_PLUGIN_ROOT}= 설치 위치. 업데이트 시 바뀜 → 상태 저장 금지${CLAUDE_PLUGIN_DATA}=~/.claude/plugins/data/{id}/. 업데이트에도 살아남는 영속 폴더${CLAUDE_PROJECT_DIR}= 프로젝트 루트
marketplace.json (.claude-plugin/marketplace.json)
| 필드 | 필수 | 핵심 |
|---|---|---|
name | 카탈로그 식별자(@name). 사용자당 같은 이름 하나(덮어씀) | |
owner | {name 필수, email?} | |
plugins | 엔트리 목록(엔트리는 name+source 필수) |
[!note]- 펼쳐보기:
source다섯 종류와 strict
- 상대경로
"./plugins/foo"—./로 시작,..금지- github —
repo,ref?,sha?- url —
url,ref?,sha?(GitLab 등 git URL)- git-subdir —
url,path,ref?,sha?(모노레포 sparse clone)- npm —
package,version?,registry?
strict(기본 true): true면plugin.json이 권위·마켓 엔트리는 보충, false면 마켓 엔트리가 전체 정의.
영속성은 세 곳으로 분리된다
flowchart TD I["설치 기록<br/>installed_plugins.json<br/>(무엇을 어디에)"] K["카탈로그 등록<br/>known_marketplaces.json<br/>(사용자당 1회)"] E["켜짐 상태(enable)<br/>settings.json<br/>(스코프별)"] S["실제 소스<br/>cache/<마켓>/<플러그인>/<버전>"] I --- K --- E --- S
체크리스트: 설치 기록·카탈로그 등록·enable 상태·실제 소스 캐시 — 네 개가 따로 산다. 특히 설치와 enable은 별개 파일이다.
OMC 팀 워커의 영속 상태 경로 (모두 .omc/state/team/<팀>/ 아래)
| 경로 | 용도 |
|---|---|
tasks/<id>.json (+.lock, .failure.json) | 공유 태스크·클레임 락·실패 사이드카 |
workers/<w>/inbox·outbox·heartbeat·status | 워커 메일박스/하트비트 |
shared-memory / logs/team-bridge-<팀>.jsonl | 공유 메모리 / 감사 로그 |
실제 예시
최소 마켓플레이스 + 플러그인 (복붙용)
my-marketplace/
├── .claude-plugin/marketplace.json
└── plugins/my-tool/
├── .claude-plugin/plugin.json
├── skills/greet/SKILL.md
└── hooks/hooks.json # 선택
// marketplace.json
{ "name": "my-plugins", "owner": { "name": "Your Name" },
"plugins": [ { "name": "my-tool", "source": "./plugins/my-tool", "description": "데모 도구" } ] }
// plugins/my-tool/.claude-plugin/plugin.json
{ "name": "my-tool", "version": "0.1.0", "description": "데모 도구" }
claude plugin validate ./my-marketplace # 스키마/중복/경로 검사
/plugin marketplace add ./my-marketplace
/plugin install my-tool@my-plugins
/my-tool:greet # 네임스페이스: <plugin>:<skill>
[!note]- 펼쳐보기: 실제 Anthropic 번들 + 영속 장부 디스크 상태 번들 마켓플레이스/플러그인 (최소 plugin.json의 모범)
// harness-work/claude-code/.claude-plugin/marketplace.json { "$schema": "https://json.schemastore.org/claude-code-marketplace.json", "name": "claude-code-plugins", "owner": { "name": "Anthropic", "email": "support@anthropic.com" }, "plugins": [ { "name": "hookify", "description": "Easily create custom hooks...", "version": "0.1.0", "source": "./plugins/hookify", "category": "productivity" } // agent-sdk-dev, code-review, plugin-dev, ralph-wiggum, security-guidance ... ] }// plugins/hookify/.claude-plugin/plugin.json { "name": "hookify", "version": "0.1.0", "description": "Easily create hooks to prevent unwanted behaviors...", "author": { "name": "Daisy Hollman", "email": "daisy@anthropic.com" } }주목: hookify엔
hooks필드가 없다 →hooks/폴더 자동발견에 맡김.strict:true기본이라 마켓 엔트리의description은 보충일 뿐.영속 장부 두 장 (실제 디스크)
// ~/.claude/plugins/installed_plugins.json — "무엇을 어디에" { "version": 2, "plugins": { "oh-my-claudecode@omc": [ { "scope": "user", "installPath": "~/.claude/plugins/cache/omc/oh-my-claudecode/4.14.6", "version": "4.14.6", "installedAt": "2026-06-10T04:03:46.951Z", "gitCommitSha": "deee3a446dadc9bfea31cdc8b19b00b16718082e" } ], "dd@gptaku-plugins": [ { "scope": "user", "version": "0.2.1" } ] } }// ~/.claude/plugins/known_marketplaces.json — "어떤 카탈로그를 등록" { "omc": { "source": { "source": "git", "url": "https://github.com/Yeachan-Heo/oh-my-claudecode.git" }, "installLocation": "~/.claude/plugins/marketplaces/omc", "lastUpdated": "2026-06-10T04:03:26.437Z" } }
OMC가 MCP 서버 하나로 멀티에이전트의 문을 여는 법
코어가 보기엔 그냥 MCP 도구 하나지만, 그 도구가 호출되면 코어 바깥 tmux에서 워커들이 떠 파일시스템으로 협업한다.
sequenceDiagram participant CC as Claude Code(코어) participant T as MCP 서버 t participant TX as tmux 패널 participant FS as 파일시스템 칠판 CC->>T: team_start(agentTypes, tasks, cwd) T->>TX: 패널마다 워커 기동(claude/codex/gemini) TX->>FS: tasks 클레임(.lock O_EXCL)·결과 기록 FS-->>TX: 남은 태스크·공유 메모리 읽기 Note over TX,FS: 완료 또는 cleanup까지 워커 생존
[!note]- 펼쳐보기: 브리지 코드 전문(MCP 등록·스키마·CLI 기동)
// marketplaces/omc/.mcp.json { "mcpServers": { "t": { "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/bridge/mcp-server.cjs"] } } }// bridge/team-mcp.cjs (startSchema) — 팀 시작 도구의 입력 스키마 var startSchema = external_exports.object({ teamName: ...string().describe('Slug name (e.g. "auth-review")'), agentTypes: ...array(...string()).describe('"claude" | "codex" | "gemini"'), tasks: ...array(...object({ subject: ...string(), description: ...string() })) .describe("Tasks to distribute to workers"), cwd: ...string().describe("Working directory (absolute path)"), newWindow: ...boolean().optional() // 전용 tmux 윈도우 vs 현재 분할 });// bridge/team-bridge.cjs (spawnCliProcess) — 외부 CLI 기동 if (provider === "codex") { cmd = "codex"; args = ["exec", "-m", model || "gpt-5.3-codex", "--json", "--dangerously-bypass-approvals-and-sandbox", "--skip-git-repo-check"]; } else { cmd = "gemini"; args = ["--approval-mode", "yolo"]; if (model) args.push("--model", model); } const child = spawn(cmd, args, { stdio: ["pipe","pipe","pipe"], cwd }); child.stdin?.write(prompt); child.stdin?.end(); // 프롬프트는 stdin으로
[!warning] 보안 게이트 (남의 팀 런타임을 흉내 낼 때 필수) 브리지는
--config <path>로만 기동되고,validateConfigPath가 경로를~/.claude/·~/.omc/하위로 강제한다.workingDirectory도 홈 하위 + git 워크트리 내부여야 한다. 태스크 본문은sanitizePromptContent로<system-reminder>류 주입 태그를 무력화한 뒤 넣는다(프롬프트 인젝션 방어).
[!note]- 펼쳐보기: 멀티에이전트 런타임을 직접 얹는 5단계 (OMC 패턴 모방)
plugin.json의mcpServers로 stdio MCP 서버 등록(${CLAUDE_PLUGIN_ROOT}/server.js).- 그 서버에
team_start(agentTypes, tasks, cwd)류 도구 노출.- 도구가 tmux를
new-session/split하고, 각 패널 워커를--config <state/config.json>로 기동.- 통신·영속은 파일시스템으로:
tasks/<id>.json(+.lockO_EXCL),outbox.jsonl,heartbeat.json,shared-memory/.- 외부 CLI(codex/gemini)는 stdin으로 프롬프트 주입, stdout을
outputs/*.md에 기록.
요약 & 셀프체크
3줄 요약:
- 플러그인은 낱개 확장을 한 폴더+이름표로 묶은 포장, 마켓플레이스는 그 박스들의 카탈로그다.
- Claude Code는 설치한 것을
installed_plugins.json·known_marketplaces.json두 장부에 기록해 꺼져도 기억한다(영속성). 단 모델이 소비하는 건 봉투가 아니라 안의 컴포넌트다. - OMC는 평범한 플러그인이지만 MCP 서버
t로 멀티에이전트 런타임을 얹어, tmux 워커들이 파일시스템 칠판으로 태스크를 나눈다 — 통신 매체가 컨텍스트가 아니라 디스크라서 재개가 가능하다.
스스로 답해 보기:
- Q1. 플러그인 폴더에
CLAUDE.md를 넣으면 모델 컨텍스트에 실릴까? (왜?) - Q2.
version을 생략하면 무슨 일이 일어나고, 안정 배포 시 왜 매 릴리스마다 올려야 하나? - Q3. OMC 워커들은 서로 어떻게 일을 나누고 진척을 공유하나? 세션이 죽어도 이어서 할 수 있는 이유는?
연결
CC_개요 · _분석축_루브릭 · CC_40_skills · CC_60_subagents · CC_70_mcp-and-tools · CC_80_guardrails
[!tip]- Codex 교차검증 (원문 분석 시점 보존)
- hookify엔
hooks필드가 없고hooks/자동발견에 맡김 —strict:true기본이라 마켓 엔트리description은 보충일 뿐 확인됨.- 설치(
installed_plugins.json)와 enable(settings.json) 분리,cache/<마켓>/<플러그인>/<버전>/버전별 복사 구조 확인됨.- OMC 팀은 코어가 보기엔 MCP 도구일 뿐, 진짜 멀티에이전트는 코어 바깥 tmux 자식 프로세스에서 돌고 통신 매체가 파일시스템 — 워커는 완료 또는 명시적
omc_run_team_cleanup까지 살며 타임아웃은wait호출에만 적용됨.- 근거 파일:
_원문아카이브/claude-code/plugins-reference.md,plugin-marketplaces.md,harness-work/claude-code/.claude-plugin/marketplace.json,plugins/hookify/.claude-plugin/plugin.json,~/.claude/plugins/installed_plugins.json,known_marketplaces.json,marketplaces/omc/.claude-plugin/plugin.json+/.mcp.json,omc/bridge/team-bridge.cjs,team-mcp.cjs,omc/CLAUDE.md.