@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.
Files changed (3) hide show
  1. package/dist/cli.cjs +616 -110
  2. package/dist/index.cjs +530 -77
  3. 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(path2, body, options) {
152
- const url = `${this.baseUrl}${path2.startsWith("/") ? path2 : `/${path2}`}`;
153
- if (options?.log !== false) {
154
- process.stderr.write(
155
- `[memoraone-mcp] http url=${url} apiKeyPrefix=${String(this.apiKey).slice(0, 8)} apiKeyLen=${String(this.apiKey).length}
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(path2, options) {
166
- const url = `${this.baseUrl}${path2.startsWith("/") ? path2 : `/${path2}`}`;
167
- if (options?.log !== false) {
168
- process.stderr.write(
169
- `[memoraone-mcp] http url=${url} apiKeyPrefix=${String(this.apiKey).slice(0, 8)} apiKeyLen=${String(this.apiKey).length}
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({ identifier: import_v42.z.string().min(1) }),
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
- projectId: import_v48.z.string().min(1)
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({ identifier: import_v49.z.string().min(1) }),
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, args) {
296
- const parsed2 = postEventInputSchema.parse(args ?? {});
297
- const projectId = getCurrentProjectId();
298
- if (!projectId) {
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
- actor: { type: config.agentType, identifier: parsed2.actor.identifier },
304
- content: parsed2.content,
305
- metadata: {
306
- source: config.source,
307
- ...parsed2.metadata
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
- await client.post("/timeline/events", body, { projectId });
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, args) {
328
- const parsed2 = askWithMemoryInputSchema.parse(args ?? {});
329
- const projectId = getCurrentProjectId();
330
- if (!projectId) {
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, { projectId });
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, args) {
362
- const parsed2 = logIntentInputSchema.parse(args ?? {});
363
- const projectId = getCurrentProjectId();
364
- if (!projectId) {
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, { projectId });
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, args) {
412
- const parsed2 = logChangeSummaryInputSchema.parse(args ?? {});
413
- const projectId = getCurrentProjectId();
414
- if (!projectId) {
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, { projectId });
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, args) {
454
- const parsed2 = logToolResultInputSchema.parse(args ?? {});
455
- const projectId = getCurrentProjectId();
456
- if (!projectId) {
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, { projectId });
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, args) {
497
- const parsed2 = logCommandInputSchema.parse(args ?? {});
498
- const projectId = getCurrentProjectId();
499
- if (!projectId) {
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, { projectId });
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
- projectId: import_v415.z.string().min(1)
992
+ projectKey: import_v415.z.string().min(1).optional(),
993
+ projectId: import_v415.z.string().min(1).optional()
536
994
  });
537
- async function handleSetProject(args) {
538
- const parsed2 = setProjectInputSchema.parse(args ?? {});
539
- setCurrentProjectId(parsed2.projectId);
540
- return { ok: true, projectId: parsed2.projectId };
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
- console.error("[memoraone-mcp] heartbeat error (silent)", err);
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(args) {
1039
+ function sanitizeArgsSummary(args2) {
569
1040
  try {
570
- const redacted = redactSensitiveFields(args);
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 (args) => {
600
- const argsSummary = sanitizeArgsSummary(args);
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(args);
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 client = new memoraClient_default(config);
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
- registerToolWithWorklog(
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 (args) => {
636
- const result = await handlePostEvent(client, args);
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 id for subsequent tool calls",
1161
+ "Set the current project key for subsequent tool calls",
668
1162
  setProjectShape,
669
- async (args) => {
670
- const result = await handleSetProject(args);
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 (args) => {
689
- const result = await handleAskWithMemory(client, args);
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 (args) => {
708
- const result = await handleLogIntent(client, args);
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 (args) => {
727
- const result = await handleLogChangeSummary(client, args);
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 (args) => {
746
- const result = await handleLogToolResult(client, args);
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 (args) => {
765
- const result = await handleLogCommand(client, args);
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
+ }