Für eine Schnellstart-Anleitung mit Beispielen, siehe Erste Schritte mit Claude Code Hooks.

Konfiguration

Claude Code Hooks werden in Ihren Einstellungsdateien konfiguriert:

  • ~/.claude/settings.json - Benutzereinstellungen
  • .claude/settings.json - Projekteinstellungen
  • .claude/settings.local.json - Lokale Projekteinstellungen (nicht committet)
  • Enterprise-verwaltete Richtlinieneinstellungen

Struktur

Hooks sind nach Matchern organisiert, wobei jeder Matcher mehrere Hooks haben kann:

{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolPattern",
        "hooks": [
          {
            "type": "command",
            "command": "your-command-here"
          }
        ]
      }
    ]
  }
}
  • matcher: Muster zum Abgleichen von Tool-Namen, groß-/kleinschreibungsabhängig (nur anwendbar für PreToolUse und PostToolUse)
    • Einfache Strings stimmen exakt überein: Write stimmt nur mit dem Write-Tool überein
    • Unterstützt Regex: Edit|Write oder Notebook.*
    • Verwenden Sie *, um alle Tools abzugleichen. Sie können auch einen leeren String ("") verwenden oder matcher leer lassen.
  • hooks: Array von Befehlen, die ausgeführt werden, wenn das Muster übereinstimmt
    • type: Derzeit wird nur "command" unterstützt
    • command: Der auszuführende Bash-Befehl (kann die Umgebungsvariable $CLAUDE_PROJECT_DIR verwenden)
    • timeout: (Optional) Wie lange ein Befehl laufen soll, in Sekunden, bevor dieser spezifische Befehl abgebrochen wird.

Für Events wie UserPromptSubmit, Notification, Stop und SubagentStop, die keine Matcher verwenden, können Sie das Matcher-Feld weglassen:

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/prompt-validator.py"
          }
        ]
      }
    ]
  }
}

Projektspezifische Hook-Skripte

Sie können die Umgebungsvariable CLAUDE_PROJECT_DIR (nur verfügbar, wenn Claude Code den Hook-Befehl startet) verwenden, um auf in Ihrem Projekt gespeicherte Skripte zu verweisen, wodurch sichergestellt wird, dass sie unabhängig von Claudes aktuellem Verzeichnis funktionieren:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/check-style.sh"
          }
        ]
      }
    ]
  }
}

Hook-Events

PreToolUse

Läuft, nachdem Claude Tool-Parameter erstellt hat und bevor der Tool-Aufruf verarbeitet wird.

Häufige Matcher:

  • Task - Agent-Aufgaben
  • Bash - Shell-Befehle
  • Glob - Dateimuster-Abgleich
  • Grep - Inhaltssuche
  • Read - Datei lesen
  • Edit, MultiEdit - Datei bearbeiten
  • Write - Datei schreiben
  • WebFetch, WebSearch - Web-Operationen

PostToolUse

Läuft unmittelbar nach erfolgreichem Abschluss eines Tools.

Erkennt dieselben Matcher-Werte wie PreToolUse.

Notification

Läuft, wenn Claude Code Benachrichtigungen sendet. Benachrichtigungen werden gesendet, wenn:

  1. Claude Ihre Erlaubnis zur Verwendung eines Tools benötigt. Beispiel: “Claude needs your permission to use Bash”
  2. Die Prompt-Eingabe mindestens 60 Sekunden inaktiv war. “Claude is waiting for your input”

UserPromptSubmit

Läuft, wenn der Benutzer einen Prompt übermittelt, bevor Claude ihn verarbeitet. Dies ermöglicht es Ihnen, zusätzlichen Kontext basierend auf dem Prompt/der Unterhaltung hinzuzufügen, Prompts zu validieren oder bestimmte Arten von Prompts zu blockieren.

Stop

Läuft, wenn der Haupt-Claude Code Agent seine Antwort beendet hat. Läuft nicht, wenn der Stopp aufgrund einer Benutzerunterbrechung erfolgte.

SubagentStop

Läuft, wenn ein Claude Code Subagent (Task-Tool-Aufruf) seine Antwort beendet hat.

PreCompact

Läuft, bevor Claude Code eine Kompaktierungsoperation ausführen wird.

Matcher:

  • manual - Aufgerufen von /compact
  • auto - Aufgerufen von Auto-Kompaktierung (aufgrund vollem Kontextfenster)

