trackmcp
Back to directory

Shared memory MCP server — persistent, searchable, cross-client Claude, Opencode

114 stars PythonOthers Updated Aug 26, 2026
ai-memorymcpmemorypgvectoragent-memoryclaudecodexllm-toolsmodel-context-protocolopencodepersistent-memoryvector-databasevector-searchpostgresllm

Documentation

Ogham MCP

*Ogham* (pronounced "OH-um") -- persistent, searchable shared memory for AI coding agents. Works across clients.

License: MIT
Docker
Python 3.13+
PyPI

What it is

AI coding agents forget everything between sessions. Switch from Claude Code to Cursor to Kiro to OpenCode and the context is gone -- decisions, gotchas, the shape of your codebase -- so you repeat yourself, re-explain, and re-debug the same issues.

Ogham gives your agents one shared memory that persists across sessions and clients. It is a retrieval engine: it stores what matters and finds it again, and your LLM reads the results.

The retrieval is structured -- hybrid search plus a typed-edge graph, not just vector similarity. That is what lets it answer questions whose answer is a *path between two facts*, the case where plain vector RAG falls down.

Quick start

bash
uvx --from ogham-mcp ogham init

`ogham init` runs a setup wizard: it connects your database, picks an embedding provider, migrates the schema, and writes the MCP client config (Claude Code, Cursor, VS Code, and others). For Claude Code it runs `claude mcp add` for you; for other clients it prints the snippet to copy.

You need a database first -- a free Supabase project or a Neon database. On Neon or self-hosted Postgres, install the postgres extra so the driver is available:

bash
uvx --from 'ogham-mcp[postgres]' ogham init

Then tell your agent to remember something and ask about it later -- from the same client or a different one. They share the database, so the memory follows you.

Manual setup, other install methods (Docker, source), HTTP multi-agent mode

Manual setup

If you'd rather configure things yourself instead of using the wizard:

bash
# Supabase
export SUPABASE_URL=https://your-project.supabase.co
export SUPABASE_KEY=your-service-role-key
export EMBEDDING_PROVIDER=openai  # or ollama, mistral, voyage
export OPENAI_API_KEY=sk-...      # for your chosen provider

# Or Postgres (Neon, self-hosted)
export DATABASE_BACKEND=postgres
export DATABASE_URL=postgresql://user:pass@host/db
export EMBEDDING_PROVIDER=openai
export OPENAI_API_KEY=sk-...

Run the schema migration (`sql/schema.sql` for Supabase, `sql/schema_postgres.sql` for Neon/self-hosted), then add the MCP server to your client.

Installation methods

MethodCommandWhen to use
uvx (recommended)`uvx ogham-mcp`Quick setup, auto-updates
Docker`docker pull ghcr.io/ogham-mcp/ogham-mcp`Isolation, self-hosted
Git clone`git clone` + `uv sync`Development, contributions

Claude Code

bash
claude mcp add ogham -- uvx ogham-mcp

OpenCode -- add to `~/.config/opencode/opencode.json`:

json
{
  "mcp": {
    "ogham": {
      "type": "local",
      "command": ["uvx", "ogham-mcp"],
      "environment": {
        "SUPABASE_URL": "https://your-project.supabase.co",
        "SUPABASE_KEY": "{env:SUPABASE_KEY}",
        "EMBEDDING_PROVIDER": "openai",
        "OPENAI_API_KEY": "{env:OPENAI_API_KEY}"
      }
    }
  }
}

Docker

bash
docker run --rm \
  -e SUPABASE_URL=https://your-project.supabase.co \
  -e SUPABASE_KEY=your-key \
  -e EMBEDDING_PROVIDER=openai \
  -e OPENAI_API_KEY=sk-... \
  ghcr.io/ogham-mcp/ogham-mcp

From source

bash
git clone https://github.com/ogham-mcp/ogham-mcp.git
cd ogham-mcp
uv sync
uv run ogham --help

HTTP transport (multi-agent)

By default, Ogham runs in stdio mode -- each MCP client spawns its own server process. To let several agents share one server, run it over HTTP:

bash
ogham serve --transport streamable-http --port 8742

The server runs as a persistent background process. All clients connect to the same instance -- one database pool, one embedding cache, shared memory.

Client config (any MCP client):

json
{
  "mcpServers": {
    "ogham": {
      "url": "http://127.0.0.1:8742/mcp"
    }
  }
}

Health check at `http://127.0.0.1:8742/health` (cached, sub-10ms). Configure via env vars (`OGHAM_TRANSPORT=streamable-http`, `OGHAM_HOST`, `OGHAM_PORT`) or CLI flags. `http` is accepted as an alias for `streamable-http`.

In Docker, bind to all interfaces. The default host is `127.0.0.1`, which inside a container means the container itself -- publishing the port will not reach it:

bash
docker run -p 8742:8742 ghcr.io/ogham-mcp/ogham-mcp:latest \
  serve --transport streamable-http --host 0.0.0.0 --port 8742

SSE (legacy)

Ogham still accepts `--transport sse` and serves that endpoint at `/sse`. It works, and it stays for existing deployments, but new setups should use streamable-http.

The MCP specification now defines two standard transports, stdio and Streamable HTTP, and treats HTTP+SSE as deprecated. The difference that matters is what happens when a session is lost. Streamable HTTP assigns an `Mcp-Session-Id` and defines the way back: the server answers a dead session with HTTP 404, and the client starts a fresh one. SSE defines no recovery at all, so a session that loses its initialized state rejects every later request with `-32602`, and the client cannot tell a lifecycle problem from a bad argument. One dropped connection can leave an agent failing every call until someone restarts it.

Entry points

  • `ogham` -- the CLI. Use this for `ogham init`, `ogham health`, `ogham search`, and other commands you run yourself. Running `ogham` with no arguments starts the MCP server.
  • `ogham-serve` -- starts the MCP server directly. This is what MCP clients should call. When you run `uvx ogham-mcp`, it invokes `ogham-serve`.

Retrieval quality

