SpyBara
Go Premium

plugins/mods/api.md 2026-10-08 22:58 UTC to 2026-10-09 22:01 UTC

This page contains 106 additions and 7 deletions.

2026
Thu 1 23:59 Fri 2 22:59 Thu 8 22:58 Fri 9 23:02

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 はプロンプトを単独で送信し、$.model.fork({ prompt }) は現在の会話の末尾にプロンプトを付けて送信します。

次の表は、それぞれのリクエストに含まれる内容を比較したものです。

リクエストの内容 $.model.complete $.model.fork
モデル 渡した model セッションのモデル
システムプロンプト 短い帰属ブロック、続いて system を渡した場合はその内容 セッションのシステムプロンプト
メッセージ 1 つのユーザーメッセージ(渡した prompt) これまでの会話、続いてユーザーメッセージとしての prompt
CLAUDE.md およびその他のプロジェクトコンテキスト 含まれない 会話の最後のリクエストと同様に含まれる
ツール なし Claude のツール(モデルは呼び出せない)

フォークは会話の最後のリクエストを繰り返すため、会話がまだキャッシュされている間は、Claude API はその大部分をプロンプトキャッシュから提供します。

どちらの呼び出しもセッションの認証情報を使用するため、ユーザーのプラン、API キー、またはクラウドプロバイダーに課金されます。ビルド用の型には、すべての $.model メソッドが記載されています。

1 つのプロンプトを送信する

$.model.complete に model と prompt を渡します。prompt はユーザーメッセージになります。役割や出力形式などの指示をモデルに与えるには、system も渡します。これはシステムプロンプトになります。

このフックは、コマンドとして登録された /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 など)を出力します。モデルが応答しない場合、ラベルは unknown です。

Claude API の失敗は呼び出しを拒否しないため、r.isAnswered をチェックし、それが false の場合は r.reason を読んでください。呼び出しは、Claude Code が送信しないリクエスト(組織がブロックするモデルなど)に対して拒否されます。

ビルド用の型には effort などの他のオプションが記載されており、制限には maxTokens のデフォルトが示されています。

プロンプトキャッシュを使用する

$.model.complete は Claude API のプロンプトキャッシュをサポートしています。API は、リクエストの先頭部分(プレフィックスと呼ばれます)を、設定したキャッシュブレークポイントまでキャッシュします。すべての呼び出しが、指示や参考資料などの同じ長い静的コンテンツで始まる場合は、そのコンテンツの末尾にブレークポイントを設定します。以降の呼び出しでは、その部分に対して入力の全額を支払う代わりに、キャッシュから読み取ります。

ブレークポイントを設定するには、prompt を文字列ではなく { text } ブロックの配列として渡し、静的コンテンツの最後のブロックに cache: true を追加します。Claude Code はそのブロックを API の cache_control フィールド付きで送信します。system も同じ配列形式を受け付けます。どちらを使うかの判断については、prompt と system のどちらを使うか選ぶを参照してください。

このバージョンの /triage フックは、ラベル付けするテキストの前に長いラベル付けルールを送信し、ルールの後にブレークポイントを置きます。RULES は独自に用意する文字列です。

on('command.run', { command: 'triage' }, async ($, e) => {
  const r = await $.model.complete({
    model: 'haiku',
    prompt: [
      // すべての呼び出しで同一なので、キャッシュされるプレフィックスになる
      { text: RULES, cache: true },
      // 呼び出しごとに変わるので、ブレークポイントの後に置く
      { text: e.args },
    ],
  })
  return { text: 'Label: ' + (r.isAnswered ? r.text.trim() : 'unknown') }
})

TTL とブレークポイントの数には次の制限があります。

  • TTL: キャッシュエントリは最後に使用されてから 5 分間保持されます。TTL は呼び出しではなく、ユーザーの Claude Code 設定によって決まります。1 時間にするには、subagentPromptCacheTtl を 1h に設定します。
  • リクエストあたりのブレークポイント数: API は最大 4 つまで受け付け、それを超えると r.reason に api-error が返されます

`prompt` と `system` のどちらを使うか選ぶ

リクエストが Claude API に直接送られることがわかっている場合を除き、呼び出し間で共有する静的コンテンツは prompt の先頭に置いてください。

  • API キーまたは Claude のサブスクリプションで Claude API に直接送る場合: どちらのフィールドでも機能します
  • Amazon Bedrock、Claude Platform on AWS、Google Cloud の Agent Platform、Microsoft Foundry、または LLM ゲートウェイを経由する場合: prompt を使用します。Claude Code はシステムプロンプトの先頭に帰属ブロックを置き、そのフィンガープリントはユーザーメッセージの先頭から生成されます。api.anthropic.com エンドポイントはキャッシュの前にそのブロックを取り除きます。その他のエンドポイントはそれをプロンプトの一部として受け取るため、prompt の先頭が異なると system 内のブレークポイントがヒットしないことがあります。
  • 他の人が実行する mod の場合: prompt を使用します。相手のプロバイダーは選べないためです

プレフィックスでは system が prompt より前に来るため、prompt 内のブレークポイントは system もカバーし、system が異なる呼び出しはキャッシュにヒットしません。

キャッシュヒットを確認する

