- Overview
- System Architecture
- Core Components
- Data Flow
- Database Design
- Security Considerations
- Performance Optimization
- Extension Points
- Future Considerations
MCP Memory Keeper is a Model Context Protocol (MCP) server designed to provide persistent context management for AI coding assistants. The architecture prioritizes:
- Reliability: SQLite with WAL mode for data persistence
- Performance: Efficient indexing and caching strategies
- Extensibility: Modular design with clear interfaces
- Simplicity: Minimal dependencies, straightforward data flow
┌─────────────────┐ MCP Protocol ┌──────────────────┐
│ │◄─────────────────────►│ │
│ Claude Code/ │ │ MCP Memory │
│ Claude Desktop │ │ Keeper Server │
│ │ │ │
└─────────────────┘ └───────┬──────────┘
│
│
┌────────▼──────────┐
│ │
│ Core Modules │
│ │
└───────┬───────────┘
│
┌────────────────┬───────────────┼───────────────┬────────────────┐
│ │ │ │ │
┌──────▼──────┐ ┌──────▼──────┐ ┌─────▼──────┐ ┌─────▼──────┐ ┌──────▼──────┐
│ Database │ │ Validation │ │ Git │ │ Knowledge │ │ Agents │
│ Manager │ │ Module │ │Integration │ │ Graph │ │ System │
└──────┬──────┘ └─────────────┘ └────────────┘ └────────────┘ └─────────────┘
│
┌──────▼──────┐
│ SQLite │
│ Database │
└─────────────┘
-
MCP Server (index.ts)
- Protocol implementation
- Request routing
- Response formatting
- Error handling
-
Core Modules
- Business logic implementation
- Cross-cutting concerns
- Module coordination
-
Utility Modules
- Specialized functionality
- Reusable components
- External integrations
The main entry point that implements the MCP protocol:
class MemoryKeeperServer {
private db: DatabaseManager;
private currentSessionId?: string;
constructor() {
// Initialize database
this.db = new DatabaseManager({
filename: 'context.db',
maxSize: 100 * 1024 * 1024,
walMode: true,
});
}
// Tool implementations
async handleToolCall(name: string, args: any): Promise<any> {
switch (name) {
case 'context_save':
return this.contextSave(args);
// ... other tools
}
}
}Handles all database operations with transaction support:
class DatabaseManager {
private db: Database.Database;
// Transaction wrapper for atomic operations
transaction<T>(fn: () => T): T {
const transaction = this.db.transaction(fn);
return transaction();
}
// Automatic size tracking and cleanup
getSessionSize(sessionId: string): SessionStats {
// Efficient size calculation
}
}Input validation and sanitization:
const validateContextSaveArgs = (args: any): ContextSaveArgs => {
if (!args.key || typeof args.key !== 'string') {
throw new ValidationError('key is required');
}
// Additional validations
return args as ContextSaveArgs;
};Git operations using simple-git:
class GitManager {
private git: SimpleGit;
async getStatus(): Promise<GitStatus> {
// Safe git operations with error handling
}
async getCurrentBranch(): Promise<string> {
// Branch detection
}
}Entity and relationship extraction:
class KnowledgeGraphManager {
extractEntities(text: string): Entity[] {
// NLP-based entity extraction
}
findRelationships(entities: Entity[]): Relation[] {
// Relationship detection
}
}Semantic search implementation:
class VectorStore {
// Character n-gram based embeddings
createEmbedding(text: string): number[] {
// Lightweight embedding generation
}
cosineSimilarity(a: number[], b: number[]): number {
// Similarity calculation
}
}Specialized agents for analysis:
abstract class Agent {
abstract process(task: AgentTask): Promise<AgentResult>;
}
class AnalyzerAgent extends Agent {
// Pattern detection, trend analysis
}
class SynthesizerAgent extends Agent {
// Summarization, recommendations
}User Request → MCP Server → Validation → Session Check → Database Insert → Response
↓
Create session if needed
Search Query → Validation → Query Building → Database Search → Ranking → Response
↓
Full-text search index
Checkpoint Request → Current State Snapshot → Transaction Begin
↓
Save Context Items
↓
Save File Cache
↓
Save Git Status
↓
Transaction Commit → Response
-- Core Tables
sessions (id, name, description, branch, parent_id, created_at, updated_at)
context_items (id, session_id, key, value, category, priority, metadata, size, created_at)
file_cache (id, session_id, file_path, content, hash, size, last_read, updated_at)
-- Checkpoint System
checkpoints (id, session_id, name, description, metadata, git_status, git_branch, created_at)
checkpoint_items (id, checkpoint_id, context_item_id)
checkpoint_files (id, checkpoint_id, file_cache_id)
-- Knowledge Graph
entities (id, session_id, type, name, attributes, created_at)
relations (id, session_id, subject_id, predicate, object_id, confidence, created_at)
observations (id, entity_id, observation, source, timestamp)
-- Advanced Features
vector_embeddings (id, content_id, content, embedding, metadata, created_at)
journal_entries (id, session_id, entry, tags, mood, created_at)
compressed_context (id, session_id, original_count, compressed_data, compression_ratio, date_range_start, date_range_end, created_at)
tool_events (id, session_id, tool_name, event_type, data, created_at)-- Performance indexes
CREATE INDEX idx_context_items_session ON context_items(session_id);
CREATE INDEX idx_context_items_category ON context_items(category);
CREATE INDEX idx_context_items_priority ON context_items(priority);
CREATE INDEX idx_entities_name ON entities(name);
CREATE INDEX idx_relations_subject ON relations(subject_id);
CREATE INDEX idx_relations_object ON relations(object_id);// Atomic checkpoint creation
db.transaction(() => {
const checkpointId = createCheckpoint();
saveCheckpointItems(checkpointId, items);
saveCheckpointFiles(checkpointId, files);
updateSessionTimestamp();
});- All user inputs are validated before processing
- SQL injection prevention through parameterized queries
- Path traversal protection for file operations
- Session-based isolation
- No cross-session data leakage
- Secure file path handling
- No sensitive information in error messages
- Proper error logging without exposing internals
- Graceful degradation on failures
// Safe file path resolution
const safePath = path.resolve(basePath, userPath);
if (!safePath.startsWith(basePath)) {
throw new SecurityError('Invalid file path');
}- WAL Mode: Better concurrency
- Prepared Statements: Query plan caching
- Batch Operations: Reduce round trips
- Size Tracking: Automatic cleanup triggers
- In-memory session cache
- Prepared statement caching
- Vector embedding cache
- Full-text search indexes
- Limit default results
- Progressive loading
// Streaming large results
function* streamResults(query: string) {
const stmt = db.prepare(query);
for (const row of stmt.iterate()) {
yield processRow(row);
}
}Add new MCP tools by extending the server:
// In index.ts
tools.push({
name: 'custom_tool',
description: 'Custom functionality',
inputSchema: {
/* schema */
},
});
// Handler implementation
async function handleCustomTool(args: any) {
// Implementation
}Abstract storage interface for different backends:
interface StorageBackend {
save(item: ContextItem): Promise<string>;
get(key: string): Promise<ContextItem>;
search(query: string): Promise<ContextItem[]>;
}Extend the agent system:
class CustomAnalyzer extends Agent {
async process(task: AgentTask): Promise<AgentResult> {
// Custom analysis logic
}
}
// Register with coordinator
coordinator.registerAgent('custom', new CustomAnalyzer());Add new export formats:
interface Exporter {
export(data: ExportData): Promise<string>;
getFileExtension(): string;
}
class CSVExporter implements Exporter {
// CSV export implementation
}Export knowledge graphs to popular visualization tools:
interface VisualizationExporter {
exportToD3(graph: GraphData): Promise<D3Format>;
exportToCytoscape(graph: GraphData): Promise<CytoscapeFormat>;
exportToNeo4j(graph: GraphData): Promise<CypherQueries>;
exportToGraphML(graph: GraphData): Promise<string>;
}
// D3.js format example
{
"nodes": [
{"id": "1", "group": "function", "value": 10},
{"id": "2", "group": "class", "value": 20}
],
"links": [
{"source": "1", "target": "2", "value": 1}
]
}
// Cytoscape format example
{
"elements": {
"nodes": [
{"data": {"id": "a", "label": "Node A"}},
{"data": {"id": "b", "label": "Node B"}}
],
"edges": [
{"data": {"source": "a", "target": "b"}}
]
}
}- Sharding: Split large databases by date/session
- Replication: Read replicas for search
- Caching Layer: Redis for hot data
- Conflict Resolution: Three-way merge for sync
- Encryption: End-to-end encryption for cloud storage
- Selective Sync: Choose what to sync
- Access Control: Role-based permissions
- Audit Trail: Track all operations
- Collaboration: Shared sessions with locking
- Machine Learning: Better entity extraction
- Visualization: Built-in graph rendering with export support
- Plugins: Third-party extensions
- Natural Language: Query translation capabilities
- Community Patterns: Shared rule sets and templates
interface Metrics {
responseTime: Histogram;
databaseSize: Gauge;
activeConnections: Counter;
errorRate: Counter;
}- Design the database schema
- Implement validation logic
- Add core functionality
- Write comprehensive tests
- Update documentation
- Unit tests for all modules
- Integration tests for workflows
- Performance tests for large datasets
- Manual testing checklist
// Consistent error handling
try {
return await operation();
} catch (error) {
logger.error('Operation failed', { error, context });
throw new McpError(ErrorCode.INTERNAL_ERROR, 'Operation failed', {
originalError: error.message,
});
}// Structured logging
logger.info('Context saved', {
sessionId,
key,
category,
size: value.length,
duration: Date.now() - startTime,
});MCP Memory Keeper's architecture is designed to be:
- Simple: Easy to understand and modify
- Reliable: Robust data persistence
- Performant: Efficient for typical use cases
- Extensible: Clear extension points
The modular design allows for future enhancements while maintaining backward compatibility and system stability.