Claude Code の Mod は、ユーザーの権限で動くコードです。社内のマシンで使う場合、管理者は「Mod を動かすか」「どれを動かすか」「どの順番で動かすか」を、管理設定(managed settings)で決められます。この記事は、Claude Code の管理設定を配る立場の方向けです。公式ドキュメント(組織向けの Mods 管理)の内容を整理し、方針を検査する Mod の例を、Claude Code 2.1.289 の claude plugin validate と claude plugin test で確認したコードで補います。
先に結論
- 何も設定しなければ、Mod はオンです。ユーザーは、許可されたマーケットプレイスの Mod を入れ、
--plugin-dirで任意のフォルダを読み込めます。 - ユーザーの持ち込みだけを止めるには、内蔵ガード(
cc-plugin-sec-default@builtin)のallowManagedModsOnlyを管理設定でオンにします。ユーザーの設定フックやステータスラインは動き続けます。 - ユーザーは、管理設定のこの値を変えられません。ユーザー、プロジェクト、ローカルの設定ファイルに同じ項目を書いても、効果がありません。
- 自社の Mod を配るには、決められた 3 つの条件を満たす必要があります。条件を外すと、ユーザーの Mod として扱われます。
- 「一部の Mod だけ許可する」方針は、
plugin.registerを扱う検査用の Mod で書きます。
何も設定しないと、どうなるか
公式ドキュメントによると、Mod の設定を何もしていない場合の挙動は次のとおりです。
- Mod はオン:許可されたマーケットプレイスの Mod を入れられ、フォルダの読み込みもできます。
- 内蔵ガードが先に走る:
sec-default@builtinという Claude Code 内蔵の Mod が、ユーザーのどの Mod よりも先に読み込まれます。ユーザーは止められません。ガードが読み込まれるのは、マシンに管理設定がある場合、または Team / Enterprise プランでサインインしているユーザーの場合です。API キー、Amazon Bedrock、Google Cloud の Agent Platform、Microsoft Foundry で認証しているユーザーは、管理設定があるマシンでのみガードが動きます。 - ガードが守るもの:ユーザーの Mod は、管理フックが受け取るものと決めること、システムプロンプト、管理する
CLAUDE.mdなどの指示、管理 MCP サーバーのツールと説明を変えられません。 - それ以外は許可:ファイルの読み書き、プロセスの起動、通信、ツール呼び出しやプロンプトの書き換え、ツール呼び出しの拒否、確認が出るはずの呼び出しの許可、画面の描画は、すべてそのユーザーの権限で可能です。
denyルールと管理フックは優先:ガードが読み込まれている環境では、ユーザーの Mod は、denyルールで拒否される呼び出しを許可できません。管理設定のPreToolUseフックの遮断も最終です。ただし、これは Claude のツール呼び出しに対してです。Mod 自身の$.fsや$.processの呼び出しには適用されません。Read(.env)を拒否していても、Mod は$.fs.readでそのファイルを読めます。
ポリシーの選び方
| やりたいこと | 設定 |
|---|---|
| インストールした Mod はすべて動かさない(設定フックはそのまま) | allowManagedModsOnly をオンにし、自社の Mod は配らない |
| インストールした Mod も、管理フックを含むすべてのフックも止める | disableAllHooks を true にする |
| 自社の Mod だけ動かす | allowManagedModsOnly をオンにし、自社の Mod を条件に合わせて配る |
| 承認したマーケットプレイスの Mod は許可する | マーケットプレイスの制限を維持し、disableSideloadFlags を true にする |
| どの Mod も許可し、自社の Mod で他の Mod を検査する | 自社の Mod を配り、prependPlugins に sec-default@builtin と並べる |
設定ごとの違いは次のとおりです。
allowManagedModsOnly:内蔵ガードのオプションです。ユーザーの Mod のフックがすべて動かなくなります。ユーザーの設定フック、ステータスライン、/goalは動き続けます。allowManagedHooksOnly:もっと広い設定です。自社の Mod と Claude Code 内蔵の Mod だけが読み込まれ、ユーザーの設定ファイルのフックも止まります。設定する前に、公式の設定リファレンスで、何が動き続けるかを確認してください。disableAllHooks:最も広い設定です。管理設定に書くと、自社の Mod を含めてすべてのインストール済みプラグインの Mod が止まり、管理設定のPreToolUseフックも何も止めなくなります。ステータスラインと/goalも止まります。disableSideloadFlags:--plugin-dirと--plugin-urlを起動時に拒否し、Claude が書いた Mod の読み込みも止めます。--agentsと--mcp-configも拒否されます。
ユーザーの持ち込みを止める設定
managed-settings.json に、次のように書きます。キーは pluginConfigs の下の cc-plugin-sec-default@builtin です。
{
"pluginConfigs": {
"cc-plugin-sec-default@builtin": {
"options": {
"allowManagedModsOnly": true
}
}
}
}この設定で、次のことが起きます。
- ユーザーのインストールしたプラグインの Mod、
--plugin-dirで読み込んだ Mod、セッション中に Claude が書いた Mod のフックは、すべて動かなくなります。 - GitHub などのリモートのマーケットプレイスから有効にしたプラグインも、ユーザーの Mod として扱われ、拒否されます。claude.ai で組織がメンバーに有効にしたものも同じです。
- ユーザーは取り消せません。ガードがこの値を読むのは、管理設定からだけです。
- ファイルや MDM で配った場合は、Amazon Bedrock などのプロバイダーを使っていても同じように効きます。claude.ai の管理コンソールから配る場合は、公式の対応状況の表を確認してください。
効いているかは、ユーザーのマシンで claude --plugin-dir ./任意のMod を実行して確かめます。Mod のフックは動かず、トランスクリプトとデバッグログに、Mod の名前と allowManagedModsOnly を含むガードのメッセージが出ます。メッセージの意味は Mod が動かないときの確認手順 にあります。
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS は、v2.1.287 以降では値にかかわらず無視されます。早期アクセスのときに 0 を設定していたなら、Mod は止まっていません。この項目の allowManagedModsOnly に置き換えてください。
自社の Mod を配る:3 つの条件
Claude Code が「組織の Mod」だと認めるのは、次の 3 つがすべて満たされたときだけです。
- 管理設定の
enabledPluginsで、そのプラグインがtrueになっている。 - 管理設定が、そのプラグインのマーケットプレイスを、ユーザーのマシン上のディレクトリとして、絶対パスで指している(
extraKnownMarketplacesがこれをします)。 - マーケットプレイスが、そのプラグインを相対パスで載せている。Claude Code がそのディレクトリからそのまま読み込むため。
デバイス管理で、マーケットプレイスのディレクトリを全マシンの同じパスにコピーしてください。そのディレクトリと、上位のディレクトリは、管理設定のファイルと同じく、管理者だけが書き込める状態にします。書き込める人は、あなたの Mod を書き換えられるからです。claude.ai の管理コンソールで配った管理設定にもキーは入れられますが、ディレクトリをマシンに置くことはできません。
/opt/example/claude-plugins/
├── .claude-plugin/
│ └── marketplace.json
└── plugins/
└── org-guard/
├── .claude-plugin/
│ └── plugin.json
└── hooks/
├── hooks.json
└── register.jsmarketplace.json は、プラグインを相対パスで載せます。
{
"name": "example-tools",
"owner": { "name": "Example" },
"plugins": [
{ "name": "org-guard", "source": "./plugins/org-guard", "description": "Example policy mod" }
]
}管理設定は、マーケットプレイスの登録、有効化、実行順序を指定します。
{
"extraKnownMarketplaces": {
"example-tools": {
"source": { "source": "directory", "path": "/opt/example/claude-plugins" }
}
},
"enabledPlugins": { "org-guard@example-tools": true },
"prependPlugins": ["org-guard@example-tools", "sec-default@builtin"]
}注意点が 3 つあります。
- GitHub、git、URL、npm から Claude Code がキャッシュにコピーするプラグインは、
enabledPluginsで有効にしても、ユーザーの Mod として扱われます。prependPluginsはそれを読み飛ばし、allowManagedModsOnlyは拒否します。 prependPluginsを管理設定で設定すると、デフォルトの並びが置き換えられます。内蔵ガードを残すには、リストにsec-default@builtinを書いてください。prependPluginsとappendPluginsは、管理設定からだけ読まれます。リポジトリの設定ファイルや、--settingsで渡したファイルは効きません。
Mod が走る順番
同じイベントに対するフックは、1 本の連なり(ミドルウェア)になります。最初の Mod が一番外側で、イベントを先に見て、結果を最後に見ます。後の Mod は、先の Mod がイベントを見るのを止められません。順番は、出どころで次のように決まります。
- 内蔵ガード、
prependPluginsに書いた組織の Mod、appendPluginsにない組織の Mod - ユーザーがインストールした Mod
appendPluginsに書いた組織の Mod- そのほかの内蔵 Mod
管理設定の PreToolUse の設定フックは、どの Mod の tool.call よりも前に動き、その遮断は最終です。ほかの設定ファイルやプラグインの PreToolUse フックは、最後の Mod が next を呼んだ後に動きます。
方針を検査する Mod を書く
「ユーザーの Mod は全部止める」だけなら、Mod は要りません。「一部は許し、一部は断る」「何が起きたか記録する」ときに、検査用の Mod を書きます。ほかの Mod が読み込まれる直前に、plugin.register というイベントが届き、claude plugin validate が出す一覧と同じ内容が e.uses に入っています。
次の Mod は、環境変数や設定を読み、同時に通信やプログラム起動もするユーザーの Modを、読み込ませません。「読んだ値を外へ出せる組み合わせ」の例です。これは例であり、推奨する方針ではありません。自分の組織の事情に合わせて決めてください。
// 読んだ値を外へ出せる組み合わせ。読む側と、出す側
const READS = ['env.get', 'settings.read']
const SENDS = ['http.fetch', 'process.run', 'process.spawn']
// 判定は関数に出しておく。.catch から同じ形で使うため
async function checkMod($, e, next) {
if (e.tier === 'user') {
const reads = e.uses.calls.filter((call) => READS.includes(call))
const sends = e.uses.calls.filter((call) => SENDS.includes(call))
if (reads.length > 0 && sends.length > 0) {
// refuse を返すと、その Mod は読み込まれない。文字列が理由になる
return { refuse: '環境変数や設定を読む Mod は、通信やプログラム起動と併用できません: ' + reads.join(', ') + ' と ' + sends.join(', ') }
}
}
return next(e)
}
export function register(on) {
on('plugin.register', checkMod).catch(async ($, e, next) => {
// 組織の Mod と内蔵の Mod は通す
if (e.tier !== 'user') return next(e)
// 検査できなかったユーザーの Mod は、読み込まない(fail closed)
return { refuse: '方針の検査に失敗したため、この Mod は読み込まれません' }
})
}ポイントは次のとおりです。
e.tierは、その Mod がどこで走るかを示す値です(prepend、user、append、builtin)。人がインストールした Mod は、すべてuserです。e.uses.callsには、Mod が呼ぶ Mods API が、$.なしの名前空間.メソッドで入ります(http.fetchのように)。.catchを付けると、検査自体が失敗したときに、ユーザーの Mod を読み込ませない側に倒せます。付けない場合、検査のフックが例外を投げたり時間を超えたりすると、Claude Code はそのフックを飛ばし、検査していた Mod がそのまま読み込まれます。- 特定の呼び出しだけを止めたいときは、その呼び出しの名前のフック(
fs.writeなど)で{ deny: '理由' }を返します。
このコードは、claude plugin validate で hooks: plugin.register が読まれることを確かめ、claude plugin test で次の 2 つの動作を確認しました。
env.getとhttp.fetchを呼ぶ Mod は、leaky: refused by org-guard: …のメッセージで読み込まれない。env.getだけを呼ぶ Mod は、読み込まれて動く。
テストの書き方は Mod のテストの書き方 で説明します。検査用の Mod のテストでは、ファイルの先頭で tier('prepend') を呼び、test の第 2 引数の plugins に、検査される側の Mod を書きます。
限界を知っておく
- 検査用の Mod は、インストールされる Mod の呼び出しの種類しか見られません。通信先やコマンドの中身までは分かりません。
- 内蔵でない Mod が動くワーカーが 3 回止まると、Claude Code は内蔵でない Mod をすべて外します。あなたの Mod も外れます。ユーザーが
/reload-pluginsを実行するか、新しいセッションを始めるまで続きます。 - ユーザーが
claude --safe-modeで起動すると、組織のものを含め、インストールした Mod なしで動きます。 - Mod は、どの設定でもサンドボックスの中では動きません。
まとめ
迷ったら、まず allowManagedModsOnly で持ち込みを止め、必要な Mod だけを自社の配布物として入れるのが、最も単純です。ユーザーに自由を残すなら、内蔵ガードと検査用の Mod を組み合わせます。審査の観点は、他人の Mod を入れる前の安全チェック にまとめました。