Claude CodeのprependPluginsとappendPluginsでmodの順序を固定
prependPluginsとappendPluginsは、組織のmodをユーザー導入modの前後に走らせる管理設定です。書き方、効く条件、sec-default@builtinの残し方、pluginTrustMessageまで。
prependPluginsとappendPluginsは何を決めるのか
prependPluginsとappendPluginsは、組織が配布したプラグインのmodを、ユーザーが入れたmodの前に走らせるか後に走らせるかを決める設定キーです。modはClaude Code上でコードを動かすプラグインで、導入したユーザーの権限で動きます。サンドボックスはありません。
2つのキーの役割は次のとおりです。
prependPlugins: 列挙したmodを、ユーザーのmodより前に、書いた順で走らせるappendPlugins: 列挙したmodを、ユーザーのmodより後に、書いた順で走らせる
どちらも値はplugin-name@marketplace-name形式の文字列の配列で、既定は未設定です。管理設定のほか、一定の条件下ではユーザー設定でも効きます(後述)。
modの作り方はClaude Codeのmodを80行で作るにまとめています。ここでは配る側、つまり管理者が順序をどう決めるかに絞ります。
modはどの順で動くのか
Claude Codeは、ツール実行などの操作の直前にイベントを起こし、modを1つずつ通して渡します。同じイベントに掛けたフックは1本のチェーンになり、最初のmodが最も外側です。外側のmodはイベントを他より先に見て、結果も他より後に見ます。後ろのmodが、前のmodにイベントを見せないようにすることはできません。
並びは、modの出どころで次の4段に決まります。
modのチェーンが並ぶ順序
- 1
組み込みガードと組織のmod
sec-default@builtin、prependPluginsに書いたmod、appendPluginsにない組織のmodが入ります。 - 2
ユーザーが導入したmod
ユーザー自身が入れたmodです。依存関係を宣言している場合、依存元が依存先より先に走ります。
- 3
appendPluginsのmod
組織が後ろに回したmodです。ユーザーのmodが通したイベントだけを、通された形で見ます。
- 4
Claude Code組み込みのその他のmod
最後に、Claude Codeに組み込まれた別のmodが走ります。
押さえておきたいのは、prependPluginsに何も書かなくても、組織のmodはユーザーのmodより前に走る点です。キーの役目は「前に走らせる」ことではなく、前の中での位置を指定することにあります。
prependPluginsのmod: すべてのイベントを最初に見て、結果を最後に受け取る。イベントを書き換えても、拒否しても、ユーザーのmodを飛ばしてもよいappendPluginsのmod: ユーザーのmodが通した後のイベントしか見えない。監査ログのように、実際に起きたことを記録する用途に向く
管理設定の書き方
ユーザーのマシンに組織のmodを届け、先頭に置く最小の例です。
{
"extraKnownMarketplaces": {
"acme-tools": {
"source": { "source": "directory", "path": "/opt/acme/claude-plugins" }
}
},
"enabledPlugins": { "acme-guard@acme-tools": true },
"prependPlugins": ["acme-guard@acme-tools", "sec-default@builtin"]
}3つのキーが1つずつ役割を持っています。
| キー | 役割 |
|---|---|
extraKnownMarketplaces | 役割acme-toolsマーケットプレイスを、マシン上のディレクトリとして宣言する |
enabledPlugins | 役割acme-guardを、この設定を受け取るすべてのユーザーで有効にする |
prependPlugins | 役割acme-guardを先頭、組み込みガードを2番目に置く |
後ろに回したいmodは、同じ形でappendPluginsに書きます。
{
"extraKnownMarketplaces": {
"acme-tools": {
"source": { "source": "directory", "path": "/opt/acme/claude-plugins" }
}
},
"enabledPlugins": { "acme-audit@acme-tools": true },
"appendPlugins": ["acme-audit@acme-tools"]
}「組織のmod」と認められる3条件
IDを書いても、組織のmodとして扱われなければ無視されます。管理設定の中では、次の3条件をすべて満たすプラグインだけが組織のmodです。
- 管理設定の
enabledPluginsでそのプラグインがtrue - 管理設定が、そのマーケットプレイスをユーザーのマシン上のディレクトリとして、絶対パスで指している
- マーケットプレイスがそのプラグインを相対パスで載せていて、Claude Codeがそのディレクトリからその場で読み込める
ここが最大のつまずきどころです。GitHub、git、URL、npmのいずれかから取得し、Claude Codeがキャッシュにコピーしたプラグインは、管理設定で有効にしてあってもユーザーのmod扱いになります。この場合、prependPluginsとappendPluginsはそのIDを読み飛ばします。allowManagedModsOnlyはそのmodを拒否し、allowManagedHooksOnlyはそのmodのフックを読み込ませません。
つまり、組織のmodを配るには、MDMなどでマーケットプレイスのディレクトリを全マシンの同じパスへコピーする必要があります。ディレクトリと、その上位のディレクトリは、管理設定ファイルと同じく管理者だけが書ける状態にします。書き込める人は誰でもmodを書き換えられるからです。claude.aiの管理コンソールから配る管理設定にもキーは載せられますが、ディレクトリそのものは届きません。
両方に書いたらどうなるか
同じIDをprependPluginsとappendPluginsの両方に書くと、prepend側が優先されます。後ろに回したつもりのmodが先頭側に入るので、片方に寄せて書くのが安全です。
リストは既定を置き換えます。この点は次の節で扱います。
sec-default@builtinを残す書き方
Claude Codeは、ユーザーのmodより前にsec-default@builtinという組み込みのガードを読み込みます。/pluginとデバッグログではcc-plugin-sec-defaultと表示され、ユーザーは止められません。ガードが読み込まれるのは、マシンに管理設定がある場合か、ユーザーがTeamまたはEnterpriseプランでサインインしている場合です。APIキーやAmazon Bedrock、Google CloudのAgent Platform、Microsoft Foundryで認証するユーザーは、管理設定のあるマシンでだけガードが読み込まれます。このため、これらの認証方式のユーザーにもガードを効かせたいなら、管理設定をマシンへ配ることが前提になります。
管理設定でprependPluginsを設定すると、リストが既定を置き換えます。組み込みガードを残すには、リストの中にsec-default@builtinを自分で書きます。このIDは組み込みなので、enabledPluginsに書く必要はありません。
先ほどの例が["acme-guard@acme-tools", "sec-default@builtin"]と書いてあるのは、このためです。書き忘れると、ガードが読み込まれない構成になりえます。ガードのオプション(allowManagedModsOnlyとallowModsToOverrideDenyRules)は、ガードが読み込まれている場合にだけ効きます。
注意が1つあります。pluginConfigsでガードのオプションを指定するときのIDはcc-plugin-sec-default@builtinで、prependPluginsのsec-default@builtinとは書き方が違います。
設定が効く条件
効く場所はキーによらず共通です。
- 管理設定: 読まれる
- ユーザー設定(
~/.claude/settings.json): 管理設定のないマシンで、かつTeamまたはEnterpriseプランでサインインしていないユーザーのときだけ読まれる - プロジェクト設定、ローカル設定、
--settingsで渡すファイル: 無視される
リポジトリ側の設定ファイルから順序を差し込まれることはありません。ユーザー設定で効く場面は、個人が自分のmod同士の順序を整えたいときに限られます。管理設定のあるマシンやTeam・Enterpriseのユーザーでは、ユーザー設定に書いても無視され、組み込みガードが増えることも減ることもありません。
順序の隣にあるガードのオプションとメッセージ
prependPluginsにガードを残す理由は、ガードがオプションの置き場でもあるからです。オプションは管理設定のpluginConfigsに、cc-plugin-sec-default@builtinをキーとして書きます。
| オプション | 未設定のとき | trueのとき |
|---|---|---|
allowManagedModsOnly | 未設定のときユーザーのmodが動く | trueのとき組織のmodと組み込みmodだけがフックを動かす。ユーザーが入れたmodも--plugin-dirで指したmodも拒否される |
allowModsToOverrideDenyRules | 未設定のときdenyルールがユーザーのmodより優先される | trueのときユーザーのmodが、denyルールで拒否される呼び出しを承認できる |
{
"pluginConfigs": {
"cc-plugin-sec-default@builtin": {
"options": { "allowManagedModsOnly": true }
}
}
}オプションは管理設定からしか読まれません。ユーザー、プロジェクト、ローカルの各設定や--settingsのファイルに同じ項目を書いても、設定にも緩和にもなりません。ガードが管理設定を読めないときは、安全側に倒れてユーザーのmodをすべて拒否します。
ユーザーの側には、ガードが働いたことがメッセージで伝わります。mods are limited to your organization's by policy (allowManagedModsOnly)は、組織が自前のmodだけを許していて、ユーザーのmodが拒否されたという意味です。tried to lift a deny rule in your settingsは、modが承認しようとした呼び出しがdenyルールで拒否されたままである、という意味になります。問い合わせを受けたときの切り分けに使えます。
効いているかをデバッグログで確かめる
ユーザーのマシンでclaude --debugを起動し、modのIDでデバッグログを検索します。
claude --debug見るべき行は2種類です。
hooks module acme-guard@acme-tools loadedにtier prependが付いている: 組織のmodとして認められ、先頭側で動いている- 同じ行に
tier userが付き、prependPlugins names acme-guard@acme-tools, which is not an enabled managed plugin with a hooks module; skippedという行が続く: ユーザーのmod扱いで、リストから読み飛ばされた
後者が出たら、前の節の3条件を順に疑います。多いのは、マーケットプレイスをGitHubなどのリモート指定にしていて、キャッシュへのコピーになっているケースです。
pluginTrustMessageで導入前の警告に社内の文言を足す
順序とは別のキーですが、同じ組織配布の設定としてpluginTrustMessageがあります。Claude Codeはプラグインの導入前に信頼の警告を出します。このキーは、その警告に組織独自の文言を足します。社内マーケットプレイスのプラグインは審査済みだと伝える、といった使い方が公式の例です。
- スコープ: 管理設定のみ
- 型: 文字列
- 既定: 未設定。標準の警告だけが出る
{
"pluginTrustMessage": "All plugins from our marketplace are approved by IT"
}標準の警告は消えず、その後ろに文言が加わります。ユーザーのマシンで導入前に何が表示されるかは、実際にプラグインを入れて確かめるのが確実です。
まとめ
順序を固定したいときは、まず組織のmodが3条件を満たしているかを確かめます。次に、前に置くものをprependPlugins、後ろに置くものをappendPluginsへ分けて書きます。prependPluginsを書いたらsec-default@builtinを入れ忘れないこと。最後にclaude --debugでtier prependを見れば、配った構成が意図どおりに読まれているかが分かります。
ユーザーのmodそのものを締め出したいなら、ガードのallowManagedModsOnlyが別に用意されています。skillsやhooksをプラグインに絞る設定はstrictPluginOnlyCustomizationの記事に、modの導入が始まった経緯はClaude Code v2.1.287のリリースノートにあります。管理設定で許可ドメインを固定する書き方はallowManagedDomainsOnlyの記事が参考になります。