@memoraone/mcp 0.1.30 → 0.1.32

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 +1827 -747
  2. package/dist/daemon.cjs +426 -146
  3. package/dist/index.cjs +371 -120
  4. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -33,8 +33,6 @@ __export(index_exports, {
33
33
  main: () => main
34
34
  });
35
35
  module.exports = __toCommonJS(index_exports);
36
- var path6 = __toESM(require("path"), 1);
37
- var import_node_url2 = require("url");
38
36
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
39
37
  var import_types = require("@modelcontextprotocol/sdk/types.js");
40
38
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
@@ -202,12 +200,12 @@ var MemoraClient = class {
202
200
  ...options?.headers ?? {}
203
201
  };
204
202
  }
205
- async post(path7, body, options) {
203
+ async post(path9, body, options) {
206
204
  console.error(
207
- `[memoraone-mcp][info] MemoraClient.post ENTER path=${path7}`
205
+ `[memoraone-mcp][info] MemoraClient.post ENTER path=${path9}`
208
206
  );
209
207
  const nonce = crypto.randomBytes(8).toString("hex");
210
- const url = `${this.baseUrl}${path7.startsWith("/") ? path7 : `/${path7}`}`;
208
+ const url = `${this.baseUrl}${path9.startsWith("/") ? path9 : `/${path9}`}`;
211
209
  this.resolveProjectId();
212
210
  console.error(
213
211
  `[memoraone-mcp][info] requestJson nonce=${nonce} stage=before_fetch method=POST url=${url}`
@@ -240,13 +238,13 @@ var MemoraClient = class {
240
238
  throw new MemoraOneHttpError(res.status, res.statusText, res.text);
241
239
  }
242
240
  console.error(
243
- `[memoraone-mcp][info] MemoraClient.post EXIT path=${path7}`
241
+ `[memoraone-mcp][info] MemoraClient.post EXIT path=${path9}`
244
242
  );
245
243
  return res.text ? JSON.parse(res.text) : null;
246
244
  }
247
- async get(path7, options) {
245
+ async get(path9, options) {
248
246
  const nonce = crypto.randomBytes(8).toString("hex");
249
- const url = `${this.baseUrl}${path7.startsWith("/") ? path7 : `/${path7}`}`;
247
+ const url = `${this.baseUrl}${path9.startsWith("/") ? path9 : `/${path9}`}`;
250
248
  this.resolveProjectId();
251
249
  console.error(
252
250
  `[memoraone-mcp][info] requestJson nonce=${nonce} stage=before_fetch method=GET url=${url}`
@@ -276,9 +274,45 @@ var MemoraClient = class {
276
274
  };
277
275
  var memoraClient_default = MemoraClient;
278
276
 
277
+ // src/initializeBinding.ts
278
+ var path5 = __toESM(require("path"), 1);
279
+ var import_node_url = require("url");
280
+
281
+ // src/bindingIdentity.ts
282
+ var crypto2 = __toESM(require("crypto"), 1);
283
+ var path2 = __toESM(require("path"), 1);
284
+ var BINDING_SOCKET_HASH_LENGTH = 16;
285
+ function bindingsMatch(a, b) {
286
+ return a.projectId.trim().toLowerCase() === b.projectId.trim().toLowerCase() && path2.resolve(a.workspaceRoot) === path2.resolve(b.workspaceRoot) && path2.resolve(a.m1Path) === path2.resolve(b.m1Path);
287
+ }
288
+ function formatMissingInitializeWorkspaceError(options) {
289
+ const lines = [
290
+ "[memoraone-mcp] Could not resolve workspace from MCP initialize params."
291
+ ];
292
+ if (options?.rootsListAttempted) {
293
+ lines.push(
294
+ "Cursor initialize lacked workspaceFolders/rootUri; roots/list was attempted but returned no usable repo root."
295
+ );
296
+ if (options.rootsListUris && options.rootsListUris.length > 0) {
297
+ lines.push(`roots/list URIs: ${options.rootsListUris.join(", ")}`);
298
+ }
299
+ lines.push(
300
+ "Global Cursor MCP cannot safely bind per-window repos without a workspace signal from Cursor (initialize roots or roots/list)."
301
+ );
302
+ lines.push(
303
+ "Reload MCP in this Cursor window, or ensure this repo has a managed .cursor/mcp.json from setup-ide-files --cursor."
304
+ );
305
+ return lines.join("\n");
306
+ }
307
+ lines.push(
308
+ "Reload MCP in this Cursor window so initialize includes workspaceFolders, rootUri, or a usable roots/list response for this repo."
309
+ );
310
+ return lines.join("\n");
311
+ }
312
+
279
313
  // src/projectBinding.ts
280
314
  var fs2 = __toESM(require("fs/promises"), 1);
281
- var path2 = __toESM(require("path"), 1);
315
+ var path3 = __toESM(require("path"), 1);
282
316
  var uuidRegex2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
283
317
  function normalizeEnvironment(raw) {
284
318
  if (raw === void 0 || raw === null || typeof raw !== "string") {
@@ -311,7 +345,7 @@ async function resolveProjectIdFromExplicitM1Path() {
311
345
  if (raw === void 0 || raw.trim() === "") {
312
346
  return null;
313
347
  }
314
- const markerPath = path2.resolve(raw);
348
+ const markerPath = path3.resolve(raw);
315
349
  try {
316
350
  const content = await fs2.readFile(markerPath, "utf8");
317
351
  const { projectId, apiKey, environment } = parseAndValidateM1(content, markerPath);
@@ -324,20 +358,20 @@ async function resolveProjectIdFromExplicitM1Path() {
324
358
  }
325
359
  }
326
360
  async function findM1WalkingUp(workspaceRoot) {
327
- let current = path2.resolve(workspaceRoot);
361
+ let current = path3.resolve(workspaceRoot);
328
362
  while (true) {
329
- const markerPath = path2.join(current, "memoraone.m1");
363
+ const markerPath = path3.join(current, "memoraone.m1");
330
364
  try {
331
365
  const content = await fs2.readFile(markerPath, "utf8");
332
366
  const { projectId, apiKey, environment } = parseAndValidateM1(content, markerPath);
333
- const repoRoot = path2.dirname(markerPath);
367
+ const repoRoot = path3.dirname(markerPath);
334
368
  return environment === void 0 ? { projectId, apiKey, repoRoot, markerPath } : { projectId, apiKey, environment, repoRoot, markerPath };
335
369
  } catch (err) {
336
370
  if (err?.code !== "ENOENT") {
337
371
  throw err;
338
372
  }
339
373
  }
340
- const parent = path2.dirname(current);
374
+ const parent = path3.dirname(current);
341
375
  if (parent === current) {
342
376
  break;
343
377
  }
@@ -360,7 +394,7 @@ function normalizeWorkspaceSearchRoots(workspaceRoot) {
360
394
  if (trimmed === "") {
361
395
  continue;
362
396
  }
363
- const resolved = path2.resolve(trimmed);
397
+ const resolved = path3.resolve(trimmed);
364
398
  if (!seen.has(resolved)) {
365
399
  seen.add(resolved);
366
400
  out.push(resolved);
@@ -382,29 +416,33 @@ function resolveApiKeyWithSource(fileApiKey) {
382
416
  }
383
417
  return { apiKey: null, apiKeySource: "none" };
384
418
  }
385
- async function resolveAuthoritativeBinding(workspaceRoot) {
386
- const explicitBinding = await resolveProjectIdFromExplicitM1Path();
387
- if (explicitBinding) {
388
- const resolved = resolveApiKeyWithSource(explicitBinding.apiKey);
389
- return {
390
- projectId: explicitBinding.projectId,
391
- workspaceRoot: path2.dirname(explicitBinding.foundAt),
392
- m1Path: explicitBinding.foundAt,
393
- apiKey: resolved.apiKey,
394
- ...explicitBinding.environment !== void 0 ? { environment: explicitBinding.environment } : {},
395
- bindingSource: "explicit-m1-path",
396
- apiKeySource: resolved.apiKeySource
397
- };
419
+ async function resolveAuthoritativeBinding(workspaceRoot, options = {}) {
420
+ const respectExplicitM1Path = options.respectExplicitM1Path !== false;
421
+ if (respectExplicitM1Path) {
422
+ const explicitBinding = await resolveProjectIdFromExplicitM1Path();
423
+ if (explicitBinding) {
424
+ const resolved = resolveApiKeyWithSource(explicitBinding.apiKey);
425
+ return {
426
+ projectId: explicitBinding.projectId,
427
+ workspaceRoot: path3.dirname(explicitBinding.foundAt),
428
+ m1Path: explicitBinding.foundAt,
429
+ apiKey: resolved.apiKey,
430
+ ...explicitBinding.environment !== void 0 ? { environment: explicitBinding.environment } : {},
431
+ bindingSource: "explicit-m1-path",
432
+ apiKeySource: resolved.apiKeySource
433
+ };
434
+ }
398
435
  }
399
436
  const candidates = normalizeWorkspaceSearchRoots(workspaceRoot);
400
437
  if (candidates.length === 0) {
401
438
  throw new Error("Could not find memoraone.m1 in workspace.\nOpen a folder containing memoraone.m1.");
402
439
  }
440
+ const bindings = [];
403
441
  for (const root of candidates) {
404
442
  const binding = await findM1WalkingUp(root);
405
443
  if (binding) {
406
444
  const resolved = resolveApiKeyWithSource(binding.apiKey);
407
- return {
445
+ bindings.push({
408
446
  projectId: binding.projectId,
409
447
  workspaceRoot: binding.repoRoot,
410
448
  m1Path: binding.markerPath,
@@ -412,21 +450,232 @@ async function resolveAuthoritativeBinding(workspaceRoot) {
412
450
  ...binding.environment !== void 0 ? { environment: binding.environment } : {},
413
451
  bindingSource: "workspace-search",
414
452
  apiKeySource: resolved.apiKeySource
415
- };
453
+ });
416
454
  }
417
455
  }
418
- throw new Error("Could not find memoraone.m1 in workspace.\nOpen a folder containing memoraone.m1.");
456
+ if (bindings.length === 0) {
457
+ throw new Error("Could not find memoraone.m1 in workspace.\nOpen a folder containing memoraone.m1.");
458
+ }
459
+ const distinctProjectIds = new Set(bindings.map((b) => b.projectId));
460
+ if (distinctProjectIds.size > 1) {
461
+ const lines = bindings.map(
462
+ (b) => ` - workspace=${b.workspaceRoot} project=${b.projectId} m1=${b.m1Path}`
463
+ );
464
+ throw new Error(
465
+ "[memoraone-mcp] Ambiguous workspace binding: multiple open roots map to different MemoraOne projects.\n" + lines.join("\n") + "\nOpen one repo per Cursor window, or use repo-scoped .cursor/mcp.json from setup-ide-files --cursor."
466
+ );
467
+ }
468
+ return bindings[0];
469
+ }
470
+
471
+ // src/socketPaths.ts
472
+ var os = __toESM(require("os"), 1);
473
+ var path4 = __toESM(require("path"), 1);
474
+ var fs3 = __toESM(require("fs"), 1);
475
+ var BASE_DIR = process.env.MEMORAONE_MCP_LOCK_DIR || path4.join(os.homedir(), ".memoraone-mcp");
476
+ var HASH_SOCKET_FILENAME_RE = new RegExp(
477
+ `^mcp-[0-9a-f]{${BINDING_SOCKET_HASH_LENGTH}}\\.sock$`,
478
+ "i"
479
+ );
480
+ var IDE_TYPES = ["cursor", "copilot-vscode", "jetbrains"];
481
+ var IDE_TYPE_SET = new Set(IDE_TYPES);
482
+ function parseIdeType(value) {
483
+ if (value === void 0 || value.trim() === "" || !IDE_TYPE_SET.has(value)) {
484
+ return void 0;
485
+ }
486
+ return value;
487
+ }
488
+ function resolveIdeTypeFromEnv(env2 = process.env) {
489
+ return parseIdeType(env2.MEMORAONE_IDE_TYPE);
490
+ }
491
+
492
+ // src/initializeBinding.ts
493
+ var MEMORAONE_WORKSPACE_ROOT_ENV = "MEMORAONE_WORKSPACE_ROOT";
494
+ function getBridgeBindingResolveOptions(env2 = process.env) {
495
+ const ideType = resolveIdeTypeFromEnv(env2);
496
+ if (ideType === "cursor") {
497
+ return { respectExplicitM1Path: false, allowEnvWorkspaceFallback: false };
498
+ }
499
+ return { respectExplicitM1Path: true, allowEnvWorkspaceFallback: true };
500
+ }
501
+ function uriToPath(uri) {
502
+ if (uri.startsWith("file://")) {
503
+ return (0, import_node_url.fileURLToPath)(uri);
504
+ }
505
+ return uri;
506
+ }
507
+ function getEnvWorkspaceRootCandidates() {
508
+ const raw = process.env.WORKSPACE_FOLDER_PATHS;
509
+ const parts = [];
510
+ if (raw !== void 0 && raw.trim() !== "") {
511
+ for (const p of raw.split(path5.delimiter).map((s) => s.trim()).filter(Boolean)) {
512
+ parts.push(path5.resolve(p));
513
+ }
514
+ }
515
+ parts.push(process.cwd());
516
+ const seen = /* @__PURE__ */ new Set();
517
+ const deduped = [];
518
+ for (const p of parts) {
519
+ if (!seen.has(p)) {
520
+ seen.add(p);
521
+ deduped.push(p);
522
+ }
523
+ }
524
+ return deduped;
525
+ }
526
+ function extractWorkspaceRootsFromInitialize(params) {
527
+ if (!params) {
528
+ return [];
529
+ }
530
+ const seen = /* @__PURE__ */ new Set();
531
+ const roots = [];
532
+ const addRoot = (uri) => {
533
+ if (uri === void 0 || uri.trim() === "") {
534
+ return;
535
+ }
536
+ const resolved = path5.resolve(uriToPath(uri));
537
+ if (!seen.has(resolved)) {
538
+ seen.add(resolved);
539
+ roots.push(resolved);
540
+ }
541
+ };
542
+ if (Array.isArray(params.workspaceFolders) && params.workspaceFolders.length > 0) {
543
+ for (const folder of params.workspaceFolders) {
544
+ addRoot(folder?.uri);
545
+ }
546
+ return roots;
547
+ }
548
+ if (params.rootUri) {
549
+ addRoot(params.rootUri);
550
+ }
551
+ return roots;
552
+ }
553
+ function getRepoScopedWorkspaceHint(env2 = process.env) {
554
+ const raw = env2[MEMORAONE_WORKSPACE_ROOT_ENV];
555
+ if (raw === void 0 || raw.trim() === "") {
556
+ return null;
557
+ }
558
+ return path5.resolve(raw.trim());
559
+ }
560
+ function formatWorkspaceAmbiguityError(bindings) {
561
+ const lines = bindings.map(
562
+ (b) => ` - workspace=${b.workspaceRoot} project=${b.projectId} m1=${b.m1Path}`
563
+ );
564
+ return "[memoraone-mcp] Ambiguous workspace binding: multiple open roots map to different MemoraOne projects.\n" + lines.join("\n") + `
565
+ Open one repo per Cursor window, or ensure this repo's managed .cursor/mcp.json includes ${MEMORAONE_WORKSPACE_ROOT_ENV} from setup-ide-files --cursor.`;
566
+ }
567
+ function formatRepoHintInitializeMismatchError(repoHintRoot, initializeBinding) {
568
+ return `[memoraone-mcp] Repo-scoped workspace hint conflicts with MCP initialize workspace.
569
+ ${MEMORAONE_WORKSPACE_ROOT_ENV}: ${repoHintRoot}
570
+ initialize: project=${initializeBinding.projectId} workspace=${initializeBinding.workspaceRoot} m1=${initializeBinding.m1Path}
571
+ Reload MCP in the Cursor window for this repo, or re-run setup-ide-files --cursor here if the hint is stale.`;
572
+ }
573
+ function formatRepoHintNotInRootsListError(repoHintRoot, rootsListPaths) {
574
+ return `[memoraone-mcp] Repo-scoped workspace hint does not match any Cursor roots/list entry.
575
+ ${MEMORAONE_WORKSPACE_ROOT_ENV}: ${repoHintRoot}
576
+ roots/list paths: ${rootsListPaths.join(", ")}
577
+ Reload MCP in the Cursor window for this repo, or re-run setup-ide-files --cursor if the hint is stale.`;
578
+ }
579
+ function formatBindingMismatchError(daemonHint, sessionBinding) {
580
+ return `[memoraone-mcp] Project binding mismatch between daemon and MCP initialize workspace.
581
+ daemon: project=${daemonHint.projectId} workspace=${daemonHint.workspaceRoot} m1=${daemonHint.m1Path}
582
+ initialize: project=${sessionBinding.projectId} workspace=${sessionBinding.workspaceRoot} m1=${sessionBinding.m1Path}
583
+ Reconnect or reload the MCP server in this IDE window so the bridge can bind to the correct project.`;
584
+ }
585
+ async function resolveBindingFromWorkspaceRoots(workspaceRoots, options = {}) {
586
+ if (workspaceRoots.length === 0) {
587
+ throw new Error("Could not find memoraone.m1 in workspace.\nOpen a folder containing memoraone.m1.");
588
+ }
589
+ const bindings = [];
590
+ for (const root of workspaceRoots) {
591
+ try {
592
+ bindings.push(
593
+ await resolveAuthoritativeBinding(root, {
594
+ respectExplicitM1Path: options.respectExplicitM1Path
595
+ })
596
+ );
597
+ } catch (err) {
598
+ if (err instanceof Error && err.message.includes("Could not find memoraone.m1")) {
599
+ continue;
600
+ }
601
+ throw err;
602
+ }
603
+ }
604
+ if (bindings.length === 0) {
605
+ throw new Error("Could not find memoraone.m1 in workspace.\nOpen a folder containing memoraone.m1.");
606
+ }
607
+ const distinctProjectIds = new Set(bindings.map((b) => b.projectId));
608
+ if (distinctProjectIds.size > 1) {
609
+ throw new Error(formatWorkspaceAmbiguityError(bindings));
610
+ }
611
+ return bindings[0];
612
+ }
613
+ async function resolveBindingFromInitializeParams(params, options = {}) {
614
+ const env2 = options.env ?? process.env;
615
+ const resolveOpts = {
616
+ respectExplicitM1Path: options.respectExplicitM1Path,
617
+ allowEnvWorkspaceFallback: options.allowEnvWorkspaceFallback
618
+ };
619
+ const repoHint = getRepoScopedWorkspaceHint(env2);
620
+ const initializeRoots = extractWorkspaceRootsFromInitialize(params);
621
+ if (initializeRoots.length > 0) {
622
+ const binding = await resolveBindingFromWorkspaceRoots(initializeRoots, resolveOpts);
623
+ if (repoHint !== null) {
624
+ const hintBinding = await resolveAuthoritativeBinding(repoHint, {
625
+ respectExplicitM1Path: false
626
+ });
627
+ if (!bindingsMatch(binding, hintBinding)) {
628
+ throw new Error(formatRepoHintInitializeMismatchError(repoHint, binding));
629
+ }
630
+ }
631
+ return binding;
632
+ }
633
+ const rootsListUris = options.rootsListUris ?? [];
634
+ const rootsListPaths = rootsListUris.map((uri) => uriToPath(uri)).filter(Boolean);
635
+ if (rootsListPaths.length > 1 && repoHint !== null) {
636
+ const hintResolved = path5.resolve(repoHint);
637
+ const matchingRoot = rootsListPaths.find((root) => path5.resolve(root) === hintResolved);
638
+ if (!matchingRoot) {
639
+ throw new Error(formatRepoHintNotInRootsListError(repoHint, rootsListPaths));
640
+ }
641
+ return resolveAuthoritativeBinding(repoHint, { respectExplicitM1Path: false });
642
+ }
643
+ if (rootsListPaths.length > 0) {
644
+ return resolveBindingFromWorkspaceRoots(rootsListPaths, resolveOpts);
645
+ }
646
+ if (repoHint !== null) {
647
+ return resolveAuthoritativeBinding(repoHint, { respectExplicitM1Path: false });
648
+ }
649
+ if (options.allowEnvWorkspaceFallback === false) {
650
+ throw new Error(
651
+ formatMissingInitializeWorkspaceError({
652
+ rootsListAttempted: options.rootsListAttempted === true,
653
+ rootsListUris
654
+ })
655
+ );
656
+ }
657
+ const fallbackRoots = options.fallbackWorkspaceRoots ?? getEnvWorkspaceRootCandidates();
658
+ return resolveAuthoritativeBinding(fallbackRoots, {
659
+ respectExplicitM1Path: options.respectExplicitM1Path
660
+ });
661
+ }
662
+
663
+ // src/bridgeClientRoots.ts
664
+ var readline = __toESM(require("readline"), 1);
665
+ var TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
666
+ function isInitializeDebugEnabled(env2 = process.env) {
667
+ return TRUTHY.has(String(env2.MEMORAONE_DEBUG_INIT ?? "").trim().toLowerCase());
419
668
  }
420
669
 
421
670
  // src/sourceRegistration.ts
422
- var path3 = __toESM(require("path"), 1);
423
- var import_node_url = require("url");
671
+ var path6 = __toESM(require("path"), 1);
672
+ var import_node_url2 = require("url");
424
673
  var LOG_PREFIX = "[memoraone-mcp][source-registration]";
425
674
  function buildRepoSourcePayload(normalizedRepoPath, ideType) {
426
675
  const body = {
427
676
  kind: "repo",
428
- label: path3.basename(normalizedRepoPath),
429
- uri: (0, import_node_url.pathToFileURL)(normalizedRepoPath).href
677
+ label: path6.basename(normalizedRepoPath),
678
+ uri: (0, import_node_url2.pathToFileURL)(normalizedRepoPath).href
430
679
  };
431
680
  if (ideType) {
432
681
  body.metadata = {
@@ -444,7 +693,7 @@ async function registerRepoSource(client, projectId, repoPath, ideType) {
444
693
  );
445
694
  return;
446
695
  }
447
- const normalizedRepoPath = path3.resolve(String(repoPath));
696
+ const normalizedRepoPath = path6.resolve(String(repoPath));
448
697
  const body = buildRepoSourcePayload(normalizedRepoPath, ideType);
449
698
  const primaryPath = `/v1/projects/${projectId}/sources`;
450
699
  const alternatePath = `/v1/projects/${projectId}/sources/register`;
@@ -604,11 +853,11 @@ var bindingStatusShape = {};
604
853
 
605
854
  // src/tools/handlers/postEvent.ts
606
855
  var import_v412 = require("zod/v4");
607
- var crypto3 = __toESM(require("crypto"), 1);
856
+ var crypto4 = __toESM(require("crypto"), 1);
608
857
 
609
858
  // src/runContext.ts
610
859
  var import_node_async_hooks = require("async_hooks");
611
- var crypto2 = __toESM(require("crypto"), 1);
860
+ var crypto3 = __toESM(require("crypto"), 1);
612
861
  var sessionContextStorage = new import_node_async_hooks.AsyncLocalStorage();
613
862
  function createSessionRunContext(initial = {}) {
614
863
  return {
@@ -661,7 +910,7 @@ function resolveRunId(passed) {
661
910
  return getCurrentRunId();
662
911
  }
663
912
  function generateRunId() {
664
- return crypto2.randomBytes(16).toString("hex");
913
+ return crypto3.randomBytes(16).toString("hex");
665
914
  }
666
915
 
667
916
  // src/tools/handlers/postEvent.ts
@@ -701,7 +950,7 @@ function buildPostEventContentFields(content) {
701
950
  return { message: JSON.stringify(content) };
702
951
  }
703
952
  async function handlePostEvent(client, args) {
704
- const nonce = crypto3.randomBytes(8).toString("hex");
953
+ const nonce = crypto4.randomBytes(8).toString("hex");
705
954
  console.error(
706
955
  `[memoraone-mcp][debug] tool=memora_post_event toolCallId=unknown nonce=${nonce} stage=before_post`
707
956
  );
@@ -856,9 +1105,9 @@ function buildPersonalContextPath(parsed2) {
856
1105
  }
857
1106
  async function handleGetPersonalContext(client, args) {
858
1107
  const parsed2 = getPersonalContextInputSchema.parse(args ?? {});
859
- const path7 = buildPersonalContextPath(parsed2);
1108
+ const path9 = buildPersonalContextPath(parsed2);
860
1109
  try {
861
- const result = await client.get(path7);
1110
+ const result = await client.get(path9);
862
1111
  return { ok: true, result };
863
1112
  } catch (err) {
864
1113
  if (err instanceof MemoraOneHttpError) {
@@ -1099,9 +1348,9 @@ async function handleListProjects(client) {
1099
1348
  var import_v421 = require("zod/v4");
1100
1349
 
1101
1350
  // src/repoFingerprint.ts
1102
- var fs3 = __toESM(require("fs"), 1);
1103
- var path4 = __toESM(require("path"), 1);
1104
- var crypto4 = __toESM(require("crypto"), 1);
1351
+ var fs4 = __toESM(require("fs"), 1);
1352
+ var path7 = __toESM(require("path"), 1);
1353
+ var crypto5 = __toESM(require("crypto"), 1);
1105
1354
  var parseBooleanFlag3 = (value) => {
1106
1355
  if (!value) {
1107
1356
  return false;
@@ -1126,20 +1375,20 @@ var normalizeRemoteUrl = (remoteUrl) => {
1126
1375
  return normalized.toLowerCase();
1127
1376
  };
1128
1377
  var sha256 = (value) => {
1129
- return crypto4.createHash("sha256").update(value).digest("hex");
1378
+ return crypto5.createHash("sha256").update(value).digest("hex");
1130
1379
  };
1131
1380
  var resolveGitDir = (gitPath) => {
1132
1381
  try {
1133
- const stat2 = fs3.statSync(gitPath);
1382
+ const stat2 = fs4.statSync(gitPath);
1134
1383
  if (stat2.isDirectory()) {
1135
1384
  return gitPath;
1136
1385
  }
1137
1386
  if (stat2.isFile()) {
1138
- const content = fs3.readFileSync(gitPath, "utf8");
1387
+ const content = fs4.readFileSync(gitPath, "utf8");
1139
1388
  const match = content.match(/^gitdir:\s*(.+)$/m);
1140
1389
  if (match) {
1141
1390
  const gitDir = match[1].trim();
1142
- return path4.resolve(path4.dirname(gitPath), gitDir);
1391
+ return path7.resolve(path7.dirname(gitPath), gitDir);
1143
1392
  }
1144
1393
  }
1145
1394
  } catch {
@@ -1148,16 +1397,16 @@ var resolveGitDir = (gitPath) => {
1148
1397
  return null;
1149
1398
  };
1150
1399
  var findGitRoot = (start) => {
1151
- let current = path4.resolve(start);
1400
+ let current = path7.resolve(start);
1152
1401
  while (true) {
1153
- const gitPath = path4.join(current, ".git");
1154
- if (fs3.existsSync(gitPath)) {
1402
+ const gitPath = path7.join(current, ".git");
1403
+ if (fs4.existsSync(gitPath)) {
1155
1404
  const gitDir = resolveGitDir(gitPath);
1156
1405
  if (gitDir) {
1157
1406
  return { gitRoot: current, gitDir };
1158
1407
  }
1159
1408
  }
1160
- const parent = path4.dirname(current);
1409
+ const parent = path7.dirname(current);
1161
1410
  if (parent === current) {
1162
1411
  break;
1163
1412
  }
@@ -1166,9 +1415,9 @@ var findGitRoot = (start) => {
1166
1415
  return null;
1167
1416
  };
1168
1417
  var readOriginRemote = (gitDir) => {
1169
- const configPath = path4.join(gitDir, "config");
1418
+ const configPath = path7.join(gitDir, "config");
1170
1419
  try {
1171
- const content = fs3.readFileSync(configPath, "utf8");
1420
+ const content = fs4.readFileSync(configPath, "utf8");
1172
1421
  const lines = content.split(/\r?\n/);
1173
1422
  let inOrigin = false;
1174
1423
  for (const line of lines) {
@@ -1192,7 +1441,7 @@ var readOriginRemote = (gitDir) => {
1192
1441
  function resolveRepoFingerprint(cwd2) {
1193
1442
  const found = findGitRoot(cwd2);
1194
1443
  if (!found) {
1195
- const fallbackPath = path4.resolve(cwd2);
1444
+ const fallbackPath = path7.resolve(cwd2);
1196
1445
  const fingerprint2 = sha256(fallbackPath);
1197
1446
  debugLog(`repo fingerprint=${fingerprint2} source=path-fallback`);
1198
1447
  return {
@@ -1214,7 +1463,7 @@ function resolveRepoFingerprint(cwd2) {
1214
1463
  source: "git-remote"
1215
1464
  };
1216
1465
  }
1217
- const fingerprint = sha256(path4.resolve(gitRoot));
1466
+ const fingerprint = sha256(path7.resolve(gitRoot));
1218
1467
  debugLog(`repo fingerprint=${fingerprint} source=path-fallback`);
1219
1468
  return {
1220
1469
  fingerprint,
@@ -1224,8 +1473,8 @@ function resolveRepoFingerprint(cwd2) {
1224
1473
  }
1225
1474
 
1226
1475
  // src/workspaceMap.ts
1227
- var fs4 = __toESM(require("fs/promises"), 1);
1228
- var path5 = __toESM(require("path"), 1);
1476
+ var fs5 = __toESM(require("fs/promises"), 1);
1477
+ var path8 = __toESM(require("path"), 1);
1229
1478
  var import_node_os = __toESM(require("os"), 1);
1230
1479
  var parseBooleanFlag4 = (value) => {
1231
1480
  if (!value) {
@@ -1244,11 +1493,11 @@ var debugLog2 = (message) => {
1244
1493
  };
1245
1494
  var fingerprintRegex = /^[0-9a-f]{64}$/i;
1246
1495
  function getWorkspaceMapPath() {
1247
- return path5.join(import_node_os.default.homedir(), ".memoraone", "workspaces.json");
1496
+ return path8.join(import_node_os.default.homedir(), ".memoraone", "workspaces.json");
1248
1497
  }
1249
1498
  var ensureWorkspaceDir = async () => {
1250
- const dir = path5.dirname(getWorkspaceMapPath());
1251
- await fs4.mkdir(dir, { recursive: true });
1499
+ const dir = path8.dirname(getWorkspaceMapPath());
1500
+ await fs5.mkdir(dir, { recursive: true });
1252
1501
  };
1253
1502
  async function acquireWorkspaceMapLock() {
1254
1503
  const filePath = getWorkspaceMapPath();
@@ -1262,10 +1511,10 @@ async function acquireWorkspaceMapLock() {
1262
1511
  while (!lockAcquired && retries < maxRetries) {
1263
1512
  try {
1264
1513
  try {
1265
- const stat2 = await fs4.stat(lockPath);
1514
+ const stat2 = await fs5.stat(lockPath);
1266
1515
  const ageMs = Date.now() - stat2.mtimeMs;
1267
1516
  if (ageMs > maxLockAgeMs) {
1268
- await fs4.unlink(lockPath);
1517
+ await fs5.unlink(lockPath);
1269
1518
  debugLog2(`removed stale workspace map lock (age: ${ageMs}ms)`);
1270
1519
  }
1271
1520
  } catch (err) {
@@ -1273,14 +1522,14 @@ async function acquireWorkspaceMapLock() {
1273
1522
  throw err;
1274
1523
  }
1275
1524
  }
1276
- const fd = await fs4.open(lockPath, "wx");
1525
+ const fd = await fs5.open(lockPath, "wx");
1277
1526
  await fd.close();
1278
1527
  lockAcquired = true;
1279
1528
  } catch (err) {
1280
1529
  if (err?.code === "EEXIST") {
1281
1530
  retries++;
1282
1531
  if (retries < maxRetries) {
1283
- await new Promise((resolve6) => setTimeout(resolve6, retryDelayMs));
1532
+ await new Promise((resolve7) => setTimeout(resolve7, retryDelayMs));
1284
1533
  continue;
1285
1534
  }
1286
1535
  throw new Error(
@@ -1292,7 +1541,7 @@ async function acquireWorkspaceMapLock() {
1292
1541
  }
1293
1542
  return async () => {
1294
1543
  try {
1295
- await fs4.unlink(lockPath);
1544
+ await fs5.unlink(lockPath);
1296
1545
  } catch (err) {
1297
1546
  if (err?.code !== "ENOENT") {
1298
1547
  debugLog2(`failed to release workspace map lock: ${String(err)}`);
@@ -1348,7 +1597,7 @@ var validateWorkspaceMap = (map, filePath) => {
1348
1597
  async function readWorkspaceMap() {
1349
1598
  const filePath = getWorkspaceMapPath();
1350
1599
  try {
1351
- const content = await fs4.readFile(filePath, "utf8");
1600
+ const content = await fs5.readFile(filePath, "utf8");
1352
1601
  const parsed2 = JSON.parse(content);
1353
1602
  validateWorkspaceMap(parsed2, filePath);
1354
1603
  const typed = parsed2;
@@ -1393,8 +1642,8 @@ async function writeWorkspaceMap(map) {
1393
1642
  await ensureWorkspaceDir();
1394
1643
  const tempPath = `${filePath}.tmp`;
1395
1644
  const content = JSON.stringify(map, null, 2);
1396
- await fs4.writeFile(tempPath, content, "utf8");
1397
- await fs4.rename(tempPath, filePath);
1645
+ await fs5.writeFile(tempPath, content, "utf8");
1646
+ await fs5.rename(tempPath, filePath);
1398
1647
  }
1399
1648
  async function setProjectIdForFingerprint(args) {
1400
1649
  const { fingerprint, projectKey, source, linked_at } = args;
@@ -1474,9 +1723,9 @@ function handleBindingStatus(binding) {
1474
1723
  }
1475
1724
 
1476
1725
  // src/heartbeat.ts
1477
- var crypto5 = __toESM(require("crypto"), 1);
1726
+ var crypto6 = __toESM(require("crypto"), 1);
1478
1727
  function fingerprintApiKey(apiKey) {
1479
- return crypto5.createHash("sha256").update(apiKey).digest("hex").slice(0, 12);
1728
+ return crypto6.createHash("sha256").update(apiKey).digest("hex").slice(0, 12);
1480
1729
  }
1481
1730
  function isHeartbeatDebugEnabled() {
1482
1731
  const value = String(process.env.MEMORAONE_DEBUG_HEARTBEAT ?? "").trim().toLowerCase();
@@ -1561,21 +1810,6 @@ var notInitializedResult = {
1561
1810
  ]
1562
1811
  };
1563
1812
  var initializeDiagDumped = false;
1564
- var uriToPath = (uri) => {
1565
- if (uri.startsWith("file://")) {
1566
- return (0, import_node_url2.fileURLToPath)(uri);
1567
- }
1568
- return uri;
1569
- };
1570
- function getCursorWorkspaceRootFromEnv() {
1571
- const raw = process.env.WORKSPACE_FOLDER_PATHS;
1572
- if (raw === void 0 || raw.trim() === "") {
1573
- return void 0;
1574
- }
1575
- const parts = raw.split(path6.delimiter).map((p) => p.trim()).filter(Boolean);
1576
- const first = parts[0];
1577
- return first ? path6.resolve(first) : void 0;
1578
- }
1579
1813
  function redactSensitiveFields(obj) {
1580
1814
  if (obj === null || obj === void 0) return obj;
1581
1815
  if (typeof obj !== "object") return obj;
@@ -1654,8 +1888,8 @@ function registerToolWithWorklog(server, runtime, sessionContext, toolName, desc
1654
1888
  async function main(opts = {}) {
1655
1889
  let bindingReadyResolve = null;
1656
1890
  let bindingReadyReject = null;
1657
- const bindingReady = new Promise((resolve6, reject) => {
1658
- bindingReadyResolve = resolve6;
1891
+ const bindingReady = new Promise((resolve7, reject) => {
1892
+ bindingReadyResolve = resolve7;
1659
1893
  bindingReadyReject = reject;
1660
1894
  });
1661
1895
  const devMode = Boolean(config2.devMode);
@@ -1666,7 +1900,7 @@ async function main(opts = {}) {
1666
1900
  projectId: null,
1667
1901
  apiKeySource: null,
1668
1902
  apiKeyFingerprint: null,
1669
- authoritativeBinding: opts.authoritativeBinding ?? null,
1903
+ authoritativeBinding: null,
1670
1904
  ideType: void 0
1671
1905
  };
1672
1906
  let workspaceRoot;
@@ -1674,42 +1908,53 @@ async function main(opts = {}) {
1674
1908
  name: "memoraone-mcp",
1675
1909
  version: "1.0.0"
1676
1910
  });
1677
- const resolveFallbackBindingFromInitialize = async (params) => {
1678
- let fallbackWorkspaceRoot = workspaceRoot;
1911
+ const resolveSessionBindingFromInitialize = async (params) => {
1912
+ const initializeRoots = extractWorkspaceRootsFromInitialize(params);
1913
+ if (initializeRoots.length === 0 && opts.daemonBindingHint) {
1914
+ if (isInitializeDebugEnabled()) {
1915
+ console.error(
1916
+ "[memoraone-mcp][init-debug] Workspace resolution strategy: daemonBindingHint (bridge pre-resolved)"
1917
+ );
1918
+ }
1919
+ return opts.daemonBindingHint;
1920
+ }
1921
+ let rootsListUris;
1922
+ let rootsListAttempted = false;
1679
1923
  try {
1680
1924
  const rootsResult = await server.server.listRoots({});
1925
+ rootsListAttempted = true;
1681
1926
  const roots = Array.isArray(rootsResult?.roots) ? rootsResult.roots : [];
1682
- console.error(`[memoraone-mcp] roots/list returned ${roots.length}: ${JSON.stringify(roots)}`);
1683
- if (!fallbackWorkspaceRoot && roots.length > 0 && roots[0]?.uri) {
1684
- fallbackWorkspaceRoot = uriToPath(roots[0].uri);
1685
- console.error("[memoraone-mcp] Workspace resolution strategy: roots/list (first root)");
1927
+ if (isInitializeDebugEnabled()) {
1928
+ console.error(`[memoraone-mcp][init-debug] roots/list returned ${roots.length}: ${JSON.stringify(roots)}`);
1929
+ }
1930
+ if (roots.length > 0) {
1931
+ rootsListUris = roots.map((root) => root?.uri).filter((uri) => Boolean(uri));
1932
+ if (rootsListUris.length > 0 && isInitializeDebugEnabled()) {
1933
+ console.error("[memoraone-mcp][init-debug] Workspace resolution strategy: roots/list");
1934
+ }
1686
1935
  }
1687
1936
  } catch (e) {
1688
- console.error("[memoraone-mcp] roots/list failed:", String(e));
1937
+ if (isInitializeDebugEnabled()) {
1938
+ console.error("[memoraone-mcp][init-debug] roots/list failed:", String(e));
1939
+ }
1689
1940
  }
1690
- console.error(`[memoraone-mcp] process.cwd(): ${process.cwd()}`);
1691
- console.error(
1692
- `[memoraone-mcp] WORKSPACE_FOLDER_PATHS: ${process.env.WORKSPACE_FOLDER_PATHS ?? "(unset)"}`
1693
- );
1694
- if (!fallbackWorkspaceRoot) {
1941
+ if (isInitializeDebugEnabled()) {
1942
+ console.error(`[memoraone-mcp][init-debug] process.cwd(): ${process.cwd()}`);
1943
+ console.error(
1944
+ `[memoraone-mcp][init-debug] WORKSPACE_FOLDER_PATHS: ${process.env.WORKSPACE_FOLDER_PATHS ?? "(unset)"}`
1945
+ );
1695
1946
  if (params.workspaceFolders?.length) {
1696
- fallbackWorkspaceRoot = uriToPath(params.workspaceFolders[0].uri);
1697
- console.error("[memoraone-mcp] Workspace resolution strategy: initialize.workspaceFolders");
1947
+ console.error("[memoraone-mcp][init-debug] Workspace resolution strategy: initialize.workspaceFolders");
1698
1948
  } else if (params.rootUri) {
1699
- fallbackWorkspaceRoot = uriToPath(params.rootUri);
1700
- console.error("[memoraone-mcp] Workspace resolution strategy: initialize.rootUri");
1701
- } else {
1702
- const cursorRoot = getCursorWorkspaceRootFromEnv();
1703
- if (cursorRoot !== void 0) {
1704
- fallbackWorkspaceRoot = cursorRoot;
1705
- console.error("[memoraone-mcp] Workspace resolution strategy: env.WORKSPACE_FOLDER_PATHS");
1706
- } else {
1707
- fallbackWorkspaceRoot = process.cwd();
1708
- console.error("[memoraone-mcp] Workspace resolution strategy: process.cwd() fallback");
1709
- }
1949
+ console.error("[memoraone-mcp][init-debug] Workspace resolution strategy: initialize.rootUri");
1710
1950
  }
1711
1951
  }
1712
- const binding = await resolveAuthoritativeBinding(fallbackWorkspaceRoot);
1952
+ const binding = await resolveBindingFromInitializeParams(params, {
1953
+ fallbackWorkspaceRoots: getEnvWorkspaceRootCandidates(),
1954
+ rootsListUris,
1955
+ rootsListAttempted,
1956
+ ...getBridgeBindingResolveOptions()
1957
+ });
1713
1958
  workspaceRoot = binding.workspaceRoot;
1714
1959
  return binding;
1715
1960
  };
@@ -1889,7 +2134,7 @@ async function main(opts = {}) {
1889
2134
  if (runtime.ideType && opts.onSessionIdeTypeKnown) {
1890
2135
  opts.onSessionIdeTypeKnown(runtime.ideType);
1891
2136
  }
1892
- if (!initializeDiagDumped) {
2137
+ if (isInitializeDebugEnabled() && !initializeDiagDumped) {
1893
2138
  initializeDiagDumped = true;
1894
2139
  const folders = Array.isArray(params.workspaceFolders) ? params.workspaceFolders.map((f) => ({ name: f?.name, uri: f?.uri })) : params.workspaceFolders;
1895
2140
  const capKeys = params.capabilities && typeof params.capabilities === "object" ? Object.keys(params.capabilities) : [];
@@ -1921,7 +2166,13 @@ async function main(opts = {}) {
1921
2166
  String(process.env.MEMORAONE_DEBUG_AUTH ?? "").trim().toLowerCase()
1922
2167
  );
1923
2168
  const debugLog3 = config2.devMode || debugAuth;
1924
- const binding = opts.authoritativeBinding ? opts.authoritativeBinding : await resolveFallbackBindingFromInitialize(params);
2169
+ const binding = await resolveSessionBindingFromInitialize(params);
2170
+ if (opts.daemonBindingHint && !bindingsMatch(opts.daemonBindingHint, binding)) {
2171
+ const errMsg = formatBindingMismatchError(opts.daemonBindingHint, binding);
2172
+ console.error(`[memoraone-mcp][ERROR] ${errMsg}`);
2173
+ bindingReadyReject?.(new Error(errMsg));
2174
+ throw new Error(errMsg);
2175
+ }
1925
2176
  const apiKeyToUse = binding.apiKey;
1926
2177
  if (!apiKeyToUse) {
1927
2178
  throw new Error(
@@ -2045,10 +2296,10 @@ async function main(opts = {}) {
2045
2296
  console.error("[memoraone-mcp] MCP server ready");
2046
2297
  }
2047
2298
  if (opts.sessionSocket) {
2048
- await new Promise((resolve6) => {
2299
+ await new Promise((resolve7) => {
2049
2300
  opts.sessionSocket.once("close", () => {
2050
2301
  shutdown("session closed", false);
2051
- resolve6();
2302
+ resolve7();
2052
2303
  });
2053
2304
  });
2054
2305
  }