@memoraone/mcp 0.1.29 → 0.1.31
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 +2060 -515
- package/dist/daemon.cjs +426 -146
- package/dist/index.cjs +371 -120
- package/package.json +1 -1
package/dist/daemon.cjs
CHANGED
|
@@ -32,15 +32,61 @@ __export(daemon_exports, {
|
|
|
32
32
|
runDaemon: () => runDaemon
|
|
33
33
|
});
|
|
34
34
|
module.exports = __toCommonJS(daemon_exports);
|
|
35
|
-
var
|
|
35
|
+
var fs7 = __toESM(require("fs"), 1);
|
|
36
36
|
var net = __toESM(require("net"), 1);
|
|
37
37
|
var import_stdio2 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
38
38
|
|
|
39
39
|
// src/socketPaths.ts
|
|
40
40
|
var os = __toESM(require("os"), 1);
|
|
41
|
-
var
|
|
41
|
+
var path2 = __toESM(require("path"), 1);
|
|
42
42
|
var fs = __toESM(require("fs"), 1);
|
|
43
|
-
|
|
43
|
+
|
|
44
|
+
// src/bindingIdentity.ts
|
|
45
|
+
var crypto = __toESM(require("crypto"), 1);
|
|
46
|
+
var path = __toESM(require("path"), 1);
|
|
47
|
+
var BINDING_SOCKET_HASH_LENGTH = 16;
|
|
48
|
+
function hashBindingIdentity(projectId, workspaceRoot, ideType) {
|
|
49
|
+
const input = [
|
|
50
|
+
projectId.trim().toLowerCase(),
|
|
51
|
+
path.resolve(workspaceRoot),
|
|
52
|
+
ideType
|
|
53
|
+
].join("|");
|
|
54
|
+
return crypto.createHash("sha256").update(input).digest("hex").slice(0, BINDING_SOCKET_HASH_LENGTH);
|
|
55
|
+
}
|
|
56
|
+
function bindingsMatch(a, b) {
|
|
57
|
+
return a.projectId.trim().toLowerCase() === b.projectId.trim().toLowerCase() && path.resolve(a.workspaceRoot) === path.resolve(b.workspaceRoot) && path.resolve(a.m1Path) === path.resolve(b.m1Path);
|
|
58
|
+
}
|
|
59
|
+
function formatMissingInitializeWorkspaceError(options) {
|
|
60
|
+
const lines = [
|
|
61
|
+
"[memoraone-mcp] Could not resolve workspace from MCP initialize params."
|
|
62
|
+
];
|
|
63
|
+
if (options?.rootsListAttempted) {
|
|
64
|
+
lines.push(
|
|
65
|
+
"Cursor initialize lacked workspaceFolders/rootUri; roots/list was attempted but returned no usable repo root."
|
|
66
|
+
);
|
|
67
|
+
if (options.rootsListUris && options.rootsListUris.length > 0) {
|
|
68
|
+
lines.push(`roots/list URIs: ${options.rootsListUris.join(", ")}`);
|
|
69
|
+
}
|
|
70
|
+
lines.push(
|
|
71
|
+
"Global Cursor MCP cannot safely bind per-window repos without a workspace signal from Cursor (initialize roots or roots/list)."
|
|
72
|
+
);
|
|
73
|
+
lines.push(
|
|
74
|
+
"Reload MCP in this Cursor window, or ensure this repo has a managed .cursor/mcp.json from setup-ide-files --cursor."
|
|
75
|
+
);
|
|
76
|
+
return lines.join("\n");
|
|
77
|
+
}
|
|
78
|
+
lines.push(
|
|
79
|
+
"Reload MCP in this Cursor window so initialize includes workspaceFolders, rootUri, or a usable roots/list response for this repo."
|
|
80
|
+
);
|
|
81
|
+
return lines.join("\n");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/socketPaths.ts
|
|
85
|
+
var BASE_DIR = process.env.MEMORAONE_MCP_LOCK_DIR || path2.join(os.homedir(), ".memoraone-mcp");
|
|
86
|
+
var HASH_SOCKET_FILENAME_RE = new RegExp(
|
|
87
|
+
`^mcp-[0-9a-f]{${BINDING_SOCKET_HASH_LENGTH}}\\.sock$`,
|
|
88
|
+
"i"
|
|
89
|
+
);
|
|
44
90
|
var IDE_TYPES = ["cursor", "copilot-vscode", "jetbrains"];
|
|
45
91
|
var IDE_TYPE_SET = new Set(IDE_TYPES);
|
|
46
92
|
function parseIdeType(value) {
|
|
@@ -59,11 +105,16 @@ function parseIdeTypeFromArgv(args) {
|
|
|
59
105
|
}
|
|
60
106
|
return parseIdeType(args[idx + 1]);
|
|
61
107
|
}
|
|
62
|
-
function
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
108
|
+
function resolveBindingIdeType(env2 = process.env) {
|
|
109
|
+
return resolveIdeTypeFromEnv(env2) ?? "";
|
|
110
|
+
}
|
|
111
|
+
function getBindingSocketFilename(binding, env2 = process.env) {
|
|
112
|
+
const ideType = resolveBindingIdeType(env2);
|
|
113
|
+
const hash = hashBindingIdentity(binding.projectId, binding.workspaceRoot, ideType);
|
|
114
|
+
return `mcp-${hash}.sock`;
|
|
115
|
+
}
|
|
116
|
+
function getBindingSocketPath(binding, env2 = process.env) {
|
|
117
|
+
return path2.join(BASE_DIR, getBindingSocketFilename(binding, env2));
|
|
67
118
|
}
|
|
68
119
|
function ensureBaseDir() {
|
|
69
120
|
fs.mkdirSync(BASE_DIR, { recursive: true });
|
|
@@ -72,7 +123,7 @@ function ensureBaseDir() {
|
|
|
72
123
|
|
|
73
124
|
// src/projectBinding.ts
|
|
74
125
|
var fs2 = __toESM(require("fs/promises"), 1);
|
|
75
|
-
var
|
|
126
|
+
var path3 = __toESM(require("path"), 1);
|
|
76
127
|
var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
77
128
|
function normalizeEnvironment(raw) {
|
|
78
129
|
if (raw === void 0 || raw === null || typeof raw !== "string") {
|
|
@@ -105,7 +156,7 @@ async function resolveProjectIdFromExplicitM1Path() {
|
|
|
105
156
|
if (raw === void 0 || raw.trim() === "") {
|
|
106
157
|
return null;
|
|
107
158
|
}
|
|
108
|
-
const markerPath =
|
|
159
|
+
const markerPath = path3.resolve(raw);
|
|
109
160
|
try {
|
|
110
161
|
const content = await fs2.readFile(markerPath, "utf8");
|
|
111
162
|
const { projectId, apiKey, environment } = parseAndValidateM1(content, markerPath);
|
|
@@ -118,20 +169,20 @@ async function resolveProjectIdFromExplicitM1Path() {
|
|
|
118
169
|
}
|
|
119
170
|
}
|
|
120
171
|
async function findM1WalkingUp(workspaceRoot) {
|
|
121
|
-
let current =
|
|
172
|
+
let current = path3.resolve(workspaceRoot);
|
|
122
173
|
while (true) {
|
|
123
|
-
const markerPath =
|
|
174
|
+
const markerPath = path3.join(current, "memoraone.m1");
|
|
124
175
|
try {
|
|
125
176
|
const content = await fs2.readFile(markerPath, "utf8");
|
|
126
177
|
const { projectId, apiKey, environment } = parseAndValidateM1(content, markerPath);
|
|
127
|
-
const repoRoot =
|
|
178
|
+
const repoRoot = path3.dirname(markerPath);
|
|
128
179
|
return environment === void 0 ? { projectId, apiKey, repoRoot, markerPath } : { projectId, apiKey, environment, repoRoot, markerPath };
|
|
129
180
|
} catch (err) {
|
|
130
181
|
if (err?.code !== "ENOENT") {
|
|
131
182
|
throw err;
|
|
132
183
|
}
|
|
133
184
|
}
|
|
134
|
-
const parent =
|
|
185
|
+
const parent = path3.dirname(current);
|
|
135
186
|
if (parent === current) {
|
|
136
187
|
break;
|
|
137
188
|
}
|
|
@@ -154,7 +205,7 @@ function normalizeWorkspaceSearchRoots(workspaceRoot) {
|
|
|
154
205
|
if (trimmed === "") {
|
|
155
206
|
continue;
|
|
156
207
|
}
|
|
157
|
-
const resolved =
|
|
208
|
+
const resolved = path3.resolve(trimmed);
|
|
158
209
|
if (!seen.has(resolved)) {
|
|
159
210
|
seen.add(resolved);
|
|
160
211
|
out.push(resolved);
|
|
@@ -176,29 +227,33 @@ function resolveApiKeyWithSource(fileApiKey) {
|
|
|
176
227
|
}
|
|
177
228
|
return { apiKey: null, apiKeySource: "none" };
|
|
178
229
|
}
|
|
179
|
-
async function resolveAuthoritativeBinding(workspaceRoot) {
|
|
180
|
-
const
|
|
181
|
-
if (
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
230
|
+
async function resolveAuthoritativeBinding(workspaceRoot, options = {}) {
|
|
231
|
+
const respectExplicitM1Path = options.respectExplicitM1Path !== false;
|
|
232
|
+
if (respectExplicitM1Path) {
|
|
233
|
+
const explicitBinding = await resolveProjectIdFromExplicitM1Path();
|
|
234
|
+
if (explicitBinding) {
|
|
235
|
+
const resolved = resolveApiKeyWithSource(explicitBinding.apiKey);
|
|
236
|
+
return {
|
|
237
|
+
projectId: explicitBinding.projectId,
|
|
238
|
+
workspaceRoot: path3.dirname(explicitBinding.foundAt),
|
|
239
|
+
m1Path: explicitBinding.foundAt,
|
|
240
|
+
apiKey: resolved.apiKey,
|
|
241
|
+
...explicitBinding.environment !== void 0 ? { environment: explicitBinding.environment } : {},
|
|
242
|
+
bindingSource: "explicit-m1-path",
|
|
243
|
+
apiKeySource: resolved.apiKeySource
|
|
244
|
+
};
|
|
245
|
+
}
|
|
192
246
|
}
|
|
193
247
|
const candidates = normalizeWorkspaceSearchRoots(workspaceRoot);
|
|
194
248
|
if (candidates.length === 0) {
|
|
195
249
|
throw new Error("Could not find memoraone.m1 in workspace.\nOpen a folder containing memoraone.m1.");
|
|
196
250
|
}
|
|
251
|
+
const bindings = [];
|
|
197
252
|
for (const root of candidates) {
|
|
198
253
|
const binding = await findM1WalkingUp(root);
|
|
199
254
|
if (binding) {
|
|
200
255
|
const resolved = resolveApiKeyWithSource(binding.apiKey);
|
|
201
|
-
|
|
256
|
+
bindings.push({
|
|
202
257
|
projectId: binding.projectId,
|
|
203
258
|
workspaceRoot: binding.repoRoot,
|
|
204
259
|
m1Path: binding.markerPath,
|
|
@@ -206,10 +261,25 @@ async function resolveAuthoritativeBinding(workspaceRoot) {
|
|
|
206
261
|
...binding.environment !== void 0 ? { environment: binding.environment } : {},
|
|
207
262
|
bindingSource: "workspace-search",
|
|
208
263
|
apiKeySource: resolved.apiKeySource
|
|
209
|
-
};
|
|
264
|
+
});
|
|
210
265
|
}
|
|
211
266
|
}
|
|
212
|
-
|
|
267
|
+
if (bindings.length === 0) {
|
|
268
|
+
throw new Error("Could not find memoraone.m1 in workspace.\nOpen a folder containing memoraone.m1.");
|
|
269
|
+
}
|
|
270
|
+
const distinctProjectIds = new Set(bindings.map((b) => b.projectId));
|
|
271
|
+
if (distinctProjectIds.size > 1) {
|
|
272
|
+
const lines = bindings.map(
|
|
273
|
+
(b) => ` - workspace=${b.workspaceRoot} project=${b.projectId} m1=${b.m1Path}`
|
|
274
|
+
);
|
|
275
|
+
throw new Error(
|
|
276
|
+
"[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."
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
return bindings[0];
|
|
280
|
+
}
|
|
281
|
+
function encodeResolvedBinding(binding) {
|
|
282
|
+
return Buffer.from(JSON.stringify(binding), "utf8").toString("base64");
|
|
213
283
|
}
|
|
214
284
|
function decodeResolvedBinding(value) {
|
|
215
285
|
if (!value) {
|
|
@@ -257,17 +327,43 @@ function decodeResolvedBinding(value) {
|
|
|
257
327
|
};
|
|
258
328
|
}
|
|
259
329
|
|
|
330
|
+
// src/bindingSidecar.ts
|
|
331
|
+
var fs3 = __toESM(require("fs"), 1);
|
|
332
|
+
var path4 = __toESM(require("path"), 1);
|
|
333
|
+
function bindingSidecarPath(socketPath) {
|
|
334
|
+
if (socketPath.endsWith(".sock")) {
|
|
335
|
+
return `${socketPath.slice(0, -".sock".length)}.binding.json`;
|
|
336
|
+
}
|
|
337
|
+
return `${socketPath}.binding.json`;
|
|
338
|
+
}
|
|
339
|
+
function writeBindingSidecar(socketPath, binding, ideType = resolveBindingIdeType()) {
|
|
340
|
+
const payload = encodeResolvedBinding(binding);
|
|
341
|
+
const record = {
|
|
342
|
+
v: 2,
|
|
343
|
+
...ideType ? { ideType } : {},
|
|
344
|
+
projectId: binding.projectId,
|
|
345
|
+
workspaceRoot: binding.workspaceRoot,
|
|
346
|
+
m1Path: binding.m1Path,
|
|
347
|
+
binding: payload
|
|
348
|
+
};
|
|
349
|
+
fs3.writeFileSync(bindingSidecarPath(socketPath), JSON.stringify(record), "utf8");
|
|
350
|
+
}
|
|
351
|
+
function removeBindingSidecar(socketPath) {
|
|
352
|
+
try {
|
|
353
|
+
fs3.unlinkSync(bindingSidecarPath(socketPath));
|
|
354
|
+
} catch {
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
260
358
|
// src/index.ts
|
|
261
|
-
var path7 = __toESM(require("path"), 1);
|
|
262
|
-
var import_node_url2 = require("url");
|
|
263
359
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
264
360
|
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
265
361
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
266
362
|
|
|
267
363
|
// src/config.ts
|
|
268
364
|
var process2 = __toESM(require("process"), 1);
|
|
269
|
-
var
|
|
270
|
-
var
|
|
365
|
+
var fs4 = __toESM(require("fs"), 1);
|
|
366
|
+
var path5 = __toESM(require("path"), 1);
|
|
271
367
|
var dotenv = __toESM(require("dotenv"), 1);
|
|
272
368
|
var import_v4 = require("zod/v4");
|
|
273
369
|
|
|
@@ -290,8 +386,8 @@ function resolveApiUrl(env2) {
|
|
|
290
386
|
}
|
|
291
387
|
|
|
292
388
|
// src/config.ts
|
|
293
|
-
var dotenvPath =
|
|
294
|
-
if (
|
|
389
|
+
var dotenvPath = path5.resolve(process2.cwd(), ".env");
|
|
390
|
+
if (fs4.existsSync(dotenvPath)) {
|
|
295
391
|
try {
|
|
296
392
|
dotenv.config({ path: dotenvPath });
|
|
297
393
|
} catch (err) {
|
|
@@ -358,7 +454,7 @@ var config2 = {
|
|
|
358
454
|
};
|
|
359
455
|
|
|
360
456
|
// src/client/memoraClient.ts
|
|
361
|
-
var
|
|
457
|
+
var crypto2 = __toESM(require("crypto"), 1);
|
|
362
458
|
var PROJECT_ID_HEADER = "x-project-id";
|
|
363
459
|
var uuidRegex2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
364
460
|
var parseBooleanFlag2 = (value) => {
|
|
@@ -427,12 +523,12 @@ var MemoraClient = class {
|
|
|
427
523
|
...options?.headers ?? {}
|
|
428
524
|
};
|
|
429
525
|
}
|
|
430
|
-
async post(
|
|
526
|
+
async post(path10, body, options) {
|
|
431
527
|
console.error(
|
|
432
|
-
`[memoraone-mcp][info] MemoraClient.post ENTER path=${
|
|
528
|
+
`[memoraone-mcp][info] MemoraClient.post ENTER path=${path10}`
|
|
433
529
|
);
|
|
434
|
-
const nonce =
|
|
435
|
-
const url = `${this.baseUrl}${
|
|
530
|
+
const nonce = crypto2.randomBytes(8).toString("hex");
|
|
531
|
+
const url = `${this.baseUrl}${path10.startsWith("/") ? path10 : `/${path10}`}`;
|
|
436
532
|
this.resolveProjectId();
|
|
437
533
|
console.error(
|
|
438
534
|
`[memoraone-mcp][info] requestJson nonce=${nonce} stage=before_fetch method=POST url=${url}`
|
|
@@ -465,13 +561,13 @@ var MemoraClient = class {
|
|
|
465
561
|
throw new MemoraOneHttpError(res.status, res.statusText, res.text);
|
|
466
562
|
}
|
|
467
563
|
console.error(
|
|
468
|
-
`[memoraone-mcp][info] MemoraClient.post EXIT path=${
|
|
564
|
+
`[memoraone-mcp][info] MemoraClient.post EXIT path=${path10}`
|
|
469
565
|
);
|
|
470
566
|
return res.text ? JSON.parse(res.text) : null;
|
|
471
567
|
}
|
|
472
|
-
async get(
|
|
473
|
-
const nonce =
|
|
474
|
-
const url = `${this.baseUrl}${
|
|
568
|
+
async get(path10, options) {
|
|
569
|
+
const nonce = crypto2.randomBytes(8).toString("hex");
|
|
570
|
+
const url = `${this.baseUrl}${path10.startsWith("/") ? path10 : `/${path10}`}`;
|
|
475
571
|
this.resolveProjectId();
|
|
476
572
|
console.error(
|
|
477
573
|
`[memoraone-mcp][info] requestJson nonce=${nonce} stage=before_fetch method=GET url=${url}`
|
|
@@ -501,15 +597,195 @@ var MemoraClient = class {
|
|
|
501
597
|
};
|
|
502
598
|
var memoraClient_default = MemoraClient;
|
|
503
599
|
|
|
504
|
-
// src/
|
|
505
|
-
var
|
|
600
|
+
// src/initializeBinding.ts
|
|
601
|
+
var path6 = __toESM(require("path"), 1);
|
|
506
602
|
var import_node_url = require("url");
|
|
603
|
+
var MEMORAONE_WORKSPACE_ROOT_ENV = "MEMORAONE_WORKSPACE_ROOT";
|
|
604
|
+
function getBridgeBindingResolveOptions(env2 = process.env) {
|
|
605
|
+
const ideType = resolveIdeTypeFromEnv(env2);
|
|
606
|
+
if (ideType === "cursor") {
|
|
607
|
+
return { respectExplicitM1Path: false, allowEnvWorkspaceFallback: false };
|
|
608
|
+
}
|
|
609
|
+
return { respectExplicitM1Path: true, allowEnvWorkspaceFallback: true };
|
|
610
|
+
}
|
|
611
|
+
function uriToPath(uri) {
|
|
612
|
+
if (uri.startsWith("file://")) {
|
|
613
|
+
return (0, import_node_url.fileURLToPath)(uri);
|
|
614
|
+
}
|
|
615
|
+
return uri;
|
|
616
|
+
}
|
|
617
|
+
function getEnvWorkspaceRootCandidates() {
|
|
618
|
+
const raw = process.env.WORKSPACE_FOLDER_PATHS;
|
|
619
|
+
const parts = [];
|
|
620
|
+
if (raw !== void 0 && raw.trim() !== "") {
|
|
621
|
+
for (const p of raw.split(path6.delimiter).map((s) => s.trim()).filter(Boolean)) {
|
|
622
|
+
parts.push(path6.resolve(p));
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
parts.push(process.cwd());
|
|
626
|
+
const seen = /* @__PURE__ */ new Set();
|
|
627
|
+
const deduped = [];
|
|
628
|
+
for (const p of parts) {
|
|
629
|
+
if (!seen.has(p)) {
|
|
630
|
+
seen.add(p);
|
|
631
|
+
deduped.push(p);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return deduped;
|
|
635
|
+
}
|
|
636
|
+
function extractWorkspaceRootsFromInitialize(params) {
|
|
637
|
+
if (!params) {
|
|
638
|
+
return [];
|
|
639
|
+
}
|
|
640
|
+
const seen = /* @__PURE__ */ new Set();
|
|
641
|
+
const roots = [];
|
|
642
|
+
const addRoot = (uri) => {
|
|
643
|
+
if (uri === void 0 || uri.trim() === "") {
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
const resolved = path6.resolve(uriToPath(uri));
|
|
647
|
+
if (!seen.has(resolved)) {
|
|
648
|
+
seen.add(resolved);
|
|
649
|
+
roots.push(resolved);
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
if (Array.isArray(params.workspaceFolders) && params.workspaceFolders.length > 0) {
|
|
653
|
+
for (const folder of params.workspaceFolders) {
|
|
654
|
+
addRoot(folder?.uri);
|
|
655
|
+
}
|
|
656
|
+
return roots;
|
|
657
|
+
}
|
|
658
|
+
if (params.rootUri) {
|
|
659
|
+
addRoot(params.rootUri);
|
|
660
|
+
}
|
|
661
|
+
return roots;
|
|
662
|
+
}
|
|
663
|
+
function getRepoScopedWorkspaceHint(env2 = process.env) {
|
|
664
|
+
const raw = env2[MEMORAONE_WORKSPACE_ROOT_ENV];
|
|
665
|
+
if (raw === void 0 || raw.trim() === "") {
|
|
666
|
+
return null;
|
|
667
|
+
}
|
|
668
|
+
return path6.resolve(raw.trim());
|
|
669
|
+
}
|
|
670
|
+
function formatWorkspaceAmbiguityError(bindings) {
|
|
671
|
+
const lines = bindings.map(
|
|
672
|
+
(b) => ` - workspace=${b.workspaceRoot} project=${b.projectId} m1=${b.m1Path}`
|
|
673
|
+
);
|
|
674
|
+
return "[memoraone-mcp] Ambiguous workspace binding: multiple open roots map to different MemoraOne projects.\n" + lines.join("\n") + `
|
|
675
|
+
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.`;
|
|
676
|
+
}
|
|
677
|
+
function formatRepoHintInitializeMismatchError(repoHintRoot, initializeBinding) {
|
|
678
|
+
return `[memoraone-mcp] Repo-scoped workspace hint conflicts with MCP initialize workspace.
|
|
679
|
+
${MEMORAONE_WORKSPACE_ROOT_ENV}: ${repoHintRoot}
|
|
680
|
+
initialize: project=${initializeBinding.projectId} workspace=${initializeBinding.workspaceRoot} m1=${initializeBinding.m1Path}
|
|
681
|
+
Reload MCP in the Cursor window for this repo, or re-run setup-ide-files --cursor here if the hint is stale.`;
|
|
682
|
+
}
|
|
683
|
+
function formatRepoHintNotInRootsListError(repoHintRoot, rootsListPaths) {
|
|
684
|
+
return `[memoraone-mcp] Repo-scoped workspace hint does not match any Cursor roots/list entry.
|
|
685
|
+
${MEMORAONE_WORKSPACE_ROOT_ENV}: ${repoHintRoot}
|
|
686
|
+
roots/list paths: ${rootsListPaths.join(", ")}
|
|
687
|
+
Reload MCP in the Cursor window for this repo, or re-run setup-ide-files --cursor if the hint is stale.`;
|
|
688
|
+
}
|
|
689
|
+
function formatBindingMismatchError(daemonHint, sessionBinding) {
|
|
690
|
+
return `[memoraone-mcp] Project binding mismatch between daemon and MCP initialize workspace.
|
|
691
|
+
daemon: project=${daemonHint.projectId} workspace=${daemonHint.workspaceRoot} m1=${daemonHint.m1Path}
|
|
692
|
+
initialize: project=${sessionBinding.projectId} workspace=${sessionBinding.workspaceRoot} m1=${sessionBinding.m1Path}
|
|
693
|
+
Reconnect or reload the MCP server in this IDE window so the bridge can bind to the correct project.`;
|
|
694
|
+
}
|
|
695
|
+
async function resolveBindingFromWorkspaceRoots(workspaceRoots, options = {}) {
|
|
696
|
+
if (workspaceRoots.length === 0) {
|
|
697
|
+
throw new Error("Could not find memoraone.m1 in workspace.\nOpen a folder containing memoraone.m1.");
|
|
698
|
+
}
|
|
699
|
+
const bindings = [];
|
|
700
|
+
for (const root of workspaceRoots) {
|
|
701
|
+
try {
|
|
702
|
+
bindings.push(
|
|
703
|
+
await resolveAuthoritativeBinding(root, {
|
|
704
|
+
respectExplicitM1Path: options.respectExplicitM1Path
|
|
705
|
+
})
|
|
706
|
+
);
|
|
707
|
+
} catch (err) {
|
|
708
|
+
if (err instanceof Error && err.message.includes("Could not find memoraone.m1")) {
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
throw err;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
if (bindings.length === 0) {
|
|
715
|
+
throw new Error("Could not find memoraone.m1 in workspace.\nOpen a folder containing memoraone.m1.");
|
|
716
|
+
}
|
|
717
|
+
const distinctProjectIds = new Set(bindings.map((b) => b.projectId));
|
|
718
|
+
if (distinctProjectIds.size > 1) {
|
|
719
|
+
throw new Error(formatWorkspaceAmbiguityError(bindings));
|
|
720
|
+
}
|
|
721
|
+
return bindings[0];
|
|
722
|
+
}
|
|
723
|
+
async function resolveBindingFromInitializeParams(params, options = {}) {
|
|
724
|
+
const env2 = options.env ?? process.env;
|
|
725
|
+
const resolveOpts = {
|
|
726
|
+
respectExplicitM1Path: options.respectExplicitM1Path,
|
|
727
|
+
allowEnvWorkspaceFallback: options.allowEnvWorkspaceFallback
|
|
728
|
+
};
|
|
729
|
+
const repoHint = getRepoScopedWorkspaceHint(env2);
|
|
730
|
+
const initializeRoots = extractWorkspaceRootsFromInitialize(params);
|
|
731
|
+
if (initializeRoots.length > 0) {
|
|
732
|
+
const binding = await resolveBindingFromWorkspaceRoots(initializeRoots, resolveOpts);
|
|
733
|
+
if (repoHint !== null) {
|
|
734
|
+
const hintBinding = await resolveAuthoritativeBinding(repoHint, {
|
|
735
|
+
respectExplicitM1Path: false
|
|
736
|
+
});
|
|
737
|
+
if (!bindingsMatch(binding, hintBinding)) {
|
|
738
|
+
throw new Error(formatRepoHintInitializeMismatchError(repoHint, binding));
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return binding;
|
|
742
|
+
}
|
|
743
|
+
const rootsListUris = options.rootsListUris ?? [];
|
|
744
|
+
const rootsListPaths = rootsListUris.map((uri) => uriToPath(uri)).filter(Boolean);
|
|
745
|
+
if (rootsListPaths.length > 1 && repoHint !== null) {
|
|
746
|
+
const hintResolved = path6.resolve(repoHint);
|
|
747
|
+
const matchingRoot = rootsListPaths.find((root) => path6.resolve(root) === hintResolved);
|
|
748
|
+
if (!matchingRoot) {
|
|
749
|
+
throw new Error(formatRepoHintNotInRootsListError(repoHint, rootsListPaths));
|
|
750
|
+
}
|
|
751
|
+
return resolveAuthoritativeBinding(repoHint, { respectExplicitM1Path: false });
|
|
752
|
+
}
|
|
753
|
+
if (rootsListPaths.length > 0) {
|
|
754
|
+
return resolveBindingFromWorkspaceRoots(rootsListPaths, resolveOpts);
|
|
755
|
+
}
|
|
756
|
+
if (repoHint !== null) {
|
|
757
|
+
return resolveAuthoritativeBinding(repoHint, { respectExplicitM1Path: false });
|
|
758
|
+
}
|
|
759
|
+
if (options.allowEnvWorkspaceFallback === false) {
|
|
760
|
+
throw new Error(
|
|
761
|
+
formatMissingInitializeWorkspaceError({
|
|
762
|
+
rootsListAttempted: options.rootsListAttempted === true,
|
|
763
|
+
rootsListUris
|
|
764
|
+
})
|
|
765
|
+
);
|
|
766
|
+
}
|
|
767
|
+
const fallbackRoots = options.fallbackWorkspaceRoots ?? getEnvWorkspaceRootCandidates();
|
|
768
|
+
return resolveAuthoritativeBinding(fallbackRoots, {
|
|
769
|
+
respectExplicitM1Path: options.respectExplicitM1Path
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// src/bridgeClientRoots.ts
|
|
774
|
+
var readline = __toESM(require("readline"), 1);
|
|
775
|
+
var TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
776
|
+
function isInitializeDebugEnabled(env2 = process.env) {
|
|
777
|
+
return TRUTHY.has(String(env2.MEMORAONE_DEBUG_INIT ?? "").trim().toLowerCase());
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// src/sourceRegistration.ts
|
|
781
|
+
var path7 = __toESM(require("path"), 1);
|
|
782
|
+
var import_node_url2 = require("url");
|
|
507
783
|
var LOG_PREFIX = "[memoraone-mcp][source-registration]";
|
|
508
784
|
function buildRepoSourcePayload(normalizedRepoPath, ideType) {
|
|
509
785
|
const body = {
|
|
510
786
|
kind: "repo",
|
|
511
|
-
label:
|
|
512
|
-
uri: (0,
|
|
787
|
+
label: path7.basename(normalizedRepoPath),
|
|
788
|
+
uri: (0, import_node_url2.pathToFileURL)(normalizedRepoPath).href
|
|
513
789
|
};
|
|
514
790
|
if (ideType) {
|
|
515
791
|
body.metadata = {
|
|
@@ -527,7 +803,7 @@ async function registerRepoSource(client, projectId, repoPath, ideType) {
|
|
|
527
803
|
);
|
|
528
804
|
return;
|
|
529
805
|
}
|
|
530
|
-
const normalizedRepoPath =
|
|
806
|
+
const normalizedRepoPath = path7.resolve(String(repoPath));
|
|
531
807
|
const body = buildRepoSourcePayload(normalizedRepoPath, ideType);
|
|
532
808
|
const primaryPath = `/v1/projects/${projectId}/sources`;
|
|
533
809
|
const alternatePath = `/v1/projects/${projectId}/sources/register`;
|
|
@@ -687,11 +963,11 @@ var bindingStatusShape = {};
|
|
|
687
963
|
|
|
688
964
|
// src/tools/handlers/postEvent.ts
|
|
689
965
|
var import_v412 = require("zod/v4");
|
|
690
|
-
var
|
|
966
|
+
var crypto4 = __toESM(require("crypto"), 1);
|
|
691
967
|
|
|
692
968
|
// src/runContext.ts
|
|
693
969
|
var import_node_async_hooks = require("async_hooks");
|
|
694
|
-
var
|
|
970
|
+
var crypto3 = __toESM(require("crypto"), 1);
|
|
695
971
|
var sessionContextStorage = new import_node_async_hooks.AsyncLocalStorage();
|
|
696
972
|
function createSessionRunContext(initial = {}) {
|
|
697
973
|
return {
|
|
@@ -744,7 +1020,7 @@ function resolveRunId(passed) {
|
|
|
744
1020
|
return getCurrentRunId();
|
|
745
1021
|
}
|
|
746
1022
|
function generateRunId() {
|
|
747
|
-
return
|
|
1023
|
+
return crypto3.randomBytes(16).toString("hex");
|
|
748
1024
|
}
|
|
749
1025
|
|
|
750
1026
|
// src/tools/handlers/postEvent.ts
|
|
@@ -784,7 +1060,7 @@ function buildPostEventContentFields(content) {
|
|
|
784
1060
|
return { message: JSON.stringify(content) };
|
|
785
1061
|
}
|
|
786
1062
|
async function handlePostEvent(client, args) {
|
|
787
|
-
const nonce =
|
|
1063
|
+
const nonce = crypto4.randomBytes(8).toString("hex");
|
|
788
1064
|
console.error(
|
|
789
1065
|
`[memoraone-mcp][debug] tool=memora_post_event toolCallId=unknown nonce=${nonce} stage=before_post`
|
|
790
1066
|
);
|
|
@@ -939,9 +1215,9 @@ function buildPersonalContextPath(parsed2) {
|
|
|
939
1215
|
}
|
|
940
1216
|
async function handleGetPersonalContext(client, args) {
|
|
941
1217
|
const parsed2 = getPersonalContextInputSchema.parse(args ?? {});
|
|
942
|
-
const
|
|
1218
|
+
const path10 = buildPersonalContextPath(parsed2);
|
|
943
1219
|
try {
|
|
944
|
-
const result = await client.get(
|
|
1220
|
+
const result = await client.get(path10);
|
|
945
1221
|
return { ok: true, result };
|
|
946
1222
|
} catch (err) {
|
|
947
1223
|
if (err instanceof MemoraOneHttpError) {
|
|
@@ -1182,9 +1458,9 @@ async function handleListProjects(client) {
|
|
|
1182
1458
|
var import_v421 = require("zod/v4");
|
|
1183
1459
|
|
|
1184
1460
|
// src/repoFingerprint.ts
|
|
1185
|
-
var
|
|
1186
|
-
var
|
|
1187
|
-
var
|
|
1461
|
+
var fs5 = __toESM(require("fs"), 1);
|
|
1462
|
+
var path8 = __toESM(require("path"), 1);
|
|
1463
|
+
var crypto5 = __toESM(require("crypto"), 1);
|
|
1188
1464
|
var parseBooleanFlag3 = (value) => {
|
|
1189
1465
|
if (!value) {
|
|
1190
1466
|
return false;
|
|
@@ -1209,20 +1485,20 @@ var normalizeRemoteUrl = (remoteUrl) => {
|
|
|
1209
1485
|
return normalized.toLowerCase();
|
|
1210
1486
|
};
|
|
1211
1487
|
var sha256 = (value) => {
|
|
1212
|
-
return
|
|
1488
|
+
return crypto5.createHash("sha256").update(value).digest("hex");
|
|
1213
1489
|
};
|
|
1214
1490
|
var resolveGitDir = (gitPath) => {
|
|
1215
1491
|
try {
|
|
1216
|
-
const stat2 =
|
|
1492
|
+
const stat2 = fs5.statSync(gitPath);
|
|
1217
1493
|
if (stat2.isDirectory()) {
|
|
1218
1494
|
return gitPath;
|
|
1219
1495
|
}
|
|
1220
1496
|
if (stat2.isFile()) {
|
|
1221
|
-
const content =
|
|
1497
|
+
const content = fs5.readFileSync(gitPath, "utf8");
|
|
1222
1498
|
const match = content.match(/^gitdir:\s*(.+)$/m);
|
|
1223
1499
|
if (match) {
|
|
1224
1500
|
const gitDir = match[1].trim();
|
|
1225
|
-
return
|
|
1501
|
+
return path8.resolve(path8.dirname(gitPath), gitDir);
|
|
1226
1502
|
}
|
|
1227
1503
|
}
|
|
1228
1504
|
} catch {
|
|
@@ -1231,16 +1507,16 @@ var resolveGitDir = (gitPath) => {
|
|
|
1231
1507
|
return null;
|
|
1232
1508
|
};
|
|
1233
1509
|
var findGitRoot = (start) => {
|
|
1234
|
-
let current =
|
|
1510
|
+
let current = path8.resolve(start);
|
|
1235
1511
|
while (true) {
|
|
1236
|
-
const gitPath =
|
|
1237
|
-
if (
|
|
1512
|
+
const gitPath = path8.join(current, ".git");
|
|
1513
|
+
if (fs5.existsSync(gitPath)) {
|
|
1238
1514
|
const gitDir = resolveGitDir(gitPath);
|
|
1239
1515
|
if (gitDir) {
|
|
1240
1516
|
return { gitRoot: current, gitDir };
|
|
1241
1517
|
}
|
|
1242
1518
|
}
|
|
1243
|
-
const parent =
|
|
1519
|
+
const parent = path8.dirname(current);
|
|
1244
1520
|
if (parent === current) {
|
|
1245
1521
|
break;
|
|
1246
1522
|
}
|
|
@@ -1249,9 +1525,9 @@ var findGitRoot = (start) => {
|
|
|
1249
1525
|
return null;
|
|
1250
1526
|
};
|
|
1251
1527
|
var readOriginRemote = (gitDir) => {
|
|
1252
|
-
const configPath =
|
|
1528
|
+
const configPath = path8.join(gitDir, "config");
|
|
1253
1529
|
try {
|
|
1254
|
-
const content =
|
|
1530
|
+
const content = fs5.readFileSync(configPath, "utf8");
|
|
1255
1531
|
const lines = content.split(/\r?\n/);
|
|
1256
1532
|
let inOrigin = false;
|
|
1257
1533
|
for (const line of lines) {
|
|
@@ -1275,7 +1551,7 @@ var readOriginRemote = (gitDir) => {
|
|
|
1275
1551
|
function resolveRepoFingerprint(cwd2) {
|
|
1276
1552
|
const found = findGitRoot(cwd2);
|
|
1277
1553
|
if (!found) {
|
|
1278
|
-
const fallbackPath =
|
|
1554
|
+
const fallbackPath = path8.resolve(cwd2);
|
|
1279
1555
|
const fingerprint2 = sha256(fallbackPath);
|
|
1280
1556
|
debugLog(`repo fingerprint=${fingerprint2} source=path-fallback`);
|
|
1281
1557
|
return {
|
|
@@ -1297,7 +1573,7 @@ function resolveRepoFingerprint(cwd2) {
|
|
|
1297
1573
|
source: "git-remote"
|
|
1298
1574
|
};
|
|
1299
1575
|
}
|
|
1300
|
-
const fingerprint = sha256(
|
|
1576
|
+
const fingerprint = sha256(path8.resolve(gitRoot));
|
|
1301
1577
|
debugLog(`repo fingerprint=${fingerprint} source=path-fallback`);
|
|
1302
1578
|
return {
|
|
1303
1579
|
fingerprint,
|
|
@@ -1307,8 +1583,8 @@ function resolveRepoFingerprint(cwd2) {
|
|
|
1307
1583
|
}
|
|
1308
1584
|
|
|
1309
1585
|
// src/workspaceMap.ts
|
|
1310
|
-
var
|
|
1311
|
-
var
|
|
1586
|
+
var fs6 = __toESM(require("fs/promises"), 1);
|
|
1587
|
+
var path9 = __toESM(require("path"), 1);
|
|
1312
1588
|
var import_node_os = __toESM(require("os"), 1);
|
|
1313
1589
|
var parseBooleanFlag4 = (value) => {
|
|
1314
1590
|
if (!value) {
|
|
@@ -1327,11 +1603,11 @@ var debugLog2 = (message) => {
|
|
|
1327
1603
|
};
|
|
1328
1604
|
var fingerprintRegex = /^[0-9a-f]{64}$/i;
|
|
1329
1605
|
function getWorkspaceMapPath() {
|
|
1330
|
-
return
|
|
1606
|
+
return path9.join(import_node_os.default.homedir(), ".memoraone", "workspaces.json");
|
|
1331
1607
|
}
|
|
1332
1608
|
var ensureWorkspaceDir = async () => {
|
|
1333
|
-
const dir =
|
|
1334
|
-
await
|
|
1609
|
+
const dir = path9.dirname(getWorkspaceMapPath());
|
|
1610
|
+
await fs6.mkdir(dir, { recursive: true });
|
|
1335
1611
|
};
|
|
1336
1612
|
async function acquireWorkspaceMapLock() {
|
|
1337
1613
|
const filePath = getWorkspaceMapPath();
|
|
@@ -1345,10 +1621,10 @@ async function acquireWorkspaceMapLock() {
|
|
|
1345
1621
|
while (!lockAcquired && retries < maxRetries) {
|
|
1346
1622
|
try {
|
|
1347
1623
|
try {
|
|
1348
|
-
const stat2 = await
|
|
1624
|
+
const stat2 = await fs6.stat(lockPath);
|
|
1349
1625
|
const ageMs = Date.now() - stat2.mtimeMs;
|
|
1350
1626
|
if (ageMs > maxLockAgeMs) {
|
|
1351
|
-
await
|
|
1627
|
+
await fs6.unlink(lockPath);
|
|
1352
1628
|
debugLog2(`removed stale workspace map lock (age: ${ageMs}ms)`);
|
|
1353
1629
|
}
|
|
1354
1630
|
} catch (err) {
|
|
@@ -1356,14 +1632,14 @@ async function acquireWorkspaceMapLock() {
|
|
|
1356
1632
|
throw err;
|
|
1357
1633
|
}
|
|
1358
1634
|
}
|
|
1359
|
-
const fd = await
|
|
1635
|
+
const fd = await fs6.open(lockPath, "wx");
|
|
1360
1636
|
await fd.close();
|
|
1361
1637
|
lockAcquired = true;
|
|
1362
1638
|
} catch (err) {
|
|
1363
1639
|
if (err?.code === "EEXIST") {
|
|
1364
1640
|
retries++;
|
|
1365
1641
|
if (retries < maxRetries) {
|
|
1366
|
-
await new Promise((
|
|
1642
|
+
await new Promise((resolve7) => setTimeout(resolve7, retryDelayMs));
|
|
1367
1643
|
continue;
|
|
1368
1644
|
}
|
|
1369
1645
|
throw new Error(
|
|
@@ -1375,7 +1651,7 @@ async function acquireWorkspaceMapLock() {
|
|
|
1375
1651
|
}
|
|
1376
1652
|
return async () => {
|
|
1377
1653
|
try {
|
|
1378
|
-
await
|
|
1654
|
+
await fs6.unlink(lockPath);
|
|
1379
1655
|
} catch (err) {
|
|
1380
1656
|
if (err?.code !== "ENOENT") {
|
|
1381
1657
|
debugLog2(`failed to release workspace map lock: ${String(err)}`);
|
|
@@ -1431,7 +1707,7 @@ var validateWorkspaceMap = (map, filePath) => {
|
|
|
1431
1707
|
async function readWorkspaceMap() {
|
|
1432
1708
|
const filePath = getWorkspaceMapPath();
|
|
1433
1709
|
try {
|
|
1434
|
-
const content = await
|
|
1710
|
+
const content = await fs6.readFile(filePath, "utf8");
|
|
1435
1711
|
const parsed2 = JSON.parse(content);
|
|
1436
1712
|
validateWorkspaceMap(parsed2, filePath);
|
|
1437
1713
|
const typed = parsed2;
|
|
@@ -1476,8 +1752,8 @@ async function writeWorkspaceMap(map) {
|
|
|
1476
1752
|
await ensureWorkspaceDir();
|
|
1477
1753
|
const tempPath = `${filePath}.tmp`;
|
|
1478
1754
|
const content = JSON.stringify(map, null, 2);
|
|
1479
|
-
await
|
|
1480
|
-
await
|
|
1755
|
+
await fs6.writeFile(tempPath, content, "utf8");
|
|
1756
|
+
await fs6.rename(tempPath, filePath);
|
|
1481
1757
|
}
|
|
1482
1758
|
async function setProjectIdForFingerprint(args) {
|
|
1483
1759
|
const { fingerprint, projectKey, source, linked_at } = args;
|
|
@@ -1557,9 +1833,9 @@ function handleBindingStatus(binding) {
|
|
|
1557
1833
|
}
|
|
1558
1834
|
|
|
1559
1835
|
// src/heartbeat.ts
|
|
1560
|
-
var
|
|
1836
|
+
var crypto6 = __toESM(require("crypto"), 1);
|
|
1561
1837
|
function fingerprintApiKey(apiKey) {
|
|
1562
|
-
return
|
|
1838
|
+
return crypto6.createHash("sha256").update(apiKey).digest("hex").slice(0, 12);
|
|
1563
1839
|
}
|
|
1564
1840
|
function isHeartbeatDebugEnabled() {
|
|
1565
1841
|
const value = String(process.env.MEMORAONE_DEBUG_HEARTBEAT ?? "").trim().toLowerCase();
|
|
@@ -1709,21 +1985,6 @@ var notInitializedResult = {
|
|
|
1709
1985
|
]
|
|
1710
1986
|
};
|
|
1711
1987
|
var initializeDiagDumped = false;
|
|
1712
|
-
var uriToPath = (uri) => {
|
|
1713
|
-
if (uri.startsWith("file://")) {
|
|
1714
|
-
return (0, import_node_url2.fileURLToPath)(uri);
|
|
1715
|
-
}
|
|
1716
|
-
return uri;
|
|
1717
|
-
};
|
|
1718
|
-
function getCursorWorkspaceRootFromEnv() {
|
|
1719
|
-
const raw = process.env.WORKSPACE_FOLDER_PATHS;
|
|
1720
|
-
if (raw === void 0 || raw.trim() === "") {
|
|
1721
|
-
return void 0;
|
|
1722
|
-
}
|
|
1723
|
-
const parts = raw.split(path7.delimiter).map((p) => p.trim()).filter(Boolean);
|
|
1724
|
-
const first = parts[0];
|
|
1725
|
-
return first ? path7.resolve(first) : void 0;
|
|
1726
|
-
}
|
|
1727
1988
|
function redactSensitiveFields(obj) {
|
|
1728
1989
|
if (obj === null || obj === void 0) return obj;
|
|
1729
1990
|
if (typeof obj !== "object") return obj;
|
|
@@ -1802,8 +2063,8 @@ function registerToolWithWorklog(server, runtime, sessionContext, toolName, desc
|
|
|
1802
2063
|
async function main(opts = {}) {
|
|
1803
2064
|
let bindingReadyResolve = null;
|
|
1804
2065
|
let bindingReadyReject = null;
|
|
1805
|
-
const bindingReady = new Promise((
|
|
1806
|
-
bindingReadyResolve =
|
|
2066
|
+
const bindingReady = new Promise((resolve7, reject) => {
|
|
2067
|
+
bindingReadyResolve = resolve7;
|
|
1807
2068
|
bindingReadyReject = reject;
|
|
1808
2069
|
});
|
|
1809
2070
|
const devMode = Boolean(config2.devMode);
|
|
@@ -1814,7 +2075,7 @@ async function main(opts = {}) {
|
|
|
1814
2075
|
projectId: null,
|
|
1815
2076
|
apiKeySource: null,
|
|
1816
2077
|
apiKeyFingerprint: null,
|
|
1817
|
-
authoritativeBinding:
|
|
2078
|
+
authoritativeBinding: null,
|
|
1818
2079
|
ideType: void 0
|
|
1819
2080
|
};
|
|
1820
2081
|
let workspaceRoot;
|
|
@@ -1822,42 +2083,53 @@ async function main(opts = {}) {
|
|
|
1822
2083
|
name: "memoraone-mcp",
|
|
1823
2084
|
version: "1.0.0"
|
|
1824
2085
|
});
|
|
1825
|
-
const
|
|
1826
|
-
|
|
2086
|
+
const resolveSessionBindingFromInitialize = async (params) => {
|
|
2087
|
+
const initializeRoots = extractWorkspaceRootsFromInitialize(params);
|
|
2088
|
+
if (initializeRoots.length === 0 && opts.daemonBindingHint) {
|
|
2089
|
+
if (isInitializeDebugEnabled()) {
|
|
2090
|
+
console.error(
|
|
2091
|
+
"[memoraone-mcp][init-debug] Workspace resolution strategy: daemonBindingHint (bridge pre-resolved)"
|
|
2092
|
+
);
|
|
2093
|
+
}
|
|
2094
|
+
return opts.daemonBindingHint;
|
|
2095
|
+
}
|
|
2096
|
+
let rootsListUris;
|
|
2097
|
+
let rootsListAttempted = false;
|
|
1827
2098
|
try {
|
|
1828
2099
|
const rootsResult = await server.server.listRoots({});
|
|
2100
|
+
rootsListAttempted = true;
|
|
1829
2101
|
const roots = Array.isArray(rootsResult?.roots) ? rootsResult.roots : [];
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
2102
|
+
if (isInitializeDebugEnabled()) {
|
|
2103
|
+
console.error(`[memoraone-mcp][init-debug] roots/list returned ${roots.length}: ${JSON.stringify(roots)}`);
|
|
2104
|
+
}
|
|
2105
|
+
if (roots.length > 0) {
|
|
2106
|
+
rootsListUris = roots.map((root) => root?.uri).filter((uri) => Boolean(uri));
|
|
2107
|
+
if (rootsListUris.length > 0 && isInitializeDebugEnabled()) {
|
|
2108
|
+
console.error("[memoraone-mcp][init-debug] Workspace resolution strategy: roots/list");
|
|
2109
|
+
}
|
|
1834
2110
|
}
|
|
1835
2111
|
} catch (e) {
|
|
1836
|
-
|
|
2112
|
+
if (isInitializeDebugEnabled()) {
|
|
2113
|
+
console.error("[memoraone-mcp][init-debug] roots/list failed:", String(e));
|
|
2114
|
+
}
|
|
1837
2115
|
}
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
2116
|
+
if (isInitializeDebugEnabled()) {
|
|
2117
|
+
console.error(`[memoraone-mcp][init-debug] process.cwd(): ${process.cwd()}`);
|
|
2118
|
+
console.error(
|
|
2119
|
+
`[memoraone-mcp][init-debug] WORKSPACE_FOLDER_PATHS: ${process.env.WORKSPACE_FOLDER_PATHS ?? "(unset)"}`
|
|
2120
|
+
);
|
|
1843
2121
|
if (params.workspaceFolders?.length) {
|
|
1844
|
-
|
|
1845
|
-
console.error("[memoraone-mcp] Workspace resolution strategy: initialize.workspaceFolders");
|
|
2122
|
+
console.error("[memoraone-mcp][init-debug] Workspace resolution strategy: initialize.workspaceFolders");
|
|
1846
2123
|
} else if (params.rootUri) {
|
|
1847
|
-
|
|
1848
|
-
console.error("[memoraone-mcp] Workspace resolution strategy: initialize.rootUri");
|
|
1849
|
-
} else {
|
|
1850
|
-
const cursorRoot = getCursorWorkspaceRootFromEnv();
|
|
1851
|
-
if (cursorRoot !== void 0) {
|
|
1852
|
-
fallbackWorkspaceRoot = cursorRoot;
|
|
1853
|
-
console.error("[memoraone-mcp] Workspace resolution strategy: env.WORKSPACE_FOLDER_PATHS");
|
|
1854
|
-
} else {
|
|
1855
|
-
fallbackWorkspaceRoot = process.cwd();
|
|
1856
|
-
console.error("[memoraone-mcp] Workspace resolution strategy: process.cwd() fallback");
|
|
1857
|
-
}
|
|
2124
|
+
console.error("[memoraone-mcp][init-debug] Workspace resolution strategy: initialize.rootUri");
|
|
1858
2125
|
}
|
|
1859
2126
|
}
|
|
1860
|
-
const binding = await
|
|
2127
|
+
const binding = await resolveBindingFromInitializeParams(params, {
|
|
2128
|
+
fallbackWorkspaceRoots: getEnvWorkspaceRootCandidates(),
|
|
2129
|
+
rootsListUris,
|
|
2130
|
+
rootsListAttempted,
|
|
2131
|
+
...getBridgeBindingResolveOptions()
|
|
2132
|
+
});
|
|
1861
2133
|
workspaceRoot = binding.workspaceRoot;
|
|
1862
2134
|
return binding;
|
|
1863
2135
|
};
|
|
@@ -2037,7 +2309,7 @@ async function main(opts = {}) {
|
|
|
2037
2309
|
if (runtime.ideType && opts.onSessionIdeTypeKnown) {
|
|
2038
2310
|
opts.onSessionIdeTypeKnown(runtime.ideType);
|
|
2039
2311
|
}
|
|
2040
|
-
if (!initializeDiagDumped) {
|
|
2312
|
+
if (isInitializeDebugEnabled() && !initializeDiagDumped) {
|
|
2041
2313
|
initializeDiagDumped = true;
|
|
2042
2314
|
const folders = Array.isArray(params.workspaceFolders) ? params.workspaceFolders.map((f) => ({ name: f?.name, uri: f?.uri })) : params.workspaceFolders;
|
|
2043
2315
|
const capKeys = params.capabilities && typeof params.capabilities === "object" ? Object.keys(params.capabilities) : [];
|
|
@@ -2069,7 +2341,13 @@ async function main(opts = {}) {
|
|
|
2069
2341
|
String(process.env.MEMORAONE_DEBUG_AUTH ?? "").trim().toLowerCase()
|
|
2070
2342
|
);
|
|
2071
2343
|
const debugLog3 = config2.devMode || debugAuth;
|
|
2072
|
-
const binding =
|
|
2344
|
+
const binding = await resolveSessionBindingFromInitialize(params);
|
|
2345
|
+
if (opts.daemonBindingHint && !bindingsMatch(opts.daemonBindingHint, binding)) {
|
|
2346
|
+
const errMsg = formatBindingMismatchError(opts.daemonBindingHint, binding);
|
|
2347
|
+
console.error(`[memoraone-mcp][ERROR] ${errMsg}`);
|
|
2348
|
+
bindingReadyReject?.(new Error(errMsg));
|
|
2349
|
+
throw new Error(errMsg);
|
|
2350
|
+
}
|
|
2073
2351
|
const apiKeyToUse = binding.apiKey;
|
|
2074
2352
|
if (!apiKeyToUse) {
|
|
2075
2353
|
throw new Error(
|
|
@@ -2193,10 +2471,10 @@ async function main(opts = {}) {
|
|
|
2193
2471
|
console.error("[memoraone-mcp] MCP server ready");
|
|
2194
2472
|
}
|
|
2195
2473
|
if (opts.sessionSocket) {
|
|
2196
|
-
await new Promise((
|
|
2474
|
+
await new Promise((resolve7) => {
|
|
2197
2475
|
opts.sessionSocket.once("close", () => {
|
|
2198
2476
|
shutdown("session closed", false);
|
|
2199
|
-
|
|
2477
|
+
resolve7();
|
|
2200
2478
|
});
|
|
2201
2479
|
});
|
|
2202
2480
|
}
|
|
@@ -2231,11 +2509,11 @@ function parseBindingFromEnv(projectId) {
|
|
|
2231
2509
|
}
|
|
2232
2510
|
async function ensureSocketClean(socketPath) {
|
|
2233
2511
|
try {
|
|
2234
|
-
|
|
2512
|
+
fs7.accessSync(socketPath);
|
|
2235
2513
|
} catch {
|
|
2236
2514
|
return;
|
|
2237
2515
|
}
|
|
2238
|
-
return new Promise((
|
|
2516
|
+
return new Promise((resolve7) => {
|
|
2239
2517
|
const client = net.createConnection({ path: socketPath }, () => {
|
|
2240
2518
|
client.destroy();
|
|
2241
2519
|
log("daemon already running, exiting");
|
|
@@ -2243,11 +2521,11 @@ async function ensureSocketClean(socketPath) {
|
|
|
2243
2521
|
});
|
|
2244
2522
|
client.on("error", () => {
|
|
2245
2523
|
try {
|
|
2246
|
-
|
|
2524
|
+
fs7.unlinkSync(socketPath);
|
|
2247
2525
|
log("stale socket removed");
|
|
2248
2526
|
} catch {
|
|
2249
2527
|
}
|
|
2250
|
-
|
|
2528
|
+
resolve7();
|
|
2251
2529
|
});
|
|
2252
2530
|
});
|
|
2253
2531
|
}
|
|
@@ -2255,7 +2533,7 @@ async function runDaemon() {
|
|
|
2255
2533
|
const projectId = parseProjectIdFromArgv();
|
|
2256
2534
|
const binding = parseBindingFromEnv(projectId);
|
|
2257
2535
|
const ideType = parseIdeTypeFromArgv(process.argv.slice(2)) ?? config2.ideType ?? resolveIdeTypeFromEnv();
|
|
2258
|
-
const socketPath =
|
|
2536
|
+
const socketPath = getBindingSocketPath(binding, process.env);
|
|
2259
2537
|
let nextSessionId = 1;
|
|
2260
2538
|
let activeSessions = 0;
|
|
2261
2539
|
let idleTimer = null;
|
|
@@ -2263,7 +2541,7 @@ async function runDaemon() {
|
|
|
2263
2541
|
const dir = ensureBaseDir();
|
|
2264
2542
|
log(`directory ensured: ${dir}`);
|
|
2265
2543
|
log(
|
|
2266
|
-
`
|
|
2544
|
+
`daemon spawn hint project=${binding.projectId} workspace=${binding.workspaceRoot} m1=${binding.m1Path} source=${binding.bindingSource} apiKeySource=${binding.apiKeySource}`
|
|
2267
2545
|
);
|
|
2268
2546
|
log("session policy: concurrent bridge sessions allowed per project daemon");
|
|
2269
2547
|
if (!isDaemonIdleShutdownAllowed()) {
|
|
@@ -2277,10 +2555,11 @@ async function runDaemon() {
|
|
|
2277
2555
|
await ensureSocketClean(socketPath);
|
|
2278
2556
|
const cleanupSocketFile = () => {
|
|
2279
2557
|
try {
|
|
2280
|
-
if (
|
|
2281
|
-
|
|
2558
|
+
if (fs7.existsSync(socketPath)) {
|
|
2559
|
+
fs7.unlinkSync(socketPath);
|
|
2282
2560
|
log("socket removed");
|
|
2283
2561
|
}
|
|
2562
|
+
removeBindingSidecar(socketPath);
|
|
2284
2563
|
} catch (err) {
|
|
2285
2564
|
log(`socket cleanup warning: ${String(err)}`);
|
|
2286
2565
|
}
|
|
@@ -2323,7 +2602,7 @@ async function runDaemon() {
|
|
|
2323
2602
|
const transport = new import_stdio2.StdioServerTransport(socket, socket);
|
|
2324
2603
|
try {
|
|
2325
2604
|
await main({
|
|
2326
|
-
|
|
2605
|
+
daemonBindingHint: binding,
|
|
2327
2606
|
transport,
|
|
2328
2607
|
sessionSocket: socket,
|
|
2329
2608
|
sessionLabel: `daemon-session-${sessionId}`,
|
|
@@ -2362,13 +2641,14 @@ async function runDaemon() {
|
|
|
2362
2641
|
process.on("SIGINT", () => shutdownNow("SIGINT"));
|
|
2363
2642
|
process.on("SIGTERM", () => shutdownNow("SIGTERM"));
|
|
2364
2643
|
process.on("exit", cleanupSocketFile);
|
|
2365
|
-
return new Promise((
|
|
2644
|
+
return new Promise((resolve7) => {
|
|
2366
2645
|
server.listen(socketPath, () => {
|
|
2646
|
+
writeBindingSidecar(socketPath, binding, ideType ?? "");
|
|
2367
2647
|
log(`daemon started, listening on ${socketPath}`);
|
|
2368
2648
|
void daemonHeartbeat.start().catch((err) => {
|
|
2369
2649
|
log(`daemon heartbeat start error: ${String(err)}`);
|
|
2370
2650
|
});
|
|
2371
|
-
|
|
2651
|
+
resolve7();
|
|
2372
2652
|
});
|
|
2373
2653
|
});
|
|
2374
2654
|
}
|