@memoraone/mcp 0.1.38 → 0.1.40

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 (4) hide show
  1. package/dist/cli.cjs +10630 -2440
  2. package/dist/daemon.cjs +634 -90
  3. package/dist/index.cjs +770 -226
  4. package/package.json +4 -2
package/dist/index.cjs CHANGED
@@ -39,8 +39,8 @@ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
39
39
 
40
40
  // src/config.ts
41
41
  var process2 = __toESM(require("process"), 1);
42
- var fs = __toESM(require("fs"), 1);
43
- var path = __toESM(require("path"), 1);
42
+ var fs2 = __toESM(require("fs"), 1);
43
+ var path3 = __toESM(require("path"), 1);
44
44
  var dotenv = __toESM(require("dotenv"), 1);
45
45
  var import_v4 = require("zod/v4");
46
46
 
@@ -62,9 +62,130 @@ function resolveApiUrl(env2) {
62
62
  return DEFAULT_API_URL;
63
63
  }
64
64
 
65
+ // src/socketPaths.ts
66
+ var os = __toESM(require("os"), 1);
67
+ var path2 = __toESM(require("path"), 1);
68
+ var fs = __toESM(require("fs"), 1);
69
+
70
+ // src/bindingIdentity.ts
71
+ var crypto = __toESM(require("crypto"), 1);
72
+ var path = __toESM(require("path"), 1);
73
+ var BINDING_SOCKET_HASH_LENGTH = 16;
74
+ function hashBindingIdentity(repositoryBindingId, workspaceRoot, ideType) {
75
+ const input = [
76
+ repositoryBindingId.trim(),
77
+ path.resolve(workspaceRoot),
78
+ ideType
79
+ ].join("|");
80
+ return crypto.createHash("sha256").update(input).digest("hex").slice(0, BINDING_SOCKET_HASH_LENGTH);
81
+ }
82
+ function bindingsMatch(a, b) {
83
+ const envA = a.environment ?? void 0;
84
+ const envB = b.environment ?? void 0;
85
+ return a.repositoryBindingId === b.repositoryBindingId && a.projectId.trim().toLowerCase() === b.projectId.trim().toLowerCase() && path.resolve(a.workspaceRoot) === path.resolve(b.workspaceRoot) && (a.installationPublicId ?? void 0) === (b.installationPublicId ?? void 0) && envA === envB && a.status === b.status;
86
+ }
87
+ function formatMissingInitializeWorkspaceError(options) {
88
+ const lines = [
89
+ "[memoraone-mcp] Could not resolve workspace from MCP initialize params."
90
+ ];
91
+ if (options?.rootsListAttempted) {
92
+ lines.push(
93
+ "Cursor initialize lacked workspaceFolders/rootUri; roots/list was attempted but returned no usable repo root."
94
+ );
95
+ if (options.rootsListUris && options.rootsListUris.length > 0) {
96
+ lines.push(`roots/list URIs: ${options.rootsListUris.join(", ")}`);
97
+ }
98
+ lines.push(
99
+ "Global Cursor MCP cannot safely bind per-window repos without a workspace signal from Cursor (initialize roots or roots/list)."
100
+ );
101
+ lines.push(
102
+ "Reload MCP in this Cursor window, or ensure this repo has a managed .cursor/mcp.json from setup-ide-files --cursor."
103
+ );
104
+ return lines.join("\n");
105
+ }
106
+ lines.push(
107
+ "Reload MCP in this Cursor window so initialize includes workspaceFolders, rootUri, or a usable roots/list response for this repo."
108
+ );
109
+ return lines.join("\n");
110
+ }
111
+
112
+ // src/socketPaths.ts
113
+ var BASE_DIR = process.env.MEMORAONE_MCP_LOCK_DIR || path2.join(os.homedir(), ".memoraone-mcp");
114
+ var HASH_SOCKET_FILENAME_RE = new RegExp(
115
+ `^mcp-[0-9a-f]{${BINDING_SOCKET_HASH_LENGTH}}\\.sock$`,
116
+ "i"
117
+ );
118
+ var IDE_TYPES = [
119
+ "cursor",
120
+ "copilot-vscode",
121
+ "jetbrains",
122
+ "claude-code",
123
+ "windsurf",
124
+ "opencode",
125
+ "codex",
126
+ "zed",
127
+ "kiro",
128
+ "visual-studio",
129
+ "cline",
130
+ "roo-code",
131
+ // dormant: unsupported for setup/connect
132
+ "auggie",
133
+ "continue",
134
+ // dormant: unsupported for setup/connect
135
+ "copilot-cli",
136
+ "kiro-cli",
137
+ "cline-cli",
138
+ "continue-cli",
139
+ // dormant: unsupported (no writer; wire id only)
140
+ "claude-desktop",
141
+ "gemini-cli",
142
+ // dormant: individual-user CLI unsupported; writer kept internal
143
+ "antigravity",
144
+ "antigravity-cli",
145
+ // Antigravity CLI (agy); shares MCP config with IDE; identity from clientInfo
146
+ "goose",
147
+ "junie",
148
+ "xcode",
149
+ "copilot-jetbrains",
150
+ "copilot-visual-studio"
151
+ ];
152
+ var IDE_TYPE_SET = new Set(IDE_TYPES);
153
+ var IDE_TYPE_CLI_CHOICES = IDE_TYPES.join("|");
154
+ var IDE_TYPE_ALTERNATION = [...IDE_TYPES].sort((a, b) => b.length - a.length).join("|");
155
+ var LEGACY_SOCKET_FILENAME_RE = new RegExp(
156
+ `^mcp-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:-([0-9a-f]{12}))?(?:-(${IDE_TYPE_ALTERNATION}))?\\.sock$`,
157
+ "i"
158
+ );
159
+ var IDE_FROM_COMMAND_LINE_RE = new RegExp(
160
+ `--ide\\s+(${IDE_TYPE_ALTERNATION})(?:\\s|$)`
161
+ );
162
+ function parseIdeType(value) {
163
+ if (value === void 0 || value.trim() === "" || !IDE_TYPE_SET.has(value)) {
164
+ return void 0;
165
+ }
166
+ return value;
167
+ }
168
+ function resolveIdeTypeFromEnv(env2 = process.env) {
169
+ return parseIdeType(env2.MEMORAONE_IDE_TYPE);
170
+ }
171
+ function resolveBindingIdeType(env2 = process.env, ideTypeOverride) {
172
+ if (ideTypeOverride !== void 0) {
173
+ return ideTypeOverride;
174
+ }
175
+ return resolveIdeTypeFromEnv(env2) ?? "";
176
+ }
177
+ function getBindingSocketFilename(binding, env2 = process.env, ideTypeOverride) {
178
+ const ideType = resolveBindingIdeType(env2, ideTypeOverride);
179
+ const hash = hashBindingIdentity(binding.repositoryBindingId, binding.workspaceRoot, ideType);
180
+ return `mcp-${hash}.sock`;
181
+ }
182
+ function getBindingSocketPath(binding, env2 = process.env, ideTypeOverride) {
183
+ return path2.join(BASE_DIR, getBindingSocketFilename(binding, env2, ideTypeOverride));
184
+ }
185
+
65
186
  // src/config.ts
