@memoraone/mcp 0.1.52 → 0.1.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -231,7 +231,7 @@ var require_package = __commonJS({
231
231
  "package.json"(exports2, module2) {
232
232
  module2.exports = {
233
233
  name: "@memoraone/mcp",
234
- version: "0.1.52",
234
+ version: "0.1.54",
235
235
  type: "module",
236
236
  main: "dist/index.cjs",
237
237
  exports: {
@@ -531,7 +531,7 @@ async function acquireLocalLock(lockName, options = {}) {
531
531
  `[memoraone-mcp] Failed to acquire lock ${lockName} after ${maxRetries} retries`
532
532
  );
533
533
  }
534
- await new Promise((resolve44) => setTimeout(resolve44, retryDelayMs));
534
+ await new Promise((resolve45) => setTimeout(resolve45, retryDelayMs));
535
535
  continue;
536
536
  }
537
537
  throw err;
@@ -1092,9 +1092,9 @@ var init_memoraClient = __esm({
1092
1092
  });
1093
1093
 
1094
1094
  // src/localState/localConnectClient.ts
1095
- async function requestJson(baseUrl, method, path48, options = {}) {
1095
+ async function requestJson(baseUrl, method, path49, options = {}) {
1096
1096
  const fetchImpl = options.fetchImpl ?? fetch;
1097
- const url = `${baseUrl.replace(/\/+$/, "")}${path48.startsWith("/") ? path48 : `/${path48}`}`;
1097
+ const url = `${baseUrl.replace(/\/+$/, "")}${path49.startsWith("/") ? path49 : `/${path49}`}`;
1098
1098
  const res = await fetchImpl(url, {
1099
1099
  method,
1100
1100
  headers: {
@@ -1511,7 +1511,10 @@ async function reconcileResolvedBindingWithDisk(cached, options = {}) {
1511
1511
  );
1512
1512
  }
1513
1513
  const workspaceRoot = path10.resolve(record.workspaceRoot);
1514
- const identity = await captureRootFilesystemIdentity(workspaceRoot);
1514
+ const identity = await captureRootFilesystemIdentity(
1515
+ workspaceRoot,
1516
+ options.identityDeps
1517
+ );
1515
1518
  if (!identitiesMatch(record.filesystemIdentity, identity)) {
1516
1519
  throw new ReconnectRequiredError(
1517
1520
  "[memoraone-mcp] Workspace filesystem identity changed. Run: memoraone-mcp connect <code>"
@@ -3918,7 +3921,7 @@ function formatOptionalJetBrainsHandshakeUnavailableDetail(detail) {
3918
3921
  async function verifyJetBrainsMcpHandshake(options) {
3919
3922
  const timeoutMs = options.timeoutMs ?? 15e3;
3920
3923
  const { server } = options;
3921
- return new Promise((resolve44) => {
3924
+ return new Promise((resolve45) => {
3922
3925
  let settled = false;
3923
3926
  const finish = (ok, detail, optionalUnavailable) => {
3924
3927
  if (settled) return;
@@ -3928,7 +3931,7 @@ async function verifyJetBrainsMcpHandshake(options) {
3928
3931
  child.kill();
3929
3932
  } catch {
3930
3933
  }
3931
- resolve44({ ok, detail, optionalUnavailable });
3934
+ resolve45({ ok, detail, optionalUnavailable });
3932
3935
  };
3933
3936
  const child = (0, import_node_child_process4.spawn)(server.command, [...server.args], {
3934
3937
  env: { ...process.env, ...server.env },
@@ -12961,13 +12964,13 @@ function writeEvent(event, stdoutWrite) {
12961
12964
  }
12962
12965
  function delay(ms, signal) {
12963
12966
  if (ms <= 0 || signal?.aborted) return Promise.resolve();
12964
- return new Promise((resolve44) => {
12967
+ return new Promise((resolve45) => {
12965
12968
  const timer = setTimeout(finish, ms);
12966
12969
  const onAbort = () => finish();
12967
12970
  function finish() {
12968
12971
  clearTimeout(timer);
12969
12972
  signal?.removeEventListener("abort", onAbort);
12970
- resolve44();
12973
+ resolve45();
12971
12974
  }
12972
12975
  signal?.addEventListener("abort", onAbort, { once: true });
12973
12976
  });
@@ -13270,13 +13273,180 @@ var init_runtimeIdentityCommand = __esm({
13270
13273
  }
13271
13274
  });
13272
13275
 
13276
+ // src/gatewayConfig.ts
13277
+ function resolveGatewayPort(env2 = process.env) {
13278
+ const raw = env2.MEMORAONE_GATEWAY_PORT?.trim();
13279
+ if (!raw) return GATEWAY_PORT;
13280
+ const parsed2 = Number.parseInt(raw, 10);
13281
+ if (!Number.isInteger(parsed2) || parsed2 < 1 || parsed2 > 65535) {
13282
+ throw new Error(
13283
+ `[memoraone-mcp] Invalid MEMORAONE_GATEWAY_PORT=${JSON.stringify(raw)} (expected 1\u201365535)`
13284
+ );
13285
+ }
13286
+ return parsed2;
13287
+ }
13288
+ function resolveGatewayListenConfig(env2 = process.env) {
13289
+ return {
13290
+ host: GATEWAY_HOST,
13291
+ port: resolveGatewayPort(env2)
13292
+ };
13293
+ }
13294
+ function getGatewayBaseUrl(env2 = process.env) {
13295
+ const { host, port } = resolveGatewayListenConfig(env2);
13296
+ return `http://${host}:${port}`;
13297
+ }
13298
+ function getGatewayHealthUrl(env2 = process.env) {
13299
+ return `${getGatewayBaseUrl(env2)}${GATEWAY_HEALTH_PATH}`;
13300
+ }
13301
+ function buildGatewaySpawnArgs(scriptPath) {
13302
+ return [scriptPath, "--gateway"];
13303
+ }
13304
+ var path47, GATEWAY_HOST, GATEWAY_PORT, GATEWAY_LOCK_NAME, GATEWAY_HEALTH_PATH;
13305
+ var init_gatewayConfig = __esm({
13306
+ "src/gatewayConfig.ts"() {
13307
+ path47 = __toESM(require("path"), 1);
13308
+ GATEWAY_HOST = "127.0.0.1";
13309
+ GATEWAY_PORT = 17355;
13310
+ GATEWAY_LOCK_NAME = "gateway";
13311
+ GATEWAY_HEALTH_PATH = "/health";
13312
+ }
13313
+ });
13314
+
13315
+ // src/gatewayEnsure.ts
13316
+ var gatewayEnsure_exports = {};
13317
+ __export(gatewayEnsure_exports, {
13318
+ cliEnsureGateway: () => cliEnsureGateway,
13319
+ ensureGateway: () => ensureGateway,
13320
+ probeGatewayHealth: () => probeGatewayHealth
13321
+ });
13322
+ async function probeGatewayHealth(env2 = process.env) {
13323
+ const url = getGatewayHealthUrl(env2);
13324
+ try {
13325
+ const res = await fetch(url, { signal: AbortSignal.timeout(750) });
13326
+ if (!res.ok) {
13327
+ return { healthy: false, reason: `HTTP ${res.status}` };
13328
+ }
13329
+ const body = await res.json();
13330
+ if (body?.ok !== true || typeof body.pid !== "number") {
13331
+ return { healthy: false, reason: "invalid health body" };
13332
+ }
13333
+ return { healthy: true, body: { ok: true, pid: body.pid } };
13334
+ } catch (err) {
13335
+ return { healthy: false, reason: String(err) };
13336
+ }
13337
+ }
13338
+ function defaultSpawnGateway(cliPath, env2) {
13339
+ return new Promise((resolve45, reject) => {
13340
+ let child;
13341
+ try {
13342
+ child = (0, import_node_child_process7.spawn)(process.execPath, buildGatewaySpawnArgs(cliPath), {
13343
+ detached: true,
13344
+ stdio: "ignore",
13345
+ env: { ...env2 }
13346
+ });
13347
+ } catch (err) {
13348
+ reject(err);
13349
+ return;
13350
+ }
13351
+ child.once("error", reject);
13352
+ child.unref();
13353
+ setImmediate(() => resolve45());
13354
+ });
13355
+ }
13356
+ async function waitUntilHealthy(probe, env2, waitTimeoutMs, pollIntervalMs) {
13357
+ const started = Date.now();
13358
+ let lastReason = "not started";
13359
+ while (Date.now() - started < waitTimeoutMs) {
13360
+ const result = await probe(env2);
13361
+ if (result.healthy) return result.body;
13362
+ lastReason = result.reason;
13363
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
13364
+ }
13365
+ const { host, port } = resolveGatewayListenConfig(env2);
13366
+ throw new Error(
13367
+ `[memoraone-mcp] Gateway did not become healthy on ${host}:${port} within ${waitTimeoutMs}ms (${lastReason})`
13368
+ );
13369
+ }
13370
+ async function ensureGateway(options = {}) {
13371
+ const env2 = options.env ?? process.env;
13372
+ const probe = options.probe ?? probeGatewayHealth;
13373
+ const spawnGateway = options.spawnGateway ?? defaultSpawnGateway;
13374
+ const waitTimeoutMs = options.waitTimeoutMs ?? 1e4;
13375
+ const pollIntervalMs = options.pollIntervalMs ?? 50;
13376
+ const listen = resolveGatewayListenConfig(env2);
13377
+ const lockRetryDelayMs = options.lockRetryDelayMs ?? 50;
13378
+ const lockMaxRetries = options.lockMaxRetries ?? Math.ceil(waitTimeoutMs / lockRetryDelayMs) + 40;
13379
+ const first = await probe(env2);
13380
+ if (first.healthy) {
13381
+ return { reused: true, pid: first.body.pid, listen };
13382
+ }
13383
+ return withLocalLock(
13384
+ GATEWAY_LOCK_NAME,
13385
+ async () => {
13386
+ const second = await probe(env2);
13387
+ if (second.healthy) {
13388
+ return { reused: true, pid: second.body.pid, listen };
13389
+ }
13390
+ const cliPath = options.cliPath ?? await resolveBuiltCliPathAsync() ?? process.argv[1];
13391
+ if (!cliPath) {
13392
+ throw new Error("[memoraone-mcp] Unable to resolve CLI path to spawn gateway");
13393
+ }
13394
+ log(`spawning gateway on ${listen.host}:${listen.port} via ${cliPath}`);
13395
+ await spawnGateway(cliPath, env2);
13396
+ const body = await waitUntilHealthy(probe, env2, waitTimeoutMs, pollIntervalMs);
13397
+ return { reused: false, pid: body.pid, listen };
13398
+ },
13399
+ {
13400
+ homeDir: options.homeDir,
13401
+ maxRetries: lockMaxRetries,
13402
+ retryDelayMs: lockRetryDelayMs,
13403
+ // Hold can be long while waiting for health; keep stale threshold above wait.
13404
+ maxLockAgeMs: Math.max(3e4, waitTimeoutMs + 5e3)
13405
+ }
13406
+ );
13407
+ }
13408
+ async function cliEnsureGateway(_argv = [], options = {}) {
13409
+ try {
13410
+ const result = await ensureGateway(options);
13411
+ const mode = result.reused ? "reused" : "started";
13412
+ process.stdout.write(
13413
+ `${JSON.stringify({
13414
+ status: "ok",
13415
+ mode,
13416
+ pid: result.pid,
13417
+ host: result.listen.host,
13418
+ port: result.listen.port
13419
+ })}
13420
+ `
13421
+ );
13422
+ return 0;
13423
+ } catch (err) {
13424
+ process.stderr.write(`[memoraone-mcp] ensure-gateway failed: ${String(err)}
13425
+ `);
13426
+ return 1;
13427
+ }
13428
+ }
13429
+ var import_node_child_process7, log;
13430
+ var init_gatewayEnsure = __esm({
13431
+ "src/gatewayEnsure.ts"() {
13432
+ import_node_child_process7 = require("child_process");
13433
+ init_localLocks();
13434
+ init_resolveBuiltCliPath();
13435
+ init_gatewayConfig();
13436
+ log = (msg) => {
13437
+ process.stderr.write(`[memoraone-mcp][ensure-gateway] ${msg}
13438
+ `);
13439
+ };
13440
+ }
13441
+ });
13442
+
13273
13443
  // src/bridgeClientRoots.ts
13274
13444
  function isInitializeDebugEnabled(env2 = process.env) {
13275
13445
  return TRUTHY.has(String(env2.MEMORAONE_DEBUG_INIT ?? "").trim().toLowerCase());
13276
13446
  }
13277
- function logInitializeDebug(log, env2, msg) {
13447
+ function logInitializeDebug(log2, env2, msg) {
13278
13448
  if (isInitializeDebugEnabled(env2)) {
13279
- log(`[init-debug] ${msg}`);
13449
+ log2(`[init-debug] ${msg}`);
13280
13450
  }
13281
13451
  }
13282
13452
  function clientDeclaresRootsCapability(params) {
@@ -13326,7 +13496,7 @@ function extractRootsUrisFromListResult(result) {
13326
13496
  }
13327
13497
  async function requestClientRootsListUris(options) {
13328
13498
  const env2 = options.env ?? process.env;
13329
- const log = options.log ?? (() => {
13499
+ const log2 = options.log ?? (() => {
13330
13500
  });
13331
13501
  const requestId = nextBridgeRequestId++;
13332
13502
  const deferredLines = [];
@@ -13337,7 +13507,7 @@ async function requestClientRootsListUris(options) {
13337
13507
  params: {}
13338
13508
  };
13339
13509
  logInitializeDebug(
13340
- log,
13510
+ log2,
13341
13511
  env2,
13342
13512
  `sending roots/list id=${requestId} clientDeclaresRoots=${clientDeclaresRootsCapability(options.initializeParams)}`
13343
13513
  );
@@ -13356,12 +13526,12 @@ async function requestClientRootsListUris(options) {
13356
13526
  try {
13357
13527
  message = JSON.parse(trimmed);
13358
13528
  } catch (err) {
13359
- logInitializeDebug(log, env2, `ignored non-JSON line while waiting for roots/list: ${String(err)}`);
13529
+ logInitializeDebug(log2, env2, `ignored non-JSON line while waiting for roots/list: ${String(err)}`);
13360
13530
  continue;
13361
13531
  }
13362
13532
  if (message.id !== requestId) {
13363
13533
  logInitializeDebug(
13364
- log,
13534
+ log2,
13365
13535
  env2,
13366
13536
  `deferred JSON-RPC while waiting for roots/list id=${requestId}: ${trimmed.slice(0, 200)}`
13367
13537
  );
@@ -13374,7 +13544,7 @@ async function requestClientRootsListUris(options) {
13374
13544
  );
13375
13545
  }
13376
13546
  const uris = extractRootsUrisFromListResult(message.result);
13377
- logInitializeDebug(log, env2, `roots/list id=${requestId} returned ${uris.length}: ${JSON.stringify(uris)}`);
13547
+ logInitializeDebug(log2, env2, `roots/list id=${requestId} returned ${uris.length}: ${JSON.stringify(uris)}`);
13378
13548
  return { uris, deferredLines };
13379
13549
  }
13380
13550
  }
@@ -13410,8 +13580,8 @@ var init_bridgeClientRoots = __esm({
13410
13580
  if (this.closed) {
13411
13581
  return null;
13412
13582
  }
13413
- return new Promise((resolve44) => {
13414
- this.waiters.push(resolve44);
13583
+ return new Promise((resolve45) => {
13584
+ this.waiters.push(resolve45);
13415
13585
  });
13416
13586
  }
13417
13587
  /** Re-queue lines read during an intermediate protocol step (e.g. roots/list) for the main bridge loop. */
@@ -13480,7 +13650,7 @@ function collectPluginStartupBindingRoots(options) {
13480
13650
  if (raw === void 0 || raw === null || raw.trim() === "") {
13481
13651
  return;
13482
13652
  }
13483
- const resolved = path47.resolve(raw);
13653
+ const resolved = path48.resolve(raw);
13484
13654
  if (seen.has(resolved)) {
13485
13655
  return;
13486
13656
  }
@@ -13529,11 +13699,11 @@ async function isRetryablePluginStartupBindingError(err, options = {}) {
13529
13699
  return false;
13530
13700
  }
13531
13701
  function defaultSleep(ms) {
13532
- return new Promise((resolve44) => {
13533
- setTimeout(resolve44, ms);
13702
+ return new Promise((resolve45) => {
13703
+ setTimeout(resolve45, ms);
13534
13704
  });
13535
13705
  }
13536
- async function waitForPluginStartupBinding(resolve44, options = {}) {
13706
+ async function waitForPluginStartupBinding(resolve45, options = {}) {
13537
13707
  const now = options.now ?? Date.now;
13538
13708
  const sleep = options.sleep ?? defaultSleep;
13539
13709
  const timeoutMs = options.timeoutMs ?? PLUGIN_STARTUP_BINDING_WAIT_TIMEOUT_MS;
@@ -13543,7 +13713,7 @@ async function waitForPluginStartupBinding(resolve44, options = {}) {
13543
13713
  let lastError;
13544
13714
  while (true) {
13545
13715
  try {
13546
- const binding = await resolve44();
13716
+ const binding = await resolve45();
13547
13717
  if (waitingLogged) {
13548
13718
  options.log?.(
13549
13719
  `local MemoraOne binding available after Cursor plugin pairing (${now() - startedAt}ms)`
@@ -13587,10 +13757,10 @@ async function waitForPluginStartupBinding(resolve44, options = {}) {
13587
13757
  }
13588
13758
  }
13589
13759
  }
13590
- var path47, MEMORAONE_PLUGIN_ENV, MEMORAONE_CURSOR_PLUGIN, PLUGIN_STARTUP_BINDING_WAIT_TIMEOUT_MS, PLUGIN_STARTUP_BINDING_WAIT_INTERVAL_MS, UNBOUND_MESSAGE_MARKERS, CREDENTIALS_MISSING_MARKER, PLUGIN_STARTUP_SETUP_REQUIRED;
13760
+ var path48, MEMORAONE_PLUGIN_ENV, MEMORAONE_CURSOR_PLUGIN, PLUGIN_STARTUP_BINDING_WAIT_TIMEOUT_MS, PLUGIN_STARTUP_BINDING_WAIT_INTERVAL_MS, UNBOUND_MESSAGE_MARKERS, CREDENTIALS_MISSING_MARKER, PLUGIN_STARTUP_SETUP_REQUIRED;
13591
13761
  var init_pluginStartupBindingWait = __esm({
13592
13762
  "src/pluginStartupBindingWait.ts"() {
13593
- path47 = __toESM(require("path"), 1);
13763
+ path48 = __toESM(require("path"), 1);
13594
13764
  init_initializeBinding();
13595
13765
  init_bindingStore();
13596
13766
  init_resolveLocalBinding();
@@ -13815,15 +13985,26 @@ function extractJsonRpcId(line) {
13815
13985
  return void 0;
13816
13986
  }
13817
13987
  }
13818
- function connectWithRetry(socketPath, log, maxRetries, retryDelayMs, connect2) {
13819
- return new Promise((resolve44, reject) => {
13988
+ function extractInitializeProtocolVersion(line) {
13989
+ try {
13990
+ const message = JSON.parse(line.trim());
13991
+ const version2 = message.params?.protocolVersion;
13992
+ if (typeof version2 === "string" && version2.trim() !== "") {
13993
+ return version2;
13994
+ }
13995
+ } catch {
13996
+ }
13997
+ return "2024-11-05";
13998
+ }
13999
+ function connectWithRetry(socketPath, log2, maxRetries, retryDelayMs, connect2) {
14000
+ return new Promise((resolve45, reject) => {
13820
14001
  const tryConnect = (attempt) => {
13821
- connect2(socketPath).then(resolve44).catch((err) => {
14002
+ connect2(socketPath).then(resolve45).catch((err) => {
13822
14003
  if (attempt >= maxRetries) {
13823
14004
  reject(err);
13824
14005
  return;
13825
14006
  }
13826
- log(`connect attempt ${attempt + 1} failed, retrying in ${retryDelayMs}ms: ${String(err)}`);
14007
+ log2(`connect attempt ${attempt + 1} failed, retrying in ${retryDelayMs}ms: ${String(err)}`);
13827
14008
  setTimeout(() => tryConnect(attempt + 1), retryDelayMs);
13828
14009
  });
13829
14010
  };
@@ -13840,13 +14021,13 @@ async function resolveBridgeSessionBinding(params, env2 = process.env, options =
13840
14021
  ...bridgeOptions
13841
14022
  });
13842
14023
  }
13843
- async function stopDaemonsForStaleBinding(stale, env2, log, ideTypeOverride) {
14024
+ async function stopDaemonsForStaleBinding(stale, env2, log2, ideTypeOverride) {
13844
14025
  const ideType = resolveBindingIdeType(env2, ideTypeOverride);
13845
14026
  let processes;
13846
14027
  try {
13847
14028
  processes = await defaultListDaemonProcesses();
13848
14029
  } catch (err) {
13849
- log(`could not list daemon processes while replacing stale binding: ${String(err)}`);
14030
+ log2(`could not list daemon processes while replacing stale binding: ${String(err)}`);
13850
14031
  return;
13851
14032
  }
13852
14033
  for (const proc of processes) {
@@ -13855,11 +14036,11 @@ async function stopDaemonsForStaleBinding(stale, env2, log, ideTypeOverride) {
13855
14036
  if (!matchesByBinding && !matchesByProject) continue;
13856
14037
  try {
13857
14038
  process.kill(proc.pid, "SIGTERM");
13858
- log(
14039
+ log2(
13859
14040
  `stopped stale daemon pid=${proc.pid} binding=${stale.repositoryBindingId} project=${stale.projectId}`
13860
14041
  );
13861
14042
  } catch (err) {
13862
- log(`failed to stop stale daemon pid=${proc.pid}: ${String(err)}`);
14043
+ log2(`failed to stop stale daemon pid=${proc.pid}: ${String(err)}`);
13863
14044
  }
13864
14045
  }
13865
14046
  }
@@ -13926,14 +14107,14 @@ async function runBridgeProxy(options) {
13926
14107
  ensureBaseDir();
13927
14108
  const stdin = options.stdin ?? process.stdin;
13928
14109
  const stdout = options.stdout ?? process.stdout;
13929
- const log = options.log ?? defaultLog;
14110
+ const log2 = options.log ?? defaultLog;
13930
14111
  const lineReader = options.lineReader ?? new StdioLineReader(stdin);
13931
14112
  const router = new BridgeDaemonRouter({ ...options, stdout, lineReader });
13932
14113
  try {
13933
14114
  while (true) {
13934
14115
  const line = await lineReader.readLine();
13935
14116
  if (line === null) {
13936
- log("stdin EOF; closing daemon socket");
14117
+ log2("stdin EOF; closing daemon socket");
13937
14118
  break;
13938
14119
  }
13939
14120
  const trimmed = line.trim();
@@ -13947,17 +14128,25 @@ async function runBridgeProxy(options) {
13947
14128
  throw new Error(`[memoraone-mcp] Invalid JSON-RPC on stdin: ${String(err)}`);
13948
14129
  }
13949
14130
  if (message.method === "initialize") {
13950
- log("resolve binding from initialize request before daemon connect");
14131
+ log2("resolve binding from initialize request before daemon connect");
13951
14132
  const params = message.params ?? {};
13952
- await router.ensureDaemonForInitialize(params);
14133
+ const sessionKind = await router.ensureDaemonForInitialize(params);
14134
+ if (sessionKind === "setup_required") {
14135
+ router.completeSetupRequiredInitialize(trimmed);
14136
+ continue;
14137
+ }
13953
14138
  await router.forwardInitializeToDaemon(trimmed);
13954
14139
  await router.replayDeferredClientMessages();
13955
14140
  continue;
13956
14141
  }
14142
+ if (router.isSetupRequiredSession()) {
14143
+ router.handleSetupRequiredClientLine(trimmed);
14144
+ continue;
14145
+ }
13957
14146
  if (!router.hasClientInitialize()) {
13958
14147
  const method = typeof message.method === "string" && message.method.trim() !== "" ? message.method : "(unknown)";
13959
14148
  if (message.id !== void 0 && message.id !== null) {
13960
- log(
14149
+ log2(
13961
14150
  `pre-initialize request method=${method}; responding Method not found (legacy stdio probe)`
13962
14151
  );
13963
14152
  writePreInitializeJsonRpcError(
@@ -13966,7 +14155,7 @@ async function runBridgeProxy(options) {
13966
14155
  method
13967
14156
  );
13968
14157
  } else {
13969
- log(`ignoring pre-initialize notification method=${method}`);
14158
+ log2(`ignoring pre-initialize notification method=${method}`);
13970
14159
  }
13971
14160
  continue;
13972
14161
  }
@@ -13976,12 +14165,12 @@ async function runBridgeProxy(options) {
13976
14165
  router.close();
13977
14166
  }
13978
14167
  }
13979
- var net, readline4, import_node_child_process7, defaultLog, JSONRPC_METHOD_NOT_FOUND, BridgeDaemonRouter;
14168
+ var net, readline4, import_node_child_process8, defaultLog, JSONRPC_METHOD_NOT_FOUND, SETUP_REQUIRED_FINISH_SETUP_TOOL, BridgeDaemonRouter;
13980
14169
  var init_bridgeProxy = __esm({
13981
14170
  "src/bridgeProxy.ts"() {
13982
14171
  net = __toESM(require("net"), 1);
13983
14172
  readline4 = __toESM(require("readline"), 1);
13984
- import_node_child_process7 = require("child_process");
14173
+ import_node_child_process8 = require("child_process");
13985
14174
  init_bindingIdentity();
13986
14175
  init_bindingSidecar();
13987
14176
  init_bridgeClientRoots();
@@ -13996,6 +14185,11 @@ var init_bridgeProxy = __esm({
13996
14185
  `);
13997
14186
  };
13998
14187
  JSONRPC_METHOD_NOT_FOUND = -32601;
14188
+ SETUP_REQUIRED_FINISH_SETUP_TOOL = {
14189
+ name: "memora_finish_setup",
14190
+ description: "Start MemoraOne Cursor plugin pairing for this workspace so the user can finish setup. Returns an authorization URL and request metadata; does not open a browser. Use when this plugin-managed session is unbound (setup required).",
14191
+ inputSchema: { type: "object", properties: {} }
14192
+ };
13999
14193
  BridgeDaemonRouter = class {
14000
14194
  constructor(options) {
14001
14195
  this.activeSocket = null;
@@ -14006,6 +14200,8 @@ var init_bridgeProxy = __esm({
14006
14200
  this.pendingDeferredClientLines = [];
14007
14201
  this.handshakeDeferredClientLines = [];
14008
14202
  this.clientInitializeSeen = false;
14203
+ /** Unbound Cursor-plugin session: no daemon by design; handshake is local. */
14204
+ this.setupRequiredSession = false;
14009
14205
  /** Waiters for daemon JSON-RPC responses (keyed by request id). */
14010
14206
  this.pendingDaemonResponseWaiters = /* @__PURE__ */ new Map();
14011
14207
  this.env = options.env ?? process.env;
@@ -14015,13 +14211,13 @@ var init_bridgeProxy = __esm({
14015
14211
  this.maxRetries = options.maxRetries ?? 5;
14016
14212
  this.retryDelayMs = options.retryDelayMs ?? 200;
14017
14213
  this.lineReader = options.lineReader ?? null;
14018
- this.connectImpl = options.connect ?? ((socketPath) => new Promise((resolve44, reject) => {
14019
- const socket = net.connect(socketPath, () => resolve44(socket));
14214
+ this.connectImpl = options.connect ?? ((socketPath) => new Promise((resolve45, reject) => {
14215
+ const socket = net.connect(socketPath, () => resolve45(socket));
14020
14216
  socket.on("error", reject);
14021
14217
  }));
14022
14218
  this.spawnDaemonImpl = options.spawnDaemon ?? (async (binding) => {
14023
14219
  const ideType = this.activeIdeType;
14024
- const child = (0, import_node_child_process7.spawn)(
14220
+ const child = (0, import_node_child_process8.spawn)(
14025
14221
  process.execPath,
14026
14222
  buildDaemonSpawnArgs(this.cliPath, binding.repositoryBindingId, this.env, ideType),
14027
14223
  {
@@ -14056,6 +14252,9 @@ var init_bridgeProxy = __esm({
14056
14252
  hasClientInitialize() {
14057
14253
  return this.clientInitializeSeen;
14058
14254
  }
14255
+ isSetupRequiredSession() {
14256
+ return this.setupRequiredSession;
14257
+ }
14059
14258
  async ensureDaemonForInitialize(params) {
14060
14259
  logInitializeDebug(
14061
14260
  this.log,
@@ -14115,14 +14314,22 @@ var init_bridgeProxy = __esm({
14115
14314
  log: this.log
14116
14315
  }) : await resolveBinding();
14117
14316
  if (isPluginStartupSetupRequired(binding)) {
14118
- return;
14317
+ this.setupRequiredSession = true;
14318
+ this.activeBinding = null;
14319
+ this.detachSocketReader();
14320
+ const previousSocket = this.activeSocket;
14321
+ this.activeSocket = null;
14322
+ previousSocket?.destroy();
14323
+ this.log("setup_required: no active binding; initialize will complete locally");
14324
+ return "setup_required";
14119
14325
  }
14326
+ this.setupRequiredSession = false;
14120
14327
  const environmentLog = binding.environment !== void 0 ? ` environment=${binding.environment}` : "";
14121
14328
  this.log(
14122
14329
  `session binding binding=${binding.repositoryBindingId} project=${binding.projectId} workspace=${binding.workspaceRoot} source=${binding.bindingSource}${environmentLog}`
14123
14330
  );
14124
14331
  if (this.activeBinding && bindingsMatch(this.activeBinding, binding) && this.activeSocket) {
14125
- return;
14332
+ return "connected";
14126
14333
  }
14127
14334
  if (this.activeBinding && !bindingsMatch(this.activeBinding, binding)) {
14128
14335
  this.log(
@@ -14137,11 +14344,71 @@ var init_bridgeProxy = __esm({
14137
14344
  this.activeBinding = binding;
14138
14345
  await this.connectActiveDaemon();
14139
14346
  this.log("bridge connected");
14347
+ return "connected";
14140
14348
  }
14141
14349
  recordClientInitialize(line) {
14142
14350
  this.lastInitializeLine = line;
14143
14351
  this.clientInitializeSeen = true;
14144
14352
  }
14353
+ /**
14354
+ * Finish Setup session: answer initialize on the bridge. Does not connect,
14355
+ * spawn, or forward to a daemon — there is no binding by design.
14356
+ */
14357
+ completeSetupRequiredInitialize(line) {
14358
+ this.recordClientInitialize(line);
14359
+ const initId = extractJsonRpcId(line);
14360
+ if (initId !== void 0) {
14361
+ this.stdout.write(
14362
+ `${JSON.stringify({
14363
+ jsonrpc: "2.0",
14364
+ id: initId,
14365
+ result: {
14366
+ protocolVersion: extractInitializeProtocolVersion(line),
14367
+ capabilities: { tools: { listChanged: true } },
14368
+ serverInfo: { name: "memoraone-mcp", version: "1.0.0" }
14369
+ }
14370
+ })}
14371
+ `
14372
+ );
14373
+ }
14374
+ this.log("setup_required: initialize completed locally");
14375
+ const deferred = this.pendingDeferredClientLines.slice();
14376
+ this.pendingDeferredClientLines = [];
14377
+ this.handshakeDeferredClientLines = deferred.slice();
14378
+ for (const deferredLine of deferred) {
14379
+ this.handleSetupRequiredClientLine(deferredLine);
14380
+ }
14381
+ }
14382
+ /** Local MCP replies for a setup_required session (no daemon socket). */
14383
+ handleSetupRequiredClientLine(line) {
14384
+ let message;
14385
+ try {
14386
+ message = JSON.parse(line.trim());
14387
+ } catch {
14388
+ return;
14389
+ }
14390
+ const method = typeof message.method === "string" ? message.method : "";
14391
+ const id = message.id;
14392
+ if (method === "tools/list" && (typeof id === "string" || typeof id === "number")) {
14393
+ this.stdout.write(
14394
+ `${JSON.stringify({
14395
+ jsonrpc: "2.0",
14396
+ id,
14397
+ result: { tools: [SETUP_REQUIRED_FINISH_SETUP_TOOL] }
14398
+ })}
14399
+ `
14400
+ );
14401
+ return;
14402
+ }
14403
+ if (id === void 0 || id === null) {
14404
+ return;
14405
+ }
14406
+ writePreInitializeJsonRpcError(
14407
+ this.stdout,
14408
+ id,
14409
+ method.trim() !== "" ? method : "(unknown)"
14410
+ );
14411
+ }
14145
14412
  /**
14146
14413
  * Forward initialize to the daemon and wait for its JSON-RPC response before
14147
14414
  * returning. Prevents pipelined tools/call (e.g. Claude Code lazy MCP connect)
@@ -14161,7 +14428,7 @@ var init_bridgeProxy = __esm({
14161
14428
  }
14162
14429
  waitForDaemonJsonRpcResponse(id, timeoutMs = 3e4) {
14163
14430
  const key = this.waiterKey(id);
14164
- return new Promise((resolve44, reject) => {
14431
+ return new Promise((resolve45, reject) => {
14165
14432
  const timer = setTimeout(() => {
14166
14433
  this.pendingDaemonResponseWaiters.delete(key);
14167
14434
  reject(
@@ -14170,7 +14437,7 @@ var init_bridgeProxy = __esm({
14170
14437
  )
14171
14438
  );
14172
14439
  }, timeoutMs);
14173
- this.pendingDaemonResponseWaiters.set(key, { resolve: resolve44, reject, timer });
14440
+ this.pendingDaemonResponseWaiters.set(key, { resolve: resolve45, reject, timer });
14174
14441
  });
14175
14442
  }
14176
14443
  notifyDaemonResponseWaiters(line) {
@@ -14241,6 +14508,7 @@ var init_bridgeProxy = __esm({
14241
14508
  this.pendingDeferredClientLines = [];
14242
14509
  this.handshakeDeferredClientLines = [];
14243
14510
  this.clientInitializeSeen = false;
14511
+ this.setupRequiredSession = false;
14244
14512
  this.activeBinding = null;
14245
14513
  this.activeIdeType = void 0;
14246
14514
  }
@@ -14359,6 +14627,8 @@ if (args.includes("--help") || args.includes("-h")) {
14359
14627
  memoraone-mcp connect <code> [--api-url <url>] [--verbose]
14360
14628
  memoraone-mcp plugin-pair --workspace-root <path> --source <cursor|jetbrains> [--api-url <url>]
14361
14629
  memoraone-mcp runtime-identity --workspace-root <path>
14630
+ memoraone-mcp ensure-gateway
14631
+ memoraone-mcp --gateway
14362
14632
  memoraone-mcp [--daemon --binding-id <mrb_\u2026> [--ide ${IDE_TYPE_CLI_CHOICES}]]
14363
14633
  memoraone-mcp setup-ide-files [--all|--cursor|--vscode|--jetbrains|--claude-code|--windsurf|--opencode|--codex|--kiro|--kiro-cli|--cline|--cline-cli|--claude-desktop|--zed|--visual-studio|--copilot-cli|--auggie|--antigravity|--goose|--junie|--xcode|--copilot-jetbrains|--copilot-visual-studio] [--force] [--dry-run] [--no-gitignore] [--cleanup] [--dev] [--repair] [--workspace-root <path>] [--api-url <url>] [--verbose]
14364
14634
  Cursor API environment (with --cursor or --all): --local (node + built cli.cjs + local API) | --staging (npx + staging API)
@@ -14409,6 +14679,18 @@ if (args[0] === "cleanup") {
14409
14679
  `);
14410
14680
  process.exit(1);
14411
14681
  });
14682
+ } else if (args[0] === "ensure-gateway") {
14683
+ Promise.resolve().then(() => (init_gatewayEnsure(), gatewayEnsure_exports)).then(({ cliEnsureGateway: cliEnsureGateway2 }) => cliEnsureGateway2(args.slice(1))).then((code) => process.exit(code)).catch((err) => {
14684
+ process.stderr.write(`[memoraone-mcp] ensure-gateway fatal: ${String(err)}
14685
+ `);
14686
+ process.exit(1);
14687
+ });
14688
+ } else if (args.includes("--gateway")) {
14689
+ import("./gateway.cjs").then(({ runGateway }) => runGateway()).catch((err) => {
14690
+ process.stderr.write(`[memoraone-mcp] gateway fatal: ${String(err)}
14691
+ `);
14692
+ process.exit(1);
14693
+ });
14412
14694
  } else if (args.includes("--daemon")) {
14413
14695
  import("./daemon.cjs").then(({ runDaemon }) => runDaemon()).catch((err) => {
14414
14696
  process.stderr.write(`[memoraone-mcp] daemon fatal: ${String(err)}