
こんにちは。よっしーです(^^)
背景
この連載では、Claude Codeの公式ドキュメントを1ページずつ読み解いていきます。公式ドキュメントは情報が網羅されている分、「結局どの機能を、どんな場面で使えばいいのか」は自分で考える必要があり、読むのに意外と時間がかかります。そこで、私が実務で使うために読み込んだ内容を「使う場面→実例コード」の順に整理して残していくことにしました。専門家の解説というより、一次情報を読んだ記録です。推測や動作を確認していない部分には、その都度そう書きます。
1. 一言でいうと何か
プラグインは、Claude Code をカスタム機能で拡張する自己完結型のコンポーネントディレクトリです。このページはその完全な技術仕様——スキーマ、CLI コマンド、コンポーネント仕様——を扱うリファレンスです。
前回(第54回)でチャネルを作り、最後に「配布するにはプラグインでラップする」と書きました。そのラッパーの中身がこれです。
プラグインが提供できるコンポーネントは6種類あります。
- Skills:
/nameショートカットを作る。あなたも Claude も呼び出せる - Agents:特化した subagent。Claude が必要に応じて自動呼び出しできる
- Hooks:Claude Code のイベントに自動応答するハンドラー
- MCP servers:外部ツール・サービスへの接続
- LSP servers:言語サーバーによるリアルタイムのコードインテリジェンス
- Monitors:バックグラウンドで走り、stdout の各行を Claude への通知として届ける
加えてテーマ(/theme に並ぶ色テーマ)も配布でき、Monitors とテーマは実験的コンポーネントの扱いです。
構造上の重要な事実を先に押さえます。マニフェスト(.claude-plugin/plugin.json)はオプションです。省略すると Claude Code がデフォルト場所のコンポーネントを自動検出し、ディレクトリ名からプラグイン名を導出します。マニフェストが要るのは、メタデータを付けたいときと、カスタムのコンポーネントパスが必要なときだけ。
そしてもうひとつ、マーケットプレイスを介さないプラグインの作り方があります——skills ディレクトリプラグイン。~/.claude/skills/<name>/.claude-plugin/plugin.json を置けば、次のセッションで <name>@skills-dir として読み込まれます。マーケットプレイスもインストール手順も不要です。
2. どういう場面で役立つか
シーン1:チームの作業手順をまとめて配る
skills、agents、hooks を1つのディレクトリに入れて配布すれば、チーム全員が同じ拡張を持てます。--scope project でインストールすれば .claude/settings.json に書かれ、リポジトリをクローンした全員に届きます。
シーン2:LSP を入れて Claude にコードの正しさを見せる
**Claude は各編集の直後にエラーと警告を確認できます。**定義へのジャンプ、参照検索、型情報も得られます。公式マーケットプレイスに pyright-lsp / typescript-lsp / rust-analyzer-lsp があり、それ以外の言語は自分で作る形です。
シーン3:ログやステータスの変化に Claude を反応させる
Monitors はシェルコマンドをセッション中ずっと走らせ、stdout の全行を Claude への通知として配信します。tail -F ./logs/error.log を仕掛けておけば、Claude が自分から見に行かなくてもエラーに反応できます。
シーン4:マーケットプレイスを使わずに手元で作る
claude plugin init で ~/.claude/skills/ にスキャフォルドすれば、その場でプラグインになります。プロトタイプや個人用ツールの敷居がとても低い。
不要・向かないケース
- 単一の skill を配りたいだけ。プラグインでラップせず、素の skill で足ります(ただしルートに
SKILL.mdを置くだけでも単一 skill プラグインとして読まれます)。 - プラグインディレクトリの外のファイルを参照したい。できません。
../shared-utilsのようなパスはインストール後に機能しません(キャッシュにコピーされないため)。 - プラグインから
hooks/mcpServers/permissionModeを持つエージェントを配りたい。セキュリティ上サポートされていません。 - プロジェクトスコープの
@skills-dirプラグインで monitors を使いたい。読み込まれません(後述の制限)。 CLAUDE.mdでコンテキストを配りたい。**プラグインルートのCLAUDE.mdはプロジェクトコンテキストとして読み込まれません。**配るなら skill に入れます。- シェルコマンドに
${user_config.*}を埋めたい。拒否されます(後述)。
3. スキーマとコマンドの実例
コンポーネント(1)Skills
場所は skills/ または commands/、あるいはプラグインルートの単一 SKILL.md。skills はディレクトリ+SKILL.md、commands は素のマークダウンファイルという違いがあります。インストールすれば自動検出され、Claude がタスクの文脈に応じて自動呼び出しできます。
落とし穴が1つ。skills/ ディレクトリも skills マニフェストフィールドもない場合、ルートの SKILL.md が単一 skill として読まれます。このとき呼び出し名は frontmatter の name で決まり、未設定だとインストールディレクトリ名にフォールバックします。そしてマーケットプレイスからインストールしたプラグインでは、それは更新のたびに変わるバージョン文字列です。複数 skill を配るなら skills/ レイアウトを使ってください。
コンポーネント(2)Agents
場所は agents/。マークダウンファイルで、frontmatter に設定を書きます。
---
name: agent-name
description: このエージェントが専門とする内容と、Claude がそれを呼び出すべき時期
model: sonnet
effort: medium
maxTurns: 20
disallowedTools: Write, Edit
---
エージェントの役割、専門知識、動作を説明する詳細なシステムプロンプト。
サポートされるフィールドは name / description / model / effort / maxTurns / tools / disallowedTools / skills / memory / background / isolation。isolation の有効値は "worktree" のみです。
そしてセキュリティ上の制限:hooks、mcpServers、permissionMode はプラグイン提供のエージェントではサポートされません。
UI では my-plugin:code-reviewer のようにスコープ付きの名前で @-mention に現れます。
コンポーネント(3)Hooks
場所は hooks/hooks.json、または plugin.json にインライン。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/format-code.sh"
}
]
}
]
}
}
対応するライフサイクルイベントはユーザー定義 hooks と同じで、公式ページには30以上が表で列挙されています。全列挙はせず、性格ごとにまとめておきます。
- セッション:
SessionStart/SessionEnd/Setup(--init-onlyなどの一回限りの準備用) - プロンプト:
UserPromptSubmit/UserPromptExpansion(展開をブロック可能) - ツール:
PreToolUse(ブロック可能)/PostToolUse/PostToolUseFailure/PostToolBatch - 権限:
PermissionRequest/PermissionDenied - サブエージェントとタスク:
SubagentStart/SubagentStop/TaskCreated/TaskCompleted/TeammateIdle - ターン終了:
Stop/StopFailure - 環境の変化:
ConfigChange/CwdChanged/DirectoryAdded/FileChanged/InstructionsLoaded - worktree:
WorktreeCreate/WorktreeRemove(デフォルトの git 動作を置き換える) - コンテキスト:
PreCompact/PostCompact - モデル:
PreModelSwitch(切り替えをブロック可能)/PostModelSwitch - MCP:
Elicitation/ElicitationResult - その他:
Notification/MessageDisplay
PermissionDenied には第47回(オートモード)と繋がる仕様があります。JSON の hookSpecificOutput.retry: true でモデルに再試行可能だと伝えられますが、分類器が判定を出さなかった場合、Claude Code は retry を無視します。
hook タイプは5種:command(シェル)、http(イベント JSON を POST)、mcp_tool(MCP サーバーのツールを呼ぶ)、prompt(LLM で評価、$ARGUMENTS プレースホルダー)、agent(ツール付きの agentic verifier)。
見落としやすい規則が1つ。**プラグイン自身がバンドルする MCP サーバーを対象にする hook は、スコープ付き名を使う必要があります。**ツールマッチャーと if は mcp__plugin_<plugin-name>_<server-name>__<tool>、mcp_tool hook の server は plugin:<plugin-name>:<server-name>。素のサーバーキーで書いたマッチャーは発火しません。
コンポーネント(4)MCP servers
場所は .mcp.json またはインライン。形式は標準の MCP サーバー設定で、プラグインが有効になると自動起動し、Claude のツールキットに標準ツールとして現れます。ユーザーの MCP サーバーとは独立して設定できます。
コンポーネント(5)LSP servers
場所は .lsp.json またはインライン。言語サーバー名を設定にマップします。
{
"go": {
"command": "gopls",
"args": ["serve"],
"extensionToLanguage": {
".go": "go"
}
}
}
**必須は command(PATH にあること)と extensionToLanguage の2つだけ。**オプションは args / transport(stdio か socket)/ env / initializationOptions / settings / workspaceFolder / startupTimeout / shutdownTimeout / restartOnCrash / maxRestarts / diagnostics。
実用上効くものを挙げると、restartOnCrash(デフォルト true。クラッシュ後に停止させたいなら false)と、diagnostics(デフォルト true。コードナビは残しつつ自動の診断注入だけ抑えたいなら false)。なお restartOnCrash と shutdownTimeout は v2.1.205 以降が必要で、それ以前は設定スキーマが受け付けるのに、指定するとそのサーバーが起動時にスキップされました(理由は claude --debug にしか出ない)。
競合時の挙動も明快です。同じ拡張子を複数の有効なサーバーが宣言した場合、最初に登録されたものが処理し、他は起動しません(同一プラグイン内か別プラグインかを問わない)。/plugin に警告が出ます。設定が無効なサーバーはスキップされ、他は起動します。そして v2.1.205 以降はスキップされたサーバーはその拡張子を要求しないので、別の有効なサーバーが処理できます(それ以前はブロックしていました)。
最重要の注意:言語サーバーのバイナリは別途インストールが必要です。/plugin の Errors タブに Executable not found in $PATH が出たら、そのバイナリを入れてください。
コンポーネント(6)Monitors
場所は monitors/monitors.json またはインライン。JSON 配列です。
[
{
"name": "deploy-status",
"command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/poll-deploy.sh",
"description": "Deployment status changes"
},
{
"name": "error-log",
"command": "tail -F ./logs/error.log",
"description": "Application error log",
"when": "on-skill-invoke:debug"
}
]
必須は name(プラグイン内で一意。再読み込みや skill 再呼び出し時の重複プロセスを防ぐ)、command、description(タスクパネルと通知サマリーに表示される)。オプションは when で、**"always"(デフォルト)**か "on-skill-invoke:<skill-name>"(そのプラグイン内の指定 skill が最初にディスパッチされたときに開始)。
制約が3つあります。Monitor tool と同じメカニズム・同じ可用性制約——インタラクティブ CLI セッションでのみ動き、hooks と同じ信頼レベルでサンドボックスなしに実行され、Monitor tool が使えないホストではスキップされます。${user_config.*} は参照できません(後述)。そしてセッション中にプラグインを無効にしても、実行中の monitors は止まりません——セッション終了時に止まります。
マニフェストの全体像
{
"name": "plugin-name",
"displayName": "Plugin Name",
"version": "1.2.0",
"description": "Brief plugin description",
"author": {
"name": "Author Name",
"email": "author@example.com",
"url": "https://github.com/author"
},
"homepage": "https://docs.example.com/plugin",
"repository": "https://github.com/author/plugin",
"license": "MIT",
"keywords": ["keyword1", "keyword2"],
"skills": "./custom/skills/",
"commands": ["./custom/commands/special.md"],
"agents": ["./custom/agents/reviewer.md"],
"hooks": "./config/hooks.json",
"mcpServers": "./mcp-config.json",
"outputStyles": "./styles/",
"lspServers": "./.lsp.json",
"experimental": {
"themes": "./themes/",
"monitors": "./monitors.json"
},
"dependencies": [
"helper-lib",
{ "name": "secrets-vault", "version": "~2.1.0" }
]
}
必須フィールドは name だけ(kebab-case、スペースなし)。これがコンポーネントの名前空間になります。ただしマーケットプレイスエントリが別名でリストしている場合、enabledPlugins のキーと /plugin で使われるのはマーケットプレイスエントリ名です。
メタデータで押さえるべきは3つ。
displayName(v2.1.143 以降):UI 表示用の人間可読な名前。名前空間やルックアップには使われません。version:後述のバージョン管理の要。defaultEnabled(v2.1.154 以降):falseにすると無効な状態でインストールされる。外部サービスに接続するなど、ユーザーがオプトインすべきコストやスコープを追加するプラグイン向け。
defaultEnabled はフォールバックにすぎず、2つのものが優先します。ユーザーの設定(enabledPlugins のエントリ。一度書かれると更新・再インストールを越えて保持されるので、後のリリースで defaultEnabled を変えても既存ユーザーは変わりません)と、依存関係要件(他のアクティブなプラグインが必要とする場合、Claude Code が true を書き込む)。マーケットプレイスエントリの同名フィールドは plugin.json より優先します。
認識されないフィールドは無視されます。これは意図的な設計で、VS Code / Cursor の拡張マニフェスト、npm の package.json、MCPB/DXT バンドルマニフェストを兼ねる1つのファイルを保てるようにするためです。claude plugin validate は警告として報告し、エラーにしません(1〜2文字違いなら意図された名前を提案してくれます)。ただし型が違うフィールドは失敗します(keywords が配列でなく文字列、など)。CI では --strict で警告をエラーに昇格できます。
パス指定の「置き換え」と「追加」
コンポーネントパスのフィールドで、デフォルトディレクトリを置き換えるか追加するかが分かれます。ここは誤解の温床です。
- デフォルトを置き換える:
commands、agents、outputStyles、experimental.themes、experimental.monitors。**マニフェストでcommandsを指定すると、デフォルトのcommands/はスキャンされません。**両方使うなら"commands": ["./commands/", "./extras/"]と明示します。 - デフォルトに追加:
skillsのみ。skills/は常にスキャンされます(例外:マーケットプレイスエントリのsourceがマーケットプレイスルートに解決される場合)。 - 独自のマージルール:hooks、MCP servers、LSP servers。
共通ルールとして、すべてのパスはプラグインルート相対で ./ 始まり。v2.1.140 以降は、デフォルトフォルダとマニフェストキーの両方がある場合、無視されたフォルダが claude plugin list と /plugin で警告されます。
ユーザー設定(userConfig)
プラグインが有効化されたときに Claude Code がユーザーに尋ねる値を宣言します。settings.json を手で編集させる代わりに使います。
{
"userConfig": {
"api_endpoint": {
"type": "string",
"title": "API endpoint",
"description": "Your team's API endpoint"
},
"api_token": {
"type": "string",
"title": "API token",
"description": "API authentication token",
"sensitive": true
}
}
}
type は string / number / boolean / directory / file。title と description も必須。オプションは sensitive(入力をマスクし、settings.json ではなくセキュアストレージに保存)、required、default、multiple、min / max。
値の参照方法は**${user_config.KEY}** で、MCP・LSP サーバー設定と hook コマンドで置換されます。機密でない値は skill とエージェントのコンテンツでも置換可能。そしてすべての値が CLAUDE_PLUGIN_OPTION_<KEY> 環境変数としてプラグインのサブプロセスにエクスポートされます。
ここが今回いちばん重要な安全仕様です。シェルで実行されるフィールドは ${user_config.*} を拒否します。理由は明快で——設定値をシェルコマンドに置換すると、シェルがその値の中身を実行できてしまうから。拒否されるのはシェル形式の hook コマンド、monitor コマンド、MCP の headersHelper の3つで、それぞれ代替手段があります。hook なら**args を使う exec 形式にするか CLAUDE_PLUGIN_OPTION_<KEY> を環境から読む、monitor と headersHelper ならスクリプトが自分の設定ファイルから読む**。v2.1.207 より前はこれらも置換していたので、依存しているプラグインは更新が必要です。
保存先も整理されています。機密でない値は settings.json の pluginConfigs[<plugin-id>].options。Claude Code はユーザー設定に書き、ユーザー設定・--settings・管理設定から読みます。プロジェクトの .claude/settings.json と .local.json のエントリは無視されます(v2.1.207 より前は読んでいました)。機密値は macOS Keychain、非対応環境では ~/.claude/.credentials.json。キーチェーンは OAuth トークンと共有で合計約 2KB の制限があるので、機密値は小さく保つこと。
チャネル宣言
第54回の続きがここです。各チャネルはプラグインが提供する MCP サーバーにバインドされます。
{
"channels": [
{
"server": "telegram",
"userConfig": {
"bot_token": {
"type": "string",
"title": "Bot token",
"description": "Telegram bot token",
"sensitive": true
},
"owner_id": {
"type": "string",
"title": "Owner ID",
"description": "Your Telegram user ID"
}
}
}
]
}
server は必須で、プラグインの mcpServers のキーと一致する必要があります。チャネルごとの userConfig でボットトークンやオーナー ID を有効化時に尋ねられます——第45回で見た Telegram プラグインの設定フローは、これで実装されているわけです。
3つの環境変数
| 変数 | 解決先 | 用途 |
|---|---|---|
${CLAUDE_PLUGIN_ROOT} | プラグインのインストールディレクトリの絶対パス | バンドルしたスクリプト・バイナリ・設定 |
${CLAUDE_PLUGIN_DATA} | 永続ディレクトリ(初回参照時に作成、プラグイン更新後も保持) | node_modules や Python 仮想環境、生成コード、キャッシュ |
${CLAUDE_PROJECT_DIR} | プロジェクトルート | プロジェクトローカルのスクリプトと設定 |
3つとも hook プロセスと MCP / LSP サーバーのサブプロセスに環境変数としてエクスポートされます。インライン置換されるフィールドはコンポーネントごとに違い、skill / エージェントのコンテンツと hook / monitor コマンドはどこでも、MCP stdio は command / args / env、MCP http 系は url / headers / headersHelper、LSP は command / args / env / workspaceFolder。
引用の作法も明示されています。hook では args を使う exec 形式にして、各パスが引用符なしの1引数として渡るようにする。シェル形式の hook と monitor コマンドではダブルクォートで囲む。
{
"hooks": {
"PostToolUse": [
{
"hooks": [
{
"type": "command",
"command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/process.sh"
}
]
}
]
}
}
${CLAUDE_PLUGIN_ROOT} はプラグイン更新で変わります。前バージョンのディレクトリは約7日ディスクに残りますが、一時的なものとして扱い、ここに状態を書かないでください。セッション中に更新された場合、hook・monitors・MCP・LSP は前バージョンのパスを使い続けます。/reload-plugins で hook・MCP・LSP は切り替わりますが、monitors はセッション再起動が必要です。
永続データディレクトリの使い方
${CLAUDE_PLUGIN_DATA} は ~/.claude/plugins/data/{id}/ に解決されます({id} は a-zA-Z0-9_- 以外を - に置換したプラグイン識別子。formatter@my-marketplace なら formatter-my-marketplace)。
典型的な用途は言語依存関係を1回入れて再利用すること。ただしデータディレクトリは単一のプラグインバージョンより長生きするので、存在チェックだけでは、更新が依存関係マニフェストを変えたことを検出できません。公式の推奨パターンはバンドルされたマニフェストとデータディレクトリのコピーを比較し、違えば再インストールです。
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "diff -q \"${CLAUDE_PLUGIN_ROOT}/package.json\" \"${CLAUDE_PLUGIN_DATA}/package.json\" >/dev/null 2>&1 || (cd \"${CLAUDE_PLUGIN_DATA}\" && cp \"${CLAUDE_PLUGIN_ROOT}/package.json\" . && npm install) || rm -f \"${CLAUDE_PLUGIN_DATA}/package.json\""
}
]
}
]
}
}
diff は保存済みコピーが無いか異なるときに非ゼロ終了するので、初回と依存関係変更の両方をカバーします。そして末尾の rm は npm install が失敗したときにコピーを消して、次のセッションで再試行させる——短いですがよくできた1行です。
データディレクトリは最後のスコープからアンインストールすると自動削除されます。/plugin はサイズを表示して確認を求め、CLI はデフォルトで削除します(--keep-data で保持)。
キャッシュとパストラバーサル
プラグインの指定方法は2つ——--plugin-dir / --plugin-url でセッション限りか、マーケットプレイス経由でインストールか。
マーケットプレイスのプラグインは、その場で使うのではなくローカルの「プラグインキャッシュ」(~/.claude/plugins/cache)にコピーされます。ここから2つの帰結が出ます。
帰結1:パストラバーサルは効きません。../shared-utils のようにプラグインルートの外へ出るパスは、外部ファイルがキャッシュにコピーされないため、インストール後は機能しません。
帰結2:シンボリックリンクの扱いが3分岐します。リンク先がプラグイン自身のディレクトリ内なら相対シンボリックリンクとして保持、同じマーケットプレイス内の他所なら逆参照されて中身がコピー(メタプラグインの skills/ が兄弟プラグインの skill にリンクできる)、マーケットプレイス外ならセキュリティのためスキップ(任意のホストファイルをキャッシュに引き込ませない)。--plugin-dir やローカルパスのプラグインでは、自分のディレクトリ内に解決されるものだけが保持されます。
各インストール済みバージョンはキャッシュ内の別ディレクトリで、更新・アンインストールすると孤立扱いになり7日後に自動削除されます。猶予期間は、古いバージョンを既に読み込んだ同時実行セッションをエラーなく走らせ続けるため。そして Glob / Grep は孤立バージョンをスキップするので、検索結果に古いプラグインコードは混ざりません。
ディレクトリ構造
enterprise-plugin/
├── .claude-plugin/ # メタデータディレクトリ(オプション)
│ └── plugin.json # プラグインマニフェスト
├── skills/ # Skills
│ ├── code-reviewer/
│ │ └── SKILL.md
│ └── pdf-processor/
│ ├── SKILL.md
│ └── scripts/
├── commands/ # フラット .md ファイルとしての Skills
│ ├── status.md
│ └── logs.md
├── agents/ # Subagent 定義
│ ├── security-reviewer.md
│ ├── performance-tester.md
│ └── compliance-checker.md
├── output-styles/ # 出力スタイル定義
│ └── terse.md
├── themes/ # カラーテーマ定義
│ └── dracula.json
├── monitors/ # バックグラウンド monitor 設定
│ └── monitors.json
├── hooks/ # Hook 設定
│ ├── hooks.json # メイン hook 設定
│ └── security-hooks.json # 追加 hooks
├── bin/ # PATH に追加されるプラグイン実行可能ファイル
│ └── my-tool # Bash tool で裸のコマンドとして呼び出し可能
├── settings.json # プラグインのデフォルト設定
├── .mcp.json # MCP サーバー定義
├── .lsp.json # LSP サーバー設定
├── scripts/ # Hook とユーティリティスクリプト
│ ├── security-scan.sh
│ ├── format-code.py
│ └── deploy.js
├── LICENSE # ライセンスファイル
└── CHANGELOG.md # バージョン履歴
最頻出のミスが警告として明記されています。.claude-plugin/ に入るのは plugin.json だけ。他のディレクトリ(commands/、agents/、skills/、output-styles/、themes/、monitors/、hooks/)はすべてプラグインルートに置きます。「プラグインは読み込まれるがコンポーネントが見つからない」という症状は、たいていこれです。
bin/ の説明も見逃せません。ここに置いた実行可能ファイルは Bash tool の PATH に追加され、プラグインが有効なら任意の Bash 呼び出しから裸のコマンドとして使えます。
なお settings.json(プラグインのデフォルト設定)は、現在 agent と subagentStatusLine キーのみサポートです。
インストールスコープと skills ディレクトリプラグイン
スコープは他の設定と同じ4種です。
| スコープ | 設定ファイル | 用途 |
|---|---|---|
user | ~/.claude/settings.json | 全プロジェクトで使える個人プラグイン(デフォルト) |
project | .claude/settings.json | バージョン管理で共有するチームプラグイン |
local | .claude/settings.local.json | プロジェクト固有・gitignored |
managed | 管理設定 | 管理プラグイン(読み取り専用、更新のみ) |
skills ディレクトリプラグインは、.claude-plugin/plugin.json を持つフォルダを skills ディレクトリの下に置くだけで、<name>@skills-dir として読み込まれるものです。マーケットプレイスにコピーされず、その場で検出されます。
置き場所で挙動が変わります。~/.claude/skills/ は personal スコープで全プロジェクトで読まれ、<cwd>/.claude/skills/ は project スコープでワークスペースの信頼ダイアログを受け入れた後のみ読まれます。
プロジェクトスコープには追加の制限があります。理由は明快で、内容があなたではなくリポジトリから来るから。MCP サーバーはプロジェクト .mcp.json と同じサーバーごとの承認を通り、LSP サーバーは信頼後にのみ起動し、バックグラウンド monitors は読み込まれません。個人スコープにはこれらの制限はありません。
そして踏みやすい罠が警告されています。**プロジェクトスコープの @skills-dir プラグインは、Claude Code を起動したディレクトリの .claude/skills/ からしか読まれません。素の skills や commands のようにリポジトリルートまで遡りません。**サブディレクトリから起動するとリポジトリルートのプラグインが見つからないので、ルートから起動するか、cd 後に /reload-plugins を実行します。
編集の反映も非対称です。SKILL.md の変更は現在のセッションで即座に有効ですが、他のコンポーネント(hooks/、.mcp.json、agents/、output-styles/ など)の変更は有効になりません——/reload-plugins か再起動が必要です。
読み込みを止めるにはフォルダを消すか、名前で無効化します。マーケットプレイスから何もインストールしていないので uninstall はありません。
claude plugin disable my-tool@skills-dir
CLI コマンド
非対話でのプラグイン管理コマンドが揃っています。個々の構文は公式を参照するとして、全体像を挙げておきます。
| コマンド | 役割 | 押さえどころ |
|---|---|---|
claude plugin init <name> | ~/.claude/skills/<name>/ にスキャフォルド | --with で skills / agents / hooks / mcp / lsp / output-style / channel のスターターを追加。エイリアス new |
claude plugin install <plugin> | マーケットプレイスからインストール | --scope で user / project / local |
claude plugin uninstall <plugin> | 削除 | --keep-data でデータディレクトリを保持、--prune で不要な依存も削除。エイリアス remove / rm |
claude plugin prune | 不要になった自動インストール依存を削除 | 直接インストールしたプラグインは決して削除されない。--dry-run あり。エイリアス autoremove。v2.1.121 以降 |
claude plugin enable / disable | 有効化・無効化 | enable は依存を同じスコープで推移的に有効化(未インストールなら失敗)、disable は依存元があると失敗し、連鎖コマンドを提示 |
claude plugin update <plugin> | 最新へ更新 | --scope に managed も指定可 |
claude plugin list | 一覧 | --json / --available(--json 必須)。対話中は /plugin list、--enabled / --disabled と ls が使える |
claude plugin details <name> | コンポーネント一覧とトークンコスト推定 | 後述 |
claude plugin validate <path> | マニフェスト検証 | --strict で警告をエラーに |
claude plugin tag | リリース用 git タグ作成 | --push / --dry-run / --force |
claude plugin details のトークンコスト表示は運用上ありがたい機能です。2種類の数値が出ます。
- Always-on:skill の説明、エージェントの説明、コマンド名など、プラグインのリスト表示テキストが全セッションに追加するトークン。コンポーネントが実行されるかどうかに関係なくかかる。
- On-invoke:コンポーネントが実行されるときのコスト。プラグイン全体ではなくコンポーネント単位で表示される(典型的なセッションでは一部しか呼ばれないため)。
Always-on の合計はアクティブなモデルの count_tokens API で計算され、コンポーネントごとの数値はそこから比例配分。API に到達できない場合は文字ベースの推定にフォールバックします。
plugin init に関する管理者向けの注意も1つ。スキャフォルドされるプラグインは @skills-dir ソースを使うので、管理者は strictKnownMarketplaces や、管理設定の blockedMarketplaces に {"source": "skills-dir"} を加えることでブロックできます。ブロックされると plugin init は書き込み前に失敗します。
バージョン管理 — 最大の落とし穴
Claude Code はプラグインのバージョンをキャッシュキーとして使い、更新の要否を判断します。バージョンは次の順で解決されます。
plugin.jsonのversionmarketplace.jsonのマーケットプレイスエントリのversion- git ホストのマーケットプレイス(
github/url/git-subdir/ 相対パス)なら git コミット SHA - npm ソースや git 外のローカルディレクトリなら
unknown
つまりバージョニングの方法は2つに分かれます。
| アプローチ | 方法 | 更新動作 | 向く用途 |
|---|---|---|---|
| 明示的バージョン | plugin.json に "version": "2.1.0" | このフィールドをバンプしたときだけ更新が届く | 安定したリリースサイクルの公開プラグイン |
| コミット SHA | plugin.json とマーケットプレイスエントリの両方から version を省略 | git ソースへの新規コミットごとに更新が届く | 積極的に開発中の内部・チームプラグイン |
**警告が明示されています。version を設定するなら、ユーザーに届けたい変更のたびにバンプが必須。**新しいコミットをプッシュしただけでは、Claude Code は同じバージョン文字列を見てキャッシュを保持し、/plugin update は「既に最新バージョンです」と報告します。速く反復しているなら version を設定しないままにして、git コミット SHA に任せるのが正解です。
よくある問題
| 問題 | 原因 | 解決策 |
|---|---|---|
| プラグインが読み込まれない | 無効な plugin.json | claude plugin validate / /plugin validate で構文とスキーマを確認 |
| Skills が表示されない | ディレクトリ構造が違う | skills/ や commands/ をプラグインルートに置く(.claude-plugin/ 内ではない) |
| Hooks が発火しない | スクリプトが実行可能でない | chmod +x script.sh |
| MCP サーバーが失敗 | ${CLAUDE_PLUGIN_ROOT} が不足 | すべてのプラグインパスで変数を使う |
| パスエラー | 絶対パスを使っている | 相対パスで ./ 始まりにする |
LSP の Executable not found in $PATH | 言語サーバー未インストール | バイナリを入れる |
デバッグは claude --debug で、どのプラグインが読み込まれたか、マニフェストのエラー、skill / agent / hook の登録、MCP サーバーの初期化が見えます。hook が動かないときは実行権限 → shebang(#!/bin/bash など)→ ${CLAUDE_PLUGIN_ROOT} の使用 → 手動実行の順、イベントが合わないときはイベント名の大文字小文字(PostToolUse であって postToolUse ではない)→ マッチャー → hook タイプが5種のいずれかの順で確認します。
4. まとめ + 次回予告
- プラグインはskills / agents / hooks / MCP / LSP / monitors を配れる自己完結型ディレクトリ。テーマも配れる(monitors とテーマは実験的)。
- **マニフェストはオプション。**省略すればデフォルト場所を自動検出し、名前はディレクトリ名から導出される。必須フィールドは
nameのみ。 - **
.claude-plugin/に入るのはplugin.jsonだけ。**他は全部プラグインルート。これが最頻出のミス。 skillsだけがデフォルトに「追加」、他のパスフィールドは「置き換え」。- プラグイン提供エージェントでは
hooks/mcpServers/permissionModeが使えない(セキュリティ上)。 - **プラグイン自身の MCP サーバーを対象にする hook はスコープ付き名が必要。**素のキーでは発火しない。
- **LSP は言語サーバーのバイナリを別途インストールする必要がある。**同じ拡張子は先に登録した1つだけが処理する。
${user_config.*}はシェルで実行されるフィールドでは拒否される(値の中身が実行されうるため)。代わりに exec 形式かCLAUDE_PLUGIN_OPTION_<KEY>、または設定ファイル経由で渡す。${CLAUDE_PLUGIN_ROOT}は更新で変わる。状態は${CLAUDE_PLUGIN_DATA}に置く。依存関係の再インストール判定はマニフェストのdiffで。- マーケットプレイスのプラグインはキャッシュにコピーされる。だからプラグイン外へのパストラバーサルは効かず、シンボリックリンクは3分岐する。
claude plugin detailsでトークンコスト(always-on / on-invoke)が見える。versionを設定したらバンプ必須。しないと更新が届かない。速く回すなら設定せずコミット SHA に任せる。- skills ディレクトリプラグインなら、マーケットプレイスなしで
<name>@skills-dirとして読み込まれる。ただしプロジェクトスコープでは monitors が読まれず、起動ディレクトリを遡らない。
第54回でチャネルの作り方を、今回でその配布容器の仕様を扱いました。残るのは配布経路そのものです。
次回予告(暫定):**プラグインマーケットプレイス(plugin-marketplaces)**を取り上げ、marketplace.json のスキーマ、プラグインエントリの書き方、管理設定によるマーケットプレイス制限などを扱う予定です。

何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)


コメント