# Semantic Recall

> Persistent semantic memory for LLM apps : zero config, zero API keys, two methods.

- **Type:** MCP server
- **Install:** `agentstack add mcp-skt329-semantic-recall`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Skt329](https://agentstack.voostack.com/s/skt329)
- **Installs:** 0
- **Category:** [Databases](https://agentstack.voostack.com/c/databases)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Skt329](https://github.com/Skt329)
- **Source:** https://github.com/Skt329/semantic-recall
- **Website:** https://www.npmjs.com/package/semantic-recall

## Install

```sh
agentstack add mcp-skt329-semantic-recall
```

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

## About

🧠 semantic-recall
  
    Give your AI a brain that remembers.
    
    Persistent semantic memory for LLM apps — zero config, zero API keys, two methods.
  
  
    
    
    
    
    
  
  
    📦 View on npm ·
    ⭐ Star on GitHub ·
    🐛 Report a Bug ·
    💡 Request a Feature
  

---

Every LLM chatbot forgets everything between sessions. Users repeat themselves. Context is lost. `semantic-recall` fixes this — **in two lines of code**.

```typescript
import { Memory } from 'semantic-recall'

const memory = new Memory({ userId: 'user_123' })

memory.remember("User is vegetarian and allergic to nuts")

const facts = await memory.recall("What should I recommend for dinner?")
// → ["User is vegetarian and allergic to nuts"]
```

No vector database. No API keys. No Docker containers. Just `npm install` and go.

---

## Why semantic-recall?

Most memory solutions require you to set up infrastructure, manage API keys, or lock into a paid platform. `semantic-recall` is different:

| | semantic-recall | [Mem0](https://github.com/mem0ai/mem0) | [Zep](https://github.com/getzep/zep) | [LangChain Memory](https://js.langchain.com/docs/modules/memory/) |
|---|:---:|:---:|:---:|:---:|
| **npm install & go** | ✅ | ❌ Requires API key or self-host setup | ❌ Requires server (Docker) | ✅ |
| **Works offline** | ✅ Local embeddings | ❌ Cloud API calls | ❌ Server required | ❌ No built-in embeddings |
| **Persistent across sessions** | ✅ SQLite on disk | ✅ Cloud-managed | ✅ Server-managed | ❌ In-memory by default |
| **Semantic search** | ✅ Cosine similarity | ✅ | ✅ Knowledge graph | ❌ Keyword/buffer only |
| **Auto deduplication** | ✅ Configurable threshold | ✅ | ❌ | ❌ |
| **Crash recovery** | ✅ Persistent queue | ❌ | ❌ | ❌ |
| **Worker thread isolation** | ✅ CPU never blocks | ❌ | N/A (separate server) | ❌ |
| **TTL / auto-expiry** | ✅ `"7d"`, `"1h"` | ❌ | ❌ | ❌ |
| **Multi-tenant** | ✅ userId + namespace | ✅ user/session/agent | ✅ Sessions | ❌ |
| **Bundle size** | ~67 KB | Cloud SDK | Cloud SDK | Large framework |
| **Free & open-source** | ✅ MIT, forever | Freemium (paid tiers) | Freemium (credit-based) | ✅ MIT |
| **Self-contained** | ✅ Single package | ❌ Platform dependency | ❌ Server + Redis + Postgres | ❌ Framework dependency |

> **TL;DR** — semantic-recall is the only solution that gives you persistent, semantic, crash-safe memory with zero infrastructure and zero API keys out of the box.

---

## Installation

```bash
npm install semantic-recall
```

> **First-run note:** The initial call downloads a ~25 MB embedding model to a local cache. After that, everything runs offline with zero network calls.

---

## Works Great With

- [OpenAI Node SDK](https://github.com/openai/openai-node) — inject recalled facts directly into your `messages[]` array
- [Vercel AI SDK](https://sdk.vercel.ai) — wrap `recall()` as a tool call for streaming chat apps
- [LangChain JS](https://js.langchain.com) — use as a persistent, semantic drop-in memory module
- [Turso](https://turso.tech) — serverless edge storage adapter built-in
- [Supabase](https://supabase.com) — Postgres storage adapter built-in
- [Transformers.js](https://huggingface.co/docs/transformers.js) — powers the local offline embeddings under the hood

---

## Quick Start

### The Basics — `remember()` and `recall()`

```typescript
import { Memory } from 'semantic-recall'

const memory = new Memory({ userId: 'user_123' })

// Store memories (fire-and-forget — returns instantly, never throws)
memory.remember("User prefers dark mode")
memory.remember("User is a senior TypeScript developer")
memory.remember("User lives in San Francisco")

// Retrieve relevant context for your LLM prompt
const context = await memory.recall("What IDE theme should I suggest?")
// → ["User prefers dark mode"]

// Inject into your system prompt
const systemPrompt = `You are a helpful assistant.
Known facts about the user:
${context.map(f => `- ${f}`).join('\n')}`
```

### Synchronous Confirmation

```typescript
const result = await memory.rememberAndWait("User is vegetarian")
console.log(result) // → { saved: true, duplicate: false }

const result2 = await memory.rememberAndWait("User is vegetarian")
console.log(result2) // → { saved: false, duplicate: true }
```

### Namespaces — Organize by Topic

```typescript
const memory = new Memory({ userId: 'user_123', namespace: 'health' })

memory.remember("User is allergic to peanuts")

// Only searches the 'health' namespace
const health = await memory.recall("allergies")

// Cross-namespace query
const work = await memory.recall("allergies", { namespace: 'work' }) // → []
```

### TTL — Auto-Expiring Memories

```typescript
// Memory expires after 7 days
memory.remember("User is in Paris for a conference", { ttl: "7d" })

// Supported formats: '500ms', '60s', '30m', '12h', '7d'
memory.remember("Session preference: compact view", { ttl: "1h" })
```

### LLM Auto-Extraction

Automatically extract memorable facts from conversations:

```typescript
const memory = new Memory({
  userId: 'user_123',
  llmProvider: 'openai',
  llmApiKey: process.env.OPENAI_API_KEY,
})

await memory.extractAndRemember([
  { role: 'user', content: "I just moved to Tokyo from London" },
  { role: 'assistant', content: "Welcome to Tokyo! How exciting..." },
  { role: 'user', content: "Yeah, I'm starting a new job as a ML engineer at Google" },
])
// Automatically extracts and stores:
// → "User lives in Tokyo"
// → "User previously lived in London"
// → "User works as a ML engineer at Google"
```

Supported providers: `'openai'` · `'gemini'` · `'claude'` · or any custom `LLMFunction`.

---

## How It Works

```
remember("user is vegetarian")
         │
         ▼
  ┌─────────────┐     ┌──────────────┐     ┌─────────────────┐
  │   Enqueue    │────▶│  Embed Text  │────▶│  Dedup Check    │
  │ (persistent  │     │ (worker      │     │ (cosine sim     │
  │  queue)      │     │  thread)     │     │  ≥ 0.92?)       │
  └─────────────┘     └──────────────┘     └────────┬────────┘
                                                     │
                                            ┌────────┴────────┐
                                            │                 │
                                       Unique            Duplicate
                                            │                 │
                                            ▼                 ▼
                                     ┌────────────┐    ┌────────────┐
                                     │   INSERT    │    │   Skip     │
                                     │ + emit      │    │ (mark done)│
                                     │ memory:saved│    │            │
                                     └────────────┘    └────────────┘
```

### Reliability — Built Like Infrastructure

Every call to `remember()` is **crash-safe**. Memories are first written to a persistent `pending_memories` queue, then processed asynchronously. If your process crashes mid-pipeline:

```
PENDING ──▶ PROCESSING ──▶ DONE
                │
                ▼
             FAILED ──(exponential backoff)──▶ PENDING
                │
                ▼ (after max attempts)
              DEAD ──(manual retry)──▶ PENDING
```

- **Stale recovery**: On startup, stuck `PROCESSING` jobs are automatically reset to `PENDING`
- **Exponential backoff**: Failed jobs retry with 2^n second delays (2s → 4s → 8s)
- **Dead letter queue**: After max attempts, jobs move to `DEAD` for manual inspection
- **Never throws**: `remember()` swallows all errors — your app never crashes because of memory storage

---

## Observability

Real-time events for monitoring and debugging:

```typescript
memory.on('memory:saved', ({ content, jobId }) => {
  console.log(`✓ Saved: "${content}" (id: ${jobId})`)
})

memory.on('memory:duplicate', ({ content }) => {
  console.log(`⊘ Duplicate skipped: "${content}"`)
})

memory.on('memory:retry', ({ content, error, attempts }) => {
  console.warn(`↻ Retry #${attempts}: "${content}" — ${error}`)
})

memory.on('memory:dead', ({ content, error }) => {
  console.error(`☠ Dead: "${content}" — ${error}`)
})
```

---

## Storage Adapters

### SQLite (Default) — Zero Config

Works everywhere with a filesystem. WAL mode enabled for concurrent reads.

```typescript
const memory = new Memory({
  userId: 'user_123',
  dbPath: './my-memories.db', // default: './semantic-recall.db'
})
```

### Turso — Serverless Edge

For serverless and edge deployments with [Turso](https://turso.tech):

```bash
npm install @libsql/client
```

```typescript
import { Memory } from 'semantic-recall'
import { createTursoAdapter } from 'semantic-recall/adapters/storage/turso'

const memory = new Memory({
  userId: 'user_123',
  storage: createTursoAdapter({
    url: 'libsql://your-db.turso.io',
    authToken: 'your-token',
  }),
})
```

### Supabase — Postgres Scale

For production Postgres deployments with [Supabase](https://supabase.com):

```bash
npm install @supabase/supabase-js
```

```typescript
import { Memory } from 'semantic-recall'
import { createSupabaseAdapter } from 'semantic-recall/adapters/storage/supabase'

const memory = new Memory({
  userId: 'user_123',
  storage: createSupabaseAdapter({
    url: 'https://your-project.supabase.co',
    anonKey: 'your-anon-key',
    dimensions: 384,
  }),
})
```

### Custom Adapter

Implement the `StorageAdapter` interface for any backend:

```typescript
import { Memory, type StorageAdapter } from 'semantic-recall'

const myAdapter: StorageAdapter = {
  async init() { /* create tables */ },
  async insertMemory(params) { /* insert */ },
  async searchMemories(params) { /* return all rows */ },
  async deleteMemory(id) { /* delete by id */ },
  async deleteAllMemories(userId, namespace) { /* bulk delete */ },
  async listMemories(userId, namespace, limit) { /* list */ },
  async pruneExpired(userId) { /* remove expired */ },
  async enqueue(job) { /* queue job, return id */ },
  async markProcessing(jobId) { /* update status */ },
  async markDone(jobId) { /* update status */ },
  async markFailed(jobId, error) { /* update status + backoff */ },
  async getRetryable() { /* return pending/failed jobs */ },
  async getDeadJobs(userId) { /* return dead jobs */ },
  async resetStaleProcessing() { /* crash recovery */ },
  async cleanupDoneJobs(olderThanMs) { /* prune */ },
  async retryDeadJob(jobId) { /* reset dead → pending */ },
  close() { /* cleanup */ },
}

const memory = new Memory({ userId: 'user_123', storage: myAdapter })
```

---

## Embedder Adapters

### Local (Default) — No API Keys

Uses [Transformers.js](https://huggingface.co/docs/transformers.js) in an isolated worker thread. The main thread is never blocked.

```typescript
const memory = new Memory({
  userId: 'user_123',
  embedder: 'local',
  embeddingModel: 'Xenova/all-MiniLM-L6-v2', // 384 dims, ~25 MB
})
```

### OpenAI

```typescript
const memory = new Memory({
  userId: 'user_123',
  embedder: 'openai',
  openaiApiKey: process.env.OPENAI_API_KEY,
  embeddingModel: 'text-embedding-3-small',
})
```

### Custom Embedder

```typescript
const memory = new Memory({
  userId: 'user_123',
  embedder: async (text: string): Promise => {
    const res = await fetch('https://my-api.com/embed', {
      method: 'POST',
      body: JSON.stringify({ text }),
    })
    return res.json()
  },
})
```

---

## Full Configuration

```typescript
const memory = new Memory({
  // ─── Required ──────────────────────────────────
  userId: 'user_123',

  // ─── Storage ───────────────────────────────────
  storage: 'sqlite',            // 'sqlite' | StorageAdapter
  dbPath: './semantic-recall.db',

  // ─── Embedder ──────────────────────────────────
  embedder: 'local',            // 'local' | 'openai' | EmbedderFunction
  embeddingModel: 'Xenova/all-MiniLM-L6-v2',
  openaiApiKey: '...',          // Required if embedder: 'openai'

  // ─── Behavior ──────────────────────────────────
  namespace: 'default',
  dedupThreshold: 0.92,         // Cosine sim threshold for dedup (0–1)
  recallThreshold: 0.70,        // Min similarity to return (0–1)
  topK: 5,                      // Max results per recall()

  // ─── Reliability ───────────────────────────────
  maxAttempts: 3,                // Retries before marking dead
  retryIntervalMs: 30_000,      // Retry scheduler interval

  // ─── LLM Auto-Extraction ──────────────────────
  llmProvider: 'openai',        // 'openai' | 'gemini' | 'claude' | LLMFunction
  llmApiKey: '...',
  llmModel: 'gpt-4o-mini',
})
```

---

## API Reference

| Method | Returns | Description |
|---|---|---|
| `memory.remember(text, opts?)` | `void` | Store a memory. Fire-and-forget, never throws. |
| `memory.rememberAndWait(text, opts?)` | `Promise` | Store and wait. Returns `{ saved, duplicate }`. |
| `memory.recall(query, opts?)` | `Promise` | Semantic search. Returns content strings. |
| `memory.recallDetailed(query, opts?)` | `Promise` | Like recall but with similarity scores + metadata. |
| `memory.extractAndRemember(messages, opts?)` | `Promise` | LLM-powered fact extraction from conversations. |
| `memory.forget(memoryId)` | `Promise` | Delete a specific memory. |
| `memory.forgetAll(opts?)` | `Promise` | Delete all memories for user+namespace. |
| `memory.list(opts?)` | `Promise` | List all stored memories (no search). |
| `memory.getDeadJobs()` | `Promise` | Inspect failed jobs. |
| `memory.retryDead(jobId)` | `Promise` | Retry a dead job. |
| `memory.cleanup(opts?)` | `Promise` | Prune old done jobs from queue. |
| `memory.destroy()` | `void` | Stop scheduler, close DB. |

### Events

| Event | Payload | When |
|---|---|---|
| `memory:saved` | `{ jobId, content, replayed?, retried? }` | Memory stored successfully |
| `memory:retry` | `{ jobId, content, error, attempts }` | Job failed, will retry |
| `memory:dead` | `{ jobId, content, error, attempts }` | Job exhausted all retries |

### Types

All types are exported for TypeScript consumers:

```typescript
import type {
  MemoryOptions,
  RememberOptions,
  RecallOptions,
  MemoryResult,
  RememberResult,
  MemoryJob,
  StorageAdapter,
  EmbedderFunction,
  ConversationMessage,
  LLMFunction,
  MemorySavedEvent,
  MemoryRetryEvent,
  MemoryDeadEvent,
} from 'semantic-recall'
```

---

## Real-World Patterns

### Inject Context Into Any LLM

```typescript
import OpenAI from 'openai'
import { Memory } from 'semantic-recall'

const memory = new Memory({ userId: 'user_123' })
const openai = new OpenAI()

async function chat(userMessage: string) {
  // Recall relevant memories
  const context = await memory.recall(userMessage)

  const response = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [
      {
        role: 'system',
        content: `You are a helpful assistant.
Known facts about the user:
${context.map(f => `- ${f}`).join('\n')}`,
      },
      { role: 'user', content: userMessage },
    ],
  })

  const reply = response.choices[0].message.content!

  // Auto-extract facts from this exchange
  await memory.extractAndRemember([
    { role: 'user', content: userMessage },
    { role: 'assistant', content: reply },
  ])

  return reply
}
```

### Graceful Shutdown

```typescript
process.on('SIGTERM', () => {
  memory.destroy() // Stops retry scheduler, closes DB
  process.exit(0)
})
```

### Dead Job Monitoring

```typescript
// In a health check endpoint
app.get('/health/memory', async (req, res) => {
  const dead = await memory.getDeadJobs()
  res.json({
    status: dead.length === 0 ? 'healthy' : 'degraded',
    deadJobs: dead.length,
  })
})
```

---

## Comparison Deep Dive

### vs Mem0

[Mem0](https://github.com/mem0ai/mem0) is a managed memory platform (cloud-hosted or self-hosted). It's a great product if you want a managed service — but it requires API keys for the cloud version and Docker + Redis for self-hosting. `semantic-recall` runs entirely locally with `npm install` and zero infrastructure.

### vs Zep

[Zep](https://github.com/getz

…

## Source & license

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

- **Author:** [Skt329](https://github.com/Skt329)
- **Source:** [Skt329/semantic-recall](https://github.com/Skt329/semantic-recall)
- **License:** MIT
- **Homepage:** https://www.npmjs.com/package/semantic-recall

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:** no
- **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-skt329-semantic-recall
- Seller: https://agentstack.voostack.com/s/skt329
- 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%.
