mcp-memory-keeper 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +433 -0
- package/LICENSE +21 -0
- package/README.md +1051 -0
- package/bin/mcp-memory-keeper +52 -0
- package/dist/__tests__/helpers/database-test-helper.js +160 -0
- package/dist/__tests__/helpers/test-server.js +92 -0
- package/dist/__tests__/integration/advanced-features.test.js +614 -0
- package/dist/__tests__/integration/backward-compatibility.test.js +245 -0
- package/dist/__tests__/integration/batchOperationsE2E.test.js +396 -0
- package/dist/__tests__/integration/batchOperationsHandler.test.js +1230 -0
- package/dist/__tests__/integration/channelManagementHandler.test.js +1291 -0
- package/dist/__tests__/integration/channels.test.js +376 -0
- package/dist/__tests__/integration/checkpoint.test.js +251 -0
- package/dist/__tests__/integration/concurrent-access.test.js +190 -0
- package/dist/__tests__/integration/context-operations.test.js +243 -0
- package/dist/__tests__/integration/contextDiff.test.js +852 -0
- package/dist/__tests__/integration/contextDiffHandler.test.js +976 -0
- package/dist/__tests__/integration/contextExportHandler.test.js +510 -0
- package/dist/__tests__/integration/contextGetPaginationDefaults.test.js +298 -0
- package/dist/__tests__/integration/contextReassignChannelHandler.test.js +908 -0
- package/dist/__tests__/integration/contextRelationshipsHandler.test.js +1151 -0
- package/dist/__tests__/integration/contextSearch.test.js +938 -0
- package/dist/__tests__/integration/contextSearchHandler.test.js +552 -0
- package/dist/__tests__/integration/contextWatchActual.test.js +165 -0
- package/dist/__tests__/integration/contextWatchHandler.test.js +1500 -0
- package/dist/__tests__/integration/cross-session-sharing.test.js +302 -0
- package/dist/__tests__/integration/database-initialization.test.js +134 -0
- package/dist/__tests__/integration/enhanced-context-operations.test.js +1082 -0
- package/dist/__tests__/integration/enhancedContextGetHandler.test.js +915 -0
- package/dist/__tests__/integration/enhancedContextTimelineHandler.test.js +716 -0
- package/dist/__tests__/integration/error-cases.test.js +407 -0
- package/dist/__tests__/integration/export-import.test.js +367 -0
- package/dist/__tests__/integration/feature-flags.test.js +542 -0
- package/dist/__tests__/integration/file-operations.test.js +264 -0
- package/dist/__tests__/integration/git-integration.test.js +237 -0
- package/dist/__tests__/integration/index-tools.test.js +496 -0
- package/dist/__tests__/integration/issue11-actual-bug-demo.test.js +304 -0
- package/dist/__tests__/integration/issue11-search-filters-bug.test.js +561 -0
- package/dist/__tests__/integration/issue12-checkpoint-restore-behavior.test.js +621 -0
- package/dist/__tests__/integration/issue13-key-validation.test.js +433 -0
- package/dist/__tests__/integration/knowledge-graph.test.js +338 -0
- package/dist/__tests__/integration/migrations.test.js +528 -0
- package/dist/__tests__/integration/multi-agent.test.js +546 -0
- package/dist/__tests__/integration/pagination-critical-fix.test.js +296 -0
- package/dist/__tests__/integration/paginationDefaultsHandler.test.js +600 -0
- package/dist/__tests__/integration/project-directory.test.js +283 -0
- package/dist/__tests__/integration/resource-cleanup.test.js +149 -0
- package/dist/__tests__/integration/retention.test.js +513 -0
- package/dist/__tests__/integration/search.test.js +333 -0
- package/dist/__tests__/integration/semantic-search.test.js +266 -0
- package/dist/__tests__/integration/server-initialization.test.js +307 -0
- package/dist/__tests__/integration/session-management.test.js +219 -0
- package/dist/__tests__/integration/simplified-sharing.test.js +346 -0
- package/dist/__tests__/integration/smart-compaction.test.js +230 -0
- package/dist/__tests__/integration/summarization.test.js +308 -0
- package/dist/__tests__/integration/watcher-migration-validation.test.js +544 -0
- package/dist/__tests__/security/input-validation.test.js +115 -0
- package/dist/__tests__/utils/agents.test.js +473 -0
- package/dist/__tests__/utils/database.test.js +177 -0
- package/dist/__tests__/utils/git.test.js +122 -0
- package/dist/__tests__/utils/knowledge-graph.test.js +297 -0
- package/dist/__tests__/utils/migrationHealthCheck.test.js +302 -0
- package/dist/__tests__/utils/project-directory-messages.test.js +188 -0
- package/dist/__tests__/utils/timezone-safe-dates.js +119 -0
- package/dist/__tests__/utils/validation.test.js +200 -0
- package/dist/__tests__/utils/vector-store.test.js +231 -0
- package/dist/handlers/contextWatchHandlers.js +206 -0
- package/dist/index.js +4310 -0
- package/dist/index.phase1.backup.js +410 -0
- package/dist/index.phase2.backup.js +704 -0
- package/dist/migrations/003_add_channels.js +174 -0
- package/dist/migrations/004_add_context_watch.js +151 -0
- package/dist/migrations/005_add_context_watch.js +98 -0
- package/dist/migrations/simplify-sharing.js +117 -0
- package/dist/repositories/BaseRepository.js +30 -0
- package/dist/repositories/CheckpointRepository.js +140 -0
- package/dist/repositories/ContextRepository.js +1873 -0
- package/dist/repositories/FileRepository.js +104 -0
- package/dist/repositories/RepositoryManager.js +62 -0
- package/dist/repositories/SessionRepository.js +66 -0
- package/dist/repositories/WatcherRepository.js +252 -0
- package/dist/repositories/index.js +15 -0
- package/dist/server.js +384 -0
- package/dist/test-helpers/database-helper.js +128 -0
- package/dist/types/entities.js +3 -0
- package/dist/utils/agents.js +791 -0
- package/dist/utils/channels.js +150 -0
- package/dist/utils/database.js +731 -0
- package/dist/utils/feature-flags.js +476 -0
- package/dist/utils/git.js +145 -0
- package/dist/utils/knowledge-graph.js +264 -0
- package/dist/utils/migrationHealthCheck.js +373 -0
- package/dist/utils/migrations.js +452 -0
- package/dist/utils/retention.js +460 -0
- package/dist/utils/timestamps.js +112 -0
- package/dist/utils/validation.js +296 -0
- package/dist/utils/vector-store.js +247 -0
- package/package.json +84 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MCP Memory Keeper CLI
|
|
5
|
+
* This wrapper ensures the server runs correctly when invoked via npx
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { spawn } = require('child_process');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
|
|
13
|
+
// Determine the data directory
|
|
14
|
+
const DATA_DIR = process.env.DATA_DIR || path.join(os.homedir(), 'mcp-data', 'memory-keeper');
|
|
15
|
+
|
|
16
|
+
// Ensure data directory exists
|
|
17
|
+
if (!fs.existsSync(DATA_DIR)) {
|
|
18
|
+
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Set environment variable for the server
|
|
22
|
+
process.env.DATA_DIR = DATA_DIR;
|
|
23
|
+
|
|
24
|
+
// Get the path to the actual server
|
|
25
|
+
const serverPath = path.join(__dirname, '..', 'dist', 'index.js');
|
|
26
|
+
|
|
27
|
+
// Check if the server is built
|
|
28
|
+
if (!fs.existsSync(serverPath)) {
|
|
29
|
+
console.error('Error: Server not built. This should not happen with the npm package.');
|
|
30
|
+
console.error('Please report this issue at: https://github.com/mkreyman/mcp-memory-keeper/issues');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Change to data directory (where context.db will be created)
|
|
35
|
+
process.chdir(DATA_DIR);
|
|
36
|
+
|
|
37
|
+
// Spawn the server
|
|
38
|
+
const child = spawn(process.execPath, [serverPath, ...process.argv.slice(2)], {
|
|
39
|
+
stdio: 'inherit',
|
|
40
|
+
env: process.env
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Handle exit
|
|
44
|
+
child.on('exit', (code) => {
|
|
45
|
+
process.exit(code);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Handle errors
|
|
49
|
+
child.on('error', (err) => {
|
|
50
|
+
console.error('Failed to start memory-keeper server:', err);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
});
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DatabaseTestHelper = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Helper utilities for managing database behavior in tests
|
|
6
|
+
*/
|
|
7
|
+
class DatabaseTestHelper {
|
|
8
|
+
db;
|
|
9
|
+
originalTriggers = new Map();
|
|
10
|
+
constructor(db) {
|
|
11
|
+
this.db = db;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Disable timestamp update triggers to prevent unexpected timestamp updates
|
|
15
|
+
* during tests that are sensitive to update counts.
|
|
16
|
+
*/
|
|
17
|
+
disableTimestampTriggers() {
|
|
18
|
+
const triggerNames = [
|
|
19
|
+
'update_context_items_timestamp',
|
|
20
|
+
'update_sessions_timestamp',
|
|
21
|
+
'update_retention_policies_timestamp',
|
|
22
|
+
'update_feature_flags_timestamp',
|
|
23
|
+
'increment_sequence_update',
|
|
24
|
+
];
|
|
25
|
+
for (const triggerName of triggerNames) {
|
|
26
|
+
// Get the trigger definition before dropping it
|
|
27
|
+
const triggerDef = this.db
|
|
28
|
+
.prepare(`SELECT sql FROM sqlite_master WHERE type = 'trigger' AND name = ?`)
|
|
29
|
+
.get(triggerName);
|
|
30
|
+
if (triggerDef && triggerDef.sql) {
|
|
31
|
+
this.originalTriggers.set(triggerName, triggerDef.sql);
|
|
32
|
+
// Drop the trigger
|
|
33
|
+
this.db.exec(`DROP TRIGGER IF EXISTS ${triggerName}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Re-enable timestamp update triggers after tests
|
|
39
|
+
*/
|
|
40
|
+
enableTimestampTriggers() {
|
|
41
|
+
for (const [triggerName, sql] of this.originalTriggers) {
|
|
42
|
+
if (sql) {
|
|
43
|
+
try {
|
|
44
|
+
this.db.exec(sql);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.warn(`Failed to recreate trigger ${triggerName}:`, error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
this.originalTriggers.clear();
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Normalize timestamps for comparison in tests.
|
|
55
|
+
* Sets all timestamps to a fixed value to avoid timestamp-based failures.
|
|
56
|
+
*/
|
|
57
|
+
normalizeTimestamps(items) {
|
|
58
|
+
const fixedTimestamp = '2024-01-01T00:00:00.000Z';
|
|
59
|
+
return items.map(item => ({
|
|
60
|
+
...item,
|
|
61
|
+
created_at: fixedTimestamp,
|
|
62
|
+
updated_at: fixedTimestamp,
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Update an item without triggering timestamp updates.
|
|
67
|
+
* Useful for tests that need to control timestamps precisely.
|
|
68
|
+
*/
|
|
69
|
+
updateWithoutTimestamp(table, updates, where) {
|
|
70
|
+
// Build the UPDATE statement
|
|
71
|
+
const setClauses = Object.keys(updates)
|
|
72
|
+
.map(key => `${key} = ?`)
|
|
73
|
+
.join(', ');
|
|
74
|
+
const whereClauses = Object.keys(where)
|
|
75
|
+
.map(key => `${key} = ?`)
|
|
76
|
+
.join(' AND ');
|
|
77
|
+
const sql = `UPDATE ${table} SET ${setClauses} WHERE ${whereClauses}`;
|
|
78
|
+
const values = [...Object.values(updates), ...Object.values(where)];
|
|
79
|
+
// Temporarily disable triggers
|
|
80
|
+
this.disableTimestampTriggers();
|
|
81
|
+
try {
|
|
82
|
+
this.db.prepare(sql).run(...values);
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
// Re-enable triggers
|
|
86
|
+
this.enableTimestampTriggers();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get counts of items by their modification status
|
|
91
|
+
* Useful for tests that check added vs modified items
|
|
92
|
+
*/
|
|
93
|
+
getModificationCounts(sessionId, sinceTimestamp) {
|
|
94
|
+
const added = this.db
|
|
95
|
+
.prepare(`SELECT COUNT(*) as count FROM context_items
|
|
96
|
+
WHERE session_id = ? AND created_at > ?`)
|
|
97
|
+
.get(sessionId, sinceTimestamp);
|
|
98
|
+
const modified = this.db
|
|
99
|
+
.prepare(`SELECT COUNT(*) as count FROM context_items
|
|
100
|
+
WHERE session_id = ?
|
|
101
|
+
AND created_at <= ?
|
|
102
|
+
AND updated_at > ?`)
|
|
103
|
+
.get(sessionId, sinceTimestamp, sinceTimestamp);
|
|
104
|
+
return {
|
|
105
|
+
added: added.count,
|
|
106
|
+
modified: modified.count,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Create a test item with controlled timestamps
|
|
111
|
+
*/
|
|
112
|
+
createTestItem(params) {
|
|
113
|
+
const { id, sessionId, key, value, createdAt = new Date().toISOString(), updatedAt = createdAt, category = null, priority = 'normal', channel = 'general', } = params;
|
|
114
|
+
// Temporarily disable triggers to control timestamps
|
|
115
|
+
this.disableTimestampTriggers();
|
|
116
|
+
try {
|
|
117
|
+
this.db
|
|
118
|
+
.prepare(`INSERT INTO context_items
|
|
119
|
+
(id, session_id, key, value, category, priority, channel, created_at, updated_at)
|
|
120
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
121
|
+
.run(id, sessionId, key, value, category, priority, channel, createdAt, updatedAt);
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
this.enableTimestampTriggers();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Helper to check if change tracking triggers are affecting counts
|
|
129
|
+
*/
|
|
130
|
+
getChangeTrackingCount(sessionId, sinceSequence = 0) {
|
|
131
|
+
try {
|
|
132
|
+
const result = this.db
|
|
133
|
+
.prepare(`SELECT COUNT(*) as count FROM context_changes
|
|
134
|
+
WHERE session_id = ? AND sequence_id > ?`)
|
|
135
|
+
.get(sessionId, sinceSequence);
|
|
136
|
+
return result.count;
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Table might not exist in all test environments
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Clear all change tracking data for a clean test state
|
|
145
|
+
*/
|
|
146
|
+
clearChangeTracking(sessionId) {
|
|
147
|
+
try {
|
|
148
|
+
if (sessionId) {
|
|
149
|
+
this.db.prepare('DELETE FROM context_changes WHERE session_id = ?').run(sessionId);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
this.db.prepare('DELETE FROM context_changes').run();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
// Table might not exist in all test environments
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
exports.DatabaseTestHelper = DatabaseTestHelper;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.TestServer = void 0;
|
|
37
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
38
|
+
const database_js_1 = require("../../utils/database.js");
|
|
39
|
+
const git_js_1 = require("../../utils/git.js");
|
|
40
|
+
const os = __importStar(require("os"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
class TestServer {
|
|
44
|
+
server;
|
|
45
|
+
dbManager;
|
|
46
|
+
gitOps;
|
|
47
|
+
tempDbPath;
|
|
48
|
+
constructor() {
|
|
49
|
+
// Create temporary database for testing
|
|
50
|
+
this.tempDbPath = path.join(os.tmpdir(), `test-mcp-${Date.now()}.db`);
|
|
51
|
+
this.dbManager = new database_js_1.DatabaseManager({
|
|
52
|
+
filename: this.tempDbPath,
|
|
53
|
+
maxSize: 10 * 1024 * 1024, // 10MB
|
|
54
|
+
walMode: true,
|
|
55
|
+
});
|
|
56
|
+
this.gitOps = new git_js_1.GitOperations(os.tmpdir());
|
|
57
|
+
// Import and configure the server logic here
|
|
58
|
+
// For now, we'll create a minimal server for testing
|
|
59
|
+
this.server = new index_js_1.Server({
|
|
60
|
+
name: 'test-memory-keeper',
|
|
61
|
+
version: '1.0.0',
|
|
62
|
+
}, {
|
|
63
|
+
capabilities: {
|
|
64
|
+
tools: {},
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async callTool(name, args) {
|
|
69
|
+
// This would call the actual tool handler
|
|
70
|
+
// For now, returning a mock response
|
|
71
|
+
return {
|
|
72
|
+
content: [
|
|
73
|
+
{
|
|
74
|
+
type: 'text',
|
|
75
|
+
text: `Called ${name} with ${JSON.stringify(args)}`,
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
cleanup() {
|
|
81
|
+
this.dbManager.close();
|
|
82
|
+
try {
|
|
83
|
+
fs.unlinkSync(this.tempDbPath);
|
|
84
|
+
fs.unlinkSync(`${this.tempDbPath}-wal`);
|
|
85
|
+
fs.unlinkSync(`${this.tempDbPath}-shm`);
|
|
86
|
+
}
|
|
87
|
+
catch (_e) {
|
|
88
|
+
// Ignore
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.TestServer = TestServer;
|