mcp-memory-keeper 0.10.2 → 0.12.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 +46 -1
- package/README.md +86 -0
- package/dist/__tests__/integration/git-integration.test.js +3 -0
- package/dist/__tests__/integration/issue-token-limit-channel-query.test.js +128 -0
- package/dist/__tests__/integration/project-directory.test.js +6 -0
- package/dist/__tests__/integration/tool-profiles-integration.test.js +150 -0
- package/dist/__tests__/utils/project-directory-messages.test.js +3 -0
- package/dist/__tests__/utils/tool-profiles.test.js +374 -0
- package/dist/index.js +1075 -1052
- package/dist/utils/tool-profiles.js +242 -0
- package/examples/config.json +31 -0
- package/examples/project-directory-setup.md +114 -0
- package/package.json +4 -3
|
@@ -0,0 +1,242 @@
|
|
|
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.DEFAULT_PROFILES = exports.ALL_TOOL_NAMES_SET = exports.ALL_TOOL_NAMES = void 0;
|
|
37
|
+
exports.loadConfigFile = loadConfigFile;
|
|
38
|
+
exports.validateToolNames = validateToolNames;
|
|
39
|
+
exports.resolveActiveProfile = resolveActiveProfile;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const os = __importStar(require("os"));
|
|
43
|
+
/**
|
|
44
|
+
* All known tool names - source of truth for validation.
|
|
45
|
+
* Update this list whenever a tool is added or removed from the
|
|
46
|
+
* ListToolsRequestSchema handler in src/index.ts.
|
|
47
|
+
*/
|
|
48
|
+
exports.ALL_TOOL_NAMES = [
|
|
49
|
+
// Session Management
|
|
50
|
+
'context_session_start',
|
|
51
|
+
'context_session_list',
|
|
52
|
+
'context_set_project_dir',
|
|
53
|
+
// Core Context
|
|
54
|
+
'context_save',
|
|
55
|
+
'context_get',
|
|
56
|
+
'context_status',
|
|
57
|
+
// File Caching
|
|
58
|
+
'context_cache_file',
|
|
59
|
+
'context_file_changed',
|
|
60
|
+
// Checkpoints
|
|
61
|
+
'context_checkpoint',
|
|
62
|
+
'context_restore_checkpoint',
|
|
63
|
+
// Summarization & Compaction
|
|
64
|
+
'context_summarize',
|
|
65
|
+
'context_prepare_compaction',
|
|
66
|
+
// Git Integration
|
|
67
|
+
'context_git_commit',
|
|
68
|
+
// Search
|
|
69
|
+
'context_search',
|
|
70
|
+
'context_search_all',
|
|
71
|
+
'context_semantic_search',
|
|
72
|
+
// Export/Import
|
|
73
|
+
'context_export',
|
|
74
|
+
'context_import',
|
|
75
|
+
// Knowledge Graph
|
|
76
|
+
'context_analyze',
|
|
77
|
+
'context_find_related',
|
|
78
|
+
'context_visualize',
|
|
79
|
+
// Multi-Agent
|
|
80
|
+
'context_delegate',
|
|
81
|
+
// Session Branching/Merging
|
|
82
|
+
'context_branch_session',
|
|
83
|
+
'context_merge_sessions',
|
|
84
|
+
// Journal & Timeline
|
|
85
|
+
'context_journal_entry',
|
|
86
|
+
'context_timeline',
|
|
87
|
+
// Advanced Features
|
|
88
|
+
'context_compress',
|
|
89
|
+
'context_integrate_tool',
|
|
90
|
+
'context_diff',
|
|
91
|
+
// Channel Management
|
|
92
|
+
'context_list_channels',
|
|
93
|
+
'context_channel_stats',
|
|
94
|
+
'context_reassign_channel',
|
|
95
|
+
// Watch
|
|
96
|
+
'context_watch',
|
|
97
|
+
// Batch Operations
|
|
98
|
+
'context_batch_save',
|
|
99
|
+
'context_batch_delete',
|
|
100
|
+
'context_batch_update',
|
|
101
|
+
// Relationships
|
|
102
|
+
'context_link',
|
|
103
|
+
'context_get_related',
|
|
104
|
+
];
|
|
105
|
+
/** Pre-computed Set for O(1) lookups — used internally and exported for index.ts */
|
|
106
|
+
exports.ALL_TOOL_NAMES_SET = new Set(exports.ALL_TOOL_NAMES);
|
|
107
|
+
/** Built-in default profiles */
|
|
108
|
+
exports.DEFAULT_PROFILES = {
|
|
109
|
+
minimal: [
|
|
110
|
+
'context_session_start',
|
|
111
|
+
'context_session_list',
|
|
112
|
+
'context_save',
|
|
113
|
+
'context_get',
|
|
114
|
+
'context_search',
|
|
115
|
+
'context_status',
|
|
116
|
+
'context_checkpoint',
|
|
117
|
+
'context_restore_checkpoint',
|
|
118
|
+
],
|
|
119
|
+
standard: [
|
|
120
|
+
'context_session_start',
|
|
121
|
+
'context_session_list',
|
|
122
|
+
'context_set_project_dir',
|
|
123
|
+
'context_save',
|
|
124
|
+
'context_get',
|
|
125
|
+
'context_status',
|
|
126
|
+
'context_checkpoint',
|
|
127
|
+
'context_restore_checkpoint',
|
|
128
|
+
'context_search',
|
|
129
|
+
'context_search_all',
|
|
130
|
+
'context_summarize',
|
|
131
|
+
'context_prepare_compaction',
|
|
132
|
+
'context_git_commit',
|
|
133
|
+
'context_export',
|
|
134
|
+
'context_import',
|
|
135
|
+
'context_journal_entry',
|
|
136
|
+
'context_timeline',
|
|
137
|
+
'context_list_channels',
|
|
138
|
+
'context_channel_stats',
|
|
139
|
+
'context_batch_save',
|
|
140
|
+
'context_batch_delete',
|
|
141
|
+
'context_batch_update',
|
|
142
|
+
],
|
|
143
|
+
full: [...exports.ALL_TOOL_NAMES],
|
|
144
|
+
};
|
|
145
|
+
const CONFIG_DIR = path.join(os.homedir(), '.mcp-memory-keeper');
|
|
146
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
147
|
+
/** Load config file, returning null if absent or invalid */
|
|
148
|
+
function loadConfigFile(configPath = CONFIG_FILE) {
|
|
149
|
+
try {
|
|
150
|
+
if (!fs.existsSync(configPath)) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
154
|
+
const parsed = JSON.parse(raw);
|
|
155
|
+
if (!parsed ||
|
|
156
|
+
typeof parsed !== 'object' ||
|
|
157
|
+
Array.isArray(parsed) ||
|
|
158
|
+
!parsed.profiles ||
|
|
159
|
+
typeof parsed.profiles !== 'object' ||
|
|
160
|
+
Array.isArray(parsed.profiles)) {
|
|
161
|
+
console.warn(`[MCP-Memory-Keeper] Config file at ${configPath} is missing a valid "profiles" key. Ignoring file.`);
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
// Validated: parsed.profiles is a non-null, non-array object
|
|
165
|
+
return { profiles: parsed.profiles };
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
169
|
+
console.warn(`[MCP-Memory-Keeper] Failed to load config file at ${configPath}: ${message}. Ignoring file.`);
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/** Validate tool names against ALL_TOOL_NAMES, returning unknown names */
|
|
174
|
+
function validateToolNames(tools) {
|
|
175
|
+
return tools.filter(name => !exports.ALL_TOOL_NAMES_SET.has(name));
|
|
176
|
+
}
|
|
177
|
+
/** Resolve the active profile based on env var and config file */
|
|
178
|
+
function resolveActiveProfile(configPath) {
|
|
179
|
+
const warnings = [];
|
|
180
|
+
let profileName = (process.env.TOOL_PROFILE || '').trim();
|
|
181
|
+
const hasEnvVar = profileName.length > 0;
|
|
182
|
+
if (!hasEnvVar) {
|
|
183
|
+
profileName = 'full';
|
|
184
|
+
}
|
|
185
|
+
const config = loadConfigFile(configPath);
|
|
186
|
+
let toolList;
|
|
187
|
+
let source = 'default';
|
|
188
|
+
// Resolution precedence: config file > built-in defaults
|
|
189
|
+
if (config && config.profiles[profileName] !== undefined) {
|
|
190
|
+
const candidate = config.profiles[profileName];
|
|
191
|
+
// Validate that the profile value is actually an array of strings
|
|
192
|
+
if (!Array.isArray(candidate) || !candidate.every(item => typeof item === 'string')) {
|
|
193
|
+
warnings.push(`Profile "${profileName}" in config file is not a valid array of strings. Falling back to built-in default.`);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
toolList = candidate;
|
|
197
|
+
source = hasEnvVar ? 'env+config' : 'config';
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (toolList === undefined && exports.DEFAULT_PROFILES[profileName] !== undefined) {
|
|
201
|
+
toolList = [...exports.DEFAULT_PROFILES[profileName]];
|
|
202
|
+
source = hasEnvVar ? 'env+builtin' : 'default';
|
|
203
|
+
}
|
|
204
|
+
if (toolList === undefined) {
|
|
205
|
+
// Profile not found anywhere
|
|
206
|
+
const allProfiles = {
|
|
207
|
+
...exports.DEFAULT_PROFILES,
|
|
208
|
+
...(config ? config.profiles : {}),
|
|
209
|
+
};
|
|
210
|
+
const profileList = Object.entries(allProfiles)
|
|
211
|
+
.map(([name, tools]) => `${name}(${Array.isArray(tools) ? tools.length : '?'})`)
|
|
212
|
+
.join(', ');
|
|
213
|
+
warnings.push(`Unknown TOOL_PROFILE "${profileName}". Available profiles: ${profileList}. Using "full".`);
|
|
214
|
+
profileName = 'full';
|
|
215
|
+
toolList = [...exports.DEFAULT_PROFILES.full];
|
|
216
|
+
source = 'default';
|
|
217
|
+
}
|
|
218
|
+
// Validate tool names
|
|
219
|
+
const unknownNames = validateToolNames(toolList);
|
|
220
|
+
if (unknownNames.length > 0) {
|
|
221
|
+
warnings.push(`Unknown tool names in profile "${profileName}": ${unknownNames.join(', ')}. These will be ignored.`);
|
|
222
|
+
}
|
|
223
|
+
// Filter to only valid tools
|
|
224
|
+
const validTools = toolList.filter(name => exports.ALL_TOOL_NAMES_SET.has(name));
|
|
225
|
+
// Guard against empty profile
|
|
226
|
+
if (validTools.length === 0) {
|
|
227
|
+
warnings.push(`Profile "${profileName}" has no valid tools after filtering. Using "full".`);
|
|
228
|
+
profileName = 'full';
|
|
229
|
+
return {
|
|
230
|
+
profileName,
|
|
231
|
+
tools: new Set(exports.ALL_TOOL_NAMES),
|
|
232
|
+
source: 'default',
|
|
233
|
+
warnings,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
profileName,
|
|
238
|
+
tools: new Set(validTools),
|
|
239
|
+
source,
|
|
240
|
+
warnings,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Place this file at ~/.mcp-memory-keeper/config.json. Built-in profiles (minimal, standard, full) are available without configuration. Define custom profiles or override built-ins here.",
|
|
3
|
+
"profiles": {
|
|
4
|
+
"my_custom_workflow": [
|
|
5
|
+
"context_session_start",
|
|
6
|
+
"context_save",
|
|
7
|
+
"context_get",
|
|
8
|
+
"context_search",
|
|
9
|
+
"context_status",
|
|
10
|
+
"context_checkpoint",
|
|
11
|
+
"context_restore_checkpoint",
|
|
12
|
+
"context_diff",
|
|
13
|
+
"context_timeline",
|
|
14
|
+
"context_batch_save"
|
|
15
|
+
],
|
|
16
|
+
"git_focused": [
|
|
17
|
+
"context_session_start",
|
|
18
|
+
"context_session_list",
|
|
19
|
+
"context_set_project_dir",
|
|
20
|
+
"context_save",
|
|
21
|
+
"context_get",
|
|
22
|
+
"context_search",
|
|
23
|
+
"context_status",
|
|
24
|
+
"context_checkpoint",
|
|
25
|
+
"context_restore_checkpoint",
|
|
26
|
+
"context_git_commit",
|
|
27
|
+
"context_diff",
|
|
28
|
+
"context_export"
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Smart Project Directory Management
|
|
2
|
+
|
|
3
|
+
As of version 0.8.3, MCP Memory Keeper provides intelligent assistance for setting up project directories to enable git tracking. This enhancement helps users configure their project directory correctly, especially when starting Claude Code from parent directories.
|
|
4
|
+
|
|
5
|
+
## How it Works
|
|
6
|
+
|
|
7
|
+
When you start a new session without providing a `projectDir`, the server will:
|
|
8
|
+
|
|
9
|
+
1. Check if the current directory has a git repository
|
|
10
|
+
2. Scan subdirectories for git repositories
|
|
11
|
+
3. Provide smart suggestions for setting up the project directory
|
|
12
|
+
4. Prompt for project directory when git operations are attempted
|
|
13
|
+
|
|
14
|
+
## Examples
|
|
15
|
+
|
|
16
|
+
### Starting from Parent Directory
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// If you're in ~/workspace and your project is in ~/workspace/my-project
|
|
20
|
+
await mcp.call('context_session_start', {
|
|
21
|
+
name: 'My Feature Work',
|
|
22
|
+
description: 'Working on the new feature',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Response will show:
|
|
26
|
+
// Started new session: <session-id>
|
|
27
|
+
// Name: My Feature Work
|
|
28
|
+
// Git branch: unknown
|
|
29
|
+
//
|
|
30
|
+
// 💡 Found git repositories in: my-project, another-project
|
|
31
|
+
// To enable git tracking, start a session with your project directory:
|
|
32
|
+
// context_session_start({ name: "My Feature Work", projectDir: "/Users/you/workspace/my-project" })
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Explicit Directory (Override)
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// You can still explicitly set a different directory if needed
|
|
39
|
+
await mcp.call('context_session_start', {
|
|
40
|
+
name: 'Another Project',
|
|
41
|
+
projectDir: '/path/to/different/project',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Response will show:
|
|
45
|
+
// Started new session: <session-id>
|
|
46
|
+
// Name: Another Project
|
|
47
|
+
// Working directory: /path/to/different/project (explicitly set)
|
|
48
|
+
// Git branch: develop
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Non-Git Directory
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// If the directory is not a git repository
|
|
55
|
+
await mcp.call('context_session_start', {
|
|
56
|
+
name: 'Non-Git Project',
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Response will show:
|
|
60
|
+
// Started new session: <session-id>
|
|
61
|
+
// Name: Non-Git Project
|
|
62
|
+
// Working directory: /path/to/current/directory (auto-detected)
|
|
63
|
+
// Git: No repository found in working directory
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Git Operations Without Project Directory
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// If you try to commit without setting project directory
|
|
70
|
+
await mcp.call('context_git_commit', {
|
|
71
|
+
message: 'feat: Add new feature',
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Response will show:
|
|
75
|
+
// ⚠️ No project directory set for git tracking!
|
|
76
|
+
//
|
|
77
|
+
// To enable git tracking for your project, use one of these methods:
|
|
78
|
+
//
|
|
79
|
+
// 1. For the current session:
|
|
80
|
+
// context_set_project_dir({ projectDir: "/path/to/your/project" })
|
|
81
|
+
//
|
|
82
|
+
// 2. When starting a new session:
|
|
83
|
+
// context_session_start({ name: "My Session", projectDir: "/path/to/your/project" })
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Benefits
|
|
87
|
+
|
|
88
|
+
1. **Smart Suggestions**: Automatically detects git repositories and suggests the right paths
|
|
89
|
+
2. **Clear Guidance**: Provides helpful messages when project directory is needed
|
|
90
|
+
3. **Prevents Mistakes**: Avoids using wrong directories when started from parent folders
|
|
91
|
+
4. **Context Preservation**: The working directory is saved with the session for future reference
|
|
92
|
+
5. **Backward Compatible**: Existing code that provides `projectDir` continues to work as before
|
|
93
|
+
|
|
94
|
+
## Database Schema
|
|
95
|
+
|
|
96
|
+
The sessions table now includes a `working_directory` column to persist this information:
|
|
97
|
+
|
|
98
|
+
```sql
|
|
99
|
+
CREATE TABLE sessions (
|
|
100
|
+
id TEXT PRIMARY KEY,
|
|
101
|
+
name TEXT,
|
|
102
|
+
description TEXT,
|
|
103
|
+
branch TEXT,
|
|
104
|
+
working_directory TEXT, -- New column
|
|
105
|
+
parent_id TEXT,
|
|
106
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
107
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
108
|
+
FOREIGN KEY (parent_id) REFERENCES sessions(id)
|
|
109
|
+
);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Migration Support
|
|
113
|
+
|
|
114
|
+
For existing databases, the schema is automatically updated when the server starts. The migration checks if the `working_directory` column exists and adds it if missing.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-memory-keeper",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "MCP server for persistent context management in AI coding assistants",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -45,19 +45,20 @@
|
|
|
45
45
|
"files": [
|
|
46
46
|
"dist/**/*",
|
|
47
47
|
"bin/**/*",
|
|
48
|
+
"examples/**/*",
|
|
48
49
|
"README.md",
|
|
49
50
|
"LICENSE",
|
|
50
51
|
"CHANGELOG.md"
|
|
51
52
|
],
|
|
52
53
|
"engines": {
|
|
53
|
-
"node": ">=
|
|
54
|
+
"node": ">=20.0.0"
|
|
54
55
|
},
|
|
55
56
|
"publishConfig": {
|
|
56
57
|
"access": "public"
|
|
57
58
|
},
|
|
58
59
|
"dependencies": {
|
|
59
60
|
"@modelcontextprotocol/sdk": "^1.12.3",
|
|
60
|
-
"better-sqlite3": "^
|
|
61
|
+
"better-sqlite3": "^12.1.0",
|
|
61
62
|
"simple-git": "^3.28.0",
|
|
62
63
|
"uuid": "^11.1.0"
|
|
63
64
|
},
|