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.10.2",
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": ">=18.0.0"
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": "^11.10.0",
60
+ "better-sqlite3": "^12.1.0",
61
61
  "simple-git": "^3.28.0",
62
62
  "uuid": "^11.1.0"
63
63
  },