Ogham is a retrieval engine -- it finds the memories, your LLM reads them. The headline numbers, and they measure different things:

  • Retrieval: 97.2% R@10 on LongMemEval with one Postgres query (pgvector + tsvector CCF hybrid search). The paper baseline is 78.4%. Other systems that report similar R@10 typically stack cross-encoder reranking, NLI verification, and knowledge-graph enrichment.
  • End-to-end QA: **85.8% on the AMB harness (500 questions, April 2026, strict substring judge, GPT-5-mini reader; R@10 99.5%), and 0.554 nugget on BEAM 100K** (paper baseline 0.358; seven of nine categories beat the paper).

QA accuracy tests whether the full system (retrieval + LLM) produces the correct answer. R@10 tests whether retrieval alone found the right memories. Full tables, methodology, and the competitor comparison live at ogham-mcp.dev/features; the write-ups explain why the AMB and internal numbers differ (LongMemEval, BEAM).

Full benchmark tables (QA, R@10, per-category, competitors)

**85.8% QA accuracy on the AMB benchmark harness** (500 questions, April 2026) -- 429/500 questions answered correctly using GPT-5-mini with reasoning, evaluated by Gemini 2.5 Flash Lite as a strict judge. Retrieval R@10: 99.5%. AMB is the standardised evaluation harness built by the Vectorize team (creators of Hindsight). Thanks to Nicolo and the Vectorize team for making the harness open.

Previously: 91.8% on our internal LongMemEval benchmark pipeline (gpt-5.4-mini reader, rubric judge). The AMB number is lower because AMB uses a stricter substring-matching judge -- see the full write-up for methodology differences.

**0.554 nugget score on BEAM 100K** (400 questions across 10 memory abilities, ICLR 2026), using the paper's exact judge prompt from Appendix G. The published baseline is 0.358 (Llama-4-Maverick + LIGHT). Retrieval R@10: 0.737. Seven of nine categories beat the paper. Full write-up.

End-to-end QA accuracy on LongMemEval (retrieval + LLM reads and answers):

SystemAccuracyArchitecture
OMEGA95.4%Classification + extraction pipeline
Observational Memory (Mastra)94.9%Observation extraction + GPT-5-mini
Ogham v0.9.285.8%Verbatim + read-time extraction + gpt-5-mini (AMB harness, strict judge)
Ogham v0.9.191.8%Hybrid search + context engineering + gpt-5.4-mini (internal benchmark)
Hindsight (Vectorize)91.4%4 memory types + Gemini-3
Zep (Graphiti)71.2%Temporal knowledge graph + GPT-4o
Mem049.0%RAG-based

Retrieval only (R@10 -- no LLM in the search loop):

SystemR@10Architecture
Ogham97.2%1 SQL query (pgvector + tsvector CCF hybrid search)
LongMemEval paper baseline78.4%Session decomposition + fact-augmented keys

Other retrieval systems that report similar R@10 numbers typically use cross-encoder reranking, NLI verification, knowledge graph enrichment, and LLM-as-a-judge pipelines. Ogham reaches 97.2% with one Postgres query. Optional FlashRank reranking is available for self-hosters who want extra ranking precision.

These tables measure different things. QA accuracy tests whether the full system (retrieval + LLM) produces the correct answer. R@10 tests whether retrieval alone finds the right memories. Ogham is a retrieval engine -- it finds the memories, your LLM reads them.

CategoryR@10Questions
single-session-assistant100%56
knowledge-update100%78
single-session-user98.6%70
multi-session97.3%133
single-session-preference96.7%30
temporal-reasoning93.5%133

Full breakdown: ogham-mcp.dev/features

How it works

code
AI Client (Claude Code, Cursor, Kiro, OpenCode, ...)
    |
    | stdio or SSE (MCP protocol)
    |
Ogham MCP Server
    |
    | HTTPS (Supabase REST API) or direct connection (Postgres)
    |
PostgreSQL + pgvector

Memories are stored as rows with vector embeddings. Search combines pgvector cosine similarity with PostgreSQL full-text search using Reciprocal Rank Fusion (RRF) -- position-based, score-agnostic fusion that handles different score scales correctly. The knowledge graph lives in a `memory_relationships` table walked with recursive CTEs; the typed-edge graph (v0.16) adds structural, predicate-typed relationships for two-fact join queries. No separate graph database. Optional FlashRank cross-encoder reranking adds a second pass for self-hosters.

What's in it

  • Memory operations -- store memories, decisions, preferences, facts, and events; update, reinforce, and contradict.
  • Hybrid search -- semantic + full-text (RRF), tag filters, multi-profile search, read-time fact extraction.
  • Typed-edge graph (v0.16) -- `store_triple` / `query_join` for two-fact join queries against a controlled predicate vocabulary. docs
  • Knowledge graph -- auto-linking, spreading-activation retrieval, and connection suggestions via shared entities.
  • Wiki layer -- synthesize a tag's memories into a cached markdown page; walk the graph; lint health.
  • Open Knowledge Format -- portable round-trip bundles (markdown + a self-contained graph viewer), OKF v0.1.
  • Entity enrichment -- regex entity tags across 18 languages with no LLM in the write path; a timeline table; Lost-in-the-Middle reordering.
  • Memory lifecycle -- FRESH / STABLE / EDITING stages, ACT-R importance, Hebbian decay, and automatic condensing.
  • Importers -- Claude Code auto-memory, Claude.ai export, Linear issues, and JSON.
  • Ingestion adapters (v0.17) -- capture into memory from an Obsidian/markdown vault (`ingest-obsidian`), Telegram (`ingest-telegram`), and Slack (`ingest-slack`). Outbound-only, idempotent, and timer-friendly; all three share one server-side enrichment and dedup path.
  • Lifecycle hooks -- recall context at session start, inscribe signal (not noise) after tool use; secrets masked before storage.
  • Skills -- `ogham-research`, `ogham-recall`, `ogham-maintain`.
  • Self-hoster options -- ONNX local embeddings (BGE-M3), optional FlashRank reranking, five embedding providers.

Full reference for every tool, env var, and setup path is in Reference below.

The deeper story

