monitors.jsonでプラグインにバックグラウンド監視を組み込む
プラグインのmonitors.jsonでログ監視やステータス監視を自動起動する設定方法と、claude plugin validateで検査される範囲、whenトリガー、Monitorツールとの違いを解説します。
monitors.jsonでできること
プラグインのmonitors/monitors.jsonに監視対象を書いておくと、そのプラグインが有効な間、Claude Codeが自動でバックグラウンド監視を起動します。ユーザーが毎回「このログを見ていて」と頼む必要がありません。
対象はログファイルの追跡、外部APIのステータスポーリング、ファイル変更の監視など、標準出力に1行ずつ書き出せるものなら何でも使えます。監視コマンドが出力した各行はセッション中の通知としてClaudeに届き、エラーや状態変化にその場で反応できます。
導入した全員に同じ監視を行き渡らせたいプラグインでは、この仕組みが頼み忘れを防いでくれます。一方で、監視を定義するファイルは書き間違えても気づきにくい作りです。後半で、v2.1.289のclaude plugin validateが何を検査し、何を素通りさせるかを実際に動かして示します。
最小構成の書き方
プラグインルートにmonitors/monitors.jsonを置き、監視対象を配列で並べます。
[
{
"name": "error-log",
"command": "tail -F ./logs/error.log",
"description": "Application error log"
}
]必須フィールドは3つだけです。
| フィールド | 役割 |
|---|---|
name | 役割プラグイン内で一意な識別子 |
command | 役割セッションの作業ディレクトリで常駐実行するシェルコマンド |
description | 役割何を監視しているかの短い説明。タスクパネルと通知の要約に表示される |
各エントリは厳密なオブジェクトで、表に載っていないキーを書くとプラグインが読み込まれません。plugin.jsonのexperimental.monitorsにこの配列をそのまま書けばインラインでも指定でき、"./config/monitors.json"のような相対パス文字列を渡せば置き場所も変えられます。
トップレベルのmonitorsキーは今も読み込まれますが、claude plugin validateが警告を出します。monitorsは仕様が固まりきっていない実験的コンポーネントで、将来のリリースでスキーマが変わる可能性があります。
起動タイミングをwhenで絞る
すべての監視を常時起動する必要はありません。whenフィールドで起動条件を選べます。
| 値 | 挙動 |
|---|---|
"always"(既定) | 挙動セッション開始時とプラグインのリロード時に起動する |
"on-skill-invoke:<skill-name>" | 挙動同じプラグイン内の指定スキルが最初に呼ばれたタイミングで起動する |
常時起動と条件起動は1つのファイル内で混在させられます。
[
{
"name": "deploy-status",
"command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/poll-deploy.sh",
"description": "Deployment status changes"
},
{
"name": "error-log",
"command": "tail -F ./logs/error.log",
"description": "Application error log",
"when": "on-skill-invoke:debug"
}
]deploy-statusは${CLAUDE_PLUGIN_ROOT}配下のスクリプトを常時実行し、デプロイの状態変化をセッション開始から拾い続けます。error-logはdebugスキルを最初に呼んだときだけ起動します。
commandで使えるパス変数
commandの値には${CLAUDE_PLUGIN_ROOT}(プラグイン自身のディレクトリ)、${CLAUDE_PLUGIN_DATA}(永続データ領域)、${CLAUDE_PROJECT_DIR}に加えて、環境変数由来の${ENV_VAR}も展開できます。これらはコマンド文字列の中で置換されるだけで、monitorのプロセスの環境変数としては渡されません。
プラグイン自身のディレクトリを起点にしたいときは、コマンドの先頭にcd "${CLAUDE_PLUGIN_ROOT}" && を付けます。
user_configを参照できない制約
monitorsのcommandは${user_config.*}の値を参照できません。コマンドはシェル経由で実行されるため、設定値をそのまま埋め込むと、値に含まれる$(...)やバッククォート、;がコードとして実行されてしまうからです。
参照するとClaude Codeは次のエラーを返し、その監視を起動しません。
Monitor "<name>" from plugin <plugin> references ${user_config.*} in its commandこの検査はコマンドのテンプレートに対して走るため、設定値がまだ入っていない段階でもエラーになります。v2.1.206以前は値がそのままシェルコマンドへ埋め込まれていました。
APIトークンのような設定値を監視スクリプトに渡したいときは、スクリプト側で自分が持つ設定ファイルを読ませます。monitorプロセスにはCLAUDE_PLUGIN_OPTION_<KEY>環境変数も渡らないため、hookやMCPサーバー設定と同じ感覚で書くと動きません。
claude plugin validateは何を検査するか
書き間違いを手元で先に見つけたくなりますが、claude plugin validateの検査範囲はmonitorsの書き方によって変わります。v2.1.289で、一時ディレクトリに置いたプラグインをclaude plugin validate .にかけた結果は次のとおりです。
| monitorsの書き方 | 書き間違いの内容 | 結果 |
|---|---|---|
monitors/monitors.json(既定のファイル) | 書き間違いの内容表にないキーrestartを追加 | 結果Validation passed |
monitors/monitors.json | 書き間違いの内容descriptionを省略 | 結果Validation passed |
monitors/monitors.json | 書き間違いの内容commandに${user_config.token} | 結果Validation passed |
experimental.monitorsにパス文字列 | 書き間違いの内容指す先のファイルに未知キー | 結果Validation passed |
experimental.monitorsにパス文字列 | 書き間違いの内容存在しないパス | 結果失敗(Path not found) |
experimental.monitorsのインライン配列 | 書き間違いの内容未知キーrestart | 結果失敗(Unrecognized key: "restart") |
experimental.monitorsのインライン配列 | 書き間違いの内容descriptionを省略 | 結果失敗(Invalid input) |
experimental.monitorsのインライン配列 | 書き間違いの内容commandに${user_config.t} | 結果Validation passed |
トップレベルのmonitors | 書き間違いの内容正しい配列 | 結果警告付きでValidation passed with warnings |
インライン配列はマニフェストの一部としてスキーマを検査され、ファイルに書いた中身は検査されません。パス文字列の場合に検査されるのはパスの存在までで、user_configの参照はどの書き方でも検査をすり抜けます。
存在しないパスと、トップレベルのmonitorsを宣言した場合は、それぞれ次のように出力されました。
✘ Found 1 error:
❯ experimental.monitors: Path not found: ./nope.json. The runtime loader will report this as a load failure.
✘ Validation failed⚠ Found 1 warning:
❯ monitors: 'monitors' is an experimental component; declare it under 'experimental.monitors' instead of at the top level. Top-level still loads for now but will be removed in a future release.
✔ Validation passed with warningsmonitors/monitors.jsonの中身は読み込み時にはじめて評価されます。公式の仕様では、未知キーのあるmonitorsエントリは読み込み時のエラーになり、プラグインごと読み込まれません。validateが失敗にする条件(マニフェストの型の不一致、存在しない・プラグインの外を指すパス、monitorsエントリなどの未知キー)は、読み込み時にも同じ問題として報告されます。パスが欠けているときは/pluginのErrorsタブに<component> path not found: <path>の形で出ます。validateが通ってもそれだけで安心せず、実際にセッションを起動して監視が動くかまで確かめます。
手動のMonitorツールとの違い
Claude Codeにはユーザーが対話中に「このログを見ていて」と頼んで使うMonitorツールもあります。プラグインのmonitorsはこのツールが使える環境でだけ動くため、可用性の制約は共有しますが、起動の主導権が違います。
Monitorツールとプラグインのmonitors
Monitorツール
ユーザーやClaudeがセッション中に依頼して起動します。対象はその場で決めた任意のコマンドかWebSocketで、期限は既定5分・最長30分です。停止はClaudeへのキャンセル依頼かセッション終了で行います。一時的な調査向きです。
プラグインのmonitors
プラグインの有効化時、またはwhenで指定したスキルの初回実行時に自動起動します。対象はmonitors.jsonに書いた固定のコマンドで、セッションが終わるまで動き続けます。導入者全員に同じ監視を配りたいときに向きます。
対話的なMonitorツールの期限まわりの変更はMonitorのpersistentが廃止され30分で監視が切れる理由で扱っています(時期は後述の年表のとおりです)。
動作条件と権限
プラグインのmonitorsが起動するのは対話的なCLIセッションだけです。-pを付けた非対話モードでは起動しません。API提供元やテレメトリの設定でMonitorツールが使えない環境でも起動せず、該当するのは次の場合です。
- Amazon Bedrock・Google Cloudのエージェントプラットフォーム・Microsoft Foundry経由で使っている
DISABLE_TELEMETRYまたはCLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFICが設定されている
なお、Monitorツール自体はWindowsではGit Bashが入っている場合にだけ使えます。
リポジトリにコミットして配るプロジェクトスコープのプラグインでは、バックグラウンドmonitorsは読み込まれません。個人スコープのプラグインにはこの制限がなく、社内に配るときの導入先をどのスコープにするかは、この点から決まります。
Monitorツールがコマンドを実行するときの権限判定はBashと同じで、Bashに設定したallow/denyルールが適用されます。auto modeの間はMonitorそのものを名指ししたallowルールが脇に置かれ、分類器がBashと同じ基準でコマンドを審査します。
Linux・WSLでCLAUDE_CODE_TOOL_MEMORY_LIMITによるメモリ上限を設定している場合、Monitorツールのコマンドも、Bash・PowerShellの使用量と合算した1つの上限の対象です。個々のコマンドごとに上限が分かれるわけではありません。この上限自体はv2.1.233以降の機能で、Monitorツールのコマンドが対象に入ったのはv2.1.246からです。
monitors機構が育ってきた流れ
プラグイン向けのmonitors機構はv2.1.105を起点に、宣言の置き場所と検査が段階的に整えられてきました。
Monitorとプラグインmonitorsの変遷
- 2026年4月9日Monitorツール追加(v2.1.98)
バックグラウンドのスクリプトからイベントを流すMonitorツールが入りました。
- 2026年4月13日プラグインがmonitorsを宣言可能に(v2.1.105)
トップレベルの
monitorsキーで、セッション開始時またはスキル呼び出し時に自動起動する監視を宣言できるようになりました。 - 2026年5月6日experimental配下へ移動(v2.1.129)
monitorsとthemesはexperimentalの下に宣言する形になりました。トップレベルの宣言は動き続け、validateが警告します。 - 2026年7月11日user_config参照を拒否(v2.1.207)
shell形式のコマンドでの
${user_config.*}参照が、hooks・monitors・MCPのheadersHelperで拒否されるようになりました。 - 2026年9月14日Monitorに必ず期限(v2.1.271)
対話的なMonitorの監視は最長30分(単発プロンプトの
-pでは10分)の期限付きになりました。 - 2026年9月25日validateがパスを検査(v2.1.283)
claude plugin validateが、monitorsなどのパスが存在しない・プラグインの外を指す場合に失敗するようになりました。
Monitorツールは現在WebSocketソースにも対応しています。サーバー側がすでにイベントをpushしているなら、ポーリングスクリプトを書かずに直接接続できます(仕様はClaude Code Monitorツールの使い方で扱っています)。ただしWebSocket入力は対話的なMonitorツールの機能です。
プラグインのmonitorsエントリに書けるキーはname・command・description・whenの4つだけなので、WebSocket配信のサービスを監視するときは、自前のクライアントスクリプトをcommandで動かして標準出力に流します。
プラグイン無効化・更新時の挙動
セッションの途中でプラグインを無効化しても、すでに起動しているmonitorプロセスはその場では止まらず、セッションが終わるまで動き続けます。無効化した直後に監視が止まらなくても、想定どおりの動作です。
プラグインをセッション中に新しいバージョンへ更新した場合、hooks・MCPサーバー・LSPサーバーは/reload-pluginsで新しいパスに切り替わります。monitorsは古いバージョンのパスを使い続け、切り替えにはセッションの再起動が必要です。--plugin-dirでmonitors.jsonを編集しながら開発するときも同じなので、確認の流れは次のようになります。
monitors.jsonを直して確かめる流れ
- 1
monitors.jsonを編集する
/reload-pluginsでは反映されないので、保存だけしておきます。 - 2
claude plugin validateにかける
インライン配列の書き間違いとパスの欠落はここで拾えます。ファイルの中身は検査されません。
- 3
セッションを再起動する
再起動したセッションで、通知が届くか・
user_configエラーが出ていないかを見ます。
よくあるつまずき
CLAUDE_PLUGIN_ROOTをダブルクォートで囲み忘れる: パスにスペースを含む環境で展開が壊れる。shell形式のmonitorコマンドでは"${CLAUDE_PLUGIN_ROOT}"のようにクォートで囲む- 常時監視が必要ないのに
whenを省略する: 既定は"always"。デバッグ用の監視まで毎セッション起動させると、不要な常駐プロセスが増える
まとめ
配布先がプロジェクトスコープのプラグインだとmonitorsは読み込まれないため、監視を配るなら先に導入スコープを決めます。