Claude CodeのプラグインマーケットプレイスをURLで配る落とし穴
marketplace.jsonをURLで配るとき、相対パスsourceが落ちる理由、5MiB・256MiBの上限、Git LFSがポインタファイルになる点を、配布前の確認手順にまとめます。
自社サーバーに置いたmarketplace.jsonのURLを /plugin marketplace add で配る方法は、git不要で手軽です。ただし、ダウンロードされるのはそのファイルだけです。ここを見落とすと、追加には成功するのにインストールで落ちます。
この記事では、URL配布で踏みやすい3点を扱います。相対パスのsourceが使えないこと、marketplace.jsonとzipのダウンロード上限、Git LFSのファイルがポインタファイルのまま届くことです。
URL配布は「ファイル1枚」を配る方式
Claude Codeのプラグインのマーケットプレイスは、ホスト先ごとに利用者が打つコマンドと必要なものが違います。
| ホスト | 利用者が打つコマンド | 利用者側の前提 |
|---|---|---|
| GitHub | 利用者が打つコマンド/plugin marketplace add your-org/your-marketplace | 利用者側の前提git |
| GitLabなど別のgitホスト | 利用者が打つコマンド/plugin marketplace add https://gitlab.example.com/team/plugins.git | 利用者側の前提gitとホストへのアクセス |
marketplace.jsonのURL | 利用者が打つコマンド/plugin marketplace add https://plugins.example.com/marketplace.json | 利用者側の前提URLへのHTTPSアクセス |
| 共有フォルダ | 利用者が打つコマンド/plugin marketplace add /Volumes/shared/claude-plugins | 利用者側の前提パスの読み取り権限 |
URL配布の利点は、カタログ自体の取得にgitが要らないことです。裏返すと、gitのようにリポジトリ全体は取得されません。以降の3つの落とし穴は、すべてこの一点から来ています。
追加が成功すると Successfully added marketplace: your-marketplace と表示されます。この名前はリポジトリ名やURLではなく、marketplace.jsonのnameフィールドから取られます。
落とし穴1: 相対パスのsourceはインストールで失敗する
URLで追加したマーケットプレイスでは、plugins配列のsourceに./plugins/formatterのような相対パスを書いたエントリが、インストール時に次のエラーで落ちます。
Cannot install formatter@your-marketplace: its marketplace entry path does not stay inside the marketplace directoryエラー文そのものから原因を切り分けたいときは、marketplace entry pathが拒否される原因が詳しく扱っています。ここではURL配布をする側の設計に絞ります。
すでにインストール済みのプラグインは、読み込み時にPlugin source path refusedで失敗します。どちらも同じ原因です。相対パスは「マーケットプレイスのファイル一式を手元に持っている」ことが前提で、URL配布ではそのディレクトリが取得されていません。
相対パスが解決できるかは、マーケットプレイスのソース種別で決まります。
github/git/file/directory: ファイル一式があるので解決できるurl:marketplace.jsonしか取得しないので解決できないsettings: 相対パスは受け付けない
直し方は2つあります。
相対パスsourceの直し方
エントリ側を直す
各エントリに、単体で取得できるsourceを書きます。githubリポジトリか、archiveのURLが候補です。
ホスト側を変える
マーケットプレイスをgitリポジトリに置き、リポジトリのURLで追加してもらいます。リポジトリ全体がcloneされるので相対パスが通ります。
エントリ側を直す場合の最小形は次のとおりです。
{ "name": "my-plugin", "source": { "source": "github", "repo": "owner/repo" } }sourceの種類ごとの使い分けはプラグインソースの使い分けにまとめています。
落とし穴2: ダウンロードには上限と時間制限がある
URLで追加したmarketplace.jsonと、archiveソースのzipは、Claude Codeがサーバーから直接ダウンロードします。次の上限を超えるとダウンロードが失敗します。
| 対象 | 最大サイズ | サーバーの応答待ち | リダイレクト |
|---|---|---|---|
urlソースのmarketplace.json | 最大サイズ5 MiB | サーバーの応答待ち10秒 | リダイレクト別オリジンへはhttps://のみ |
archiveソースのzip | 最大サイズ256 MiB | サーバーの応答待ち120秒 | リダイレクト最大5回、すべてhttps:// |
リダイレクト先には、ループバック・リンクローカル・クラウドのメタデータ用ホストを指せないという制限もあります。そのためhttps://からhttp://へのリダイレクトは失敗します。CDNや社内ゲートウェイの前段で、HTTPに落とす設定になっていないか確認します。
認証ヘッダーを使う場合の見落としもあります。別オリジンへリダイレクトされたリクエストには、マーケットプレイスのソースやプラグインエントリに設定したheadersが付きません。署名付きURLへ飛ばす構成では、リダイレクト後に認証が外れる前提で設計します。
headersは、マーケットプレイスのurlソース(extraKnownMarketplacesのエントリなど)か、v2.1.238以降ならプラグインのエントリに書けます。短命なトークンはheadersHelperのコマンドで渡します。エントリ側にheadersHelperを置くときは、"strict": falseも必要です。
{
"name": "my-plugin",
"strict": false,
"source": {
"source": "archive",
"url": "https://registry.example.com/plugins/my-plugin-2.1.0.zip"
},
"headersHelper": "/opt/bin/mint-registry-token.sh"
}コマンドは10秒以内にヘッダー名と文字列値のJSONオブジェクトを標準出力へ出して終了する必要があります。失敗するとダウンロード自体が行われません。
コマンド本文は500文字以内の印字可能なASCIIで、sh経由で実行されます。作業ディレクトリは~/.claude(またはCLAUDE_CONFIG_DIR)なので、相対パスではなく絶対パスで書きます。marketplace.jsonのエントリやプロジェクトの.claude/settings.jsonに書いたコマンドでは、認証情報らしい名前の環境変数が取り除かれます。トークンは環境変数ではなく、ファイルか資格情報ストアから読ませます。
ヘッダーには、認証用の名前ならAuthorizationが残ります。一方でHostやCookie、X-Forwarded-*のような経路・クライアント識別用の名前は取り除かれます。マーケットプレイスのURLがhttps://で始まらない場合は、urlソースのheadersHelperが実行されず、headersに列挙した値だけが送られます。社内向けにhttp://で試験配布しているときは、この点で認証が効かなくなります。
zipには展開後の上限もある
ダウンロードに成功しても、展開後に次のどれかを超えるとインストールが失敗します。
- エントリ数: 10万(ファイルとディレクトリの合計)
- 1ファイル: 展開後512 MiB
- 合計: 展開後1 GiB
- 圧縮率: zipの50倍
ダウンロード上限は256 MiBでも、展開後は別の制限が先に当たることがあります。小さなファイルを大量に含むzipは、エントリ数の上限に注意します。
配布前にサーバーの応答を測る
手元で次のように測ると、サイズ・所要時間・リダイレクト回数を一度に見られます。
curl -sL --max-redirs 5 -o /dev/null \
-w 'size=%{size_download} time=%{time_total}s redirects=%{num_redirects}\n' \
https://plugins.example.com/marketplace.jsonmarketplace.jsonなら size が5 MiB(5,242,880バイト)未満、time が10秒未満であることを目安にします。zipなら256 MiBと120秒が基準です。サーバーの応答待ちには時間制限があるので、初回アクセスが遅い構成は計測で確かめます。
zipの完全性検査で落ちる場合は、上限とは別の原因です。切り分けはarchive integrity checkの対処、zipの作り方とsha256の固定はzip配布の手順で扱っています。
落とし穴3: Git LFSのファイルはポインタのまま届く
gitでホストするマーケットプレイスや、gitベースのプラグインは、利用者のマシンにcloneされます。このcloneはLFSの中身をダウンロードしません。LFSで管理しているファイルは、実体ではなくポインタファイルとして届きます。
プラグインが同梱するスクリプト、バイナリ、画像、MCPサーバーの実行ファイルなどをLFSで管理していると、インストール自体は成功しても動かない、という症状になります。githubやgitのマーケットプレイスソースにはskipLfsというフィールドがありますが、受け付けられるだけで効果はありません。
対処は、プラグインに必要なファイルをLFSの管理対象から外すことです。手元で対象を洗い出す手順の例です。
git lfs ls-files # LFSで管理されているファイル
git check-attr -a -- path/to/plugin/bin/tool # filter=lfs が付くか大きなバイナリをどうしても置きたい場合は、gitではなくarchiveソースのzipに入れる選択肢があります。zipは256 MiBまで受け付けられます。
チーム全員に自動で登録するには
URLを全員に伝えて打ってもらう代わりに、リポジトリ側へ登録を埋め込めます。そのリポジトリでclaude plugin marketplace addを--scope project付きで一度実行し、書き出された.claude/settings.jsonをコミットします。フォルダを信頼したメンバーに、Claude Codeがマーケットプレイスを登録します。設定キーの書き方はextraKnownMarketplacesの記事にあります。
症状から原因を絞る
| 症状 | 疑う先 |
|---|---|
marketplace addは成功し、installで失敗する | 疑う先相対パスのsource |
| 追加やインストール中にダウンロードが失敗する | 疑う先サイズ・応答時間の上限、http://へのリダイレクト |
| インストールは成功したが、スクリプトやバイナリが動かない | 疑う先LFSのポインタファイル |
配布前チェックリスト
URL配布を始める前に、次の順で確認します。
URL配布の事前確認
- 1
sourceを洗い出す
plugins[].sourceに./で始まる文字列がないか調べます。あればgithubやarchiveに置き換えます。 - 2
marketplace.jsonを測る
上のcurlで5 MiB・10秒に収まるか確認します。
- 3
zipを測る
archiveを使うなら、256 MiB・120秒・リダイレクト5回に加え、展開後の4つの上限も見ます。 - 4
LFSを確認する
git lfs ls-filesの出力にプラグイン用のファイルがないことを確かめます。 - 5
別の端末で追加してみる
開発環境の外から
/plugin marketplace addと/plugin installを通します。ローカルの共有フォルダでは相対パスが通るため、URL配布の問題はそこでは再現しません。
よくある質問
部署ごとに見せるプラグインを変えられますか
エントリに対象ユーザーを指定するフィールドはなく、マーケットプレイスを追加した全員がカタログ全体を見ます。部署ごとに分けたい場合は、マーケットプレイスを別々に用意します。
まとめ
大きなバイナリを含むプラグインが1つでもあるなら、カタログはURLで配り、中身はarchiveのzipに分ける構成が、3つの落とし穴を一度に避けられます。相対パスは使わずに済み、LFSも通らず、サイズ上限はzipの256 MiBまで広がります。残る確認は、zipの展開後の上限と、リダイレクト後に認証が外れる点です。