The retrieval pipeline is built on established information-retrieval and cognitive-science work, not ad-hoc heuristics:

  • Hybrid search -- Reciprocal Rank Fusion (Cormack, Clarke & Butt, SIGIR 2009): dense vector similarity rank-fused with BM25-style keyword matching, no score normalisation.
  • ACT-R importance + Hebbian decay -- recency, frequency, and surprise weighting (Anderson & Lebiere, 1998; Hebb, 1949). Unaccessed memories fade; frequently accessed ones potentiate and persist.
  • Read-time fact extraction -- verbatim storage with query-aware extraction at retrieval, so the ground truth stays re-extractable with different questions later (Anthropic, arXiv:2510.05179). Supports local models via Ollama for full data sovereignty.
  • Contradiction detection + supersession -- opposite-polarity memories are linked, not deleted; the edge records that the newer memory superseded the older one.
  • Append-only audit trail -- every store, search, delete, and update logged to an `audit_log` table in the same Postgres instance, aligned with GDPR Article 15 and OTEL GenAI conventions.

Research foundations (full list with citations)

Ogham's retrieval pipeline combines established information retrieval and cognitive science techniques:

  • Hybrid search -- Reciprocal Rank Fusion (Cormack, Clarke & Butt, SIGIR 2009) combining dense vector similarity (pgvector) with BM25-style keyword matching (PostgreSQL tsvector). Two independent retrieval systems, rank-fused without score normalisation.
  • Entity overlap boost -- memories sharing named entities with the query receive a bounded relevance boost (up to 1.4x), inspired by entity-linking literature (Kolitsas et al., CoNLL 2018). Entity extraction covers 18 languages via YAML-based word lists with no LLM in the write path.
  • Matryoshka embeddings -- flexible dimensionality via Matryoshka Representation Learning (Kusupati et al., NeurIPS 2022). Embedding providers (OpenAI, Voyage, Gemini, Ollama) produce native-dimension vectors truncated to 512d, enabling provider-portable storage without re-embedding.
  • Temporal diversity re-ranking -- density-gated soft penalty preventing semantic clustering on a single time period, extending Maximal Marginal Relevance principles (Carbonell & Goldstein, SIGIR 1998). Only activates when the top-k results are temporally concentrated, leaving well-distributed results untouched.
  • ACT-R importance scoring -- cognitive-architecture-inspired memory weighting based on recency, access frequency, and surprise (Anderson & Lebiere, 1998). Frequently accessed memories stay sharp, rarely accessed ones fade, disputed ones drop in ranking without deletion.
  • Hebbian decay and potentiation -- memories that are not accessed lose importance over time (5% per 30-day idle period). Memories accessed 10+ times become "potentiated" with a slower decay rate (1% per 30 days), simulating long-term potentiation. Based on Hebb's learning rule (Hebb, 1949) and computational models of synaptic plasticity (Bi & Poo, 2001). Importance serves as a multiplier in the relevance formula -- decayed memories sink in rankings but remain retrievable (floor at 0.05). Original importance is preserved in metadata for recovery. Run as a batch job via `ogham decay` or pg_cron.
  • Memory lifecycle (v0.11.0): FRESH / STABLE / EDITING. Every memory

now has an explicit stage tracked in a dedicated `memory_lifecycle`

table. New memories land at `fresh`. The session-start hook sweeps

aged fresh memories to `stable` when they clear an importance-or-surprise

gate and have dwelled long enough. Retrieval opens a 30-minute

`editing` window on the returned memories so follow-up

`update_memory` calls refine recent thoughts in place; windows

auto-close on the next sweep. Memories retrieved together also

strengthen their pairwise graph edges (eta=0.01 per co-retrieval,

capped at 1.0). The design draws on three lines of prior art:

Hebbian co-activation (Hebb, 1949),

the hybrid exponential-then-power-law forgetting curve characterised

by Wixted (2004)

building on Ebbinghaus (1885),

and the memory reconsolidation window from neuroscience

(Nader, Schafe & LeDoux, 2000)

for the editing-on-retrieval mechanic. Stage state lives in its

own table so transitions do not touch the HNSW vector index.

  • Spreading activation -- when a search hits one memory, activation spreads along relationship edges to pull in connected memories that wouldn't have matched on their own. Integrated into cross-reference, ordering, and summary queries. Density-adaptive weighting means sparse graphs lean harder on graph signal, dense graphs rely more on retrieval score. Inspired by Collins & Loftus (1975) semantic network theory.
  • Contradiction detection -- when a new memory has opposite polarity to a high-similarity existing memory, Ogham automatically creates a `contradicts` relationship edge. Polarity detection uses negation markers across 18 languages loaded from YAML word lists. Contradicted memories are not deleted -- the edge records that the newer memory superseded the older one.
  • Read-time fact extraction -- query-aware extraction at retrieval time preserves verbatim storage for auditability, contrasting with write-time compression approaches. Verbatim storage ensures the ground truth is always available for re-extraction with different questions later -- a design choice informed by alignment considerations in persistent agent memory (Anthropic, arXiv:2510.05179). Supports local models via Ollama for full data sovereignty.
  • Append-only audit trails -- every store, search, delete, and update operation is logged to an `audit_log` table in the same Postgres instance. Designed for GDPR Article 15 subject access requests and cost governance. Fields align with OTEL GenAI Semantic Conventions. Query via `ogham audit` CLI. No extra infrastructure -- runs in the same database as memories.

Reference

CLI commands

bash
ogham init                      # Interactive setup wizard
ogham health                    # Check database + embedding provider
ogham config                    # Show runtime configuration (secrets masked)
ogham store "some fact"         # Store a memory
ogham search "query"            # Search memories (hybrid: semantic + keyword)
ogham search "q" --json         # JSON output for scripting
ogham search "q" --tags "a,b"   # Filter by comma-separated tags
ogham list                      # List recent memories
ogham list --json               # JSON output
ogham delete                # Delete a memory by ID
ogham use              # Switch default profile
ogham profiles                  # List profiles and counts
ogham stats                     # Profile statistics
ogham export -o backup.json     # Export memories (JSON)
ogham export --format markdown  # Export as Obsidian-compatible markdown
ogham export --format okf       # Export as Open Knowledge Format v0.1 bundle
ogham import backup.json        # Import a JSON export
ogham import    # Import an OKF bundle directory (auto-detected)
ogham import  --with-graph # ...including its entities/ graph layer (opt-in)
ogham cleanup                   # Remove expired memories
ogham hooks install             # Auto-detect client + configure hooks
ogham hooks recall              # Read from the stone (load project context)
ogham hooks inscribe            # Carve into the stone (capture activity)
ogham hooks inscribe --dry-run  # Preview hook memory without storing
ogham serve                     # Start MCP server (stdio, default)
ogham serve --transport http    # Start HTTP server on port 8742
ogham openapi                   # Generate OpenAPI spec

