Claude Code Mods 사용법: 첫 Mod 만들기·테스트·설치와 커뮤니티 예시 15선
업데이트

Claude Code의 Mod는 Claude Code 안에서 실행되는 JavaScript 또는 TypeScript 함수로 만든 플러그인입니다. Mod는 패널이나 프롬프트 위의 한 줄을 그릴 수 있습니다. 즉시 실행되는 슬래시 커맨드를 추가할 수 있고, 도구 호출을 실행 전에 막거나 고쳐 쓸 수도 있습니다. Mods는 2026년 10월 1일 Claude Code 2.1.287과 함께 나왔고, 기본으로 켜져 있습니다.
목적에 맞는 가장 빠른 방법을 고르세요.
| 하고 싶은 일 | 방법 |
|---|---|
| 코드를 쓰지 않고 Mod를 얻고 싶을 때 | 세션에서 원하는 Mod를 설명합니다. 예를 들어 "턴마다 걸린 시간을 보여 주는 Mod를 만들어 줘"라고 요청하면 Claude가 코드를 쓰고, 핫 리로드를 켤지 한 번만 묻습니다 |
| 다른 사람이 만든 Mod를 설치하고 싶을 때 | 터미널 세션에서 /plugin install <name>@<marketplace>, 또는 셸에서 claude plugin install <name>@<marketplace> |
| 개발 중인 폴더를 불러오고 싶을 때 | claude --plugin-dir ./my-mod |
| 내장 사이드 에이전트를 써 보고 싶을 때 | /plugin enable cc-plugin-you-should-know@builtin |
먼저 버전부터 확인하세요. claude --version이 2.1.287 이상을 출력해야 합니다. Desktop 앱은 자체 Claude Code를 내장하고 있고, 여기서는 2.1.286부터 Mods가 동작합니다. Code 탭에서 /status를 입력하면 지금 쓰는 버전을 볼 수 있습니다.
직접 테스트한 내용. 필자는 2026년 10월 7일 macOS의 Desktop 앱 Code 탭에서 Claude Code 2.1.293으로 Mod 3개를 만들었습니다. 명령 가드, 프롬프트 위 턴 미터, Python 작업용
/pyenv커맨드입니다. 세 Mod 모두claude plugin validate를 통과했고,claude plugin test로 돌린 자동 테스트 8개도 모두 통과했습니다. 가드는 핫 리로드를 켠 실제 세션에서도 돌려 봤습니다. 커뮤니티 관련 내용은 10월 1일부터 7일까지 Zenn, Qiita, note.com에 올라온 일본어 글 약 50편, 공식 Mods 문서 (opens in a new tab), 그리고 10월 6일 자 awesome-claude-code-mods (opens in a new tab) 카탈로그의 스캔 데이터를 바탕으로 했습니다.
- Claude Code Mods 사용법: 첫 Mod 만들기·테스트·설치와 커뮤니티 예시 15선
- Claude Code AGENTS.md 지원: 설정 방법, 4가지 로딩 모드, Codex·OpenCode·Cursor와 한 파일 공유하기
- GPT Image 2.5 사용법: Flare·Sunburst 비교와 API 가격
- DeepSeek Harness 사용법: 설치, 설정, 첫 에이전트 실행까지
- Runcell Science: Claude Science를 대체할 오픈소스 AI 연구 워크스페이스
- 맥 잠자기 방지: 맥북 닫아도 Codex와 Claude Code 계속 실행하기
- OpenClaw vs ZeroClaw vs Pi Agent vs Nanobot: 2026년에 어떤 AI 에이전트 스택을 선택해야 할까?
- Claude Code로 Jupyter 노트북을 분석하는 방법 | Data Science 실무 가이드와 한계
- Claude Code 루틴 사용법: AI 에이전트 cron 작업과 자동 트리거
- Claude Code Desktop에서 Bypass permissions 켜는 법
- Google의 A2A 프로토콜을 사용한 두 개의 Python 에이전트 빌드하기 - 단계별 튜토리얼
- 2025년 파이썬에서 가장 성장하는 상위 10개 데이터 시각화 라이브러리
Mod란 무엇이고, 언제 다른 방법을 써야 할까
Mods가 나오기 전에는 settings hook, 스킬, 상태 표시줄 스크립트, MCP 서버로 Claude Code를 바깥에서 확장했습니다. 차이는 코드가 실행되는 위치입니다. settings hook은 Claude Code가 바깥에서 실행하는 스크립트입니다. Mod의 함수는 Claude Code 자체 프로세스 안에서 실행됩니다. 그래서 Mod는 인터페이스에 그릴 수 있고, 나머지는 그릴 수 없습니다.
공식 개요 문서는 이 방법들을 대략 이렇게 비교합니다.
| Mod | Settings hook | 스킬 | MCP 서버 | |
|---|---|---|---|---|
| 형태 | 플러그인 안의 JS 또는 TS 함수 | settings.json에 적는 셸 명령, HTTP 호출, 프롬프트 | 지시 사항을 담은 SKILL.md 파일 | Claude에게 도구를 제공하는 외부 프로세스 |
| UI에 그리기 | 가능 | 불가 | 불가 | 불가 |
| 바꿀 수 있는 것 | 도구 호출, 프롬프트, 커맨드, 턴, UI | 호출을 진행할지 여부, 호출의 인자와 결과, 추가 컨텍스트 | Claude가 아는 것과 하는 일 | Claude가 쓸 수 있는 도구 |
| 이럴 때 선택 | 패널, 프롬프트 위 밴드, 즉시 실행되는 커맨드가 필요하거나 이벤트를 고쳐 쓰고 싶을 때 | 차단하거나 로그를 남기는 스크립트가 이미 있을 때 | 같은 지시를 계속 붙여 넣고 있을 때 | Claude가 외부 시스템에 접근해야 할 때 |
이름에 함정이 하나 있습니다. Mods 문서에서 "hook"은 Mod의 함수를 뜻하고, 예전 방식은 "settings hook"이라고 부릅니다. settings hook은 지금도 동작하고, 지원 중단되지도 않았습니다. 플러그인 하나에 Mod, 스킬, MCP 서버를 함께 넣을 수도 있습니다.
Mod가 동작하는 곳
훅은 거의 모든 곳에서 실행됩니다. 화면에 그리는 기능은 터미널과 Desktop 앱에서만 동작합니다.
| Claude Code를 실행하는 곳 | 훅 실행 | Mod UI 표시 |
|---|---|---|
터미널의 claude, 에디터 내장 터미널과 JetBrains 포함 | 예 | 예 |
| Desktop 앱의 Code 탭 | 예 | 예, 터미널 전용 요소는 제외 |
| Desktop 앱의 WSL 세션 | 아니요 | 아니요 |
| VS Code 확장의 채팅 패널 | 예 | 아니요 |
claude -p와 Agent SDK | 예 | 아니요 |
| 클라우드 세션 | 예, 플러그인이 세션까지 전달되는 경우 | 아니요 |
이 표는 공식 개요 문서에서 가져왔습니다. 가드 Mod는 VS Code와 claude -p에서도 계속 보호해 주지만, 미터 Mod는 그릴 곳이 없다는 뜻입니다.
Claude Code가 두 벌 설치된 환경도 조심하세요. 일본 개발자 여러 명이 헷갈리는 validate 오류를 겪었습니다. Homebrew로 설치한 오래된 터미널 CLI가 Desktop 앱에 내장된 2.1.286과 함께 깔려 있었기 때문입니다. 보고된 CLI 버전은 2.1.226과 2.1.234였습니다. 사용하는 곳마다 버전을 확인하세요.
Mod의 파일 구성
작은 Mod는 파일 세 개면 됩니다.
safe-shell/
├── .claude-plugin/
│ └── plugin.json name, version, description
└── hooks/
├── hooks.json { "modules": ["./register.ts"] }
└── register.ts your coderegister.ts는 register(on) 함수 하나를 export합니다. on(event, matcher, hook)을 호출할 때마다 훅이 하나씩 추가되고, 모든 훅은 같은 세 인자를 받습니다.
$는 엔진입니다. 내 코드 바깥의 모든 것은 엔진을 거칩니다.$.ui.toast,$.process.run,$.state,$.model.complete가 그 예입니다.e는 이벤트입니다. 예를 들어 Claude가 곧 실행하려는 Bash 명령이 여기에 들어 있습니다.next(e)는 이벤트를 다른 Mod에 넘기고, 그다음 Claude Code에 넘깁니다. 호출하지 않고 반환하면 그 훅이 직접 답한 것이 됩니다.next({ ...e, command })를 호출하면 실제로 일어나는 일이 바뀝니다.
모듈에는 Node도 DOM도 없습니다. 이 제약 덕분에 Claude Code는 Mod가 호출하는 모든 것을 실행 전에 나열할 수 있습니다. 아무 Mod 폴더에서나 실행해 보세요.
claude plugin validate ./safe-shellMod 1: 파괴적인 명령을 막는 가드
auto 모드나 Bypass permissions 모드로 Claude를 실행하면, git reset --hard 앞에서 Claude를 멈춰 줄 장치가 없습니다. 이 Mod는 세 종류의 셸 명령을 거부하고, .env 파일이 대화에 들어오지 않게 막습니다.
import type { Register } from 'claude-code'
// Shell commands an agent should never run without a human typing them.
const BLOCKED: { pattern: RegExp; reason: string }[] = [
{
pattern: /\brm\s+-[a-zA-Z]*[rR][a-zA-Z]*\s+(\/|~|\$HOME)\/?\*?(\s|$)/,
reason: 'recursive delete of / or the home folder',
},
{
pattern: /\bgit\s+push\b.*(--force\b|--force-with-lease\b|\s-f\b).*\b(main|master)\b/,
reason: 'force push to main or master',
},
{
pattern: /\bgit\s+reset\s+--hard\b/,
reason: 'git reset --hard throws away uncommitted work',
},
]
// .env, .env.local, config/.env.production ... but not .env.example.
const isSecretEnvFile = (path: string) =>
/(^|\/)\.env(\.[^/]+)?$/.test(path) && !/\.(example|sample|template)$/.test(path)
export const register: Register = on => {
on('tool.call', { tool: 'Bash' }, ($, e, next) => {
const hit = BLOCKED.find(rule => rule.pattern.test(e.command))
if (hit === undefined) {
return next(e)
}
$.ui.toast(`safe-shell blocked: ${hit.reason}`)
return { deny: `safe-shell: blocked (${hit.reason}). Ask the user to run it themselves.` }
}).catch(($, e, next) =>
// Fail closed: if the guard itself breaks, refuse instead of letting the command through.
next.called ? next(e) : { deny: 'safe-shell: the guard failed, so the command was not run.' },
)
for (const tool of ['Read', 'Edit', 'Write'] as const) {
on('tool.call', { tool }, ($, e, next) =>
isSecretEnvFile(e.file_path)
? { deny: `safe-shell: ${e.file_path} holds secrets and stays out of the conversation.` }
: next(e),
).catch(($, e, next) =>
next.called ? next(e) : { deny: 'safe-shell: the guard failed, so the file was not touched.' },
)
}
}정규식보다 중요한 점이 두 가지 있습니다.
가드는 next를 호출하기 전에 판단합니다. next가 실행되면 명령도 이미 실행된 것입니다. 그 뒤에 deny를 반환해도 되돌릴 수 있는 것은 없습니다.
.catch가 가드를 fail closed로 만듭니다. 훅이 예외를 던지거나 10초 예산을 넘기면 엔진은 그 훅을 건너뜁니다. 그리고 건너뛴 가드는 명령을 그대로 통과시킵니다. next.called ? next(e) : deny 핸들러는 이때 통과 대신 거부를 택합니다. validate는 호출을 통과시킬지 결정하는 gating hook을 모두 나열하고, 각각에 .catch가 있는지 보여 줍니다. 다른 사람이 만든 가드를 볼 때 가장 먼저 확인할 부분입니다.
❯ ./register.ts hooks: tool.call{tool=Bash}, tool.call{tool=Read|Edit|Write}
❯ ./register.ts gating hook with .catch: tool.call{tool=Bash}
❯ ./register.ts gating hook with .catch: tool.call{tool=Read|Edit|Write}
❯ ./register.ts calls: $.ui.toast
✔ Validation passed세션 없이 테스트하기
claude plugin test는 Mod의 *.test.ts 파일을 엔진 위에서 실행합니다. 세션, 로그인, 네트워크가 없어도 됩니다. 다만 테스트 키트에는 Mod 아래에 아무것도 없습니다. 그래서 테스트마다 Mod가 다음으로 넘기는 호출을 대신 받아 줄 스텁 훅을 등록합니다.
import { expect, test } from 'claude-code/testing'
const ran = { stdout: 'ran', stderr: '', interrupted: false }
test('blocks rm -rf on the home folder', async ($, on) => {
on('tool.call', { tool: 'Bash' }, () => ({ result: ran }))
const answer = await $.tool.call({ tool: 'Bash', command: 'rm -rf ~' })
expect(answer.deny).toContain('recursive delete')
})
test('lets ordinary commands through', async ($, on) => {
on('tool.call', { tool: 'Bash' }, () => ({ result: ran }))
const answer = await $.tool.call({ tool: 'Bash', command: 'rm -rf ./build && python -m pytest -q' })
expect(answer.deny).toBeUndefined()
})전체 파일에는 테스트가 4개 있습니다.
(pass) blocks rm -rf on the home folder [16.23ms]
(pass) blocks a force push to main [7.73ms]
(pass) lets ordinary commands through [7.22ms]
(pass) keeps .env out of Read but allows .env.example [9.40ms]
4 pass
0 fail필자의 첫 실행은 on("tool.call") after the test first called $ 오류로 실패했습니다. 스텁은 각 테스트의 맨 위, 첫 $ 호출보다 먼저 등록하세요.
실제 세션에서 확인한 동작
핫 리로드를 켠 상태에서 Claude에게 echo "the words git reset --hard inside an echo"를 실행하게 했습니다. 가드는 "blocked (git reset --hard throws away uncommitted work)"라는 메시지로 거부했고, 토스트 알림도 떴습니다. 그런데 이 echo는 무해했습니다. 패턴 매칭은 명령과 그 명령을 언급하는 텍스트를 구분하지 못합니다. 변수, 스크립트, alias에 숨은 명령도 놓칩니다. rafi-guard, delete-guard, board-guard처럼 비슷한 가드를 공개한 일본 개발자들도 두 문제를 모두 보고했습니다.
가드 Mod는 두 번째 안전망으로 다루세요. 절대 일어나면 안 되는 일은 권한 deny 규칙에도 넣어 두세요. 반대 방향도 알아 둬야 합니다. 공식 문서에 따르면 managed settings가 없는 개인 컴퓨터에서는 deny 규칙이 거부하는 호출을 Mod가 승인할 수 있습니다. Mod는 샌드박스에서 실행되지 않습니다. 사용자의 권한으로 실행됩니다.
Mod 2: 프롬프트 위의 미터
Mod가 가장 많이 그리는 곳은 프롬프트 위 밴드와 사이드 패널입니다. 10월 6일 카탈로그 스캔에서 Mod의 41%가 밴드에 그리고, 40%가 패널을 엽니다. 이번 Mod는 직전 턴의 소요 시간, 도구 호출 수, 토큰 수를 보여 주고, Hide 버튼으로 숨길 수 있습니다.
화면에 그릴 때 읽는 값은 $.state에 둡니다. 각 값은 작은 타입 파일에 선언합니다.
// types/index.d.ts, named in plugin.json as "types": "./types/index.d.ts"
export type TurnStats = {
seconds: number
tools: number
inputTokens: number
outputTokens: number
model: string | null
}
declare module 'claude-code' {
interface PluginState {
'turn-meter': { last: TurnStats | null; isHidden: boolean }
}
}훅은 도구 호출을 세고, turn.complete에서 durationMs와 usage를 읽은 뒤 밴드를 그립니다.
import { atom, read, update } from 'claude-code'
import type { Register } from 'claude-code'
import type { TurnStats } from '../types'
const last = atom({ plugin: 'turn-meter', key: 'last' } as const, null)
const isHidden = atom({ plugin: 'turn-meter', key: 'isHidden' } as const, false)
const formatTokens = (n: number) => (n >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n))
export const register: Register = on => {
let tools = 0
on('prompt.submit', ($, e, next) => {
tools = 0
return next(e)
})
on('tool.call', ($, e, next) => {
// Count the main conversation's calls; subagents carry an agentId.
if (e.agentId === undefined) tools += 1
return next(e)
})
on('turn.complete', async ($, e, next) => {
if (e.agentId !== undefined) return next(e)
const usage = e.usage
const stats: TurnStats = {
seconds: Math.round(e.durationMs / 1000),
tools,
inputTokens: usage
? usage.input_tokens + usage.cache_read_input_tokens + usage.cache_creation_input_tokens
: 0,
outputTokens: usage?.output_tokens ?? 0,
model: usage?.model ?? null,
}
await update($, last, () => stats)
if (stats.seconds > 120) $.ui.toast(`That turn took ${stats.seconds}s`)
return next(e)
})
on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
const stats = await read($, last)
if (e.props.hasSurvey || stats === null || (await read($, isHidden))) return next(e)
const { Box, Button, Text } = $.ui.resolve(e)
const model = stats.model === null ? '' : ` · ${stats.model}`
// The band is shared by every mod: keep what the mods beneath drew, then add a row.
const below = await next(e)
return (
<Box flexDirection="column">
{below}
<Box>
<Text dimColor>
{`Last turn: ${stats.seconds}s · ${stats.tools} tool calls · ${formatTokens(stats.inputTokens)} in / ${formatTokens(stats.outputTokens)} out${model} `}
</Text>
<Button key="hide" label="Hide" onPress={() => update($, isHidden, () => true)} />
</Box>
</Box>
)
})
}테스트에서는 터미널과 Desktop 양쪽에서 밴드가 이렇게 나옵니다.
Last turn: 42s · 0 tool calls · 18.2k in / 800 out · claude-opus-5-5 [ Hide ]만들면서 알게 된 점 세 가지입니다.
- 그리는 값은 모듈 변수가 아니라
$.state에 두세요. 핫 리로드는register를 다시 실행해 변수를 초기화하지만,$.state는 그대로 남습니다./clear를 하면 처음부터 다시 시작하므로, 그보다 오래 남겨야 하는 값은session.end에서$.store에 쓰세요. - 밴드는 모든 Mod가 함께 씁니다.
next(e)를 호출하고 그 결과를 내Box안에 넣으세요. 그러지 않으면 다른 Mod의 줄을 전부 가리게 됩니다. note.com의 한 저자도 이 구조를 알기 전까지 밴드 하나가 다른 Mod에 가려지는 문제를 겪었습니다. Text요소는key를 유지하지 않습니다. 테스트에서는 표시된 문구로 찾으세요.ui.find({ type: 'Text', text: /Last turn/ })처럼 씁니다.
Mod 3: Claude가 엉뚱한 Python을 실행할 때 쓰는 /pyenv
데이터 사이언스용 Mod를 찾아보다가 빈 곳을 발견했습니다. 10월 6일 스캔에 잡힌 Mod 2,690개 중 이름이나 설명에 Jupyter, pandas, DataFrame, Polars, venv, conda를 언급한 것은 하나도 없습니다.
이 Mod가 해결하는 문제는 데이터 작업에서 흔합니다. Claude의 Bash 도구는 PATH에서 가장 먼저 찾은 python3를 실행합니다. 그런데 그것이 프로젝트 환경이 아닌 경우가 많습니다. 필자의 컴퓨터에서 환경 확인용 프로브 스크립트를 돌리면 이렇게 출력됩니다.
python: /Users/<you>/.local/bin/python3 (3.14.2)
env: none (system or tool-managed Python)
pandas: 3.0.1
numpy: 2.4.2
polars: not installed
matplotlib: 3.11.0
pygwalker: not installed활성화된 가상 환경이 없었습니다. 이 상태에서 Claude가 pip install을 실행했다면 전역 인터프리터에 패키지가 설치됐을 것입니다.
/pyenv 커맨드는 이 프로브를 실행하고 두 부분으로 답합니다. text는 사용자가 보는 줄입니다. context는 모델만 읽는 메모입니다. 덕분에 Claude는 사용자가 다시 알려 주지 않아도 다음 Bash 호출에서 어떤 인터프리터를 쓰는지 압니다.
import type { Register } from 'claude-code'
// Reads package versions from metadata instead of importing them, so it stays fast.
const PROBE = `
import os, sys
from importlib.metadata import PackageNotFoundError, version
env = os.environ.get("VIRTUAL_ENV") or os.environ.get("CONDA_PREFIX") or "none (system or tool-managed Python)"
print(f"python: {sys.executable} ({sys.version.split()[0]})")
print(f"env: {env}")
for name in ("pandas", "numpy", "polars", "matplotlib", "pygwalker"):
try:
print(f"{name}: {version(name)}")
except PackageNotFoundError:
print(f"{name}: not installed")
`
export const register: Register = on => {
on('session.start', async ($, e, next) => {
await $.command.register({
name: 'pyenv',
description: "Show which Python, environment and data packages this session's shell uses",
})
return next(e)
})
on('command.run', { command: 'pyenv' }, async $ => {
try {
const probe = await $.process.run(['python3', '-c', PROBE], { timeoutMs: 15_000 })
if (probe.exitCode !== 0) {
return { text: `pyenv: python3 exited with ${probe.exitCode}: ${probe.stderr.trim().slice(0, 300)}` }
}
const report = probe.stdout.trim()
// `text` is the row you see; `context` is a note only the model reads.
return {
text: report,
context: [`Python environment of this session's shell, from /pyenv:\n${report}`],
}
} catch (error) {
return { text: `pyenv: could not run python3 (${String(error).slice(0, 200)})` }
}
})
}Mod의 슬래시 커맨드는 모델 턴 없이 함수를 바로 실행합니다. Claude가 작업 중일 때도 실행됩니다. $.process.run은 셸을 거치지 않고 인자 목록을 받습니다. timeoutMs를 지정하지 않으면 30초 뒤에 타임아웃됩니다.
그렇다면 $.process.run은 Claude의 Bash 도구와 같은 PATH를 볼까요? 커뮤니티의 한 저자가 Mod가 띄운 프로세스에서 nvm의 node를 찾지 못했다고 보고해서, 임시 프로브 Mod로 확인해 봤습니다. 필자의 Desktop 세션에서는 그냥 호출한 $.process.run이 Bash 도구와 똑같은 PATH를 받았고, python3도 같은 것을 가리켰습니다. 반면 로그인 셸 zsh -lc를 거쳐 프로브를 실행하면 틀린 답이 나왔습니다. python.org의 3.10.9를 가리켰습니다. 비대화형 로그인 셸은 ~/.zshrc를 건너뛰는데, PATH 추가 설정은 대부분 그 파일에 있기 때문입니다. 그냥 호출하는 방식을 유지하세요. 내 컴퓨터에서 결과가 이상해 보이면 Claude Code 프롬프트에서 !which python3를 입력해 비교해 보세요.
테스트하면서 함정을 하나 더 발견했습니다. $.process.run과 $.command.register 같은 $ 호출에도 스텁이 필요하고, $ 호출용 스텁은 { value: ... } 형태로 응답해야 합니다. 결과를 그대로 반환하면 테스트 키트가 받아들이지 않습니다.
분석 대부분을 스크립트가 아니라 노트북에서 한다면 이 방법에는 한계가 있습니다. Claude Code는 .ipynb 파일을 JSON으로 편집할 뿐, 실행 중인 커널을 보지 못합니다. RunCell (opens in a new tab)은 셀을 실행하고 그 출력을 직접 읽는 노트북 에이전트라서 그런 작업에 더 잘 맞습니다.
Mod를 Claude에게 만들게 하기
이 코드를 전부 손으로 쓸 필요는 없습니다. 세션에서 "5시간 사용량을 프롬프트 위에 보여 주는 Mod를 만들어 줘"처럼 요청하면 됩니다. Claude는 내장 plugin-authoring 스킬을 불러와 ~/.claude/dev-mods/<session-id>/에 파일을 씁니다. 첫 파일을 쓸 때 "Enable hot reloading for this session?"이라는 질문이 한 번 나옵니다. 동의하면 Claude의 턴이 끝날 때마다 수정 사항이 다시 로드됩니다. 위의 Mod 3개도 이렇게 만들고 불러왔습니다.
문서와 커뮤니티가 덧붙이는 내용입니다.
- 폴더는 영구적인 위치에 복사해 두세요. Qiita와 Zenn의 저자들은 dev-mods 폴더가 약 30일 뒤 오래된 세션과 함께 정리된다고 보고합니다.
- 개발할 때는
claude --plugin-dir ./my-mod를 쓰세요. 설치한 Mod는 캐시된 사본으로 실행되므로, 원래 폴더를 고쳐도 플러그인을 업데이트하기 전까지는 아무것도 바뀌지 않습니다. - Desktop 앱에는 실행 플래그를 넘길 수 없습니다.
~/.claude/settings.json의env블록에CLAUDE_CODE_PLUGIN_DIRS를 설정하세요. 이 값은 프로젝트 설정에 넣으면 무시됩니다. /plugin을 실행하세요.1 mod active · safe-shell같은 흐린 줄이 보이면 Mod가 로드된 것입니다.- 만드는 동안에는
claude --debug로 시작하세요. 디버그 로그에는 건너뛴 훅과 엔진이 거부한 그리기 작업이 모두 남습니다.
살펴볼 만한 커뮤니티 Mod 15선
스타 316개를 받은 awesome-claude-code-mods (opens in a new tab) 카탈로그는 출시 5일 뒤인 10월 6일에 저장소 1,265개에 있는 공개 Mod 2,690개를 스캔했습니다. 스캔 데이터를 보면 사람들이 무엇을 만드는지 알 수 있습니다.
- 68%는 슬래시 커맨드를 추가합니다.
- 53%는 도구 호출에 훅을 겁니다.
- 41%는 프롬프트 위에 그리고, 40%는 패널을 엽니다.
- 40%는 프로세스를 실행하고, 12%는 모델을 호출하고, 11%는 네트워크를 씁니다.
아래 15개를 보면 그 범위를 알 수 있습니다. 스타 수는 2026년 10월 7일 기준입니다.
| Mod | 하는 일 | 스타 | 읽어 볼 포인트 |
|---|---|---|---|
| terminal-browser (opens in a new tab) | 대화 옆에 웹 브라우저를 열어 웹사이트, 로컬 HTML 미리보기, 풀 리퀘스트를 봅니다 | 3,695 | 지금까지 나온 패널 중 가장 야심 찬 시도 |
| claude-image-view (opens in a new tab) | 붙여 넣은 이미지를 [Image #1] 대신 프롬프트 위에 썸네일로 보여 줍니다 | 160 | 터미널에서 이미지를 그리는 방법 |
| hamzafer/claude-code-mods (opens in a new tab) | 컨텍스트 바, blast-radius 가드, Markdown 미리보기, 서브에이전트 대시보드를 묶은 세트 | 138 | 저장소 하나에 여러 종류의 Mod |
| claude-auto-handoff (opens in a new tab) | 길어진 세션을 구조화된 브리핑과 함께 새 세션에 넘깁니다 | 60 | 컨텍스트 관리 |
| prismantis (opens in a new tab) | 답변에 테마를 입힙니다. 표, 코드, 차트를 박스 아트로 그리고, 오른쪽에서 왼쪽으로 쓰는 텍스트도 지원합니다 | 49 | Claude 자신의 메시지 스타일 바꾸기 |
| cc-arcade (opens in a new tab) | 프롬프트 위에서 즐기는 게임 9개. Claude가 작업을 마치면 일시 정지합니다 | 41 | 프레임 클록을 쓰는 애니메이션 |
| jev-permission-gate (opens in a new tab) | auto 모드에서 Jev 판단 모델이 도구 호출을 심사합니다 | 28 | 개인정보 설명이 꼼꼼합니다. 최근 메시지 3개를 각각 최대 1,500자까지 TypeSafe의 API로 보낸다고 밝힙니다 |
| prompt-rail (opens in a new tab) | 지난 프롬프트를 레일처럼 늘어놓습니다. 마우스를 올리면 내용이 보이고, 클릭하면 그 위치로 이동합니다 | 22 | Zenn에서 좋아요를 가장 많이 받은 일본어 Mods 리뷰 글의 주인공 |
| claude-paste-view (opens in a new tab) | 붙여 넣은 이미지와 긴 텍스트를 미리 보여 줍니다 | 18 | 작고 읽기 쉬운 코드 |
| claude-gfm-render (opens in a new tab) | 답변 속 GitHub 알림, 작업 목록, Mermaid를 박스 아트나 SVG로 그립니다 | 14 | 실행 환경에 따라 다르게 그리는 방법 |
| claude_qamods (opens in a new tab) | qa-guide가 Claude의 질문과 선택지를 사이드 패널에서 설명합니다 | 10 | 내장 대화상자 개선 |
| CC-Usage-Band (opens in a new tab) | 5시간 한도와 7일 한도, 컨텍스트, 캐시 적중률을 프롬프트 위에 표시합니다 | 10 | 가장 흔한 종류의 Mod인 미터 |
| harness-scope (opens in a new tab) | 저장소별 프로필로 전역 스킬, 에이전트, 규칙을 숨깁니다 | 1 | 작성자가 측정해 보니 스킬 목록이 98개에서 49개로 줄었습니다 |
| touch-map (opens in a new tab) | Claude가 어떤 파일을 나열하고, 읽고, 고치고, 만들었는지 보여 줍니다 | 0 | 작성자는 리팩터링 조사 한 번에 Claude가 파일 304개 중 125개를 건드린 것을 확인했습니다 |
| claude-mods-router (opens in a new tab) | $.model.classify로 프롬프트마다 effort 수준을 정합니다 | 0 | 좋아요 26개를 받은 Qiita 해설 글은 모델을 바꾸면 프롬프트 캐싱이 깨진다고 지적합니다 |
Anthropic도 claude-code-playground (opens in a new tab)에 샘플 3개를 공개했습니다. token-weather, blast-radius, replay-theater입니다. /diff를 포함한 내장 Mod의 소스는 claude-code 저장소 (opens in a new tab)에 있습니다.
커뮤니티에서 자리 잡은 베스트 프랙티스
출시 첫 주에 일본 개발자들만 해도 글을 약 50편 올렸습니다. 동작하는 Mod, 측정 결과, 실패 보고가 담긴 글입니다. 그중 반복해서 나온 습관은 다음과 같습니다.
- 설치하기 전에 Mod를 읽습니다. 저장소를 clone하고
claude plugin validate를 실행한 뒤hooks:와calls:줄을 읽습니다.$.process,$.http,$.fs.write,$.env.get,$.model이 나오는 곳은 소스까지 읽으세요. 업데이트할 때마다 다시 validate하세요. 통과했다는 것은 엔진이 코드를 불러올 수 있다는 뜻이지, 코드가 안전하다는 뜻이 아닙니다. - 가드는
next전에 판단하고 fail closed로 둡니다. 지켜보기만 하는 Mod는.catch(($, e, next) => next(e))로 fail open하게 두세요. 그래야 미터의 버그가 작업을 막지 않습니다. - 기다릴 때는
$호출 안에서 기다립니다. 훅에는 자기 몫의 시간 10초가 주어집니다. 진행 중인$.process.run이나$.ui.ask는 이 시간에 포함되지 않지만, 직접 만든 promise는 포함됩니다. $를 저장하거나 구조 분해하지 않습니다. 파일 최상위에 선언한 함수에만 넘기세요. 여러 저자의 Mod가 이 문제를 고치기 전까지 오류 메시지도 없이 로드되지 않았습니다.- 프롬프트 캐시를 신경 씁니다. 세션 중간에 모델을 바꾸거나 요청마다 시스템 프롬프트를 바꾸면 캐시가 버려집니다.
e.surface로 분기합니다.Svg는 Desktop 앱에서만 그려지고,Raster와Image는 터미널에서만 그려집니다.- 팀에서 쓸 때는 버전을 고정합니다. API는 얼리 액세스 단계라서 릴리스마다 바뀝니다. 출시 뒤 나온 2.1.288부터 2.1.293까지 릴리스 6개 중 5개가 Mod 동작을 바꾸거나 고쳤습니다.
비밀 값 마스킹은 경계해야 할 사례입니다. note.com의 한 저자는 도구 출력에 들어 있는 API 키를 Claude가 보기 전에 바꿔치기하는 Mod를 만들었습니다. 그래도 Claude는 실패한 편집을 od -c로 디버깅하면서 원래 값을 그대로 출력했습니다. 파일 전체를 쓰는 Write는 진짜 키 자리에 플레이스홀더를 덮어썼습니다. 끝까지 막아 낸 것은 그 파일에 건 권한 deny 규칙뿐이었습니다. 화면에 보이는 내용을 바꾸는 Mod는 모델에게서 아무것도 숨기지 못합니다.
자주 겪는 문제
| 증상 | 가능한 원인 | 해결 방법 |
|---|---|---|
/plugin에 Mod가 보이지 않음 | 신뢰하지 않은 폴더, --safe-mode, --bare, disableAllHooks | 폴더를 신뢰하고 플래그 없이 다시 시작하세요 |
hooks modules are turned off in this process | Anthropic이 설치된 Mod를 원격으로 껐습니다 | 로컬에서 바꿀 것은 없습니다. 2.1.289와 2.1.290에서 Mod가 꺼진 채 남던 두 가지 경우를 고쳤으니 업데이트하세요 |
validate가 "expected record, received undefined"로 실패 | PATH 앞쪽에 오래된 CLI가 있습니다 | claude --version을 실행하고 업데이트하세요 |
| 오류 표시 없이 훅이 건너뛰어짐 | 훅이 예외를 던졌습니다. 예를 들어 속성을 $.plugin.name()처럼 함수로 호출한 경우입니다 | claude --debug로 실행하고 건너뛴 이유가 적힌 줄을 읽으세요 |
| 패널이 열리지 않음 | 요청하지 않아도 열리는 패널은 창 너비가 144열이 될 때까지 기다립니다. 대화 기록 옆에 붙는 것도 전체 화면 레이아웃에서만 가능하고, /diff가 패널을 가릴 수도 있습니다 | 창을 넓히고, 전체 화면 레이아웃을 써 보고, /diff를 닫으세요 |
| 터미널에서는 되는데 Desktop에서는 안 됨 | Desktop은 자체 버전을 내장하고 있고, 일부 요소는 터미널 전용입니다 | Code 탭에서 /status를 확인하고 e.surface로 분기하세요 |
VS Code나 claude -p에서 Mod UI가 안 보임 | 이 환경들은 그리기를 지원하지 않습니다 | 대화 기록에 남는 줄이나 커맨드가 돌려주는 텍스트로 대신하세요 |
| 테스트가 "nothing beneath the plugins answers"로 실패 | 테스트 키트에는 엔진이 없습니다 | 스텁 훅을 추가하고, $ 호출에는 { value }로 응답하세요 |
Mod 공유하기
.claude-plugin/ 안에서 plugin.json 옆에 marketplace.json을 둡니다.
{
"name": "my-mods",
"owner": { "name": "you" },
"plugins": [{ "name": "safe-shell", "source": "./" }]
}폴더를 GitHub에 push하세요. 그러면 누구나 터미널 세션에서 설치할 수 있습니다.
/plugin install safe-shell --marketplace you/safe-shellDesktop 앱의 로컬 세션과 SSH 세션에서는 +, Plugins, Add plugin 순서로 들어갑니다. 저장소에 claude-code-mod 토픽을 붙여 두면, 보통 다음 push 후 몇 시간 안에 커뮤니티 카탈로그에 올라갑니다.
FAQ
관련 가이드
- Claude Code Desktop에서 Bypass permissions 켜는 법
- Claude Code AGENTS.md 사용법: CLAUDE.md 차이, 4가지 로딩 모드, Codex·OpenCode와 공유
- Claude Code 루틴 사용법: AI 에이전트 cron 작업과 자동 트리거
- Claude Agent SDK (TypeScript)로 Claude Code 스타일의 AI 에이전트 만들기
- OpenCode 사용법 가이드: 시작 방법, 실전 팁 7가지, Oh My OpenCode와 함께 쓰는 법