@memoraone/mcp 0.1.18 → 0.1.20
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 +280 -14
- package/dist/daemon.cjs +372 -134
- package/dist/index.cjs +372 -134
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -30,7 +30,7 @@ var require_package = __commonJS({
|
|
|
30
30
|
"package.json"(exports2, module2) {
|
|
31
31
|
module2.exports = {
|
|
32
32
|
name: "@memoraone/mcp",
|
|
33
|
-
version: "0.1.
|
|
33
|
+
version: "0.1.20",
|
|
34
34
|
type: "module",
|
|
35
35
|
main: "dist/index.cjs",
|
|
36
36
|
bin: {
|
|
@@ -67,7 +67,7 @@ var require_package = __commonJS({
|
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
// src/cli.ts
|
|
70
|
-
var
|
|
70
|
+
var path4 = __toESM(require("path"), 1);
|
|
71
71
|
var net = __toESM(require("net"), 1);
|
|
72
72
|
var import_node_child_process = require("child_process");
|
|
73
73
|
|
|
@@ -219,6 +219,270 @@ function encodeResolvedBinding(binding) {
|
|
|
219
219
|
return Buffer.from(JSON.stringify(binding), "utf8").toString("base64");
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
// src/setupIdeFiles.ts
|
|
223
|
+
var fs3 = __toESM(require("fs/promises"), 1);
|
|
224
|
+
var path3 = __toESM(require("path"), 1);
|
|
225
|
+
var MANAGED_MARKER = "<!-- MemoraOne managed IDE helper -->";
|
|
226
|
+
var MEMORAONE_MCP_SERVER = {
|
|
227
|
+
command: "npx",
|
|
228
|
+
args: ["-y", "@memoraone/mcp"]
|
|
229
|
+
};
|
|
230
|
+
function assertUnderRepoRoot(repoRoot, absPath) {
|
|
231
|
+
const normRoot = path3.resolve(repoRoot) + path3.sep;
|
|
232
|
+
const normPath = path3.resolve(absPath);
|
|
233
|
+
if (normPath !== path3.resolve(repoRoot) && !normPath.startsWith(normRoot)) {
|
|
234
|
+
throw new Error(`[setup-ide-files] Refusing to write outside repo root: ${absPath}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async function pathExists(filePath) {
|
|
238
|
+
try {
|
|
239
|
+
await fs3.access(filePath);
|
|
240
|
+
return true;
|
|
241
|
+
} catch {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
async function findRepoRoot(startDir) {
|
|
246
|
+
let current = path3.resolve(startDir);
|
|
247
|
+
const root = path3.parse(current).root;
|
|
248
|
+
while (true) {
|
|
249
|
+
const gitPath = path3.join(current, ".git");
|
|
250
|
+
const m1Path = path3.join(current, "memoraone.m1");
|
|
251
|
+
if (await pathExists(gitPath) || await pathExists(m1Path)) {
|
|
252
|
+
return current;
|
|
253
|
+
}
|
|
254
|
+
if (current === root) {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
current = path3.dirname(current);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function stripLeadingLineComments(text) {
|
|
261
|
+
return text.split("\n").filter((line) => !/^\s*\/\//.test(line)).join("\n");
|
|
262
|
+
}
|
|
263
|
+
function cursorRuleBody() {
|
|
264
|
+
return `${MANAGED_MARKER}
|
|
265
|
+
|
|
266
|
+
## MemoraOne MCP (IDE agent only)
|
|
267
|
+
|
|
268
|
+
This repository uses **MemoraOne** via the MCP server named **user-memoraone** (Cursor MCP configuration). This guidance applies to the **IDE coding agent** only \u2014 not the MemoraOne Studio runtime path.
|
|
269
|
+
|
|
270
|
+
### Tools
|
|
271
|
+
|
|
272
|
+
- Before answering questions about **prior decisions**, **remembered facts**, **identity or personal recall**, **preferences**, **repo history**, or **what to do next**, call **\`memora_ask_with_memory\`** so replies stay aligned with MemoraOne memory.
|
|
273
|
+
- After **meaningful repository changes** (decisions, fixes, new endpoints or schema, migrations, meaningful wiring), call **\`memora_post_event\`** to append a concise timeline note.
|
|
274
|
+
|
|
275
|
+
Keep tool usage proportional to the task; do not spam events for trivial edits.
|
|
276
|
+
`;
|
|
277
|
+
}
|
|
278
|
+
function copilotAndJetBrainsBody(title) {
|
|
279
|
+
return `${MANAGED_MARKER}
|
|
280
|
+
|
|
281
|
+
# ${title}
|
|
282
|
+
|
|
283
|
+
This repo is set up to use **MemoraOne** through MCP where your editor exposes it. These instructions apply to the **IDE assistant** in this workspace, not to other MemoraOne runtimes.
|
|
284
|
+
|
|
285
|
+
## Behavior
|
|
286
|
+
|
|
287
|
+
- For questions about **earlier decisions**, **stored facts**, **personal or identity recall**, **preferences**, **project history**, or **recommended next steps**, use MemoraOne memory tools (e.g. **\`memora_ask_with_memory\`**) when available before answering.
|
|
288
|
+
- After **substantive changes** to this codebase (feature work, bugfixes, API/schema changes, migrations, meaningful integration steps), record a short timeline note with **\`memora_post_event\`** when that tool is available.
|
|
289
|
+
|
|
290
|
+
Use judgment: skip logging for trivial typo-only edits.
|
|
291
|
+
`;
|
|
292
|
+
}
|
|
293
|
+
function mcpJsonHeader() {
|
|
294
|
+
return `// ${MANAGED_MARKER}
|
|
295
|
+
`;
|
|
296
|
+
}
|
|
297
|
+
function buildMcpJsonBody(existing) {
|
|
298
|
+
const base = existing && typeof existing === "object" ? { ...existing } : { mcpServers: {} };
|
|
299
|
+
const servers = typeof base.mcpServers === "object" && base.mcpServers !== null && !Array.isArray(base.mcpServers) ? { ...base.mcpServers } : {};
|
|
300
|
+
servers.memoraone = { ...MEMORAONE_MCP_SERVER };
|
|
301
|
+
const merged = { ...base, mcpServers: servers };
|
|
302
|
+
return mcpJsonHeader() + JSON.stringify(merged, null, 2) + "\n";
|
|
303
|
+
}
|
|
304
|
+
async function writeManagedMarkdown(repoRoot, relPath, fullContent, opts) {
|
|
305
|
+
const abs = path3.join(repoRoot, relPath);
|
|
306
|
+
assertUnderRepoRoot(repoRoot, abs);
|
|
307
|
+
let prior = "";
|
|
308
|
+
let existed = false;
|
|
309
|
+
try {
|
|
310
|
+
prior = await fs3.readFile(abs, "utf8");
|
|
311
|
+
existed = true;
|
|
312
|
+
} catch (err) {
|
|
313
|
+
if (err?.code !== "ENOENT") throw err;
|
|
314
|
+
}
|
|
315
|
+
if (!existed) {
|
|
316
|
+
if (opts.dryRun) return "created";
|
|
317
|
+
await fs3.mkdir(path3.dirname(abs), { recursive: true });
|
|
318
|
+
await fs3.writeFile(abs, fullContent, "utf8");
|
|
319
|
+
return "created";
|
|
320
|
+
}
|
|
321
|
+
if (prior.includes(MANAGED_MARKER)) {
|
|
322
|
+
if (prior === fullContent) return "skipped";
|
|
323
|
+
if (opts.dryRun) return "updated";
|
|
324
|
+
await fs3.mkdir(path3.dirname(abs), { recursive: true });
|
|
325
|
+
await fs3.writeFile(abs, fullContent, "utf8");
|
|
326
|
+
return "updated";
|
|
327
|
+
}
|
|
328
|
+
if (!opts.force) return "skipped-untracked";
|
|
329
|
+
if (opts.dryRun) return "updated";
|
|
330
|
+
await fs3.mkdir(path3.dirname(abs), { recursive: true });
|
|
331
|
+
await fs3.writeFile(abs, fullContent, "utf8");
|
|
332
|
+
return "updated";
|
|
333
|
+
}
|
|
334
|
+
async function writeMcpJson(repoRoot, opts) {
|
|
335
|
+
const abs = path3.join(repoRoot, ".vscode/mcp.json");
|
|
336
|
+
assertUnderRepoRoot(repoRoot, abs);
|
|
337
|
+
let raw = "";
|
|
338
|
+
let existed = false;
|
|
339
|
+
try {
|
|
340
|
+
raw = await fs3.readFile(abs, "utf8");
|
|
341
|
+
existed = true;
|
|
342
|
+
} catch (err) {
|
|
343
|
+
if (err?.code !== "ENOENT") throw err;
|
|
344
|
+
}
|
|
345
|
+
if (!existed) {
|
|
346
|
+
const body = buildMcpJsonBody(null);
|
|
347
|
+
if (opts.dryRun) return "created";
|
|
348
|
+
await fs3.mkdir(path3.dirname(abs), { recursive: true });
|
|
349
|
+
await fs3.writeFile(abs, body, "utf8");
|
|
350
|
+
return "created";
|
|
351
|
+
}
|
|
352
|
+
const managed = raw.includes(MANAGED_MARKER);
|
|
353
|
+
if (!managed && !opts.force) return "skipped-untracked";
|
|
354
|
+
let parsed = null;
|
|
355
|
+
try {
|
|
356
|
+
parsed = JSON.parse(stripLeadingLineComments(raw));
|
|
357
|
+
} catch {
|
|
358
|
+
parsed = null;
|
|
359
|
+
}
|
|
360
|
+
if (!parsed && !opts.force) return "skipped-untracked";
|
|
361
|
+
const next = buildMcpJsonBody(parsed);
|
|
362
|
+
if (managed && next === raw) return "skipped";
|
|
363
|
+
if (opts.dryRun) return "updated";
|
|
364
|
+
await fs3.mkdir(path3.dirname(abs), { recursive: true });
|
|
365
|
+
await fs3.writeFile(abs, next, "utf8");
|
|
366
|
+
return "updated";
|
|
367
|
+
}
|
|
368
|
+
function parseSetupIdeFlags(argv) {
|
|
369
|
+
let cursor = false;
|
|
370
|
+
let vscode = false;
|
|
371
|
+
let jetbrains = false;
|
|
372
|
+
let all = false;
|
|
373
|
+
let force = false;
|
|
374
|
+
let dryRun = false;
|
|
375
|
+
const unknown = [];
|
|
376
|
+
for (const a of argv) {
|
|
377
|
+
if (a === "--cursor") cursor = true;
|
|
378
|
+
else if (a === "--vscode") vscode = true;
|
|
379
|
+
else if (a === "--jetbrains") jetbrains = true;
|
|
380
|
+
else if (a === "--all") all = true;
|
|
381
|
+
else if (a === "--force") force = true;
|
|
382
|
+
else if (a === "--dry-run") dryRun = true;
|
|
383
|
+
else if (a.startsWith("-")) unknown.push(a);
|
|
384
|
+
}
|
|
385
|
+
const specific = cursor || vscode || jetbrains;
|
|
386
|
+
let targets;
|
|
387
|
+
if (all || !specific) {
|
|
388
|
+
targets = { cursor: true, vscode: true, jetbrains: true };
|
|
389
|
+
} else {
|
|
390
|
+
targets = { cursor, vscode, jetbrains };
|
|
391
|
+
}
|
|
392
|
+
return { targets, force, dryRun, unknown };
|
|
393
|
+
}
|
|
394
|
+
function summarizeOutcomes(outcomes) {
|
|
395
|
+
const created = [];
|
|
396
|
+
const updated = [];
|
|
397
|
+
const skipped = [];
|
|
398
|
+
const skippedUntracked = [];
|
|
399
|
+
for (const [file, o] of Object.entries(outcomes)) {
|
|
400
|
+
if (o === "created") created.push(file);
|
|
401
|
+
else if (o === "updated") updated.push(file);
|
|
402
|
+
else if (o === "skipped") skipped.push(file);
|
|
403
|
+
else skippedUntracked.push(file);
|
|
404
|
+
}
|
|
405
|
+
const lines = ["[setup-ide-files] Summary"];
|
|
406
|
+
if (created.length) lines.push(` created: ${created.join(", ")}`);
|
|
407
|
+
if (updated.length) lines.push(` updated: ${updated.join(", ")}`);
|
|
408
|
+
if (skipped.length) lines.push(` skipped (unchanged): ${skipped.join(", ")}`);
|
|
409
|
+
if (skippedUntracked.length) {
|
|
410
|
+
lines.push(` skipped (unmanaged existing file, use --force): ${skippedUntracked.join(", ")}`);
|
|
411
|
+
}
|
|
412
|
+
console.log(lines.join("\n"));
|
|
413
|
+
}
|
|
414
|
+
async function runSetupIdeFiles(o) {
|
|
415
|
+
const outcomes = {};
|
|
416
|
+
const repoRoot = await findRepoRoot(o.cwd);
|
|
417
|
+
if (!repoRoot) {
|
|
418
|
+
return {
|
|
419
|
+
exitCode: 1,
|
|
420
|
+
repoRoot: null,
|
|
421
|
+
outcomes,
|
|
422
|
+
error: "[setup-ide-files] No repo root found (looked for .git or memoraone.m1)."
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
const cursorContent = `---
|
|
426
|
+
description: MemoraOne MCP \u2014 IDE agent instructions
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
` + cursorRuleBody();
|
|
430
|
+
if (o.targets.cursor) {
|
|
431
|
+
outcomes[".cursor/rules/memoraone-mcp.mdc"] = await writeManagedMarkdown(
|
|
432
|
+
repoRoot,
|
|
433
|
+
".cursor/rules/memoraone-mcp.mdc",
|
|
434
|
+
cursorContent,
|
|
435
|
+
{ force: o.force, dryRun: o.dryRun }
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
if (o.targets.vscode) {
|
|
439
|
+
outcomes[".vscode/mcp.json"] = await writeMcpJson(repoRoot, {
|
|
440
|
+
force: o.force,
|
|
441
|
+
dryRun: o.dryRun
|
|
442
|
+
});
|
|
443
|
+
outcomes[".github/copilot-instructions.md"] = await writeManagedMarkdown(
|
|
444
|
+
repoRoot,
|
|
445
|
+
".github/copilot-instructions.md",
|
|
446
|
+
copilotAndJetBrainsBody("MemoraOne MCP \u2014 GitHub Copilot"),
|
|
447
|
+
{ force: o.force, dryRun: o.dryRun }
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
if (o.targets.jetbrains) {
|
|
451
|
+
outcomes[".aiassistant/rules/memoraone.md"] = await writeManagedMarkdown(
|
|
452
|
+
repoRoot,
|
|
453
|
+
".aiassistant/rules/memoraone.md",
|
|
454
|
+
copilotAndJetBrainsBody("MemoraOne MCP \u2014 JetBrains AI Assistant"),
|
|
455
|
+
{ force: o.force, dryRun: o.dryRun }
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
return { exitCode: 0, repoRoot, outcomes };
|
|
459
|
+
}
|
|
460
|
+
async function cliSetupIdeFiles(argv) {
|
|
461
|
+
const { targets, force, dryRun, unknown } = parseSetupIdeFlags(argv);
|
|
462
|
+
if (unknown.length) {
|
|
463
|
+
console.error(`[setup-ide-files] Unknown option(s): ${unknown.join(", ")}`);
|
|
464
|
+
return 1;
|
|
465
|
+
}
|
|
466
|
+
const result = await runSetupIdeFiles({
|
|
467
|
+
cwd: process.cwd(),
|
|
468
|
+
targets,
|
|
469
|
+
force,
|
|
470
|
+
dryRun
|
|
471
|
+
});
|
|
472
|
+
if (result.error) {
|
|
473
|
+
console.error(result.error);
|
|
474
|
+
return result.exitCode;
|
|
475
|
+
}
|
|
476
|
+
if (result.repoRoot) {
|
|
477
|
+
console.log(`[setup-ide-files] Repo root: ${result.repoRoot}`);
|
|
478
|
+
}
|
|
479
|
+
summarizeOutcomes(result.outcomes);
|
|
480
|
+
if (dryRun) {
|
|
481
|
+
console.log("[setup-ide-files] Dry run: no files written.");
|
|
482
|
+
}
|
|
483
|
+
return 0;
|
|
484
|
+
}
|
|
485
|
+
|
|
222
486
|
// src/cli.ts
|
|
223
487
|
var { version } = require_package();
|
|
224
488
|
var args = process.argv.slice(2);
|
|
@@ -227,10 +491,18 @@ if (args.includes("--version") || args.includes("-v")) {
|
|
|
227
491
|
process.exit(0);
|
|
228
492
|
}
|
|
229
493
|
if (args.includes("--help") || args.includes("-h")) {
|
|
230
|
-
console.log(
|
|
494
|
+
console.log(
|
|
495
|
+
"Usage: memoraone-mcp [--version] [--help] [--daemon --project-id <uuid>]\n memoraone-mcp setup-ide-files [--all|--cursor|--vscode|--jetbrains] [--force] [--dry-run]"
|
|
496
|
+
);
|
|
231
497
|
process.exit(0);
|
|
232
498
|
}
|
|
233
|
-
if (args
|
|
499
|
+
if (args[0] === "setup-ide-files") {
|
|
500
|
+
cliSetupIdeFiles(args.slice(1)).then((code) => process.exit(code)).catch((err) => {
|
|
501
|
+
process.stderr.write(`[memoraone-mcp] setup-ide-files fatal: ${String(err)}
|
|
502
|
+
`);
|
|
503
|
+
process.exit(1);
|
|
504
|
+
});
|
|
505
|
+
} else if (args.includes("--daemon")) {
|
|
234
506
|
import("./daemon.cjs").then(({ runDaemon }) => runDaemon()).catch((err) => {
|
|
235
507
|
process.stderr.write(`[memoraone-mcp] daemon fatal: ${String(err)}
|
|
236
508
|
`);
|
|
@@ -241,8 +513,8 @@ if (args.includes("--daemon")) {
|
|
|
241
513
|
const raw = process.env.WORKSPACE_FOLDER_PATHS;
|
|
242
514
|
const parts = [];
|
|
243
515
|
if (raw !== void 0 && raw.trim() !== "") {
|
|
244
|
-
for (const p of raw.split(
|
|
245
|
-
parts.push(
|
|
516
|
+
for (const p of raw.split(path4.delimiter).map((s) => s.trim()).filter(Boolean)) {
|
|
517
|
+
parts.push(path4.resolve(p));
|
|
246
518
|
}
|
|
247
519
|
}
|
|
248
520
|
parts.push(process.cwd());
|
|
@@ -256,10 +528,10 @@ if (args.includes("--daemon")) {
|
|
|
256
528
|
}
|
|
257
529
|
return deduped;
|
|
258
530
|
}, connectWithRetry = function(socketPath) {
|
|
259
|
-
return new Promise((
|
|
531
|
+
return new Promise((resolve4, reject) => {
|
|
260
532
|
const tryConnect = (attempt) => {
|
|
261
533
|
const socket = net.connect(socketPath, () => {
|
|
262
|
-
|
|
534
|
+
resolve4(socket);
|
|
263
535
|
});
|
|
264
536
|
socket.on("error", (err) => {
|
|
265
537
|
if (attempt >= MAX_RETRIES) {
|
|
@@ -308,12 +580,6 @@ if (args.includes("--daemon")) {
|
|
|
308
580
|
}
|
|
309
581
|
log("bridge connected");
|
|
310
582
|
log("forwarding active");
|
|
311
|
-
process.stdin.on("data", (chunk) => {
|
|
312
|
-
const text = chunk.toString("utf8");
|
|
313
|
-
const hasToolsCall = text.includes('"tools/call"') || text.includes("tools/call");
|
|
314
|
-
const hasInitialize = text.includes('"initialize"');
|
|
315
|
-
log(`[stdin-diagnostic] bytes=${chunk.length} tools/call=${hasToolsCall} initialize=${hasInitialize}`);
|
|
316
|
-
});
|
|
317
583
|
process.stdin.pipe(socket);
|
|
318
584
|
socket.pipe(process.stdout);
|
|
319
585
|
socket.on("close", () => process.exit(0));
|