Multi-profile search -- search across multiple profiles in a single query (v0.8.5+):

python
# MCP tool
hybrid_search(query="architecture decisions", profiles=["work", "shared"])

# Python library
from ogham.service import search_memories_enriched
results = search_memories_enriched(
    query="architecture decisions",
    profile="work",
    profiles=["work", "shared", "project-alpha"],
)

When `profiles` is set, results include memories from all listed profiles with a `profile` field showing which profile each result came from.

Configuration (env vars, embedding providers, temporal search)

VariableRequiredDefaultDescription
`DATABASE_BACKEND`No`supabase``supabase` or `postgres`
`SUPABASE_URL`If supabase--Your Supabase project URL
`SUPABASE_KEY`If supabase--Supabase secret key (service_role)
`DATABASE_URL`If postgres--PostgreSQL connection string
`EMBEDDING_PROVIDER`No`ollama``ollama`, `openai`, `mistral`, `voyage`, `gemini`, or `onnx`
`EMBEDDING_DIM`No`512`Vector dimensions -- must match your schema (see below)
`OPENAI_API_KEY`If openai--OpenAI API key
`MISTRAL_API_KEY`If mistral--Mistral API key
`VOYAGE_API_KEY`If voyage--Voyage AI API key
`GEMINI_API_KEY`If gemini--Google Gemini API key
`OLLAMA_URL`No`http://localhost:11434`Ollama server URL
`OLLAMA_EMBED_MODEL`No`embeddinggemma`Ollama embedding model
`MISTRAL_EMBED_MODEL`No`mistral-embed`Mistral embedding model
`VOYAGE_EMBED_MODEL`No`voyage-4-lite`Voyage embedding model
`GEMINI_EMBED_MODEL`No`gemini-embedding-2-preview`Gemini embedding model
`RERANK_ENABLED`No`false`Enable FlashRank cross-encoder reranking
`RERANK_ALPHA`No`0.55`Cross-encoder score weight (0-1)
`DEFAULT_MATCH_THRESHOLD`No`0.7`Similarity threshold (see below)
`DEFAULT_MATCH_COUNT`No`10`Max results per search
`DEFAULT_PROFILE`No`default`Memory profile name
`OGHAM_RECALL_ENABLED`No`true`Enable memory recall/context retrieval
`OGHAM_INSCRIBE_ENABLED`No`true`Enable memory capture/content writes

Embedding providers

ProviderDefault dimensionsRecommended thresholdNotes
OpenAI512 (schema default)0.35Set `EMBEDDING_DIM=512` explicitly -- OpenAI defaults to 1024
Ollama5120.70Tight clustering, scores run 0.8-0.9
Mistral10240.60Fixed 1024 dims, can't truncate. Schema must be `vector(1024)`
Voyage512 (schema default)0.45Moderate spread
Gemini5120.35`gemini-embedding-2-preview`, supports MRL truncation
ONNX10240.35Local BGE-M3 inference, dense + sparse vectors. See ONNX section

`EMBEDDING_DIM` must match the `vector(N)` column in your database schema. The default schema uses `vector(512)`. If you use Mistral, you need to alter the column to `vector(1024)` before storing anything.

Each provider clusters vectors differently, so the similarity threshold matters. Start with the recommended value and adjust based on your results.

Temporal search -- queries with time expressions like "last week" or "three months ago" are resolved automatically using parsedatetime, no configuration needed. This handles roughly 80% of temporal queries at zero cost. For expressions parsedatetime cannot parse ("the quarter before last", "around Thanksgiving"), set `TEMPORAL_LLM_MODEL` to call an LLM as a fallback:

bash
# Self-hosted with Ollama (free, local)
TEMPORAL_LLM_MODEL=ollama/llama3.2

# Cloud API
TEMPORAL_LLM_MODEL=gpt-4o-mini

Any litellm-compatible model string works -- `deepseek/deepseek-chat`, `moonshot/moonshot-v1-8k`, etc. The LLM is only called when parsedatetime fails and the query has temporal intent, so costs stay near zero. If `TEMPORAL_LLM_MODEL` is empty (the default), parsedatetime handles everything on its own. Requires the `litellm` package.

Lifecycle hooks (recall / inscribe, smart filtering, secret masking)

Ogham hooks inject memory context at session start and preserve it across compaction. Install for your client:

bash
ogham hooks install
ClientWhat gets installed
Claude CodeHooks in `~/.claude/settings.json` (recall on SessionStart/PostCompact, inscribe on PostToolUse/PreCompact)
KiroInstructions for Hook UI (recall on Prompt Submit, inscribe on Agent Stop)
Codex, Cursor, othersProject instruction file (CLAUDE.md, AGENTS.md, or .cursorrules)

Two commands, named after the Ogham stones:

  • recall -- read from the stone. Searches Ogham for memories relevant to your project and injects them as context. Fires at session start and after compaction.
  • inscribe -- carve into the stone. Captures meaningful tool activity as memories. Skips noise (`ls`, `cat`, `git status`) and only stores signal (commits, deploys, errors, config changes). Fires after tool use and before compaction. Secrets are masked before storing.

Disable either flow when you want an agent attached to Ogham without letting it pull memory into context or write new memory:

bash
OGHAM_RECALL_ENABLED=false ogham serve       # no context injection / memory search
OGHAM_INSCRIBE_ENABLED=false ogham serve     # no memory capture / content writes
ogham hooks recall --no-recall               # one-off hook recall skip
ogham hooks inscribe --no-inscribe           # one-off hook capture skip
ogham search "query" --no-recall             # one-off CLI search skip
ogham store "some fact" --no-inscribe        # one-off CLI store skip

For MCP clients, put the env vars in that client's Ogham server config:

json
{
  "mcpServers": {
    "ogham": {
      "command": "ogham-serve",
      "env": {
        "OGHAM_RECALL_ENABLED": "false",
        "OGHAM_INSCRIBE_ENABLED": "false"
      }
    }
  }
}

Admin operations such as config, health, stats, audit, export, delete, and cleanup remain available so you can inspect or clean memory even when recall or inscribe is disabled.