Hook-Eingabe

Hooks erhalten JSON-Daten über stdin, die Sitzungsinformationen und ereignisspezifische Daten enthalten:

{
  // Gemeinsame Felder
  session_id: string
  transcript_path: string  // Pfad zur Unterhaltungs-JSON
  cwd: string              // Das aktuelle Arbeitsverzeichnis, wenn der Hook aufgerufen wird

  // Ereignisspezifische Felder
  hook_event_name: string
  ...
}

PreToolUse-Eingabe

Das genaue Schema für tool_input hängt vom Tool ab.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PreToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  }
}

PostToolUse-Eingabe

Das genaue Schema für tool_input und tool_response hängt vom Tool ab.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  },
  "tool_response": {
    "filePath": "/path/to/file.txt",
    "success": true
  }
}

Notification-Eingabe

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Notification",
  "message": "Task completed successfully"
}

UserPromptSubmit-Eingabe

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "Write a function to calculate the factorial of a number"
}

Stop- und SubagentStop-Eingabe

stop_hook_active ist true, wenn Claude Code bereits als Ergebnis eines Stop-Hooks fortfährt. Überprüfen Sie diesen Wert oder verarbeiten Sie das Transkript, um zu verhindern, dass Claude Code unendlich läuft.

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "hook_event_name": "Stop",
  "stop_hook_active": true
}

PreCompact-Eingabe

Für manual stammt custom_instructions von dem, was der Benutzer in /compact übergibt. Für auto ist custom_instructions leer.

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "hook_event_name": "PreCompact",
  "trigger": "manual",
  "custom_instructions": ""
}

Hook-Ausgabe

Es gibt zwei Möglichkeiten für Hooks, Ausgaben an Claude Code zurückzugeben. Die Ausgabe kommuniziert, ob blockiert werden soll und welches Feedback Claude und dem Benutzer gezeigt werden soll.

Einfach: Exit-Code

Hooks kommunizieren den Status über Exit-Codes, stdout und stderr:

  • Exit-Code 0: Erfolg. stdout wird dem Benutzer im Transkript-Modus gezeigt (CTRL-R), außer bei UserPromptSubmit, wo stdout zum Kontext hinzugefügt wird.
  • Exit-Code 2: Blockierender Fehler. stderr wird automatisch an Claude zurückgegeben, um es zu verarbeiten. Siehe Verhalten pro Hook-Event unten.
  • Andere Exit-Codes: Nicht-blockierender Fehler. stderr wird dem Benutzer gezeigt und die Ausführung wird fortgesetzt.

Erinnerung: Claude Code sieht stdout nicht, wenn der Exit-Code 0 ist, außer beim UserPromptSubmit-Hook, wo stdout als Kontext injiziert wird.

Exit-Code 2 Verhalten

Hook-EventVerhalten
PreToolUseBlockiert den Tool-Aufruf, zeigt stderr Claude
PostToolUseZeigt stderr Claude (Tool ist bereits gelaufen)
NotificationN/A, zeigt stderr nur dem Benutzer
UserPromptSubmitBlockiert Prompt-Verarbeitung, löscht Prompt, zeigt stderr nur dem Benutzer
StopBlockiert Stopp, zeigt stderr Claude
SubagentStopBlockiert Stopp, zeigt stderr Claude-Subagent
PreCompactN/A, zeigt stderr nur dem Benutzer

Erweitert: JSON-Ausgabe

Hooks können strukturiertes JSON in stdout für anspruchsvollere Kontrolle zurückgeben:

Gemeinsame JSON-Felder

Alle Hook-Typen können diese optionalen Felder enthalten:

{
  "continue": true, // Ob Claude nach Hook-Ausführung fortfahren soll (Standard: true)
  "stopReason": "string" // Nachricht, die gezeigt wird, wenn continue false ist
  "suppressOutput": true, // stdout vom Transkript-Modus verbergen (Standard: false)
}

Wenn continue false ist, stoppt Claude die Verarbeitung nach dem Ausführen der Hooks.

  • Für PreToolUse ist dies anders als "permissionDecision": "deny", was nur einen spezifischen Tool-Aufruf blockiert und automatisches Feedback an Claude liefert.
  • Für PostToolUse ist dies anders als "decision": "block", was automatisiertes Feedback an Claude liefert.
  • Für UserPromptSubmit verhindert dies, dass der Prompt verarbeitet wird.
  • Für Stop und SubagentStop hat dies Vorrang vor jeder "decision": "block"-Ausgabe.
  • In allen Fällen hat "continue" = false Vorrang vor jeder "decision": "block"-Ausgabe.

