memory-mcp
by vinnyho
README.md
# memory-mcp
A persistent, conflict-aware memory MCP server for AI coding assistants (Cursor, Claude Code).
Every AI coding session starts from zero. This MCP server gives the AI a queryable memory store that survives across sessions - and unlike append-only tools, it detects when a new fact contradicts an older one and automatically invalidates the stale memory instead of letting both exist.
## How it works
When you store a memory like "we switched from Postgres to SQLite":
1. The content is embedded (OpenAI `text-embedding-3-small`)
2. Vector search finds similar active memories
3. Claude Haiku checks each candidate - does this contradict the new fact?
4. Confirmed conflicts get invalidated (`valid_to = now`) - never deleted
5. The new memory is inserted as currently true
6. A supersession link is recorded so the history is traceable
Old facts are never deleted. You can query what was true at any point in time.
## Tools
| Tool | Description |
|---|---|
| `store_memory` | Save a fact about the project. Detects and invalidates conflicts automatically. |
| `retrieve_context` | Hybrid vector + keyword search over currently-true memories. |
| `query_history` | Point-in-time query - what was true at a given timestamp? |
| `list_conflicts_resolved` | Audit log of every invalidation and why. |
## Stack
- TypeScript + Node.js 18
- PostgreSQL 16 + pgvector (HNSW index, cosine similarity)
- OpenAI `text-embedding-3-small` (1536 dims)
- Anthropic Claude Haiku (conflict detection)
- MCP SDK (stdio transport)
- Zod (input validation)
## Setup
```bash
# 1. Start Postgres
docker compose up -d
# 2. Install dependencies
npm install
# 3. Copy and fill in API keys
cp .env.example .env
# edit .env - add OPENAI_API_KEY and ANTHROPIC_API_KEY
# 4. Run migrations
npm run migrate
# 5. Start the server
npm run dev
```
## Register in Cursor
Add to `.cursor/mcp.json`:
```json
{
"mcpServers": {
"developer-memory": {
"command": "node",
"args": ["/absolute/path/to/my-memory-mcp/dist/index.js"]
}
}
}
```
Build first: `npm run build`
## Key design decisions
**Invalidate, never delete** - setting `valid_to` instead of deleting preserves history. Point-in-time queries (`query_history`) would be impossible otherwise.
**Conflict similarity threshold: 0.45** - the spec suggested 0.75, but measured against labeled contradiction pairs on `text-embedding-3-small`, true conflicts score 0.50 to 0.82 cosine similarity. At 0.75 the candidate filter silently drops most real conflicts before the LLM sees them. 0.45 catches them all.
**RRF over score averaging** - cosine similarity and `ts_rank` live on incomparable scales. Reciprocal Rank Fusion uses only rank positions, so the two search legs fuse cleanly without normalization guesswork.
**LLM default to no-conflict** - if Claude returns an unparseable response, the code treats it as no conflict. A false negative leaves a stale memory (recoverable). A false positive deletes a true fact (worse).
## Project structure
```
src/
├── index.ts entry point
├── config.ts env → typed config
├── types.ts shared interfaces
├── db.ts MemoryStore + migrations + vector/keyword search
├── embeddings.ts OpenAI embeddings client
├── memory.ts conflict detection + invalidation + ingestion pipeline
├── retrieval.ts RRF hybrid retrieval
└── server.ts MCP server + 4 tool handlers
```
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues