Claude Code and Codex: Revolutionizing Memory with Neo4j
Le brief IA que les pros lisent chaque soir
Les 7 actus IA du jour, décryptées en 5 min. Gratuit.
Inclus dès l'inscription : notre sélection des meilleurs guides & comparatifs IA.
Choisis ton rythme
Gratuit · Pas de spam · Désabonnement en 1 clic
The implementation of hooks allows Claude Code, Codex, and Cursor to benefit from persistent memory via Neo4j, while avoiding vendor lock-in. The real challenge is not when the next superior model will be available, but rather who will be able to build the right framework around these technologies. A framework constitutes the infrastructure surrounding the model: the agent loop, tool definitions, context management, memory, prompts, and workflows, transforming a raw LLM into a useful product. Examples of these frameworks include Cursor, Claude Desktop, and others.
A debate persists in the field of AI coding tools: does committing to a specific framework imply vendor lock-in? Memory is the most critical aspect of this question. If your agent's memory is confined within a closed framework or behind a proprietary API, you do not truly own it, and the costs of switching can accumulate quickly. However, this should not be the case.
The central idea of this article is simple: keep the memory layer outside the framework, allowing any framework to connect to it.
Designing a Unified Agent Memory
In this article, we will demonstrate how to build a shared memory layer that works across three different coding agents — Claude Code, OpenAI's Codex, and Cursor — using hooks as the integration mechanism and Neo4j as the persistent store. The code for the hook integration is available on GitHub.
MCP Tools Are Not Enough for Memory
MCP (Model Context Protocol) servers are often used to give agents access to external systems. And they work. You can expose a Neo4j database as an MCP tool and let the agent query it whenever it decides.
However, MCP tools are agent-initiated. The model must decide to call the tool, and it must know when and why to do so. This means that:
- The agent must "remember to remember"; it must proactively decide to store something worth recalling later.
- There is no guarantee of consistency; one session may log everything, while the next may log nothing.
- You rely on the model's judgment regarding what is important for memory, in real-time, while it is busy doing something else.
What you really want is passive and deterministic logging, which captures every session event regardless of what the model is doing, without consuming its context or attention.
This is exactly what hooks offer you.
How Hooks Work
Hooks allow you to write programmatic and deterministic flows based on a predefined set of events. Hooks are shell commands that automatically execute during lifecycle events: when the session starts, when the user submits a prompt, before and after each tool use, and when the session ends. The agent does not decide to call them; they execute programmatically.
The key idea is that hooks are remarkably standardized across providers. Claude Code, Codex, Cursor, and others essentially support the same lifecycle events:
- SessionStart for the beginning of the agent's session
- UserPromptSubmit (or beforeSubmitPrompt in Cursor) for when the user sends a message
- PreToolUse / PostToolUse for before and after each tool call
- Stop for the end of the session
The hook receives a JSON payload on stdin with the session ID, event name, tool details, and user prompt. And the hook can emit JSON on stdout to inject additional context into the conversation. Same contract, three frameworks/clients.
Shared Memory Layer
Now, we need a place to persist the memory. A small clarification: I work at Neo4j, so we will use it in this example.
Session Structure
The model is simple. Each agent session is a node, connected to a linked list of event nodes, one for each hook invocation. Events are typed by the lifecycle event that triggered them: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop. A session ends with an ordered timeline of everything that happened during that run.
All five types of events are written to the store, giving you a complete audit trail of each session across each framework. Two of them are also injection points. SessionStart triggers before the agent reads its system prompt, so anything the hook emits there is added to the system prompt. This is how persistent memory at the agent level enters the context. UserPromptSubmit triggers just before the user message is sent, and anything emitted there is added to the user prompt. This is the hook for context at the turn level, like integrating relevant memories to what the user just typed.
Interaction Examples in Cursor
If we inspect the results in the Neo4j browser.
An example of a persisted session represented as a graph in Neo4j.
An important constraint: hooks execute outside the model session of the framework. You cannot reuse the LLM with which the agent communicates. If you want LLM-powered work inside a hook, you must make your own model call, which adds latency to each event the agent triggers. This is why hooks here only do two things: log events and inject pre-computed memories. They remain fast and deterministic.
The real memory work occurs in a separate dreaming phase: extracting facts from sessions, summarizing what happened, updating the graph. It's just a batch job that runs every few hours, reading the accumulated events since the last run, and writing back to the memory store. You could theoretically trigger a memory update asynchronously every time a session stops, but that seems a bit too much; a periodic batch is simpler and works well for this demonstration.
The dreaming phase takes each event from the last session checkpoint, feeds them to Claude with the current memory store, and asks it to write a small set of durable notes. The notes themselves mimic a markdown wiki, the same format that Karpathy and others tend to use for LLM personal memory and the same format that Anthropic's skills already use: each memory is a file at a semantic path like profile/role.md, tools/bash/common-flags.md, or project/neo4j-skills.md, with YAML metadata at the top and text below. Claude is prompted to merge rather than add, so a path is a living document, not a log; if new events contradict an old note, the old note is rewritten. The result is a tree of small autonomous markdown files that a future session can read, indistinguishable in form from a skill, just written by the dreaming phase instead of being done by hand.
Accessing Memory
The last piece of the puzzle is allowing the agent to access the memory layer. As mentioned, there are two ways to inject information into the agent: hooks and MCP tools.
Hooks are deterministic and execute at the beginning of each session to populate the system prompt. This is where profile information and instructions on how to use memory effectively should be integrated. You can also add additional context when the user prompt submission event triggers, but this is in append-only mode; you cannot manipulate other parts of the prompt.
MCP tools, on the other hand, give the LLM direct access to the memory layer on demand. Instead of passively receiving context at startup, the agent can search for relevant memories, store new information, and update or delete existing entries. Essentially, it's a basic CRUD on the abstract markdown files stored in Neo4j.
In the end, I think you will almost always need both. In this project, we only have hooks, no MCP tools, but you can still plug in the official Neo4j MCP to allow the agent to explore the graph.
Implementation
Somewhat interestingly, the way I set up the hooks was to point the agent to any of the frameworks and ask it to install hooks, but I’m sure there are better approaches as well.
If you do not own your memory, you do not own your agent. Each framework today builds its own walled garden of context, preferences, and session history. Changing them means starting from scratch. This should not be the case.
Hooks break this pattern. They allow you to write integrations that plug into any framework from the outside, and the interface is remarkably consistent. Claude Code, Codex, and Cursor all trigger the same lifecycle events: session start, prompt submission, tool use, session end. The hook receives JSON on stdin, optionally emits JSON on stdout to inject context, and that’s the entire contract. Since hooks execute deterministically at each event, they do not consume the model's attention nor rely on the agent to decide what is worth saving. The same two Python scripts handle all three clients; thin shell wrappers that pass a --client flag are the only link per framework.
Architecture
The architecture has three layers:
-
Hooks (online) — passively log each event in Neo4j as a linked list by session. No model calls, no latency cost, just logging.
-
Dreaming Phase (offline) — a batch job reads the accumulated events, asks Claude to distill them into durable markdown memories, and writes them back. Memories are organized by topic and merged rather than added, so they remain current instead of growing indefinitely.
-
Injection (online) — at the beginning of the next session in any framework, profile memories are loaded into context. At each user prompt, relevant memories are searched for and automatically added.
The result is a memory layer that sits beneath all three frameworks, operates without any of them knowing about the others, and is entirely yours. You can switch from Cursor to Claude Code to Codex mid-project and pick up exactly where you left off. Your agent's understanding of who you are, what you are working on, and how you prefer to work follows you, not the other way around.
Brief IA — L'actualité IA en français
L'essentiel de l'actualité de l'intelligence artificielle, décrypté et expliqué chaque jour.