Claude Codeプラグインのリリースチャネルとバージョン解決
Claude Codeプラグインのバージョンは5段階の優先順位で決まります。stable/latestをグループ別に配るリリースチャネルの設計手順も扱います。
更新したのに届かない、触っていないのに届く
Claude Codeは、プラグインごとに「バージョン」を計算します。このバージョンは更新の検知に使われ、キャッシュ用ディレクトリの名前にもなります。claude plugin update や自動更新は、計算し直した値が installed_plugins.json の記録と同じなら、キャッシュを入れ替えません。
社内マーケットプレイスを運用すると、問い合わせは2方向から来ます。作者がコミットを積んだのに claude plugin update が <name> is already at the latest version (<version>). と返す。逆に、作者は何もしていないのに更新が全員へ届く。どちらも原因は、バージョンが5段階の優先順位のどこで決まっているかにあります。
優先順位は5段階: どこで値が決まるか
command ソース以外のプラグインは、次の順で値を探します。上から見て最初に見つかった値で確定し、下の候補は読まれません。
| 優先順位 | 参照元 | 該当するソース |
|---|---|---|
| 1 | 参照元plugin.json の version | 該当するソースすべてのソース |
| 2 | 参照元マーケットプレイスエントリの version | 該当するソースすべてのソース |
| 3 | 参照元コミットSHA(github / url / git-subdir は先頭12文字) | 該当するソースgithub / url / git-subdir / gitホスト型マーケットプレイス内の相対パス |
| 4 | 参照元SHA-256ダイジェストの先頭12文字 | 該当するソースarchive。エントリで sha256 を固定していればその値、なければダウンロードしたファイルの値 |
| 5 | 参照元unknown | 該当するソースnpm、またはgitリポジトリでないローカルディレクトリ |
git-subdir のSHA版には、サブディレクトリのパスのハッシュも加わります。~/.claude のようにgit管理された親ディレクトリのSHAは、バージョンの算出に使われません。
command ソースだけは別ルールです。コマンドの出力から作った12文字のハッシュが常に使われ、plugin.json に version があれば <version>-<hash> になります。エントリ側の version は読まれません。つまり command では、バージョン文字列を触らなくても出力が変われば更新が届きます。
優先順位の外にも、バージョンが効かない形が2つあります。ローカルディレクトリとして追加したマーケットプレイスのプラグインは、バージョン文字列と無関係に、セッション開始のたびに現在のファイルを読み込みます。claude.aiでホストされるマーケットプレイスのプラグインは、claude.aiが記録した値がバージョンで、マニフェストの version は読まれません。
症状から原因を切り分ける
「更新が届かない」と「勝手に届く」は、次の順に見ると絞り込めます。
バージョン起因の問い合わせを切り分ける
- 1
どのソース形式かを見る
commandならコマンド出力のハッシュが原因です。ローカルディレクトリ追加なら、そもそもバージョンの対象外です。 - 2
plugin.json の version を見る
値があれば、コミットをいくつ積んでも文字列が変わるまで更新は届きません。ここで決まっていれば、以降の段階は見る必要がありません。
- 3
エントリ側の version を見る
plugin.jsonに値が無いときだけ効きます。両方に書いてあれば、エントリ側は黙って無視されています。 - 4
どちらにも無ければ SHA かダイジェスト
参照先のコミットまたはファイルが動いたときだけ更新されます。
refが動かないブランチやタグなら、動かない限り届きません。
自動更新の側にも落とし穴があります。自作・社内のマーケットプレイスの自動更新は既定でオフです。公式マーケットプレイスと、claude.aiから追加したものは既定でオンになります。
marketplace.json にこれを有効にするフィールドはありません。利用者が /plugin のMarketplacesでEnable auto-updateを選ぶか、管理者が管理設定の autoUpdate で指定します。有効にした場合も、インタラクティブなセッションで最初のメッセージを送ってから最大10分のランダムな待ちを挟んで動きます。
更新が入っても、動いているセッションは読み込み済みのバージョンのままです。Plugin updated: <name> · Run /reload-plugins to apply と表示されるので、反映は /reload-plugins で行います。更新や削除をした旧バージョンのディレクトリには .orphaned_at が書かれ、14日後にバックグラウンドで消えます。
3通りのバージョン管理を使い分ける
command 以外のソースでは、運用は3種類に分かれます。
| 方法 | 設定 | 更新の効き方 | 向いている用途 |
|---|---|---|---|
| 明示バージョン | 設定plugin.json に "version": "2.1.0" | 更新の効き方値を上げたときだけ更新 | 向いている用途安定したリリースサイクルの公開プラグイン |
| コミットSHA版 | 設定plugin.json とエントリの両方で version を省略 | 更新の効き方参照先コミットが変わるたび更新 | 向いている用途活発に開発中の社内・チーム向け |
| ダイジェスト版 | 設定archive ソースで version を省略 | 更新の効き方sha256 を固定していればその値を変えたとき。固定なしならzipのバイト列が変わるたび | 向いている用途静的サーバーにzipで置くプラグイン |
選ぶ基準は「誰が、いつ更新を受け取るか」です。数人で使う開発中のツールならSHA版が手間いらずです。複数チームが依存する基盤プラグインは、意図したタイミングだけ配れる明示バージョンが向きます。
セマンティックバージョニングの形式は、バージョン文字列としては検査されません。ただし依存側が ^2.0 や ~2.1.0 のような範囲で指定するときは、<plugin名>--v<version> というgitタグに対して範囲が照合されます。依存される側のプラグインは、タグを打つ前提で MAJOR.MINOR.PATCH を守ると、依存側の範囲指定が意図どおりに働きます。
プレリリース版には注意が要ります。^2.0 のような範囲は、2.0.0-beta.1 のようなプレリリース版に一致しません。一致させたい依存側は ^2.0.0-0 のようにプレリリースの接尾辞を付けて指定します。latestチャネルを 2.2.0-beta.1 で配っても、^2.0 で依存するプラグインには届かないということです。
versionを両方に書くと何が起きるか
この食い違いは検査コマンドで見えます。次は、plugin.json が1.0.0、エントリが2.0.0のマーケットプレイスを、空のHOMEと一時ディレクトリで検査した出力です(v2.1.287で確認)。
claude plugin validate .⚠ Found 2 warnings:
❯ description: No marketplace description provided. ...
❯ plugins[0].version: Entry declares version "2.0.0" but
fmt/.claude-plugin/plugin.json says "1.0.0". At install time,
plugin.json wins (calculatePluginVersion precedence) — the entry
version is silently ignored. Update this entry to "1.0.0" to match.
✔ Validation passed with warnings終了コードは0のままで、CIで落とすには --strict が要ります。リリース用のタグを切る claude plugin tag は、同じ不一致を警告でなくエラーとして止めます。validate はマーケットプレイスのルートで実行しましたが、tag は対象の fmt プラグインのディレクトリで実行します。ルートで実行すると、No plugin manifest found で止まります。
claude plugin tag --dry-run .✘ Version mismatch: plugin.json says "1.0.0" but ../.claude-plugin/marketplace.json
plugins[0].version says "2.0.0". plugin.json wins at install time, so update the
marketplace entry to "1.0.0" (or remove it) before tagging.バージョンは片方だけで管理します。タグ付けを手順に入れておけば、不一致のまま配ることがなくなります。
リリースチャネルでstable/latestを使い分ける
安定版だけ使いたいチームと、最新を試したいチームに分けて配る設計です。Claude Codeにリリースチャネルという概念は無く、1つのマーケットプレイスは各プラグインの1つのバージョンしか配れません。そこで、同じリポジトリの別のrefを指す2つのマーケットプレイスを用意します。
2つの marketplace.json は、name を別にする必要があります。Claude Codeはマーケットプレイスを name で識別するため、同じ名前を2つ登録できません。
{
"name": "stable-tools",
"owner": { "name": "Acme Corp" },
"plugins": [
{
"name": "code-formatter",
"source": { "source": "github", "repo": "acme-corp/code-formatter", "ref": "stable" }
}
]
}{
"name": "latest-tools",
"owner": { "name": "Acme Corp" },
"plugins": [
{
"name": "code-formatter",
"source": { "source": "github", "repo": "acme-corp/code-formatter", "ref": "latest" }
}
]
}チャネルが成り立つ前提は、2つのrefが別のバージョンに解決されることです。更新は文字列の比較で検知されるので、同じ値に解決されるとrefが動いてもキャッシュのままになります。
チャネルを区別する2つの方法
明示バージョン
stable ブランチの plugin.json は 2.1.0、latest は 2.2.0-beta.1 のように、refごとに値を変えます。
version を省略
plugin.json とエントリの両方から version を外します。refが指すコミットが違えばSHAが違うので、チャネルも自然に別物になります。
グループへの割り当て: 3つの落とし穴
マーケットプレイスを用意したら、ユーザーグループごとに見せる先を決めます。管理コンソールのサーバー管理設定は組織の全員に一律で適用されるため、グループ別の出し分けには使えません。選択肢は2つあります。1つはエンドポイント管理設定(managed settingsファイルやMDMプロファイル)をグループ別に配る方法です。もう1つは、グループごとにClaude appsゲートウェイのポリシーを1つ定義する方法です。
割り当てで踏みやすい3点
サーバー管理設定が1キーでもあると、端末の設定は読まれない
既定では、サーバー管理設定、MDM、managed settingsファイルの順に調べ、最初に値を返した1つだけが使われます。サーバー管理設定が無関係なキーを1つ配っているだけで、MDMやファイルのプラグイン関連キーは無視されます。
managedSourcesBehaviorを"merge"にすると全部を合成します。ゲートウェイは最初に一致したポリシーだけ
マッチ条件に合う最初のポリシーだけが適用されます。各ユーザーが自分のグループのポリシーに先に届くよう、並び順を決めます。
extraKnownMarketplaces はマージされず置き換わる
グループ用ポリシーの
extraKnownMarketplacesは、ほかのポリシーのものとマージされません。全体向けのcatch-allで登録した社内共通ツール用のマーケットプレイスも、グループ側に書かなければ見えなくなります。チャネル用だけでなく、グループが使う全部を毎回書き出します。
managed settings同士でも、同名のマーケットプレイスは優先度の低いほうを置き換え、フィールドは混ざりません。stableグループに配る設定は次のとおりです。
{
"extraKnownMarketplaces": {
"stable-tools": {
"source": { "source": "github", "repo": "acme-corp/stable-tools" }
}
}
}早期アクセスのグループには、stable-tools を latest-tools に替えた同じ形の設定を配ります。managed settingsの構造や配布経路はClaude Code組織管理ガイドにあります。
管理者が割り当てずに、利用者へ選ばせる方法もあります。マーケットプレイスの追加時にrefを付け、your-org/your-marketplace#stable のように追加すると、カタログそのもののブランチやタグを固定できます。
この設計の利点と限界
リリースチャネルの実体は、同じプラグイン名を複数のマーケットプレイスに登録し、見せる相手を設定で分けることだけです。利点は、既存のgit運用をそのまま流用できることです。ref を変えるだけでチャネルが増え、canary を足すのも3つ目のマーケットプレイスを置くだけで済みます。
限界は、チャネルの数だけマーケットプレイス定義と割り当て設定が増えることです。数チームのツールなら管理できますが、数十のプラグインを複数チャネルで配る規模では、marketplace.json とmanaged settingsの整合を保つ手間が重くなります。
バージョンを固定する手段は、チャネルのほかにもあります。エントリの ref と sha、追加コマンドの #<ref>、依存側のバージョン範囲です。
よくある質問
作者は更新を出したのに、利用者側で marketplace not refreshed と出ました
claude plugin install は、name@marketplace の形で指定すると、先にそのマーケットプレイスの最新カタログを取りに行きます。取得に失敗したときはキャッシュ済みのカタログで続行し、marketplace not refreshed と表示します。name だけを指定した claude plugin install は、そもそもカタログを更新しません。
name@marketplace で指定しても、更新を省くケースがあります。ローカルの file や directory から追加した、または設定に settings ソースで直接書いたマーケットプレイスは対象外です。シードディレクトリが供給するもの、30秒以内に更新したばかりのもの、CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC を設定した環境も同じです。管理設定でブロックされたマーケットプレイスは、インストール自体が拒否されます。この更新はマーケットプレイスの自動更新設定や DISABLE_AUTOUPDATER に左右されません。
まとめ
グループごとに追う線を変えたいならチャネル、全員を特定のコミットに留めたいなら sha の固定、という分け方で選べます。数チーム規模を超えるなら、チャネルを増やす前に定義の整合を保つ手間を見積もっておく価値があります。
マーケットプレイス自体の作り方はClaude Codeプラグイン完全ガイドにあります。配布前の検証はマーケットプレイスのstrict modeとvalidate、プラグイン名を変えるときの移行はrenamesマップの解説が扱っています。