{
"event": "PreToolUse",
"tool_name": "mcp__mcp-docs__docs_update",
"tool_input": {
"id": 141,
"content": "<h2>Task-Completion Guard Hook<\/h2>\n\n<p>Pre-Hook der verhindert, dass Tasks als \"completed\" markiert werden, ohne dass vorher ein Ergebnis via <code>tasks_result()<\/code> geschrieben wurde.<\/p>\n\n<h3>Problem<\/h3>\n<p>Claude ruft <code>tasks_status(id, \"completed\")<\/code> auf, ohne vorher <code>tasks_result()<\/code> zu rufen. Dies führt zu 77+ Fehlern pro Woche mit der Meldung:<\/p>\n<pre><code>Task kann nicht abgeschlossen werden. Fehlend: Ergebnis (tasks_result)<\/code><\/pre>\n\n<h3>Lösung<\/h3>\n<p>Ein PreToolUse-Hook der VOR dem MCP-Call prüft, ob ein Result existiert und bei Fehlen die Aktion blockiert.<\/p>\n\n<table>\n <tr><th>Datei<\/th><td><code>\/var\/www\/tools\/ki-protokoll\/claude-hook\/task_completion_guard.py<\/code><\/td><\/tr>\n <tr><th>Trigger<\/th><td><code>PreToolUse<\/code> mit Matcher <code>mcp__mcp-tasks__tasks_status<\/code><\/td><\/tr>\n <tr><th>Exit-Codes<\/th><td>0 = allow, 2 = block<\/td><\/tr>\n<\/table>\n\n<h3>Technische Details<\/h3>\n\n<h4>1. Input-Format (stdin)<\/h4>\n<pre><code>{\n \"tool_name\": \"mcp__mcp-tasks__tasks_status\",\n \"tool_input\": {\n \"id\": 425,\n \"status\": \"completed\"\n }\n}<\/code><\/pre>\n\n<h4>2. Prüflogik<\/h4>\n<ol>\n <li>Parse JSON von stdin<\/li>\n <li>Prüfe: <code>tool_name == \"mcp__mcp-tasks__tasks_status\"<\/code><\/li>\n <li>Prüfe: <code>status == \"completed\"<\/code><\/li>\n <li>Wenn ja: DB-Abfrage <code>SELECT COUNT(*) FROM task_results WHERE task_id = ?<\/code><\/li>\n <li>Wenn count == 0: Block mit Exit-Code 2 + stderr-Nachricht<\/li>\n<\/ol>\n\n<h4>3. Block-Nachricht<\/h4>\n<pre><code>BLOCKIERT: Task kann nicht als 'completed' markiert werden!\n\nGRUND: Kein Ergebnis vorhanden (tasks_result fehlt).\n\nLÖSUNG: Vor tasks_status(id, \"completed\") MUSS tasks_result() aufgerufen werden:\n\n tasks_result(\n id=TASK_ID,\n response=\"Zusammenfassung der erledigten Arbeit...\",\n executor=\"claude\",\n executor_type=\"claude\"\n )\n\nErst danach: tasks_status(id, \"completed\")<\/code><\/pre>\n\n<h4>4. Konfiguration in settings.json<\/h4>\n<pre><code>{\n \"PreToolUse\": [\n {\n \"matcher\": \"mcp__mcp-tasks__tasks_status\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"\/var\/www\/tools\/ki-protokoll\/claude-hook\/task_completion_guard.py\",\n \"timeout\": 5\n }\n ]\n }\n ]\n}<\/code><\/pre>\n\n<h3>Ablauf<\/h3>\n<pre>\n┌─────────────────────────────┐\n│ Claude: tasks_status( │\n│ id=425, status=\"completed\"│\n│ ) │\n└─────────────┬───────────────┘\n │\n ▼\n┌─────────────────────────────┐\n│ PreToolUse Hook auslösen │\n│ Matcher: mcp__mcp-tasks__ │\n│ tasks_status │\n└─────────────┬───────────────┘\n │\n ▼\n┌─────────────────────────────┐\n│ task_completion_guard.py │\n│ - Parse Input │\n│ - Check: status==\"completed\"│\n└─────────────┬───────────────┘\n │\n ┌──────┴──────┐\n │ │\n ▼ ▼\n┌─────────────┐ ┌─────────────┐\n│ status != │ │ status == │\n│ \"completed\" │ │ \"completed\" │\n└──────┬──────┘ └──────┬──────┘\n │ │\n ▼ ▼\n┌─────────────┐ ┌─────────────────┐\n│ exit(0) │ │ DB-Check: │\n│ → Allow │ │ task_results │\n└─────────────┘ │ WHERE task_id=? │\n └────────┬────────┘\n │\n ┌──────┴──────┐\n │ │\n ▼ ▼\n ┌──────────┐ ┌──────────┐\n │ count > 0│ │ count = 0│\n └────┬─────┘ └────┬─────┘\n │ │\n ▼ ▼\n ┌─────────┐ ┌─────────────┐\n │ exit(0) │ │ stderr: │\n │ → Allow │ │ BLOCKIERT │\n └─────────┘ │ exit(2) │\n │ → Block │\n └─────────────┘\n<\/pre>\n\n<h3>Implementierungsplan<\/h3>\n\n<h4>Schritt 1: Hook-Script erstellen<\/h4>\n<p>Datei: <code>\/var\/www\/tools\/ki-protokoll\/claude-hook\/task_completion_guard.py<\/code><\/p>\n<pre><code>#!\/usr\/bin\/env python3\n\"\"\"\nTask Completion Guard Hook\n\nBlockiert tasks_status(completed) wenn kein Result existiert.\nErzwingt korrekten Workflow: tasks_result() vor tasks_status(completed).\n\nTrigger: PreToolUse (mcp__mcp-tasks__tasks_status)\n\"\"\"\n\nimport json\nimport sys\nimport os\nfrom pathlib import Path\nfrom dotenv import load_dotenv\nimport pymysql\n\n# Lade Environment aus .env im Hook-Verzeichnis\nload_dotenv(Path(__file__).parent \/ '.env')\n\nDB_CONFIG = {\n 'host': os.environ.get('CLAUDE_DB_HOST', 'localhost'),\n 'port': int(os.environ.get('CLAUDE_DB_PORT', '3306')),\n 'user': os.environ.get('CLAUDE_DB_USER', 'root'),\n 'password': os.environ.get('CLAUDE_DB_PASSWORD', ''),\n 'database': 'ki_dev',\n 'charset': 'utf8mb4'\n}\n\nBLOCK_MESSAGE = \"\"\"BLOCKIERT: Task kann nicht als 'completed' markiert werden!\n\nGRUND: Kein Ergebnis vorhanden (tasks_result fehlt).\n\nLÖSUNG: Vor tasks_status(id, \"completed\") MUSS tasks_result() aufgerufen werden:\n\n tasks_result(\n id={task_id},\n response=\"Zusammenfassung der erledigten Arbeit...\",\n executor=\"claude\",\n executor_type=\"claude\"\n )\n\nErst danach: tasks_status({task_id}, \"completed\")\"\"\"\n\n\ndef check_result_exists(task_id: int) -> bool:\n \"\"\"Prüft ob ein Result für den Task existiert.\"\"\"\n try:\n conn = pymysql.connect(**DB_CONFIG)\n cursor = conn.cursor()\n cursor.execute(\n \"SELECT COUNT(*) FROM task_results WHERE task_id = %s\",\n (task_id,)\n )\n count = cursor.fetchone()[0]\n conn.close()\n return count > 0\n except Exception as e:\n # Bei DB-Fehler durchlassen (fail-open)\n print(f\"DB-Check fehlgeschlagen: {e}\", file=sys.stderr)\n return True\n\n\ndef main():\n try:\n input_data = json.load(sys.stdin)\n except json.JSONDecodeError:\n sys.exit(0)\n \n # Nur mcp__mcp-tasks__tasks_status prüfen\n tool_name = input_data.get(\"tool_name\", \"\")\n if tool_name != \"mcp__mcp-tasks__tasks_status\":\n sys.exit(0)\n \n # Prüfe ob Status auf \"completed\" gesetzt wird\n tool_input = input_data.get(\"tool_input\", {})\n status = tool_input.get(\"status\", \"\")\n task_id = tool_input.get(\"id\")\n \n if status.lower() != \"completed\":\n sys.exit(0)\n \n if not task_id:\n sys.exit(0)\n \n # Prüfe ob Result existiert\n if not check_result_exists(task_id):\n print(BLOCK_MESSAGE.format(task_id=task_id), file=sys.stderr)\n sys.exit(2)\n \n sys.exit(0)\n\n\nif __name__ == \"__main__\":\n main()\n<\/code><\/pre>\n\n<h4>Schritt 2: Script ausführbar machen<\/h4>\n<pre><code>chmod +x \/var\/www\/tools\/ki-protokoll\/claude-hook\/task_completion_guard.py<\/code><\/pre>\n\n<h4>Schritt 3: settings.json erweitern<\/h4>\n<p>Datei: <code>\/root\/.claude\/settings.json<\/code><\/p>\n<p>Füge folgenden Eintrag in \"PreToolUse\" Array hinzu:<\/p>\n<pre><code>{\n \"matcher\": \"mcp__mcp-tasks__tasks_status\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"\/var\/www\/tools\/ki-protokoll\/claude-hook\/task_completion_guard.py\",\n \"timeout\": 5\n }\n ]\n}<\/code><\/pre>\n\n<h4>Schritt 4: Testen<\/h4>\n<pre><code># Test 1: Task ohne Result → sollte blockiert werden (Exit 2)\necho '{\"tool_name\":\"mcp__mcp-tasks__tasks_status\",\"tool_input\":{\"id\":999,\"status\":\"completed\"}}' | \\\n \/var\/www\/tools\/ki-protokoll\/claude-hook\/task_completion_guard.py\necho \"Exit code: $?\"\n\n# Test 2: Anderer Status → sollte durchgelassen werden (Exit 0)\necho '{\"tool_name\":\"mcp__mcp-tasks__tasks_status\",\"tool_input\":{\"id\":999,\"status\":\"in_progress\"}}' | \\\n \/var\/www\/tools\/ki-protokoll\/claude-hook\/task_completion_guard.py\necho \"Exit code: $?\"\n\n# Test 3: Anderes Tool → sollte durchgelassen werden (Exit 0)\necho '{\"tool_name\":\"Bash\",\"tool_input\":{\"command\":\"ls\"}}' | \\\n \/var\/www\/tools\/ki-protokoll\/claude-hook\/task_completion_guard.py\necho \"Exit code: $?\"\n<\/code><\/pre>\n\n<h3>Voraussetzungen<\/h3>\n<ul>\n <li>Python 3.x<\/li>\n <li>PyMySQL (bereits installiert: <code>pip3 install pymysql<\/code>)<\/li>\n <li>python-dotenv (bereits installiert)<\/li>\n <li>Zugriff auf ki_dev.task_results<\/li>\n <li>.env Datei im Hook-Verzeichnis mit DB-Credentials<\/li>\n<\/ul>\n\n<h3>Fail-Safe Verhalten<\/h3>\n<p>Bei DB-Verbindungsfehlern wird der Hook \"fail-open\" - d.h. die Aktion wird durchgelassen, um Claude nicht zu blockieren. Die Server-seitige Validierung in MCP-Tasks greift dann als zweite Verteidigung.<\/p>\n\n<h3>Validierung<\/h3>\n<table>\n <tr><th>Prüfpunkt<\/th><th>Status<\/th><\/tr>\n <tr><td>PyMySQL verfügbar<\/td><td>✓ PyMySQL 1.1.2 installiert<\/td><\/tr>\n <tr><td>python-dotenv Pattern<\/td><td>✓ Konsistent mit log_storage.py<\/td><\/tr>\n <tr><td>DB_CONFIG Pattern<\/td><td>✓ Identisch zu bestehenden Hooks<\/td><\/tr>\n <tr><td>Exit-Code Pattern<\/td><td>✓ 0=allow, 2=block (wie block_direct_db.py)<\/td><\/tr>\n <tr><td>Matcher-Syntax<\/td><td>✓ Regex-kompatibel für MCP-Tools<\/td><\/tr>\n <tr><td>Fail-Open Design<\/td><td>✓ Bei Fehler durchlassen<\/td><\/tr>\n<\/table>\n"
}
}
{
"tool_response": [
{
"type": "text",
"text": "{\n \"success\": true,\n \"doc\": {\n \"id\": 141,\n \"parent_id\": 92,\n \"slug\": \"task-completion-guard\",\n \"path\": \"\/prozesse\/claude-hooks-prozess\/task-completion-guard\",\n \"title\": \"Task-Completion Guard Hook\",\n \"description\": \"Pre-Hook zur Validierung von Task-Completion - verhindert tasks_status(completed) ohne vorheriges tasks_result()\",\n \"content\": \"<h2>Task-Completion Guard Hook<\/h2>\\n\\n<p>Pre-Hook der verhindert, dass Tasks als \\\"completed\\\" markiert werden, ohne dass vorher ein Ergebnis via <code>tasks_result()<\/code> geschrieben wurde.<\/p>\\n\\n<h3>Problem<\/h3>\\n<p>Claude ruft <code>tasks_status(id, \\\"completed\\\")<\/code> auf, ohne vorher <code>tasks_result()<\/code> zu rufen. Dies führt zu 77+ Fehlern pro Woche mit der Meldung:<\/p>\\n<pre><code>Task kann nicht abgeschlossen werden. Fehlend: Ergebnis (tasks_result)<\/code><\/pre>\\n\\n<h3>Lösung<\/h3>\\n<p>Ein PreToolUse-Hook der VOR dem MCP-Call prüft, ob ein Result existiert und bei Fehlen die Aktion blockiert.<\/p>\\n\\n<table>\\n <tr><th>Datei<\/th><td><code>\/var\/www\/tools\/ki-protokoll\/claude-hook\/task_completion_guard.py<\/code><\/td><\/tr>\\n <tr><th>Trigger<\/th><td><code>PreToolUse<\/code> mit Matcher <code>mcp__mcp-tasks__tasks_status<\/code><\/td><\/tr>\\n <tr><th>Exit-Codes<\/th><td>0 = allow, 2 = block<\/td><\/tr>\\n<\/table>\\n\\n<h3>Technische Details<\/h3>\\n\\n<h4>1. Input-Format (stdin)<\/h4>\\n<pre><code>{\\n \\\"tool_name\\\": \\\"mcp__mcp-tasks__tasks_status\\\",\\n \\\"tool_input\\\": {\\n \\\"id\\\": 425,\\n \\\"status\\\": \\\"completed\\\"\\n }\\n}<\/code><\/pre>\\n\\n<h4>2. Prüflogik<\/h4>\\n<ol>\\n <li>Parse JSON von stdin<\/li>\\n <li>Prüfe: <code>tool_name == \\\"mcp__mcp-tasks__tasks_status\\\"<\/code><\/li>\\n <li>Prüfe: <code>status == \\\"completed\\\"<\/code><\/li>\\n <li>Wenn ja: DB-Abfrage <code>SELECT COUNT(*) FROM task_results WHERE task_id = ?<\/code><\/li>\\n <li>Wenn count == 0: Block mit Exit-Code 2 + stderr-Nachricht<\/li>\\n<\/ol>\\n\\n<h4>3. Block-Nachricht<\/h4>\\n<pre><code>BLOCKIERT: Task kann nicht als 'completed' markiert werden!\\n\\nGRUND: Kein Ergebnis vorhanden (tasks_result fehlt).\\n\\nLÖSUNG: Vor tasks_status(id, \\\"completed\\\") MUSS tasks_result() aufgerufen werden:\\n\\n tasks_result(\\n id=TASK_ID,\\n response=\\\"Zusammenfassung der erledigten Arbeit...\\\",\\n executor=\\\"claude\\\",\\n executor_type=\\\"claude\\\"\\n )\\n\\nErst danach: tasks_status(id, \\\"completed\\\")<\/code><\/pre>\\n\\n<h4>4. Konfiguration in settings.json<\/h4>\\n<pre><code>{\\n \\\"PreToolUse\\\": [\\n {\\n \\\"matcher\\\": \\\"mcp__mcp-tasks__tasks_status\\\",\\n \\\"hooks\\\": [\\n {\\n \\\"type\\\": \\\"command\\\",\\n \\\"command\\\": \\\"\/var\/www\/tools\/ki-protokoll\/claude-hook\/task_completion_guard.py\\\",\\n \\\"timeout\\\": 5\\n }\\n ]\\n }\\n ]\\n}<\/code><\/pre>\\n\\n<h3>Ablauf<\/h3>\\n<pre>\\n┌─────────────────────────────┐\\n│ Claude: tasks_status( │\\n│ id=425, status=\\\"completed\\\"│\\n│ ) │\\n└─────────────┬───────────────┘\\n │\\n ▼\\n┌─────────────────────────────┐\\n│ PreToolUse Hook auslösen │\\n│ Matcher: mcp__mcp-tasks__ │\\n│ tasks_status │\\n└─────────────┬───────────────┘\\n │\\n ▼\\n┌─────────────────────────────┐\\n│ task_completion_guard.py │\\n│ - Parse Input │\\n│ - Check: status==\\\"completed\\\"│\\n└─────────────┬───────────────┘\\n │\\n ┌──────┴──────┐\\n │ │\\n ▼ ▼\\n┌─────────────┐ ┌─────────────┐\\n│ status != │ │ status == │\\n│ \\\"completed\\\" │ │ \\\"completed\\\" │\\n└──────┬──────┘ └──────┬──────┘\\n │ │\\n ▼ ▼\\n┌─────────────┐ ┌─────────────────┐\\n│ exit(0) │ │ DB-Check: │\\n│ → Allow │ │ task_results │\\n└─────────────┘ │ WHERE task_id=? │\\n └────────┬────────┘\\n │\\n ┌──────┴──────┐\\n │ │\\n ▼ ▼\\n ┌──────────┐ ┌──────────┐\\n │ count > 0│ │ count = 0│\\n └────┬─────┘ └────┬─────┘\\n │ │\\n ▼ ▼\\n ┌─────────┐ ┌─────────────┐\\n │ exit(0) │ │ stderr: │\\n │ → Allow │ │ BLOCKIERT │\\n └─────────┘ │ exit(2) │\\n │ → Block │\\n └─────────────┘\\n<\/pre>\\n\\n<h3>Implementierungsplan<\/h3>\\n\\n<h4>Schritt 1: Hook-Script erstellen<\/h4>\\n<p>Datei: <code>\/var\/www\/tools\/ki-protokoll\/claude-hook\/task_completion_guard.py<\/code><\/p>\\n<pre><code>#!\/usr\/bin\/env python3\\n\\\"\\\"\\\"\\nTask Completion Guard Hook\\n\\nBlockiert tasks_status(completed) wenn kein Result existiert.\\nErzwingt korrekten Workflow: tasks_result() vor tasks_status(completed).\\n\\nTrigger: PreToolUse (mcp__mcp-tasks__tasks_status)\\n\\\"\\\"\\\"\\n\\nimport json\\nimport sys\\nimport os\\nfrom pathlib import Path\\nfrom dotenv import load_dotenv\\nimport pymysql\\n\\n# Lade Environment aus .env im Hook-Verzeichnis\\nload_dotenv(Path(__file__).parent \/ '.env')\\n\\nDB_CONFIG = {\\n 'host': os.environ.get('CLAUDE_DB_HOST', 'localhost'),\\n 'port': int(os.environ.get('CLAUDE_DB_PORT', '3306')),\\n 'user': os.environ.get('CLAUDE_DB_USER', 'root'),\\n 'password': os.environ.get('CLAUDE_DB_PASSWORD', ''),\\n 'database': 'ki_dev',\\n 'charset': 'utf8mb4'\\n}\\n\\nBLOCK_MESSAGE = \\\"\\\"\\\"BLOCKIERT: Task kann nicht als 'completed' markiert werden!\\n\\nGRUND: Kein Ergebnis vorhanden (tasks_result fehlt).\\n\\nLÖSUNG: Vor tasks_status(id, \\\"completed\\\") MUSS tasks_result() aufgerufen werden:\\n\\n tasks_result(\\n id={task_id},\\n response=\\\"Zusammenfassung der erledigten Arbeit...\\\",\\n executor=\\\"claude\\\",\\n executor_type=\\\"claude\\\"\\n )\\n\\nErst danach: tasks_status({task_id}, \\\"completed\\\")\\\"\\\"\\\"\\n\\n\\ndef check_result_exists(task_id: int) -> bool:\\n \\\"\\\"\\\"Prüft ob ein Result für den Task existiert.\\\"\\\"\\\"\\n try:\\n conn = pymysql.connect(**DB_CONFIG)\\n cursor = conn.cursor()\\n cursor.execute(\\n \\\"SELECT COUNT(*) FROM task_results WHERE task_id = %s\\\",\\n (task_id,)\\n )\\n count = cursor.fetchone()[0]\\n conn.close()\\n return count > 0\\n except Exception as e:\\n # Bei DB-Fehler durchlassen (fail-open)\\n print(f\\\"DB-Check fehlgeschlagen: {e}\\\", file=sys.stderr)\\n return True\\n\\n\\ndef main():\\n try:\\n input_data = json.load(sys.stdin)\\n except json.JSONDecodeError:\\n sys.exit(0)\\n \\n # Nur mcp__mcp-tasks__tasks_status prüfen\\n tool_name = input_data.get(\\\"tool_name\\\", \\\"\\\")\\n if tool_name != \\\"mcp__mcp-tasks__tasks_status\\\":\\n sys.exit(0)\\n \\n # Prüfe ob Status auf \\\"completed\\\" gesetzt wird\\n tool_input = input_data.get(\\\"tool_input\\\", {})\\n status = tool_input.get(\\\"status\\\", \\\"\\\")\\n task_id = tool_input.get(\\\"id\\\")\\n \\n if status.lower() != \\\"completed\\\":\\n sys.exit(0)\\n \\n if not task_id:\\n sys.exit(0)\\n \\n # Prüfe ob Result existiert\\n if not check_result_exists(task_id):\\n print(BLOCK_MESSAGE.format(task_id=task_id), file=sys.stderr)\\n sys.exit(2)\\n \\n sys.exit(0)\\n\\n\\nif __name__ == \\\"__main__\\\":\\n main()\\n<\/code><\/pre>\\n\\n<h4>Schritt 2: Script ausführbar machen<\/h4>\\n<pre><code>chmod +x \/var\/www\/tools\/ki-protokoll\/claude-hook\/task_completion_guard.py<\/code><\/pre>\\n\\n<h4>Schritt 3: settings.json erweitern<\/h4>\\n<p>Datei: <code>\/root\/.claude\/settings.json<\/code><\/p>\\n<p>Füge folgenden Eintrag in \\\"PreToolUse\\\" Array hinzu:<\/p>\\n<pre><code>{\\n \\\"matcher\\\": \\\"mcp__mcp-tasks__tasks_status\\\",\\n \\\"hooks\\\": [\\n {\\n \\\"type\\\": \\\"command\\\",\\n \\\"command\\\": \\\"\/var\/www\/tools\/ki-protokoll\/claude-hook\/task_completion_guard.py\\\",\\n \\\"timeout\\\": 5\\n }\\n ]\\n}<\/code><\/pre>\\n\\n<h4>Schritt 4: Testen<\/h4>\\n<pre><code># Test 1: Task ohne Result → sollte blockiert werden (Exit 2)\\necho '{\\\"tool_name\\\":\\\"mcp__mcp-tasks__tasks_status\\\",\\\"tool_input\\\":{\\\"id\\\":999,\\\"status\\\":\\\"completed\\\"}}' | \\\\\\n \/var\/www\/tools\/ki-protokoll\/claude-hook\/task_completion_guard.py\\necho \\\"Exit code: $?\\\"\\n\\n# Test 2: Anderer Status → sollte durchgelassen werden (Exit 0)\\necho '{\\\"tool_name\\\":\\\"mcp__mcp-tasks__tasks_status\\\",\\\"tool_input\\\":{\\\"id\\\":999,\\\"status\\\":\\\"in_progress\\\"}}' | \\\\\\n \/var\/www\/tools\/ki-protokoll\/claude-hook\/task_completion_guard.py\\necho \\\"Exit code: $?\\\"\\n\\n# Test 3: Anderes Tool → sollte durchgelassen werden (Exit 0)\\necho '{\\\"tool_name\\\":\\\"Bash\\\",\\\"tool_input\\\":{\\\"command\\\":\\\"ls\\\"}}' | \\\\\\n \/var\/www\/tools\/ki-protokoll\/claude-hook\/task_completion_guard.py\\necho \\\"Exit code: $?\\\"\\n<\/code><\/pre>\\n\\n<h3>Voraussetzungen<\/h3>\\n<ul>\\n <li>Python 3.x<\/li>\\n <li>PyMySQL (bereits installiert: <code>pip3 install pymysql<\/code>)<\/li>\\n <li>python-dotenv (bereits installiert)<\/li>\\n <li>Zugriff auf ki_dev.task_results<\/li>\\n <li>.env Datei im Hook-Verzeichnis mit DB-Credentials<\/li>\\n<\/ul>\\n\\n<h3>Fail-Safe Verhalten<\/h3>\\n<p>Bei DB-Verbindungsfehlern wird der Hook \\\"fail-open\\\" - d.h. die Aktion wird durchgelassen, um Claude nicht zu blockieren. Die Server-seitige Validierung in MCP-Tasks greift dann als zweite Verteidigung.<\/p>\\n\\n<h3>Validierung<\/h3>\\n<table>\\n <tr><th>Prüfpunkt<\/th><th>Status<\/th><\/tr>\\n <tr><td>PyMySQL verfügbar<\/td><td>✓ PyMySQL 1.1.2 installiert<\/td><\/tr>\\n <tr><td>python-dotenv Pattern<\/td><td>✓ Konsistent mit log_storage.py<\/td><\/tr>\\n <tr><td>DB_CONFIG Pattern<\/td><td>✓ Identisch zu bestehenden Hooks<\/td><\/tr>\\n <tr><td>Exit-Code Pattern<\/td><td>✓ 0=allow, 2=block (wie block_direct_db.py)<\/td><\/tr>\\n <tr><td>Matcher-Syntax<\/td><td>✓ Regex-kompatibel für MCP-Tools<\/td><\/tr>\\n <tr><td>Fail-Open Design<\/td><td>✓ Bei Fehler durchlassen<\/td><\/tr>\\n<\/table>\\n\",\n \"status\": \"published\",\n \"sort_order\": 0,\n \"depth\": 2,\n \"created_at\": \"2025-12-27T10:08:47\",\n \"updated_at\": \"2025-12-27T10:10:14\"\n },\n \"message\": \"Document #141 updated... [TRUNCATED-ad4193f81db02c83]"
}
]
}