Skip to main content
Glama
README.md
<div align="center">

# ๐Ÿง  CORTEX Memory MCP

**A production-grade, long-term semantic memory server for AI agents.**  
Built on [Model Context Protocol](https://modelcontextprotocol.io), LangGraph.js, Qdrant, and local ONNX embeddings.

[![MCP Compatible](https://img.shields.io/badge/MCP-Compatible-6C47FF?style=flat-square&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0xMiAyQzYuNDggMiAyIDYuNDggMiAxMnM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTBTMTcuNTIgMiAxMiAyem0tMiAxNWwtNS01IDEuNDEtMS40MUwxMCAxNC4xN2w3LjU5LTcuNTlMMTkgOGwtOSA5eiIvPjwvc3ZnPg==)](https://modelcontextprotocol.io)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.3-3178C6?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
[![Qdrant](https://img.shields.io/badge/Qdrant-Vector%20DB-DC244C?style=flat-square)](https://qdrant.tech/)
[![LangGraph](https://img.shields.io/badge/LangGraph.js-0.0.25-1C3C3C?style=flat-square)](https://github.com/langchain-ai/langgraphjs)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE)

</div>

---

## โœจ What is CORTEX?

CORTEX is a **persistent semantic memory layer** for LLM-based agents. Instead of losing context between conversations, CORTEX lets your AI assistant remember decisions, facts, patterns, and past sessions โ€” and recall them intelligently using **hybrid search** (dense + sparse BM25).

Think of it as a **knowledge base with a brain**: it stores decisions, facts and patterns permanently, surfaces the most relevant ones through intelligent ranking, consolidates duplicates, and uses a Knowledge Graph to understand how entities relate to each other.

> **Design principle**: In software development, memories don't expire with time. A decision made 6 months ago is still valid today. CORTEX never deletes memories due to age โ€” it only surfaces them by relevance. The only way a memory is invalidated is when it is explicitly contradicted by a newer one (via `consolidate`).

### Why CORTEX over naive RAG?

| Feature | Naive RAG | CORTEX |
|---|---|---|
| Search strategy | Dense-only | **Hybrid BM25 + dense + cross-encoder rerank** |
| Memory decay | None | **Bayesian + FSRS (spaced repetition)** |
| Deduplication | None | **LLM-powered consolidation** |
| Session continuity | None | **Episodic memory with event timeline** |
| Entity relationships | None | **Knowledge Graph (Kuzu Cypher)** |
| Operator profile | None | **Persistent preferences + patterns** |
| LLM dependency for embeddings | Required | **100% local ONNX (no GPU needed)** |

---

## ๐Ÿ—๏ธ Architecture

```mermaid
graph TB
    subgraph MCP["MCP Interface (stdio)"]
        TOOLS["22 Tools exposed\nvia MCP Protocol"]
    end

    subgraph CORTEX["CORTEX Core"]
        direction TB
        OBS["observe\nLangGraph workflow"]
        RECALL["recall\nHybrid Search"]
        CONS["consolidate\nLangGraph workflow"]
        CTX["get_context_for\nRAG pipeline"]
    end

    subgraph EMBED["Embedding Layer (local, no GPU)"]
        DENSE["all-MiniLM-L6-v2\n384d ยท ONNX"]
        SPARSE["SPLADE_PP_en_v1\nSparse BM25-like ยท ONNX"]
    end

    subgraph STORE["Storage"]
        QDRANT["Qdrant\nVector DB\nDense + Sparse indexes"]
        KUZU["KuzuDB\nKnowledge Graph\nCypher queries"]
    end

    subgraph LLM["LLM (Ollama - optional)"]
        QWEN["qwen3\nScoring ยท Tagging\nConsolidation ยท Rerank"]
    end

    MCP --> CORTEX
    CORTEX --> EMBED
    CORTEX --> LLM
    EMBED --> STORE
    CORTEX --> STORE

    style MCP fill:#6C47FF,color:#fff
    style CORTEX fill:#1C3C3C,color:#fff
    style EMBED fill:#0F4C81,color:#fff
    style STORE fill:#DC244C,color:#fff
    style LLM fill:#2D2D2D,color:#fff
```

---

## ๐Ÿ“š Memory Layers

CORTEX organizes memory across **4 complementary layers**:

```mermaid
graph LR
    subgraph L1["โšก Layer 1 โ€” Temp Buffer"]
        QB["quick_observe\nbatch_observe\nInstant, no LLM"]
    end

    subgraph L2["๐Ÿง  Layer 2 โ€” Semantic Memory"]
        OB["observe\nFull pipeline:\nEmbed + Score + Tag + Link"]
    end

    subgraph L3["๐Ÿ“ผ Layer 3 โ€” Episodic Memory"]
        EP["start_session\nlog_event\nrecall_sessions\nTimeline of work sessions"]
    end

    subgraph L4["๐Ÿ•ธ๏ธ Layer 4 โ€” Knowledge Graph"]
        KG["Entities + Relations\ngraph_neighbors\ngraph_timeline\ngraph_query (Cypher)"]
    end

    QB --"auto-indexed\n(scheduler, ~60s)"--> OB
    OB --> L3
    OB --> L4

    style L1 fill:#F59E0B,color:#000
    style L2 fill:#6C47FF,color:#fff
    style L3 fill:#0F4C81,color:#fff
    style L4 fill:#065F46,color:#fff
```

---

## ๐Ÿ” Recall Pipeline

When you call `recall`, CORTEX runs a multi-stage pipeline to surface the most relevant memories:

```mermaid
flowchart TD
    Q["User Query"] --> E1["Dense Embedding\nall-MiniLM-L6-v2 ONNX"]
    Q --> E2["Sparse Embedding\nSPLADE_PP_en ONNX"]

    E1 --> PRE1["Qdrant Prefetch\nDense top-N\n(cosine similarity)"]
    E2 --> PRE2["Qdrant Prefetch\nSparse top-N\n(BM25 IDF)"]

    PRE1 --> RRF["Reciprocal Rank Fusion\nfusion: rrf"]
    PRE2 --> RRF

    RRF --> DECAY["Temporal Decay Scoring\nBayesian ร— FSRS\nper engrama type"]
    DECAY --> RERANK["Cross-Encoder Rerank\nqwen3 via Ollama\nbatch, single call"]
    RERANK --> FINAL["Final Ranked Results\n40% semantic ยท 40% rerank\n20% decay"]
    FINAL --> UPDATE["Update Access Stats\naccessCount ++ ยท lastAccessed"]
    UPDATE --> RESULT["๐Ÿ“š Results returned\nto agent"]

    style Q fill:#6C47FF,color:#fff
    style RRF fill:#DC244C,color:#fff
    style FINAL fill:#065F46,color:#fff
```

---

## ๐ŸŒŸ Features

### Core Memory (`observe` / `recall`)
- **Full LangGraph workflow** on `observe`: score โ†’ tag โ†’ embed โ†’ link โ†’ upsert
- **Hybrid search**: dense (cosine) + sparse (BM25/IDF) fused via Reciprocal Rank Fusion
- **Cross-encoder reranking** with a local LLM (qwen3) in a single batch call
- **Cross-project search**: queries the project collection + global in one pass

### Retrieval Priority (not expiration)

> **CORTEX does not delete memories.** Decay is a **retrieval priority signal**, not a forgetting mechanism. A memory with a low decay score still exists โ€” it simply ranks lower if a more recent and frequently accessed memory is equally relevant. If the semantic match is strong enough, any memory surfaces regardless of age.

Two priority engines, chosen automatically by engrama type:

- **Bayesian** (`DECISION`, `FACT`, `ERROR`, `PATTERN`): the priority of technical memories is anchored to importance and access frequency. A high-importance decision holds its position for months without being accessed. It is only *invalidated* โ€” never deleted โ€” when `consolidate` detects a contradiction and marks it `status: superseded`.

- **FSRS-inspired** (`PREFERENCE`, `CONTEXT`): conversational context and preferences get a mild recency boost. Frequently revisited preferences gain stability. Lower-traffic ones rank below newer signals โ€” but are never removed.

### Engrama Types
| Type | Priority Engine | Semantic Weight | Use Case |
|---|---|---|---|
| `DECISION` | Bayesian | 55% | Architecture choices, design decisions |
| `FACT` | Bayesian | 65% | Technical facts, API docs, config |
| `ERROR` | Bayesian | 55% | Bugs found, anti-patterns |
| `PATTERN` | Bayesian | 50% | Recurring behaviors detected |
| `PREFERENCE` | FSRS-inspired | 75% | Operator style preferences |
| `CONTEXT` | FSRS-inspired | 75% | Conversational context |

### Episodic Memory (`start_session` / `log_event` / `recall_sessions`)
- Tracks **work sessions** with structured event timelines
- Event types: `DECISION`, `ERROR`, `SOLUTION`, `INSIGHT`, `CONTEXT_CHANGE`
- Sessions are **searchable by semantic similarity** (fastembed ONNX, no LLM)
- Auto-closes previous open sessions when a new one starts
- Injected into `get_context_for` as recent session summaries

### Knowledge Graph (`graph_*`)
- Built on **KuzuDB** โ€” an embedded graph database with Cypher support
- Auto-populated when `observe` extracts entities from memories
- Query neighbors, timelines, or run arbitrary Cypher queries
- Included in `get_context_for` context block automatically

### Two-Speed Ingestion + Automatic Indexing
```
Fast path (no LLM, no embedding, instant):
  quick_observe โ†’ temp_memories buffer

Full pipeline (embedding + LLM scoring):
  observe โ†’ project collection (direct)

Automatic upgrade (no manual step required):
  Maintenance Scheduler โ†’ runs autoIndexTemp() every cycle:
    โ€ข T+60s after server start: rescues orphaned buffer memories
      from previous sessions (full drain, all projects)
    โ€ข Every 1 hour: indexes up to 10 pending memories per project
    โ€ข Manual call: index_temp is still available for on-demand indexing
  Each batch: 1 ONNX call + 1 LLM scoring call + 1 Qdrant upsert
```

### Operator Profile
- Automatically updated by `detect_patterns`
- Stores: coding preferences, active projects, recurring behavioral patterns
- Injected at session start via `get_context_for`

---

## ๐Ÿš€ Getting Started

### Prerequisites

| Dependency | Version | Notes |
|---|---|---|
| Node.js | โ‰ฅ 18 | ESM support required |
| Qdrant | โ‰ฅ 1.9 | With sparse vector support |
| **LLM** *(choose one)* | โ€” | OpenRouter API key **or** local Ollama **or** `none` |
| TypeScript | 5.3 | Dev only |

### 1. Install Qdrant

```bash
docker run -p 6333:6333 -p 6334:6334 \
  -v $(pwd)/qdrant_storage:/qdrant/storage \
  qdrant/qdrant
```

### 2. Choose your LLM backend

CORTEX routes all LLM operations (scoring, tagging, reranking, consolidation) through a single router controlled by `CORTEX_LLM_BACKEND`. Three backends are supported:

#### Option A โ€” OpenRouter (cloud, recommended for CPU-only machines)

No local GPU required. Use any model available on [openrouter.ai](https://openrouter.ai) โ€” free `:free` models work out of the box.

```env
CORTEX_LLM_BACKEND=openrouter
OPENROUTER_API_KEY=sk-or-v1-...
OPENROUTER_MODEL=google/gemma-4-27b-it:free   # any OpenRouter model slug
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
```

#### Option B โ€” Ollama (local, GPU recommended)

Fully offline. `qwen3` or any model you have pulled locally.

```bash
# Install Ollama: https://ollama.com
ollama pull qwen3
```

```env
CORTEX_LLM_BACKEND=ollama
OLLAMA_URL=http://localhost:11434   # default if omitted
```

> โš ๏ธ On CPU-only machines Ollama can take 90โ€“130 s per LLM call. Use OpenRouter or `none` if latency matters.

#### Option C โ€” none (fastest, no LLM at all)

All LLM operations fall back to safe defaults: `importance: 5`, `type: FACT`, no tags. Embeddings and vector search still work normally.

```env
CORTEX_LLM_BACKEND=none
```

#### Auto-detection logic

If `CORTEX_LLM_BACKEND` is not set, CORTEX auto-detects:
1. If `OPENROUTER_API_KEY` is defined โ†’ uses **openrouter**
2. Otherwise โ†’ falls back to **ollama**

#### Reranker flag

```env
# true  = activates LLM cross-encoder reranking after fastembed (better precision)
# false = fastembed ONNX only (<1s, sufficient for collections < 200 engramas)
CORTEX_RERANKER_ENABLED=true
```

---

### 3. Install CORTEX

```bash
git clone https://github.com/your-org/cortex-memory-mcp.git
cd cortex-memory-mcp
npm install
```

### 4. Configure `.env`

Minimal setup:

```env
# โ”€โ”€ Qdrant โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
QDRANT_URL=http://localhost:6333
QDRANT_API_KEY=your_api_key_here          # omit if no auth
FASTEMBED_CACHE_DIR=./.fastembed_cache    # ONNX model cache

# โ”€โ”€ LLM Backend โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# "openrouter" | "ollama" | "none"
# Auto-detected: openrouter if OPENROUTER_API_KEY is set, otherwise ollama
CORTEX_LLM_BACKEND=openrouter

# โ”€โ”€ OpenRouter (if CORTEX_LLM_BACKEND=openrouter) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
OPENROUTER_API_KEY=sk-or-v1-...
OPENROUTER_MODEL=google/gemma-4-27b-it:free
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1

# โ”€โ”€ Ollama (if CORTEX_LLM_BACKEND=ollama) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# OLLAMA_URL=http://localhost:11434

# โ”€โ”€ Reranker โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
CORTEX_RERANKER_ENABLED=true
```

### 5. Build & Run

```bash
npm run build
npm start
```

On first startup, CORTEX will download the ONNX models (~130 MB total) and warm them up in the background. All subsequent calls are instant.

### 6. Add to your MCP client (Claude Desktop / Cursor / etc.)

```json
{
  "mcpServers": {
    "cortex-memory": {
      "command": "node",
      "args": ["/absolute/path/to/cortex-memory-mcp/dist/server.js"],
      "env": {
        "QDRANT_URL": "http://localhost:6333",
        "QDRANT_API_KEY": "your_key"
      }
    }
  }
}
```

---

## ๐Ÿ“– Tool Reference

### ๐ŸŸฃ Semantic Memory

#### `observe`
Save a fact, decision, or context to long-term memory. Runs the full LangGraph pipeline: score โ†’ tag โ†’ embed (dense + sparse) โ†’ link to related memories โ†’ persist.

```json
{
  "projectName": "my-project",
  "content": "Decided to use PostgreSQL over MongoDB for the user table due to relational integrity requirements"
}
```

**Response:**
```
โœ… Engrama guardado.
โ€ข ID: 550e8400-e29b-41d4-a716-446655440000
โ€ข Importancia: 8/10
โ€ข Tipo: DECISION
โ€ข Tags: postgresql, database, architecture
Vinculado con 2 memorias relacionadas.
```

---

#### `recall`
Retrieve semantically relevant memories using hybrid search (BM25 + dense + cross-encoder reranking). Results are weighted by relevance and temporal decay.

```json
{
  "projectName": "my-project",
  "query": "which database did we choose for users?",
  "limit": 5
}
```

**Response:**
```
๐Ÿ“š 3 memorias para "which database did we choose for users?" en my-project (hybrid BM25+dense, reranked):

1. [DECISION] [imp:8/10] โ˜…8.2 Decided to use PostgreSQL over MongoDB for the user table...
   Tags: postgresql, database, architecture [2 vรญnculos]

2. [FACT] [imp:6/10] โ˜…6.1 PostgreSQL connection pool configured with max 20 connections
   Tags: postgresql, config

3. [ERROR] [imp:7/10] โ˜…5.9 MongoDB ObjectId serialization caused issues with our REST API
   Tags: mongodb, api, bug
```

---

#### `get_context_for`
**The main RAG entry point.** Call this at the start of every session to inject relevant memory, recent episodes, and Knowledge Graph context into the system prompt.

```json
{
  "projectName": "my-project",
  "message": "Let's continue working on the authentication module",
  "maxItems": 6
}
```

**Response (inject into system prompt):**
```markdown
## ๐Ÿง  Memoria CORTEX โ€” Proyecto: my-project
*(6 engramas mรกs relevantes, reranked)*

โ€ข [DECISION] We use JWT with 15-min access tokens + 7-day refresh tokens (imp:9/10)
โ€ข [ERROR] Refresh token rotation failed when Redis went down โ€” added fallback to DB (imp:8/10)
โ€ข [FACT] Auth middleware is in src/middleware/auth.ts (imp:6/10)
...

## ๐Ÿ“ผ Sesiones recientes
โ€ข **12/06/2026** (47 min): Implementar refresh tokens JWT
  โ†’ Decidimos usar Redis para blacklist de tokens revocados
  โ†’ Soluciรณn: fallback a PostgreSQL si Redis no responde

> Usa este contexto como conocimiento previo. No lo repitas textualmente.
```

---

#### `consolidate`
Merge duplicate or contradictory memories using LLM-powered analysis. Superseded memories are marked and excluded from future searches.

```json
{ "projectName": "my-project" }
```

---

#### `detect_patterns`
Analyze recent memories to extract behavioral patterns, coding preferences, and recurring issues. Saves them as high-importance `PATTERN` engramas and updates the Operator Profile.

```json
{
  "projectName": "my-project",
  "limit": 50
}
```

**Response:**
```
๐Ÿ” 5 patrones detectados en "my-project" โ†’ guardados + Operator Profile actualizado:

1. Prefers TypeScript strict mode with explicit return types
2. Always adds error boundaries around external API calls
3. Uses environment variables for all configuration, never hardcodes
4. Writes tests before refactoring existing code
5. Recurrently encounters timezone issues with date handling
```

---

### ๐ŸŸก Fast Buffer (no LLM, no embedding)

#### `quick_observe`
Save a memory **instantly** โ€” no LLM, no embedding. Goes to a temporary buffer that is **automatically indexed by the maintenance scheduler** (within ~60s on startup, or the next hourly cycle). You never need to call `index_temp` manually unless you want immediate indexing.

```json
{
  "projectName": "my-project",
  "content": "Found that rate limiter needs to be per-IP not per-user"
}
```

---

#### `batch_observe`
Save multiple memories in one call. All go to the temporary buffer (no LLM, no embedding).

```json
{
  "projectName": "my-project",
  "memories": [
    "Redis maxmemory set to 2gb with allkeys-lru policy",
    "Nginx configured as reverse proxy on port 80/443",
    "PM2 process manager used for Node.js clustering"
  ]
}
```

---

#### `list_pending`
Show what's waiting in the temporary buffer to be indexed.

```json
{ "projectName": "my-project" }
```

**Response:**
```
๐Ÿ“ฅ 3 memorias pendientes en temp_memories:

**my-project** (3 pendientes):
  1. [13/06/2026, 08:15] Redis maxmemory set to 2gb with allkeys-lru policy
     ID: abc-123...
  2. [13/06/2026, 08:15] Nginx configured as reverse proxy on port 80/443
     ID: abc-124...
```

---

#### `index_temp`
Process pending buffer memories with full embedding + LLM scoring. **Optional** โ€” the maintenance scheduler does this automatically every hour (and 60s after server start). Call manually when you need immediate indexing without waiting for the next scheduler cycle.

```json
{
  "projectName": "my-project",
  "batchSize": 10,
  "skipScoring": false
}
```

**Response:**
```
๐Ÿ”„ 3/3 memorias indexadas en "my-project" con scoring qwen3 (batch):

  โ€ข [FACT] imp:7/10 โ€” Redis maxmemory set to 2gb with allkeys-lru policy
  โ€ข [FACT] imp:6/10 โ€” Nginx configured as reverse proxy on port 80/443
  โ€ข [FACT] imp:5/10 โ€” PM2 process manager used for Node.js clustering

(โœ… todo indexado)
```

---

#### `recall_hybrid`
Search **both** the temp buffer (keyword) and indexed memory (semantic) in one call. Best for finding something you saved recently.

```json
{
  "projectName": "my-project",
  "query": "redis configuration",
  "limit": 5
}
```

---

### ๐Ÿ“ผ Episodic Memory

#### `start_session`
Open a new work session. Records the topic/goal and auto-closes any previously open session.

```json
{
  "projectName": "my-project",
  "context": "Implement OAuth2 Google login flow"
}
```

**Response:**
```
โ–ถ๏ธ Sesiรณn episรณdica iniciada.
โน๏ธ Sesiรณn anterior cerrada (47 min, 8 eventos)
โ€ข ID: 7f3a1c9d-...
โ€ข Proyecto: my-project
โ€ข Contexto: Implement OAuth2 Google login flow
```

---

#### `log_event`
Record a structured event in the active session.

```json
{
  "projectName": "my-project",
  "sessionId": "7f3a1c9d-...",
  "eventType": "DECISION",
  "description": "Using Passport.js instead of custom OAuth handler โ€” better maintained"
}
```

Event types:
- ๐Ÿ”ต `DECISION` โ€” Architecture or design choice made
- ๐Ÿ”ด `ERROR` โ€” Bug or problem encountered
- ๐ŸŸข `SOLUTION` โ€” How a problem was resolved
- ๐Ÿ’ก `INSIGHT` โ€” Important realization or learning
- ๐Ÿ”„ `CONTEXT_CHANGE` โ€” Shift in focus or requirements

---

#### `recall_sessions`
Find past sessions relevant to a query using semantic search.

```json
{
  "projectName": "my-project",
  "query": "authentication JWT decisions",
  "limit": 3
}
```

**Response:**
```
๐Ÿ“ผ 2 sesiones relevantes para "authentication JWT decisions" en my-project:

1. ๐Ÿ“ผ **11/06/2026** (47 min) โ€” Implementar refresh tokens JWT
    ๐Ÿ”ต Decidimos usar Redis para blacklist de tokens revocados
    ๐ŸŸข Fallback a PostgreSQL si Redis no responde

2. ๐Ÿ“ผ **09/06/2026** (31 min) โ€” Setup inicial del mรณdulo de auth
    ๐Ÿ”ต JWT elegido sobre session-based auth por arquitectura stateless
    ๐Ÿ”ด Token expiry de 1h causรณ problemas en mobile โ€” reducido a 15min
```

---

### ๐Ÿ•ธ๏ธ Knowledge Graph

#### `graph_neighbors`
Show entities directly connected to a node in the Knowledge Graph.

```json
{
  "projectName": "my-project",
  "entity": "PostgreSQL",
  "limit": 10
}
```

**Response:**
```
๐Ÿ•ธ๏ธ Vecinos de "PostgreSQL" en my-project:

โ€ข [USES] โ†’ user_table โ€” primary storage for user accounts
โ€ข [REPLACES] โ†’ MongoDB โ€” chosen for relational integrity
โ€ข [CONNECTS_TO] โ†’ connection_pool โ€” max 20 connections
โ€ข [REFERENCED_BY] โ† auth_middleware
```

---

#### `graph_timeline`
View the temporal evolution of an entity โ€” what relationships were recorded and when.

```json
{
  "projectName": "my-project",
  "entity": "Redis",
  "limit": 20
}
```

---

#### `graph_query`
Run a raw Cypher query against the Knowledge Graph (KuzuDB). For advanced users.

```json
{
  "cypher": "MATCH (n:Entity)-[r]->(m:Entity) WHERE n.project = 'my-project' RETURN n.name, type(r), m.name LIMIT 20"
}
```

---

### ๐Ÿ”ง Management

#### `cortex_status`
Get a full system health overview.

```
## ๐Ÿง  CORTEX Status
*v3.0.0 โ€” LangGraph.js + Qdrant + fastembed*

**Colecciones activas**: 4
โ€ข my-project: 147 engramas
โ€ข social: 896 engramas
โ€ข global: 12 engramas
โ€ข pets: 4 engramas

**Buffer temporal (temp_memories)**: โœ… vacรญo
**Operator Profile**: โœ… (actualizado 13/06/2026)
```

---

#### `get_operator_profile`
Retrieve the persistent operator profile (coding preferences, active projects, detected patterns).

---

#### `get_all_memories`
List all memories for a project, ranked by decay score.

```json
{ "projectName": "my-project", "limit": 20 }
```

---

#### `update_memory`
Update an engrama's content. Recalculates embedding and LLM score.

```json
{
  "id": "550e8400-...",
  "content": "Decided to use PostgreSQL 16 with pgvector extension for embeddings storage"
}
```

---

#### `delete_memory`
Delete a single engrama by ID.

```json
{ "id": "550e8400-..." }
```

---

#### `delete_all_memories`
Delete all memories for a project. **Irreversible** โ€” requires explicit confirmation.

```json
{
  "projectName": "my-project",
  "confirm": true
}
```

---

#### `export_memories`
Export all memories as JSON for backup or migration.

```json
{ "projectName": "my-project" }
```

---

## ๐Ÿ’ก Recommended Session Workflow

Here is the recommended pattern for using CORTEX within an AI agent session:

```mermaid
sequenceDiagram
    participant U as User
    participant A as AI Agent
    participant C as CORTEX

    U->>A: New conversation starts
    A->>C: get_context_for(project, first_message)
    C-->>A: Relevant memories + recent sessions + KG context
    A->>C: start_session(project, "Today's goal")
    Note over A: Agent now has full context

    loop During conversation
        A->>C: quick_observe(fact) or observe(decision)
        A->>C: log_event(sessionId, DECISION/ERROR/SOLUTION)
    end

    U->>A: Conversation ends
    A->>C: detect_patterns(project) [optional, periodically]
    Note over C: Session auto-closes on next start_session
```

---

## ๐Ÿงฎ Retrieval Priority Score Reference

> **Important distinction**: CORTEX's "decay" is not biological forgetting. It is a **dynamic retrieval priority score** (0โ€“1). A score of 0.1 does not mean the memory is gone โ€” it means it ranks lower than a score of 0.9 in the final result list. All memories are permanent unless explicitly superseded or manually deleted.

### Bayesian Priority (technical memories)

Used for `DECISION`, `FACT`, `ERROR`, `PATTERN`.

```
ฮฑ = accessCount + importance/2   โ†’ grows with use and relevance
ฮฒ = max(0.1, daysSince ร— (10 / importance))  โ†’ time introduces uncertainty,
                                               but importance resists it
score = ฮฑ / (ฮฑ + ฮฒ)              โ†’ E[ฮธ] of the Beta distribution
```

**Example:** A `DECISION` with `importance: 9` stays above 0.85 for ~60 days
without any access. A `FACT` with `importance: 3` drops faster โ€” but
still exists and will surface if semantically matched by a query.

### FSRS-inspired Priority (conversational memories)

Used for `PREFERENCE`, `CONTEXT`.

```
stability      = log(1 + accessCount) ร— (importance / 5)
priority_score = exp(โˆ’daysSince / max(stability, 1))
```

Frequently revisited preferences gain stability and hold their rank.
Less-accessed context fades in ranking โ€” but remains retrievable.

### Combined Score (final ranking)

```
With LLM reranker:   0.40 ร— semantic + 0.40 ร— rerank + 0.20 ร— priority
Without reranker:    semantic_weight ร— semantic + decay_weight ร— priority
                     (weights vary by engrama type โ€” see table above)
```

---

## ๐Ÿ› ๏ธ Migration: Adding Sparse Index to Existing Collections

If you have existing Qdrant collections without sparse vectors, use the included migration script:

```bash
# Dry run โ€” see what would be migrated
node migrate_sparse_index.mjs --dry-run

# Migrate all collections
node migrate_sparse_index.mjs

# Migrate a single collection
node migrate_sparse_index.mjs --only=cortex_hotetec

# Skip a collection
node migrate_sparse_index.mjs --skip=temp_memories
```

> โš ๏ธ The script recreates collections (required by Qdrant โ€” sparse vectors cannot be added to existing collections). It exports all points, deletes the collection, recreates it with `sparse_vectors.bm25` declared, then re-imports. Dense vectors are preserved; sparse vectors will be populated on the next upsert.

---

## ๐Ÿ“ฆ Tech Stack

| Component | Technology | Purpose |
|---|---|---|
| Protocol | MCP SDK 1.29 | Expose tools to AI agents |
| Workflow | LangGraph.js 0.0.25 | `observe` & `consolidate` pipelines |
| Vector DB | Qdrant 1.9+ | Dense + sparse storage & search |
| Graph DB | KuzuDB 0.11 | Knowledge Graph with Cypher |
| Dense Embedding | fastembed all-MiniLM-L6-v2 | 384d, ONNX, local, no GPU |
| Sparse Embedding | fastembed SPLADE_PP_en_v1 | BM25-like, ONNX, local, no GPU |
| LLM | OpenRouter / Ollama / none | Scoring, tagging, reranking, consolidation |
| Language | TypeScript 5.3 + ESM | |

---

## ๐Ÿ“‚ Project Structure

```
cortex-memory-mcp/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ server.ts              # MCP server โ€” all 22 tool handlers
โ”‚   โ”œโ”€โ”€ bootstrap.ts           # Server initialization
โ”‚   โ”œโ”€โ”€ services/
โ”‚   โ”‚   โ”œโ”€โ”€ qdrant.ts          # Vector DB client, search, sparse index
โ”‚   โ”‚   โ”œโ”€โ”€ fastembed.ts       # ONNX embedding (dense + sparse)
โ”‚   โ”‚   โ”œโ”€โ”€ ollama.ts          # LLM scoring, tagging, reranking
โ”‚   โ”‚   โ”œโ”€โ”€ decay.ts           # Bayesian + FSRS temporal decay
โ”‚   โ”‚   โ”œโ”€โ”€ episode.ts         # Episodic memory (sessions + events)
โ”‚   โ”‚   โ””โ”€โ”€ kuzu.ts            # Knowledge Graph (KuzuDB + Cypher)
โ”‚   โ”œโ”€โ”€ graph/
โ”‚   โ”‚   โ”œโ”€โ”€ observe/           # LangGraph observe workflow
โ”‚   โ”‚   โ””โ”€โ”€ consolidate/       # LangGraph consolidation workflow
โ”‚   โ””โ”€โ”€ types/
โ”‚       โ”œโ”€โ”€ engrama.ts         # Engrama type definitions
โ”‚       โ””โ”€โ”€ episode.ts         # Episode type definitions
โ”œโ”€โ”€ migrate_sparse_index.mjs   # Migration script for sparse indexes
โ”œโ”€โ”€ test_cortex.mjs            # Integration test suite
โ”œโ”€โ”€ eval/
โ”‚   โ””โ”€โ”€ harness.mjs            # Evaluation harness
โ””โ”€โ”€ .env                       # Configuration (not committed)
```

---

## ๐Ÿงช Running Tests

```bash
npm test
```

The test suite covers: `observe`, `recall`, `quick_observe`, `index_temp`, `recall_hybrid`, `get_context_for`, `consolidate`, `start_session`, `log_event`, `recall_sessions`, and `cortex_status`.

---

## ๐Ÿค Contributing

Contributions are welcome! Please:

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'feat: add amazing feature'`)
4. Push and open a Pull Request

---

## ๐Ÿ“„ License

MIT โ€” see [LICENSE](LICENSE) for details.

---

<div align="center">

Built with โค๏ธ for developers who want their AI to actually **remember**.

</div>