stopReason begleitet continue mit einem Grund, der dem Benutzer gezeigt wird, nicht Claude.

PreToolUse Entscheidungskontrolle

PreToolUse-Hooks können kontrollieren, ob ein Tool-Aufruf fortgesetzt wird.

  • "allow" umgeht das Berechtigungssystem. permissionDecisionReason wird dem Benutzer gezeigt, aber nicht Claude. (Veralteter "approve"-Wert + reason hat dasselbe Verhalten.)
  • "deny" verhindert, dass der Tool-Aufruf ausgeführt wird. permissionDecisionReason wird Claude gezeigt. ("block"-Wert + reason hat dasselbe Verhalten.)
  • "ask" bittet den Benutzer, den Tool-Aufruf in der UI zu bestätigen. permissionDecisionReason wird dem Benutzer gezeigt, aber nicht Claude.
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow" | "deny" | "ask",
    "permissionDecisionReason": "My reason here (shown to user)"
  },
  "decision": "approve" | "block" | undefined, // Veraltet für PreToolUse, aber noch unterstützt
  "reason": "Explanation for decision" // Veraltet für PreToolUse, aber noch unterstützt
}

PostToolUse Entscheidungskontrolle

PostToolUse-Hooks können kontrollieren, ob ein Tool-Aufruf fortgesetzt wird.

  • "block" fordert Claude automatisch mit reason auf.
  • undefined tut nichts. reason wird ignoriert.
{
  "decision": "block" | undefined,
  "reason": "Explanation for decision"
}

UserPromptSubmit Entscheidungskontrolle

UserPromptSubmit-Hooks können kontrollieren, ob ein Benutzer-Prompt verarbeitet wird.

  • "block" verhindert, dass der Prompt verarbeitet wird. Der übermittelte Prompt wird aus dem Kontext gelöscht. "reason" wird dem Benutzer gezeigt, aber nicht zum Kontext hinzugefügt.
  • undefined erlaubt dem Prompt, normal fortzufahren. "reason" wird ignoriert.
  • "hookSpecificOutput.additionalContext" fügt den String zum Kontext hinzu, wenn nicht blockiert.
{
  "decision": "block" | undefined,
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "My additional context here"
  }
}

Stop/SubagentStop Entscheidungskontrolle

Stop- und SubagentStop-Hooks können kontrollieren, ob Claude fortfahren muss.

  • "block" verhindert, dass Claude stoppt. Sie müssen reason ausfüllen, damit Claude weiß, wie es weitergehen soll.
  • undefined erlaubt Claude zu stoppen. reason wird ignoriert.
{
  "decision": "block" | undefined,
  "reason": "Must be provided when Claude is blocked from stopping"
}

Exit-Code-Beispiel: Bash-Befehl-Validierung

#!/usr/bin/env python3
import json
import re
import sys

# Definiere Validierungsregeln als Liste von (Regex-Muster, Nachricht) Tupeln
VALIDATION_RULES = [
    (
        r"\bgrep\b(?!.*\|)",
        "Use 'rg' (ripgrep) instead of 'grep' for better performance and features",
    ),
    (
        r"\bfind\s+\S+\s+-name\b",
        "Use 'rg --files | rg pattern' or 'rg --files -g pattern' instead of 'find -name' for better performance",
    ),
]


def validate_command(command: str) -> list[str]:
    issues = []
    for pattern, message in VALIDATION_RULES:
        if re.search(pattern, command):
            issues.append(message)
    return issues


try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)

tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
command = tool_input.get("command", "")

if tool_name != "Bash" or not command:
    sys.exit(1)

# Validiere den Befehl
issues = validate_command(command)

if issues:
    for message in issues:
        print(f"• {message}", file=sys.stderr)
    # Exit-Code 2 blockiert Tool-Aufruf und zeigt stderr Claude
    sys.exit(2)

JSON-Ausgabe-Beispiel: UserPromptSubmit zum Hinzufügen von Kontext und Validierung