Smart filtering: Hooks don't capture everything. Routine commands (`ls`, `pwd`, `git add`) are skipped. Only signal events (errors, deployments, commits, config changes) are stored -- typically 20-30 memories per session instead of hundreds.

Secret masking: API keys, tokens, passwords, and JWTs are automatically replaced with `*MASKED*` before storing. The event is captured ("configured Stripe API key") but the actual secret never touches the database.

MCP tools (memory, search, graph, typed-edge, importers, profiles, import/export, maintenance)

Memory operations

ToolDescriptionKey parameters
`store_memory`Store a new memory with embedding`content` (required), `source`, `tags[]`, `auto_link`
`store_decision`Store an architectural decision`decision`, `reasoning`, `alternatives[]`, `tags[]`
`store_preference`Store a user preference with strength metadata`preference`, `subject`, `alternatives[]`, `strength`
`store_fact`Store a factual statement with confidence and citation`fact`, `subject`, `confidence`, `source_citation`
`store_event`Store an event with temporal and participant metadata`event`, `when`, `participants[]`, `location`
`update_memory`Update content of existing memory`memory_id`, `content`, `tags[]`
`delete_memory`Delete a memory by ID`memory_id`
`reinforce_memory`Increase confidence score`memory_id`
`contradict_memory`Decrease confidence score`memory_id`

Search

ToolDescriptionKey parameters
`hybrid_search`Combined semantic + full-text search (RRF)`query`, `limit`, `tags[]`, `graph_depth`, `profiles[]`, `extract_facts`
`list_recent`List recent memories`limit`, `profile`
`find_related`Find memories related to a given one`memory_id`, `limit`

Knowledge graph

ToolDescriptionKey parameters
`link_unlinked`Auto-link memories by embedding similarity`threshold`, `limit`
`explore_knowledge`Traverse the knowledge graph`memory_id`, `depth`, `direction`
`suggest_connections`Find hidden connections via shared entities`memory_id`, `min_shared_entities`, `limit`

Typed-edge graph (v0.16) -- structural, typed relationships for two-fact join queries. Full detail in `/docs/typed-edges/`.

ToolDescriptionKey parameters
`store_triple`Write a typed edge against a controlled predicate vocabulary; supersedes the prior current edge for the same subject + predicate + object`subject`, `predicate`, `object`, `profile`, `source_memory_id`
`query_join`Walk a typed predicate path from a start entity; returns the entities (BFS order), edges, and citations along the path -- no fuzzy ranking`start_entity`, `predicate_path[]`, `hop_limit` (required), `direction`

Referring to an entity (v0.19). Entities are keyed on

`(canonical_name, entity_type)`, so a bare name is not always enough to identify

one. The same word can legitimately land under two types -- `ValueError` is both

an `entity:` (interior capitalisation) and an `error:` (the `Error` suffix). Pass

a qualified reference to be explicit:

code
store_triple(subject="error:ValueError", ...)   # exactly the error node
store_triple(subject="ValueError", ...)         # lowest-id match, and logs the collision

An unqualified reference still resolves, deterministically, and warns when it had

to choose. Before v0.19 it chose silently.

Evidence class (v0.19). Every entity now records how it was established:

classmeaning
`syntactic`an unambiguous marker in the text -- interior capitalisation, a path separator, an `Error` suffix, a number with a unit
`inferred`a dictionary or keyword lookup with no marker -- places, events, preferences, emotions
`structured`derived from adapter provenance rather than from text

It is deliberately not a confidence score. Calibration is a property of an

estimated probability (Guo, Pleiss, Sun and Weinberger, ICML 2017);

a fixed class per rule estimates nothing, so a float here would invite consumers

to multiply it into a relevance score as though it meant something. It is an

enumerated value with a `CHECK` constraint.

Existing entities are classified from their type on upgrade. Nothing in retrieval

reads it yet -- it exists so that when enrichment does start writing entities, a

machine-suggested one is distinguishable from adapter-derived fact.

Importers

ToolDescriptionKey parameters
`import_linear`Import Linear issues as memories, read-only, deduped by tracker id (v0.16)see `/docs/import-linear/`

Profiles

ToolDescriptionKey parameters
`switch_profile`Switch active memory profile`profile`
`current_profile`Show active profile--
`list_profiles`List all profiles with counts--
`set_profile_ttl`Set auto-expiry for a profile`profile`, `ttl_days`

Import / export

ToolDescriptionKey parameters
`export_profile`Export all memories in active profile`format` (`json`, `markdown`, or `okf`), `include_viewer` (default true for OKF)
`import_memories_tool`Import a JSON export string OR auto-detect an OKF bundle directory by path`data`, `dedup_threshold`

`--format okf` writes an Open Knowledge Format v0.1 conformant bundle: a directory of one-markdown-file-per-memory with YAML frontmatter, a bundle-root `index.md` declaring `okf_version: "0.1"`, and a self-contained `viewer.html` (Cytoscape.js graph, opens with `file://`, no server or CDN needed). Round-trip preserves UUID, content, tags, source, and metadata; the embedding is regenerated on import. Pass `include_viewer=false` to skip the HTML graph. See the OKF round-trip write-up for the background.

Maintenance

ToolDescriptionKey parameters
`re_embed_all`Re-embed all memories (after switching providers)--
`compress_old_memories`Condense old inactive memories (full text to summary to tags)--
`cleanup_expired`Remove expired memories (TTL)--
`health_check`Check database and embedding connectivity--
`get_config`Show runtime configuration with masked secrets--
`get_stats`Memory counts, sources, tags, and profile health (orphans, decay, tagging)--
`get_cache_stats`Embedding cache hit rates--

Wiki layer

The wiki layer turns a tag full of related memories into a synthesized markdown page. Run `compile_wiki` on a tag, get back a summarized topic; the cache invalidates automatically when underlying memories change. Four MCP tools cover the lifecycle:

ToolDescriptionKey parameters
`compile_wiki`Compile a tag's memories into a synthesized markdown page (LLM call, cached)`topic`, `provider`, `model`, `force`
`query_topic_summary`Read the cached page for a topic without recomputing`topic`
`walk_knowledge`Direction-aware graph walk from a known memory along relationship edges`start_id`, `depth`, `direction` (`outgoing`, `incoming`, `both`), `min_strength`, `relationship_types`
`lint_wiki`Health report: contradictions, orphans, stale lifecycle, stale summaries, summary drift`stable_days`, `sample_size`, `include_drift`

