# Claude Code 플러그인 만들어 배포하기 — 스킬·에이전트를 묶어 명령 한 줄로 설치하게

> Claude Code 플러그인은 스킬·서브에이전트·hooks·MCP 서버를 한 폴더에 묶은 것이다. plugin.json에 무엇을 적는지, --plugin-dir로 시험하는 법, claude plugin validate로 검사하는 법, marketplace.json으로 배포하는 법, 저장소 하나로 내보내는 예까지 정리했다.

- URL: https://aiweekly.app/blog/claude-code-plugins/
- 날짜: 2026-11-07
- 발행: AI Weekly (https://aiweekly.app)

## 한눈에 보기

Claude Code 플러그인은 스킬·서브에이전트·hooks·MCP 서버를 한 단위로 묶은 폴더다. 매니페스트는 .claude-plugin/plugin.json이고 꼭 필요한 칸은 name 하나다. skills/·agents/·hooks/hooks.json·.mcp.json은 .claude-plugin/ 안이 아니라 플러그인 맨 위에 둔다. 플러그인 스킬은 /플러그인이름:스킬이름으로 불린다. 만드는 중에는 claude --plugin-dir로 한 세션만 불러 시험하고, claude plugin validate로 검사한다. 배포는 폴더나 .zip을 직접 주기, 내 마켓플레이스(.claude-plugin/marketplace.json이 든 git 저장소), Anthropic 디렉터리 제출 세 가지다. 사용자는 claude plugin marketplace add 소유자/저장소 뒤 claude plugin install 이름@마켓플레이스로 설치한다. version을 적었다면 릴리스마다 올려야 업데이트가 간다.


결론부터. **플러그인은 스킬·서브에이전트·hooks를 한 폴더에 묶어, 남이 명령 한 줄로 설치하게 만드는 포장이다.** 폴더 맨 위에 `.claude-plugin/plugin.json`을 두고, 그 옆에 `skills/`·`agents/` 같은 폴더를 둔다. 배포는 이 폴더가 든 git 저장소에 `marketplace.json`을 하나 더 넣으면 끝난다. 이 글은 AI(Claude)가 썼고, 내용은 [Claude Code 공식 문서 — Plugins](https://code.claude.com/docs/en/plugins)와 그 아래 문서들과 대조했다.

## 언제 플러그인으로 만드나

스킬·서브에이전트·hooks·MCP 서버는 모두 **플러그인 없이도** 돈다. [5편](https://aiweekly.app/blog/claude-code-skills/)의 스킬은 `~/.claude/skills/`에 두면 내 모든 프로젝트에서 쓰인다. 공식 문서는 이렇게 가른다.

- **나 혼자, 한 프로젝트에서만** 쓰면 → 그대로 둔다
- **팀과 나누거나, 여러 프로젝트에 깔거나, 버전을 붙여 내보내려면** → 플러그인으로 묶는다

플러그인으로 옮기면 바뀌는 게 둘 있다. 파일이 플러그인 폴더 아래로 가고, 이름 앞에 플러그인 이름이 붙는다. `hello` 스킬은 `/my-plugin:hello`가 된다. 그래서 두 플러그인이 같은 이름의 스킬을 가져도 부딪치지 않는다.

비용도 있다. 켜 둔 플러그인은 **모든 세션**에 들어온다. 스킬·에이전트의 이름과 설명이 매 턴 컨텍스트에 올라가고, hooks는 이벤트마다 돈다. 모두 **내 권한으로** 돈다.

## 폴더 모양

플러그인 맨 위 폴더를 '플러그인 루트'라고 부른다. 쓰는 폴더만 만들면 된다.

| 위치 | 담는 것 |
|---|---|
| `.claude-plugin/plugin.json` | 매니페스트(이름·설명·버전 등) |
| `skills/<이름>/SKILL.md` | 스킬. 폴더 하나에 하나 |
| `agents/<이름>.md` | 서브에이전트. 파일 하나에 하나 |
| `hooks/hooks.json` | hooks 설정 |
| `.mcp.json` | MCP 서버 정의 |
| `commands/` | 옛 형식 명령. 새로 만들 땐 `skills/`를 쓴다 |

공식 문서가 경고로 박아 둔 것 둘.

- **`.claude-plugin/` 안에는 `plugin.json`만 둔다.** `skills/`를 그 안에 넣으면 불러오지 않는다. "플러그인은 뜨는데 스킬이 없다"는 증상의 흔한 원인이다.
- **플러그인 루트에 둔 `CLAUDE.md`는 읽히지 않는다.** 지침은 스킬로 넣는다.

## plugin.json 칸

공식 문서: **매니페스트는 선택이고, 쓴다면 필수 칸은 `name` 하나다.** 매니페스트가 없으면 기본 폴더에서 찾은 것을 불러오고, 이름은 폴더 이름(또는 마켓플레이스 항목)에서 온다.

| 칸 | 정하는 것 |
|---|---|
| `name` | 플러그인 식별자. kebab-case. 모든 스킬·에이전트 이름 앞에 붙음 |
| `displayName` | 화면에 `name` 대신 보일 이름. 띄어쓰기 가능 |
| `version` | 버전 글. **적으면 바꿀 때까지 사용자가 그 버전에 머문다** |
| `description` | `/plugin`에서 보이는 설명 |
| `author` | `name` 필수, `email`·`url` 선택 |
| `homepage` | 문서 URL. URL로 안 읽히면 플러그인이 안 뜬다 |
| `repository`·`license`·`keywords` | 저장소 주소, SPDX 라이선스(`MIT` 등), 검색 태그 |
| `userConfig` | 켤 때 사용자에게 물어볼 값(토큰은 `sensitive: true`) |

`skills`·`agents`·`hooks`·`mcpServers` 칸으로 기본 폴더 말고 다른 위치도 가리킬 수 있다. 경로는 플러그인 루트 기준이고 **`./`로 시작해야** 하며, `..`로 루트 밖을 가리키면 안 뜬다.

모르는 최상위 칸은 오류 없이 지워지고, 검사에서 경고만 뜬다.

## 첫 플러그인 만들고 돌려 보기

공식 문서의 첫 예제 순서다.

```bash
mkdir -p my-first-plugin/.claude-plugin
mkdir -p my-first-plugin/skills/hello
```

`my-first-plugin/.claude-plugin/plugin.json`:

```json
{
  "name": "my-first-plugin",
  "description": "A greeting plugin to learn the basics",
  "version": "1.0.0",
  "author": { "name": "Your Name" }
}
```

`skills/hello/SKILL.md`는 5편의 스킬 파일 그대로다. 이제 검사하고 불러 본다.

```bash
claude plugin validate ./my-first-plugin
claude --plugin-dir ./my-first-plugin
```

검사가 통과하면 `✔ Validation passed`가 찍힌다. 세션이 뜨면 `/my-first-plugin:hello`를 친다.

- `--plugin-dir`은 **그 세션에만** 불러오고 설정에 아무것도 쓰지 않는다. 폴더 대신 `.zip`도 된다.
- 세션 중에 파일을 고쳤으면 `/reload-plugins`를 친다.
- 안 뜨면 `/plugin`의 **Errors** 탭을 본다. 없는 경로는 `path not found`로 뜬다.

### 이미 .claude/에 만들어 둔 게 있다면

`.claude/` 아래 `skills`·`agents`·`commands`를 플러그인 루트로 복사하고, `settings.json`의 `hooks` 덩어리는 모양 그대로 `hooks/hooks.json`에 옮긴다. 주의점 하나. 원본을 지우기 전까지 **hooks는 두 번 돈다.** hooks에는 플러그인 이름 같은 구분이 없기 때문이다. 플러그인이 잘 도는 걸 확인하고 원본을 지운다.

## 배포하는 세 가지 길

| 길 | 누가 설치하나 | 업데이트 |
|---|---|---|
| 폴더나 `.zip`을 직접 준다 | 받은 사람 | 없음. 받은 사본 그대로 |
| 내 마켓플레이스 | 저장소에 접근할 수 있는 사람(비공개 저장소도 됨) | 사용자가 `claude plugin update`, 또는 자동 업데이트를 켰을 때 |
| Anthropic 디렉터리 제출 | claude.ai·Cowork에서 추가한 사람. Claude Code에도 계정 동기화로 들어옴 | 제출한 버전이 공개된 뒤 |

디렉터리 제출은 유료 claude.ai 요금제가 필요하고, [claude.ai/directory/manage](https://claude.ai/directory/manage) 개발자 포털에서 한다. 공식 마켓플레이스(`claude-plugins-official`)는 이 포털로 받지 않는다.

## 마켓플레이스 만들기

마켓플레이스는 **가게가 아니라 목록**이다. `.claude-plugin/marketplace.json`이 든 폴더나 저장소이고, 여기에 플러그인 이름과 가져올 곳을 적는다. 필수 칸은 `name`·`owner`·`plugins` 셋이고, `plugins`의 항목마다 `name`과 `source`가 필요하다.

`source`는 플러그인이 어디 있느냐로 고른다.

| source | 쓰는 때 |
|---|---|
| `"./plugins/my-plugin"` 같은 상대 경로 | 플러그인이 같은 저장소 안에 있을 때 |
| `github` | 플러그인이 따로 GitHub 저장소일 때 |
| `git-subdir` | 다른 저장소(모노레포)의 하위 폴더일 때 |
| `url`·`archive`·`npm`·`command` | 다른 git 호스트, HTTPS zip, npm 패키지, 명령 출력 |

상대 경로는 `.claude-plugin/`의 바깥, **마켓플레이스 루트에서** 센다. 공식 문서는 설치 실패 대부분이 이 기준을 틀렸거나 항목 `name`과 `plugin.json`의 `name`이 다를 때라고 한다. **두 이름은 같게** 둔다.

푸시하고 나면 사용자는 셸에서 두 줄을 친다.

```bash
claude plugin marketplace add your-org/your-marketplace
claude plugin install deploy-helper@your-marketplace
```

설치 아이디는 `플러그인이름@마켓플레이스이름`이다. Claude Code v2.1.275부터는 세션 안에서 `/plugin install deploy-helper --marketplace your-org/your-marketplace` 한 줄로 둘을 같이 할 수 있다.

## 예: 저장소 하나에 마켓플레이스와 플러그인 같이 두기

혼자 플러그인 하나를 내보낼 때 흔한 모양이다. 저장소 하나가 마켓플레이스이자 플러그인 보관소다. 이름은 예로 `team-tools`를 쓴다.

```text
team-tools/
├── .claude-plugin/
│   └── marketplace.json      (마켓플레이스 목록)
└── plugin/                   (플러그인 루트)
    ├── .claude-plugin/
    │   ├── plugin.json
    │   └── icon.png
    ├── skills/review/SKILL.md
    ├── agents/               (checker.md 등)
    └── README.md
```

저장소 맨 위 `marketplace.json`:

```json
{
  "name": "team-tools",
  "owner": { "name": "your-org" },
  "plugins": [
    { "name": "team-tools", "source": "./plugin" }
  ]
}
```

`plugin/.claude-plugin/plugin.json`:

```json
{
  "name": "team-tools",
  "displayName": "Team Tools",
  "version": "0.1.0",
  "description": "Review skill and checker agent for our team",
  "author": { "name": "your-org" },
  "license": "MIT",
  "icon": "./.claude-plugin/icon.png",
  "privacyPolicyUrl": "https://example.com/privacy"
}
```

공식 문서와 맞춰 보면 이렇다.

1. **플러그인을 `plugin/` 하위 폴더에 뒀다.** 그래서 `source`가 `"./plugin"`이고, 저장소의 다른 코드와 섞이지 않는다.
2. **마켓플레이스 항목과 `plugin.json`의 `name`이 둘 다 `team-tools`다.** 설치 아이디는 `team-tools@team-tools`가 된다.
3. **에이전트는 `team-tools:checker`처럼 플러그인 이름이 붙는다.** 스킬도 `/team-tools:review`다. README에 `/review`라고 적는 실수가 흔하다. 플러그인 에이전트는 `permissionMode`·`hooks`·`mcpServers`·`initialPrompt` 머리말이 무시되니, hooks·MCP 서버는 플러그인 쪽에 둔다.
4. **`icon`·`privacyPolicyUrl`은 Claude Code가 읽지 않는 칸이다.** Anthropic 디렉터리 목록이 읽는다. 공식 문서는 이 칸들을 마켓플레이스 항목이 아니라 `plugin.json`에만 두라고 한다.
5. **`version`을 적었다.** 그래서 릴리스할 때마다 숫자를 올려야 한다(아래 실수 목록 참고).

README의 설치 안내는 한 줄이면 된다.

```text
/plugin install team-tools --marketplace your-org/team-tools
```

v2.1.275보다 옛 버전에서는 `/plugin marketplace add your-org/team-tools` 뒤 `/plugin install team-tools@team-tools`로 나눠 친다.

## 자주 하는 실수

- **`version`을 그대로 두고 푸시한다.** `plugin.json`에 `version`을 적었는데 숫자를 안 올리면, `claude plugin update`가 "이미 최신"이라며 옛 사본을 둔다. 릴리스마다 올리거나, git 마켓플레이스라면 `version`을 빼서 커밋 SHA를 버전으로 쓰게 한다.
- **배포한 플러그인의 `name`을 바꾼다.** 기존 설치는 옛 이름으로 기록돼 있어서 플러그인이 사라진다. 보이는 이름만 바꾸려면 `displayName`을 쓰고, 꼭 바꿔야 하면 `marketplace.json`의 `renames`를 쓴다.
- **`--plugin-dir`에 마켓플레이스 루트를 준다.** 이 옵션은 `marketplace.json`을 읽지 않는다. 오류도 없이 플러그인이 안 뜬다. 플러그인 폴더 하나를 가리킨다.
- **hooks 스크립트 경로를 고정 경로로 쓴다.** 설치 위치는 사람마다 다르다. `"${CLAUDE_PLUGIN_ROOT}"/scripts/format.sh`처럼 쓴다. 이 경로는 업데이트 때 바뀌니 남길 데이터는 `${CLAUDE_PLUGIN_DATA}`에 둔다.
- **검사만 하고 설치는 안 해 본다.** 없는 폴더를 가리킨 상대 경로는 `validate`를 통과하고 설치 때 실패한다. 공유 전에 `claude plugin marketplace add ./경로`로 직접 설치해 본다.

## 자주 묻는 질문

### 플러그인과 스킬은 뭐가 다른가요?

스킬은 일하는 법을 적은 `SKILL.md` 한 장이고, 플러그인은 스킬·서브에이전트·hooks·MCP 서버를 묶은 포장이다. 남에게 나누거나 버전을 붙여 내보낼 때 플러그인으로 묶는다.

### 배포 전에 무엇을 검사하나요?

셸에서 `claude plugin validate --strict ./내-플러그인`을 돌린다. `--strict`는 경고도 실패로 친다. 마켓플레이스 폴더에 돌리면 `marketplace.json`과 상대 경로 플러그인들의 `plugin.json`까지 본다.

### 이름에 claude를 넣어도 되나요?

`claude-`나 `anthropic-`으로 시작하면 검사에서 오류가 난다. `mcp-for-claude`처럼 중간에 단어로 들어가면 경고가 뜬다. 마켓플레이스 이름도 `claude-plugins-official` 같은 공식 이름은 쓸 수 없다.

## 참고한 링크

- [Claude Code 공식 문서 — Plugins overview](https://code.claude.com/docs/en/plugins)
- [Claude Code 공식 문서 — Create a plugin](https://code.claude.com/docs/en/plugins/create)
- [Claude Code 공식 문서 — Plugin components](https://code.claude.com/docs/en/plugins/components)
- [Claude Code 공식 문서 — Plugin manifest reference](https://code.claude.com/docs/en/plugins/manifest-reference)
- [Claude Code 공식 문서 — Create a marketplace](https://code.claude.com/docs/en/plugins/create-marketplace)
- [Claude Code 공식 문서 — Marketplace reference](https://code.claude.com/docs/en/plugins/marketplace-reference)
- [Claude Code 공식 문서 — Publish and distribute a plugin](https://code.claude.com/docs/en/plugins/publish)
- [Claude Code 스킬 만들기 (5편)](https://aiweekly.app/blog/claude-code-skills/)
- [Claude Code 서브에이전트 만들기 (4편)](https://aiweekly.app/blog/claude-code-subagents/)
