Content
# MCP Memory Server
General API for OpenClaw, Codex, Claude Code, and future clients' memory.
## Quick Start
```bash
# Installation in user Python environment
python3 -m pip install --user mcp-memory
# Root of working memory; defaults to current directory
export MCP_MEMORY_ROOT="$HOME/my-agent-workspace"
# Run server in stdio mode
MCP_MEMORY_CLIENT_ID=codex mcp-memory
# Manual test (in another terminal)
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | MCP_MEMORY_CLIENT_ID=codex mcp-memory
```
## Installation as MCP Server
### Claude Code
Add to `.claude/settings.json`:
```json
{
"mcpServers": {
"memory": {
"command": "mcp-memory",
"env": {
"MCP_MEMORY_ROOT": "/path/to/your/workspace",
"MCP_MEMORY_CLIENT_ID": "claude"
}
}
}
}
```
### Codex
Add to Codex config:
```json
{
"mcpServers": {
"memory": {
"command": "mcp-memory",
"env": {
"MCP_MEMORY_ROOT": "/path/to/your/workspace",
"MCP_MEMORY_CLIENT_ID": "codex"
}
}
}
}
```
OpenClaw connects to the same server through its config with `MCP_MEMORY_CLIENT_ID=openclaw`. The value from env is considered authoritative: if a model or client passes a different `client_id` in tool-calls, the server will overwrite it with the env value.
## Architecture
- **Namespace routing.** Identity is taken from `MCP_MEMORY_CLIENT_ID` if the env value is set, and only if it's not set, from the `client_id` argument. `openclaw`, `claude`, `codex`, and `external:<name>` are routed to their daily-note directories and wiki inbox.
- **Master file protection.** `MEMORY.md` and `wiki/syntheses/` are considered master memory. OpenClaw edits them directly by ACL, while Codex, Claude Code, and external clients must use the proposal flow through `memory_propose_update`.
- **Path safety.** Reads and writes go through `Path.resolve(...).relative_to(...)`, so traversal through `..`, absolute paths outside allowed roots, and symlink exits outside roots are blocked.
- **File locking.** Writes take an exclusive `fcntl` lock on the target file; before locking, the parent directory is created so that new namespace paths work without manual preparation.
- **Logging.** The server writes diagnostic messages only to stderr through `logging`. stdout is used only for JSON-RPC responses.
## Connecting a New MCP Client
1. Add identity to `config/server.json` → `allowedClients` and `namespaces`.
2. Run the server with access to the client (stdio or Unix socket).
3. Pin identity through `MCP_MEMORY_CLIENT_ID=<client_id>`.
4. The client gets access within its namespace.
Example of an external client:
```json
{
"namespaces": {
"external": {
"write": ["memory/external/my-agent/", "wiki/inbox/external/my-agent/"],
"prefix": "external"
}
}
}
```
Running the server for an external client must contain `MCP_MEMORY_CLIENT_ID=external:my-agent`. The `client_id` passed in the request is left for backward compatibility but is not authoritative if an env value is present.
## Remote Access
To connect a workstation:
1. On the server: run the server in stdio mode through an MCP client.
2. On the remote machine, use a transport provided by this client (e.g., SSH), without exposing the memory server to the public network.
```bash
MCP_MEMORY_ROOT=/path/to/your/workspace mcp-memory
```
## Tools
| Tool | Description |
|------|-------------|
| `memory_search(query, corpus, maxResults)` | Search through ripgrep |
| `memory_get(path, from, lines)` | Read file |
| `memory_append(client_id, content, metadata)` | Write to daily note |
| `memory_list(corpus, pattern)` | List files |
| `wiki_get(slug)` | Read wiki page |
| `wiki_update(slug, content, claims, title, heading, client_id, create, refresh)` | Create/update wiki page: human note, structured claims, and digest rebuild |
| `memory_propose_update(target, content, rationale)` | Proposal for master |
| `memory_status()` | Namespace status |
## Namespace Routing
| client_id | Writes to |
|-----------|-----------|
| `openclaw` | `memory/openclaw/`, `wiki/inbox/openclaw/`, `wiki/syntheses/` |
| `claude` | `memory/claude/`, `wiki/inbox/claude/` |
| `codex` | `memory/codex/`, `wiki/inbox/codex/` |
| `external:<name>` | `memory/external/<name>/`, `wiki/inbox/external/<name>/` |
## Master Memory
`MEMORY.md` and `wiki/syntheses/` are changed **only** through:
- OpenClaw directly (`MCP_MEMORY_CLIENT_ID=openclaw`), even if OpenClaw runs on top of Codex runtime
- `wiki_update` for OpenClaw and other clients with explicit write-ACL on `wiki/syntheses/`
- Proposal flow (`memory_propose_update`) for Codex, Claude Code, and external clients
For `client_id=openclaw`, the server blocks `memory_propose_update` on targets `MEMORY.md` and `wiki/syntheses/*`: OpenClaw is the master curator and must apply such changes directly.
## Security
- Secrets are not stored in memory.
- External clients write only to their namespace.
- Write operations use file locks (`fcntl`).
- After writing, a git commit is made (if the repository is initialized).
## Tests
```bash
python3 -m unittest discover -s tests -v
```
`MCP_MEMORY_ROOT` sets the directory with `MEMORY.md` and the `memory/` folder. `MCP_MEMORY_WIKI_ROOT` (default `$MCP_MEMORY_ROOT/wiki`) and `MCP_MEMORY_GIT_REPO` can be set separately if needed. For local setup, a JSON file can be passed through `MCP_MEMORY_CONFIG`.
## File Structure
```
mcp-memory/
src/
server.py ← MCP server (Python, stdio)
config/
server.json ← Namespace and permission configuration
examples/
claude-code.json ← Example .claude/settings.json
codex.json ← Example Codex config
README.md
```