@memoraone/mcp 0.1.47 → 0.1.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -593,7 +593,7 @@ async function acquireLocalLock(lockName, options = {}) {
593
593
  `[memoraone-mcp] Failed to acquire lock ${lockName} after ${maxRetries} retries`
594
594
  );
595
595
  }
596
- await new Promise((resolve10) => setTimeout(resolve10, retryDelayMs));
596
+ await new Promise((resolve11) => setTimeout(resolve11, retryDelayMs));
597
597
  continue;
598
598
  }
599
599
  throw err;
@@ -612,9 +612,9 @@ async function withLocalLock(lockName, fn, options = {}) {
612
612
 
613
613
  // src/localState/localConnectClient.ts
614
614
  var path6 = __toESM(require("path"), 1);
615
- async function requestJson(baseUrl, method, path15, options = {}) {
615
+ async function requestJson(baseUrl, method, path16, options = {}) {
616
616
  const fetchImpl = options.fetchImpl ?? fetch;
617
- const url = `${baseUrl.replace(/\/+$/, "")}${path15.startsWith("/") ? path15 : `/${path15}`}`;
617
+ const url = `${baseUrl.replace(/\/+$/, "")}${path16.startsWith("/") ? path16 : `/${path16}`}`;
618
618
  const res = await fetchImpl(url, {
619
619
  method,
620
620
  headers: {
@@ -950,9 +950,9 @@ var MemoraClient = class {
950
950
  ...options?.headers ?? {}
951
951
  };
952
952
  }
953
- async perform(method, path15, body, options, retried = false) {
953
+ async perform(method, path16, body, options, retried = false) {
954
954
  const nonce = crypto2.randomBytes(8).toString("hex");
955
- const url = `${this.baseUrl}${path15.startsWith("/") ? path15 : `/${path15}`}`;
955
+ const url = `${this.baseUrl}${path16.startsWith("/") ? path16 : `/${path16}`}`;
956
956
  this.resolveProjectId();
957
957
  console.error(
958
958
  `[memoraone-mcp][info] requestJson nonce=${nonce} stage=before_fetch method=${method} url=${url}`
@@ -979,7 +979,7 @@ var MemoraClient = class {
979
979
  if (res.status === 401 && !options?.skipAuthRefresh && !retried) {
980
980
  try {
981
981
  await this.refreshAccessToken();
982
- return this.perform(method, path15, body, options, true);
982
+ return this.perform(method, path16, body, options, true);
983
983
  } catch (err) {
984
984
  if (err instanceof ReconnectRequiredError) {
985
985
  await this.markReconnectRequired();
@@ -999,24 +999,22 @@ var MemoraClient = class {
999
999
  }
1000
1000
  return res.text ? JSON.parse(res.text) : null;
1001
1001
  }
1002
- async post(path15, body, options) {
1003
- console.error(`[memoraone-mcp][info] MemoraClient.post ENTER path=${path15}`);
1004
- const result = await this.perform("POST", path15, body, options);
1005
- console.error(`[memoraone-mcp][info] MemoraClient.post EXIT path=${path15}`);
1002
+ async post(path16, body, options) {
1003
+ console.error(`[memoraone-mcp][info] MemoraClient.post ENTER path=${path16}`);
1004
+ const result = await this.perform("POST", path16, body, options);
1005
+ console.error(`[memoraone-mcp][info] MemoraClient.post EXIT path=${path16}`);
1006
1006
  return result;
1007
1007
  }
1008
- async get(path15, options) {
1009
- return this.perform("GET", path15, void 0, options);
1008
+ async get(path16, options) {
1009
+ return this.perform("GET", path16, void 0, options);
1010
1010
  }
1011
1011
  };
1012
1012
  var memoraClient_default = MemoraClient;
1013
1013
 
1014
1014
  // src/projectBinding.ts
1015
- var fs8 = __toESM(require("fs/promises"), 1);
1016
1015
  var path11 = __toESM(require("path"), 1);
1017
1016
 
1018
1017
  // src/localState/resolveLocalBinding.ts
1019
- var fs7 = __toESM(require("fs/promises"), 1);
1020
1018
  var path10 = __toESM(require("path"), 1);
1021
1019
 
1022
1020
  // src/localState/pathIndex.ts
@@ -1234,19 +1232,9 @@ function identityMatchesStored(stored, current) {
1234
1232
  }
1235
1233
 
1236
1234
  // src/localState/resolveLocalBinding.ts
1237
- async function detectLegacyM1Warning(workspaceRoot) {
1238
- const candidate = path10.join(path10.resolve(workspaceRoot), CANONICAL_M1_FILENAME);
1239
- try {
1240
- await fs7.access(candidate);
1241
- return candidate;
1242
- } catch {
1243
- return void 0;
1244
- }
1245
- }
1246
1235
  async function ensureRepositoryBindingForRoot(workspaceRoot, options = {}) {
1247
1236
  const resolved = path10.resolve(workspaceRoot);
1248
1237
  const identity = await captureRootFilesystemIdentity(resolved, options.identityDeps);
1249
- const legacyM1WarningPath = await detectLegacyM1Warning(resolved);
1250
1238
  const index = await loadPathIndex(options.homeDir);
1251
1239
  const lookup = lookupPathIndex(index, resolved, identity);
1252
1240
  if (lookup.kind === "path") {
@@ -1270,8 +1258,7 @@ async function ensureRepositoryBindingForRoot(workspaceRoot, options = {}) {
1270
1258
  return {
1271
1259
  repositoryBindingId: record.repositoryBindingId,
1272
1260
  identity,
1273
- created: false,
1274
- legacyM1WarningPath
1261
+ created: false
1275
1262
  };
1276
1263
  }
1277
1264
  }
@@ -1295,8 +1282,7 @@ async function ensureRepositoryBindingForRoot(workspaceRoot, options = {}) {
1295
1282
  repositoryBindingId: record.repositoryBindingId,
1296
1283
  identity,
1297
1284
  created: false,
1298
- renamedFrom: lookup.previousPath,
1299
- legacyM1WarningPath
1285
+ renamedFrom: lookup.previousPath
1300
1286
  };
1301
1287
  }
1302
1288
  }
@@ -1309,13 +1295,36 @@ async function ensureRepositoryBindingForRoot(workspaceRoot, options = {}) {
1309
1295
  return {
1310
1296
  repositoryBindingId,
1311
1297
  identity,
1312
- created: true,
1313
- legacyM1WarningPath
1298
+ created: true
1299
+ };
1300
+ }
1301
+ async function lookupLocalBindingIdentity(workspaceRoot, options = {}) {
1302
+ const resolved = path10.resolve(workspaceRoot);
1303
+ const identity = await captureRootFilesystemIdentity(resolved, options.identityDeps);
1304
+ const index = await loadPathIndex(options.homeDir);
1305
+ const lookup = lookupPathIndex(index, resolved, identity);
1306
+ if (lookup.kind !== "path") {
1307
+ return { status: "unbound", workspaceRoot: resolved };
1308
+ }
1309
+ const record = await readBindingRecord(lookup.repositoryBindingId, options.homeDir);
1310
+ if (!record) {
1311
+ return { status: "unbound", workspaceRoot: resolved };
1312
+ }
1313
+ if (path10.resolve(record.workspaceRoot) !== resolved) {
1314
+ return { status: "unbound", workspaceRoot: resolved };
1315
+ }
1316
+ if (!identityMatchesStored(record.filesystemIdentity, identity)) {
1317
+ return { status: "unbound", workspaceRoot: resolved };
1318
+ }
1319
+ return {
1320
+ status: "resolved",
1321
+ workspaceRoot: resolved,
1322
+ repositoryBindingId: record.repositoryBindingId
1314
1323
  };
1315
1324
  }
1316
1325
  async function resolveLocalBinding(workspaceRoot, options = {}) {
1317
1326
  const resolved = path10.resolve(workspaceRoot);
1318
- const { repositoryBindingId, legacyM1WarningPath } = await ensureRepositoryBindingForRoot(
1327
+ const { repositoryBindingId } = await ensureRepositoryBindingForRoot(
1319
1328
  resolved,
1320
1329
  { ...options, createIfMissing: false }
1321
1330
  );
@@ -1358,13 +1367,11 @@ async function resolveLocalBinding(workspaceRoot, options = {}) {
1358
1367
  installationPublicId: record.installationPublicId ?? creds.installationPublicId,
1359
1368
  environment: record.environment,
1360
1369
  bindingSource: "local-binding",
1361
- status: record.status,
1362
- legacyM1WarningPath
1370
+ status: record.status
1363
1371
  };
1364
1372
  }
1365
1373
 
1366
1374
  // src/projectBinding.ts
1367
- var CANONICAL_M1_FILENAME = "memoraone.m1";
1368
1375
  function toResolvedBinding(local) {
1369
1376
  return {
1370
1377
  repositoryBindingId: local.repositoryBindingId,
@@ -1373,23 +1380,9 @@ function toResolvedBinding(local) {
1373
1380
  installationPublicId: local.installationPublicId,
1374
1381
  environment: local.environment,
1375
1382
  bindingSource: "local-binding",
1376
- status: local.status,
1377
- legacyM1WarningPath: local.legacyM1WarningPath
1383
+ status: local.status
1378
1384
  };
1379
1385
  }
1380
- async function warnLegacyM1IfPresent(workspaceRoot) {
1381
- const candidate = path11.join(path11.resolve(workspaceRoot), CANONICAL_M1_FILENAME);
1382
- try {
1383
- await fs8.access(candidate);
1384
- process.stderr.write(
1385
- `[memoraone-mcp] warning: ignoring legacy ${CANONICAL_M1_FILENAME} (not used for credentials or binding)
1386
- `
1387
- );
1388
- return candidate;
1389
- } catch {
1390
- return void 0;
1391
- }
1392
- }
1393
1386
  async function resolveAuthoritativeBinding(workspaceRoot, _options = {}) {
1394
1387
  const candidates = normalizeWorkspaceSearchRoots(workspaceRoot);
1395
1388
  if (candidates.length === 0) {
@@ -1399,7 +1392,6 @@ async function resolveAuthoritativeBinding(workspaceRoot, _options = {}) {
1399
1392
  }
1400
1393
  const bindings = [];
1401
1394
  for (const root of candidates) {
1402
- await warnLegacyM1IfPresent(root);
1403
1395
  try {
1404
1396
  const local = await resolveLocalBinding(root);
1405
1397
  bindings.push(toResolvedBinding(local));
@@ -1690,9 +1682,148 @@ async function resolveBindingFromInitializeParams(params, options = {}) {
1690
1682
  });
1691
1683
  }
1692
1684
 
1693
- // src/bindingSidecar.ts
1694
- var fs9 = __toESM(require("fs"), 1);
1685
+ // src/pluginStartupBindingWait.ts
1695
1686
  var path13 = __toESM(require("path"), 1);
1687
+ var MEMORAONE_PLUGIN_ENV = "MEMORAONE_PLUGIN";
1688
+ var MEMORAONE_CURSOR_PLUGIN = "cursor";
1689
+ var PLUGIN_STARTUP_BINDING_WAIT_TIMEOUT_MS = 5 * 60 * 1e3;
1690
+ var PLUGIN_STARTUP_BINDING_WAIT_INTERVAL_MS = 250;
1691
+ var UNBOUND_MESSAGE_MARKERS = [
1692
+ "No local MemoraOne binding for this workspace",
1693
+ "No local binding for workspace"
1694
+ ];
1695
+ var CREDENTIALS_MISSING_MARKER = "Installation credentials missing";
1696
+ function errorMessage(err) {
1697
+ return err instanceof Error ? err.message : String(err);
1698
+ }
1699
+ function isUnboundPluginStartupBindingMessage(message) {
1700
+ return UNBOUND_MESSAGE_MARKERS.some((marker) => message.includes(marker));
1701
+ }
1702
+ function isCredentialsMissingMessage(message) {
1703
+ return message.includes(CREDENTIALS_MISSING_MARKER);
1704
+ }
1705
+ async function pendingBindingWindowOpen(isPendingBinding) {
1706
+ if (!isPendingBinding) {
1707
+ return false;
1708
+ }
1709
+ try {
1710
+ return Boolean(await isPendingBinding());
1711
+ } catch {
1712
+ return false;
1713
+ }
1714
+ }
1715
+ function shouldWaitForPluginStartupBinding(options = {}) {
1716
+ const env2 = options.env ?? process.env;
1717
+ if (env2[MEMORAONE_PLUGIN_ENV] !== MEMORAONE_CURSOR_PLUGIN) {
1718
+ return false;
1719
+ }
1720
+ if (options.daemonBindingHint) {
1721
+ return false;
1722
+ }
1723
+ return true;
1724
+ }
1725
+ function collectPluginStartupBindingRoots(options) {
1726
+ const seen = /* @__PURE__ */ new Set();
1727
+ const roots = [];
1728
+ const add = (raw) => {
1729
+ if (raw === void 0 || raw === null || raw.trim() === "") {
1730
+ return;
1731
+ }
1732
+ const resolved = path13.resolve(raw);
1733
+ if (seen.has(resolved)) {
1734
+ return;
1735
+ }
1736
+ seen.add(resolved);
1737
+ roots.push(resolved);
1738
+ };
1739
+ for (const root of options.initializeRoots ?? []) {
1740
+ add(root);
1741
+ }
1742
+ for (const uri of options.rootsListUris ?? []) {
1743
+ add(uriToPath(uri));
1744
+ }
1745
+ add(getRepoScopedWorkspaceHint(options.env));
1746
+ return roots;
1747
+ }
1748
+ async function hasPendingPluginStartupBinding(workspaceRoots, options = {}) {
1749
+ for (const root of workspaceRoots) {
1750
+ try {
1751
+ const identity = await lookupLocalBindingIdentity(root, {
1752
+ homeDir: options.homeDir
1753
+ });
1754
+ if (identity.status !== "resolved") {
1755
+ continue;
1756
+ }
1757
+ const record = await readBindingRecord(
1758
+ identity.repositoryBindingId,
1759
+ options.homeDir
1760
+ );
1761
+ if (record?.status === "pending") {
1762
+ return true;
1763
+ }
1764
+ } catch {
1765
+ continue;
1766
+ }
1767
+ }
1768
+ return false;
1769
+ }
1770
+ async function isRetryablePluginStartupBindingError(err, options = {}) {
1771
+ const message = errorMessage(err);
1772
+ if (isUnboundPluginStartupBindingMessage(message)) {
1773
+ return true;
1774
+ }
1775
+ if (isCredentialsMissingMessage(message) && await pendingBindingWindowOpen(options.isPendingBinding)) {
1776
+ return true;
1777
+ }
1778
+ return false;
1779
+ }
1780
+ function defaultSleep(ms) {
1781
+ return new Promise((resolve11) => {
1782
+ setTimeout(resolve11, ms);
1783
+ });
1784
+ }
1785
+ async function waitForPluginStartupBinding(resolve11, options = {}) {
1786
+ const now = options.now ?? Date.now;
1787
+ const sleep = options.sleep ?? defaultSleep;
1788
+ const timeoutMs = options.timeoutMs ?? PLUGIN_STARTUP_BINDING_WAIT_TIMEOUT_MS;
1789
+ const intervalMs = options.intervalMs ?? PLUGIN_STARTUP_BINDING_WAIT_INTERVAL_MS;
1790
+ const startedAt = now();
1791
+ let waitingLogged = false;
1792
+ let lastError;
1793
+ while (true) {
1794
+ try {
1795
+ const binding = await resolve11();
1796
+ if (waitingLogged) {
1797
+ options.log?.(
1798
+ `local MemoraOne binding available after Cursor plugin pairing (${now() - startedAt}ms)`
1799
+ );
1800
+ }
1801
+ return binding;
1802
+ } catch (err) {
1803
+ lastError = err;
1804
+ const retryable = await isRetryablePluginStartupBindingError(err, {
1805
+ isPendingBinding: options.isPendingBinding
1806
+ });
1807
+ if (!retryable) {
1808
+ throw err;
1809
+ }
1810
+ if (now() - startedAt >= timeoutMs) {
1811
+ throw lastError;
1812
+ }
1813
+ if (!waitingLogged) {
1814
+ options.log?.(
1815
+ "waiting for local MemoraOne binding after Cursor plugin pairing"
1816
+ );
1817
+ waitingLogged = true;
1818
+ }
1819
+ await sleep(intervalMs);
1820
+ }
1821
+ }
1822
+ }
1823
+
1824
+ // src/bindingSidecar.ts
1825
+ var fs7 = __toESM(require("fs"), 1);
1826
+ var path14 = __toESM(require("path"), 1);
1696
1827
  function bindingSidecarPath(socketPath) {
1697
1828
  if (socketPath.endsWith(".sock")) {
1698
1829
  return `${socketPath.slice(0, -".sock".length)}.binding.json`;
@@ -1716,7 +1847,7 @@ function writeBindingSidecar(socketPath, binding, ideType = resolveBindingIdeTyp
1716
1847
  if (/["']?(accessToken|refreshToken|apiKey|clientRedeemKey|clientRefreshKey)["']?\s*:/.test(text)) {
1717
1848
  throw new Error("[memoraone-mcp] Refusing to write sidecar containing secrets");
1718
1849
  }
1719
- fs9.writeFileSync(bindingSidecarPath(socketPath), text, "utf8");
1850
+ fs7.writeFileSync(bindingSidecarPath(socketPath), text, "utf8");
1720
1851
  }
1721
1852
 
1722
1853
  // src/bridgeClientRoots.ts
@@ -2153,9 +2284,9 @@ function buildPersonalContextPath(parsed2) {
2153
2284
  }
2154
2285
  async function handleGetPersonalContext(client, args) {
2155
2286
  const parsed2 = getPersonalContextInputSchema.parse(args ?? {});
2156
- const path15 = buildPersonalContextPath(parsed2);
2287
+ const path16 = buildPersonalContextPath(parsed2);
2157
2288
  try {
2158
- const result = await client.get(path15);
2289
+ const result = await client.get(path16);
2159
2290
  return { ok: true, result };
2160
2291
  } catch (err) {
2161
2292
  if (err instanceof MemoraOneHttpError) {
@@ -2627,8 +2758,8 @@ async function handleCreateConceptVersion(client, args) {
2627
2758
  if (input.tags !== void 0) body.tags = input.tags;
2628
2759
  if (input.parent_id !== void 0) body.parent_id = input.parent_id;
2629
2760
  try {
2630
- const path15 = `/concepts/${encodeURIComponent(input.id)}/versions`;
2631
- return projectCreatedConceptVersionResponse(await client.post(path15, body));
2761
+ const path16 = `/concepts/${encodeURIComponent(input.id)}/versions`;
2762
+ return projectCreatedConceptVersionResponse(await client.post(path16, body));
2632
2763
  } catch (error) {
2633
2764
  rethrowToolRequestError("memora_create_concept_version", error);
2634
2765
  }
@@ -2662,8 +2793,8 @@ function handleBindingStatus(binding, options = {}) {
2662
2793
 
2663
2794
  // src/heartbeat.ts
2664
2795
  var crypto5 = __toESM(require("crypto"), 1);
2665
- var fs10 = __toESM(require("fs"), 1);
2666
- var path14 = __toESM(require("path"), 1);
2796
+ var fs8 = __toESM(require("fs"), 1);
2797
+ var path15 = __toESM(require("path"), 1);
2667
2798
 
2668
2799
  // src/localState/mcpSessionId.ts
2669
2800
  var import_node_crypto4 = require("crypto");
@@ -2696,13 +2827,13 @@ function resolvePackageVersion() {
2696
2827
  if (fromEnv) return fromEnv;
2697
2828
  const moduleDir = typeof __dirname !== "undefined" ? __dirname : void 0;
2698
2829
  const candidates = [
2699
- ...moduleDir ? [path14.join(moduleDir, "..", "package.json"), path14.join(moduleDir, "package.json")] : [],
2700
- path14.join(process.cwd(), "package.json"),
2701
- path14.join(process.cwd(), "packages", "mcp", "package.json")
2830
+ ...moduleDir ? [path15.join(moduleDir, "..", "package.json"), path15.join(moduleDir, "package.json")] : [],
2831
+ path15.join(process.cwd(), "package.json"),
2832
+ path15.join(process.cwd(), "packages", "mcp", "package.json")
2702
2833
  ];
2703
2834
  for (const candidate of candidates) {
2704
2835
  try {
2705
- const pkg = JSON.parse(fs10.readFileSync(candidate, "utf8"));
2836
+ const pkg = JSON.parse(fs8.readFileSync(candidate, "utf8"));
2706
2837
  if (typeof pkg.version === "string" && pkg.version.trim()) {
2707
2838
  return pkg.version.trim();
2708
2839
  }
@@ -3217,8 +3348,8 @@ function registerToolWithWorklog(server, runtime, sessionContext, toolName, desc
3217
3348
  async function main(opts = {}) {
3218
3349
  let bindingReadyResolve = null;
3219
3350
  let bindingReadyReject = null;
3220
- const bindingReady = new Promise((resolve10, reject) => {
3221
- bindingReadyResolve = resolve10;
3351
+ const bindingReady = new Promise((resolve11, reject) => {
3352
+ bindingReadyResolve = resolve11;
3222
3353
  bindingReadyReject = reject;
3223
3354
  });
3224
3355
  const devMode = Boolean(config2.devMode);
@@ -3299,12 +3430,25 @@ async function main(opts = {}) {
3299
3430
  console.error("[memoraone-mcp][init-debug] Workspace resolution strategy: initialize.rootUri");
3300
3431
  }
3301
3432
  }
3302
- const binding = await resolveBindingFromInitializeParams(params, {
3433
+ const resolveBinding = () => resolveBindingFromInitializeParams(params, {
3303
3434
  fallbackWorkspaceRoots: getEnvWorkspaceRootCandidates(),
3304
3435
  rootsListUris,
3305
3436
  rootsListAttempted,
3306
3437
  ...getBridgeBindingResolveOptions()
3307
3438
  });
3439
+ const binding = shouldWaitForPluginStartupBinding({
3440
+ env: process.env,
3441
+ daemonBindingHint: opts.daemonBindingHint
3442
+ }) ? await waitForPluginStartupBinding(resolveBinding, {
3443
+ isPendingBinding: () => hasPendingPluginStartupBinding(
3444
+ collectPluginStartupBindingRoots({
3445
+ initializeRoots,
3446
+ rootsListUris,
3447
+ env: process.env
3448
+ })
3449
+ ),
3450
+ log: (msg) => console.error(`[memoraone-mcp] ${msg}`)
3451
+ }) : await resolveBinding();
3308
3452
  workspaceRoot = binding.workspaceRoot;
3309
3453
  return binding;
3310
3454
  };
@@ -3578,11 +3722,6 @@ async function main(opts = {}) {
3578
3722
  if (debugLog) {
3579
3723
  console.error("[memoraone-mcp][debug] Resolved installation credentials from OS keyring");
3580
3724
  }
3581
- if (binding.legacyM1WarningPath) {
3582
- console.error(
3583
- `[memoraone-mcp] warning: ignoring legacy memoraone.m1 at ${binding.legacyM1WarningPath}`
3584
- );
3585
- }
3586
3725
  const projectId = binding.projectId;
3587
3726
  const existing = getBoundProjectId();
3588
3727
  if (existing !== null && existing !== projectId) {
@@ -3691,10 +3830,10 @@ async function main(opts = {}) {
3691
3830
  console.error("[memoraone-mcp] MCP server ready");
3692
3831
  }
3693
3832
  if (opts.sessionSocket) {
3694
- await new Promise((resolve10) => {
3833
+ await new Promise((resolve11) => {
3695
3834
  opts.sessionSocket.once("close", () => {
3696
3835
  shutdown("session closed", false);
3697
- resolve10();
3836
+ resolve11();
3698
3837
  });
3699
3838
  });
3700
3839
  }