cf-memory-mcp 3.8.5 โ 3.8.7
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/README.md +95 -669
- package/bin/cf-memory-mcp-indexer.js +125 -37
- package/bin/cf-memory-mcp.js +1213 -65
- package/package.json +15 -53
|
@@ -25,20 +25,51 @@ const { URL } = require('url');
|
|
|
25
25
|
const os = require('os');
|
|
26
26
|
const process = require('process');
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
// Resolve API URL, overriding the dead workers.dev URL if it's still in the
|
|
29
|
+
// shell environment from before the memcp.ai migration.
|
|
30
|
+
function _resolveApiUrl() {
|
|
31
|
+
const raw = process.env.CF_MEMORY_API_URL || process.env.CF_MEMORY_BASE_URL;
|
|
32
|
+
if (!raw || raw.includes('workers.dev')) {
|
|
33
|
+
return 'https://memcp.ai';
|
|
34
|
+
}
|
|
35
|
+
return raw;
|
|
36
|
+
}
|
|
37
|
+
const API_URL = _resolveApiUrl();
|
|
29
38
|
const API_KEY = process.env.CF_MEMORY_API_KEY;
|
|
30
39
|
const CACHE_DIR = path.join(os.homedir(), '.cache', 'cf-memory-indexer');
|
|
31
40
|
|
|
32
41
|
// File patterns to include/exclude
|
|
42
|
+
// Mirrors src-simplified/utils/index.ts DEFAULT_INCLUDE_EXTENSIONS (110+ languages)
|
|
33
43
|
const DEFAULT_INCLUDE = [
|
|
34
|
-
|
|
35
|
-
'**/*.
|
|
36
|
-
'**/*.
|
|
44
|
+
// Mainstream
|
|
45
|
+
'**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.mjs', '**/*.cjs',
|
|
46
|
+
'**/*.py', '**/*.rb', '**/*.go', '**/*.rs', '**/*.java', '**/*.kt', '**/*.scala',
|
|
47
|
+
'**/*.cs', '**/*.cpp', '**/*.c', '**/*.h', '**/*.hpp', '**/*.swift', '**/*.php',
|
|
48
|
+
// Functional & systems
|
|
49
|
+
'**/*.lua', '**/*.ex', '**/*.exs', '**/*.hs', '**/*.lhs',
|
|
50
|
+
'**/*.clj', '**/*.cljs', '**/*.cljc', '**/*.jl', '**/*.elm', '**/*.purs',
|
|
51
|
+
'**/*.dart', '**/*.zig', '**/*.nim', '**/*.cr', '**/*.erl', '**/*.hrl',
|
|
52
|
+
'**/*.ml', '**/*.fs', '**/*.fsi',
|
|
53
|
+
// Web & data
|
|
54
|
+
'**/*.html', '**/*.css', '**/*.scss', '**/*.vue', '**/*.svelte', '**/*.astro',
|
|
55
|
+
'**/*.json', '**/*.yaml', '**/*.yml', '**/*.toml',
|
|
56
|
+
'**/*.md', '**/*.mdx',
|
|
57
|
+
// Infra & build
|
|
58
|
+
'**/*.tf', '**/*.hcl', '**/*.sql', '**/*.sh', '**/*.bash',
|
|
59
|
+
'**/*.proto', '**/*.graphql', '**/*.gql',
|
|
60
|
+
// Shaders & GPU
|
|
61
|
+
'**/*.glsl', '**/*.hlsl', '**/*.wgsl', '**/*.metal', '**/*.cu', '**/*.cuh',
|
|
37
62
|
];
|
|
63
|
+
|
|
38
64
|
const DEFAULT_EXCLUDE = [
|
|
39
65
|
'**/node_modules/**', '**/.git/**', '**/dist/**',
|
|
40
|
-
'**/build/**', '**/.next/**', '
|
|
41
|
-
'
|
|
66
|
+
'**/build/**', '**/out/**', '**/.next/**', '**/.nuxt/**',
|
|
67
|
+
'**/coverage/**', '**/__pycache__/**', '**/venv/**', '**/.venv/**',
|
|
68
|
+
'**/.wrangler/**', '**/.turbo/**', '**/.cache/**',
|
|
69
|
+
'**/.history/**', '**/.vscode/**', '**/.idea/**', '**/.vs/**',
|
|
70
|
+
'**/.augment/**', '**/.kiro/**', '**/.intent/**', '**/.husky/**', '**/.claude/**',
|
|
71
|
+
'**/*.min.js', '**/*.min.css', '**/*.map', '**/*.d.ts',
|
|
72
|
+
'**/package-lock.json', '**/yarn.lock', '**/pnpm-lock.yaml',
|
|
42
73
|
];
|
|
43
74
|
|
|
44
75
|
class IncrementalIndexer {
|
|
@@ -205,6 +236,16 @@ class IncrementalIndexer {
|
|
|
205
236
|
console.log(`๐ค Uploading ${changedFiles.length} files...`);
|
|
206
237
|
await this.uploadFiles(changedFiles);
|
|
207
238
|
}
|
|
239
|
+
|
|
240
|
+
if (this.cache.projectId && deletedFiles.length > 0) {
|
|
241
|
+
console.log(`๐งน Cleaning up ${deletedFiles.length} deleted files...`);
|
|
242
|
+
await this.cleanupDeletedFiles(deletedFiles);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (this.cache.projectId && (changedFiles.length > 0 || deletedFiles.length > 0)) {
|
|
246
|
+
console.log('๐ Finalizing project stats...');
|
|
247
|
+
await this.finalizeProject();
|
|
248
|
+
}
|
|
208
249
|
|
|
209
250
|
// Update cache
|
|
210
251
|
for (const file of changedFiles) {
|
|
@@ -238,59 +279,101 @@ class IncrementalIndexer {
|
|
|
238
279
|
}
|
|
239
280
|
|
|
240
281
|
async uploadBatch(files) {
|
|
241
|
-
|
|
242
|
-
const
|
|
282
|
+
if (!this.cache.projectId) {
|
|
283
|
+
const initResult = await this.callMcpTool('index_project', {
|
|
243
284
|
project_path: this.projectPath,
|
|
244
|
-
project_name: this.projectName
|
|
245
|
-
files: files.map(f => ({
|
|
246
|
-
path: f.path,
|
|
247
|
-
content: f.content
|
|
248
|
-
}))
|
|
285
|
+
project_name: this.projectName
|
|
249
286
|
});
|
|
250
|
-
|
|
251
|
-
|
|
287
|
+
const initContent = this.parseToolTextResult(initResult);
|
|
288
|
+
|
|
289
|
+
if (!initContent.project_id) {
|
|
290
|
+
throw new Error('Project initialization did not return project_id');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
this.cache.projectId = initContent.project_id;
|
|
294
|
+
this.saveCache();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return this.postJson(`/api/projects/${encodeURIComponent(this.cache.projectId)}/files/batch`, {
|
|
298
|
+
files: files.map(f => ({
|
|
299
|
+
file_path: f.path,
|
|
300
|
+
content: f.content
|
|
301
|
+
}))
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async callMcpTool(name, args) {
|
|
306
|
+
return this.postJson('/mcp', {
|
|
307
|
+
jsonrpc: '2.0',
|
|
308
|
+
id: `idx-${Date.now()}`,
|
|
309
|
+
method: 'tools/call',
|
|
310
|
+
params: {
|
|
311
|
+
name,
|
|
312
|
+
arguments: args
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async cleanupDeletedFiles(filePaths) {
|
|
318
|
+
return this.postJson(`/api/projects/${encodeURIComponent(this.cache.projectId)}/files/cleanup`, {
|
|
319
|
+
file_paths: filePaths
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async finalizeProject() {
|
|
324
|
+
return this.postJson(`/api/projects/${encodeURIComponent(this.cache.projectId)}/finalize`, {});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
parseToolTextResult(result) {
|
|
328
|
+
const text = result?.result?.content?.[0]?.text;
|
|
329
|
+
if (!text) {
|
|
330
|
+
throw new Error('Tool response did not contain text content');
|
|
331
|
+
}
|
|
332
|
+
return JSON.parse(text);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async postJson(route, payload) {
|
|
336
|
+
return new Promise((resolve, reject) => {
|
|
337
|
+
const body = JSON.stringify(payload);
|
|
338
|
+
const url = new URL(`${API_URL}${route}`);
|
|
252
339
|
const options = {
|
|
253
340
|
hostname: url.hostname,
|
|
254
|
-
|
|
341
|
+
port: url.port || 443,
|
|
342
|
+
path: url.pathname + url.search,
|
|
255
343
|
method: 'POST',
|
|
256
344
|
headers: {
|
|
257
345
|
'Content-Type': 'application/json',
|
|
258
346
|
'Authorization': `Bearer ${API_KEY}`,
|
|
259
|
-
'
|
|
347
|
+
'X-API-Key': API_KEY,
|
|
348
|
+
'Content-Length': Buffer.byteLength(body)
|
|
260
349
|
},
|
|
261
350
|
timeout: 120000
|
|
262
351
|
};
|
|
263
|
-
|
|
352
|
+
|
|
264
353
|
const req = https.request(options, (res) => {
|
|
265
354
|
let data = '';
|
|
266
355
|
res.on('data', chunk => data += chunk);
|
|
267
356
|
res.on('end', () => {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
if (content.project_id) {
|
|
274
|
-
this.cache.projectId = content.project_id;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
resolve(result);
|
|
278
|
-
} catch (err) {
|
|
279
|
-
reject(new Error(`Invalid response: ${err.message}`));
|
|
357
|
+
try {
|
|
358
|
+
const parsed = JSON.parse(data || '{}');
|
|
359
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
360
|
+
resolve(parsed);
|
|
361
|
+
return;
|
|
280
362
|
}
|
|
281
|
-
} else {
|
|
282
363
|
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
|
|
364
|
+
} catch (err) {
|
|
365
|
+
reject(new Error(`Invalid response: ${err.message}`));
|
|
283
366
|
}
|
|
284
367
|
});
|
|
285
368
|
});
|
|
286
|
-
|
|
369
|
+
|
|
287
370
|
req.on('error', reject);
|
|
288
371
|
req.on('timeout', () => {
|
|
289
372
|
req.destroy();
|
|
290
373
|
reject(new Error('Request timeout'));
|
|
291
374
|
});
|
|
292
|
-
|
|
293
|
-
req.write(
|
|
375
|
+
|
|
376
|
+
req.write(body);
|
|
294
377
|
req.end();
|
|
295
378
|
});
|
|
296
379
|
}
|
|
@@ -350,15 +433,20 @@ async function main() {
|
|
|
350
433
|
|
|
351
434
|
// Detect command from how the script was called (cf-memory-index vs cf-memory-watch)
|
|
352
435
|
const scriptName = path.basename(process.argv[1]);
|
|
353
|
-
let command =
|
|
354
|
-
let projectPath =
|
|
436
|
+
let command = 'index';
|
|
437
|
+
let projectPath = '.';
|
|
355
438
|
|
|
356
|
-
if (
|
|
439
|
+
if (args[0] === 'watch' || args[0] === 'index') {
|
|
440
|
+
command = args[0];
|
|
441
|
+
projectPath = args[1] || '.';
|
|
442
|
+
} else if (scriptName.includes('watch')) {
|
|
357
443
|
command = 'watch';
|
|
358
444
|
projectPath = args[0] || '.';
|
|
359
445
|
} else if (scriptName.includes('index')) {
|
|
360
446
|
command = 'index';
|
|
361
447
|
projectPath = args[0] || '.';
|
|
448
|
+
} else {
|
|
449
|
+
projectPath = args[0] || '.';
|
|
362
450
|
}
|
|
363
451
|
|
|
364
452
|
const options = {
|