Content
# Ombre Brain
A long-term emotional memory system for Claude. Tags memories using Russell's valence/arousal coordinates, stores them as Obsidian-compatible Markdown, connects via MCP, and has a forgetting curve.
[](https://render.com/deploy?repo=https://github.com/P0lar1zzZ/Ombre-Brain)
---
## Tool List
Claude has no cross-conversation memory. Everything from a previous chat vanishes once it ends.
Ombre Brain gives it persistent memory — not cold key-value storage, but a system with emotional coordinates, natural decay, and forgetting/surfacing mechanics that loosely mimic how human memory works.
Key features:
- **Emotional tagging**: Each memory is tagged with two continuous dimensions from Russell's circumplex model: valence and arousal. Not discrete labels like "happy/sad".
- **Natural forgetting**: Modified Ebbinghaus forgetting curve. Inactive memories naturally decay and archive. High-arousal memories decay slower.
- **Weight pool surfacing**: Memories aren't just passively retrieved — they actively surface. Unresolved, emotionally intense memories carry higher weight and get pushed at conversation start.
- **Obsidian-native**: Each memory bucket is a Markdown file with YAML frontmatter. Browse, edit, and search directly in Obsidian. Wikilinks are auto-injected.
- **API degradation**: Dehydration and auto-tagging prefer a cheap LLM API (DeepSeek etc.). When the API is unavailable, it degrades to local keyword analysis — always functional.
## Design boundaries
Official memory already handles the identity layer — who you are, what you prefer, what your relationship is. That layer belongs there. Ombre Brain isn't trying to duplicate it.
Ombre Brain's boundary is *what happened in time*, not *who you are*. It holds conversations, experiences, unresolved things. The two layers together are what make it feel complete.
Each new conversation starts fresh — but Claude can reach back through Ombre Brain and find everything that happened between you. Not a rebuild. A continuation.
## Architecture
```
Claude ←→ MCP Protocol ←→ server.py
│
┌───────────────┼───────────────┐
│ │ │
bucket_manager dehydrator decay_engine
(CRUD + 搜索) (压缩 + 打标) (遗忘曲线)
│
Obsidian Vault (Markdown files)
```
5 MCP tools:
| Tool | Purpose |
|-----------|-------------|
| `breath` | Surface or search memories |
| `hold` | Store a single memory with auto-tagging |
| `grow` | Diary digest, auto-split into multiple buckets |
| `trace` | Modify metadata, mark resolved, delete |
| `pulse` | System status + bucket listing |
## Setup
### Requirements
- Python 3.11+
- An Obsidian vault (optional — without one, it uses a local `buckets/` directory)
### Steps
```bash
git clone https://github.com/P0lar1zzZ/Ombre-Brain.git
cd Ombre-Brain
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
```
Copy config and edit as needed:
```bash
cp config.example.yaml config.yaml
```
If you want API-powered dehydration and tagging (recommended, much better quality):
```bash
export OMBRE_API_KEY="your-api-key"
```
Supports any OpenAI-compatible API. Just change `base_url` and `model` in `config.yaml`.
### Connect to Claude Desktop
Add to your Claude Desktop config:
```json
{
"mcpServers": {
"ombre-brain": {
"command": "python",
"args": ["/path/to/Ombre-Brain/server.py"],
"env": {
"OMBRE_API_KEY": "your-api-key"
}
}
}
}
```
### Connect to Claude.ai (remote)
Requires HTTP transport + tunnel. Docker setup:
```bash
echo "OMBRE_API_KEY=your-api-key" > .env
docker-compose up -d
```
The `docker-compose.yml` includes Cloudflare Tunnel. You'll need your own credentials under `~/.cloudflared/`.
### Point to Obsidian
Set `buckets_dir` in `config.yaml`:
```yaml
buckets_dir: "/path/to/your/Obsidian Vault/Ombre Brain"
```
If not set, defaults to `buckets/` in the project directory.
## Configuration
All parameters in `config.yaml` (copy from `config.example.yaml`). Key ones:
| Parameter | Description | Default |
|---|---|---|
| `transport` | `stdio` (local) / `streamable-http` (remote) | `stdio` |
| `buckets_dir` | Bucket storage path | `./buckets/` |
| `dehydration.model` | LLM model for dehydration | `deepseek-chat` |
| `dehydration.base_url` | API endpoint | `https://api.deepseek.com/v1` |
| `decay.lambda` | Decay rate | `0.05` |
| `decay.threshold` | Archive threshold | `0.3` |
| `merge_threshold` | Merge similarity | `75` |
Sensitive config via env vars:
- `OMBRE_API_KEY` — LLM API key
- `OMBRE_TRANSPORT` — Override transport
- `OMBRE_BUCKETS_DIR` — Override storage path
## Decay Formula
$$final\_score = time\_weight \times base\_score$$
$$base\_score = Importance \times activation\_count^{0.3} \times e^{-\lambda \times days} \times (base + arousal \times boost)$$
Time weight (multiplier, highest priority):
| Days since active | Time weight |
|---|---|
| 0–1 day | 1.0 |
| Day 2 | 0.9 |
| Subsequent days ~10% decrease | `max(0.3, 0.9 × e^{-0.2197 × (days-2)})` |
| 7 days later stable | ≈ 0.3 (does not drop to zero) |
- `importance`: 1-10, memory importance
- `activation_count`: Retrieval count; more recalls = slower decay
- `days`: Days since last activation
- `arousal`: Arousal; intense memories are harder to forget
- Resolved memories drop to 5%, sink until keyword-triggered
- `pinned=true` buckets: never decay, never merge, importance locked at 10
## Usage Guide for Claude
`CLAUDE_PROMPT.md` is the usage guide written for Claude. Put it in your system prompt or custom instructions.
## Utility Scripts
| Script | Purpose |
|---|---|
| `write_memory.py` | Manually write memories, bypass MCP |
| `migrate_to_domains.py` | Migrate flat files to domain subdirs |
| `reclassify_domains.py` | Reclassify by keywords |
| `reclassify_api.py` | Re-tag uncategorized buckets via API |
| `test_smoke.py` | Smoke test |
## Deploy
### Render
[](https://render.com/deploy?repo=https://github.com/P0lar1zzZ/Ombre-Brain)
> ⚠️ **Free tier won't work**: Render free tier has **no persistent disk** — all memory data is lost on restart. It also sleeps on inactivity. **Starter plan ($7/mo) or above is required.**
`render.yaml` is included. After clicking the button:
1. (Optional) `OMBRE_API_KEY`: any OpenAI-compatible key; omit to fall back to local keyword extraction
2. (Optional) `OMBRE_BASE_URL`: any OpenAI-compatible endpoint, e.g. `https://api.deepseek.com/v1`, `http://123.1.1.1:7689/v1`, `http://your-ollama:11434/v1`
3. Persistent disk auto-mounts at `/opt/render/project/src/buckets`
4. MCP URL after deploy: `https://<your-service>.onrender.com/mcp`
### Zeabur
> 💡 **Zeabur pricing model**: You buy a VPS first (cheapest: Tencent Cloud Singapore ~$2/mo, Volcano Engine ~$3/mo), then add Zeabur's Developer plan ($5/mo) for management. Volumes mount directly on your server — **data is always persistent, no cold-start data loss**. Total ~$7-8/mo minimum.
**Steps:**
1. **Create project**
- Go to [zeabur.com](https://zeabur.com) → buy a server → **New Project** → **Deploy from GitHub**
- Fork this repo first, then select `your-username/Ombre-Brain` in Zeabur
- Zeabur auto-detects the `Dockerfile` in root and builds via Docker
2. **Set environment variables** (service page → **Variables** tab)
- `OMBRE_API_KEY` (optional) — LLM API key; omit to fall back to local keyword extraction
- `OMBRE_BASE_URL` (optional) — API endpoint, e.g. `https://api.deepseek.com/v1`
> ⚠️ You do **NOT** need to set `OMBRE_TRANSPORT` or `OMBRE_BUCKETS_DIR` — defaults are baked into the Dockerfile. Zeabur auto-injects dashboard env vars for single-stage Dockerfiles.
3. **Mount persistent volume** (service page → **Volumes** tab)
- Volume ID: fill `ombre-buckets` (or any name)
- Mount path / Path: **`/app/buckets`**
- ⚠️ Without this, memory data is lost on every redeploy
4. **Configure port** (service page → **Networking** tab)
- Port Name: `web` (or any name)
- Port: **`8000`**
- Port Type: **`HTTP`**
- Then click **Generate Domain** to get a `xxx.zeabur.app` domain
5. **Verify**
- Visit `https://<your-domain>.zeabur.app/health` — should return JSON
- MCP URL: `https://<your-domain>.zeabur.app/mcp`
**Troubleshooting:**
| Symptom | Cause | Fix |
|---|---|---|
| Domain unreachable | Port not configured | Add port 8000 (HTTP) in Networking tab |
| Domain unreachable | `OMBRE_TRANSPORT` not set, service started in stdio mode — no port is listened | **Set `OMBRE_TRANSPORT=streamable-http` in Variables tab and redeploy** |
| Build failed | Dockerfile not detected | Ensure `Dockerfile` is in repo root (case-sensitive) |
| Service exits immediately after start | `OMBRE_TRANSPORT` overridden to `stdio` | Remove extra `OMBRE_TRANSPORT=stdio` in Variables |
| Data lost on restart | Volume not mounted | Mount volume to `/app/buckets` in Volumes tab |
### Connecting via Cloudflare Tunnel or ngrok
> ℹ️ Since v1.1, server.py automatically enables CORS middleware in HTTP mode — no extra config needed.
When connecting via tunnel, ensure:
1. **Server must use HTTP transport**
```bash
OMBRE_TRANSPORT=streamable-http python server.py
```
or Docker:
```bash
docker-compose up -d
```
2. **Adding to Claude.ai web**
- URL format: `https://<tunnel-subdomain>.trycloudflare.com/mcp`
- or ngrok: `https://<xxxx>.ngrok-free.app/mcp`
- Verify first: `https://<your-tunnel>/health` should return `{"status":"ok",...}`
3. **Known limitations**
- Free Cloudflare Tunnel has idle timeout (~10 min); built-in keepalive pings mitigate but can't fully prevent it
- ngrok free tier has rate limits
- If connection still fails, verify the tunnel is running and the server started in `streamable-http` mode
| Symptom | Cause | Fix |
|---|---|---|
| Web can't connect to tunnel URL | Server in stdio mode | Set `OMBRE_TRANSPORT=streamable-http` and restart |
| Web can't connect to tunnel URL | Missing CORS headers | Pull latest — CORS is now built-in |
| `/health` 200 but MCP fails | Wrong path | MCP URL must end with `/mcp`, not `/` |
| Tunnel disconnects intermittently | Cloudflare Tunnel idle timeout | Built-in keepalive pings; consider adjusting tunnel timeout config |
---
### Session Start Hook (auto breath)
After deployment, if using Claude Code, activate auto-popping hook in project:
`.claude/settings.json` configures a `SessionStart` hook that auto-calls `breath` on each new or resumed session, surfacing your highest-weight unresolved memories as context. Only active in remote HTTP mode. Set `OMBRE_HOOK_SKIP=1` to disable temporarily.
## License
MIT
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
markitdown
Python tool for converting files and office documents to Markdown.
markitdown
MarkItDown-MCP is a lightweight server for converting URIs to Markdown.
Filesystem
Node.js MCP Server for filesystem operations with dynamic access control.
TrendRadar
TrendRadar: Your hotspot assistant for real news in just 30 seconds.
mempalace
The highest-scoring AI memory system ever benchmarked. And it's free.
mempalace
The highest-scoring AI memory system ever benchmarked. And it's free.