Short answer
Codex CLI stores its memories under `~/.codex/memories/`, off by default and generated by Codex from its own past threads. They live on one machine, under one account, and hold your preferences rather than your team's decisions. Connecting Codex to a Baalda vault over MCP puts the durable half in markdown files the whole team can open.
Codex CLI is not amnesiac any more. It ships a memories feature that summarises your past threads and feeds them back into new ones, and it reads an instruction chain of AGENTS.md files every time it starts. Both of those are real memory and both answer to one person. They sit under one home directory, on one laptop, written by Codex about Codex's own work. Nothing in that path carries a decision to the four other people who need it.
What does Codex CLI actually remember between sessions?
Three layers, and they are easy to confuse because people call all of them memory.
The first is AGENTS.md. Codex builds an instruction chain when it starts, walking from the project root down to your current directory and concatenating what it finds, with files closer to you appearing later so they override earlier guidance. In the Codex home directory, which defaults to ~/.codex unless you set CODEX_HOME, it reads AGENTS.override.md if that exists and AGENTS.md otherwise. This is static text you maintain by hand, capped by project_doc_max_bytes, which the configuration reference describes as the "Maximum bytes read from AGENTS.md when building project instructions". It is instructions, not knowledge.
The second is the thread itself, plus compaction. That is in-session only. When the session ends, the compacted state ends with it.
The third is the one worth paying attention to. Codex memories are a feature flag, features.memories, which the configuration reference marks "off by default". Turn it on and Codex extracts and consolidates its own past threads into files under ~/.codex/memories/, then injects them into later sessions. memories.use_memories defaults to true and controls the injection; memories.generate_memories defaults to true and controls whether new threads become inputs for memory generation.
So Codex remembers, and the docs are clear about what kind of thing it remembers: stable preferences, recurring workflows, tech stacks, project conventions, known pitfalls. That is a description of you and your habits. It is not a description of why the team picked Postgres over DynamoDB in March, or what the customer actually asked for.
Why does none of it reach the rest of the team?
Because every layer is scoped to one machine and one account, and two of the three are written by the agent rather than by a person.
The memories live under your Codex home directory. They are generated: Codex summarises its own threads in the background using memories.extract_model and memories.consolidation_model, which means nobody wrote them and nobody reviewed them. A teammate cannot open them, cannot correct the bit that is wrong, and cannot inherit them when you go on holiday. Hand-editing is not the supported path.
There is one setting that points directly at this boundary, and it is the most revealing thing in the whole reference. memories.disable_on_external_context defaults to false, and the docs describe it as: when true, "threads that use external context such as MCP tool calls, web search, or tool search are kept out of memory generation." Codex ships a switch for keeping work it did through MCP out of its own memory. There is no switch in the other direction. Nothing in Codex pushes what it learned out to a place the team can read.
That boundary is correct, and it is the reason a second store has to exist. Baalda is a team second brain built on plain markdown files on your own disk: several people edit the same note in real time, access is set per file, and the whole thing is open source under Apache-2.0 and self-hostable. It serves the Model Context Protocol at POST /api/mcp over Streamable HTTP, which is the door Codex walks through. Codex keeps its private recall of your preferences. The vault holds the half a colleague would also want to read.
How do you connect Codex CLI to a vault over MCP?
1. Mint a vault token. In the Baalda desktop app, open Vault settings → MCP and create a token. It is scoped to one user and one vault and carries that user's permissions.
2. Keep the token out of the config file. Codex reads the bearer token from an environment variable rather than from config.toml, which is the right default when the config might be committed to a repo:
export BAALDA_MCP_TOKEN=mcp_your_token_here3. Add the server to `~/.codex/config.toml`. Each server is an [mcp_servers.<name>] table. For a remote Streamable HTTP server the fields are url, described in the configuration reference as the "Endpoint for an MCP streamable HTTP server", and bearer_token_env_var, the "Environment variable sourcing the bearer token for an MCP HTTP server":
[mcp_servers.vault]
url = "http://localhost:3010/api/mcp"
bearer_token_env_var = "BAALDA_MCP_TOKEN"
enabled_tools = ["list_vaults", "search_notes", "read_note", "create_note", "append_note"]
startup_timeout_sec = 20http://localhost:3010/api/mcp is the default local address. On the managed service it is https://api.baalda.com/api/mcp, and for a self-hosted server it is your server URL plus /api/mcp.
enabled_tools is an allow list of tool names, and there is a disabled_tools deny list applied after it. Both are worth using. Baalda exposes a fuller catalogue (list_vaults, list_folders, list_notes, read_note, search_notes, list_attachments, read_attachment_text, create_note, update_note, append_note, edit_note, delete_note, move_note, create_folder, delete_folder, move_folder and the access-management tools), and opening with reads plus additive writes is the safer first position. startup_timeout_sec overrides a default of 10 seconds and tool_timeout_sec overrides a default of 60.
If you would rather not hand-edit TOML, codex mcp add vault --url http://localhost:3010/api/mcp registers the server, and you can add the token line afterwards. You can also scope a server to one repository with a project-local .codex/config.toml, which Codex only loads for projects you have marked as trusted.
What changes once the notes are behind a tool call?
The useful jobs are unglamorous. Codex can search the vault before it answers, so it stops re-deriving context somebody already wrote down. It can append the outcome of a migration to the runbook that describes the migration. It can draft a decision record that two people then edit by hand while Codex is still working, because writes land through the same sync path as human keystrokes rather than arriving as a conflicting copy an hour later.
Two properties make that safe enough to leave switched on.
- Writes merge instead of overwriting. Baalda syncs over a CRDT, so a write from Codex and a teammate typing in the same paragraph both survive.
- The token is a person. Every MCP call runs as the user the token belongs to, and access is set per folder and per note, so a note that person cannot open is a note the agent acting for them cannot reach. That is the same gate a human hits, which is the point.
There is also a concurrency guard worth knowing about, because an agent will eventually read a note, think for thirty seconds, and write back into a file that moved underneath it. read_note returns a revision, and passing it back as expectedRevision on update_note, append_note or edit_note refuses the write with a conflict if the note changed in the meantime. edit_note adds a second check: each anchor must match exactly once, and a missing or ambiguous anchor refuses the whole call with nothing written.
Should you turn Codex memories off once it can reach a vault?
No. They are doing different jobs and they compose better than they compete.
Leave features.memories on for what it is good at: that you prefer pnpm, that this repo's tests need a database running, that you have been burned by a particular migration pattern before. That is personal and it belongs in a personal store.
The thing worth revisiting is memories.disable_on_external_context. It defaults to false, so by default a thread that called the vault over MCP can still feed memory generation, which means fragments of the team's notes may end up paraphrased into your local memory files. If that bothers you, setting it to true keeps MCP threads out of memory generation entirely. Once the durable material is in the vault, where a person can read and correct it, there is less reason to want a second lossy copy of it summarised into ~/.codex/memories/ anyway.
This is roughly the same split that an `AGENTS.md` file calls for: instructions that must apply unconditionally go in the file that is loaded unconditionally, and knowledge the team has to be able to read and fix goes behind a tool call.
Where this is the wrong answer
Three places, and they are worth saying out loud.
Nothing is automatic. A note exists in the vault because something called create_note, append_note, update_note or edit_note. Nothing watches a Codex thread and extracts facts from it the way Codex's own memory generation does. If the decision was never written by a tool call, it is not in the vault, and no amount of configuration fixes that. This is the real limit and it is a habit problem, not a product one.
If you are one person with a local vault, you do not need MCP at all. Codex already has file tools. Point it at the folder and it reads and writes the markdown directly, with no server and no token. MCP earns its place when the vault is somewhere Codex cannot reach from disk, or when permissions and live merge actually matter, which is to say when there is more than one of you.
If your team's knowledge is already in Notion or Confluence and nobody is unhappy, moving it is a large job for a benefit you may not feel. Notion's hosted MCP server needs nothing running and is reachable whether or not your laptop is on. Baalda needs a server up somewhere: your machine for a local vault, or a self-hosted or managed instance if you want an agent to reach it while you are asleep.
One last operational detail, because it will otherwise cost somebody an afternoon. Baalda answers POST /api/mcp and returns 405 on GET and DELETE, because it does not offer a server-to-client SSE stream. Codex's url field is the Streamable HTTP endpoint, which is the right transport, so this is only a problem if you try to point an SSE-only client at it.