66
- var dotenvPath = path.resolve(process2.cwd(), ".env");
67
- if (fs.existsSync(dotenvPath)) {
187
+ var dotenvPath = path3.resolve(process2.cwd(), ".env");
188
+ if (fs2.existsSync(dotenvPath)) {
68
189
  try {
69
190
  dotenv.config({ path: dotenvPath });
70
191
  } catch (err) {
@@ -78,7 +199,7 @@ var EnvSchema = import_v4.z.object({
78
199
  MEMORAONE_AGENT_NAME: import_v4.z.string().min(1).optional(),
79
200
  MEMORAONE_AGENT_TYPE: import_v4.z.string().min(1).optional(),
80
201
  MEMORAONE_SOURCE: import_v4.z.string().min(1).optional(),
81
- MEMORAONE_IDE_TYPE: import_v4.z.enum(["cursor", "copilot-vscode", "jetbrains"]).optional(),
202
+ MEMORAONE_IDE_TYPE: import_v4.z.enum(IDE_TYPES).optional(),
82
203
  MEMORAONE_WORKLOG: import_v4.z.string().min(1).optional(),
83
204
  MEMORAONE_HEARTBEAT: import_v4.z.string().min(1).optional(),
84
205
  MEMORAONE_HEARTBEAT_INTERVAL_MS: import_v4.z.string().min(1).optional()
@@ -127,11 +248,13 @@ var config2 = {
127
248
  devMode: parseBooleanFlag(parsed.data.MEMORAONE_DEV_MODE, false),
128
249
  worklogEnabled: parseBooleanFlag(parsed.data.MEMORAONE_WORKLOG, true),
129
250
  heartbeatEnabled: parseBooleanFlag(parsed.data.MEMORAONE_HEARTBEAT, true),
130
- heartbeatIntervalMs: Number.parseInt(parsed.data.MEMORAONE_HEARTBEAT_INTERVAL_MS ?? "30000", 10)
251
+ // Cadence is owned by LOCAL_MCP_HEARTBEAT_INTERVAL_MS in heartbeat.ts (1_000).
252
+ // Env override is accepted for forward-compat but the timer path ignores it.
253
+ heartbeatIntervalMs: Number.parseInt(parsed.data.MEMORAONE_HEARTBEAT_INTERVAL_MS ?? "1000", 10)
131
254
  };
132
255
 
133
256
  // src/client/memoraClient.ts
134
- var crypto = __toESM(require("crypto"), 1);
257
+ var crypto2 = __toESM(require("crypto"), 1);
135
258
 
136
259
  // src/localState/installationCredentials.ts
137
260
  var import_node_crypto2 = require("crypto");
@@ -344,39 +467,39 @@ function hasUsableAccessToken(payload) {
344
467
  }
345
468
 
346
469
  // src/localState/localLocks.ts
347
- var fs3 = __toESM(require("fs/promises"), 1);
470
+ var fs4 = __toESM(require("fs/promises"), 1);
348
471
 
349
472
  // src/localState/atomicFs.ts
350
- var fs2 = __toESM(require("fs/promises"), 1);
351
- var path2 = __toESM(require("path"), 1);
473
+ var fs3 = __toESM(require("fs/promises"), 1);
474
+ var path4 = __toESM(require("path"), 1);
352
475
  var import_node_crypto3 = require("crypto");
353
476
  var STATE_DIR_MODE = 448;
354
477
  var STATE_FILE_MODE = 384;
355
478
  async function ensurePrivateDir(dirPath) {
356
- await fs2.mkdir(dirPath, { recursive: true, mode: STATE_DIR_MODE });
479
+ await fs3.mkdir(dirPath, { recursive: true, mode: STATE_DIR_MODE });
357
480
  try {
358
- await fs2.chmod(dirPath, STATE_DIR_MODE);
481
+ await fs3.chmod(dirPath, STATE_DIR_MODE);
359
482
  } catch {
360
483
  }
361
484
  }
362
485
  async function writeFileAtomic(filePath, content, options = {}) {
363
486
  const mode = options.mode ?? STATE_FILE_MODE;
364
- const dir = path2.dirname(filePath);
487
+ const dir = path4.dirname(filePath);
365
488
  await ensurePrivateDir(dir);
366
- const tmpPath = path2.join(
489
+ const tmpPath = path4.join(
367
490
  dir,
368
- `.${path2.basename(filePath)}.${process.pid}.${(0, import_node_crypto3.randomBytes)(8).toString("hex")}.tmp`
491
+ `.${path4.basename(filePath)}.${process.pid}.${(0, import_node_crypto3.randomBytes)(8).toString("hex")}.tmp`
369
492
  );
370
493
  try {
371
- await fs2.writeFile(tmpPath, content, { encoding: "utf8", mode });
372
- await fs2.rename(tmpPath, filePath);
494
+ await fs3.writeFile(tmpPath, content, { encoding: "utf8", mode });
495
+ await fs3.rename(tmpPath, filePath);
373
496
  try {
374
- await fs2.chmod(filePath, mode);
497
+ await fs3.chmod(filePath, mode);
375
498
  } catch {
376
499
  }
377
500
  } catch (err) {
378
501
  try {
379
- await fs2.unlink(tmpPath);
502
+ await fs3.unlink(tmpPath);
380
503
  } catch {
381
504
  }
382
505
  throw err;
@@ -384,7 +507,7 @@ async function writeFileAtomic(filePath, content, options = {}) {
384
507
  }
385
508
  async function readJsonFile(filePath) {
386
509
  try {
387
- const raw = await fs2.readFile(filePath, "utf8");
510
+ const raw = await fs3.readFile(filePath, "utf8");
388
511
  return JSON.parse(raw);
389
512
  } catch (err) {
390
513
  if (err?.code === "ENOENT") {
@@ -402,26 +525,26 @@ async function writeJsonAtomic(filePath, value, options = {}) {
402
525
  }
403
526
 
404
527
  // src/localState/statePaths.ts
405
- var os = __toESM(require("os"), 1);
406
- var path3 = __toESM(require("path"), 1);
528
+ var os2 = __toESM(require("os"), 1);
529
+ var path5 = __toESM(require("path"), 1);
407
530
  var MEMORAONE_STATE_DIRNAME = ".memoraone";
408
- function getMemoraoneStateDir(homeDir = os.homedir()) {
409
- return path3.join(homeDir, MEMORAONE_STATE_DIRNAME);
531
+ function getMemoraoneStateDir(homeDir = os2.homedir()) {
532
+ return path5.join(homeDir, MEMORAONE_STATE_DIRNAME);
410
533
  }
411
- function getPathIndexPath(homeDir = os.homedir()) {
412
- return path3.join(getMemoraoneStateDir(homeDir), "path-index.json");
534
+ function getPathIndexPath(homeDir = os2.homedir()) {
535
+ return path5.join(getMemoraoneStateDir(homeDir), "path-index.json");
413
536
  }
414
- function getBindingsDir(homeDir = os.homedir()) {
415
- return path3.join(getMemoraoneStateDir(homeDir), "bindings");
537
+ function getBindingsDir(homeDir = os2.homedir()) {
538
+ return path5.join(getMemoraoneStateDir(homeDir), "bindings");
416
539
  }
417
- function getBindingFilePath(repositoryBindingId, homeDir = os.homedir()) {
418
- return path3.join(getBindingsDir(homeDir), `${repositoryBindingId}.json`);
540
+ function getBindingFilePath(repositoryBindingId, homeDir = os2.homedir()) {
541
+ return path5.join(getBindingsDir(homeDir), `${repositoryBindingId}.json`);
419
542
  }
420
- function getLocksDir(homeDir = os.homedir()) {
421
- return path3.join(getMemoraoneStateDir(homeDir), "locks");
543
+ function getLocksDir(homeDir = os2.homedir()) {
544
+ return path5.join(getMemoraoneStateDir(homeDir), "locks");
422
545
  }
423
- function getLockPath(lockName, homeDir = os.homedir()) {
424
- return path3.join(getLocksDir(homeDir), `${lockName}.lock`);
546
+ function getLockPath(lockName, homeDir = os2.homedir()) {
547
+ return path5.join(getLocksDir(homeDir), `${lockName}.lock`);
425
548
  }
426
549
 
427
550
  // src/localState/localLocks.ts
@@ -436,16 +559,16 @@ async function acquireLocalLock(lockName, options = {}) {
436
559
  while (retries <= maxRetries) {
437
560
  try {
438
561
  try {
439
- const stat3 = await fs3.stat(lockPath);
562
+ const stat3 = await fs4.stat(lockPath);
440
563
  if (Date.now() - stat3.mtimeMs > maxLockAgeMs) {
441
- await fs3.unlink(lockPath);
564
+ await fs4.unlink(lockPath);
442
565
  }
443
566
  } catch (err) {
444
567
  if (err?.code !== "ENOENT") {
445
568
  throw err;
446
569
  }
447
570
  }
448
- const fd = await fs3.open(lockPath, "wx");
571
+ const fd = await fs4.open(lockPath, "wx");
449
572
  await fd.writeFile(
450
573
  JSON.stringify({
451
574
  pid: process.pid,
@@ -456,7 +579,7 @@ async function acquireLocalLock(lockName, options = {}) {
456
579
  await fd.close();
457
580
  return async () => {
458
581
  try {
459
- await fs3.unlink(lockPath);
582
+ await fs4.unlink(lockPath);
460
583
  } catch (err) {
461
584
  if (err?.code !== "ENOENT") {
462
585
  }
@@ -595,8 +718,8 @@ async function refreshInstallationAccessToken(options) {
595
718
  }
596
719
 
597
720
  // src/localState/bindingStore.ts
598
- var fs4 = __toESM(require("fs/promises"), 1);
599
- var path4 = __toESM(require("path"), 1);
721
+ var fs5 = __toESM(require("fs/promises"), 1);
722
+ var path6 = __toESM(require("path"), 1);
600
723
 
601
724
  // src/localState/bindingRecord.ts
602
725
  var BINDING_RECORD_VERSION = 1;
@@ -827,7 +950,7 @@ var MemoraClient = class {
827
950
  };
828
951
  }
829
952
  async perform(method, path14, body, options, retried = false) {
830
- const nonce = crypto.randomBytes(8).toString("hex");
953
+ const nonce = crypto2.randomBytes(8).toString("hex");
831
954
  const url = `${this.baseUrl}${path14.startsWith("/") ? path14 : `/${path14}`}`;
832
955
  this.resolveProjectId();
833
956
  console.error(
@@ -891,20 +1014,20 @@ var MemoraClient = class {
891
1014
  var memoraClient_default = MemoraClient;
892
1015
 
893
1016
  // src/projectBinding.ts
894
- var fs7 = __toESM(require("fs/promises"), 1);
895
- var path8 = __toESM(require("path"), 1);
1017
+ var fs8 = __toESM(require("fs/promises"), 1);
1018
+ var path10 = __toESM(require("path"), 1);
896
1019
 
897
1020
  // src/localState/resolveLocalBinding.ts
898
- var fs6 = __toESM(require("fs/promises"), 1);
899
- var path7 = __toESM(require("path"), 1);
1021
+ var fs7 = __toESM(require("fs/promises"), 1);
1022
+ var path9 = __toESM(require("path"), 1);
900
1023
 
901
1024
  // src/localState/pathIndex.ts
902
- var path6 = __toESM(require("path"), 1);
1025
+ var path8 = __toESM(require("path"), 1);
903
1026
 
904
1027
  // src/localState/rootFilesystemIdentity.ts
905
- var fs5 = __toESM(require("fs/promises"), 1);
906
- var os2 = __toESM(require("os"), 1);
907
- var path5 = __toESM(require("path"), 1);
1028
+ var fs6 = __toESM(require("fs/promises"), 1);
1029
+ var os3 = __toESM(require("os"), 1);
1030
+ var path7 = __toESM(require("path"), 1);
908
1031
  var import_node_child_process = require("child_process");
909
1032
  var import_node_util = require("util");
910
1033
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
@@ -929,13 +1052,13 @@ async function readDarwinDeviceId() {
929
1052
  }
930
1053
  async function readLinuxDeviceId() {
931
1054
  try {
932
- const content = await fs5.readFile("/etc/machine-id", "utf8");
1055
+ const content = await fs6.readFile("/etc/machine-id", "utf8");
933
1056
  const id = content.trim();
934
1057
  if (id) return id;
935
1058
  } catch {
936
1059
  }
937
1060
  try {
938
- const content = await fs5.readFile("/var/lib/dbus/machine-id", "utf8");
1061
+ const content = await fs6.readFile("/var/lib/dbus/machine-id", "utf8");
939
1062
  const id = content.trim();
940
1063
  if (id) return id;
941
1064
  } catch {
@@ -955,7 +1078,7 @@ async function readWindowsDeviceId() {
955
1078
  }
956
1079
  return match[1].trim();
957
1080
  }
958
- async function resolveDeviceId(platform2 = os2.platform()) {
1081
+ async function resolveDeviceId(platform2 = os3.platform()) {
959
1082
  if (platform2 === "darwin") return readDarwinDeviceId();
960
1083
  if (platform2 === "linux") return readLinuxDeviceId();
961
1084
  if (platform2 === "win32") return readWindowsDeviceId();
@@ -968,10 +1091,10 @@ async function resolveDeviceId(platform2 = os2.platform()) {
968
1091
  throw new Error(`[memoraone-mcp] Unsupported platform for device ID: ${platform2}`);
969
1092
  }
970
1093
  async function captureRootFilesystemIdentity(rootPath, deps = {}) {
971
- const resolved = path5.resolve(rootPath);
972
- const platform2 = deps.platform ?? os2.platform();
1094
+ const resolved = path7.resolve(rootPath);
1095
+ const platform2 = deps.platform ?? os3.platform();
973
1096
  const statRoot = deps.statRoot ?? (async (p) => {
974
- const st2 = await fs5.stat(p);
1097
+ const st2 = await fs6.stat(p);
975
1098
  return {
976
1099
  ino: st2.ino,
977
1100
  dev: st2.dev,
@@ -1055,7 +1178,7 @@ async function savePathIndex(index, homeDir) {
1055
1178
  await writeJsonAtomic(getPathIndexPath(homeDir), next);
1056
1179
  }
1057
1180
  function lookupPathIndex(index, workspaceRoot, identity) {
1058
- const resolved = path6.resolve(workspaceRoot);
1181
+ const resolved = path8.resolve(workspaceRoot);
1059
1182
  const byPathId = index.byPath[resolved];
1060
1183
  if (byPathId) {
1061
1184
  return { kind: "path", repositoryBindingId: assertRepositoryBindingId(byPathId) };
@@ -1081,14 +1204,14 @@ function lookupPathIndex(index, workspaceRoot, identity) {
1081
1204
  }
1082
1205
  async function upsertPathIndexEntry(options) {
1083
1206
  const repositoryBindingId = assertRepositoryBindingId(options.repositoryBindingId);
1084
- const resolved = path6.resolve(options.workspaceRoot);
1207
+ const resolved = path8.resolve(options.workspaceRoot);
1085
1208
  const identityKey = filesystemIdentityKey(options.identity);
1086
1209
  return withLocalLock(
1087
1210
  "path-index",
1088
1211
  async () => {
1089
1212
  const index = await loadPathIndex(options.homeDir);
1090
- if (options.previousPath && path6.resolve(options.previousPath) !== resolved) {
1091
- delete index.byPath[path6.resolve(options.previousPath)];
1213
+ if (options.previousPath && path8.resolve(options.previousPath) !== resolved) {
1214
+ delete index.byPath[path8.resolve(options.previousPath)];
1092
1215
  }
1093
1216
  for (const [p, id] of Object.entries(index.byPath)) {
1094
1217
  if (id === repositoryBindingId && p !== resolved) {
@@ -1114,16 +1237,16 @@ function identityMatchesStored(stored, current) {
1114
1237
 
1115
1238
  // src/localState/resolveLocalBinding.ts
1116
1239
  async function detectLegacyM1Warning(workspaceRoot) {
1117
- const candidate = path7.join(path7.resolve(workspaceRoot), CANONICAL_M1_FILENAME);
1240
+ const candidate = path9.join(path9.resolve(workspaceRoot), CANONICAL_M1_FILENAME);
1118
1241
  try {
1119
- await fs6.access(candidate);
1242
+ await fs7.access(candidate);
1120
1243
  return candidate;
1121
1244
  } catch {
1122
1245
  return void 0;
1123
1246
  }
1124
1247
  }
1125
1248
  async function ensureRepositoryBindingForRoot(workspaceRoot, options = {}) {
1126
- const resolved = path7.resolve(workspaceRoot);
1249
+ const resolved = path9.resolve(workspaceRoot);
1127
1250
  const identity = await captureRootFilesystemIdentity(resolved, options.identityDeps);
1128
1251
  const legacyM1WarningPath = await detectLegacyM1Warning(resolved);
1129
1252
  const index = await loadPathIndex(options.homeDir);
@@ -1131,7 +1254,7 @@ async function ensureRepositoryBindingForRoot(workspaceRoot, options = {}) {
1131
1254
  if (lookup.kind === "path") {
1132
1255
  const record = await readBindingRecord(lookup.repositoryBindingId, options.homeDir);
1133
1256
  if (record && identityMatchesStored(record.filesystemIdentity, identity)) {
1134
- if (path7.resolve(record.workspaceRoot) !== resolved) {
1257
+ if (path9.resolve(record.workspaceRoot) !== resolved) {
1135
1258
  const updated = {
1136
1259
  ...record,
1137
1260
  workspaceRoot: resolved,
@@ -1193,7 +1316,7 @@ async function ensureRepositoryBindingForRoot(workspaceRoot, options = {}) {
1193
1316
  };
1194
1317
  }
1195
1318
  async function resolveLocalBinding(workspaceRoot, options = {}) {
1196
- const resolved = path7.resolve(workspaceRoot);
1319
+ const resolved = path9.resolve(workspaceRoot);
1197
1320
  const { repositoryBindingId, legacyM1WarningPath } = await ensureRepositoryBindingForRoot(
1198
1321
  resolved,
1199
1322
  { ...options, createIfMissing: false }
@@ -1257,9 +1380,9 @@ function toResolvedBinding(local) {
1257
1380
  };
1258
1381
  }
1259
1382
  async function warnLegacyM1IfPresent(workspaceRoot) {
1260
- const candidate = path8.join(path8.resolve(workspaceRoot), CANONICAL_M1_FILENAME);
1383
+ const candidate = path10.join(path10.resolve(workspaceRoot), CANONICAL_M1_FILENAME);
1261
1384
  try {
1262
- await fs7.access(candidate);
1385
+ await fs8.access(candidate);
1263
1386
  process.stderr.write(
1264
1387
  `[memoraone-mcp] warning: ignoring legacy ${CANONICAL_M1_FILENAME} (not used for credentials or binding)
1265
1388
  `
@@ -1316,7 +1439,7 @@ function normalizeWorkspaceSearchRoots(workspaceRoot) {
1316
1439
  if (raw === void 0) continue;
1317
1440
  const trimmed = String(raw).trim();
1318
1441
  if (trimmed === "") continue;
1319
- const resolved = path8.resolve(trimmed);
1442
+ const resolved = path10.resolve(trimmed);
1320
1443
  if (!seen.has(resolved)) {
1321
1444
  seen.add(resolved);
1322
1445
  out.push(resolved);
@@ -1327,7 +1450,7 @@ function normalizeWorkspaceSearchRoots(workspaceRoot) {
1327
1450
  function bindingRelevantValuesMatch(a, b) {
1328
1451
  const envA = a.environment ?? void 0;
1329
1452
  const envB = b.environment ?? void 0;
1330
- return a.repositoryBindingId === b.repositoryBindingId && a.projectId.trim().toLowerCase() === b.projectId.trim().toLowerCase() && path8.resolve(a.workspaceRoot) === path8.resolve(b.workspaceRoot) && (a.installationPublicId ?? void 0) === (b.installationPublicId ?? void 0) && envA === envB && a.status === b.status;
1453
+ return a.repositoryBindingId === b.repositoryBindingId && a.projectId.trim().toLowerCase() === b.projectId.trim().toLowerCase() && path10.resolve(a.workspaceRoot) === path10.resolve(b.workspaceRoot) && (a.installationPublicId ?? void 0) === (b.installationPublicId ?? void 0) && envA === envB && a.status === b.status;
1331
1454
  }
1332
1455
  async function reconcileResolvedBindingWithDisk(cached) {
1333
1456
  const repositoryBindingId = assertRepositoryBindingId(cached.repositoryBindingId);
@@ -1337,7 +1460,7 @@ async function reconcileResolvedBindingWithDisk(cached) {
1337
1460
  `[memoraone-mcp] Cached binding missing for ${repositoryBindingId}. Run: memoraone-mcp connect <code>`
1338
1461
  );
1339
1462
  }
1340
- const workspaceRoot = path8.resolve(record.workspaceRoot);
1463
+ const workspaceRoot = path10.resolve(record.workspaceRoot);
1341
1464
  const identity = await captureRootFilesystemIdentity(workspaceRoot);
1342
1465
  if (!identitiesMatch(record.filesystemIdentity, identity)) {
1343
1466
  throw new ReconnectRequiredError(
@@ -1379,82 +1502,6 @@ function encodeResolvedBinding(binding) {
1379
1502
  // src/initializeBinding.ts
1380
1503
  var path11 = __toESM(require("path"), 1);
1381
1504
  var import_node_url = require("url");
1382
-
1383
- // src/bindingIdentity.ts
1384
- var crypto2 = __toESM(require("crypto"), 1);
1385
- var path9 = __toESM(require("path"), 1);
1386
- var BINDING_SOCKET_HASH_LENGTH = 16;
1387
- function hashBindingIdentity(repositoryBindingId, workspaceRoot, ideType) {
1388
- const input = [
1389
- repositoryBindingId.trim(),
1390
- path9.resolve(workspaceRoot),
1391
- ideType
1392
- ].join("|");
1393
- return crypto2.createHash("sha256").update(input).digest("hex").slice(0, BINDING_SOCKET_HASH_LENGTH);
1394
- }
1395
- function bindingsMatch(a, b) {
1396
- const envA = a.environment ?? void 0;
1397
- const envB = b.environment ?? void 0;
1398
- return a.repositoryBindingId === b.repositoryBindingId && a.projectId.trim().toLowerCase() === b.projectId.trim().toLowerCase() && path9.resolve(a.workspaceRoot) === path9.resolve(b.workspaceRoot) && (a.installationPublicId ?? void 0) === (b.installationPublicId ?? void 0) && envA === envB && a.status === b.status;
1399
- }
1400
- function formatMissingInitializeWorkspaceError(options) {
1401
- const lines = [
1402
- "[memoraone-mcp] Could not resolve workspace from MCP initialize params."
1403
- ];
1404
- if (options?.rootsListAttempted) {
1405
- lines.push(
1406
- "Cursor initialize lacked workspaceFolders/rootUri; roots/list was attempted but returned no usable repo root."
1407
- );
1408
- if (options.rootsListUris && options.rootsListUris.length > 0) {
1409
- lines.push(`roots/list URIs: ${options.rootsListUris.join(", ")}`);
1410
- }
1411
- lines.push(
1412
- "Global Cursor MCP cannot safely bind per-window repos without a workspace signal from Cursor (initialize roots or roots/list)."
1413
- );
1414
- lines.push(
1415
- "Reload MCP in this Cursor window, or ensure this repo has a managed .cursor/mcp.json from setup-ide-files --cursor."
1416
- );
1417
- return lines.join("\n");
1418
- }
1419
- lines.push(
1420
- "Reload MCP in this Cursor window so initialize includes workspaceFolders, rootUri, or a usable roots/list response for this repo."
1421
- );
1422
- return lines.join("\n");
1423
- }
1424
-
1425
- // src/socketPaths.ts
1426
- var os3 = __toESM(require("os"), 1);
1427
- var path10 = __toESM(require("path"), 1);
1428
- var fs8 = __toESM(require("fs"), 1);
1429
- var BASE_DIR = process.env.MEMORAONE_MCP_LOCK_DIR || path10.join(os3.homedir(), ".memoraone-mcp");
1430
- var HASH_SOCKET_FILENAME_RE = new RegExp(
1431
- `^mcp-[0-9a-f]{${BINDING_SOCKET_HASH_LENGTH}}\\.sock$`,
1432
- "i"
1433
- );
1434
- var IDE_TYPES = ["cursor", "copilot-vscode", "jetbrains"];
1435
- var IDE_TYPE_SET = new Set(IDE_TYPES);
1436
- function parseIdeType(value) {
1437
- if (value === void 0 || value.trim() === "" || !IDE_TYPE_SET.has(value)) {
1438
- return void 0;
1439
- }
1440
- return value;
1441
- }
1442
- function resolveIdeTypeFromEnv(env2 = process.env) {
1443
- return parseIdeType(env2.MEMORAONE_IDE_TYPE);
1444
- }
1445
- function resolveBindingIdeType(env2 = process.env) {
1446
- return resolveIdeTypeFromEnv(env2) ?? "";
1447
- }
1448
- function getBindingSocketFilename(binding, env2 = process.env) {
1449
- const ideType = resolveBindingIdeType(env2);
1450
- const hash = hashBindingIdentity(binding.repositoryBindingId, binding.workspaceRoot, ideType);
1451
- return `mcp-${hash}.sock`;
1452
- }
1453
- function getBindingSocketPath(binding, env2 = process.env) {
1454
- return path10.join(BASE_DIR, getBindingSocketFilename(binding, env2));
1455
- }
1456
-
1457
- // src/initializeBinding.ts
1458
1505
  var MEMORAONE_WORKSPACE_ROOT_ENV = "MEMORAONE_WORKSPACE_ROOT";
1459
1506
  function getBridgeBindingResolveOptions(env2 = process.env) {
1460
1507
  const ideType = resolveIdeTypeFromEnv(env2);
@@ -1770,8 +1817,79 @@ var logCommandShape = {
1770
1817
  // src/tools/bindingStatus.ts
1771
1818
  var bindingStatusShape = {};
1772
1819
 
1773
- // src/tools/handlers/postEvent.ts
1820
+ // src/tools/listTimeline.ts
1774
1821
  var import_v411 = require("zod/v4");
1822
+ var listTimelineDescription = "List timeline events for the project bound to this Local MCP installation.";
1823
+ var listTimelineInputSchema = import_v411.z.object({
1824
+ since: import_v411.z.string().optional(),
1825
+ concept: import_v411.z.string().optional(),
1826
+ kind: import_v411.z.union([import_v411.z.string(), import_v411.z.array(import_v411.z.string())]).optional(),
1827
+ sort: import_v411.z.enum(["newest", "oldest"]).optional(),
1828
+ limit: import_v411.z.number().int().min(1).max(200).optional(),
1829
+ cursor: import_v411.z.string().optional()
1830
+ }).strict();
1831
+
1832
+ // src/tools/listConcepts.ts
1833
+ var import_v412 = require("zod/v4");
1834
+ var listConceptsDescription = "List concepts for the project bound to this Local MCP installation.";
1835
+ var listConceptsInputSchema = import_v412.z.object({
1836
+ q: import_v412.z.string().min(1).optional(),
1837
+ tag: import_v412.z.string().min(1).optional(),
1838
+ parent_id: import_v412.z.string().min(1).optional(),
1839
+ limit: import_v412.z.number().int().min(1).max(200).optional(),
1840
+ cursor: import_v412.z.string().min(1).optional()
1841
+ }).strict();
1842
+
1843
+ // src/tools/getConcept.ts
1844
+ var import_v413 = require("zod/v4");
1845
+ var getConceptDescription = "Get one concept by ID from the project bound to this Local MCP installation.";
1846
+ var getConceptInputSchema = import_v413.z.object({
1847
+ id: import_v413.z.string().trim().min(1)
1848
+ }).strict();
1849
+
1850
+ // src/tools/createConceptVersion.ts
1851
+ var import_v414 = require("zod/v4");
1852
+ var createConceptVersionDescription = "Create a version of a concept in the project bound to this Local MCP installation.";
1853
+ var createConceptVersionInputSchema = import_v414.z.object({
1854
+ id: import_v414.z.string().trim().min(1),
1855
+ value: import_v414.z.unknown(),
1856
+ reason: import_v414.z.string().optional(),
1857
+ confidence: import_v414.z.number().finite().min(0).max(1).optional(),
1858
+ source_ref: import_v414.z.string().optional(),
1859
+ tags: import_v414.z.array(import_v414.z.string()).optional(),
1860
+ parent_id: import_v414.z.union([import_v414.z.string(), import_v414.z.null()]).optional()
1861
+ }).strict();
1862
+ function isJsonValue(value) {
1863
+ if (value === null) return true;
1864
+ if (typeof value === "boolean" || typeof value === "string") return true;
1865
+ if (typeof value === "number") return Number.isFinite(value);
1866
+ if (Array.isArray(value)) return value.every(isJsonValue);
1867
+ if (typeof value !== "object") return false;
1868
+ const prototype = Object.getPrototypeOf(value);
1869
+ if (prototype !== Object.prototype && prototype !== null) return false;
1870
+ return Object.values(value).every(isJsonValue);
1871
+ }
1872
+
1873
+ // src/tools/toolInventory.ts
1874
+ var LOCAL_MCP_TOOL_NAMES = [
1875
+ "memora_ask_with_memory",
1876
+ "memora_post_event",
1877
+ "memora_create_fact",
1878
+ "memora_add_personal_context",
1879
+ "memora_get_personal_context",
1880
+ "memora_log_intent",
1881
+ "memora_log_change_summary",
1882
+ "memora_log_tool_result",
1883
+ "memora_log_command",
1884
+ "memora_status",
1885
+ "memora_list_timeline",
1886
+ "memora_list_concepts",
1887
+ "memora_get_concept",
1888
+ "memora_create_concept_version"
1889
+ ];
1890
+
1891
+ // src/tools/handlers/postEvent.ts
1892
+ var import_v415 = require("zod/v4");
1775
1893
  var crypto4 = __toESM(require("crypto"), 1);
1776
1894
 
1777
1895
  // src/runContext.ts
@@ -1827,14 +1945,14 @@ function generateRunId() {
1827
1945
  }
1828
1946
 
1829
1947
  // src/tools/handlers/postEvent.ts
1830
- var postEventInputSchema = import_v411.z.object({
1831
- kind: import_v411.z.string().min(1),
1832
- actor: import_v411.z.object({
1833
- identifier: import_v411.z.string().min(1),
1834
- id: import_v411.z.string().min(1).optional()
1948
+ var postEventInputSchema = import_v415.z.object({
1949
+ kind: import_v415.z.string().min(1),
1950
+ actor: import_v415.z.object({
1951
+ identifier: import_v415.z.string().min(1),
1952
+ id: import_v415.z.string().min(1).optional()
1835
1953
  }),
1836
- content: import_v411.z.record(import_v411.z.string(), import_v411.z.any()),
1837
- metadata: import_v411.z.record(import_v411.z.string(), import_v411.z.any()).optional()
1954
+ content: import_v415.z.record(import_v415.z.string(), import_v415.z.any()),
1955
+ metadata: import_v415.z.record(import_v415.z.string(), import_v415.z.any()).optional()
1838
1956
  });
1839
1957
  function buildPostEventContentFields(content) {
1840
1958
  if (typeof content.message === "string") {
@@ -1906,10 +2024,10 @@ async function handlePostEvent(client, args) {
1906
2024
  }
1907
2025
 
1908
2026
  // src/tools/handlers/createFact.ts
1909
- var import_v412 = require("zod/v4");
1910
- var createFactInputSchema = import_v412.z.object({
1911
- content: import_v412.z.string().min(1),
1912
- metadata: import_v412.z.record(import_v412.z.string(), import_v412.z.any()).optional()
2027
+ var import_v416 = require("zod/v4");
2028
+ var createFactInputSchema = import_v416.z.object({
2029
+ content: import_v416.z.string().min(1),
2030
+ metadata: import_v416.z.record(import_v416.z.string(), import_v416.z.any()).optional()
1913
2031
  });
1914
2032
  async function handleCreateFact(client, args) {
1915
2033
  const parsed2 = createFactInputSchema.parse(args ?? {});
@@ -1954,13 +2072,13 @@ async function handleCreateFact(client, args) {
1954
2072
  }
1955
2073
 
1956
2074
  // src/tools/handlers/addPersonalContext.ts
1957
- var import_v413 = require("zod/v4");
1958
- var addPersonalContextInputSchema = import_v413.z.object({
1959
- content: import_v413.z.string().min(1),
1960
- category: import_v413.z.string().optional(),
1961
- tags: import_v413.z.array(import_v413.z.string()).optional(),
1962
- scope_type: import_v413.z.enum(["general", "project"]).optional(),
1963
- scope_id: import_v413.z.string().optional()
2075
+ var import_v417 = require("zod/v4");
2076
+ var addPersonalContextInputSchema = import_v417.z.object({
2077
+ content: import_v417.z.string().min(1),
2078
+ category: import_v417.z.string().optional(),
2079
+ tags: import_v417.z.array(import_v417.z.string()).optional(),
2080
+ scope_type: import_v417.z.enum(["general", "project"]).optional(),
2081
+ scope_id: import_v417.z.string().optional()
1964
2082
  });
1965
2083
  async function handleAddPersonalContext(client, args) {
1966
2084
  const parsed2 = addPersonalContextInputSchema.parse(args ?? {});
@@ -1996,12 +2114,12 @@ async function handleAddPersonalContext(client, args) {
1996
2114
  }
1997
2115
 
1998
2116
  // src/tools/handlers/getPersonalContext.ts
1999
- var import_v414 = require("zod/v4");
2000
- var getPersonalContextInputSchema = import_v414.z.object({
2001
- query: import_v414.z.string().optional(),
2002
- scope_type: import_v414.z.enum(["general", "project"]).optional(),
2003
- scope_id: import_v414.z.string().optional(),
2004
- limit: import_v414.z.number().int().positive().optional()
2117
+ var import_v418 = require("zod/v4");
2118
+ var getPersonalContextInputSchema = import_v418.z.object({
2119
+ query: import_v418.z.string().optional(),
2120
+ scope_type: import_v418.z.enum(["general", "project"]).optional(),
2121
+ scope_id: import_v418.z.string().optional(),
2122
+ limit: import_v418.z.number().int().positive().optional()
2005
2123
  });
2006
2124
  function buildPersonalContextPath(parsed2) {
2007
2125
  const params = new URLSearchParams();
@@ -2038,13 +2156,13 @@ async function handleGetPersonalContext(client, args) {
2038
2156
  }
2039
2157
 
2040
2158
  // src/tools/handlers/askWithMemory.ts
2041
- var import_v415 = require("zod/v4");
2042
- var askWithMemoryInputSchema = import_v415.z.object({
2043
- question: import_v415.z.string().min(1),
2044
- code_context: import_v415.z.object({
2045
- file_path: import_v415.z.string().optional(),
2046
- selected_text: import_v415.z.string().optional(),
2047
- language: import_v415.z.string().optional()
2159
+ var import_v419 = require("zod/v4");
2160
+ var askWithMemoryInputSchema = import_v419.z.object({
2161
+ question: import_v419.z.string().min(1),
2162
+ code_context: import_v419.z.object({
2163
+ file_path: import_v419.z.string().optional(),
2164
+ selected_text: import_v419.z.string().optional(),
2165
+ language: import_v419.z.string().optional()
2048
2166
  }).optional()
2049
2167
  });
2050
2168
  function isAskWithMemoryResponse(value) {
@@ -2080,13 +2198,13 @@ async function handleAskWithMemory(client, args) {
2080
2198
  }
2081
2199
 
2082
2200
  // src/tools/handlers/logIntent.ts
2083
- var import_v416 = require("zod/v4");
2084
- var logIntentInputSchema = import_v416.z.object({
2085
- intent: import_v416.z.enum(["task", "decision"]),
2086
- message: import_v416.z.string().min(1),
2087
- context: import_v416.z.record(import_v416.z.string(), import_v416.z.any()).optional(),
2088
- intent_source: import_v416.z.string().optional().default("cursor_chat"),
2089
- run_id: import_v416.z.string().min(1).optional()
2201
+ var import_v420 = require("zod/v4");
2202
+ var logIntentInputSchema = import_v420.z.object({
2203
+ intent: import_v420.z.enum(["task", "decision"]),
2204
+ message: import_v420.z.string().min(1),
2205
+ context: import_v420.z.record(import_v420.z.string(), import_v420.z.any()).optional(),
2206
+ intent_source: import_v420.z.string().optional().default("cursor_chat"),
2207
+ run_id: import_v420.z.string().min(1).optional()
2090
2208
  });
2091
2209
  async function handleLogIntent(client, args) {
2092
2210
  const parsed2 = logIntentInputSchema.parse(args ?? {});
@@ -2128,18 +2246,18 @@ async function handleLogIntent(client, args) {
2128
2246
  }
2129
2247
 
2130
2248
  // src/tools/handlers/logChangeSummary.ts
2131
- var import_v417 = require("zod/v4");
2132
- var logChangeSummaryInputSchema = import_v417.z.object({
2133
- summary: import_v417.z.string().min(1),
2134
- scope: import_v417.z.string().min(1).optional(),
2135
- files: import_v417.z.array(import_v417.z.string().min(1)).optional(),
2136
- stats: import_v417.z.object({
2137
- files: import_v417.z.number().int().nonnegative().optional(),
2138
- add: import_v417.z.number().int().nonnegative().optional(),
2139
- del: import_v417.z.number().int().nonnegative().optional()
2249
+ var import_v421 = require("zod/v4");
2250
+ var logChangeSummaryInputSchema = import_v421.z.object({
2251
+ summary: import_v421.z.string().min(1),
2252
+ scope: import_v421.z.string().min(1).optional(),
2253
+ files: import_v421.z.array(import_v421.z.string().min(1)).optional(),
2254
+ stats: import_v421.z.object({
2255
+ files: import_v421.z.number().int().nonnegative().optional(),
2256
+ add: import_v421.z.number().int().nonnegative().optional(),
2257
+ del: import_v421.z.number().int().nonnegative().optional()
2140
2258
  }).optional(),
2141
- commit: import_v417.z.string().min(1).optional(),
2142
- run_id: import_v417.z.string().min(1).optional()
2259
+ commit: import_v421.z.string().min(1).optional(),
2260
+ run_id: import_v421.z.string().min(1).optional()
2143
2261
  });
2144
2262
  async function handleLogChangeSummary(client, args) {
2145
2263
  const parsed2 = logChangeSummaryInputSchema.parse(args ?? {});
@@ -2174,17 +2292,17 @@ async function handleLogChangeSummary(client, args) {
2174
2292
  }
2175
2293
 
2176
2294
  // src/tools/handlers/logToolResult.ts
2177
- var import_v418 = require("zod/v4");
2178
- var logToolResultInputSchema = import_v418.z.object({
2179
- tool: import_v418.z.string().min(1),
2180
- status: import_v418.z.enum(["ok", "error", "partial"]),
2181
- summary: import_v418.z.string().min(1),
2182
- run_id: import_v418.z.string().min(1).optional(),
2183
- duration_ms: import_v418.z.number().int().nonnegative().optional(),
2184
- error_code: import_v418.z.string().min(1).optional(),
2185
- error_message: import_v418.z.string().min(1).optional(),
2186
- error_kind: import_v418.z.enum(["infra", "logic", "auth", "rate_limit", "validation", "unknown"]).optional(),
2187
- stats: import_v418.z.record(import_v418.z.string(), import_v418.z.any()).optional()
2295
+ var import_v422 = require("zod/v4");
2296
+ var logToolResultInputSchema = import_v422.z.object({
2297
+ tool: import_v422.z.string().min(1),
2298
+ status: import_v422.z.enum(["ok", "error", "partial"]),
2299
+ summary: import_v422.z.string().min(1),
2300
+ run_id: import_v422.z.string().min(1).optional(),
2301
+ duration_ms: import_v422.z.number().int().nonnegative().optional(),
2302
+ error_code: import_v422.z.string().min(1).optional(),
2303
+ error_message: import_v422.z.string().min(1).optional(),
2304
+ error_kind: import_v422.z.enum(["infra", "logic", "auth", "rate_limit", "validation", "unknown"]).optional(),
2305
+ stats: import_v422.z.record(import_v422.z.string(), import_v422.z.any()).optional()
2188
2306
  });
2189
2307
  async function handleLogToolResult(client, args) {
2190
2308
  const parsed2 = logToolResultInputSchema.parse(args ?? {});
@@ -2222,15 +2340,15 @@ async function handleLogToolResult(client, args) {
2222
2340
  }
2223
2341
 
2224
2342
  // src/tools/handlers/logCommand.ts
2225
- var import_v419 = require("zod/v4");
2226
- var logCommandInputSchema = import_v419.z.object({
2227
- cmd: import_v419.z.string().min(1),
2228
- summary: import_v419.z.string().min(1),
2229
- cwd: import_v419.z.string().min(1).optional(),
2230
- exit_code: import_v419.z.number().int().optional(),
2231
- duration_ms: import_v419.z.number().int().nonnegative().optional(),
2232
- run_id: import_v419.z.string().min(1).optional(),
2233
- stats: import_v419.z.record(import_v419.z.string(), import_v419.z.any()).optional()
2343
+ var import_v423 = require("zod/v4");
2344
+ var logCommandInputSchema = import_v423.z.object({
2345
+ cmd: import_v423.z.string().min(1),
2346
+ summary: import_v423.z.string().min(1),
2347
+ cwd: import_v423.z.string().min(1).optional(),
2348
+ exit_code: import_v423.z.number().int().optional(),
2349
+ duration_ms: import_v423.z.number().int().nonnegative().optional(),
2350
+ run_id: import_v423.z.string().min(1).optional(),
2351
+ stats: import_v423.z.record(import_v423.z.string(), import_v423.z.any()).optional()
2234
2352
  });
2235
2353
  async function handleLogCommand(client, args) {
2236
2354
  const parsed2 = logCommandInputSchema.parse(args ?? {});
@@ -2265,6 +2383,244 @@ async function handleLogCommand(client, args) {
2265
2383
  return { ok: true };
2266
2384
  }
2267
2385
 
2386
+ // src/tools/responseProjection.ts
2387
+ var timelineItemKeys = [
2388
+ "id",
2389
+ "ts",
2390
+ "kind",
2391
+ "concept",
2392
+ "old_value",
2393
+ "new_value",
2394
+ "reason",
2395
+ "confidence",
2396
+ "links",
2397
+ "source_ref",
2398
+ "redacted",
2399
+ "redaction_reason",
2400
+ "summary",
2401
+ "consolidated_of"
2402
+ ];
2403
+ var conceptVersionKeys = [
2404
+ "version",
2405
+ "ts",
2406
+ "reason",
2407
+ "confidence",
2408
+ "source_ref",
2409
+ "value"
2410
+ ];
2411
+ function invalidResponse() {
2412
+ throw new Error("Invalid Memora response");
2413
+ }
2414
+ function isRecord(value) {
2415
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2416
+ }
2417
+ function pickFields(raw, keys) {
2418
+ const result = {};
2419
+ for (const key of keys) {
2420
+ if (Object.prototype.hasOwnProperty.call(raw, key) && raw[key] !== void 0) {
2421
+ result[key] = raw[key];
2422
+ }
2423
+ }
2424
+ return result;
2425
+ }
2426
+ function pickConceptVersion(raw) {
2427
+ return pickFields(raw, conceptVersionKeys);
2428
+ }
2429
+ function pickConceptItem(raw) {
2430
+ if (typeof raw.id !== "string" || raw.id.trim() === "") invalidResponse();
2431
+ const item = { id: raw.id };
2432
+ if (Object.prototype.hasOwnProperty.call(raw, "tags")) {
2433
+ if (!Array.isArray(raw.tags) || !raw.tags.every((tag) => typeof tag === "string")) {
2434
+ invalidResponse();
2435
+ }
2436
+ item.tags = raw.tags;
2437
+ }
2438
+ if (Object.prototype.hasOwnProperty.call(raw, "parent_id")) {
2439
+ if (raw.parent_id !== null && typeof raw.parent_id !== "string") invalidResponse();
2440
+ item.parent_id = raw.parent_id;
2441
+ }
2442
+ if (Object.prototype.hasOwnProperty.call(raw, "latest")) {
2443
+ if (raw.latest === null) {
2444
+ item.latest = null;
2445
+ } else if (isRecord(raw.latest)) {
2446
+ item.latest = pickConceptVersion(raw.latest);
2447
+ } else {
2448
+ invalidResponse();
2449
+ }
2450
+ }
2451
+ return item;
2452
+ }
2453
+ function normalizeCursor(raw) {
2454
+ for (const key of ["next_cursor", "nextCursor"]) {
2455
+ if (!Object.prototype.hasOwnProperty.call(raw, key)) continue;
2456
+ const value = raw[key];
2457
+ if (value === null) return null;
2458
+ if (typeof value === "string") return value;
2459
+ invalidResponse();
2460
+ }
2461
+ return null;
2462
+ }
2463
+ function projectTimelineResponse(data) {
2464
+ if (!isRecord(data) || !Array.isArray(data.items) || !isRecord(data.page)) {
2465
+ invalidResponse();
2466
+ }
2467
+ if (typeof data.page.limit !== "number" || !Number.isFinite(data.page.limit) || data.page.sort !== "newest" && data.page.sort !== "oldest") {
2468
+ invalidResponse();
2469
+ }
2470
+ const items = data.items.map((entry) => {
2471
+ if (!isRecord(entry)) invalidResponse();
2472
+ return pickFields(entry, timelineItemKeys);
2473
+ });
2474
+ return {
2475
+ items,
2476
+ page: {
2477
+ limit: data.page.limit,
2478
+ sort: data.page.sort,
2479
+ next_cursor: normalizeCursor(data.page)
2480
+ }
2481
+ };
2482
+ }
2483
+ function projectConceptListResponse(data, requestedLimit) {
2484
+ if (!isRecord(data)) invalidResponse();
2485
+ let entries;
2486
+ if (Object.prototype.hasOwnProperty.call(data, "rows")) {
2487
+ entries = data.rows;
2488
+ } else if (Object.prototype.hasOwnProperty.call(data, "items")) {
2489
+ entries = data.items;
2490
+ } else {
2491
+ invalidResponse();
2492
+ }
2493
+ if (!Array.isArray(entries)) invalidResponse();
2494
+ let cursorSource = data;
2495
+ if (!Object.prototype.hasOwnProperty.call(data, "next_cursor") && !Object.prototype.hasOwnProperty.call(data, "nextCursor") && Object.prototype.hasOwnProperty.call(data, "page")) {
2496
+ if (!isRecord(data.page)) invalidResponse();
2497
+ cursorSource = data.page;
2498
+ }
2499
+ return {
2500
+ items: entries.map((entry) => {
2501
+ if (!isRecord(entry)) invalidResponse();
2502
+ return pickConceptItem(entry);
2503
+ }),
2504
+ page: {
2505
+ limit: requestedLimit ?? 50,
2506
+ next_cursor: normalizeCursor(cursorSource)
2507
+ }
2508
+ };
2509
+ }
2510
+ function projectConceptResponse(data) {
2511
+ if (!isRecord(data)) invalidResponse();
2512
+ const item = pickConceptItem(data);
2513
+ if (!Object.prototype.hasOwnProperty.call(data, "history")) {
2514
+ return { ...item, history: [] };
2515
+ }
2516
+ if (!Array.isArray(data.history)) invalidResponse();
2517
+ const history = data.history.map((entry) => {
2518
+ if (!isRecord(entry)) invalidResponse();
2519
+ return pickConceptVersion(entry);
2520
+ });
2521
+ return { ...item, history };
2522
+ }
2523
+ function projectCreatedConceptVersionResponse(data) {
2524
+ if (!isRecord(data)) invalidResponse();
2525
+ return pickConceptItem(data);
2526
+ }
2527
+
2528
+ // src/tools/handlers/toolRequestError.ts
2529
+ function rethrowToolRequestError(toolName, error) {
2530
+ if (error instanceof SyntaxError) {
2531
+ throw new Error("Invalid Memora response");
2532
+ }
2533
+ if (!(error instanceof MemoraOneHttpError)) throw error;
2534
+ let detail = "request failed";
2535
+ if (error.status === 401) detail = "authentication failed";
2536
+ else if (error.status === 403) detail = "connection unavailable";
2537
+ else if (error.status === 404) detail = "resource not found";
2538
+ else if (error.status === 409) detail = "conflict";
2539
+ else if (error.status >= 500) detail = "backend error";
2540
+ throw new Error(`${toolName} failed: ${error.status} ${detail}`);
2541
+ }
2542
+
2543
+ // src/tools/handlers/listTimeline.ts
2544
+ function buildQuery(input) {
2545
+ const parts = [];
2546
+ const append = (key, value) => {
2547
+ parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
2548
+ };
2549
+ if (input.since !== void 0) append("since", input.since);
2550
+ if (input.concept !== void 0) append("concept", input.concept);
2551
+ if (input.kind !== void 0) {
2552
+ for (const kind of Array.isArray(input.kind) ? input.kind : [input.kind]) {
2553
+ append("kind", kind);
2554
+ }
2555
+ }
2556
+ if (input.sort !== void 0) append("sort", input.sort);
2557
+ if (input.limit !== void 0) append("limit", String(input.limit));
2558
+ if (input.cursor !== void 0) append("cursor", input.cursor);
2559
+ return parts.length === 0 ? "" : `?${parts.join("&")}`;
2560
+ }
2561
+ async function handleListTimeline(client, args) {
2562
+ const input = listTimelineInputSchema.parse(args ?? {});
2563
+ try {
2564
+ return projectTimelineResponse(await client.get(`/timeline${buildQuery(input)}`));
2565
+ } catch (error) {
2566
+ rethrowToolRequestError("memora_list_timeline", error);
2567
+ }
2568
+ }
2569
+
2570
+ // src/tools/handlers/listConcepts.ts
2571
+ function buildQuery2(input) {
2572
+ const parts = [];
2573
+ const append = (key, value) => {
2574
+ parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
2575
+ };
2576
+ if (input.q !== void 0) append("q", input.q);
2577
+ if (input.tag !== void 0) append("tag", input.tag);
2578
+ if (input.parent_id !== void 0) append("parent_id", input.parent_id);
2579
+ if (input.limit !== void 0) append("limit", String(input.limit));
2580
+ if (input.cursor !== void 0) append("cursor", input.cursor);
2581
+ return parts.length === 0 ? "" : `?${parts.join("&")}`;
2582
+ }
2583
+ async function handleListConcepts(client, args) {
2584
+ const input = listConceptsInputSchema.parse(args ?? {});
2585
+ try {
2586
+ const data = await client.get(`/concepts${buildQuery2(input)}`);
2587
+ return projectConceptListResponse(data, input.limit);
2588
+ } catch (error) {
2589
+ rethrowToolRequestError("memora_list_concepts", error);
2590
+ }
2591
+ }
2592
+
2593
+ // src/tools/handlers/getConcept.ts
2594
+ async function handleGetConcept(client, args) {
2595
+ const input = getConceptInputSchema.parse(args ?? {});
2596
+ try {
2597
+ const data = await client.get(`/concepts/${encodeURIComponent(input.id)}`);
2598
+ return projectConceptResponse(data);
2599
+ } catch (error) {
2600
+ rethrowToolRequestError("memora_get_concept", error);
2601
+ }
2602
+ }
2603
+
2604
+ // src/tools/handlers/createConceptVersion.ts
2605
+ async function handleCreateConceptVersion(client, args) {
2606
+ const input = createConceptVersionInputSchema.parse(args ?? {});
2607
+ if (!isJsonValue(input.value)) {
2608
+ throw new Error("Invalid arguments: value must be a valid JSON value");
2609
+ }
2610
+ const body = { value: input.value };
2611
+ if (input.reason !== void 0) body.reason = input.reason;
2612
+ if (input.confidence !== void 0) body.confidence = input.confidence;
2613
+ if (input.source_ref !== void 0) body.source_ref = input.source_ref;
2614
+ if (input.tags !== void 0) body.tags = input.tags;
2615
+ if (input.parent_id !== void 0) body.parent_id = input.parent_id;
2616
+ try {
2617
+ const path14 = `/concepts/${encodeURIComponent(input.id)}/versions`;
2618
+ return projectCreatedConceptVersionResponse(await client.post(path14, body));
2619
+ } catch (error) {
2620
+ rethrowToolRequestError("memora_create_concept_version", error);
2621
+ }
2622
+ }
2623
+
2268
2624
  // src/tools/handlers/bindingStatus.ts
2269
2625
  function buildBindingStatus(binding, options = {}) {
2270
2626
  const status = {
@@ -2315,8 +2671,9 @@ function isHeartbeatDebugEnabled() {
2315
2671
  const value = String(process.env.MEMORAONE_DEBUG_HEARTBEAT ?? "").trim().toLowerCase();
2316
2672
  return ["1", "true", "yes", "on"].includes(value);
2317
2673
  }
2674
+ var LOCAL_MCP_HEARTBEAT_INTERVAL_MS = 1e3;
2318
2675
  function resolveHeartbeatIntervalMs() {
2319
- return Number.isFinite(config2.heartbeatIntervalMs) ? Math.max(1e3, config2.heartbeatIntervalMs) : 3e4;
2676
+ return LOCAL_MCP_HEARTBEAT_INTERVAL_MS;
2320
2677
  }
2321
2678
  function redactSensitiveText(text) {
2322
2679
  return text.replace(/mcs_[A-Za-z0-9_-]+/g, "mcs_[redacted]").replace(/mia_[A-Za-z0-9_-]+/g, "mia_[redacted]").replace(/mir_[A-Za-z0-9_-]+/g, "mir_[redacted]").replace(/mcc_[A-Za-z0-9_-]+/g, "mcc_[redacted]").replace(/Bearer\s+\S+/gi, "Bearer [redacted]");
@@ -2426,6 +2783,7 @@ async function sendProjectHeartbeat(client, ctx) {
2426
2783
  }
2427
2784
  function createDaemonHeartbeat(opts) {
2428
2785
  let interval = null;
2786
+ let runGeneration = 0;
2429
2787
  let client = null;
2430
2788
  let announced = false;
2431
2789
  let studioActive = null;
@@ -2494,21 +2852,24 @@ function createDaemonHeartbeat(opts) {
2494
2852
  studioActive = true;
2495
2853
  }
2496
2854
  };
2497
- const tick = async () => {
2498
- if (!client) return;
2855
+ const tick = async (generation) => {
2856
+ if (!client || generation !== runGeneration) return;
2499
2857
  const outcome = await sendProjectHeartbeat(client, ctx);
2858
+ if (generation !== runGeneration || !interval) return;
2500
2859
  applyHeartbeatOutcome(outcome);
2501
2860
  };
2502
2861
  const beginInterval = () => {
2503
2862
  if (interval) return;
2504
2863
  const intervalMs = resolveHeartbeatIntervalMs();
2864
+ const generation = runGeneration;
2505
2865
  log(
2506
2866
  `daemon owns heartbeat for binding=${opts.binding.repositoryBindingId} project=${opts.binding.projectId} ideType=${ctx.ideType ?? "unknown"} interval=${intervalMs}ms`
2507
2867
  );
2508
- void tick();
2509
2868
  interval = setInterval(() => {
2510
- void tick();
2869
+ void tick(generation);
2511
2870
  }, intervalMs);
2871
+ interval.unref?.();
2872
+ void tick(generation);
2512
2873
  };
2513
2874
  const start = async () => {
2514
2875
  if (!config2.heartbeatEnabled) {
@@ -2541,6 +2902,7 @@ function createDaemonHeartbeat(opts) {
2541
2902
  }
2542
2903
  };
2543
2904
  const stop = () => {
2905
+ runGeneration += 1;
2544
2906
  if (interval) {
2545
2907
  clearInterval(interval);
2546
2908
  interval = null;
@@ -2560,7 +2922,7 @@ function createDaemonHeartbeat(opts) {
2560
2922
  return;
2561
2923
  }
2562
2924
  if (client && interval) {
2563
- void tick();
2925
+ void tick(runGeneration);
2564
2926
  }
2565
2927
  };
2566
2928
  const getIdeType = () => ctx.ideType;
@@ -2580,12 +2942,125 @@ function createDaemonHeartbeat(opts) {
2580
2942
  }
2581
2943
 
2582
2944
  // src/ideType.ts
2945
+ var RELIABLE_CLIENT_INFO_NAMES = /* @__PURE__ */ new Map([
2946
+ // Existing
2947
+ ["claude-code", "claude-code"],
2948
+ ["devin", "windsurf"],
2949
+ ["windsurf", "windsurf"],
2950
+ // Zed
2951
+ ["zed", "zed"],
2952
+ // Kiro IDE (exact only — not generic "kiro …" mcp-remote wrappers)
2953
+ ["kiro", "kiro"],
2954
+ // Visual Studio (never Visual Studio Code)
2955
+ ["visual studio", "visual-studio"],
2956
+ ["visual-studio", "visual-studio"],
2957
+ // Cline IDE
2958
+ ["cline", "cline"],
2959
+ // Roo Code
2960
+ ["roo code", "roo-code"],
2961
+ ["roo-code", "roo-code"],
2962
+ // Auggie / Augment Code (canonical wire ID: auggie)
2963
+ ["auggie", "auggie"],
2964
+ ["augment", "auggie"],
2965
+ ["augment code", "auggie"],
2966
+ ["augment-code", "auggie"],
2967
+ // Continue IDE (not bare "continue")
2968
+ ["continue-client", "continue"],
2969
+ // Continue CLI
2970
+ ["continue-cli-client", "continue-cli"],
2971
+ ["continue-cli", "continue-cli"],
2972
+ ["continue cli", "continue-cli"],
2973
+ // Copilot CLI (not GitHub Copilot in VS Code)
2974
+ ["github-copilot-developer", "copilot-cli"],
2975
+ ["copilot-cli", "copilot-cli"],
2976
+ ["copilot cli", "copilot-cli"],
2977
+ // Kiro CLI (explicit CLI names only — not Amazon Q / Q-DEV-CLI)
2978
+ ["kiro-cli", "kiro-cli"],
2979
+ ["kiro cli", "kiro-cli"],
2980
+ // Cline CLI
2981
+ ["cline-cli", "cline-cli"],
2982
+ ["cline cli", "cline-cli"],
2983
+ // Claude Desktop (never bare "claude"; never claude-code)
2984
+ ["claude-ai", "claude-desktop"],
2985
+ ["claude-desktop", "claude-desktop"],
2986
+ ["claude desktop", "claude-desktop"],
2987
+ // Gemini CLI (not bare "gemini")
2988
+ ["gemini-cli", "gemini-cli"],
2989
+ ["gemini cli", "gemini-cli"],
2990
+ // Google Antigravity IDE vs CLI (shared MCP config; clientInfo distinguishes)
2991
+ ["antigravity", "antigravity"],
2992
+ ["antigravity ide", "antigravity"],
2993
+ ["antigravity-client", "antigravity-cli"],
2994
+ // Goose
2995
+ ["goose", "goose"],
2996
+ // JetBrains Junie (distinct from generic jetbrains / copilot-jetbrains)
2997
+ ["junie", "junie"],
2998
+ // GitHub Copilot for Xcode
2999
+ ["xcode", "xcode"],
3000
+ ["copilot-xcode", "xcode"],
3001
+ ["github-copilot-xcode", "xcode"],
3002
+ // GitHub Copilot for JetBrains (distinct from jetbrains AI Assistant)
3003
+ ["copilot-jetbrains", "copilot-jetbrains"],
3004
+ ["github-copilot-jetbrains", "copilot-jetbrains"],
3005
+ ["github copilot jetbrains", "copilot-jetbrains"],
3006
+ // GitHub Copilot in Visual Studio (distinct from visual-studio / copilot-vscode)
3007
+ ["copilot-visual-studio", "copilot-visual-studio"],
3008
+ ["github-copilot-visual-studio", "copilot-visual-studio"],
3009
+ ["github copilot visual studio", "copilot-visual-studio"]
3010
+ ]);
3011
+ function mapReliableClientInfoName(name) {
3012
+ if (typeof name !== "string") {
3013
+ return void 0;
3014
+ }
3015
+ const normalized = name.trim().toLowerCase();
3016
+ if (normalized === "") {
3017
+ return void 0;
3018
+ }
3019
+ const exact = RELIABLE_CLIENT_INFO_NAMES.get(normalized);
3020
+ if (exact) {
3021
+ return exact;
3022
+ }
3023
+ if (/^windsurf[\s_-].+$/.test(normalized)) {
3024
+ return "windsurf";
3025
+ }
3026
+ return void 0;
3027
+ }
3028
+ function mapReliableHostIdentity(env2) {
3029
+ if (env2.WINDSURF_IDE_TYPE === "windsurf") {
3030
+ return "windsurf";
3031
+ }
3032
+ if (env2.ACP_BACKEND === "windsurf") {
3033
+ return "windsurf";
3034
+ }
3035
+ if (env2.__CFBundleIdentifier === "com.exafunction.windsurf") {
3036
+ return "windsurf";
3037
+ }
3038
+ if (env2.__CFBundleIdentifier === "com.anthropic.claudefordesktop") {
3039
+ return "claude-desktop";
3040
+ }
3041
+ if (env2.__CFBundleIdentifier === "dev.zed.Zed") {
3042
+ return "zed";
3043
+ }
3044
+ if (Object.prototype.hasOwnProperty.call(env2, "COPILOT_CLI")) {
3045
+ return "copilot-cli";
3046
+ }
3047
+ return void 0;
3048
+ }
2583
3049
  function inferIdeType(params, options = {}) {
2584
3050
  const env2 = options.env ?? process.env;
2585
3051
  const argv = (options.argv ?? process.argv).join(" ").toLowerCase();
2586
- const configIdeType = options.configIdeType ?? config2.ideType;
2587
- if (configIdeType) {
2588
- return configIdeType;
3052
+ const hasExplicitConfigOption = Object.prototype.hasOwnProperty.call(options, "configIdeType");
3053
+ const explicitHint = hasExplicitConfigOption ? options.configIdeType : resolveIdeTypeFromEnv(env2) ?? config2.ideType;
3054
+ const fromClientInfo = mapReliableClientInfoName(params?.clientInfo?.name);
3055
+ if (fromClientInfo) {
3056
+ return fromClientInfo;
3057
+ }
3058
+ const fromHost = mapReliableHostIdentity(env2);
3059
+ if (fromHost) {
3060
+ return fromHost;
3061
+ }
3062
+ if (explicitHint) {
3063
+ return explicitHint;
2589
3064
  }
2590
3065
  const clientInfoName = String(params?.clientInfo?.name ?? "").toLowerCase();
2591
3066
  const clientInfoVersion = String(params?.clientInfo?.version ?? "").toLowerCase();
@@ -2610,7 +3085,7 @@ function inferIdeType(params, options = {}) {
2610
3085
  if (hasJetBrainsSignals) {
2611
3086
  return "jetbrains";
2612
3087
  }
2613
- const hasVsCodeSignals = envKeys.some((key) => key.startsWith("VSCODE_")) || /(visual studio code|vscode|vs code|github copilot)/.test(clientInfoName) || /(visual studio code|vscode|vs code)/.test(argv);
3088
+ const hasVsCodeSignals = envKeys.some((key) => key.startsWith("VSCODE_")) || /(visual studio code|vscode|vs code)/.test(clientInfoName) || /(visual studio code|vscode|vs code)/.test(argv);
2614
3089
  if (hasVsCodeSignals) {
2615
3090
  return "copilot-vscode";
2616
3091
  }
@@ -2742,7 +3217,11 @@ async function main(opts = {}) {
2742
3217
  `[memoraone-mcp] refreshed stale cached binding ${reconciled.binding.repositoryBindingId}: project=${reconciled.binding.projectId}`
2743
3218
  );
2744
3219
  try {
2745
- const socketPath = getBindingSocketPath(opts.daemonBindingHint);
3220
+ const socketPath = getBindingSocketPath(
3221
+ opts.daemonBindingHint,
3222
+ process.env,
3223
+ runtime.ideType ?? ""
3224
+ );
2746
3225
  writeBindingSidecar(socketPath, reconciled.binding, runtime.ideType ?? "");
2747
3226
  } catch (err) {
2748
3227
  console.error(
@@ -2935,6 +3414,71 @@ async function main(opts = {}) {
2935
3414
  }
2936
3415
  );
2937
3416
  registeredToolNames.push("memora_log_command");
3417
+ server.registerTool(
3418
+ "memora_list_timeline",
3419
+ {
3420
+ description: listTimelineDescription,
3421
+ inputSchema: listTimelineInputSchema
3422
+ },
3423
+ async (args) => runWithSessionContext(sessionContext, async () => {
3424
+ if (!runtime.client || !runtime.projectId) return notInitializedResult;
3425
+ const result = await handleListTimeline(runtime.client, args);
3426
+ return {
3427
+ content: [{ type: "text", text: JSON.stringify(result) }]
3428
+ };
3429
+ })
3430
+ );
3431
+ registeredToolNames.push("memora_list_timeline");
3432
+ server.registerTool(
3433
+ "memora_list_concepts",
3434
+ {
3435
+ description: listConceptsDescription,
3436
+ inputSchema: listConceptsInputSchema
3437
+ },
3438
+ async (args) => runWithSessionContext(sessionContext, async () => {
3439
+ if (!runtime.client || !runtime.projectId) return notInitializedResult;
3440
+ const result = await handleListConcepts(runtime.client, args);
3441
+ return {
3442
+ content: [{ type: "text", text: JSON.stringify(result) }]
3443
+ };
3444
+ })
3445
+ );
3446
+ registeredToolNames.push("memora_list_concepts");
3447
+ server.registerTool(
3448
+ "memora_get_concept",
3449
+ {
3450
+ description: getConceptDescription,
3451
+ inputSchema: getConceptInputSchema
3452
+ },
3453
+ async (args) => runWithSessionContext(sessionContext, async () => {
3454
+ if (!runtime.client || !runtime.projectId) return notInitializedResult;
3455
+ const result = await handleGetConcept(runtime.client, args);
3456
+ return {
3457
+ content: [{ type: "text", text: JSON.stringify(result) }]
3458
+ };
3459
+ })
3460
+ );
3461
+ registeredToolNames.push("memora_get_concept");
3462
+ server.registerTool(
3463
+ "memora_create_concept_version",
3464
+ {
3465
+ description: createConceptVersionDescription,
3466
+ inputSchema: createConceptVersionInputSchema
3467
+ },
3468
+ async (args) => runWithSessionContext(sessionContext, async () => {
3469
+ if (!runtime.client || !runtime.projectId) return notInitializedResult;
3470
+ const result = await handleCreateConceptVersion(runtime.client, args);
3471
+ return {
3472
+ content: [{ type: "text", text: JSON.stringify(result) }]
3473
+ };
3474
+ })
3475
+ );
3476
+ registeredToolNames.push("memora_create_concept_version");
3477
+ if (registeredToolNames.length !== LOCAL_MCP_TOOL_NAMES.length || registeredToolNames.some(
3478
+ (name) => !LOCAL_MCP_TOOL_NAMES.includes(name)
3479
+ )) {
3480
+ throw new Error("Local MCP tool registration inventory mismatch");
3481
+ }
2938
3482
  server.server.setRequestHandler(
2939
3483
  import_types.InitializeRequestSchema,
2940
3484
  async (request) => runWithSessionContext(sessionContext, async () => {