mem-universe
by H1an1
README.md
# mem-universe
A self-hosted **MCP memory server** that gives a personal multi-agent fleet
(Claude Code, ChatGPT, Codex, Cursor, and any other MCP client) **one shared,
git-backed memory**. Agents install nothing. They connect over MCP and
`search` / `read` / `write`.
> The problem it solves: *"Claude on one machine figured out how to do X, but
> Codex on another machine has no idea."* One memory, every agent, instantly.
---
## The idea
Most "give your agent memory" setups sync skill files into each tool, in each
tool's own format. That does not scale across a fleet: every new agent needs a
converter, and knowledge learned in one place is invisible everywhere else.
mem-universe flips it: keep **one** plain-text store, and let every agent read
and write it **at use time** over MCP. A skill is just markdown any agent can
follow, so there is nothing to convert. Learn something once, and the whole
fleet can recall it on the next task.
---
## Architecture
```mermaid
flowchart LR
subgraph agents [Your agent fleet]
A1[Claude Code]
A2[ChatGPT]
A3[Codex / Cursor]
A4[Other MCP clients]
end
A1 -- "Bearer token" --> S
A3 -- "Bearer token" --> S
A4 -- "Bearer token" --> S
A2 -- "token in URL path" --> S
subgraph server [mem-universe server · FastMCP over HTTP]
S[auth + path validation]
S --> T["tools: search / read / write / list / delete<br/>put_skill / get_skill / list_skills"]
T --> IDX[("BM25 index<br/>local, rebuildable")]
T --> ST[git working copy]
end
ST -- "commit, then async push" --> GH[("private store repo<br/>on GitHub")]
ST --> L1["shared/ · skills, lessons, rules"]
ST --> L2["personal/ · owner profile, notes"]
```
### Key design decisions
1. **MCP is the integration layer, not file-syncing.** Knowledge is stored once
as plain text and retrieved on demand. Add one entry and the whole fleet sees
it, with no per-agent format conversion.
2. **Git is the database.** The store is a git repo of markdown files. Every
`write` is a commit, then an async push to GitHub. You get version history,
recoverable deletes (delete is also a commit), human-readable data, and
"backup" for free. Writes are serialized by a single-writer lock; reads come
from the local clone, so they never wait on the network. Last write wins, so
there are no merge conflicts by design.
3. **Code and data live in two separate repos.** This server (code) is one repo.
Your memory (data) is a *separate, private* store repo that the server clones
and writes to. That split is why this codebase can be public while your
memory stays private. **Never put the store in this repo.**
4. **Two layers, one permission.** `shared/` is cross-agent knowledge (skills,
lessons, rules). `personal/` is the owner's own content. Any valid token can
read and write everything: tokens gate *who connects*, not *which layer*. For
a single owner's fleet on a private network, scoped permissions are
over-engineering. Anything truly secret should not go in the store at all.
5. **Retrieval, not installation.** Agents install nothing. To make an agent
*use* the library, drop a one-line "recall first" marker into its existing
instruction file (`CLAUDE.md`, `AGENTS.md`, ...) telling it to `search`
before acting. Multi-file, runnable skills travel as packages
(`put_skill` / `get_skill`).
6. **Two ways to authenticate, so Claude and ChatGPT both connect.** Clients
that can send headers use `Authorization: Bearer <token>`. Clients that
cannot set custom headers (such as a ChatGPT connector) put the token in the
URL path: `https://<host>/<token>/mcp`. Same server, both worlds.
7. **Hand-rolled, bilingual BM25 search.** No external embedding service. It is
light, works offline, and tokenizes mixed CJK + English. The index lives
locally and is never committed (it rebuilds from the store).
---
## The tools
| Tool | What it does |
|------|--------------|
| `search` | BM25 search across the store; returns ranked `{path, score, snippet, type}` |
| `read` | Read one entry by store-relative path |
| `write` | Write an entry (also indexes it); `layer` inferred from the path |
| `list` | List entries under a layer / prefix |
| `delete` | Delete an entry (git keeps history, so it is recoverable) |
| `put_skill` | Store a multi-file skill package under `shared/skills/<name>/` |
| `get_skill` | Fetch a skill package to install locally |
| `list_skills` | List available skill packages |
## Layers
```
<store>/
├── shared/ # cross-agent knowledge: skills / tools / lessons / rules
└── personal/ # the owner's own content: profile, notes, imports
```
A `write` path starts with the layer name (`shared/...` or `personal/...`); the
`layer` argument is optional and inferred from the path.
## What happens on one write
```
write(path, content)
-> validate path (must stay in-layer; reject ../ and cross-layer symlinks)
-> acquire single-writer lock
-> write file + stamp frontmatter
-> git commit
-> return ok
-> (background) async push to the private store repo
-> update the local BM25 index
```
Reads and searches always hit the local clone, so they do not block on git.
---
## Deploy
Full steps are in [`DEPLOY.md`](DEPLOY.md). The short version (Docker):
```bash
git clone https://github.com/H1an1/mem-universe && cd mem-universe/deploy
cp mem.env.example mem.env # set MEMORY_TOKEN and MEM_STORE_REMOTE (your private store repo)
# edit Caddyfile: point your domain at this VPS
docker compose up -d --build
```
This runs the server behind Caddy (automatic TLS). Agents reach it at
`https://<your-domain>/mcp`. Generate a token with `openssl rand -hex 32`.
A Tailscale-only (no public exposure) compose file is also included.
## Connect your agents
All clients need the **URL** plus a **token**.
**Claude Code**
```bash
claude mcp add --transport http mem-universe https://<your-domain>/mcp \
--header "Authorization: Bearer <YOUR_TOKEN>"
```
**Claude Desktop / claude.ai** (custom connector): add an HTTP MCP server with
URL `https://<your-domain>/mcp`. If the client supports a header, use
`Authorization: Bearer <YOUR_TOKEN>`; otherwise use the path-token URL below.
**ChatGPT** (custom connector): ChatGPT connectors cannot set custom headers, so
put the token in the path:
```
https://<your-domain>/<YOUR_TOKEN>/mcp
```
**Cursor / Codex / others**: add an HTTP MCP server with the same URL and an
`Authorization: Bearer <YOUR_TOKEN>` header (see your client's MCP docs).
Every agent uses the same token. To make agents actually *recall* before acting,
add a short "recall first" marker to each agent's instruction file.
---
## Security model
- One token unlocks every layer; tokens gate connection, not content. A
read-only token variant exists for untrusted external readers.
- The server rejects path traversal (`../`) and cross-layer symlinks at the
filesystem level, independent of auth.
- Run it on a private network (such as Tailscale) or behind TLS with a strong
token. **Anything that truly must never be read by any agent does not belong
in the store.**
- Secrets live only in `deploy/mem.env` (gitignored). Never commit them.
## How it was built
Built with a maker/checker loop: each feature was written, then reviewed by a
separate pass plus an adversarial verifier whose job was to break it. That
caught real bugs before release, including a cross-layer symlink that let a
read-scoped token reach another layer, and an `rmtree` that followed symlinks.
The suite has 100+ tests and is linted clean.
## Develop
```bash
uv sync # create venv (Python 3.12) + install deps
uv run pytest # tests
uv run ruff check .
uv run mem-server # run the MCP server over stdio
```
## License
[MIT](LICENSE)
TDQS
A3.6/5.0
Scored across 8 tools
Disambiguation5/5
Each tool serves a distinct purpose: file operations (delete, read, write), skill management (get_skill, put_skill, list_skills), and discovery (list, search). No two tools have overlapping functionality.
Naming Consistency4/5
Names are lowercase with underscores for multi-word tools (e.g., get_skill), but single-word names like delete and read break the pattern slightly. Still, the naming is predictable and readable.
Tool Count5/5
8 tools cover core operations for memory files and skills without bloat. The set is well-scoped for the stated purpose.
Completeness4/5
Covers create/read/delete for files and CRUD for skills except delete_skill. Write presumably overwrites, acting as update. Minor gap: no dedicated skill deletion tool.
Maintenance
ActivityInactive
ResponsivenessNo issues