Metadata-Version: 2.4
Name: mnemox
Version: 0.2.0
Summary: mnemox local-first agent memory system with MCP server. Atom-based context for AI agents.
License: MIT
Project-URL: Homepage, https://github.com/ajadi/mnemox
Keywords: mcp,memory,llm,claude,ai-agent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: mcp<2,>=1.10
Requires-Dist: onnxruntime~=1.20
Requires-Dist: tokenizers~=0.21
Requires-Dist: numpy~=2.1
Requires-Dist: argon2-cffi<24,>=23.1
Requires-Dist: cryptography<46,>=42
Requires-Dist: keyring<26,>=24
Provides-Extra: vec
Requires-Dist: sqlite-vec>=0.1.6; extra == "vec"
Dynamic: license-file

<div align="center">

<img src="assets/logo.svg" alt="Ember" width="420">

### Your Claude Code sessions never forget again.

Local-first memory via MCP — one SQLite file, on-device embeddings,<br>
encrypted cross-machine sync. No cloud. No Docker.

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)
[![MCP](https://img.shields.io/badge/MCP-native-8A2BE2.svg)](https://modelcontextprotocol.io/)
[![tests](https://img.shields.io/badge/tests-727%20passing-brightgreen.svg)](#status)

<img src="assets/demo.svg" alt="Ember recalls a past decision in a fresh Claude Code session" width="760">

</div>

---

## The problem

- **Claude forgets between sessions.** Every `/clear` or new window throws away the context you built — decisions, trade-offs, gotchas — and you re-explain them from scratch.
- **Cloud memory pulls your context off-machine and costs money.** mem0 & co. ship your project history to someone else's servers and meter it.
- **Self-hosted memory means heavy infra.** Qdrant + Neo4j + Docker is a lot of moving parts just to remember what you decided yesterday.

**Ember solves it with one SQLite file on your machine.**

---

## Why Ember

|                                       | **Ember 🔥** | mem0 (cloud) | OpenMemory | Self-hosted stack |
|---------------------------------------|:------------:|:------------:|:----------:|:-----------------:|
| Runs fully local (no cloud)           | ✅           | ❌           | ⚠️ partial | ✅                |
| No heavy infra (no Docker/Qdrant/Neo4j) | ✅         | ✅           | ❌         | ❌                |
| Single-file store (SQLite)            | ✅           | ❌           | ❌         | ❌                |
| MCP-native                            | ✅           | ⚠️           | ⚠️         | ⚠️                |
| Zero-knowledge E2E sync               | ✅           | ❌           | ❌         | ❌                |

Ember is the only one that is **local, single-file, MCP-native, zero-infra, and privately synced** at the same time.

> ¹ mem0 "no heavy infra" = managed SaaS — you run nothing, but your context goes to their cloud.

---

Already using Claude Code? Just tell it: *"set up Ember for me"* — it can run the installer and wire up your `.mcp.json`.

---

## 60-second quickstart

**1. Install** (creates a dedicated venv at `~/.ember/venv` and fetches the embedding model):

```sh
git clone https://github.com/ajadi/ember && cd ember
./install.sh
```

**2. Connect to Claude.** Add one entry to your project's `.mcp.json`
(replace `<your-project-root>` with the absolute path to your project):

```json
{
  "mcpServers": {
    "ember": {
      "command": "~/.ember/venv/bin/python",
      "args": ["-m", "ember", "serve"],
      "env": {
        "EMBER_MODE": "project",
        "EMBER_PROJECT_ROOT": "<your-project-root>"
      }
    }
  }
}
```

**3. Verify.** Restart Claude Code and ask it to write and recall a memory — the
`ember_write`, `ember_search`, and `ember_list` tools appear in the MCP tool list.

> `ember init` prints the exact `.mcp.json` snippet for your platform, including
> the absolute Python path. See [Reference](#reference) for details.

---

## How it works

```mermaid
flowchart LR
    A["atoms<br/>(decisions, notes,<br/>questions, retros)"] --> B["SQLite<br/>(one file)"]
    B --> C["MCP server<br/>(python -m ember serve)"]
    C --> D["Claude Code"]
    D -- "ember_write / ember_search" --> C
```

Claude writes **atoms** (decisions, observations, questions, retrospectives) into a
single local SQLite file. On recall, Ember ranks them with multi-tier retrieval
(FTS5 BM25 + on-device cosine fusion) and serves them straight back over MCP.

- **All local.** Memory lives in one SQLite file on your machine — nothing leaves it.
- **On-device embeddings.** EmbeddingGemma-300m runs locally via ONNX; no embedding API calls.
- **Private by design.** Cross-machine sync is end-to-end encrypted — the relay sees only ciphertext. Your memories never leave your control in plaintext.

---

## Status

**✅ Works today (Phase 1–3)**
- Local-first MCP memory server (`python -m ember serve`) with 7 tools: write, search, get, list, invalidate, flush, stats
- On-device EmbeddingGemma-300m embeddings (ONNX, no network)
- Single-file SQLite store, `project` and `global` modes
- Multi-tier retrieval R0–R3 (exact, cosine, FTS5 BM25, fusion)
- Auto-invalidation of superseded memories by cosine similarity
- Transparent `sqlite-vec` auto-upgrade for large stores (graceful fallback to pure-Python cosine)
- Universal native-hook capture loop with durable spool + `ember drain`
- CLI: `init` / `serve` / `mirror` / `drain` / `backfill`
- Capture memory from Forge workflows (optional adapter)
- **Encrypted, zero-knowledge cross-machine sync** via a self-hostable hub — your memories never leave your control in plaintext (real-world multi-machine-over-network validation in progress)
- Oplog backfill — retroactively sync memories written before sync was enabled (`ember backfill`)

**📋 Planned**
- Hosted zero-knowledge sync hub (managed option)

> Actively developed — **star to follow.**

---

## Reference

<details>
<summary><strong>CLI commands</strong></summary>

```
ember [--version]
  init [--mode {project,global,server}]   # initialise store + write config.json
  serve                                    # start the MCP server over stdio
  mirror --project-id <id> [--output-dir memory] [--db-path PATH]
  drain [--queue-path PATH] [--db-path PATH] [--project-id ID]
```

- **`init`** — creates the store and writes `config.json`. Idempotent: re-running preserves `project_id` and `created`, refreshes `ember_version` / `last_init`. Prints the exact `.mcp.json` snippet for your platform. `--mode server` initialises a server-mode store and config (`client_id` + `hub_url`) for hub sync; set `EMBER_HUB_TOKEN` in the environment.
- **`serve`** — starts the Ember MCP server over stdio (register this in `.mcp.json`).
- **`mirror`** — renders stored atoms to Markdown under `memory/` (or `--output-dir`).
- **`drain`** — drains the capture queue into the store (used by the capture loop).

| Mode | DB location | config.json |
|------|-------------|-------------|
| `project` (default) | `<project-root>/.ember/ember.db` | `<project-root>/.ember/config.json` |
| `global` | `~/.ember/global.db` | `~/.ember/config.json` |
| `server` | `<project-root>/.ember/ember.db` | `<project-root>/.ember/config.json` (server: client_id + hub_url) |

</details>

<details>
<summary><strong>Environment variables</strong></summary>

| Variable | Description | Default |
|----------|-------------|---------|
| `EMBER_MODE` | `project` or `global` | `project` |
| `EMBER_PROJECT_ROOT` | Root directory for project-mode DB | `cwd` |
| `EMBER_DB_PATH` | Explicit DB path (overrides mode routing) | — |
| `EMBER_MODEL_DIR` | Override for ONNX model cache directory | `~/.ember/models/embeddinggemma-300m/` |
| `EMBER_INVALIDATION_THRESHOLD` | Cosine threshold for auto-invalidation (0–1] | `0.92` |
| `ANTHROPIC_API_KEY` | Enables optional `ember_flush` enrichment via Claude Haiku; absent ⇒ enrichment skipped | — |

</details>

<details>
<summary><strong>Global mode .mcp.json</strong></summary>

One shared DB for all projects:

```json
{
  "mcpServers": {
    "ember": {
      "command": "~/.ember/venv/bin/python",
      "args": ["-m", "ember", "serve"],
      "env": { "EMBER_MODE": "global" }
    }
  }
}
```

</details>

<details>
<summary><strong>Model cache</strong></summary>

EmbeddingGemma-300m ONNX weights are stored at
`~/.ember/models/embeddinggemma-300m/` (overridable via `EMBER_MODEL_DIR`).
`install.sh` fetches them on first run and skips the download afterwards.

**One-time setup (gated model):** EmbeddingGemma-300m requires a Hugging Face
account and license acceptance before the first download:

1. Accept the license at <https://huggingface.co/google/embeddinggemma-300m>
2. Run `huggingface-cli login` (token from <https://huggingface.co/settings/tokens>)
3. Re-run the installer (or fetch manually below).

To fetch manually:

```sh
huggingface-cli download google/embeddinggemma-300m \
    --local-dir ~/.ember/models/embeddinggemma-300m
```

</details>

<details>
<summary><strong>MCP tools</strong></summary>

| Tool | Purpose |
|------|---------|
| `ember_write` | Write a memory record (atom). |
| `ember_search` | Multi-tier retrieval (BM25 + cosine fusion); tier `R0`–`R3`. |
| `ember_get` | Fetch a single record by ID (project-scoped). |
| `ember_list` | List records for a project, newest first. |
| `ember_invalidate` | Soft-delete (invalidate) a record. |
| `ember_flush` | Optional Haiku enrichment pass (requires `ANTHROPIC_API_KEY`; skipped otherwise). |
| `ember_stats` | Record counts and last-flush timestamp. |

</details>

<details>
<summary><strong>Optional dependencies</strong></summary>

Vector-search acceleration (transparent; activates automatically for large stores):

```sh
pip install "ember[vec]"
```

Without `sqlite-vec`, Ember stays on pure-Python cosine — no configuration needed.

</details>

<details>
<summary><strong>Platform support</strong></summary>

| Platform | Status |
|----------|--------|
| macOS (Apple Silicon / Intel) | Supported |
| Linux (x86-64, arm64) | Supported |
| Windows | Best-effort |

See [Windows setup (best-effort)](#windows-setup-best-effort) below.

</details>

---

## Windows setup (best-effort)

> Windows support is best-effort. The native installer (`install.ps1`) covers the common case; edge cases (antivirus, execution-policy restrictions, PATH quirks) may need manual adjustment.

**Prerequisites**

- Python 3.10+ — download from [python.org](https://www.python.org/) or run:
  ```
  winget install Python.Python.3.12
  ```
  Ensure Python is on your `PATH` (the installer wizard has an "Add Python to PATH" checkbox).

**Recommended shell**

- **PowerShell** — use `install.ps1` (native Windows, no git-bash required):
  ```powershell
  Set-ExecutionPolicy -Scope CurrentUser RemoteSigned   # once, if needed
  .\install.ps1
  ```
- **git-bash** — alternatively run the POSIX `install.sh` inside git-bash.

**After install — `.mcp.json` command path**

On Windows the Python executable is `Scripts\python.exe`, not `bin/python`:

```json
{
  "mcpServers": {
    "ember": {
      "command": "%USERPROFILE%\\.ember\\venv\\Scripts\\python.exe",
      "args": ["-m", "ember", "serve"],
      "env": {
        "EMBER_MODE": "project",
        "EMBER_PROJECT_ROOT": "<your-project-root>"
      }
    }
  }
}
```

`install.ps1` prints the exact snippet (with your resolved `%USERPROFILE%` path) at the end of the install.

**Model download (gated)**

EmbeddingGemma-300m requires a one-time license acceptance and `huggingface-cli login` before the download will succeed. See the [Reference](#reference) note above.

---

## Contributing

Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). Issues
labelled **`good first issue`** are a friendly place to start.

## License

[MIT](LICENSE) © 2026 David Sandler