The wiki layer needs an LLM. Synthesis is the LLM step that turns a list of memories into a coherent page; embeddings alone aren't enough. You can run that LLM locally (Ollama with `llama3.2`, vLLM, or any OpenAI-compatible local server) or in the cloud (Gemini, OpenAI, Anthropic, Mistral, Groq, OpenRouter). Local keeps everything private and free; cloud generally writes more polished prose at the cost of a few cents per compile.

Set the default with `LLM_PROVIDER` and `LLM_MODEL` in your environment (e.g. `LLM_PROVIDER=gemini` + `LLM_MODEL=gemini-2.5-flash`, or `LLM_PROVIDER=ollama` + `LLM_MODEL=llama3.2`). Override per call with `compile_wiki(topic=..., provider=..., model=...)`. The provider/model is stamped into the resulting page's frontmatter, so you can re-compile the same topic with a different LLM and see how the synthesis changes.

`compile_wiki` short-circuits when the source memories haven't changed since the last compile -- the call is effectively free if nothing has moved. Pass `force=True` to bypass that check (useful for re-compiling with a different model on the same source set).

The wiki layer requires migrations 028, 030, and 031 applied to your database. See Database setup for details.

Obsidian export

Snapshot your wiki layer to a folder of Obsidian-compatible markdown files. One `.md` per topic with full YAML frontmatter, plus a `README.md` index. Wikilinks between topics are auto-detected and wrapped in `[[brackets]]` for Obsidian's graph view.

bash
ogham export-obsidian /path/to/vault
ogham export-obsidian /path/to/vault --profile work --force

The export is read-only -- it writes files but never reads them back. Edits in Obsidian stay in Obsidian; re-run the export to refresh the snapshot. By design the exporter refuses to write into a directory that already contains files it didn't create; pass `--force` to override that guardrail.

Full guide with frontmatter reference, troubleshooting, and screenshots: obsidian export docs.

Open Knowledge Format (portable round-trip bundles)

Round-trip portability for your memories. Ogham reads and writes Open Knowledge Format (OKF) v0.1, the markdown-based interchange format Google Cloud published in June 2026. The same bundle is portable to any other OKF-speaking tool -- Google's Knowledge Catalog, a colleague's homegrown reader, or your own future system.

bash
# Export your profile as an OKF v0.1 bundle directory
ogham export --format okf

# Round-trip it back (auto-detected as an OKF bundle)
ogham import ogham-okf--

The bundle is a directory tree:

code
ogham-okf--/
├── index.md                     # declares okf_version: "0.1" (+ ogham_graph_version: 1)
├── context.jsonld               # predicate URIs, with Schema.org alignments where they fit
├── viewer.html                  # self-contained Cytoscape.js graph (opens with file://)
├── memories/
│   ├── -.md        # one markdown file per memory
│   └── ...
└── entities/                    # present only if the profile has a typed-edge graph
    ├── -e.md   # one file per entity, edges as frontmatter triples
    └── ...

Each memory file has YAML frontmatter (`type`, `id`, `tags`, `timestamp`, `source`, optional `title`) and the memory body as markdown. `type:` is derived from the first `type:X` tag alphabetically (falling back to `Memory`). Round-trip preserves UUID, content, tags, source, and any extension metadata; the embedding is regenerated on import.

The `viewer.html` is a 425 KB self-contained file with Cytoscape.js (MIT) vendored inline -- no server, no internet, no CDN. Open it in any browser via `file://` to see your memories as a graph coloured by type, with edges following intra-bundle markdown links. Pass `include_viewer=false` to skip it.

Import behaviour:

  • Memories with the `id:` extension upsert by UUID (idempotent re-imports).
  • Memories without `id:` insert as new; the count surfaces as `missing_id_count` in the result.
  • The importer requires a bundle-root `index.md` with `okf_version` declared so pointing it at a random directory fails fast.

Importing the entity graph is opt-in and off by default:

bash
# See what it would do -- writes nothing
ogham import  --with-graph --graph-dry-run

# Actually import the entities/ layer
ogham import  --with-graph

The default is off because the `entities` table has no profile column -- it is

global, scoped only through `memory_entities` and `entity_edges` -- so importing

a graph touches rows every profile reads.

Two things worth knowing before you run it:

  • It merges, it does not restore. Importing into a profile that already has

a graph moves matching edges forward rather than returning the profile to the

bundle's state. Into a fresh profile the result is exact.

  • There is no undo, so there is a dry run. Snapshotting the profile first

does not give you one -- the snapshot is built from this same export path, and

edge qualifiers do not round-trip through it. `--graph-dry-run` reports exactly

what the real run would do: entities to create, edges to write, edges already

present, and edges whose target is missing from the bundle.

Re-running is safe. An edge that already exists is skipped rather than rewritten,

so a retry after a partial failure converges instead of churning.

Scope: this is for your own bundles. Importing a bundle from someone else's

install is not supported -- the reader is bounded against corruption, not against

a hostile bundle.

User-facing write-up: Ogham v0.15 speaks Open Knowledge Format.

Importing existing memory (Claude Code, Claude.ai, JSON)

From Claude Code (auto-memory MD files)

Claude Code's auto-memory system writes per-project notes under `~/.claude/projects//memory/`. Each note is a markdown file with YAML frontmatter (`name`, `description`, `type`, optional `originSessionId`). Pull them into Ogham:

bash
ogham import-claude-code ~/.claude/projects//memory \
    --project ogham --dedup 0.8

Each parseable file becomes one Ogham memory tagged `source:claude-code-memory + type: + project:`. `MEMORY.md` (the index) and dotfiles are skipped. Files without recognisable frontmatter log a warning and are skipped.

The encoded-cwd directory naming is lossy on hyphenated repo names: `openbrain-sharedmemory` decodes to `sharedmemory` because every `/` and `-` becomes the same separator. Pass `--project NAME` to override the inferred tag and keep your project tags consistent.

The importer respects `inscribe_enabled()` -- `--no-inscribe` and `OGHAM_INSCRIBE_ENABLED=false` skip the import. Re-runs are dedup-safe via the `--dedup` cosine threshold (default 0.8). MCP tool: `import_claude_code_memories(directory, project_tag=...)`.

From Claude.ai (conversation data export)

