mods API を使用する
Claude Code mod から mods API を呼び出して、コマンドとツールを追加し、モデルを呼び出し、タイマーで作業を実行し、他のセッションにメッセージを送信し、ファイルとネットワークにアクセスします。
mods API は、mod が動作するために呼び出すメソッドのセットです。コマンドとツールを追加し、モデルを呼び出し、イベント間で作業を実行し、ファイルシステム、プロセス、ネットワークにアクセスします。すべてのフックは、最初の引数として $ を受け取り、メソッドは $.ui や $.fs などの名前空間でグループ化されています。Events はフックが実行されるタイミングを決定し、mods API はフックが実行されたときに呼び出すものです。
最初の mod を作成 してからここを開始してください。すべてのメソッドについては、mods API メソッド を参照するか、ビルド用の型 を読んでください。
コマンドまたはツールを追加する
mod はユーザーが実行するコマンドと Claude が呼び出すツールを追加できます。両方を session.start フックに登録します。Claude Code はそのフックを最初のプロンプトの前に待つため、登録したものは最初のターンから利用可能です。
コマンドを追加する
コマンドはユーザー向けです。登録してから、その名前の command.run を処理します。この例は、オプションの日数を取る /standup コマンドを追加します。
on('session.start', async ($, e, next) => {
// /standup をコマンドリストに追加し、ユーザーがそこで見る説明を付ける
await $.command.register({ name: 'standup', description: 'Summarize what changed today', argumentHint: '[days]' })
return next(e)
})
// マッチャーはフックを /standup に制限するため、他のコマンドは到達しない
on('command.run', { command: 'standup' }, async ($, e) => {
// e.args はコマンド名の後に入力されたテキスト、または空の文字列
return { text: 'Summary for the last ' + (e.args || '1') + ' day(s): ...' }
})
セッションが開始した後、/standup はその説明とともに / を入力したときに表示されるリストに表示されます。argumentHint は、コマンドを入力してスペースを入力した後、プロンプトに /standup [days] として表示されます。/standup 3 を実行すると、2 番目のフックは Summary for the last 3 day(s): ... を返し、トランスクリプトはプラグイン名の後にそのテキストを表示します。フックは next を呼び出しません。コマンドはあなたのもの以外に動作がないためです。
返す text はトランスクリプトに出力され、Claude がそれを読みます。何も出力しない場合、ペイン を開くだけのコマンドの場合は、{} を返します。Claude が作業中にコマンドを実行できるようにするには、登録に immediate: true を追加します。
組み込みコマンドが使用していない名前を選択してください。セッションで / を入力して、それらを確認してください。$.command.register は、"/focus" refused: it is the built-in /focus などのメッセージで、取得された名前に対してスローします。スローするフックはスキップされるため、その session.start フックの残りも実行されません。そのフックの最後にコマンドを登録するか、呼び出しを try と catch でラップします。
ツールを追加する
ツールは Claude 向けです。名前、Claude が読む説明、入力用の JSON Schema で登録します。Claude は、mcp__、プラグイン名、2 つのアンダースコア、登録した名前で構成される長い名前の下にそれを見ます。その呼び出しを、その完全な名前にフィルタリングされた tool.call フックで処理します。この例は、my-mod という名前のプラグインから、ticket を登録するため、完全な名前は mcp__my-mod__ticket です。Claude に問題追跡ツールでチケットを検索するツールを提供します。
on('session.start', async ($, e, next) => {
await $.tool.register({
name: 'ticket',
// Claude はこの説明からツールを呼び出すタイミングを決定する
description: 'Look up a ticket by its id and return its title and status',
// Claude が送信する必要がある引数:id という名前の 1 つの必須文字列
inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
})
return next(e)
})
// 完全なツール名は mcp__、プラグイン名、登録された名前
on('tool.call', { tool: 'mcp__my-mod__ticket' }, async ($, e) => {
// ツールの引数は e のフィールドなので、id は e.id
const response = await $.http.fetch('https://tickets.example.com/api/' + encodeURIComponent(e.id))
// どちらの場合でも結果を返すため、Claude は検索が失敗したときに学習する
return { result: response.ok ? response.text : 'Lookup failed with status ' + response.status }
})
チケットについて質問すると、Claude は mcp__my-mod__ticket をその id で呼び出すことができます。2 番目のフックはチケットを取得し、応答本文を返します。Claude はそれをツールの結果として読みます。サーバーがエラーステータスで応答すると、Claude は Lookup failed with status と数字を読みます。
モデルを呼び出す
mod は、テキストの並べ替えや要約などの小さなジョブのために、会話の外で独自にモデルに質問を尋ねることができます。$.model.complete はセッションの認証情報を使用して 1 つのプロンプトをモデルに送信し、返信に解決します。会話履歴はありません。
このフックは、コマンドとして登録された /triage コマンドに答え、小さなモデルに、その後に入力されたテキストにラベルを付けるよう尋ねます。
on('command.run', { command: 'triage' }, async ($, e) => {
const r = await $.model.complete({
model: 'haiku',
// システムプロンプトはジョブを設定し、プロンプトはラベル付けするテキストを運ぶ
system: 'Reply with one word: bug, feature, or question.',
prompt: e.args,
// 1 語は少数のトークンが必要で、呼び出しは 15 秒後にあきらめる
maxTokens: 20,
timeoutMs: 15000,
})
// r.text は、モデルが応答したときのみ存在するため、最初に r.isAnswered をチェック
const label = r.isAnswered ? r.text.trim() : 'unknown'
return { text: 'Label: ' + label }
})
/triage the export button does nothing を実行すると、mod はそのテキストをモデルに送信し、その答え(Label: bug など)を出力します。Claude の会話は要求の一部ではありません。モデルが応答しない場合、ラベルは unknown です。
Claude API の失敗は呼び出しを拒否しないため、r.isAnswered をチェックし、それが false の場合は r.reason を読んでください。呼び出しは、Claude Code が送信しない要求(組織がブロックするモデルなど)に対してのみ拒否します。ビルド用の型 は、effort などの他のオプションをリストし、制限 は maxTokens のデフォルトを示します。
$.model.fork({ prompt }) は、代わりに現在の会話に 1 つの質問を尋ね、同じモデルとシステムプロンプトを使用するため、Claude API はプロンプトキャッシュからほとんどを提供します。
これらの呼び出しはユーザーのプランまたは API キーを使用します。
バックグラウンドで作業を実行する
1 つのイベントを超える作業(1 分ごとに何かをチェックするなど)は、session.start から開始するタイマーで実行されます。フック自体は 1 つのイベントに対して実行され、独自の実行時間の 10 秒の時間制限があります。next または mods API 呼び出しで費やされた時間はカウントされません。ただし、$.clock.sleep は例外です。$.clock.every と $.clock.after は setInterval と setTimeout の代わりになり、遅延はミリ秒で最初に来ます。$.clock.after(5000, fn) は fn を 1 回呼び出し、今から 5 秒後です。各々はタイマーを返し、cancel() メソッドを持ち、await $.clock.now() はミリ秒単位の時間を与えます。
このフックはプルリクエストのチェックを 1 分ごとに検索し、プロンプトの下に結果を表示します。summarize はコマンドの JSON 出力を数語に変換する独自の関数です。
on('session.start', async ($, e, next) => {
// 60,000 ミリ秒ごとに関数を呼び出し、今から 1 分後に開始
$.clock.every(60_000, async () => {
const status = await $.process.run(['gh', 'pr', 'checks', '--json', 'state'])
// プロンプトの下の行を最新の要約に置き換える
$.ui.status('checks: ' + summarize(status.stdout))
})
// タイマーを待たずに戻るため、セッションはすぐに開始される
return next(e)
})
セッションは通常どおり開始されます。1 分後、プロンプトの下に ⚠、mod の名前、その後 checks: とあなたの要約を含む行が表示されます。その後、1 分ごとに置き換えられます。タイマーのコールバックはイベント外で実行されるため、ターン間で実行され続け、ターンを開始しません。コールバックがスローする場合、エラーは デバッグログ に移動し、タイマーは次の間隔で再度実行されます。
ターンを開始せずに何かを表示する
バックグラウンドジョブは、ターンを開始せずにユーザーに何かを表示できます。これらの各呼び出しは、異なる場所にテキストを配置します。
| 呼び出し | ユーザーが見るもの |
|---|---|
$.ui.status(text) |
プロンプトの下の 1 行で、変更するまで残ります。⚠ と mod の名前で始まります。⚠ my-mod: checks: 3 passing など。 |
$.ui.toast(text) |
右上の小さなボックスで、mod の名前がテキストの上にあり、数秒後に消えます |
$.ui.log(text) |
Claude が読まない、トランスクリプト内の薄い行。● と mod の名前で始まります。● my-mod: build finished など。 |
バックグラウンドジョブからターンを開始する
バックグラウンドジョブが Claude の注意が必要なものを見つけた場合、$.prompt.submit({ text }) でプロンプトを送信してターンを開始できます。Claude は、送信者として mod に名前を付ける文の後にテキストを読みます。ユーザー自身の言葉として送信するには、その文なしで、asUser: true を追加します。呼び出しはセッションがアイドル状態になるまで待機してから、新しいターンを開始します。そのターンが開始されたときに解決するため、Claude が作業中に実行されるハンドラーで await しないでください。
バックグラウンド作業を停止する
バックグラウンド作業は 2 つの方法で停止します。モジュールが再読み込みされるとタイマーが停止します。フック内の長時間実行作業の場合、next.signal は AbortSignal で、フックが処理しているイベントが放棄されたときに中止されます。たとえば、ユーザーが割り込むと、長時間実行されるものに渡します。
セッション間でメッセージを送受信する
mod は、別のセッションまたはこのセッションのサブエージェントの 1 つにプレーンテキストメッセージを送信し、到着して離れるメッセージを観察できます。$.session.send({ to, text }) は 1 つを送信し、SendMessage ツールが行う配信と同じです。to は、セッションの場合は { sessionId }、$.agent.list() からのサブエージェントの場合は { agentId }、または受信したメッセージが来たアドレスです。呼び出しはメッセージがキューに入ったら解決し、{ isDelivered: true } で解決します。何も配信されなかった場合、{ isDelivered: false, reason } で解決し、reason は理由を述べます。
このフックは、コマンドとして登録された /ping コマンドに答え、その後に入力したセッション ID のセッションにステータスを尋ねます。
on('command.run', { command: 'ping' }, async ($, e) => {
// e.args は /ping の後に入力されたセッション ID
const sent = await $.session.send({ to: { sessionId: e.args }, text: 'Status? One line.' })
// 呼び出しはどちらの場合でも解決するため、isDelivered をチェックして何が起こったかを学習
if (!sent.isDelivered) $.ui.toast('Not delivered: ' + sent.reason)
// 空の結果はこのセッションのトランスクリプトに何も出力しない
return {}
})
メッセージがキューに入ると、セッションに何も表示されず、他のセッションの Claude は Status? One line. を読みます。何も配信されなかった場合、右上の小さなボックスが理由を示し、数秒後に消えます。
2 つのイベントにより、mod はメッセージを観察できます。両方から next(e) を返して、各メッセージを変更されずに渡します。
| イベント | 発火するタイミング | 有用なフィールド |
|---|---|---|
session.receive |
メッセージがこのセッションに到着し、Claude がそれを読む前 | e.text、および e.origin.kind(別のセッションまたはエージェント、task-notification、または scheduled-trigger の場合は peer または peer-send-message など)。{ consumed: reason } を返して Claude から保持します。 |
session.send |
メッセージが SendMessage ツールまたは mod から離れようとしている | e.to、e.text、および e.origin.kind(model または plugin) |
インバウンドメッセージを拒否 するように設定されたセッションは、session.receive が発火する前にメッセージを拒否するため、フックはそれを見ません。承認待ちのメッセージはフックに最初に到達するため、mod はまだ承認していないメッセージを読むことができます。フックの next(e) はメッセージが配信されないときに拒否します。
受信したメッセージの送信者の名前は、送信者が書いたものなので、それに基づいて決定しないでください。
ファイル、プロセス、ネットワークにアクセスする
mod は、Claude Code を実行しているユーザーと同じ権限で、mods API を通じてファイルシステム、プロセス、ネットワークにアクセスします。フックモジュール自体には Node.js API、setTimeout などのタイマーグローバル、独自のネットワークまたはファイルアクセスはありません。URL、TextEncoder、AbortController、crypto.subtle などの標準 JavaScript および Web API が利用可能です。以下の各名前空間は、1 種類のアクセスをカバーしています。
| 名前空間 | 何をするか |
|---|---|
$.fs |
read(path)、write(path, text)、exists(path)、stat(path)、list(path) はファイルとディレクトリで動作 |
$.process |
run(['git', 'status']) はコマンドを開始し、終了時に解決。spawn は長時間実行コマンドの出力をストリーム。 |
$.http |
http または https 上の fetch(url, init)。本文が読み込まれたら { status, ok, headers, text } に解決。 |
$.store |
プラグイン独自の JSON キー値ストア、セッション間で保持 |
$.env |
環境変数を get および set。名前をリテラル文字列として記述。 |
$.settings |
設定ファイルと管理ポリシーが保持するものを read |
$.session |
messages() はトランスクリプトを { role, text, toolUses } のリストとして返す。また、作業ディレクトリ、モデルなど。usage() はコンテキストウィンドウの使用とプラン制限を返す。 |
$.mcp |
接続された MCP サーバーのツールを call |
ファイルとプロセスには、独自のいくつかのルールがあります。
- パス:相対パスはセッションの作業ディレクトリの下
$.fs.list:1 つのディレクトリのエントリを{ name, kind, size, isLink }として返し、サブディレクトリに下降しない$.process.run:引数リストを取り、シェルを使用しない。終了コードに関係なく{ exitCode, stdout, stderr }に解決。プログラムが開始できないか、タイムアウト時にまだ実行中の場合は拒否します。デフォルトは 30 秒なので、tryとcatchでラップします。
これらの呼び出しのそれぞれは、それ自体がイベントであり、$.fs.read の場合は fs.read など、$. なしで名前空間とメソッドに対して名前が付けられています。チェーンの前にある mod は、呼び出しを観察、書き直し、または拒否できます。これは、組織が mod が到達するものを制限する方法です。
次のステップ
- イベントに反応する:ツール呼び出し、プロンプト、ターンをフック
- インターフェイスに描画する:mod が収集するものをペインまたはプロンプトの上に表示
- mod をテストする:テストでこれらの呼び出しのいずれかをスタブ
- Mods リファレンス:すべてのイベント、すべての mods API メソッド、制限