一. Hooks とは何か?何に使えるか?どんな仕組みか?
1. Hooks とは
Hooks(フック)は Claude Code が提供するインターセプト機構です。Claude がツールを呼び出す前または後に、カスタムコマンドを割り込ませて実行することができます。
Hooks がない場合、ツール呼び出しフローは次のとおりです:
ユーザーがリクエストを送る
→ Claude モデルがツールの使用を決定
→ Claude Code がツールを実行
→ 結果が Claude に返る
Hooks を導入した場合:
ユーザーがリクエストを送る
→ Claude モデルがツールの使用を決定
→ [PreToolUse Hook が発火] ← ここでインターセプトできる!
→ Claude Code がツールを実行
→ [PostToolUse Hook が発火] ← またはここで後処理を行う
→ 結果が Claude に返る
2. Hookの種類
| 種類 | 発火タイミング | 操作をブロックできるか | 主な用途 |
|---|---|---|---|
| PreToolUse | ツール実行前 | ✅ できる | アクセス制御、インターセプト |
| PostToolUse | ツール実行後 | ❌ できない(実行済み) | フォーマット、テスト、ログ |
3. よくある用途
-
アクセス制御:Claude が特定のファイル(
.envなど)を読み取ったり変更したりするのをブロックする - コードフォーマット:ファイルが変更された後、自動で Prettier を実行する
- 自動テスト:ファイル変更時に自動でテストを走らせる
- コード品質:ESLint / TypeScript の型チェックを実行し、Claude にフィードバックする
- ログ記録:Claude がどのファイルにアクセスしたかを追跡する
二. Hooks の導入方法
1. 設定ファイルの場所
Hooks は Claude の設定ファイルで定義します。設定できる場所は 3 つあります:
| 設定ファイルのパス | 適用範囲 | 使い所 |
|---|---|---|
~/.claude/settings.json |
グローバル、全プロジェクトに影響 | 共通のセキュリティルール |
.claude/settings.json |
現在のプロジェクト、Git にコミットされる | チーム共有の設定 |
.claude/settings.local.json |
現在のプロジェクト、Git にコミットしない | 個人のローカル設定 |
おすすめ:
.envのような機密ファイルを保護する場合は.claude/settings.local.jsonに記述するだけで十分です。有効に機能しつつ、リポジトリにコミットされる心配もありません。
2. 設定方法
方法①:設定ファイルを直接編集する(本記事ではこちらをメインで解説)
方法②:Claude Code 内でコマンドを使う
/hooks
Claude Code のインターフェース上で /hooks と入力すると、対話形式で Hooks を追加できます。JSON の設定に不慣れな方にも使いやすい方法です。
三. Hooks の定義方法(設定の構造)
1. 設定ファイルにおける Hooks の基本構造は以下のとおりです:
{
"hooks": {
"PreToolUse": [
{
"matcher": "ツール名",
"hooks": [
{
"type": "command",
"command": "node ./hooks/your_hook.js"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "node ./hooks/format_hook.js"
}
]
}
]
}
}
各フィールドの説明
-
matcher:監視するツールを指定します。複数のツールを|で区切ることができます(OR 条件)。例:"Read|Grep"は両方を監視します。 -
type:現在は"command"固定です。 -
command:実行するコマンドです。Hook スクリプトは**標準入力(stdin)**からツール呼び出しの JSON データを受け取ります。
2. Claude Code の組み込みツール一覧
以下のツールを監視対象として指定できます(カスタム MCP Server を追加するとさらに増えます):
-
Read— ファイルの内容を読み取る -
Write— ファイルに書き込む -
Edit/MultiEdit— ファイルを編集する -
Grep— ファイル内を検索する -
Bash— シェルコマンドを実行する -
List— ディレクトリ構造を一覧表示する
ヒント:Claude に「現在使えるツールをすべて教えて」と聞くと、最新の完全な一覧を教えてくれます。
3. Hook スクリプトが受け取るデータの形式
Hook が発火すると、Claude Code は stdin 経由で以下の JSON をスクリプトに渡します:
{
"session_id": "2d6a1e4d-6...",
"transcript_path": "/Users/ユーザー名/...",
"hook_event_name": "PreToolUse",
"tool_name": "Read",
"tool_input": {
"file_path": "/code/myproject/.env"
}
}
4. Exit Code による動作の制御
Hook スクリプトは**終了コード(exit code)**を使って Claude Code に次の動作を伝えます:
| Exit Code | 意味 |
|---|---|
0 |
正常。ツールの実行を続行する |
2 |
ツールの実行をブロックする(PreToolUse のみ有効) |
exit code が 2 の場合、stderr に書き込んだ内容がエラーメッセージとして Claude に送られ、Claude は「Hook によって操作がブロックされた」と理解して適切に対応します。
四. 実装:Hooks を使って Claude が .env を読み取れないようにする
1.ステップ①:Hook スクリプトを作成する
プロジェクトのルートディレクトリに ./hooks/read_hook.js を作成します:
// hooks/read_hook.js
// このスクリプトは Claude が Read または Grep を実行しようとしたときに発火する
async function main() {
// stdin から Claude が渡してきたツール呼び出しデータを読み取る
const chunks = [];
for await (const chunk of process.stdin) {
chunks.push(chunk);
}
// Buffer を結合して JSON としてパースする
const toolArgs = JSON.parse(Buffer.concat(chunks).toString());
// Claude がアクセスしようとしているファイルパスを取得する
// Read ツールは file_path、Grep ツールは path を使うため両方チェックする
const readPath =
toolArgs.tool_input?.file_path ||
toolArgs.tool_input?.path ||
"";
// パスに .env が含まれているか確認する
if (readPath.includes(".env")) {
// stderr に書き込んだ内容がエラーメッセージとして Claude に送られる
console.error("⛔ アクセス拒否:.env ファイルの読み取りは許可されていません。このファイルには機密の環境変数が含まれています。");
// exit code 2 = このツール呼び出しをブロックする
process.exit(2);
}
// 機密パスに一致しなかった場合、exit code 0 = 正常に通過させる
process.exit(0);
}
main();
コードのロジック解説:
-
process.stdinで Claude Code から渡された JSON データを読み取る - パース後、
tool_inputからファイルパスを取り出す - パスに
.envが含まれているか確認する - 含まれている →
console.errorでエラーメッセージを書き、process.exit(2)で操作をブロック - 含まれていない →
process.exit(0)で正常通過
2. ステップ②:settings.local.json を設定する
.claude/settings.local.json を開く(なければ新規作成)し、以下の設定を追加します:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Read|Grep",
"hooks": [
{
"type": "command",
"command": "node ./hooks/read_hook.js"
}
]
}
]
}
}
なぜ Read と Grep の両方を監視するのか?
.env の内容は 2 種類のツールから読み取られる可能性があるためです:
-
Read:ファイルの全文を直接読み取る -
Grep:ファイル内でキーワードを検索する際にも内容が露出する
| で両方を繋ぐことで、すべての入り口をふさぐことができます。
3.ステップ③:Claude Code を再起動する
設定変更を反映するには、Claude Code の再起動が必要です:
# 現在の Claude Code セッションを終了し、再起動する
claude
4.ステップ④:動作を確認する
再起動後、Claude に .env の読み取りを試みさせてみましょう:
プロジェクトルートの .env ファイルを読み取ってください
Claude は Hook スクリプトの console.error に書いたエラーメッセージを受け取り、以下のように返答します:
「
.envファイルを読み取ろうとしましたが、PreToolUse Hook によってこの操作がブロックされました。理由:⛔ アクセス拒否:.env ファイルの読み取りは許可されていません。」
5. 完全なファイル構成
your-project/
├── .claude/
│ └── settings.local.json ← Hook の設定(Git にコミットしない)
├── hooks/
│ └── read_hook.js ← Hook スクリプト
├── .env ← 保護された機密ファイル
└── ...
五. より多くの機密ファイルを保護
複数の種類のファイルを同時に保護したい場合は、read_hook.js の判定ロジックを拡張するだけです:
// アクセスを禁止するファイルパターンを定義する
const BLOCKED_PATTERNS = [
".env", // 環境変数
".env.local",
".env.production",
"id_rsa", // SSH 秘密鍵
".pem", // 証明書ファイル
"secrets.json", // その他のシークレットファイル
];
// いずれかの禁止パターンに一致するか確認する
const isBlocked = BLOCKED_PATTERNS.some((pattern) =>
readPath.includes(pattern)
);
if (isBlocked) {
console.error(`⛔ アクセス拒否:ファイル "${readPath}" は保護された機密ファイルです。`);
process.exit(2);
}
