Alle Artikel

Claude Code Hooks: So blocken Sie riskante Befehle

Claude Code Hooks sind Befehle, die an festen Punkten einer Session ausgeführt werden: bevor ein Tool läuft, nachdem es gelaufen ist, wenn ein Prompt gesendet wird, wenn die Session endet. Sie sind der Weg, aus „der Agent soll das nicht tun“ etwas zu machen, das tatsächlich geprüft wird. Dieser Artikel zeigt Claude-Code-Hooks-Beispiele, die blockieren, was blockiert werden muss, wie man sie testet und welche Fehler hinter den meisten Suchanfragen zu „hook error“ stecken.

Alles im Folgenden orientiert sich an der offiziellen Hooks-Referenz und der Permissions-Dokumentation. Wir setzen Claude Code mit Hooks täglich in unserem eigenen Studio ein; einer davon ist ein Stop-Hook, der warnt, wenn die Token-Nutzung einer Session ein Limit überschreitet.

Wo Claude Code Hooks konfiguriert werden

Hooks leben in Settings-Dateien:

Datei Gilt für Geteilt
~/.claude/settings.json alle Ihre Projekte nein
.claude/settings.json ein Projekt ja, kann eingecheckt werden
.claude/settings.local.json ein Projekt nein

Einträge aus diesen Dateien werden zusammengeführt, nicht ersetzt. Auch Plugins, Skills und Subagents können eigene Hooks mitbringen.

Ein Hook-Eintrag hat drei Ebenen: das Event, eine Matcher-Gruppe und die Handler:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "python3 ${CLAUDE_PROJECT_DIR}/.claude/hooks/block_rm.py" }
        ]
      }
    ]
  }
}

CLAUDE_PROJECT_DIR ist der Projekt-Root, in dem die Session gestartet wurde. Rufen Sie Python unter Windows so auf, wie es Ihr System erwartet.

Ein PreToolUse-Hook, der einen riskanten Befehl blockiert

Ein PreToolUse-Hook erhält den geplanten Tool-Aufruf als JSON auf der Standard-Eingabe: tool_name, tool_input und einige Session-Felder. Für Bash ist tool_input.command der Befehlstext.

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

data = json.load(sys.stdin)
cmd = data.get("tool_input", {}).get("command", "")

if re.search(r"\brm\s+-\w*(rf|fr)\w*\b", cmd):
    print("Blocked: recursive force delete. Ask the user to run it.", file=sys.stderr)
    sys.exit(2)
sys.exit(0)

Die entscheidende Regel: Exit-Code 2 blockiert den Aufruf, und stderr wird zur Begründung, die Claude sieht. Exit 0 lässt ihn durch. Jeder andere Code ist ein nicht blockierender Fehler: Die Aktion wird ausgeführt, und Sie erhalten nur einen Hinweis.

Ein Textmuster erkennt die übliche Form eines Befehls, aber nicht jede Form: Dieses hier stoppt rm -rf und rm -fr, aber rm -r -f mit getrennten Flags kommt durch. Deshalb braucht jede Regel Tests, und deshalb gibt es neben Hooks auch Permission-Regeln und die Sandbox.

Wenn Sie strukturierte Ausgaben bevorzugen: Ein Hook kann mit Exit 0 enden und JSON mit hookSpecificOutput ausgeben, wobei hookEventName PreToolUse ist und permissionDecision allow, deny, ask oder defer lautet, zusammen mit einem permissionDecisionReason.

Der PreToolUse-Matcher, ohne Überraschungen

Der Matcher filtert nach dem Tool-Namen:

  • Bash oder eine Liste wie Edit|Write — exakte Namen.
  • Alles mit anderen Zeichen ist ein regulärer Ausdruck, nicht verankert: ^Notebook, mcp__memory__.*.
  • MCP-Tools heißen mcp__<server>__<tool>. Ein Matcher wie mcp__memory allein trifft auf nichts zu; Sie brauchen mcp__memory__.*.
  • *, ein leerer String oder kein Matcher bedeutet jedes Tool.

Es gibt außerdem ein if-Feld mit der Syntax der Permission-Regeln, etwa Bash(git *). Die Dokumentation nennt es best-effort: Für ein hartes allow oder deny nutzen Sie das Permission-System.

Testen Sie jeden Hook: ein Aufruf blockiert, ein Aufruf erlaubt

Ein Hook, den Sie nie getestet haben, ist ein Hook, von dem Sie hoffen, dass er funktioniert. Der günstigste Test führt ihm dasselbe JSON zu, das Claude Code senden würde, und prüft den Exit-Code:

echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf build"}}' | python3 .claude/hooks/block_rm.py; echo "exit $?"   # expect 2
echo '{"tool_name":"Bash","tool_input":{"command":"ls build"}}'    | python3 .claude/hooks/block_rm.py; echo "exit $?"   # expect 0

Unsere Regel für jeden Hook: eine Aktion, die blockiert werden muss, und eine, die durchgehen muss — beide in der Testdatei. Der zweite Test findet den anderen häufigen Fehler: ein Muster, so breit, dass es normale Arbeit blockiert.

Warum ein PreToolUse-Hook-Fehler nichts stoppt

