← Zum Blog
Blog

Vier Memory-Hooks, zwei haben gepasst, zwei nicht

Ich hab den neuen mcp_tool Hook an unserem eigenen Memory-Server getestet. Was deterministisch funktioniert, und wo ich zwei Stunden an einem stillen Fehler hing.

02. Juni 2026
Memory-Hooks hatte ich im April durchverdrahtet. Bash-Wrapper, klassisch: ein Script das dem Modell zuruft "ruf jetzt nex_summarize auf bevor Du gehst". Lief. Meistens. Bis ich nach zwei Wochen in studiomeyer-memory geschaut hab und Lücken sah. 88 Sessions im Store, aber bei einer Handvoll fehlte die Summary komplett. Das Modell hatte den Reminder gelesen und trotzdem ignoriert. Genau dafür gibt es seit dem 23. April einen Fix. Claude Code 2.1.118 hat einen Hook-Type bekommen der das Modell aus der Gleichung nimmt: `type: "mcp_tool"`. Der Hook ruft das MCP-Tool direkt auf. Kein Bash, kein Reminder, kein Hoffen dass es zuhört. Ich hab das an unserem eigenen Memory-Server getestet. studiomeyer-memory, der Store mit aktuell 275 Learnings, 88 Sessions und 29 Decisions. Vier Recipes wollte ich verdrahten. Zwei haben sauber gepasst. Zwei nicht. Und genau die zwei die nicht passten haben mir am meisten erklärt wie der Hook wirklich tickt. ## Was sich technisch ändert Bis 2.1.118 hatte ein Hook vier Handler-Typen: command, http, prompt, agent. Alle vier laufen über einen Umweg. command startet eine Shell. prompt schiebt dem Modell Text unter. Beim mcp_tool fällt der Umweg weg. Du schreibst den Tool-Call direkt in `~/.claude/settings.json`: ```json { "hooks": { "UserPromptSubmit": [ { "hooks": [ { "type": "mcp_tool", "server": "studiomeyer-memory", "tool": "nex_search", "input": { "query": "..." } } ] } ] } } ``` Das Tool-Output läuft durch denselben JSON-Parser wie ein command-Hook. Kein jq, kein PATH-Problem, keine Shell die auf einem frischen Rechner anders heißt. `server` zeigt auf deinen konfigurierten MCP-Server, `tool` ist der Tool-Name, `input` das Argument-Objekt. ## Recipe 1, das funktioniert: Suche bei jedem Prompt Der beste Use-Case. UserPromptSubmit feuert bevor Claude deinen Prompt verarbeitet. Ich hänge da nex_search dran, damit relevante alte Learnings automatisch im Kontext landen bevor das Modell überhaupt antwortet. Kein "denk dran was wir letzte Woche entschieden haben". Es ist einfach da. Den Prompt-Text gibst Du über die Input-Interpolation rein. Die genaue Variable hängt vom Event ab, für PostToolUse ist es zum Beispiel `${tool_input.file_path}`, für UserPromptSubmit steht sie in der Hooks-Referenz. Wichtig: setz ein knappes Timeout, ich fahre 10 Sekunden. Eine Memory-Suche die deinen Prompt 40 Sekunden blockiert ist schlimmer als gar keine. ## Recipe 2, das funktioniert: Kontext beim Sessionstart SessionStart feuert wenn eine Session aufmacht. Da lädt mcp_tool einmal den Projekt-Kontext. Statt dass ich jede Session manuell nex_context aufrufe, kommt der Stand automatisch rein. Deterministisch, jedes Mal, ohne dass ich dran denken muss. Bei einem Store mit 275 Learnings macht das einen echten Unterschied, weil ich nicht mehr blind in eine Session starte. ## Recipe 3, das NICHT sauber passt: Summary bei Stop Hier lag mein Denkfehler. Ich wollte Stop, dann nex_summarize, also: Claude ist fertig, schreib die Session-Summary weg. Klingt logisch. Geht aber nicht sauber, und der Grund ist wichtig. Ein mcp_tool-Hook übergibt statisches Input. Er kann interpolieren, aber er kann das Modell nicht bitten "fass mal die letzten zwei Stunden zusammen". nex_summarize braucht aber genau das, einen generierten Summary-Text. Der Hook hat den nicht. Er kann nur weiterreichen was schon da ist. Heißt: für alles wo eine LLM-Generierung mitten drin steckt, ist der mcp_tool-Hook das falsche Werkzeug. Da bleibst Du beim command-Hook oder prompt-Hook der das Modell einbindet. Ich hab zwei Stunden gebraucht das zu kapieren, weil die settings.json keinen Fehler wirft. Der Hook feuert brav, ruft das Tool mit leerem Input auf, und schreibt Müll weg. Stiller Fehler, die schlimmste Sorte. ## Recipe 4, die Trigger-Phrase-Falle Zweiter Denkfehler. Ich wollte nex_search nur bei bestimmten Prompts feuern, wenn ich zum Beispiel "erinnerst Du Dich" schreibe. Also dachte ich an einen Matcher. Matcher gibt es aber nur für PreToolUse und PostToolUse. Alle anderen Events, auch UserPromptSubmit, ignorieren den Matcher komplett. Mein Hook feuerte bei jedem einzelnen Prompt, egal was ich schrieb. Was für Recipe 1 super ist, aber meine Idee mit der Trigger-Phrase killt. Wenn Du wirklich nach Phrase filtern willst, brauchst Du wieder einen command-Hook der die Logik selbst macht. ## Was ich mitnehme Der mcp_tool-Hook ist kein Ersatz für Bash-Wrapper. Er ist ein scharfes Werkzeug für einen engen Fall: deterministischer Tool-Call mit Input das schon existiert. Retrieval, Logging, Kontext laden. Genau da ist er Gold, weil das Modell nichts mehr "vergessen" kann. Für alles mit Generierung drin bleibst Du bei den alten Typen. Zwei Regeln die ich mir aufgeschrieben hab. Erstens, halt die Hooks idempotent, der gleiche Trigger darf zweimal feuern ohne Schaden. Zweitens, ein Memory-Hook der bei jedem Prompt läuft sieht jeden Prompt. Wenn da sensibler Kram durchgeht, gehört das in deine DSGVO-Überlegung, nicht erst beim Audit Monate später. Die Mechanik im Detail, wann mcp_tool passt und wann nicht, steht in unserer Lektion [mcp_tool Hooks](/levels/4/10-mcp-tool-hooks). Die Grundlagen zu Hooks und Skills davor findest Du in [Hooks und Skills](/levels/4/04-hooks-und-skills). Wenn ein Hook gar nicht feuert, hilft das Playbook [Hooks debuggen wenn nichts feuert](/playbooks/hooks-debuggen-wenn-nichts-feuert). Und gegen stille Memory-Lücken wie meine oben das Playbook [Hooks gegen Halluzinationen](/playbooks/hooks-gegen-halluzinationen). Die offizielle Referenz mit allen Events und Input-Variablen liegt unter https://code.claude.com/docs/en/hooks.
← Weitere Blog-Posts