@memoraone/mcp 0.1.14 → 0.1.17
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/dist/cli.cjs +616 -110
- package/dist/index.cjs +530 -77
- package/package.json +8 -8
package/dist/cli.cjs
CHANGED
|
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
9
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
10
|
+
};
|
|
8
11
|
var __copyProps = (to, from, except, desc) => {
|
|
9
12
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
13
|
for (let key of __getOwnPropNames(from))
|
|
@@ -22,6 +25,44 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
22
25
|
mod
|
|
23
26
|
));
|
|
24
27
|
|
|
28
|
+
// package.json
|
|
29
|
+
var require_package = __commonJS({
|
|
30
|
+
"package.json"(exports2, module2) {
|
|
31
|
+
module2.exports = {
|
|
32
|
+
name: "@memoraone/mcp",
|
|
33
|
+
version: "0.1.17",
|
|
34
|
+
type: "module",
|
|
35
|
+
main: "dist/index.cjs",
|
|
36
|
+
bin: {
|
|
37
|
+
"memoraone-mcp": "dist-bin/memoraone-mcp.cjs"
|
|
38
|
+
},
|
|
39
|
+
files: [
|
|
40
|
+
"dist",
|
|
41
|
+
"dist-bin"
|
|
42
|
+
],
|
|
43
|
+
publishConfig: {
|
|
44
|
+
access: "public"
|
|
45
|
+
},
|
|
46
|
+
scripts: {
|
|
47
|
+
build: "tsup && node scripts/writeBinWrapper.cjs",
|
|
48
|
+
prepublishOnly: "pnpm run build",
|
|
49
|
+
dev: "tsx src/index.ts",
|
|
50
|
+
lint: "eslint ."
|
|
51
|
+
},
|
|
52
|
+
dependencies: {
|
|
53
|
+
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
54
|
+
dotenv: "^16.4.5",
|
|
55
|
+
zod: "^4.0.0"
|
|
56
|
+
},
|
|
57
|
+
devDependencies: {
|
|
58
|
+
tsx: "^4.21.0",
|
|
59
|
+
tsup: "^8.5.1",
|
|
60
|
+
typescript: "^5.9.2"
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
25
66
|
// src/index.ts
|
|
26
67
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
27
68
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
@@ -114,6 +155,40 @@ var config = {
|
|
|
114
155
|
};
|
|
115
156
|
|
|
116
157
|
// src/client/memoraClient.ts
|
|
158
|
+
var crypto2 = __toESM(require("crypto"), 1);
|
|
159
|
+
|
|
160
|
+
// src/runContext.ts
|
|
161
|
+
var crypto = __toESM(require("crypto"), 1);
|
|
162
|
+
var currentRunId = null;
|
|
163
|
+
var currentProjectId = null;
|
|
164
|
+
function setCurrentRunId(id) {
|
|
165
|
+
currentRunId = id;
|
|
166
|
+
}
|
|
167
|
+
function getCurrentProjectId() {
|
|
168
|
+
return currentProjectId;
|
|
169
|
+
}
|
|
170
|
+
function setCurrentProjectId(id) {
|
|
171
|
+
currentProjectId = id;
|
|
172
|
+
}
|
|
173
|
+
function resolveRunId(passed) {
|
|
174
|
+
if (passed) {
|
|
175
|
+
return passed;
|
|
176
|
+
}
|
|
177
|
+
return currentRunId;
|
|
178
|
+
}
|
|
179
|
+
function generateRunId() {
|
|
180
|
+
return crypto.randomBytes(16).toString("hex");
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/client/memoraClient.ts
|
|
184
|
+
var parseBooleanFlag2 = (value) => {
|
|
185
|
+
if (!value) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
const normalized = value.trim().toLowerCase();
|
|
189
|
+
return ["1", "true", "yes", "on"].includes(normalized);
|
|
190
|
+
};
|
|
191
|
+
var debugEnabled = parseBooleanFlag2(process.env.MEMORAONE_DEV_MODE);
|
|
117
192
|
async function requestJson(url, method, headers, body) {
|
|
118
193
|
const res = await fetch(url, {
|
|
119
194
|
method,
|
|
@@ -137,9 +212,21 @@ var MemoraOneHttpError = class extends Error {
|
|
|
137
212
|
}
|
|
138
213
|
};
|
|
139
214
|
var MemoraClient = class {
|
|
140
|
-
constructor(cfg) {
|
|
215
|
+
constructor(cfg, projectKey) {
|
|
216
|
+
if (!projectKey?.trim()) {
|
|
217
|
+
throw new Error("[memoraone-mcp] Invalid projectKey for MemoraClient");
|
|
218
|
+
}
|
|
141
219
|
this.baseUrl = cfg.apiUrl;
|
|
142
220
|
this.apiKey = cfg.apiKey;
|
|
221
|
+
this.projectKey = projectKey;
|
|
222
|
+
}
|
|
223
|
+
resolveProjectKey() {
|
|
224
|
+
const selected = getCurrentProjectId();
|
|
225
|
+
const projectKey = (selected ?? this.projectKey)?.trim();
|
|
226
|
+
if (!projectKey) {
|
|
227
|
+
throw new Error("Missing projectKey: select a project first");
|
|
228
|
+
}
|
|
229
|
+
return projectKey;
|
|
143
230
|
}
|
|
144
231
|
buildHeaders(options) {
|
|
145
232
|
return {
|
|
@@ -148,30 +235,66 @@ var MemoraClient = class {
|
|
|
148
235
|
...options?.headers ?? {}
|
|
149
236
|
};
|
|
150
237
|
}
|
|
151
|
-
async post(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
`
|
|
238
|
+
async post(path5, body, options) {
|
|
239
|
+
console.error(
|
|
240
|
+
`[memoraone-mcp][info] MemoraClient.post ENTER path=${path5}`
|
|
241
|
+
);
|
|
242
|
+
const nonce = crypto2.randomBytes(8).toString("hex");
|
|
243
|
+
const url = `${this.baseUrl}${path5.startsWith("/") ? path5 : `/${path5}`}`;
|
|
244
|
+
this.resolveProjectKey();
|
|
245
|
+
console.error(
|
|
246
|
+
`[memoraone-mcp][info] requestJson nonce=${nonce} stage=before_fetch method=POST url=${url}`
|
|
247
|
+
);
|
|
248
|
+
const res = await requestJson(url, "POST", this.buildHeaders(options), body);
|
|
249
|
+
if (debugEnabled && options?.log !== false) {
|
|
250
|
+
const snippet = res.text.length > 200 ? `${res.text.slice(0, 200)}...` : res.text;
|
|
251
|
+
console.error(
|
|
252
|
+
`[memoraone-mcp][info] requestJson nonce=${nonce} stage=before_response_log`
|
|
253
|
+
);
|
|
254
|
+
const line = `[memoraone-mcp][info] http response method=POST url=${url} status=${res.status} body=${snippet}`;
|
|
255
|
+
console.error(line);
|
|
256
|
+
console.error(
|
|
257
|
+
`[memoraone-mcp][info] requestJson nonce=${nonce} stage=after_response_log`
|
|
157
258
|
);
|
|
158
259
|
}
|
|
159
|
-
const res = await requestJson(url, "POST", this.buildHeaders(options), body);
|
|
160
260
|
if (!res.ok) {
|
|
261
|
+
const snippet = res.text.length > 200 ? `${res.text.slice(0, 200)}...` : res.text;
|
|
262
|
+
process.stderr.write(
|
|
263
|
+
`[memoraone-mcp][error] http error method=POST url=${url} status=${res.status} body=${snippet}
|
|
264
|
+
`
|
|
265
|
+
);
|
|
161
266
|
throw new MemoraOneHttpError(res.status, res.statusText, res.text);
|
|
162
267
|
}
|
|
268
|
+
console.error(
|
|
269
|
+
`[memoraone-mcp][info] MemoraClient.post EXIT path=${path5}`
|
|
270
|
+
);
|
|
163
271
|
return res.text ? JSON.parse(res.text) : null;
|
|
164
272
|
}
|
|
165
|
-
async get(
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
`
|
|
273
|
+
async get(path5, options) {
|
|
274
|
+
const nonce = crypto2.randomBytes(8).toString("hex");
|
|
275
|
+
const url = `${this.baseUrl}${path5.startsWith("/") ? path5 : `/${path5}`}`;
|
|
276
|
+
this.resolveProjectKey();
|
|
277
|
+
console.error(
|
|
278
|
+
`[memoraone-mcp][info] requestJson nonce=${nonce} stage=before_fetch method=GET url=${url}`
|
|
279
|
+
);
|
|
280
|
+
const res = await requestJson(url, "GET", this.buildHeaders(options));
|
|
281
|
+
if (debugEnabled && options?.log !== false) {
|
|
282
|
+
const snippet = res.text.length > 200 ? `${res.text.slice(0, 200)}...` : res.text;
|
|
283
|
+
console.error(
|
|
284
|
+
`[memoraone-mcp][info] requestJson nonce=${nonce} stage=before_response_log`
|
|
285
|
+
);
|
|
286
|
+
const line = `[memoraone-mcp][info] http response method=GET url=${url} status=${res.status} body=${snippet}`;
|
|
287
|
+
console.error(line);
|
|
288
|
+
console.error(
|
|
289
|
+
`[memoraone-mcp][info] requestJson nonce=${nonce} stage=after_response_log`
|
|
171
290
|
);
|
|
172
291
|
}
|
|
173
|
-
const res = await requestJson(url, "GET", this.buildHeaders(options));
|
|
174
292
|
if (!res.ok) {
|
|
293
|
+
const snippet = res.text.length > 200 ? `${res.text.slice(0, 200)}...` : res.text;
|
|
294
|
+
process.stderr.write(
|
|
295
|
+
`[memoraone-mcp][error] http error method=GET url=${url} status=${res.status} body=${snippet}
|
|
296
|
+
`
|
|
297
|
+
);
|
|
175
298
|
throw new MemoraOneHttpError(res.status, res.statusText, res.text);
|
|
176
299
|
}
|
|
177
300
|
return res.text ? JSON.parse(res.text) : null;
|
|
@@ -179,11 +302,339 @@ var MemoraClient = class {
|
|
|
179
302
|
};
|
|
180
303
|
var memoraClient_default = MemoraClient;
|
|
181
304
|
|
|
305
|
+
// src/repoFingerprint.ts
|
|
306
|
+
var fs2 = __toESM(require("fs"), 1);
|
|
307
|
+
var path2 = __toESM(require("path"), 1);
|
|
308
|
+
var crypto3 = __toESM(require("crypto"), 1);
|
|
309
|
+
var parseBooleanFlag3 = (value) => {
|
|
310
|
+
if (!value) {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
const normalized = value.trim().toLowerCase();
|
|
314
|
+
return ["1", "true", "yes", "on"].includes(normalized);
|
|
315
|
+
};
|
|
316
|
+
var debugEnabled2 = parseBooleanFlag3(process.env.MEMORAONE_DEV_MODE);
|
|
317
|
+
var debugLog = (message) => {
|
|
318
|
+
if (!debugEnabled2) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
process.stderr.write(`[memoraone-mcp][debug] ${message}
|
|
322
|
+
`);
|
|
323
|
+
};
|
|
324
|
+
var normalizeRemoteUrl = (remoteUrl) => {
|
|
325
|
+
let normalized = remoteUrl.trim();
|
|
326
|
+
normalized = normalized.replace(/^[a-z]+:\/\//i, "");
|
|
327
|
+
normalized = normalized.replace(/^git@([^:]+):/i, "$1/");
|
|
328
|
+
normalized = normalized.replace(/\.git$/i, "");
|
|
329
|
+
normalized = normalized.replace(/\/+$/, "");
|
|
330
|
+
return normalized.toLowerCase();
|
|
331
|
+
};
|
|
332
|
+
var sha256 = (value) => {
|
|
333
|
+
return crypto3.createHash("sha256").update(value).digest("hex");
|
|
334
|
+
};
|
|
335
|
+
var resolveGitDir = (gitPath) => {
|
|
336
|
+
try {
|
|
337
|
+
const stat = fs2.statSync(gitPath);
|
|
338
|
+
if (stat.isDirectory()) {
|
|
339
|
+
return gitPath;
|
|
340
|
+
}
|
|
341
|
+
if (stat.isFile()) {
|
|
342
|
+
const content = fs2.readFileSync(gitPath, "utf8");
|
|
343
|
+
const match = content.match(/^gitdir:\s*(.+)$/m);
|
|
344
|
+
if (match) {
|
|
345
|
+
const gitDir = match[1].trim();
|
|
346
|
+
return path2.resolve(path2.dirname(gitPath), gitDir);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
} catch {
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
return null;
|
|
353
|
+
};
|
|
354
|
+
var findGitRoot = (start) => {
|
|
355
|
+
let current = path2.resolve(start);
|
|
356
|
+
while (true) {
|
|
357
|
+
const gitPath = path2.join(current, ".git");
|
|
358
|
+
if (fs2.existsSync(gitPath)) {
|
|
359
|
+
const gitDir = resolveGitDir(gitPath);
|
|
360
|
+
if (gitDir) {
|
|
361
|
+
return { gitRoot: current, gitDir };
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
const parent = path2.dirname(current);
|
|
365
|
+
if (parent === current) {
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
current = parent;
|
|
369
|
+
}
|
|
370
|
+
return null;
|
|
371
|
+
};
|
|
372
|
+
var readOriginRemote = (gitDir) => {
|
|
373
|
+
const configPath = path2.join(gitDir, "config");
|
|
374
|
+
try {
|
|
375
|
+
const content = fs2.readFileSync(configPath, "utf8");
|
|
376
|
+
const lines = content.split(/\r?\n/);
|
|
377
|
+
let inOrigin = false;
|
|
378
|
+
for (const line of lines) {
|
|
379
|
+
const sectionMatch = line.match(/^\s*\[(.+)]\s*$/);
|
|
380
|
+
if (sectionMatch) {
|
|
381
|
+
inOrigin = sectionMatch[1].trim() === 'remote "origin"';
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
if (inOrigin) {
|
|
385
|
+
const urlMatch = line.match(/^\s*url\s*=\s*(.+)\s*$/);
|
|
386
|
+
if (urlMatch) {
|
|
387
|
+
return urlMatch[1].trim();
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
} catch {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
return null;
|
|
395
|
+
};
|
|
396
|
+
function resolveRepoFingerprint(cwd2) {
|
|
397
|
+
const found = findGitRoot(cwd2);
|
|
398
|
+
if (!found) {
|
|
399
|
+
const fallbackPath = path2.resolve(cwd2);
|
|
400
|
+
const fingerprint2 = sha256(fallbackPath);
|
|
401
|
+
debugLog(`repo fingerprint=${fingerprint2} source=path-fallback`);
|
|
402
|
+
return {
|
|
403
|
+
fingerprint: fingerprint2,
|
|
404
|
+
gitRoot: fallbackPath,
|
|
405
|
+
source: "path-fallback"
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
const { gitRoot, gitDir } = found;
|
|
409
|
+
const remoteUrl = readOriginRemote(gitDir);
|
|
410
|
+
if (remoteUrl) {
|
|
411
|
+
const normalized = normalizeRemoteUrl(remoteUrl);
|
|
412
|
+
const fingerprint2 = sha256(normalized);
|
|
413
|
+
debugLog(`repo fingerprint=${fingerprint2} source=git-remote`);
|
|
414
|
+
return {
|
|
415
|
+
fingerprint: fingerprint2,
|
|
416
|
+
gitRoot,
|
|
417
|
+
remoteUrl,
|
|
418
|
+
source: "git-remote"
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
const fingerprint = sha256(path2.resolve(gitRoot));
|
|
422
|
+
debugLog(`repo fingerprint=${fingerprint} source=path-fallback`);
|
|
423
|
+
return {
|
|
424
|
+
fingerprint,
|
|
425
|
+
gitRoot,
|
|
426
|
+
source: "path-fallback"
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// src/workspaceMap.ts
|
|
431
|
+
var fs3 = __toESM(require("fs/promises"), 1);
|
|
432
|
+
var path3 = __toESM(require("path"), 1);
|
|
433
|
+
var import_node_os = __toESM(require("os"), 1);
|
|
434
|
+
var parseBooleanFlag4 = (value) => {
|
|
435
|
+
if (!value) {
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
const normalized = value.trim().toLowerCase();
|
|
439
|
+
return ["1", "true", "yes", "on"].includes(normalized);
|
|
440
|
+
};
|
|
441
|
+
var debugEnabled3 = parseBooleanFlag4(process.env.MEMORAONE_DEV_MODE);
|
|
442
|
+
var debugLog2 = (message) => {
|
|
443
|
+
if (!debugEnabled3) {
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
process.stderr.write(`[memoraone-mcp][debug] ${message}
|
|
447
|
+
`);
|
|
448
|
+
};
|
|
449
|
+
var fingerprintRegex = /^[0-9a-f]{64}$/i;
|
|
450
|
+
function getWorkspaceMapPath() {
|
|
451
|
+
return path3.join(import_node_os.default.homedir(), ".memoraone", "workspaces.json");
|
|
452
|
+
}
|
|
453
|
+
var ensureWorkspaceDir = async () => {
|
|
454
|
+
const dir = path3.dirname(getWorkspaceMapPath());
|
|
455
|
+
await fs3.mkdir(dir, { recursive: true });
|
|
456
|
+
};
|
|
457
|
+
var validateWorkspaceMap = (map, filePath) => {
|
|
458
|
+
if (!map || typeof map !== "object" || Array.isArray(map)) {
|
|
459
|
+
throw new Error(
|
|
460
|
+
`[memoraone-mcp] Invalid workspace map schema in ${filePath}`
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
for (const [fingerprint, entry] of Object.entries(map)) {
|
|
464
|
+
if (!fingerprintRegex.test(fingerprint)) {
|
|
465
|
+
throw new Error(
|
|
466
|
+
`[memoraone-mcp] Invalid workspace fingerprint in ${filePath}`
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
if (typeof entry === "string") {
|
|
470
|
+
if (!entry.trim()) {
|
|
471
|
+
throw new Error(
|
|
472
|
+
`[memoraone-mcp] Invalid workspace projectKey in ${filePath}`
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
478
|
+
throw new Error(
|
|
479
|
+
`[memoraone-mcp] Invalid workspace projectKey in ${filePath}`
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
const projectKey = entry.projectKey ?? entry.project_id;
|
|
483
|
+
if (!projectKey || !projectKey.trim()) {
|
|
484
|
+
throw new Error(
|
|
485
|
+
`[memoraone-mcp] Invalid workspace projectKey in ${filePath}`
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
const source = entry.source;
|
|
489
|
+
if (source !== void 0 && typeof source !== "string") {
|
|
490
|
+
throw new Error(
|
|
491
|
+
`[memoraone-mcp] Invalid workspace source in ${filePath}`
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
const linkedAt = entry.linked_at;
|
|
495
|
+
if (linkedAt !== void 0 && typeof linkedAt !== "string") {
|
|
496
|
+
throw new Error(
|
|
497
|
+
`[memoraone-mcp] Invalid workspace linked_at in ${filePath}`
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
async function readWorkspaceMap() {
|
|
503
|
+
const filePath = getWorkspaceMapPath();
|
|
504
|
+
try {
|
|
505
|
+
const content = await fs3.readFile(filePath, "utf8");
|
|
506
|
+
const parsed2 = JSON.parse(content);
|
|
507
|
+
validateWorkspaceMap(parsed2, filePath);
|
|
508
|
+
const typed = parsed2;
|
|
509
|
+
let migrated = false;
|
|
510
|
+
const normalized = {};
|
|
511
|
+
for (const [fingerprint, entry] of Object.entries(typed)) {
|
|
512
|
+
if (typeof entry === "string") {
|
|
513
|
+
normalized[fingerprint] = entry;
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
const projectKey = entry.projectKey ?? entry.project_id ?? "";
|
|
517
|
+
if (entry.project_id && !entry.projectKey) {
|
|
518
|
+
migrated = true;
|
|
519
|
+
}
|
|
520
|
+
normalized[fingerprint] = {
|
|
521
|
+
...projectKey ? { projectKey } : {},
|
|
522
|
+
...entry.source ? { source: entry.source } : {},
|
|
523
|
+
...entry.linked_at ? { linked_at: entry.linked_at } : {}
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
if (migrated) {
|
|
527
|
+
await writeWorkspaceMap(normalized);
|
|
528
|
+
}
|
|
529
|
+
debugLog2(
|
|
530
|
+
`workspace map loaded path=${filePath} entries=${Object.keys(normalized).length}`
|
|
531
|
+
);
|
|
532
|
+
return normalized;
|
|
533
|
+
} catch (err) {
|
|
534
|
+
if (err?.code === "ENOENT") {
|
|
535
|
+
const emptyMap = {};
|
|
536
|
+
debugLog2(`workspace map loaded path=${filePath} entries=0`);
|
|
537
|
+
return emptyMap;
|
|
538
|
+
}
|
|
539
|
+
if (err instanceof SyntaxError) {
|
|
540
|
+
throw new Error(
|
|
541
|
+
`[memoraone-mcp] Failed to parse workspace map at ${filePath}`
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
throw err;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
async function writeWorkspaceMap(map) {
|
|
548
|
+
const filePath = getWorkspaceMapPath();
|
|
549
|
+
validateWorkspaceMap(map, filePath);
|
|
550
|
+
await ensureWorkspaceDir();
|
|
551
|
+
const tempPath = `${filePath}.tmp`;
|
|
552
|
+
const content = JSON.stringify(map, null, 2);
|
|
553
|
+
await fs3.writeFile(tempPath, content, "utf8");
|
|
554
|
+
await fs3.rename(tempPath, filePath);
|
|
555
|
+
}
|
|
556
|
+
async function getProjectIdForFingerprint(fingerprint) {
|
|
557
|
+
if (!fingerprintRegex.test(fingerprint)) {
|
|
558
|
+
throw new Error("[memoraone-mcp] Invalid fingerprint");
|
|
559
|
+
}
|
|
560
|
+
const map = await readWorkspaceMap();
|
|
561
|
+
const entry = map[fingerprint];
|
|
562
|
+
if (!entry) {
|
|
563
|
+
return { projectKey: null, source: "unknown" };
|
|
564
|
+
}
|
|
565
|
+
if (typeof entry === "string") {
|
|
566
|
+
return { projectKey: entry, source: "unknown" };
|
|
567
|
+
}
|
|
568
|
+
return {
|
|
569
|
+
projectKey: entry.projectKey ?? entry.project_id ?? null,
|
|
570
|
+
source: entry.source ?? "unknown"
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
async function setProjectIdForFingerprint(args2) {
|
|
574
|
+
const { fingerprint, projectKey, source, linked_at } = args2;
|
|
575
|
+
if (!fingerprintRegex.test(fingerprint)) {
|
|
576
|
+
throw new Error("[memoraone-mcp] Invalid fingerprint");
|
|
577
|
+
}
|
|
578
|
+
if (!projectKey.trim()) {
|
|
579
|
+
throw new Error("[memoraone-mcp] Invalid projectKey");
|
|
580
|
+
}
|
|
581
|
+
const map = await readWorkspaceMap();
|
|
582
|
+
map[fingerprint] = {
|
|
583
|
+
projectKey,
|
|
584
|
+
...source ? { source } : {},
|
|
585
|
+
linked_at: linked_at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
586
|
+
};
|
|
587
|
+
await writeWorkspaceMap(map);
|
|
588
|
+
debugLog2(
|
|
589
|
+
`workspace map set fingerprint=${fingerprint} projectKey=${projectKey}`
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// src/projectBinding.ts
|
|
594
|
+
var fs4 = __toESM(require("fs/promises"), 1);
|
|
595
|
+
var path4 = __toESM(require("path"), 1);
|
|
596
|
+
function readRepoProjectIdPath(gitRoot) {
|
|
597
|
+
return path4.join(gitRoot, ".memoraone-project");
|
|
598
|
+
}
|
|
599
|
+
async function readRepoProjectId(gitRoot) {
|
|
600
|
+
const filePath = readRepoProjectIdPath(gitRoot);
|
|
601
|
+
try {
|
|
602
|
+
const content = await fs4.readFile(filePath, "utf8");
|
|
603
|
+
const value = content.trim();
|
|
604
|
+
return value ? value : null;
|
|
605
|
+
} catch (err) {
|
|
606
|
+
if (err?.code === "ENOENT") {
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
609
|
+
throw err;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
function resolveProjectIdOrThrow(args2) {
|
|
613
|
+
const envProjectKey = args2.envProjectKey?.trim();
|
|
614
|
+
if (envProjectKey) {
|
|
615
|
+
return { projectKey: envProjectKey, source: "env" };
|
|
616
|
+
}
|
|
617
|
+
const repoFileProjectKey = args2.repoFileProjectKey?.trim();
|
|
618
|
+
if (repoFileProjectKey) {
|
|
619
|
+
return { projectKey: repoFileProjectKey, source: "repo-file" };
|
|
620
|
+
}
|
|
621
|
+
const workspaceProjectKey = args2.workspaceProjectKey?.trim();
|
|
622
|
+
if (workspaceProjectKey) {
|
|
623
|
+
return { projectKey: workspaceProjectKey, source: "workspace-map" };
|
|
624
|
+
}
|
|
625
|
+
throw new Error(
|
|
626
|
+
`Repo not linked to a MemoraOne project. Set MEMORAONE_PROJECT_ID or create ${args2.repoFilePath} containing a project key.`
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
|
|
182
630
|
// src/tools/postEvent.ts
|
|
183
631
|
var import_v42 = require("zod/v4");
|
|
184
632
|
var postEventShape = {
|
|
185
633
|
kind: import_v42.z.string().min(1),
|
|
186
|
-
actor: import_v42.z.object({
|
|
634
|
+
actor: import_v42.z.object({
|
|
635
|
+
identifier: import_v42.z.string().min(1),
|
|
636
|
+
id: import_v42.z.string().min(1).optional()
|
|
637
|
+
}),
|
|
187
638
|
content: import_v42.z.record(import_v42.z.string(), import_v42.z.any()),
|
|
188
639
|
metadata: import_v42.z.record(import_v42.z.string(), import_v42.z.any()).optional()
|
|
189
640
|
};
|
|
@@ -256,58 +707,59 @@ var listProjectsShape = {};
|
|
|
256
707
|
// src/tools/setProject.ts
|
|
257
708
|
var import_v48 = require("zod/v4");
|
|
258
709
|
var setProjectShape = {
|
|
259
|
-
|
|
710
|
+
projectKey: import_v48.z.string().min(1).optional(),
|
|
711
|
+
projectId: import_v48.z.string().min(1).optional()
|
|
260
712
|
};
|
|
261
713
|
|
|
262
714
|
// src/tools/handlers/postEvent.ts
|
|
263
715
|
var import_v49 = require("zod/v4");
|
|
264
|
-
|
|
265
|
-
// src/runContext.ts
|
|
266
|
-
var crypto = __toESM(require("crypto"), 1);
|
|
267
|
-
var currentRunId = null;
|
|
268
|
-
var currentProjectId = null;
|
|
269
|
-
function setCurrentRunId(id) {
|
|
270
|
-
currentRunId = id;
|
|
271
|
-
}
|
|
272
|
-
function getCurrentProjectId() {
|
|
273
|
-
return currentProjectId;
|
|
274
|
-
}
|
|
275
|
-
function setCurrentProjectId(id) {
|
|
276
|
-
currentProjectId = id;
|
|
277
|
-
}
|
|
278
|
-
function resolveRunId(passed) {
|
|
279
|
-
if (passed) {
|
|
280
|
-
return passed;
|
|
281
|
-
}
|
|
282
|
-
return currentRunId;
|
|
283
|
-
}
|
|
284
|
-
function generateRunId() {
|
|
285
|
-
return crypto.randomBytes(16).toString("hex");
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// src/tools/handlers/postEvent.ts
|
|
716
|
+
var crypto4 = __toESM(require("crypto"), 1);
|
|
289
717
|
var postEventInputSchema = import_v49.z.object({
|
|
290
718
|
kind: import_v49.z.string().min(1),
|
|
291
|
-
actor: import_v49.z.object({
|
|
719
|
+
actor: import_v49.z.object({
|
|
720
|
+
identifier: import_v49.z.string().min(1),
|
|
721
|
+
id: import_v49.z.string().min(1).optional()
|
|
722
|
+
}),
|
|
292
723
|
content: import_v49.z.record(import_v49.z.string(), import_v49.z.any()),
|
|
293
724
|
metadata: import_v49.z.record(import_v49.z.string(), import_v49.z.any()).optional()
|
|
294
725
|
});
|
|
295
|
-
async function handlePostEvent(client,
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
726
|
+
async function handlePostEvent(client, args2) {
|
|
727
|
+
const nonce = crypto4.randomBytes(8).toString("hex");
|
|
728
|
+
console.error(
|
|
729
|
+
`[memoraone-mcp][debug] tool=memora_post_event toolCallId=unknown nonce=${nonce} stage=before_post`
|
|
730
|
+
);
|
|
731
|
+
const parsed2 = postEventInputSchema.parse(args2 ?? {});
|
|
732
|
+
const projectKey = getCurrentProjectId();
|
|
733
|
+
if (!projectKey) {
|
|
299
734
|
throw new Error("No project selected. Use memora_list_projects and memora_set_project to select a project.");
|
|
300
735
|
}
|
|
736
|
+
const content = parsed2.content ?? {};
|
|
737
|
+
const message = typeof content.message === "string" ? content.message : typeof content.text === "string" ? content.text : JSON.stringify(content);
|
|
301
738
|
const body = {
|
|
302
739
|
kind: parsed2.kind,
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
740
|
+
message,
|
|
741
|
+
projectKey,
|
|
742
|
+
actor: {
|
|
743
|
+
type: "agent",
|
|
744
|
+
identifier: config.agentName,
|
|
745
|
+
...parsed2.actor.id ? { id: parsed2.actor.id } : {}
|
|
746
|
+
},
|
|
747
|
+
...parsed2.metadata ? { metadata: parsed2.metadata } : {}
|
|
309
748
|
};
|
|
310
|
-
|
|
749
|
+
try {
|
|
750
|
+
await client.post("/timeline/events", body);
|
|
751
|
+
console.error(
|
|
752
|
+
`[memoraone-mcp][debug] tool=memora_post_event toolCallId=unknown nonce=${nonce} stage=after_post`
|
|
753
|
+
);
|
|
754
|
+
} catch (err) {
|
|
755
|
+
if (err instanceof MemoraOneHttpError) {
|
|
756
|
+
const bodyText = typeof err.body === "string" ? err.body : JSON.stringify(err.body ?? "");
|
|
757
|
+
throw new Error(
|
|
758
|
+
`memora_post_event failed: ${err.status} ${err.message} ${bodyText}`.trim()
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
throw err;
|
|
762
|
+
}
|
|
311
763
|
return { ok: true, forwarded: true };
|
|
312
764
|
}
|
|
313
765
|
|
|
@@ -324,19 +776,20 @@ var askWithMemoryInputSchema = import_v410.z.object({
|
|
|
324
776
|
function isAskWithMemoryResponse(value) {
|
|
325
777
|
return typeof value === "object" && value !== null && typeof value.answer === "string" && value.answer !== "";
|
|
326
778
|
}
|
|
327
|
-
async function handleAskWithMemory(client,
|
|
328
|
-
const parsed2 = askWithMemoryInputSchema.parse(
|
|
329
|
-
const
|
|
330
|
-
if (!
|
|
779
|
+
async function handleAskWithMemory(client, args2) {
|
|
780
|
+
const parsed2 = askWithMemoryInputSchema.parse(args2 ?? {});
|
|
781
|
+
const projectKey = getCurrentProjectId();
|
|
782
|
+
if (!projectKey) {
|
|
331
783
|
throw new Error("No project selected. Use memora_list_projects and memora_set_project to select a project.");
|
|
332
784
|
}
|
|
333
785
|
const payload = {
|
|
334
|
-
question: parsed2.question
|
|
786
|
+
question: parsed2.question,
|
|
787
|
+
projectKey
|
|
335
788
|
};
|
|
336
789
|
if (parsed2.code_context) {
|
|
337
790
|
payload.code_context = parsed2.code_context;
|
|
338
791
|
}
|
|
339
|
-
const res = await client.post("/agent/ask-with-memory", payload
|
|
792
|
+
const res = await client.post("/agent/ask-with-memory", payload);
|
|
340
793
|
if (!isAskWithMemoryResponse(res)) {
|
|
341
794
|
const err = new Error("Unexpected response from MemoraOne");
|
|
342
795
|
err.status = 502;
|
|
@@ -358,10 +811,10 @@ var logIntentInputSchema = import_v411.z.object({
|
|
|
358
811
|
intent_source: import_v411.z.string().optional().default("cursor_chat"),
|
|
359
812
|
run_id: import_v411.z.string().min(1).optional()
|
|
360
813
|
});
|
|
361
|
-
async function handleLogIntent(client,
|
|
362
|
-
const parsed2 = logIntentInputSchema.parse(
|
|
363
|
-
const
|
|
364
|
-
if (!
|
|
814
|
+
async function handleLogIntent(client, args2) {
|
|
815
|
+
const parsed2 = logIntentInputSchema.parse(args2 ?? {});
|
|
816
|
+
const projectKey = getCurrentProjectId();
|
|
817
|
+
if (!projectKey) {
|
|
365
818
|
throw new Error("No project selected. Use memora_list_projects and memora_set_project to select a project.");
|
|
366
819
|
}
|
|
367
820
|
const intent = parsed2.intent;
|
|
@@ -381,6 +834,7 @@ async function handleLogIntent(client, args) {
|
|
|
381
834
|
concept,
|
|
382
835
|
// MUST be TOP-LEVEL so it populates timeline_events.concept
|
|
383
836
|
message,
|
|
837
|
+
projectKey,
|
|
384
838
|
metadata: {
|
|
385
839
|
source: config.source,
|
|
386
840
|
purpose,
|
|
@@ -390,7 +844,7 @@ async function handleLogIntent(client, args) {
|
|
|
390
844
|
...context ? { context } : {}
|
|
391
845
|
}
|
|
392
846
|
};
|
|
393
|
-
await client.post("/timeline/events", body
|
|
847
|
+
await client.post("/timeline/events", body);
|
|
394
848
|
return { ok: true, run_id };
|
|
395
849
|
}
|
|
396
850
|
|
|
@@ -408,10 +862,10 @@ var logChangeSummaryInputSchema = import_v412.z.object({
|
|
|
408
862
|
commit: import_v412.z.string().min(1).optional(),
|
|
409
863
|
run_id: import_v412.z.string().min(1).optional()
|
|
410
864
|
});
|
|
411
|
-
async function handleLogChangeSummary(client,
|
|
412
|
-
const parsed2 = logChangeSummaryInputSchema.parse(
|
|
413
|
-
const
|
|
414
|
-
if (!
|
|
865
|
+
async function handleLogChangeSummary(client, args2) {
|
|
866
|
+
const parsed2 = logChangeSummaryInputSchema.parse(args2 ?? {});
|
|
867
|
+
const projectKey = getCurrentProjectId();
|
|
868
|
+
if (!projectKey) {
|
|
415
869
|
throw new Error("No project selected. Use memora_list_projects and memora_set_project to select a project.");
|
|
416
870
|
}
|
|
417
871
|
const { summary, scope, files, stats, commit } = parsed2;
|
|
@@ -422,6 +876,7 @@ async function handleLogChangeSummary(client, args) {
|
|
|
422
876
|
concept: "concept:change_summary",
|
|
423
877
|
actor: { type: config.agentType, name: config.agentName },
|
|
424
878
|
message,
|
|
879
|
+
projectKey,
|
|
425
880
|
metadata: {
|
|
426
881
|
source: config.source,
|
|
427
882
|
purpose: "change_summary",
|
|
@@ -433,7 +888,7 @@ async function handleLogChangeSummary(client, args) {
|
|
|
433
888
|
...run_id ? { run_id } : {}
|
|
434
889
|
}
|
|
435
890
|
};
|
|
436
|
-
await client.post("/timeline/events", body
|
|
891
|
+
await client.post("/timeline/events", body);
|
|
437
892
|
return { ok: true };
|
|
438
893
|
}
|
|
439
894
|
|
|
@@ -450,10 +905,10 @@ var logToolResultInputSchema = import_v413.z.object({
|
|
|
450
905
|
error_kind: import_v413.z.enum(["infra", "logic", "auth", "rate_limit", "validation", "unknown"]).optional(),
|
|
451
906
|
stats: import_v413.z.record(import_v413.z.string(), import_v413.z.any()).optional()
|
|
452
907
|
});
|
|
453
|
-
async function handleLogToolResult(client,
|
|
454
|
-
const parsed2 = logToolResultInputSchema.parse(
|
|
455
|
-
const
|
|
456
|
-
if (!
|
|
908
|
+
async function handleLogToolResult(client, args2) {
|
|
909
|
+
const parsed2 = logToolResultInputSchema.parse(args2 ?? {});
|
|
910
|
+
const projectKey = getCurrentProjectId();
|
|
911
|
+
if (!projectKey) {
|
|
457
912
|
throw new Error("No project selected. Use memora_list_projects and memora_set_project to select a project.");
|
|
458
913
|
}
|
|
459
914
|
const { tool, status, summary, duration_ms, error_code, error_message, error_kind, stats } = parsed2;
|
|
@@ -464,6 +919,7 @@ async function handleLogToolResult(client, args) {
|
|
|
464
919
|
concept: "concept:tool_result",
|
|
465
920
|
actor: { type: config.agentType, name: config.agentName },
|
|
466
921
|
message,
|
|
922
|
+
projectKey,
|
|
467
923
|
metadata: {
|
|
468
924
|
source: config.source,
|
|
469
925
|
purpose: "tool_result",
|
|
@@ -478,7 +934,7 @@ async function handleLogToolResult(client, args) {
|
|
|
478
934
|
...stats ? { stats } : {}
|
|
479
935
|
}
|
|
480
936
|
};
|
|
481
|
-
await client.post("/timeline/events", body
|
|
937
|
+
await client.post("/timeline/events", body);
|
|
482
938
|
return { ok: true };
|
|
483
939
|
}
|
|
484
940
|
|
|
@@ -493,10 +949,10 @@ var logCommandInputSchema = import_v414.z.object({
|
|
|
493
949
|
run_id: import_v414.z.string().min(1).optional(),
|
|
494
950
|
stats: import_v414.z.record(import_v414.z.string(), import_v414.z.any()).optional()
|
|
495
951
|
});
|
|
496
|
-
async function handleLogCommand(client,
|
|
497
|
-
const parsed2 = logCommandInputSchema.parse(
|
|
498
|
-
const
|
|
499
|
-
if (!
|
|
952
|
+
async function handleLogCommand(client, args2) {
|
|
953
|
+
const parsed2 = logCommandInputSchema.parse(args2 ?? {});
|
|
954
|
+
const projectKey = getCurrentProjectId();
|
|
955
|
+
if (!projectKey) {
|
|
500
956
|
throw new Error("No project selected. Use memora_list_projects and memora_set_project to select a project.");
|
|
501
957
|
}
|
|
502
958
|
const { cmd, summary, cwd: cwd2, exit_code, duration_ms, stats } = parsed2;
|
|
@@ -507,6 +963,7 @@ async function handleLogCommand(client, args) {
|
|
|
507
963
|
concept: "concept:command",
|
|
508
964
|
actor: { type: config.agentType, name: config.agentName },
|
|
509
965
|
message,
|
|
966
|
+
projectKey,
|
|
510
967
|
metadata: {
|
|
511
968
|
source: config.source,
|
|
512
969
|
purpose: "command",
|
|
@@ -519,7 +976,7 @@ async function handleLogCommand(client, args) {
|
|
|
519
976
|
...stats ? { stats } : {}
|
|
520
977
|
}
|
|
521
978
|
};
|
|
522
|
-
await client.post("/timeline/events", body
|
|
979
|
+
await client.post("/timeline/events", body);
|
|
523
980
|
return { ok: true };
|
|
524
981
|
}
|
|
525
982
|
|
|
@@ -532,12 +989,23 @@ async function handleListProjects(client) {
|
|
|
532
989
|
// src/tools/handlers/setProject.ts
|
|
533
990
|
var import_v415 = require("zod/v4");
|
|
534
991
|
var setProjectInputSchema = import_v415.z.object({
|
|
535
|
-
|
|
992
|
+
projectKey: import_v415.z.string().min(1).optional(),
|
|
993
|
+
projectId: import_v415.z.string().min(1).optional()
|
|
536
994
|
});
|
|
537
|
-
async function handleSetProject(
|
|
538
|
-
const parsed2 = setProjectInputSchema.parse(
|
|
539
|
-
|
|
540
|
-
|
|
995
|
+
async function handleSetProject(args2) {
|
|
996
|
+
const parsed2 = setProjectInputSchema.parse(args2 ?? {});
|
|
997
|
+
const resolvedProjectKey = parsed2.projectKey ?? parsed2.projectId;
|
|
998
|
+
if (!resolvedProjectKey) {
|
|
999
|
+
throw new Error("projectKey is required");
|
|
1000
|
+
}
|
|
1001
|
+
setCurrentProjectId(resolvedProjectKey);
|
|
1002
|
+
const repo = resolveRepoFingerprint(process.cwd());
|
|
1003
|
+
await setProjectIdForFingerprint({
|
|
1004
|
+
fingerprint: repo.fingerprint,
|
|
1005
|
+
projectKey: resolvedProjectKey,
|
|
1006
|
+
source: "manual"
|
|
1007
|
+
});
|
|
1008
|
+
return { ok: true, projectKey: resolvedProjectKey };
|
|
541
1009
|
}
|
|
542
1010
|
|
|
543
1011
|
// src/index.ts
|
|
@@ -545,7 +1013,10 @@ async function sendHeartbeat(client) {
|
|
|
545
1013
|
try {
|
|
546
1014
|
await client.post("/admin/api-keys/heartbeat", {}, { log: false });
|
|
547
1015
|
} catch (err) {
|
|
548
|
-
|
|
1016
|
+
process.stderr.write(
|
|
1017
|
+
`[memoraone-mcp][info] heartbeat error (silent) ${String(err)}
|
|
1018
|
+
`
|
|
1019
|
+
);
|
|
549
1020
|
}
|
|
550
1021
|
}
|
|
551
1022
|
function redactSensitiveFields(obj) {
|
|
@@ -565,9 +1036,9 @@ function redactSensitiveFields(obj) {
|
|
|
565
1036
|
}
|
|
566
1037
|
return redacted;
|
|
567
1038
|
}
|
|
568
|
-
function sanitizeArgsSummary(
|
|
1039
|
+
function sanitizeArgsSummary(args2) {
|
|
569
1040
|
try {
|
|
570
|
-
const redacted = redactSensitiveFields(
|
|
1041
|
+
const redacted = redactSensitiveFields(args2);
|
|
571
1042
|
const summary = JSON.stringify(redacted);
|
|
572
1043
|
return summary.length > 200 ? summary.slice(0, 200) + "..." : summary;
|
|
573
1044
|
} catch {
|
|
@@ -576,11 +1047,16 @@ function sanitizeArgsSummary(args) {
|
|
|
576
1047
|
}
|
|
577
1048
|
async function postWorklogEvent(client, message) {
|
|
578
1049
|
try {
|
|
1050
|
+
const projectKey = getCurrentProjectId();
|
|
1051
|
+
if (!projectKey) {
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
579
1054
|
const body = {
|
|
580
1055
|
kind: "note",
|
|
581
1056
|
concept: "concept:worklog",
|
|
582
1057
|
actor: { type: config.agentType, name: config.agentName },
|
|
583
1058
|
message,
|
|
1059
|
+
projectKey,
|
|
584
1060
|
metadata: {
|
|
585
1061
|
source: config.source,
|
|
586
1062
|
purpose: "worklog"
|
|
@@ -596,12 +1072,12 @@ function registerToolWithWorklog(server, client, toolName, description, schema,
|
|
|
596
1072
|
server.tool(toolName, description, schema, handler);
|
|
597
1073
|
return;
|
|
598
1074
|
}
|
|
599
|
-
server.tool(toolName, description, schema, async (
|
|
600
|
-
const argsSummary = sanitizeArgsSummary(
|
|
1075
|
+
server.tool(toolName, description, schema, async (args2) => {
|
|
1076
|
+
const argsSummary = sanitizeArgsSummary(args2);
|
|
601
1077
|
const start = Date.now();
|
|
602
1078
|
await postWorklogEvent(client, `tool_start: ${toolName} ${argsSummary}`);
|
|
603
1079
|
try {
|
|
604
|
-
const result = await handler(
|
|
1080
|
+
const result = await handler(args2);
|
|
605
1081
|
const durationMs = Date.now() - start;
|
|
606
1082
|
await postWorklogEvent(client, `tool_end: ${toolName} ok (${durationMs}ms)`);
|
|
607
1083
|
return result;
|
|
@@ -615,8 +1091,28 @@ function registerToolWithWorklog(server, client, toolName, description, schema,
|
|
|
615
1091
|
});
|
|
616
1092
|
}
|
|
617
1093
|
async function main() {
|
|
618
|
-
const
|
|
1094
|
+
const repo = resolveRepoFingerprint(process.cwd());
|
|
1095
|
+
const envProjectKey = process.env.MEMORAONE_PROJECT_ID;
|
|
1096
|
+
const repoFilePath = readRepoProjectIdPath(repo.gitRoot);
|
|
1097
|
+
const repoFileProjectKey = await readRepoProjectId(repo.gitRoot);
|
|
1098
|
+
const workspaceResolved = await getProjectIdForFingerprint(repo.fingerprint);
|
|
1099
|
+
const binding = resolveProjectIdOrThrow({
|
|
1100
|
+
envProjectKey,
|
|
1101
|
+
repoFileProjectKey,
|
|
1102
|
+
workspaceProjectKey: workspaceResolved.projectKey,
|
|
1103
|
+
repoFilePath
|
|
1104
|
+
});
|
|
1105
|
+
const projectKey = binding.projectKey;
|
|
1106
|
+
const source = binding.source;
|
|
1107
|
+
setCurrentProjectId(projectKey);
|
|
619
1108
|
const devMode = Boolean(config.devMode);
|
|
1109
|
+
if (devMode) {
|
|
1110
|
+
const remoteInfo = repo.remoteUrl ? ` remoteUrl=${repo.remoteUrl}` : "";
|
|
1111
|
+
console.error(
|
|
1112
|
+
`[memoraone-mcp][debug] repo fingerprint=${repo.fingerprint} projectKey=${projectKey} source=${source} gitRoot=${repo.gitRoot}${remoteInfo}`
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
const client = new memoraClient_default(config, projectKey);
|
|
620
1116
|
console.error(
|
|
621
1117
|
`[memoraone-mcp] Agent identity: name="${config.agentName}", type="${config.agentType}", source="${config.source}"`
|
|
622
1118
|
);
|
|
@@ -626,14 +1122,12 @@ async function main() {
|
|
|
626
1122
|
});
|
|
627
1123
|
const registeredToolNames = [];
|
|
628
1124
|
registeredToolNames.push("memora_post_event");
|
|
629
|
-
|
|
630
|
-
server,
|
|
631
|
-
client,
|
|
1125
|
+
server.tool(
|
|
632
1126
|
"memora_post_event",
|
|
633
1127
|
"Forward an event to MemoraOne timeline",
|
|
634
1128
|
postEventShape,
|
|
635
|
-
async (
|
|
636
|
-
const result = await handlePostEvent(client,
|
|
1129
|
+
async (args2) => {
|
|
1130
|
+
const result = await handlePostEvent(client, args2);
|
|
637
1131
|
return {
|
|
638
1132
|
content: [
|
|
639
1133
|
{
|
|
@@ -664,10 +1158,10 @@ async function main() {
|
|
|
664
1158
|
registeredToolNames.push("memora_set_project");
|
|
665
1159
|
server.tool(
|
|
666
1160
|
"memora_set_project",
|
|
667
|
-
"Set the current project
|
|
1161
|
+
"Set the current project key for subsequent tool calls",
|
|
668
1162
|
setProjectShape,
|
|
669
|
-
async (
|
|
670
|
-
const result = await handleSetProject(
|
|
1163
|
+
async (args2) => {
|
|
1164
|
+
const result = await handleSetProject(args2);
|
|
671
1165
|
return {
|
|
672
1166
|
content: [
|
|
673
1167
|
{
|
|
@@ -685,8 +1179,8 @@ async function main() {
|
|
|
685
1179
|
"memora_ask_with_memory",
|
|
686
1180
|
"Ask MemoraOne with project memory context",
|
|
687
1181
|
askWithMemoryShape,
|
|
688
|
-
async (
|
|
689
|
-
const result = await handleAskWithMemory(client,
|
|
1182
|
+
async (args2) => {
|
|
1183
|
+
const result = await handleAskWithMemory(client, args2);
|
|
690
1184
|
return {
|
|
691
1185
|
content: [
|
|
692
1186
|
{
|
|
@@ -704,8 +1198,8 @@ async function main() {
|
|
|
704
1198
|
"memora_log_intent",
|
|
705
1199
|
"Log a natural-language TASK or DECISION intent to the MemoraOne timeline",
|
|
706
1200
|
logIntentShape,
|
|
707
|
-
async (
|
|
708
|
-
const result = await handleLogIntent(client,
|
|
1201
|
+
async (args2) => {
|
|
1202
|
+
const result = await handleLogIntent(client, args2);
|
|
709
1203
|
return {
|
|
710
1204
|
content: [
|
|
711
1205
|
{
|
|
@@ -723,8 +1217,8 @@ async function main() {
|
|
|
723
1217
|
"memora_log_change_summary",
|
|
724
1218
|
"Log a concise code change summary to the MemoraOne timeline",
|
|
725
1219
|
logChangeSummaryShape,
|
|
726
|
-
async (
|
|
727
|
-
const result = await handleLogChangeSummary(client,
|
|
1220
|
+
async (args2) => {
|
|
1221
|
+
const result = await handleLogChangeSummary(client, args2);
|
|
728
1222
|
return {
|
|
729
1223
|
content: [
|
|
730
1224
|
{
|
|
@@ -742,8 +1236,8 @@ async function main() {
|
|
|
742
1236
|
"memora_log_tool_result",
|
|
743
1237
|
"Log a tool execution result to the MemoraOne timeline",
|
|
744
1238
|
logToolResultShape,
|
|
745
|
-
async (
|
|
746
|
-
const result = await handleLogToolResult(client,
|
|
1239
|
+
async (args2) => {
|
|
1240
|
+
const result = await handleLogToolResult(client, args2);
|
|
747
1241
|
return {
|
|
748
1242
|
content: [
|
|
749
1243
|
{
|
|
@@ -761,8 +1255,8 @@ async function main() {
|
|
|
761
1255
|
"memora_log_command",
|
|
762
1256
|
"Log a command execution to the MemoraOne timeline",
|
|
763
1257
|
logCommandShape,
|
|
764
|
-
async (
|
|
765
|
-
const result = await handleLogCommand(client,
|
|
1258
|
+
async (args2) => {
|
|
1259
|
+
const result = await handleLogCommand(client, args2);
|
|
766
1260
|
return {
|
|
767
1261
|
content: [
|
|
768
1262
|
{
|
|
@@ -811,3 +1305,15 @@ main().catch((err) => {
|
|
|
811
1305
|
console.error("[memoraone-mcp] fatal error", err);
|
|
812
1306
|
process.exit(1);
|
|
813
1307
|
});
|
|
1308
|
+
|
|
1309
|
+
// src/cli.ts
|
|
1310
|
+
var { version } = require_package();
|
|
1311
|
+
var args = process.argv.slice(2);
|
|
1312
|
+
if (args.includes("--version") || args.includes("-v")) {
|
|
1313
|
+
console.log(version);
|
|
1314
|
+
process.exit(0);
|
|
1315
|
+
}
|
|
1316
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
1317
|
+
console.log("Usage: memoraone-mcp [--version] [--help]");
|
|
1318
|
+
process.exit(0);
|
|
1319
|
+
}
|