Die üblichen Ursachen, alle aus der Referenz:

  1. Exit-Code 1 statt 2. Es erscheint ein Fehlerhinweis, und das Tool läuft trotzdem.
  2. Timeout. Ein PreToolUse-Command-Hook, der in einen Timeout läuft, blockiert nicht; der Aufruf geht weiter durch den normalen Permission-Ablauf. Command-Hooks haben standardmäßig 600 Sekunden.
  3. Ein Matcher, der nie trifft, wie mcp__memory ohne .*.
  4. Ein fehlerhaftes Skript. Standardmäßig blockiert ein fehlschlagender Hook nicht. Neuere Versionen unterstützen "onFailure": "block" (v2.1.295 oder später), um zu blockieren, wenn ein Command-Hook fehlschlägt, in einen Timeout läuft oder eine ungültige Ausgabe liefert.

Hooks, Permission-Regeln und Skills: wer was tut

  • Permission-Regeln (allow, ask, deny in settings.json) werden in dieser Reihenfolge geprüft: deny, dann ask, dann allow. Eine Deny-Regel greift sogar dann, wenn ein PreToolUse-Hook allow zurückgibt.
  • Ein blockierender Hook (Exit 2) stoppt den Aufruf, bevor die Permission-Regeln ausgewertet werden, setzt sich also auch gegen eine Allow-Regel durch.
  • Skills sind Anweisungen und Ressourcen, die der Agent bei Bedarf lädt. Sie lenken; sie erzwingen nicht.

Beginnen Sie bei vertraulichen Ordnern mit Deny-Regeln wie Read(./secrets/**). Kennen Sie ihre Grenze: Sie decken die integrierten Datei-Tools und Befehle wie cat, head und tail ab, aber nicht grep -r aus dem Ordner oder ein Skript, das selbst Dateien öffnet. Ein PreToolUse-Hook kann in solchen Fällen den vollständigen Befehlstext prüfen, und für Durchsetzung auf Betriebssystemebene verweist die Dokumentation auf die Sandbox.

Wann Sie es abgeben sollten

Wenn Ihr Agent-Setup schnell gewachsen ist und Sie nicht sicher sind, welche Regeln tatsächlich greifen, kartieren wir die Agents, schreiben die Hook-Regeln mit Tests und zeigen den Testlauf, nicht nur die Konfiguration. Wie unser eigenes System aufgebaut ist, sehen Sie auf der Work-Seite.

Fragen

Warum blockiert mein PreToolUse-Hook nichts?

Meistens beendet er sich mit Exit-Code 1. Nur Exit-Code 2 blockiert den Tool-Aufruf; jeder andere Code ungleich null ist ein nicht blockierender Fehler, und die Aktion wird ausgeführt. Auch ein Hook, der in einen Timeout läuft, blockiert nicht.

Brauche ich hooks, wenn ich bereits Permission-Deny-Regeln habe?

Für einfache Regeln haben Deny-Regeln Vorrang: Sie werden sogar dann geprüft, wenn ein Hook allow zurückgibt. Hooks sind für Logik, die ein Muster nicht ausdrücken kann, etwa die Prüfung des gesamten Befehlstexts vor der Ausführung.

Stoppen Read-Deny-Regeln auch cat und grep in Bash?

Sie decken die integrierten Datei-Tools und die Datei-Befehle ab, die Claude Code in Bash erkennt, etwa cat, head und tail. Nicht abgedeckt ist ein Befehl, der Dateien liest, ohne sie zu nennen, wie grep -r aus dem Ordner, oder ein Skript, das selbst Dateien öffnet. Für solche Fälle verweist die Dokumentation auf die Sandbox.

Wo werden Claude Code Hooks konfiguriert?

In Settings-Dateien: ~/.claude/settings.json für alle Ihre Projekte, .claude/settings.json für ein Projekt und Ihr Team oder .claude/settings.local.json nur für Sie selbst. Einträge aus mehreren Dateien werden zusammengeführt.

Kontakt

Dieses Postfach liest eine Person — dieselbe, die baut, was Sie bestellen.

Beschreiben Sie Ihre Aufgabe

Schreiben Sie, was Sie haben und welches Ergebnis Sie brauchen — auch wenn es nur eine kleine Aufgabe ist. Kein Lastenheft nötig: innerhalb von 48 Stunden erhalten Sie Plan und Preis schriftlich, eine Rückfrage oder ein ehrliches „passt nicht“.

Nennen Sie Situation, Dateien oder Links, Termin, Sprache, Datenschutzgrenzen und Budgetrahmen, falls vorhanden.

Datei (optional)
Keine Datei ausgewählt

Ein Briefing, ein Foto, eine Tabelle — was die Aufgabe schneller erklärt als Text. Bis 8 MB, gespeichert bei unserem Hosting-Anbieter. Bitte senden Sie keine sensiblen persönlichen oder medizinischen Unterlagen über die Website.

Das Absenden erzeugt keinen Auftrag und keine Zahlung. Antwort innerhalb von 48 Stunden. Lieber per E-Mail? contact@castel.studio

Sprechen Sie mit uns

Lieber per Telefon oder Messenger? Schreiben oder rufen Sie an — es antwortet der Gründer des Studios.

Auf LinkedIn vernetzen

Telefon · WhatsApp · Telegram

Frankreich+33 7 49 57 41 69WhatsAppTelegram

Ukraine+38 063 95 77 111WhatsAppTelegram

Lieber über eine Plattform?

Bestellen Sie dieselbe Arbeit dort, mit der sicheren Zahlung der Plattform.

Ein-Personen-KI-Studio in Nizza. Wer auf Ihre Nachricht antwortet, macht auch die Arbeit — keine Account-Manager dazwischen. Dateien werden auf unserer eigenen Maschine verarbeitet, nicht in fremder Cloud.

Nizza 06100, Frankreich · In Karten öffnen →

SIREN 103 115 705