Blog
Warum direkte mcp_tool-Hooks die Bash-Wrapper-Ära beenden
Vier Wochen Pilot, fuenf MCP-Server, 180 Hook-Feuerungen. Was sich seit Claude Code v2.1.118 mit dem mcp_tool Hook-Type strukturell ändert, mit Zahlen, mit zwei Failures, und der Stelle wo ich bewusst beim Bash-Wrapper geblieben bin.
26. Mai 2026
Vier Wochen Pilot, fuenf MCP-Server angebunden, ungefaehr 180 Hook-Feuerungen über alle Setups zusammen. Und genau eine Stelle an der ich nochmal zum alten Bash-Wrapper-Pattern zurück gegriffen hab.
Die Frage die mir Matthias vor zwei Wochen am Telefon gestellt hat: warum redest du dauernd vom mcp_tool Hook als waere das jetzt anders. Hooks gabs vorher auch. Du hast Bash-Wrapper gebaut, der Bash-Wrapper hat `mcp-cli call foo --arg=bar` ausgefuehrt, fertig. Was bringt der direkte Hook?
Auf den ersten Blick: nichts. Auf den zweiten: alles.
## Was der Bash-Wrapper wirklich kostete
Ich hab das vier Monate lang so gemacht. Stop-Hook feuert, Bash startet, Bash startet einen Node-Prozess, der Node-Prozess startet einen MCP-Client, der MCP-Client connectet zum Server, schickt den Tool-Call, wartet auf Antwort, parst die Antwort, schreibt Output in stderr, Bash gibt das an Claude Code zurück.
Median-Latenz auf meinem Dev-Setup: 2.8 Sekunden. Davon waren 2.2 Sekunden Cold-Start des MCP-Clients und die Connection-Handshake. Der eigentliche Tool-Call lief in 600ms.
Und dann die zweite Sache: Idempotenz. Wenn Bash crasht weil ein Argument nicht sauber escaped war, hat sich der Tool-Call entweder nicht oder doppelt ausgeloest, und ich hab es nicht gemerkt bis drei Tage später beim Auswerten der Memory-Logs ein Duplikat aufgetaucht ist. Sechs Mal in vier Monaten, alle wegen meiner eigenen Escape-Fehler.
## Was der direkte mcp_tool Hook jetzt tut
Mit dem `type: "mcp_tool"` Hook (Claude Code seit v2.1.118) brauchst du keinen Bash mehr. Du sagst direkt im settings.json welcher Server, welches Tool, welche Argumente. Claude Code nutzt die schon offene MCP-Verbindung wieder und ruft das Tool direkt auf der existierenden Session auf.
```json
{
"hooks": {
"Stop": [
{
"type": "mcp_tool",
"server": "studiomeyer-memory",
"tool": "nex_summarize",
"if": { "tool_used": "Edit|Write|MultiEdit" }
}
]
}
}
```
Median-Latenz im neuen Setup: 380ms. Memory-Stop-Hook 290ms, geo_check 4.1s (das ist Tool-intrinsisch, externe API), crm_search 510ms. Cold-Start ist null weil die Verbindung schon steht. Argument-Parsing macht Claude Code statt Bash, da kannst du nichts mehr falsch escapen.
Idempotenz: in den 180 Hook-Feuerungen über alle fuenf Setups null Duplikate. Eine Stop-Hook für `nex_summarize` hat sich einmal nicht ausgeloest weil ich die `if`-Condition kaputt hatte, aber das ist kein Idempotenz-Problem sondern ein Logik-Fehler in meinem Matcher.
## Wo ich mir das eigene Knie geschossen hab
Stelle eins. Ich dachte ich kann mit `type: "mcp_tool"` auch zu einem nicht-verbundenen MCP-Server feuern. Das geht nicht. Der Server muss in mcp.json existieren, in dieser Session geladen sein, sonst skippt Claude Code den Hook still. Ich hab für einen Test mit Crew angefangen ohne `mcp-crew` in mcp.json zu listen, der Hook lief 12 mal nicht und es war nur eine schweigende Notiz im Debug-Log. Eine kleine Doku-Zeile haette mich davor bewahrt. Jetzt ist es in der offiziellen Hooks-Doku unter "MCP tool hooks" sauber beschrieben, aber ich hatte es ueberlesen.
Stelle zwei. Beim ersten Versuch hab ich versucht, einen Stop-Hook für `nex_summarize` ohne `if`-Condition zu feuern. Das hat in jeder Stop-Phase Memory gefuettert, also auch wenn ich drei Sekunden lang `ls` getippt hatte. In zwei Tagen waren 130 sinnlose Memory-Eintraege drin, jeder mit confidence 0.4, ich konnte zwei Stunden lang sortieren. Lehre: bei jedem Stop-Hook eine `if`-Condition mit Tool-Matcher mitgeben, sonst feuert das auch für triviale Turns.
## Was sich strukturell ändert
Pre-mcp_tool: du hast Bash-Wrapper als Mini-Glue-Layer. Jeder Wrapper ein eigenes Script, jedes Script eigene Fehler-Modi, irgendwann zwei Dutzend Mini-Scripts im `~/.claude/hooks/` Ordner und keiner weiss mehr welcher noch lebt.
Post-mcp_tool: alle Hooks liegen in settings.json oder per-Project in `.claude/settings.local.json`. Lesbar, diff-bar, versionierbar. Du committest die Hook-Config zusammen mit dem Repo, neue Mitspielende ziehen das Repo und haben den gleichen Hook-Stack.
Das ist mehr als ein Performance-Gewinn. Das ist eine Verhaltensaenderung. Hooks waren bei mir vorher rare und immer ein bisschen wackelig. Jetzt sind sie das Default. Memory ist verkabelt, CRM ist verkabelt, GEO ist verkabelt, Crew aktiviert sich pro Projekt. Vier Werkzeuge die vorher manuell waren, jetzt automatisch und idempotent.
## Wann der Bash-Wrapper noch Sinn macht
Eine Stelle wo ich bewusst beim Bash-Wrapper geblieben bin: ein Pre-Commit-Hook der ein externes CLI ohne MCP-Anbindung aufruft, in dem Fall `gpg --verify` für signed commits. Der hat mit MCP nichts zu tun, das CLI ist nicht als MCP-Server verfuegbar, da ist der direkte Bash-Aufruf weiter das richtige. Bash-Wrapper sterben nicht ganz. Sie sterben für alles wo es schon einen MCP-Server gibt.
## Zahlen aus dem Pilot
Vier Wochen Realbetrieb. Fuenf MCPs angebunden. 180 Hook-Feuerungen. 174 OK, 6 nicht ausgeloest (Matcher-Bugs), 0 Duplikate, 0 falsche Tools. Median-Latenz quer über alle Hooks 410ms. Lokale settings.json ist von 0 auf 14 Hooks gewachsen. Davon 4 für Memory, 3 für CRM, 3 für GEO, 2 für Crew, 2 generic (ESLint und Test-Runner).
Das ist kein riesiger Zahlenwall. Aber es ist genug um zu sagen: das Pattern haelt. Vier Wochen ohne Crash, ohne dass ich ein Setup zurueckdrehen musste, ohne dass Matthias mich fragen musste warum der Hook wieder mal Mist gemacht hat.
## Was du als naechstes lesen kannst
Wenn du mit Hooks überhaupt nicht warm bist, fang bei der Lesson [Hooks und Skills](/levels/4/hooks-und-skills) an. Wenn du die Grundlagen hast und konkret das mcp_tool-Setup nachbauen willst, dann die Lesson [mcp_tool Hooks](/levels/4/mcp-tool-hooks) plus das Playbook [Hooks gegen Halluzinationen](/playbooks/hooks-gegen-halluzinationen). Und wenn du sehen willst wie wir das hier konkret verkabelt haben, scroll durch die letzten vier Build-in-Public Posts, vier konkrete Setups für Memory, CRM, GEO und Crew.
Spec-Referenz: alle Hook-Types und Felder sind in der offiziellen Doku unter https://code.claude.com/docs/en/hooks beschrieben. Wenn ich hier was abweichendes sage, hat sich entweder die Doku geaendert oder ich hab mich verschrieben. Die Doku gewinnt.