Anthropic offers a first-party data export at Settings → Privacy → Request your data. After ~24-48h you receive a ZIP containing `conversations.json`. Pull it into Ogham:

bash
ogham import-claude-ai ~/Downloads/data--batch-0000 --profile claude-ai

Accepts the ZIP itself, the unzipped directory, or `conversations.json` directly. Each `(human, assistant)` turn-pair becomes one memory with the assistant turn as content (the signal you'll search on) and the human prompt in `metadata.user_prompt` (recoverable for context). Tagging: `source:claude-ai`, `claude-conversation:`, optional `project:`.

A conservative smart filter drops pleasantry exchanges; pass `--no-smart-filter` to keep them. Use `--since 2026-01-01` to import only recent conversations, or `--mode raw` for one memory per individual message instead of per turn-pair.

To get an LLM-distilled summary of an imported conversation, call `compile_wiki(topic="claude-conversation:")` via MCP. Verbatim ingest plus on-demand synthesis means you keep the raw turns *and* get a digest, without the importer making LLM calls upfront. The summary regenerates whenever the source memories change.

UUIDs from the export land in metadata so re-importing the same export later only adds new turns. MCP tool: `import_claude_ai_export(path, profile, mode=...)`.

Bulk imports skip per-memory enrichment -- the Claude Code, Claude.ai, and JSON importers all write through `import_memories`, which embeds + dedups + inserts in batches but skips per-memory entity extraction and auto-link (a 600-memory import would otherwise run thousands of secondary RPCs). Imported memories are immediately searchable via embedding + keyword. To populate the entity graph after a bulk import, run `ogham backfill-entities --profile ` (see Entity graph below).

From a JSON export

bash
ogham export --profile work > backup.json
ogham import backup.json --profile work-restored

Round-trips the full memory schema (content, embeddings, tags, metadata, lifecycle state). Useful for migrating between deployments or seeding a fresh profile from another.

Entity graph + backfill

Ogham extracts entity tags from every stored memory at ingest (people, files, errors, locations, projects -- pure regex, no LLM). v0.14 added the live wire-up so those tags also populate a separate entity graph used for spreading-activation retrieval and cross-memory connection suggestions.

After applying migration 036 on an existing deployment, run a one-shot backfill to populate historical memories:

bash
ogham backfill-entities --profile work

The backfill walks the memories table, runs the entity extractor, and links each memory to its entities via `link_memory_entities`. ON CONFLICT DO NOTHING makes re-runs free. New writes after v0.14 are linked automatically; the backfill only matters for memories created before the upgrade.

Once the graph is populated:

  • `suggest_connections` returns memories that share entities with an anchor memory (was always empty before).
  • `entity_graph_density` returns real numbers (count of distinct entities and edges per profile).
  • The `spread_entity_activation_memories` RPC walks the bipartite memory/entity graph for context-rich retrieval.

MCP tool: `backfill_entities(profile=None, batch_size=200)`.

Scoring, condensing, and entity enrichment

Scoring and condensing -- three server-side features run automatically, no configuration needed:

  • Novelty detection. When you store a memory, Ogham checks how similar it is to what you already have. Redundant content gets a lower novelty score and ranks quieter in search results. You can still find it, but it won't push out more useful memories.
  • Content signal scoring. Memories that mention decisions, errors, architecture, or contain code blocks get a higher signal score. A debug session where you fixed a real bug ranks above a casual note about a meeting. The scoring is pure regex, no LLM involved.
  • Automatic condensing. Old memories that nobody accesses gradually shrink. Full text becomes a summary of key sentences, then a one-line description with tags. The original is always preserved and can be restored if the memory becomes relevant again. Run `compress_old_memories` manually or on a schedule. High-importance and frequently-accessed memories resist condensing.

Entity enrichment -- every memory is automatically enriched at ingest with structured entity tags, no LLM calls, pure regex and dictionary matching across 18 languages:

  • Six entity categories. Events (wedding, concert, meeting), activities (hiking, coding, cooking), emotions (frustrated, happy, relieved), relationships (sister, boss, colleague), quantities (3 books, 5 miles), and locations (Berlin, Tokyo -- via GeoNames database).
  • 18 languages. English, German, French, Spanish, Italian, Portuguese, Brazilian Portuguese, Dutch, Polish, Russian, Ukrainian, Turkish, Arabic, Hindi, Japanese, Korean, Chinese, and Irish. Each language includes common inflected forms (case endings, verb tenses, lenition) so "svadʹbu" matches "svadʹba" in Russian and "bhainis" matches "bainis" in Irish.
  • Timeline table. Search results include a chronological timeline with pre-computed "days ago" and memory ID cross-references. Helps LLM readers answer temporal questions without doing date arithmetic.
  • Lost in the Middle reordering. Search results are reordered so the highest-relevance memories appear at the start and end of the context, where LLMs pay the most attention (Liu et al., 2023).

Cross-encoder reranking (FlashRank) + ONNX local embeddings

Cross-encoder reranking -- optional FlashRank cross-encoder reranking for self-hosters who want better ranking precision. Adds ~300ms per search on CPU. After Ogham's hybrid search returns candidates, FlashRank (ms-marco-MiniLM-L-12-v2, 21MB) rescores each result against the query using deeper token-level attention. The final score blends retrieval ranking with cross-encoder ranking.

BEAM benchmark impact: R@10 0.69 → 0.70, MRR +8pp. Biggest gain: temporal reasoning 0.84 → 0.98. Full results.

bash
pip install ogham-mcp[rerank]
# or: uv add ogham-mcp[rerank]

export RERANK_ENABLED=true
export RERANK_ALPHA=0.55   # 55% cross-encoder, 45% retrieval score

The model downloads on first use (~21MB). Self-hosters who want speed over precision leave it off (the default).

ONNX local embeddings -- run BGE-M3 locally with ONNX Runtime, dense and sparse vectors in a single model pass, no API calls, no GPU required. Contributed by @ninthhousestudios. The ONNX provider produces 1024-dim dense vectors plus neural sparse vectors. When sparse vectors are available, Ogham automatically uses three-signal Reciprocal Rank Fusion (dense + FTS + sparse) instead of the default two-signal path.

bash
pip install ogham-mcp[onnx]

# Download the model (~2.2GB)
ogham download-model bge-m3

export EMBEDDING_PROVIDER=onnx
export EMBEDDING_DIM=1024

Your database schema must use `vector(1024)` for the embedding column. Performance on CPU: ~0.3s per short text, ~10s for long documents (5K+ chars). RSS: ~4.3GB peak. The ONNX provider is designed for self-hosters who want zero API costs. Cloud users should use Gemini or Voyage for lower latency.

Database setup, schema files, and upgrading

Ogham works with Supabase or vanilla PostgreSQL. Run the schema file that matches your setup:

FileUse case
`sql/schema.sql`Supabase Cloud
`sql/schema_selfhost_supabase.sql`Self-hosted Supabase with RLS
`sql/schema_postgres.sql`Vanilla PostgreSQL / Neon (no RLS)

Supabase and Neon both include pgvector out of the box -- no extra setup needed. If you're self-hosting Postgres, you need PostgreSQL 15+ with the pgvector extension installed. We develop and test against PostgreSQL 17. For Postgres, set `DATABASE_BACKEND=postgres` and `DATABASE_URL=postgresql://...` in your environment.

Local pgvector test database -- Postgres integration tests are intentionally scratch-only. They run real memory rows through PostgreSQL + pgvector, but skip unless `DATABASE_URL` contains `scratch` or `OGHAM_TEST_ALLOW_DESTRUCTIVE=1` is set. This keeps ordinary `pytest` runs from touching a personal or production Ogham database.

bash
make test-postgres-db

export DATABASE_BACKEND=postgres
export DATABASE_URL=postgresql://ogham:ogham@localhost:5433/ogham_scratch

uv run pytest -m postgres_integration
# or:
make test-postgres

The test harness applies the canonical `sql/schema_postgres.sql` to an empty scratch database, and reapplies the idempotent baseline migrations needed by current tests on older scratch databases. External Supabase + Ollama integration tests are opt-in:

bash
OGHAM_RUN_EXTERNAL_INTEGRATION=1 uv run pytest -m integration -v
# or:
make test-external

Upgrading an existing Ogham database

For v0.10.x → v0.11.0 (memory lifecycle release), see UPGRADING.md. The short version:

bash
./sql/upgrade.sh $DATABASE_URL     # applies 025 + 026 + 027 idempotently

Fresh installers do NOT need this -- `sql/schema.sql` already reflects the post-v0.11.0 state.

For older versions (v0.4.x through v0.10.x), the same `upgrade.sh` script walks through every migration in order -- temporal columns, halfvec compression, sparse embeddings, RRF/BM25 search, then the v0.11.0 lifecycle additions. Each is idempotent.

bash
# Postgres / Neon (psql required)
./sql/upgrade.sh $DATABASE_URL

# Supabase: paste migration files into the SQL Editor in order
#   (025_memory_lifecycle.sql → 026_memory_lifecycle_split.sql → 027_audit_log_backfill.sql
#   are the v0.11.0 set)

Selected migration highlights:

  • 016 adds the `sparse_embedding` column for ONNX BGE-M3 sparse vectors.
  • 017 upgrades the search function to true Reciprocal Rank Fusion with length-normalised keyword scoring (Cormack et al., 2009).
  • 025 / 026 add the memory lifecycle table + triggers (see UPGRADING.md).
  • 027 backfills the `audit_log` table for installs that predate it.

Rollback scripts live under `sql/migrations/rollback/` with a `DANGER_` prefix and require explicit session-variable opt-in before they do anything. See `sql/migrations/rollback/README.md`.

All migrations are idempotent -- safe to re-run. The upgrade script checks your pgvector version and skips halfvec if pgvector is below 0.7.0. New installs don't need migrations -- the schema files already include everything.

Upgrading the CLI (uv tool) -- uv caches aggressively. A plain `uv tool install ogham-mcp` after a new release may install the old version. Use `--refresh` to force a fresh resolve from PyPI:

bash
uv tool uninstall ogham-mcp
uv cache clean
uv tool install --refresh "ogham-mcp[gemini,postgres]"

Verify with `ogham config` (shows version and provider at the top). If you still see the old version, nuke the tool environment directory and retry:

bash
rm -rf ~/.local/share/uv/tools/ogham-mcp
uv tool install --refresh "ogham-mcp[gemini,postgres]"

This is a known uv caching behaviour -- the resolver cache is separate from the package cache and survives `uv cache clean` without `--refresh`.

Skills (ogham-research, ogham-recall, ogham-maintain)

Ogham ships with three workflow skills in `skills/` that wire up common MCP tool chains. Install them in Claude Code, Cursor, or any client that supports skills.

SkillTriggers onWhat it does
`ogham-research`"remember this", "store this finding", "save what we learned"Checks for duplicates via hybrid_search before storing. Auto-tags with a consistent scheme (`type:decision`, `type:gotcha`, etc.). Uses `store_decision` for architectural choices.
`ogham-recall`"what do I know about X", "find related", "context for this project"Chains hybrid_search, find_related, and explore_knowledge to surface connections. Bootstraps session context at project start.
`ogham-maintain`"memory stats", "clean up my memory", "export my brain"Runs health_check, get_stats, cleanup_expired, re_embed_all, link_unlinked. Warns before irreversible operations.

Skills call existing MCP tools -- they don't replace them. The MCP server must be connected for skills to work.

bash
npx skills add ogham-mcp/ogham-mcp                        # all three
npx skills add ogham-mcp/ogham-mcp --skill ogham-recall   # one
cp -r skills/ogham-research skills/ogham-recall skills/ogham-maintain ~/.claude/skills/   # manual

Documentation

Full docs and integration guides at ogham-mcp.dev.

Credits

Inspired by Nate B Jones and his work on persistent AI memory.

Named after Ogham, the ancient Irish alphabet carved into stone -- the original persistent memory.

License

MIT

Frequently asked questions

What is ogham-mcp?

ogham-mcp is Shared memory MCP server — persistent, searchable, cross-client Claude, Opencode

How do I install ogham-mcp?

Open the GitHub repository and follow its README. Most MCP servers are added to your client's MCP config, then called by your agent.

Is ogham-mcp open source?

Yes — it is hosted on GitHub at https://github.com/ogham-mcp/ogham-mcp and has 114 stars.

Related MCP tools

Run your own MCP server? See who uses it and what to fix.

Measure it with TrackMCP