Für UserPromptSubmit-Hooks können Sie Kontext mit beiden Methoden injizieren:

  • Exit-Code 0 mit stdout: Claude sieht den Kontext (Sonderfall für UserPromptSubmit)
  • JSON-Ausgabe: Bietet mehr Kontrolle über das Verhalten
#!/usr/bin/env python3
import json
import sys
import re
import datetime

# Lade Eingabe von stdin
try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)

prompt = input_data.get("prompt", "")

# Prüfe auf sensible Muster
sensitive_patterns = [
    (r"(?i)\b(password|secret|key|token)\s*[:=]", "Prompt contains potential secrets"),
]

for pattern, message in sensitive_patterns:
    if re.search(pattern, prompt):
        # Verwende JSON-Ausgabe zum Blockieren mit einem spezifischen Grund
        output = {
            "decision": "block",
            "reason": f"Security policy violation: {message}. Please rephrase your request without sensitive information."
        }
        print(json.dumps(output))
        sys.exit(0)

# Füge aktuelle Zeit zum Kontext hinzu
context = f"Current time: {datetime.datetime.now()}"
print(context)

"""
Das Folgende ist auch äquivalent:
print(json.dumps({
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": context,
  },
}))
"""

# Erlaube dem Prompt, mit dem zusätzlichen Kontext fortzufahren
sys.exit(0)

JSON-Ausgabe-Beispiel: PreToolUse mit Genehmigung

#!/usr/bin/env python3
import json
import sys

# Lade Eingabe von stdin
try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)

tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})

# Beispiel: Auto-Genehmigung von Datei-Lesevorgängen für Dokumentationsdateien
if tool_name == "Read":
    file_path = tool_input.get("file_path", "")
    if file_path.endswith((".md", ".mdx", ".txt", ".json")):
        # Verwende JSON-Ausgabe zur automatischen Genehmigung des Tool-Aufrufs
        output = {
            "decision": "approve",
            "reason": "Documentation file auto-approved",
            "suppressOutput": True  # Nicht im Transkript-Modus anzeigen
        }
        print(json.dumps(output))
        sys.exit(0)

# Für andere Fälle, lasse den normalen Berechtigungsfluss fortfahren
sys.exit(0)

Arbeiten mit MCP-Tools

Claude Code Hooks funktionieren nahtlos mit Model Context Protocol (MCP) Tools. Wenn MCP-Server Tools bereitstellen, erscheinen sie mit einem speziellen Benennungsmuster, das Sie in Ihren Hooks abgleichen können.

MCP-Tool-Benennung

MCP-Tools folgen dem Muster mcp__<server>__<tool>, zum Beispiel:

  • mcp__memory__create_entities - Memory-Server’s create entities Tool
  • mcp__filesystem__read_file - Filesystem-Server’s read file Tool
  • mcp__github__search_repositories - GitHub-Server’s search Tool

Konfigurieren von Hooks für MCP-Tools

Sie können spezifische MCP-Tools oder ganze MCP-Server ansprechen:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
          }
        ]
      },
      {
        "matcher": "mcp__.*__write.*",
        "hooks": [
          {
            "type": "command",
            "command": "/home/user/scripts/validate-mcp-write.py"
          }
        ]
      }
    ]
  }
}

Beispiele

Für praktische Beispiele einschließlich Code-Formatierung, Benachrichtigungen und Dateischutz, siehe Weitere Beispiele in der Erste-Schritte-Anleitung.

Sicherheitsüberlegungen

Haftungsausschluss

VERWENDUNG AUF EIGENE GEFAHR: Claude Code Hooks führen automatisch beliebige Shell-Befehle auf Ihrem System aus. Durch die Verwendung von Hooks erkennen Sie an, dass:

  • Sie allein für die von Ihnen konfigurierten Befehle verantwortlich sind
  • Hooks alle Dateien ändern, löschen oder darauf zugreifen können, auf die Ihr Benutzerkonto zugreifen kann
  • Bösartige oder schlecht geschriebene Hooks Datenverlust oder Systemschäden verursachen können
  • Anthropic keine Gewährleistung bietet und keine Haftung für Schäden übernimmt, die aus der Hook-Verwendung resultieren
  • Sie Hooks gründlich in einer sicheren Umgebung testen sollten, bevor Sie sie produktiv einsetzen

Überprüfen und verstehen Sie immer alle Hook-Befehle, bevor Sie sie zu Ihrer Konfiguration hinzufügen.

Sicherheits-Best-Practices

