Hooksの使い方
Hooksの適用手順
1. ツール実行前か後、どちらにHooksを呼び出すかを決め、PreToolUse or PostToolUseを使うかを指定する
2. どのツールに対してHooksを適用したいかを決める
MCPサーバーなどを追加されると、Toolの種類が多くなるため、Claude Code上で「ツールの一覧を教えてください」などを指示すると教えてくれる。
3. settings.jsonに呼び出すときのToolと、Hooksで使用するコマンドを入力していく
例えば、PreToolUseを使う場合、Claudeからツールの呼び出しが発生した場合、PerToolUseが動く前には以下のJSONデータが渡される
code:json
{
"session_id": "2d6a1e4d-6...",
"transcript_path": "/Users/sg/...",
"hook_event_name": "PreToolUse",
"tool_name": "Read",
"tool_input": {
"file_path": "/code/queries/.env"
}
}
4. 処理が終了した時に、Claude側のフィードバックを伝える終了コードを渡すか、ブロックするかを検討する`
終了コード0 か 2 の違いで、Claudeにフィードバック送信するかが変わる
0: 正常。ツールの呼び出しを続行
2: ツールの呼び出しをブロック(PreToolUseのみ)
Hooksを作成
1. setting.local.jsonにmatcherとcommandを記載する
matcher:監視したいToolを指定する
command:Toolが実行される前に処理したコマンドを追加する
code:setting.local.json
"hooks": {
"PreToolUse": [
{
"matcher": "Read|Grep",
"hooks": [
{
"type": "command",
"command": "node ./hooks/read_hook.js"
}
]
},
}
. ClaudeからTool呼び出しが発生した時に発生する標準入力はJSON形式で表示される
code:json
{
"session_id": "f62f6d87-872e-4324-8b1e-3ccfc0e3fafa", // セッションID
"transcript_path": "Uigen/f62f6d87-872e-4324-8b1e-3ccfc0e3fafa.jsonl",
"cwd": "/Uigen",
"hook_event_name": "PreToolUse", // フックイベント
"tool_name": "Read",// ツール名
"tool_input": {
"file_path": "Uigen/.env" // ファイルパス
}
}
2. /hooksディレクトリ内部にコマンドを記載したファイルを作成する
終了コード2を渡すことで、Claude Code側のHooksがブロックされることを伝える
code:read_hooks.js
async function main() {
const chunks = [];
for await (const chunk of process.stdin) {
chunks.push(chunk);
}
const toolArgs = JSON.parse(Buffer.concat(chunks).toString());
// readPath is the path to the file that Claude is trying to read
const readPath =
toolArgs.tool_input?.file_path || toolArgs.tool_input?.path || "";
if (readPath.includes(".env")) {
console.error(".envファイルを読み込むことはできません");
process.exit(2);
}
}
main();
実際にどんな標準出力があるかをデバッグしたい時には、 Hooksやコマンドを使ってデバッグファイルを外部出力するとデバッグしやすい
console.logなどはターミナル上に表示されないので、記載しても効果なし
code:settings.json
"hooks": {
"PreToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "jq . > pre-log.json"
}
]
}
],
code:read_hook.js
const debugData = {
timestamp: new Date().toISOString(),
chunks: chunks.map((chunk) => chunk.toString()),
chunksRaw: chunks,
};
fs.writeFileSync("debug-chunks.json", JSON.stringify(debugData, null, 2));
メリット
機密データを読み取る前にブロッックできる
Hooksの失敗する理由をClaudeが理解できる
複数のToolで動作する
意味のあるエラーメッセージを表示できる
フック(settings.local.json)に関するセキュリティ
外部からの入力をそのまましようせずにサニタイズすること
シェルコマンドで変数を使用するときにはダブルコーテーションでくくること($VAR)
絶対パスで記述すること
機密ファイル(.env/git/)などは対象外にする
ユースケース
TypeScriptの型チェックで使用する場合
Claude Codeの指示をし関数を作成した時、型チェックエラーになったときには、PostToolUseを使用して型チェックを実装する
既存実装を再利用する使い方
特定のディレクトリを含む機能実装をClaude Codeに依頼したときに、重複した関数を作成して実装することがある
その場合、特定のディレクトリでファイルを編集する前に、PreToolUseを使って重複した実装をしないようなレビューを別インスタンスで実行した上で、指示を継続することができる
上記のようなHookは以下のトレードオフになるため注意する
メリット:重複がすくないコードができあがる
コスト:ディレクトリ編集ごとに時間とコAPI使用が発生する
推奨事項:重要なディレクトリのみ監視するようにする
他にも以下のユースケースで可能
コンパイラ・Linterで即時フィードバック
個別のインスタンスを起動してコードレビューできる
最も重要なディレクトリで監視する
自動化のメリットとパフォーマンスコストのバランスを考える