$.model.complete の結果には、API のキャッシュフィールドを含む usage オブジェクトがあります。usage.cache_creation_input_tokens は呼び出しがキャッシュに書き込んだトークン数を、usage.cache_read_input_tokens はキャッシュから読み取ったトークン数を数えます。最初の呼び出しでは書き込みが発生し、TTL 内の以降の呼び出しでは読み取りが発生するはずです。

すべての呼び出しで書き込みが発生し読み取りがない場合は、呼び出し間でプレフィックスが異なっているか、呼び出しの間隔が TTL より長くなっています。プレフィックスが異なる場合については、prompt と system のどちらを使うか選ぶを参照してください。

モデルが応答した呼び出しで両方のフィールドがゼロのままの場合は、何もキャッシュされていません。次の各原因を確認してください。

  • プレフィックスが短すぎる: API はモデルの最小長未満のプレフィックスをキャッシュせず、エラーも返しません
  • プロンプトキャッシュが無効になっている: DISABLE_PROMPT_CACHING 変数がそのモデルに適用されている場合、Claude Code はブレークポイントを削除し、テキストをキャッシュなしで送信します
  • ゲートウェイが cache_control を取り除いている: ゲートウェイはフィールドを削除しても成功を返すことがあります
  • 別の mod がテキストの先頭を書き換えている: その場合、Claude Code はブレークポイントなしで送信します

`model.complete` フックが受け取るもの

model.complete イベントをフックして他の mod のリクエストを検査または変更する場合は、次のフィールドからテキストを読み取ります。

  • e.prompt: 常に文字列です。呼び出し元が配列を渡した場合は、ブロックのテキストを順に連結したものです。
  • e.system: 同じ方法で構築された文字列です。呼び出し元が system を渡さなかった場合は存在しません
  • e.promptBlocks と e.systemBlocks: 呼び出し元の配列です。それぞれ、呼び出し元がそのフィールドに配列を渡した場合に存在します

Claude Code は、フックが next に渡した文字列を送信し、それと一緒に渡された配列を使用してキャッシュブレークポイントを配置します。文字列の先頭とまだ一致している先頭のブロックはブレークポイントとともに保持し、文字列の残りはブレークポイントなしで送信します。たとえば、next({ ...e, prompt: e.prompt + NOTE }) は呼び出し元のブレークポイントを保持し、prompt の先頭を変更するフックはそれらを削除します。

バックグラウンドで作業を実行する

1 つのイベントを超える作業(1 分ごとに何かをチェックするなど)は、session.start から開始するタイマーで実行されます。フック自体は 1 つのイベントに対して実行され、自身の実行時間には時間制限があります。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 の名前を含むトースト通知で、数秒後に消えます。フルスクリーンレンダリングでは右上のボックスとして、クラシックレンダラーではプロンプトの下の右側に 1 行で表示されます。
$.ui.log(text) Claude が読まない、トランスクリプト内の薄い行。● と mod の名前で始まります。● my-mod: build finished など。

バックグラウンドジョブからターンを開始する

バックグラウンドジョブが Claude の注意が必要なものを見つけた場合、$.prompt.submit({ text }) でプロンプトを送信してターンを開始できます。Claude は、送信者として mod に名前を付ける文の後にテキストを読みます。ユーザー自身の言葉として送信するには、その文なしで、asUser: true を追加します。呼び出しはセッションがアイドル状態になるまで待機してから、新しいターンを開始します。そのターンが開始されたときに解決するため、Claude が作業中に実行されるハンドラーで await しないでください。

バックグラウンド作業を停止する

モジュールが再読み込みされるとタイマーが停止します。フック内の長時間実行作業の場合、next.signal は AbortSignal で、フックが処理しているイベントが放棄されたとき(たとえばユーザーが割り込んだとき)に中止されます。そのため、長時間実行されるものにはこれを渡してください。

セッション間でメッセージを送受信する

mod は、自分の別のセッション、このセッションのサブエージェントの 1 つ、またはそのエージェントチームのチームメイトにプレーンテキストメッセージを送信できます。また、到着して離れるメッセージを観察することもできます。

メッセージを送信するには、$.session.send({ to, text }) を呼び出します。これは SendMessage ツールが行う配信と同じです。to は受信者に応じて設定します。

  • 自分の別のセッション: { sessionId }
  • サブエージェントまたはチームメイト: { agentId }($.agent.list() から取得した ID を使用)
  • 受信したメッセージの送信者: そのメッセージの送信元の文字列アドレス

呼び出しはメッセージがキューに入ったら解決し、{ 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. を読みます。何も配信されなかった場合は、トースト通知で理由が表示されます。

session.receive と session.send により、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 は、コマンドが出力を生成した後や終了した後でも $.process.spawn の呼び出しを拒否でき、その場合もコマンドが行ったことは何も元に戻されません。このとき呼び出しは拒否され、そのメッセージの末尾には次のいずれかの文字列と、拒否した mod の理由が付きます。

  • $.process.spawn started, and a plugin withheld its result::拒否した mod がコマンドの出力を最後まで読み取っていなかった。コマンドがまだ実行中の場合、Claude Code はコマンドを停止する。
  • $.process.spawn ran, and a plugin withheld its result::拒否した mod がコマンドの出力を最後まで読み取っていたため、コマンドはすでに終了していた

次のステップ