mcp-memory-keeper 0.10.2 → 0.11.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
CHANGED
|
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.11.0] - 2025-12-10
|
|
11
|
+
|
|
12
|
+
### Breaking Changes
|
|
13
|
+
|
|
14
|
+
- **Node.js 18 support dropped** - Minimum required Node.js version is now 20.0.0
|
|
15
|
+
- Node.js 18 reached End-of-Life on April 30, 2025
|
|
16
|
+
- Users on Node.js 18 must upgrade to Node.js 20 or later
|
|
17
|
+
- Existing installations will continue working until updated
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **Installation fails on Node.js 24 (#28)** - Updated `better-sqlite3` dependency
|
|
22
|
+
- Upgraded from `^11.10.0` to `^12.1.0` to support Node.js 24 (LTS "Krypton")
|
|
23
|
+
- Prebuilt binaries now available for Node.js 20, 22, and 24
|
|
24
|
+
- Resolves `gyp ERR!` build failures on Node.js 24
|
|
25
|
+
|
|
10
26
|
## [0.10.2] - 2025-09-16
|
|
11
27
|
|
|
12
28
|
### Fixed
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Test for token limit issue with channel queries
|
|
4
|
+
*
|
|
5
|
+
* Reproduces the bug where context_get with limit: 50 still exceeds token limits
|
|
6
|
+
* when querying a channel with large items.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
const globals_1 = require("@jest/globals");
|
|
10
|
+
const database_js_1 = require("../../utils/database.js");
|
|
11
|
+
const RepositoryManager_js_1 = require("../../repositories/RepositoryManager.js");
|
|
12
|
+
const token_limits_js_1 = require("../../utils/token-limits.js");
|
|
13
|
+
(0, globals_1.describe)('Token Limit with Channel Query Bug', () => {
|
|
14
|
+
let dbManager;
|
|
15
|
+
let repositories;
|
|
16
|
+
let sessionId;
|
|
17
|
+
(0, globals_1.beforeEach)(() => {
|
|
18
|
+
dbManager = new database_js_1.DatabaseManager({ filename: ':memory:' });
|
|
19
|
+
repositories = new RepositoryManager_js_1.RepositoryManager(dbManager);
|
|
20
|
+
const session = repositories.sessions.create({
|
|
21
|
+
name: 'Test Session',
|
|
22
|
+
defaultChannel: 'test-channel',
|
|
23
|
+
});
|
|
24
|
+
sessionId = session.id;
|
|
25
|
+
});
|
|
26
|
+
(0, globals_1.afterEach)(() => {
|
|
27
|
+
dbManager.close();
|
|
28
|
+
});
|
|
29
|
+
(0, globals_1.it)('should enforce token limits even when limit parameter is provided', () => {
|
|
30
|
+
// Create 50 large context items (each ~1000 chars)
|
|
31
|
+
const largeValue = 'x'.repeat(1000);
|
|
32
|
+
for (let i = 0; i < 50; i++) {
|
|
33
|
+
repositories.contexts.save(sessionId, {
|
|
34
|
+
key: `large-item-${i}`,
|
|
35
|
+
value: largeValue,
|
|
36
|
+
channel: 'outbound-call-center',
|
|
37
|
+
priority: 'normal',
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// Query with limit: 50
|
|
41
|
+
const result = repositories.contexts.queryEnhanced({
|
|
42
|
+
sessionId,
|
|
43
|
+
channel: 'outbound-call-center',
|
|
44
|
+
limit: 50,
|
|
45
|
+
sort: 'updated_desc',
|
|
46
|
+
includeMetadata: false,
|
|
47
|
+
});
|
|
48
|
+
(0, globals_1.expect)(result.items.length).toBe(50);
|
|
49
|
+
// Check if response would exceed token limit
|
|
50
|
+
const tokenConfig = (0, token_limits_js_1.getTokenConfig)();
|
|
51
|
+
const { exceedsLimit, safeItemCount } = (0, token_limits_js_1.checkTokenLimit)(result.items, false, tokenConfig);
|
|
52
|
+
// Build the actual response structure
|
|
53
|
+
const response = {
|
|
54
|
+
items: result.items,
|
|
55
|
+
pagination: {
|
|
56
|
+
total: result.totalCount,
|
|
57
|
+
returned: result.items.length,
|
|
58
|
+
offset: 0,
|
|
59
|
+
hasMore: false,
|
|
60
|
+
nextOffset: null,
|
|
61
|
+
truncated: false,
|
|
62
|
+
truncatedCount: 0,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
const responseJson = JSON.stringify(response, null, 2);
|
|
66
|
+
const actualTokens = Math.ceil(responseJson.length / tokenConfig.charsPerToken);
|
|
67
|
+
// The bug: even with limit: 50, the response can exceed token limits
|
|
68
|
+
if (actualTokens > tokenConfig.mcpMaxTokens) {
|
|
69
|
+
// BUG REPRODUCED: The response exceeds token limits
|
|
70
|
+
// Verify that checkTokenLimit correctly detected the issue
|
|
71
|
+
(0, globals_1.expect)(exceedsLimit).toBe(true);
|
|
72
|
+
(0, globals_1.expect)(safeItemCount).toBeLessThan(50);
|
|
73
|
+
(0, globals_1.expect)(actualTokens).toBeGreaterThan(tokenConfig.mcpMaxTokens);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
(0, globals_1.it)('should respect token limits over user-provided limit parameter', () => {
|
|
77
|
+
// Create 100 large context items (each ~800 chars)
|
|
78
|
+
const largeValue = 'y'.repeat(800);
|
|
79
|
+
for (let i = 0; i < 100; i++) {
|
|
80
|
+
repositories.contexts.save(sessionId, {
|
|
81
|
+
key: `item-${i}`,
|
|
82
|
+
value: largeValue,
|
|
83
|
+
channel: 'test-channel',
|
|
84
|
+
priority: 'normal',
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
// Query with limit: 50 (user expectation)
|
|
88
|
+
const result = repositories.contexts.queryEnhanced({
|
|
89
|
+
sessionId,
|
|
90
|
+
channel: 'test-channel',
|
|
91
|
+
limit: 50,
|
|
92
|
+
sort: 'created_desc',
|
|
93
|
+
includeMetadata: false,
|
|
94
|
+
});
|
|
95
|
+
// Simulate the context_get handler logic
|
|
96
|
+
const tokenConfig = (0, token_limits_js_1.getTokenConfig)();
|
|
97
|
+
const { exceedsLimit, safeItemCount } = (0, token_limits_js_1.checkTokenLimit)(result.items, false, tokenConfig);
|
|
98
|
+
let actualItems = result.items;
|
|
99
|
+
let wasTruncated = false;
|
|
100
|
+
if (exceedsLimit && safeItemCount < result.items.length) {
|
|
101
|
+
actualItems = result.items.slice(0, safeItemCount);
|
|
102
|
+
wasTruncated = true;
|
|
103
|
+
}
|
|
104
|
+
// Build response
|
|
105
|
+
const response = {
|
|
106
|
+
items: actualItems,
|
|
107
|
+
pagination: {
|
|
108
|
+
total: result.totalCount,
|
|
109
|
+
returned: actualItems.length,
|
|
110
|
+
offset: 0,
|
|
111
|
+
hasMore: wasTruncated || actualItems.length < result.totalCount,
|
|
112
|
+
nextOffset: wasTruncated ? actualItems.length : null,
|
|
113
|
+
truncated: wasTruncated,
|
|
114
|
+
truncatedCount: wasTruncated ? result.items.length - actualItems.length : 0,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
const responseJson = JSON.stringify(response, null, 2);
|
|
118
|
+
const actualTokens = Math.ceil(responseJson.length / tokenConfig.charsPerToken);
|
|
119
|
+
// Verify token limit is not exceeded
|
|
120
|
+
(0, globals_1.expect)(actualTokens).toBeLessThanOrEqual(tokenConfig.mcpMaxTokens);
|
|
121
|
+
// Verify truncation occurred if needed
|
|
122
|
+
if (exceedsLimit) {
|
|
123
|
+
(0, globals_1.expect)(wasTruncated).toBe(true);
|
|
124
|
+
(0, globals_1.expect)(actualItems.length).toBeLessThan(50);
|
|
125
|
+
(0, globals_1.expect)(actualItems.length).toBe(safeItemCount);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-memory-keeper",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "MCP server for persistent context management in AI coding assistants",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -50,14 +50,14 @@
|
|
|
50
50
|
"CHANGELOG.md"
|
|
51
51
|
],
|
|
52
52
|
"engines": {
|
|
53
|
-
"node": ">=
|
|
53
|
+
"node": ">=20.0.0"
|
|
54
54
|
},
|
|
55
55
|
"publishConfig": {
|
|
56
56
|
"access": "public"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"@modelcontextprotocol/sdk": "^1.12.3",
|
|
60
|
-
"better-sqlite3": "^
|
|
60
|
+
"better-sqlite3": "^12.1.0",
|
|
61
61
|
"simple-git": "^3.28.0",
|
|
62
62
|
"uuid": "^11.1.0"
|
|
63
63
|
},
|