# OmniMem

> Self-hosted semantic memory for AI Agents. Persistent sessions, experience scoring, and a graveyard for dead ends, backed by knowledge acquired from the RSS you read and stored in a Valkey vector search then exposed as an MCP server.

- **Type:** MCP server
- **Install:** `agentstack add mcp-richarvey-omnimem`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [richarvey](https://agentstack.voostack.com/s/richarvey)
- **Installs:** 0
- **Category:** [Search](https://agentstack.voostack.com/c/search)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [richarvey](https://github.com/richarvey)
- **Source:** https://github.com/richarvey/OmniMem
- **Website:** https://omnimem.org

## Install

```sh
agentstack add mcp-richarvey-omnimem
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# \
Development happens on Codeberg (https://codeberg.org/ric_harvey/omnimem) — issues and PRs there please.
---

[](https://codeberg.org/ric_harvey/omnimem/actions)
[](https://codeberg.org/ric_harvey/omnimem/actions)
[](https://codeberg.org/ric_harvey/omnimem/actions)

> [!WARNING]
> **SSE transport is deprecated.** OmniMem 3.10 defaults to SSE but now supports Streamable HTTP via `MCP_TRANSPORT=http`. SSE will be removed in a future release. To migrate: set `MCP_TRANSPORT=http` in your `.env` and update your client config to use `"type": "http"` with URL `.../mcp` instead of `.../sse`. See the [connection guides](guides/) for updated examples.
>
> Streamable HTTP support is a community contribution from [@timstoop](https://codeberg.org/timstoop) — thanks!

**Stop living the same session twice.**

Every Claude Code session starts from zero. No memory of your project. No memory of what failed last week. No memory that you spent three hours last Tuesday discovering why `onnxruntime` explodes on Alpine before finding something that actually works.

So you explain the project again. Claude suggests the same broken library again. Same alarm. Same song. You are Bill Murray and Claude is Punxsutawney.

OmniMem fixes that. It is a self-hosted MCP server that gives Claude Code persistent memory across sessions, projects, and machines. It runs on your own hardware and it is free forever.

---

## What it does

OmniMem gives Claude Code four things it currently lacks.

**Episodic memory** is the decisions you made, the bugs you fixed, the patterns you discovered. The things that took real effort to learn and should not have to be re-learned every morning.

**Project context** is your stack, goals, and current state. Claude arrives at every session already briefed rather than starting cold.

**Passive knowledge** comes from RSS feeds you configure. They get fetched on a schedule, summarised by Claude Haiku, embedded, and stored. When you are working on a Rust problem and a relevant article was ingested last week, it surfaces as a starting point worth reading.

**Preferences** are prescriptive rules about how you want to work — "always update the README and CHANGELOG after a feature lands", "prefer terse responses with no trailing summary". These are extracted from your conversations automatically (via the new fact-extraction ingest mode) and surfaced whenever they apply.

All four namespaces are searched together at recall time. The top result might be a decision from six months ago on a different project, a solution from yesterday, or an article that landed in your knowledge base on Tuesday night. It does not matter where it came from as long as it is useful.

---

## The bits no other memory system has

### Memory is not binary

Most systems remember or forget. OmniMem has a lifecycle:

```
ACTIVE  ->  DEPRIORITISED  ->  ARCHIVED  ->  DELETED
 1.0x         0.2x             0.0x        gone
```

When you say "forget about X" you do not usually mean destroy it. You mean stop surfacing it. OmniMem deprioritises rather than deletes, applying a surface score multiplier at recall time. If something becomes relevant again later it can earn its way back.

You can also suppress entire topics. Calling `suppress_topic("pisource.org")` means nothing touching that topic surfaces in any recall, across any session, until you lift it.

### The Graveyard

OmniMem tracks not just what worked but what did not and why.

Every abandoned approach gets logged with its name, type, and reason for failure. Before Claude suggests a library or architectural pattern the graveyard is checked first. If you tried something before and gave up on it, that warning surfaces at the top of results before anything else does.

```
WARNING: previously abandoned approaches match this query

  onnxruntime       library     SIGILL crash on Alpine musl libc       effort: 4/5
  FLAT index        approach    too slow above 10k vectors              effort: 3/5
  openai embeddings service     API cost and latency were prohibitive   effort: 2/5
```

Dead ends do not get a second chance to waste your afternoon.

### Experience scoring

Not all successful memories are equal. Something that worked first time is useful. Something that took four attempts, two abandoned libraries, and a weird Alpine-specific workaround to crack is gold, and it should surface more readily.

OmniMem assigns an experience weight to every memory based on effort and outcome:

| Effort | Meaning | Recall weight |
|---|---|---|
| 1 | Worked first time | 1.0x |
| 2 | Minor friction | 1.1x |
| 3 | Multiple iterations | 1.25x |
| 4 | Significant struggle | 1.5x |
| 5 | Battle-hardened | 1.8x |

The recall score formula:

```
score = similarity x surface_score x recency x experience_weight
```

A score-5 success is worth nearly twice as much in recall ranking as something trivial. Knowledge earns its rank.

### Semantic deduplication

Over time memory systems accumulate near-identical entries. OmniMem catches this at two points.

At write time, `remember()` embeds the new content and checks for existing memories above a cosine similarity threshold (default 0.92, configurable via `DEDUP_SIMILARITY_THRESHOLD`). If a near-identical memory already exists it returns the duplicate instead of storing a redundant copy. Pass `force=True` when you genuinely want both versions.

For bulk cleanup, `find_duplicates()` scans an entire namespace, batch-embeds everything, computes pairwise similarity, and returns clusters of duplicates grouped by union-find. Point it at your episodic namespace once a month and archive the extras.

### Contradiction detection

The graveyard warns you about things that failed. Contradiction detection warns you about things that disagree with each other.

When `remember()` stores a new memory it runs a fast heuristic check — finding semantically similar memories and scanning for negation pattern mismatches (e.g. one says "use X" while the other says "avoid X"). If a potential contradiction is detected it stores the memory but returns a warning so you can investigate.

For deeper analysis, `check_contradictions()` can optionally call Claude Haiku (Tier 2) to evaluate candidate pairs. Confirmed contradictions are cross-linked on both memories and flagged whenever either one surfaces in a `recall()`.

```
contradiction_warning:
  existing_key: mem:episodic:01ARZ3NDEK...
  existing_content: "Always use connection pooling for Valkey..."
  explanation: "These memories discuss the same topic but contain opposing language"
```

### Session briefing

Instead of making three separate calls at session start, a single `briefing(project="myproject")` returns everything Claude needs to get up to speed:

- **Project context** — current state, stack, last update
- **Experience summary** — effort stats, graveyard, breakthroughs
- **Stale memories** — active memories not updated in 30+ days (configurable via `STALE_MEMORY_DAYS`)
- **New knowledge** — RSS articles ingested in the last 7 days
- **Contradiction warnings** — memories with unresolved contradictions
- **Reinstate candidates** — deprioritised memories whose reinstate hints match current work
- **Suppressed topics** — what is currently filtered out

One tool call, one response, full context.

### Automatic maintenance

Memory systems accumulate duplicates and contradictions over time. OmniMem handles this automatically.

Every N `briefing()` calls per project (default 10, configurable via `AUTO_MAINTENANCE_INTERVAL`), the server runs a maintenance pass:

1. **Dedup scan** — finds clusters of near-identical episodic memories and archives the oldest in each cluster, keeping the newest
2. **Contradiction scan** — checks semantically similar active project memories for negation pattern mismatches (requires cosine similarity >= 0.5 before checking, capped at 10 results)
3. **Knowledge expiry** — archives RSS-ingested knowledge articles that have passed their `expires_at` timestamp (default 30 days after ingestion, configurable via `MAX_KNOWLEDGE_AGE_DAYS`). Manually stored knowledge items are never affected

The results appear in the briefing response under `auto_maintenance` so you know what was cleaned up. Set `AUTO_MAINTENANCE_INTERVAL=0` to disable. Manual `find_duplicates()` and `check_contradictions()` calls still work as before.

---

## Self-hosted, open source, yours

No SaaS. No vendor lock-in. No context shipped to someone else's servers.

- **Valkey** is an open source Redis fork. All your data stays in a named Docker volume on your own machine.
- **Multi-arch Docker images** for amd64 and arm64. It runs on a Raspberry Pi, AWS Graviton, or Apple Silicon just as well as x86.
- **sentence-transformers** runs embeddings locally with no API calls.
- **MIT licensed** means fork it, extend it, run it wherever you want.
- **One backup command** calls `dump_to_file()` and exports everything to a JSON file you own.

Expose the MCP port through Traefik and every machine you work from shares the same memory. One deployment, everywhere.

---

## Quick start

```bash
git clone https://codeberg.org/ric_harvey/omnimem.git && cd omnimem
cp .env.example .env
# Set VALKEY_PASSWORD and ANTHROPIC_API_KEY in .env
docker compose up -d
```

Edit the `.env` file to set at least `VALKEY_PASSWORD` to a secure value. You can also set `ANTHROPIC_API_KEY` if you want AI-powered RSS article summaries and richer contradiction detection. If you leave `ANTHROPIC_API_KEY` unset (or blank), OmniMem still works — the RSS worker will fall back to simple truncation for summaries, and contradiction checks will use embedding similarity only.

Four containers start: Valkey with vector search, the OmniMem MCP server, the RSS worker, and the web UI. The MCP server listens on port `8765` by default and the web UI on port `8080`.

Open `http://localhost:8080` in a browser to access the management dashboard — browse memories, run semantic searches, manage projects, track experience, and handle backups without needing to use MCP tool calls.

Connect your coding agent to OmniMem. The example below is for Claude Code — see the full guides for other tools:

| Agent | Guide | Transport |
|-------|-------|-----------|
| claude.ai | [guides/claude-ai.md](guides/claude-ai.md) | Streamable HTTP + OAuth 2.1 |
| Open Design | [guides/open-design.md](guides/open-design.md) | Streamable HTTP + OAuth 2.1 (public/PKCE) |
| Claude Code | [guides/claude-code.md](guides/claude-code.md) | SSE (default) / Streamable HTTP |
| Claude Desktop | [guides/claude-desktop.md](guides/claude-desktop.md) | SSE / Streamable HTTP (via mcp-remote) |
| GitHub Copilot | [guides/github-copilot.md](guides/github-copilot.md) | SSE (default) / Streamable HTTP |
| GitLab Duo | [guides/gitlab-duo.md](guides/gitlab-duo.md) | SSE (default) / Streamable HTTP |
| Cursor | [guides/cursor.md](guides/cursor.md) | SSE (default) / Streamable HTTP |
| AWS Kiro | [guides/kiro.md](guides/kiro.md) | SSE (default) / Streamable HTTP |
| OpenCode | [guides/opencode.md](guides/opencode.md) | SSE (default) / Streamable HTTP |
| OpenAI Codex CLI | [guides/codex.md](guides/codex.md) | SSE (default) / Streamable HTTP |

**Claude Code** (`~/.claude.json`):

```json
{
  "mcpServers": {
    "omnimem": {
      "type": "sse",
      "url": "http://localhost:8765/sse"
    }
  }
}
```

If you set `MCP_AUTH_TOKEN` in your `.env`, add the token to the config:

```json
{
  "mcpServers": {
    "omnimem": {
      "type": "sse",
      "url": "http://localhost:8765/sse",
      "headers": {
        "Authorization": "Bearer your-token-here"
      }
    }
  }
}
```

To stop Claude Code asking for permission every time it calls an OmniMem tool, add a wildcard allow rule to your global settings (`~/.claude/settings.json`):

```json
{
  "permissions": {
    "allow": [
      "mcp__omnimem__*"
    ]
  }
}
```

This allows all OmniMem MCP tools (`remember`, `recall`, `briefing`, etc.) to run without prompts across every project. If you already have other entries in the `allow` array, just add `"mcp__omnimem__*"` to it.

That is it. The server automatically delivers its usage guide to any connecting agent via the MCP protocol's `instructions` field. Claude Code will load project context at session start, check the graveyard before suggesting approaches, and store what it learns as you go — no manual configuration file needed.

If you want to customise the instructions or use OmniMem with a setup that does not support MCP instructions, a copy of the guide lives at `claude_config/CLAUDE.md` for manual use.

---

## MCP tools

### Core memory

| Tool | What it does |
|---|---|
| `remember(content, project?, tags?, force?, mode?)` | Store a memory. In `full` mode (default) extracts atomic facts via Claude Haiku and routes preferences to the preference namespace; `raw` stores verbatim. Auto-checks for duplicates and contradictions |
| `remember_document(content, chunk_strategy, project?, tags?, namespace?, chunk_size?, mode?)` | Index a long-form document by splitting it into chunks (`turn_pairs`, `sentences`, `paragraphs`, or `fixed_tokens`) and storing each as a memory linked by a shared `doc_id` |
| `recall(query, top_k?, project_filter?, expand_queries?)` | Semantic search across all namespaces. With `expand_queries=true`, generates alternative phrasings via Claude Haiku and unions the results to improve recall coverage when query vocabulary doesn't match stored content |
| `deprioritise(key_or_query, reason, reinstate_hints?)` | Soft-suppress without deleting |
| `archive(key_or_query)` | Remove from recall but keep for history |
| `reinstate(key_or_query)` | Bring a deprioritised memory back |
| `forget(key_or_query, confirm=True)` | Hard delete, requires explicit confirmation |
| `suppress_topic(topic)` | Filter a topic from all future recalls |
| `unsuppress_topic(topic)` | Remove a topic from the suppression list |
| `list_suppressions()` | Show all currently suppressed topics |
| `find_duplicates(namespace?, threshold?, project_filter?)` | Scan for clusters of near-identical memories |
| `check_contradictions(query?, namespace?, use_api?)` | Detect memories that contradict each other |
| `briefing(project?, include_knowledge?)` | Single-call session start with full context |

### Project context

| Tool | What it does |
|---|---|
| `set_project_context(name, description, stack, goals, current_state)` | Create or update project memory |
| `get_project_context(name)` | Retrieve it, called at every session start |
| `update_project_state(name, current_state, notes?)` | Update state without re-embedding |
| `compile_project_context(name, auto_save?)` | Auto-produce or refresh a project context from its episodic memories, tags, experience data, and abandoned approaches |
| `list_projects()` | See all stored projects |
| `delete_project(name, confirm?, include_context?)` | Bulk delete every memory belonging to a project by direct key scan (no semantic search, so nothing gets missed). Preview by default; `confirm=True` deletes in pipelined batches; `include_context=True` also removes the project context entry |

### Experience scoring

| Tool | What it does |
|---|---|
| `record_experience(key, effort_score, outcome, abandoned_approaches?, breakthrough?, gotchas?)` | Log how hard it was and what failed |
| `log_abandoned(key, name, type, reason)` | Add dead ends incrementally mid-session |
| `warn_if_abandoned(query)` | Check the graveyard before proceeding |
| `experience_summary(project?)` | Graveyard, breakthroughs, and effort stats |
| `get_experience(key)` | Full experience data for one memory |

### Knowledge

| Tool | What it does |
|---|---|
| `recent_knowledge(days?, feed_name?, topics?, limit?)` | Query recent RSS articles with optional filters, sorted newest first |
| `promote_knowledge(key)` | Mark an article as permanently useful by clearing its expiry |

### Audit and backup

| Tool | What it does |
|---|---|
| `memory_audit(project?, name

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [richarvey](https://github.com/richarvey)
- **Source:** [richarvey/OmniMem](https://github.com/richarvey/OmniMem)
- **License:** MIT
- **Homepage:** https://omnimem.org

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-richarvey-omnimem
- Seller: https://agentstack.voostack.com/s/richarvey
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
