Claude Media
Claude CodeのプラグインマーケットプレイスをURLで配る落とし穴

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の直し方

URL配布のまま

エントリ側を直す

各エントリに、単体で取得できるsourceを書きます。githubリポジトリか、archiveのURLが候補です。

git配布へ

ホスト側を変える

マーケットプレイスを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.json

marketplace.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. 1

    sourceを洗い出す

    plugins[].sourceに./で始まる文字列がないか調べます。あればgithubやarchiveに置き換えます。

  2. 2

    marketplace.jsonを測る

    上のcurlで5 MiB・10秒に収まるか確認します。

  3. 3

    zipを測る

    archiveを使うなら、256 MiB・120秒・リダイレクト5回に加え、展開後の4つの上限も見ます。

  4. 4

    LFSを確認する

    git lfs ls-filesの出力にプラグイン用のファイルがないことを確かめます。

  5. 5

    別の端末で追加してみる

    開発環境の外から /plugin marketplace add と /plugin install を通します。ローカルの共有フォルダでは相対パスが通るため、URL配布の問題はそこでは再現しません。

よくある質問

部署ごとに見せるプラグインを変えられますか

エントリに対象ユーザーを指定するフィールドはなく、マーケットプレイスを追加した全員がカタログ全体を見ます。部署ごとに分けたい場合は、マーケットプレイスを別々に用意します。

まとめ

大きなバイナリを含むプラグインが1つでもあるなら、カタログはURLで配り、中身はarchiveのzipに分ける構成が、3つの落とし穴を一度に避けられます。相対パスは使わずに済み、LFSも通らず、サイズ上限はzipの256 MiBまで広がります。残る確認は、zipの展開後の上限と、リダイレクト後に認証が外れる点です。

この記事を共有:XはてブLinkedIn