Hier sind einige wichtige Praktiken für das Schreiben sichererer Hooks:

  1. Eingaben validieren und bereinigen - Vertrauen Sie niemals blind auf Eingabedaten
  2. Shell-Variablen immer in Anführungszeichen setzen - Verwenden Sie "$VAR" nicht $VAR
  3. Pfad-Traversierung blockieren - Prüfen Sie auf .. in Dateipfaden
  4. Absolute Pfade verwenden - Geben Sie vollständige Pfade für Skripte an (verwenden Sie $CLAUDE_PROJECT_DIR für den Projektpfad)
  5. Sensible Dateien überspringen - Vermeiden Sie .env, .git/, Schlüssel, etc.

Konfigurationssicherheit

Direkte Bearbeitungen von Hooks in Einstellungsdateien werden nicht sofort wirksam. Claude Code:

  1. Erfasst einen Snapshot von Hooks beim Start
  2. Verwendet diesen Snapshot während der gesamten Sitzung
  3. Warnt, wenn Hooks extern geändert werden
  4. Erfordert Überprüfung im /hooks-Menü, damit Änderungen wirksam werden

Dies verhindert, dass bösartige Hook-Änderungen Ihre aktuelle Sitzung beeinträchtigen.

Hook-Ausführungsdetails

  • Timeout: 60-Sekunden-Ausführungslimit standardmäßig, pro Befehl konfigurierbar.
    • Ein Timeout für einen einzelnen Befehl beeinflusst nicht die anderen Befehle.
  • Parallelisierung: Alle passenden Hooks laufen parallel
  • Umgebung: Läuft im aktuellen Verzeichnis mit Claude Codes Umgebung
    • Die Umgebungsvariable CLAUDE_PROJECT_DIR ist verfügbar und enthält den absoluten Pfad zum Projekt-Stammverzeichnis
  • Eingabe: JSON über stdin
  • Ausgabe:
    • PreToolUse/PostToolUse/Stop: Fortschritt im Transkript gezeigt (Ctrl-R)
    • Notification: Nur in Debug protokolliert (--debug)

Debugging

Grundlegende Fehlerbehebung

Wenn Ihre Hooks nicht funktionieren:

  1. Konfiguration prüfen - Führen Sie /hooks aus, um zu sehen, ob Ihr Hook registriert ist
  2. Syntax verifizieren - Stellen Sie sicher, dass Ihre JSON-Einstellungen gültig sind
  3. Befehle testen - Führen Sie Hook-Befehle zuerst manuell aus
  4. Berechtigungen prüfen - Stellen Sie sicher, dass Skripte ausführbar sind
  5. Logs überprüfen - Verwenden Sie claude --debug, um Hook-Ausführungsdetails zu sehen

Häufige Probleme:

  • Anführungszeichen nicht escaped - Verwenden Sie \" innerhalb von JSON-Strings
  • Falscher Matcher - Prüfen Sie, ob Tool-Namen exakt übereinstimmen (groß-/kleinschreibungsabhängig)
  • Befehl nicht gefunden - Verwenden Sie vollständige Pfade für Skripte

Erweiterte Fehlerbehebung

Für komplexe Hook-Probleme:

  1. Hook-Ausführung inspizieren - Verwenden Sie claude --debug, um detaillierte Hook- Ausführung zu sehen
  2. JSON-Schemas validieren - Testen Sie Hook-Ein-/Ausgabe mit externen Tools
  3. Umgebungsvariablen prüfen - Verifizieren Sie, dass Claude Codes Umgebung korrekt ist
  4. Grenzfälle testen - Probieren Sie Hooks mit ungewöhnlichen Dateipfaden oder Eingaben
  5. Systemressourcen überwachen - Prüfen Sie auf Ressourcenerschöpfung während Hook- Ausführung
  6. Strukturierte Protokollierung verwenden - Implementieren Sie Protokollierung in Ihren Hook-Skripten

Debug-Ausgabe-Beispiel

Verwenden Sie claude --debug, um Hook-Ausführungsdetails zu sehen:

[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 60000ms
[DEBUG] Hook command completed with status 0: <Your stdout>

Fortschrittsnachrichten erscheinen im Transkript-Modus (Ctrl-R) und zeigen:

  • Welcher Hook läuft
  • Ausgeführter Befehl
  • Erfolg-/Fehlerstatus
  • Ausgabe- oder Fehlermeldungen