File: README.md
π§ This project is currently being rewritten.
Expect breaking changes and potential bugs.
π **Weβre actively accepting volunteers!**
Weβre looking for developers, social media volunteers, designers, content creators, community helpers, and more. If youβd like to contribute and be part of what weβre building, weβd love to have you!
π© Interested? Reach out at [email protected]
OpenMemory
Real long-term memory for AI agents. Not RAG. Not a vector DB. Self-hosted, Python + Node.
OpenMemory is a cognitive memory engine for LLMs and agents.
- π§ Real long-term memory (not just embeddings in a table)
- πΎ Self-hosted, local-first (SQLite / Postgres)
- π Python + π¦ Node SDKs
- π§© Integrations: LangChain, CrewAI, AutoGen, Streamlit, MCP, VS Code
- π₯ Sources: GitHub, Notion, Google Drive, OneDrive, Web Crawler
- π Explainable traces (see why something was recalled)
Your model stays stateless. Your app stops being amnesiac.
1. TL;DR β Use It in 10 Seconds
π Python (local-first)
Install:
pip install openmemory-pyUse:
from openmemory.client import Memory
mem = Memory()
mem.add("user prefers dark mode", user_id="u1")
results = mem.search("preferences", user_id="u1")
await mem.delete("memory_id")Note:
add,search,get,deleteare async. Useawaitin async contexts.
π OpenAI
mem = Memory()
client = mem.openai.register(OpenAI(), user_id="u1")
resp = client.chat.completions.create(...)π§± LangChain
from openmemory.integrations.langchain import OpenMemoryChatMessageHistory
history = OpenMemoryChatMessageHistory(memory=mem, user_id="u1")π€ CrewAI / AutoGen / Streamlit
OpenMemory is designed to sit behind agent frameworks and UIs:
- Crew-style agents: use
Memoryas a shared long-term store - AutoGen-style orchestrations: store dialog + tool calls as episodic memory
- Streamlit apps: give each user a persistent memory by
user_id
See the integrations section in the docs for concrete patterns.
π¦ Node / JavaScript (local-first)
Install:
npm install openmemory-jsUse:
import { Memory } from "openmemory-js"
const mem = new Memory()
await mem.add("user likes spicy food", { user_id: "u1" })
const results = await mem.search("food?", { user_id: "u1" })
await mem.delete("memory_id")Drop this into:
- Node backends
- CLIs
- local tools
- anything that needs durable memory without running a separate service.
π₯ Connectors
Ingest data from external sources directly into memory:
# python
github = mem.source("github")
await github.connect(token="ghp_...")
await github.ingest_all(repo="owner/repo")// javascript
const github = await mem.source("github")
await github.connect({ token: "ghp_..." })
await github.ingest_all({ repo: "owner/repo" })Available connectors: github, notion, google_drive, google_sheets, google_slides, onedrive, web_crawler
2. Modes: SDKs, Server, MCP
OpenMemory can run inside your app or as a central service.
2.1 Python SDK
- β Local SQLite by default
- β Supports external DBs (via config)
- β Great fit for LangChain / LangGraph / CrewAI / notebooks
Docs: https://openmemory.cavira.app/docs/sdks/python
2.2 Node SDK
- Same cognitive model as Python
- Ideal for JS/TS applications
- Can either run fully local or talk to a central backend
Docs: https://openmemory.cavira.app/docs/sdks/javascript
2.3 Backend server (multi-user + dashboard + MCP)
Use when you want:
- orgβwide memory
- HTTP API
- dashboard
- MCP server for Claude / Cursor / Windsurf
Run from source:
git clone https://github.com/CaviraOSS/OpenMemory.git
cd OpenMemory
cp .env.example .env
cd packages/openmemory-js
npm install
npm run dev # default :8080Or with Docker (API + MCP):
docker compose up --build -dOptional: include the dashboard service profile:
docker compose --profile ui up --build -dUsing Doppler-managed config (recommended for hosted dashboard/API URLs):
cd OpenMemory
tools/ops/compose_with_doppler.sh up -d --buildCheck service status:
docker compose ps
curl -f http://localhost:8080/healthThe backend exposes:
/api/memory/*β memory operations/api/temporal/*β temporal knowledge graph/mcpβ MCP server- dashboard UI (when
uiprofile is enabled)
3. Why OpenMemory (vs RAG, vs βjust vectorsβ)
LLMs forget everything between messages.
Most βmemoryβ solutions are really just RAG pipelines:
- text is chunked
- embedded into a vector store
- retrieved by similarity
They donβt understand:
- whether something is a fact, event, preference, or feeling
- how recent / important it is
- how it links to other memories
- what was true at a specific time
Cloud memory APIs add:
- vendor lockβin
- latency
- opaque behavior
- privacy problems
OpenMemory gives you an actual memory system:
- π§ Multiβsector memory (episodic, semantic, procedural, emotional, reflective)
- β± Temporal reasoning (what was true when)
- π Decay & reinforcement instead of dumb TTLs
- πΈ Waypoint graph (associative, traversable links)
- π Explainable traces (see which nodes were recalled and why)
- π Selfβhosted, localβfirst, you own the DB
- π SDKs + server + VS Code + MCP
It behaves like a memory module, not a βvector DB with marketing copyβ.
4. The βOld Wayβ vs OpenMemory
Vector DB + LangChain (cloud-heavy, ceremony):
import os
import time
from langchain.chains import ConversationChain
from langchain.memory import VectorStoreRetrieverMemory
from langchain_community.vectorstores import Pinecone
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
os.environ["PINECONE_API_KEY"] = "sk-..."
os.environ["OPENAI_API_KEY"] = "sk-..."
time.sleep(3) # cloud warmup
embeddings = OpenAIEmbeddings()
pinecone = Pinecone.from_existing_index(embeddings, index_name="my-memory")
retriever = pinecone.as_retriever(search_kwargs={"k": 2})
memory = VectorStoreRetrieverMemory(retriever=retriever)
conversation = ConversationChain(llm=ChatOpenAI(), memory=memory)
conversation.predict(input="I'm allergic to peanuts")OpenMemory (3 lines, local file, no vendor lock-in):
from openmemory.client import Memory
mem = Memory()
mem.add("user allergic to peanuts", user_id="user123")
results = mem.search("allergies", user_id="user123")β Zero cloud config β’ β Local SQLite β’ β Offlineβfriendly β’ β Your DB, your schema
5. Features at a Glance
Multi-sector memory
Episodic (events), semantic (facts), procedural (skills), emotional (feelings), reflective (insights).Temporal knowledge graph
valid_from/valid_to, pointβinβtime truth, evolution over time.Composite scoring
Salience + recency + coactivation, not just cosine distance.Decay engine
Adaptive forgetting per sector instead of hard TTLs.Explainable recall
βWaypointβ traces that show exactly which nodes were used in context.Embeddings
OpenAI, Gemini, Ollama, AWS, OrcaRouter, synthetic fallback.Integrations
LangChain, CrewAI, AutoGen, Streamlit, MCP, VS Code, IDEs.Connectors
Import from GitHub, Notion, Google Drive, Google Sheets/Slides, OneDrive, Web Crawler.Migration tool
Import memories from Mem0, Zep, Supermemory and more.
If youβre building agents, copilots, journaling systems, knowledge workers, or coding assistants, OpenMemory is the piece that turns them from βgoldfishβ into something that actually remembers.
6. MCP & IDE Workflow
OpenMemory ships a native MCP server, so any MCPβaware client can treat it as a tool.
Claude / Claude Code
claude mcp add --transport http openmemory http://localhost:8080/mcpCursor / Windsurf
.mcp.json:
{
"mcpServers": {
"openmemory": {
"type": "http",
"url": "http://localhost:8080/mcp"
}
}
}Available tools include:
openmemory_queryopenmemory_storeopenmemory_listopenmemory_getopenmemory_reinforce
Your IDE assistant can query, store, list, and reinforce memories without you wiring every call manually.
7. Temporal Knowledge Graph
OpenMemory treats time as a firstβclass dimension.
Concepts
valid_from/valid_toβ truth windows- autoβevolution β new facts close previous ones
- confidence decay β old facts fade gracefully
- pointβinβtime queries β βwhat was true on X?β
- timelines β reconstruct an entityβs history
- change detection β see when something flipped
Example
POST /api/temporal/fact
{
"subject": "CompanyX",
"predicate": "has_CEO",
"object": "Alice",
"valid_from": "2021-01-01"
}Then later:
POST /api/temporal/fact
{
"subject": "CompanyX",
"predicate": "has_CEO",
"object": "Bob",
"valid_from": "2024-04-10"
}Aliceβs term is automatically closed; timeline queries stay sane.
8. CLI (opm)
The opm CLI talks directly to the engine / server.
Install
cd packages/openmemory-js
npm install
npm run build
npm link # adds `opm` to your PATHUsage
# Start the API server
opm serve
# In another terminal:
opm health
opm add "Recall that I prefer TypeScript over Python" --tags preference
opm query "language preference"Commands
opm add "user prefers dark mode" --user u1 --tags prefs
opm query "preferences" --user u1 --limit 5
opm list --user u1
opm delete
opm reinforce
opm statsUseful for scripting, debugging, and nonβLLM pipelines that still want memory.
9. Architecture (High Level)
OpenMemory uses Hierarchical Memory Decomposition with a temporal graph on top.
/* Detailed source-code truncated for AI context efficiency. */10. Migration
OpenMemory ships a migration tool to import data from other memory systems.
Supported:
- Mem0
- Zep
- Supermemory
Example:
cd migrate
python -m migrate --from zep --api-key ZEP_KEY --verify(See migrate/ and docs for detailed commands per provider.)
11. Roadmap
- 𧬠Learned sector classifier (trainable on your data)
- πΈ Federated / clustered memory nodes
- π€ Deeper LangGraph / CrewAI / AutoGen integrations
- π Memory visualizer 2.0
- π Pluggable encryption at rest
Star the repo to follow along.
12. Contributing
Issues and PRs are welcome.
- Bugs: https://github.com/CaviraOSS/OpenMemory/issues
- Feature requests: use the GitHub issue templates
- Before large changes, open a discussion or small design PR
13. License
OpenMemory is licensed under Apache 2.0. See LICENSE for details.
File: apps/vscode-extension/README.md
OpenMemory - Persistent Memory for AI Assistants
Give your AI tools instant access to your entire coding history. OpenMemory remembers everything you code and automatically provides context to GitHub Copilot, Cursor, Claude, and other AI assistants.
Features
- Works with GitHub Copilot, Cursor, Claude, Windsurf, Codex, and any MCP-compatible AI
- Auto-configures all AI tools on first run with zero manual setup
- Tracks every file edit, save, and open automatically
- NEW: Manually add code selections to memory with a single click
- Compresses memories to reduce tokens by 30-70%
- Query responses under 80ms with smart caching
- Real-time token savings and compression metrics
- Background processing never blocks UI
Quick Start
- Install this extension
- Start backend
- Click OpenMemory icon in status bar to verify connection
- Start coding - AI tools now access your coding memory
Requirements
Backend server required.
Settings
openmemory.backendUrl: Backend URL (default:http://localhost:8080)openmemory.apiKey: API key for auth (optional)openmemory.useMCP: Use MCP protocol mode (default:false) - connects to backend MCP server with tools:openmemory_query,openmemory_store,openmemory_list,openmemory_get,openmemory_reinforceopenmemory.mcpServerPath: Path to backend MCP server (default:backend/dist/ai/mcp.js)openmemory.userId: Custom User ID (optional, defaults to auto-generated)openmemory.projectName: Custom Project Name (optional, defaults to workspace name)
Commands
OpenMemory: Query Context- Search your coding memoryOpenMemory: Add Selection to Memory- Save selected code to memoryOpenMemory: Quick Note- Add a manual note to memoryOpenMemory: View Patterns- View detected patternsOpenMemory: Toggle Tracking- Pause or resume trackingOpenMemory: Setup- Configure backend and settings
Privacy
All data stores locally. No telemetry. Open source code available for audit.
Troubleshooting
Check backend running: curl http://localhost:8080/health
For issues, see GitHub
Links
Made by the Cavira team
File: dashboard/README.md
OpenMemory Dashboard
This app is the optional OpenMemory dashboard UI.
What it is
- a separate Next.js app in
dashboard/ - designed to talk to an OpenMemory backend over HTTP
- not bundled into the bare
packages/openmemory-jsnpm install
If you are running OpenMemory without the dashboard, you only need the backend in packages/openmemory-js.
Backend requirement
Start the backend first:
cd packages/openmemory-js
npm install
npm run devBy default the dashboard calls its same-origin server-side proxy at /api/openmemory, which forwards requests to the OpenMemory backend.
Configure the backend URL and optional API key in .env.local:
OPENMEMORY_API_URL=http://localhost:8080
# OPENMEMORY_API_KEY=your-secret-api-keyThis keeps authenticated backend API keys on the server. Set NEXT_PUBLIC_API_URL only when the dashboard must call a different URL directly; API keys should remain in the server-only OPENMEMORY_API_KEY or OM_API_KEY environment variables.
Run the dashboard locally
cd dashboard
npm install
npm run devThen open http://localhost:3000.
Docker
If you want the full local stack, you can also run OpenMemory with Docker and enable the dashboard/UI profile from the repository root.
Related docs
README.mdβ top-level project overviewdashboard/CHAT_SETUP.mdβ dashboard-to-backend setup detailspackages/openmemory-js/README.mdβ backend / SDK docs
File: docs/api-server.md
API Server
OpenMemory exposes a REST API for language-agnostic integration.
Base URL: http://localhost:8080 (default)
Endpoints
`POST /memory/add`
Add a new memory.
Body:
{
"content": "My cat's name is Luna",
"user_id": "user_123",
"tags": ["pet"]
}`POST /memory/search`
Search for memories.
Body:
{
"query": "What is the pet name?",
"user_id": "user_123",
"limit": 3
}Response:
{
"memories": [
{
"id": "mem_abc123",
"content": "My cat's name is Luna",
"score": 0.89
}
]
}`GET /health`
Returns 200 OK if the system is running.
Running the Server
You can run the server using Docker or the Node CLI.
Docker
docker run -p 8080:8080 openmemory/serverCLI
opm serve --port 9000File: docs/concepts.md
Core Concepts
OpenMemory differs from a standard Vector DB by implementing a cognitive architecture inspired by human memory systems.
Memory Architecture
- Episodic Memory: Stores specific events and interactions.
- Semantic Memory: Stores generalized facts and knowledge derived from episodes.
- Procedural Memory: (Planned) Stores "how-to" knowledge and tools.
The Hierarchical Storage Graph (HSG)
Data is not just dumped into a flat index. It is organized hierarchically:
- Sectors: High-level domains (e.g., "Personal", "Work", "Code").
- Nodes: Individual memory units.
- Edges: Relationships between memories (temporal, semantic, or explicit entities).
Temporal Graph
Time is a first-class citizen. OpenMemory tracks:
- Creation Time: When the memory was formed.
- Last Access: When it was last retrieved (used for decay).
- Sequence: The causal chain of events (Chat History is a temporal chain).
This allows for queries like "What did we talk about last week?" or "What happened before I mentioned the bug?".
Retrieval Dynamics (Scoring)
When you call search(), memories are scored based on three factors:
- Relevance (Vector Similarity): How semantically similar is the query?
- Recency: Recent memories are weighted higher (decay function).
- Importance: "Core" memories (often retrieved) gain persistence.
Score = (Similarity * alpha) + (Recency * beta) + (Importance * gamma)
User Partitioning
All memories are strictly partitioned by user_id. This ensures that in multi-user applications (like a SaaS bot), User A never sees User B's memories.
File: docs/faq.md
Frequently Asked Questions
How does this differ from Chroma/Pinecone?
OpenMemory is an agentic memory layer, not just a vector database. It handles user separation, temporal tracking, and memory dynamics (decay, reinforcement) out of the box.
Can I use it with any LLM?
Yes. OpenMemory is model-agnostic. You can use OpenAI, Anthropic, or local models (Llama 3 via Ollama).
Is the data persistent?
Yes. By default, it uses SQLite which saves to a file. You can also configure Postgres for production scaling.
File: docs/getting-started.md
Getting Started with OpenMemory
OpenMemory provides a drop-in, SQL-native long-term memory layer for your AI agents. It goes beyond simple vector storage by incorporating temporal graphs, user entity tracking, and advanced retrieval dynamics (Recency, Frequency, Importance).
Installation
Python
pip install openmemory-pyNode.js
npm install openmemory-jsQuick Start (Python)
import asyncio
from openmemory.client import Memory
async def main():
# 1. Initialize (uses local sqlite by default)
mem = Memory()
# 2. Add a memory
await mem.add("I am a software engineer interested in AI.", user_id="user_123")
# 3. Search related context
context = await mem.search("What is my job?", user_id="user_123")
print(context)
# output: [{'content': 'I am a software engineer...', ...}]
if __name__ == "__main__":
asyncio.run(main())Quick Start (Node.js)
const { Memory } = require('openmemory-js');
async function main() {
const mem = new Memory();
await mem.add("I prefer coffee over tea.", { user_id: 'user_123' });
const context = await mem.search("What do I drink?", { user_id: 'user_123' });
console.log(context);
}
main();Configuration
OpenMemory defaults to sqlite:///openmemory.db in the current directory. You can configure this via environment variables:
OPENMEMORY_DB_URL: Database connection string (e.g.,postgresql://...)OPENMEMORY_DEBUG: Set totruefor verbose logging.
File: docs/mcp.md
MCP Server Reference
OpenMemory provides a Model Context Protocol (MCP) server that exposes memory operations as tools for AI assistants like Claude, Cursor, and Windsurf.
Setup
Installation
# Use via npx (no installation needed)
npx -y openmemory-js mcp
# Or install globally
npm install openmemory-js
openmemory-js mcpConfiguration
Configure your MCP client (.mcp.json for Cursor/Windsurf, or Claude settings):
{
"mcpServers": {
"openmemory": {
"command": "npx",
"args": ["-y", "openmemory-js", "mcp"],
"env": {
"OM_METADATA_BACKEND": "sqlite",
"OM_EMBEDDINGS": "openai",
"OPENAI_API_KEY": "your-api-key"
}
}
}
}Environment Variables
OM_METADATA_BACKEND: Database type (sqliteorpostgres)OM_EMBEDDINGS: Embedding provider (openai,aws,ollama,synthetic,orcarouter)- Database credentials (if using Postgres):
OM_PG_HOST,OM_PG_DB,OM_PG_USER,OM_PG_PASSWORD
Memory Systems
OpenMemory provides two complementary memory systems:
Contextual Memory (HSG)
Semantic memory organized into cognitive sectors (episodic, semantic, procedural, emotional, reflective) with vector-based retrieval.
Best for: Conversations, rich context, experiential memories
Temporal Facts
Structured facts (subject-predicate-object triples) with time-based validity and automatic invalidation.
Best for: Preferences, business rules, configurations that change over time
Available Tools
openmemory_store
Store content in contextual memory, temporal facts, or both.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
content |
string | Yes | - | Raw memory text |
type |
enum | No | "contextual" | Storage type: "contextual", "factual", or "both" |
facts |
array | Conditional | - | Facts array (required if type is "factual" or "both") |
tags |
string[] | No | - | Tags for contextual storage |
metadata |
object | No | - | Additional metadata |
user_id |
string | No | - | User identifier |
project_id |
string | No | "system_global" | Project identifier for isolation |
Facts Array Schema
{
subject: string, // Entity (e.g., "user_123", "client_acme")
predicate: string, // Relationship (e.g., "prefers_format", "approval_limit")
object: string, // Value (e.g., "Excel", "50000")
confidence?: number, // 0-1 score (default: 1.0)
valid_from?: string // ISO date (default: now)
}Examples
Store contextual memory:
{
"content": "User prefers morning meetings",
"tags": ["preference", "scheduling"],
"user_id": "user_123"
}Store temporal fact:
{
"content": "Client prefers Excel format",
"type": "factual",
"facts": [{
"subject": "client_acme",
"predicate": "prefers_report_format",
"object": "Excel"
}],
"user_id": "user_123"
}Store in both systems:
{
"content": "Approval threshold updated to $75K",
"type": "both",
"facts": [{
"subject": "expense_policy",
"predicate": "approval_threshold_usd",
"object": "75000"
}],
"tags": ["policy", "threshold"],
"user_id": "user_123"
}openmemory_store_project
Store content in contextual memory or temporal facts, SCOPED to a specific PROJECT. Use this for project-specific designs, local variables, or scoped knowledge that should not bleed into other projects.
Parameters
Identical to openmemory_store, but project_id is RECOMMENDED to ensure proper isolation.
Example
{
"content": "Project uses Tailwind CSS for styling",
"project_id": "my_web_app",
"user_id": "user_123"
}openmemory_query
Query contextual memories, temporal facts, or both.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
query |
string | Yes | - | Search text |
type |
enum | No | "contextual" | Query type: "contextual", "factual", or "unified" |
fact_pattern |
object | No | - | Pattern for temporal queries |
at |
string | No | now | ISO date for point-in-time queries |
k |
number | No | 8 | Max results for contextual queries |
sector |
enum | No | - | Filter by sector (episodic, semantic, procedural, emotional, reflective) |
min_salience |
number | No | - | Minimum salience threshold (0-1) |
user_id |
string | No | - | User identifier |
project_id |
string | No | - | Project isolation filter |
Fact Pattern Schema
{
subject?: string, // Entity to match (undefined = wildcard)
predicate?: string, // Relationship to match (undefined = wildcard)
object?: string // Value to match (undefined = wildcard)
}Examples
Query contextual memories:
{
"query": "user preferences",
"user_id": "user_123"
}Query current facts:
{
"query": "client report format",
"type": "factual",
"fact_pattern": {
"subject": "client_acme",
"predicate": "prefers_report_format"
}
}Historical query:
{
"query": "Q3 approval limit",
"type": "factual",
"fact_pattern": {
"subject": "expense_policy",
"predicate": "approval_threshold_usd"
},
"at": "2023-09-30T00:00:00Z"
}Unified query:
{
"query": "client information",
"type": "unified",
"fact_pattern": {
"subject": "client_acme"
},
"k": 10,
"user_id": "user_123"
}openmemory_list
List recent memories.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
limit |
number | No | 10 | Number of memories to return (max 50) |
sector |
enum | No | - | Filter by sector |
user_id |
string | No | - | User identifier |
project_id |
string | No | - | Project isolation filter |
Example
{
"limit": 20,
"sector": "semantic",
"user_id": "user_123"
}openmemory_get
Fetch a single memory by ID.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
id |
string | Yes | - | Memory identifier |
include_vectors |
boolean | No | false | Include sector vector metadata |
user_id |
string | No | - | User identifier |
project_id |
string | No | - | Project isolation filter |
Example
{
"id": "mem_abc123",
"include_vectors": true,
"user_id": "user_123"
}openmemory_reinforce
Boost salience of a memory.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
id |
string | Yes | - | Memory identifier |
boost |
number | No | 0.1 | Salience boost amount (0.01-1.0) |
Example
{
"id": "mem_abc123",
"boost": 0.15
}Response Formats
openmemory_store
Contextual storage:
{
"type": "contextual",
"hsg": {
"id": "mem_abc123",
"primary_sector": "semantic",
"sectors": ["semantic", "procedural"]
}
}Factual storage:
{
"type": "factual",
"temporal": [{
"id": "fact_xyz789",
"subject": "client_acme",
"predicate": "prefers_format",
"object": "Excel",
"valid_from": "2024-01-15T10:00:00.000Z",
"confidence": 0.95
}]
}openmemory_query
Contextual query:
{
"type": "contextual",
"contextual": [{
"source": "hsg",
"id": "mem_abc123",
"score": 0.8921,
"content": "User prefers morning meetings...",
"primary_sector": "semantic",
"salience": 0.756
}]
}Factual query:
{
"type": "factual",
"factual": [{
"source": "temporal",
"id": "fact_xyz789",
"subject": "client_acme",
"predicate": "prefers_format",
"object": "Excel",
"confidence": 0.95,
"valid_from": "2024-01-15T10:00:00.000Z",
"valid_to": null
}]
}Unified query:
{
"type": "unified",
"contextual": [...], // HSG results
"factual": [...] // Temporal facts
}Key Features
Automatic Fact Invalidation
When storing a fact with the same subject+predicate, the previous fact is automatically closed:
Day 1: Store (client_acme, prefers_format, PDF)
β Stored with valid_to = null
Day 2: Store (client_acme, prefers_format, Excel)
β Old fact: valid_to = Day 2 (closed)
β New fact: valid_to = null (current)Point-in-Time Queries
Query what was true at any historical date:
{
"type": "factual",
"fact_pattern": {"subject": "client_acme", "predicate": "prefers_format"},
"at": "2024-01-10T00:00:00Z"
}Returns the fact that was valid on January 10th, even if it's since been updated.
Wildcard Patterns
Use undefined fields in fact_pattern for wildcards:
// All facts about client_acme
{"subject": "client_acme"}
// All preferences for any subject
{"predicate": "prefers"}
// All facts with object "Excel"
{"object": "Excel"}Best Practices
Storage Type Selection
| Type | Use Case | Example |
|---|---|---|
contextual |
Rich narratives, conversations | "Team discussed the Q4 strategy..." |
factual |
Structured data that changes | client_acme prefers_format Excel |
both |
Rich context with extractable facts | "Client wants monthly Excel reports" |
Fact Naming Conventions
Subject: Specific entity identifier
- β
user_123,client_acme_corp,expense_policy - β
the user,client,policy
Predicate: Clear relationship in snake_case
- β
prefers_report_format,requires_approval_at,uses_data_source - β
preference,requirement,uses
Object: Concrete value
- β
Excel,50000,Salesforce_CRM - β
file format,high,CRM system
Query Strategy
Use type="contextual" when:
- Asking open-ended questions
- Needing semantic similarity
- Searching conversations
Use type="factual" when:
- Asking for specific current values
- Querying structured data
- Need historical accuracy
Use type="unified" when:
- Need comprehensive context
- Unsure which system has the answer
- Want both semantic context AND facts
Integration Examples
Claude Desktop
{
"mcpServers": {
"openmemory": {
"command": "npx",
"args": ["-y", "openmemory-js", "mcp"]
}
}
}Cursor / Windsurf
Add to .cursorrules or .windsurfrules:
When storing user information, use openmemory_store:
- type="contextual" for conversations
- type="factual" for preferences that may change
- Always include user_id parameter
When querying, use openmemory_query:
- type="unified" for comprehensive context
- Construct fact_pattern for specific fact queriesProgrammatic (Node.js)
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
const client = new Client({
name: 'my-app',
version: '1.0.0'
}, { capabilities: {} });
const transport = new StdioClientTransport({
command: 'npx',
args: ['-y', 'openmemory-js', 'mcp']
});
await client.connect(transport);
// Store memory
await client.callTool({
name: 'openmemory_store',
arguments: {
content: "User prefers concise responses",
type: "both",
facts: [{
subject: "user_123",
predicate: "prefers_response_style",
object: "concise"
}],
user_id: "user_123"
}
});
// Query
const result = await client.callTool({
name: 'openmemory_query',
arguments: {
query: "user preferences",
type: "unified",
user_id: "user_123"
}
});Common Patterns
Track Evolving Preferences
// Initial preference
{
"content": "Client prefers PDF reports",
"type": "both",
"facts": [{"subject": "client_x", "predicate": "prefers_format", "object": "PDF"}]
}
// Updated preference (automatically invalidates old fact)
{
"content": "Client now prefers Excel reports",
"type": "both",
"facts": [{"subject": "client_x", "predicate": "prefers_format", "object": "Excel"}]
}
// Query current preference
{
"query": "client report format",
"type": "factual",
"fact_pattern": {"subject": "client_x", "predicate": "prefers_format"}
}
// Returns: ExcelHistorical Analysis
// What was true last quarter?
{
"query": "approval threshold in Q3",
"type": "factual",
"fact_pattern": {
"subject": "expense_policy",
"predicate": "approval_threshold_usd"
},
"at": "2023-09-30T00:00:00Z"
}Comprehensive Context
// Get both conversations and facts
{
"query": "client requirements",
"type": "unified",
"fact_pattern": {"subject": "client_x"},
"k": 10
}
// Returns: Discussions + current factsDatabase Schema
Contextual Memory Tables
memories/openmemory_memories- Memory content and metadatavectors/openmemory_vectors- Sector-specific embeddingswaypoints/openmemory_waypoints- Associative graph connections
Temporal Graph Tables
temporal_facts- Subject-predicate-object facts with validity periodstemporal_edges- Relationships between facts
Both SQLite and Postgres are supported.
Troubleshooting
"Facts array is required" Error
When using type="factual" or type="both", you must provide the facts parameter:
{
"content": "...",
"type": "factual",
"facts": [{ "subject": "...", "predicate": "...", "object": "..." }]
}Empty Temporal Results
If type="factual" returns no results:
- Verify facts were stored with correct subject/predicate
- Check
atparameter matches validity period - Use wildcards in fact_pattern to broaden search
Connection Issues
Ensure environment variables are set:
- Postgres:
OM_PG_HOST,OM_PG_DB,OM_PG_USER,OM_PG_PASSWORD - Embeddings:
OPENAI_API_KEY(or relevant provider key, e.g.ORCAROUTER_API_KEY)
Performance
- Contextual queries: ~50-200ms (vector similarity + graph traversal)
- Factual queries: ~5-20ms (indexed SQL lookups)
- Unified queries: Run in parallel, total β max(contextual, factual)
See Also
- Node SDK - Direct API usage in Node.js applications
- Python SDK - Direct API usage in Python applications
- Getting Started - Installation and setup guide