A Model Context Protocol (MCP) server that provides semantic memory storage and retrieval using PostgreSQL with pgvector for vector similarity search. This is an agentic memory system where an LLM orchestrates memory operations through natural language instructions.
The Memory MCP server enables AI assistants to store, search, and manage persistent memories with semantic understanding. Unlike traditional databases that require structured queries, this system accepts natural language instructions and uses an LLM agent to translate them into memory operations.
- Agentic Architecture: LLM-orchestrated memory operations using GPT-4/5 with internal tools
- Semantic Search: PostgreSQL + pgvector for fast similarity queries with hybrid search (vector + keyword)
- Dynamic Priority: Memories have priority scores that decay over time and boost with access
- Multi-Project Isolation: Each project has its own isolated PostgreSQL database
- Rich Metadata: Automatic extraction of topics, tags, and semantic memory types
- Memory Lifecycle: Automated consolidation, deduplication, and cleanup via refinement operations
- Multi-Index Organization: Organize memories into logical namespaces (personal, work, research, etc.)
Get the Memory MCP server running in 5 minutes:
# 1. Clone and install dependencies
git clone <repository-url>
cd memory-mcp
npm install
# 2. Set up PostgreSQL database (automated)
./scripts/setup-postgres.sh
# 3. Configure environment
cp .env.example .env
# Edit .env and set your OPENAI_API_KEY
# 4. Start the server
npm run devThe server will start and listen for MCP tool calls via STDIO. See Configuration for detailed setup and Usage for how to call the MCP tools.
The Memory MCP server uses a layered architecture:
┌─────────────────────────────────────────────────────────────────┐
│ MCP Layer (MemoryServer.ts) │
│ • MCP tools: memorize, recall, forget, refine_memories, │
│ create_index, list_indexes, scan_memories │
│ • STDIO transport for Claude Desktop integration │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ Controller Layer (MemoryController.ts) │
│ • Security boundaries (ProjectFileLoader, IndexResolver) │
│ • Index access validation │
│ • Routes tool calls to agent modes │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ Agent Layer (MemoryAgent.ts) │
│ • LLM orchestration (GPT-4/5) with mode-specific prompts │
│ • Tool Runtime: search_memories, get_memories, │
│ upsert_memories, delete_memories, read_file, │
│ analyze_text, list_relationships │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ Repository Layer (MemoryRepositoryPostgres.ts) │
│ • PostgreSQL + pgvector data access │
│ • Embedding generation, semantic search │
│ • Access tracking, relationship management │
│ • Connection pooling per project (PoolManager.ts) │
└─────────────────────────────────────────────────────────────────┘
- PromptManager: Composes base + mode-specific + host/project context into system messages
- IndexResolver: Validates index names and provides default index logic
- ProjectFileLoader: Securely loads files from project directory with size limits
- PriorityCalculator: Deterministic priority formula (recency × 0.4 + importance × 0.4 + usage × 0.2)
- PostgreSQL 14+ - Database server with vector extension support
- pgvector - PostgreSQL extension for vector similarity search
- Node.js 18+ - Runtime environment
- OpenAI API Key - For generating embeddings and LLM orchestration
Run the setup script to automatically create the database, enable pgvector, and run migrations:
./scripts/setup-postgres.shThe script will:
- Check if PostgreSQL is installed and running
- Create the
memory_defaultdatabase - Enable the pgvector extension
- Run schema migrations
- Verify the setup
If you prefer to set up manually, follow these steps:
macOS (Homebrew)
# Install PostgreSQL 14 or later
brew install postgresql@16
# Start PostgreSQL service
brew services start postgresql@16
# Install pgvector
brew install pgvectorLinux (Ubuntu/Debian)
# Install PostgreSQL
sudo apt update
sudo apt install postgresql postgresql-contrib
# Install build tools for pgvector
sudo apt install build-essential postgresql-server-dev-all
# Install pgvector from source
git clone https://github.com/pgvector/pgvector.git
cd pgvector
make
sudo make installDocker
# Use the official pgvector image
docker run -d \
--name postgres-memory \
-e POSTGRES_DB=memory_default \
-e POSTGRES_PASSWORD=postgres \
-p 5432:5432 \
ankane/pgvector:latest
# Enable pgvector extension
docker exec -it postgres-memory psql -U postgres -d memory_default -c "CREATE EXTENSION IF NOT EXISTS vector;"
# Run migrations
docker exec -i postgres-memory psql -U postgres -d memory_default < migrations/20250117000001_init_postgres_schema.sql# Create the memory_default database
createdb memory_default
# Or using psql
psql -U postgres -c "CREATE DATABASE memory_default;"# Connect to the database and enable the extension
psql -d memory_default -c "CREATE EXTENSION IF NOT EXISTS vector;"# Run the schema migration
psql -d memory_default -f migrations/20250117000001_init_postgres_schema.sql
# Optional: Load test data for quick testing
psql -d memory_default -f migrations/seeds/01_test_data.sqlRun these commands to verify your setup:
# Verify memory_default database exists
psql -U postgres -c "SELECT datname FROM pg_database WHERE datname = 'memory_default';"
# Check PostgreSQL version
psql -d memory_default -c "SELECT version();"
# Verify pgvector extension is enabled
psql -d memory_default -c "SELECT * FROM pg_extension WHERE extname = 'vector';"
# Check that tables were created
psql -d memory_default -c "\dt"Expected output should show:
- Database
memory_defaultexists - PostgreSQL version 14+
- pgvector extension in the extensions list
- Tables:
memories,memory_indexes,memory_relationships,memory_usage_log
Copy the example environment file and configure your settings:
# Copy the example file
cp .env.example .env
# Edit .env and set your configurationRequired Environment Variables:
# Postgres project configuration
MEMORY_POSTGRES_PROJECT_REGISTRY=./config/projects.json
MEMORY_ACTIVE_PROJECT=local
# OpenAI API key for embeddings and LLM orchestration
OPENAI_API_KEY=sk-your-api-key-here
# Embedding model (determines vector dimensions)
MEMORY_EMBEDDING_MODEL=text-embedding-3-smallOptional Configuration:
# Embedding dimensions (auto-detected from model, manual override available)
MEMORY_EMBEDDING_DIMENSIONS=1536
# Host/system context (inline text or file path)
MEMORY_MCP_SYSTEM_MESSAGE=./config/memory-host-context.txt
# Debug flags
MEMORY_DEBUG_MODE=true
MEMORY_DEBUG_OPERATIONS=true
MEMORY_DEBUG_VALIDATION=true
MEMORY_DEBUG_ACCESS_TRACKING=true
MEMORY_DEBUG_REPOSITORY=true
# Observability and logging
MEMORY_LOG_LEVEL=info # Log level: debug, info, warn, error (default: info)
MEMORY_LOG_FORMAT=pretty # Log format: json, pretty (default: pretty)
MEMORY_DB_SLOW_QUERY_MS=200 # Slow query threshold in milliseconds (default: 200)
# Refinement tuning
MEMORY_REFINE_DEFAULT_BUDGET=100
MEMORY_REFINE_ALLOW_DELETE=false
MEMORY_ACCESS_TRACKING_ENABLED=true
MEMORY_ACCESS_TRACKING_TOP_N=3
MEMORY_ACCESS_PRIORITY_BOOST=0.01
# Query enhancement
MEMORY_QUERY_EXPANSION_ENABLED=true
MEMORY_QUERY_EXPANSION_COUNT=2
# File ingestion limits
MEMORY_LARGE_FILE_THRESHOLD_BYTES=262144
MEMORY_CHUNK_CHAR_LENGTH=16000
MEMORY_CHUNK_CHAR_OVERLAP=2000
MEMORY_MAX_CHUNKS_PER_FILE=24
MEMORY_MAX_MEMORIES_PER_FILE=50The server uses config/projects.json to map project IDs to database URLs:
{
"local": {
"databaseUrl": "postgresql://postgres:postgres@localhost:5432/memory_default"
}
}The active project is selected via the MEMORY_ACTIVE_PROJECT environment variable. Each project has its own isolated PostgreSQL database.
Multi-Project Setup Example:
{
"local": {
"databaseUrl": "postgresql://postgres:postgres@localhost:5432/memory_default"
},
"production": {
"databaseUrl": "postgresql://user:pass@prod-host:5432/memory_prod"
},
"staging": {
"databaseUrl": "postgresql://user:pass@staging-host:5432/memory_staging"
}
}Supported models (configured via MEMORY_EMBEDDING_MODEL):
text-embedding-3-small- 1536 dimensions (default, recommended)text-embedding-3-large- 3072 dimensions (higher quality, higher cost)
vector(1536) for text-embedding-3-small. If you change the embedding model, you must update the embedding column type in the migration to match the new dimension.
The prompt system supports optional context injection:
Host Context (MEMORY_MCP_SYSTEM_MESSAGE):
- Tells the memory server what role it plays in the overall system
- Guides what kinds of information should be stored or avoided
- Can be inline text or a file path (e.g.,
./config/memory-host-context.txt)
Project Context (projectSystemMessagePath in tool calls):
- Per-request context for specific projects or use cases
- Passed as a parameter to individual tool calls
- Useful for biasing behavior on a per-operation basis
See prompts/README.md for details on the composable prompt system.
npm installnpm run devThis starts the MCP server in development mode with hot reload (uses tsx).
npm run build
npm startThe build command compiles TypeScript to dist/, and start runs the compiled server.
# Check linting
npm run lint
# Auto-fix linting issues
npm run lint:fix
# Format code with Prettier
npm run format
# Check if code is formatted
npm run format:checkAlways run npm run format or npm run lint:fix before committing to maintain code consistency.
The Memory MCP server exposes tools through the Model Context Protocol. These tools are typically called from Claude Desktop or other MCP-compatible clients.
MCP tools are called through MCP-compatible clients like Claude Desktop. When you interact with Claude, you can reference memories naturally in conversation, and Claude will use these tools automatically. The tools can also be called programmatically through the MCP protocol using JSON payloads as shown in the examples below.
Example conversational usage in Claude Desktop:
- "Remember that I prefer dark mode" → uses
memorizetool - "What are my notification preferences?" → uses
recalltool - "Forget my old email address" → uses
forgettool
Purpose: Capture durable memories from free-form text or files. The agent extracts atomic facts, enriches them with metadata (topic, tags, memoryType), and stores them in PostgreSQL + pgvector.
Parameters:
input(required): Natural language instruction describing what to memorizefiles(optional): Array of relative file paths to ingest alongside the instructionindex(optional): Index name (defaults toMEMORY_DEFAULT_INDEX)projectSystemMessagePath(optional): Relative path to project-specific system messagemetadata(optional): Additional metadata to apply to extracted memories
Example:
{
"input": "Remember that the user prefers dark mode and wants notifications disabled after 9 PM",
"metadata": {
"category": "user_preferences"
}
}With file ingestion:
{
"input": "Memorize the key design decisions from this architecture document",
"files": ["docs/architecture.md"],
"index": "project_knowledge"
}Behavior:
- Breaks down complex information into atomic, searchable memories
- Automatically extracts topics, tags, and classifies memory types (self, belief, pattern, episodic, semantic)
- For large files, uses chunking and GPT-4-mini for fast pre-processing via
analyze_texttool - Returns summary of memories created with IDs and metadata
Purpose: Search stored memories and optionally synthesize an answer. Supports metadata filters, returning raw memories, and priority-aware synthesis.
Parameters:
query(required): Natural language question or topic to search forindex(optional): Index name overridelimit(optional): Maximum number of memories to return (default: 10)filters(optional): Structured metadata filters (keys match stored metadata)filterExpression(optional): Advanced filter expression using filter DSLprojectSystemMessagePath(optional): Project-specific system message pathresponseMode(optional):"answer"(synthesized),"memories"(raw), or"both"(default:"answer")
Example - Synthesized answer:
{
"query": "What are the user's notification preferences?",
"responseMode": "answer"
}Example - Raw memories with filters:
{
"query": "design decisions",
"filters": {
"category": "architecture"
},
"responseMode": "memories",
"limit": 20
}Example - Advanced filter expression:
{
"query": "recent work tasks",
"filterExpression": "@metadata.tags contains \"work\" AND @metadata.priority > 0.7",
"responseMode": "both"
}Behavior:
- Uses semantic search (pgvector) + keyword search for hybrid retrieval
- Priority-aware synthesis privileges high-salience memories
- Automatic access tracking updates memory priority and access counts
- Returns synthesized answers and/or raw memory records with metadata
Purpose: Plan deletions with the LLM agent. Supports dry runs, metadata-scoped deletes, and explicit ID deletion.
Parameters:
input(required): Instruction describing what to forgetindex(optional): Index overridefilters(optional): Metadata filters for narrowing deletion candidatesprojectSystemMessagePath(optional): System message path for contextualizing deletionsdryRun(optional): Defaulttrue; whenfalsethe agent executes approved deletesexplicitMemoryIds(optional): Array of specific memory IDs to delete immediately
Example - Dry run (default):
{
"input": "Forget all memories about the old API design that was replaced in December",
"dryRun": true
}Example - Execute deletion with filters:
{
"input": "Delete all low-priority temporary notes",
"filters": {
"memoryType": "episodic",
"category": "temp"
},
"dryRun": false
}Example - Delete specific IDs:
{
"input": "Remove these obsolete memories",
"explicitMemoryIds": ["550e8400-e29b-41d4-a716-446655440000"],
"dryRun": false
}Behavior:
- Conservative deletion with dry-run protection (default)
- Agent searches for matching memories and explains what would be deleted
- Validates against safety rules (e.g., can't delete system memories)
- When
dryRun=false, executes approved deletions - Returns list of deleted memories with rationale
Purpose: Curate stored memories through consolidation, deduplication, reprioritization, and cleanup. The agent analyzes memories and generates structured refinement plans.
Parameters:
index(optional): Index overrideoperation(optional): Refinement mode -"consolidation","decay","cleanup", or"reflection"scope(optional): Controls which memories are consideredquery: Semantic query to find candidatesfilters: Metadata filtersseedIds: Array of specific memory IDs to start frommaxCandidates: Maximum memories to analyze
budget(optional): Maximum actions to execute (default fromMEMORY_REFINE_DEFAULT_BUDGET)dryRun(optional): Plan-only mode whentrue(default)projectSystemMessagePath(optional): Project-specific context
Example - Consolidation:
{
"operation": "consolidation",
"scope": {
"query": "user preferences",
"maxCandidates": 50
},
"dryRun": true
}Example - Decay (reprioritization):
{
"operation": "decay",
"budget": 100,
"dryRun": false
}Example - Cleanup with filters:
{
"operation": "cleanup",
"scope": {
"filters": {
"memoryType": "episodic"
}
},
"dryRun": true
}Operation Modes:
- Consolidation: Merge duplicates, create summaries, detect contradictions, link related memories
- Decay: Reprioritize memories using deterministic priority formula based on recency, usage, and importance
- Cleanup: Identify deletion candidates (low priority, superseded, obsolete) as dry-run recommendations
- Reflection: Generate high-level summaries and patterns from related memories
Action Types:
UPDATE: Reprioritize or add relationships between memoriesMERGE: Consolidate duplicate or redundant memoriesCREATE: Generate summary memories from multiple related memoriesDELETE: Remove obsolete or low-priority memories (recommendations only in dry-run)
Behavior:
- Agent uses GPT-4/5 for complex pattern analysis and planning
- Generates structured refinement actions with rationale
- Validates actions against safety rules (e.g., can't delete system memories)
- Returns refinement plan with actions and expected outcomes
- When
dryRun=false, executes approved actions
Purpose: Create or ensure a PostgreSQL-backed memory index exists for the active project.
Parameters:
name(required): New index namedescription(optional): Human description stored alongside the index record
Example:
{
"name": "work_notes",
"description": "Professional work-related notes and decisions"
}Behavior:
- Creates a new index if it doesn't exist
- If index already exists, returns existing index information
- Indexes are stored as rows in the
memory_indexestable - Each project can have multiple indexes for logical organization
Purpose: List all PostgreSQL memory indexes with document counts so agents can choose destinations.
Parameters: None
Example:
{}Returns:
{
"indexes": [
{
"name": "personal",
"documentCount": 142,
"pendingDocumentCount": 0,
"project": "local"
},
{
"name": "work_notes",
"documentCount": 87,
"pendingDocumentCount": 0,
"project": "local"
}
],
"totalMemories": 229,
"totalDiskBytes": 1048576
}Behavior:
- Returns all indexes for the active project
- Includes document counts for each index (pendingDocumentCount always 0 in PostgreSQL backend)
- Provides aggregate statistics (totalMemories, totalDiskBytes)
- Helps agents choose appropriate index for new memories
- Useful for understanding memory organization
Purpose: Run direct PostgreSQL searches without LLM orchestration. Returns raw results and diagnostics for debugging and inspection.
Parameters:
query(required): Search query textindex(optional): Index overridelimit(optional): Max results (default 10, max 1000)filters(optional): Structured metadata filtersfilterExpression(optional): Advanced filter expression stringsemanticWeight(optional): Semantic vs keyword weighting (0-1)reranking(optional): Enable reranking (default true)includeMetadata(optional): Include metadata payloads (default true)
Example:
{
"query": "user preferences",
"limit": 20,
"semanticWeight": 0.7,
"includeMetadata": true
}Behavior:
- Bypasses LLM agent and queries PostgreSQL directly
- Useful for debugging search quality and inspecting raw embeddings
- Returns raw search results with similarity scores
- Includes diagnostics about query execution
- Not typically used in normal operation (use
recallinstead for LLM-synthesized answers)
Error: ERROR: extension "vector" is not available
Solution:
# Verify pgvector is installed
pg_config --sharedir
# Check if vector.control exists in <sharedir>/extension/
# Reinstall if needed (macOS)
brew reinstall pgvector
# Reinstall if needed (Linux)
cd pgvector && sudo make installError: Error: connect ECONNREFUSED or FATAL: password authentication failed
Solution:
# Check PostgreSQL is running
psql -U postgres -l
# Verify connection string in config/projects.json
# Check username, password, host, and port match your PostgreSQL setup
# Test connection manually
psql "postgresql://postgres:postgres@localhost:5432/memory_default"Error: ERROR: permission denied to create extension "vector"
Solution:
# Connect as superuser (usually postgres)
psql -U postgres -d memory_default -c "CREATE EXTENSION vector;"Error: Error: Embedding dimension mismatch
Cause: The embedding model dimension doesn't match the database schema.
Solution:
-
Check your configured model dimension:
text-embedding-3-small: 1536 dimensionstext-embedding-3-large: 3072 dimensions
-
Update migration to match:
-- For text-embedding-3-small (default)
embedding vector(1536)
-- For text-embedding-3-large
embedding vector(3072)- Re-run migrations after updating the dimension.
Error: Error: OPENAI_API_KEY is required.
Solution:
# Add your OpenAI API key to .env
echo "OPENAI_API_KEY=sk-your-api-key-here" >> .envError: Error: No active project configured
Solution:
- Verify
MEMORY_ACTIVE_PROJECTin.envmatches a key inconfig/projects.json - Ensure
config/projects.jsonexists and is valid JSON - Check that the project's
databaseUrlis accessible
Error: Error: Index not found
Solution:
- List available indexes: Call
list_indexestool - Create the index: Call
create_indextool with the desired name - Check
MEMORY_DEFAULT_INDEXenvironment variable matches an existing index
Error: ERROR: relation "memories" does not exist or ERROR: relation "memory_indexes" does not exist
Cause: Database migrations haven't been run.
Solution:
# Run the schema migration
psql -d memory_default -f migrations/20250117000001_init_postgres_schema.sql
# Verify tables were created
psql -d memory_default -c "\dt"Error: Cannot find module '@modelcontextprotocol/sdk' or similar import errors
Solution:
# Install all dependencies
npm install
# Verify installation
npm list @modelcontextprotocol/sdkError: Cannot connect to server on stdio or MCP server not responding
Solution:
- Verify the MCP server path in Claude Desktop config is correct
- Check that the server starts successfully:
npm run dev(should show no errors) - Verify your Claude Desktop MCP configuration (usually in
~/Library/Application Support/Claude/claude_desktop_config.jsonon macOS):
{
"mcpServers": {
"memory": {
"command": "node",
"args": ["/absolute/path/to/memory-mcp/dist/index.js"],
"env": {
"OPENAI_API_KEY": "sk-...",
"MEMORY_ACTIVE_PROJECT": "local"
}
}
}
}- For development, use
npm run buildto compile TypeScript, then point todist/index.js - Check Claude Desktop logs for more detailed error messages
Error: Various TypeScript or runtime errors during npm run dev
Solution:
# Clear any caches and reinstall
rm -rf node_modules package-lock.json
npm install
# Run linting and formatting
npm run lint:fix
npm run format
# Check TypeScript compilation
npm run buildNeon provides serverless PostgreSQL with pgvector support:
- Create a new project at console.neon.tech
- Enable pgvector in the SQL Editor:
CREATE EXTENSION IF NOT EXISTS vector;
- Run the schema migration:
-- Copy contents of migrations/20250117000001_init_postgres_schema.sql - Copy the connection string to
config/projects.json:{ "production": { "databaseUrl": "postgresql://user:password@ep-cool-darkness-123456.us-east-2.aws.neon.tech/neondb?sslmode=require" } }
Supabase includes pgvector by default:
- Create a new project at app.supabase.com
- Go to SQL Editor and run:
CREATE EXTENSION IF NOT EXISTS vector;
- Run the schema migration in the SQL Editor
- Copy the connection string from Project Settings → Database:
{ "production": { "databaseUrl": "postgresql://postgres:your-password@db.xxxxxxxxxxxx.supabase.co:5432/postgres" } }
Any PostgreSQL 14+ provider with pgvector support will work:
- AWS RDS for PostgreSQL (with pgvector extension)
- Google Cloud SQL for PostgreSQL
- Azure Database for PostgreSQL
- DigitalOcean Managed Databases
- Self-hosted PostgreSQL instances
- docs/CHARACTER_MEMORY.md - Design principles for AI characters and imperfect memory behavior
- docs/SIMULATED_BRAIN.md - How the memory system simulates human-like cognition with decay, consolidation, and spreading activation
- docs/BACKDATING_GUIDE.md - Comprehensive guide for historical memory ingestion with priority decay calculations and practical examples
- migrations/20250117000001_init_postgres_schema.sql - Database schema and migration
- scripts/setup-postgres.sh - Automated setup script
- CLAUDE.md - Developer guidance for working with this codebase
- prompts/README.md - Composable prompt system documentation
Private - Internal use only