Skip to main content
Glama
README.md
# mcp-mem0

An **MCP server** for [mem0](https://docs.mem0.ai) (self-hosted / open source)
backed by **Qdrant**. Each request targets a **Qdrant collection named by an HTTP
header**, so clients that send the same name share memories and different names
stay isolated.

## Tools

| Tool | Purpose |
|------|---------|
| `add_memory` | Store memories (LLM fact-extraction, or raw with `infer=false`) |
| `search_memory` | Semantic search within the request's collection |
| `update_memory` | Replace a memory's content by id |
| `delete_memory` | Delete a memory by id |
| `get_memory` | Fetch one memory by id |
| `list_memories` | List memories for a scope |
| `memory_history` | Change history of a memory |
| `delete_all_memories` | Delete all memories for a scope |

## How collection routing works

- The **collection name** is read from an HTTP header (`X-Collection` by default) and
  mapped to the Qdrant collection `mem0_<name>` (prefix configurable; set it empty to
  use the value verbatim). The header value is validated against `^[A-Za-z0-9_-]{1,64}$`.
- **Sharing:** any clients that send the same collection name share the same memories.
  Different names are isolated.
- One `AsyncMemory` instance is built and cached per collection.
- The collection is chosen **solely by the header** — `user_id` / `agent_id` / `run_id`
  never affect which collection is used; they only narrow *within* a collection and
  are optional. Searching / listing without any of them spans the whole collection.
- `add_memory` still needs one id to store (mem0 requirement); a default `user_id`
  (`MEM0_DEFAULT_USER_ID`) is applied when the caller omits all three on a write.

## Quick start

```bash
# 1. Start Qdrant
docker compose up -d qdrant

# 2. Configure
cp .env.example .env
# edit .env: set OPENAI_API_KEY (or switch to the Ollama block for a key-free setup)

# 3. Run the server (streamable-HTTP on :8080/mcp)
uv run -m mcp_mem0
```

Health check: `curl localhost:8080/health`

## Docker

The [Dockerfile](Dockerfile) is multi-stage with two targets: `runtime` (the server)
and `test` (the suite).

### With docker compose (recommended)

```bash
cp .env.example .env          # set OPENAI_API_KEY (or the Ollama block)

# Qdrant + server together
docker compose up -d --build qdrant server
curl localhost:8080/health

# Run unit tests (no Qdrant / keys needed)
docker compose run --rm tests

# End-to-end smoke against the running server (needs an embedder key)
docker compose run --rm tests python tests/smoke.py
```

Inside the compose network the server reaches Qdrant at `QDRANT_HOST=qdrant`
(set automatically); tests reach the server at `MCP_URL=http://server:8080/mcp`.

### With plain docker

```bash
# Service image
docker build --target runtime -t mcp-mem0 .
docker run --rm -p 8080:8080 --env-file .env -e QDRANT_HOST=host.docker.internal mcp-mem0

# Test image
docker build --target test -t mcp-mem0-test .
docker run --rm mcp-mem0-test            # pytest -q
```

## Verify

```bash
# Unit tests (no Qdrant needed)
uv run pytest -q

# End-to-end smoke test (server + Qdrant + embedder must be running)
uv run python tests/smoke.py

# Confirm per-client collections were created
curl localhost:6333/collections
```

## Configuration

All settings come from environment variables / `.env` — see [.env.example](.env.example).
Key ones: `MEM0_COLLECTION_HEADER`, `MEM0_COLLECTION_PREFIX`, `QDRANT_HOST`/`QDRANT_PORT`
(or `QDRANT_URL`+`QDRANT_API_KEY`), `MEM0_EMBEDDING_DIMS` (must match the embedder),
and the `MEM0_LLM_*` / `MEM0_EMBEDDER_*` provider blocks (openai / ollama / …).