@memly/mcp-server 0.2.6 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +118 -12
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -19967,7 +19967,7 @@ class MemlyClient {
|
|
|
19967
19967
|
}
|
|
19968
19968
|
|
|
19969
19969
|
// src/tools.ts
|
|
19970
|
-
function registerTools(server, client) {
|
|
19970
|
+
function registerTools(server, client, projectId) {
|
|
19971
19971
|
server.tool("load_context", [
|
|
19972
19972
|
"ALWAYS call this tool FIRST, before responding to any message in a new conversation.",
|
|
19973
19973
|
"It loads your persistent memory from previous sessions so you can provide accurate, context-aware assistance.",
|
|
@@ -19977,7 +19977,7 @@ function registerTools(server, client) {
|
|
|
19977
19977
|
}, async ({ topic }) => {
|
|
19978
19978
|
try {
|
|
19979
19979
|
const query = topic ?? "recent technical decisions architecture patterns current work";
|
|
19980
|
-
const result = await client.searchMemories(query,
|
|
19980
|
+
const result = await client.searchMemories(query, projectId, 20);
|
|
19981
19981
|
if (result.is_cold_start || result.memories.length === 0) {
|
|
19982
19982
|
return {
|
|
19983
19983
|
content: [{
|
|
@@ -20045,9 +20045,9 @@ Then answer normally. Use the memories above to inform your response.`
|
|
|
20045
20045
|
};
|
|
20046
20046
|
}
|
|
20047
20047
|
});
|
|
20048
|
-
server.tool("search_memories", "Search your stored memories semantically. Call this when you need to recall a specific previous decision, code pattern, or technical context
|
|
20048
|
+
server.tool("search_memories", "Search your stored memories semantically across ALL projects (cross-project by default). Call this when you need to recall a specific previous decision, code pattern, or technical context. Pass project_id to scope to a specific project.", {
|
|
20049
20049
|
query: exports_external.string().min(1).describe("What to search for (natural language)"),
|
|
20050
|
-
project_id: exports_external.string().uuid().optional().describe("
|
|
20050
|
+
project_id: exports_external.string().uuid().optional().describe("Scope to a specific project UUID. Omit to search across all projects."),
|
|
20051
20051
|
limit: exports_external.number().int().min(1).max(20).optional().describe("Max results (default: 5)")
|
|
20052
20052
|
}, async ({ query, project_id, limit }) => {
|
|
20053
20053
|
try {
|
|
@@ -20085,7 +20085,7 @@ ${formatted}`
|
|
|
20085
20085
|
project_id: exports_external.string().uuid().optional().describe("Associate with a specific project")
|
|
20086
20086
|
}, async ({ content, project_id }) => {
|
|
20087
20087
|
try {
|
|
20088
|
-
await client.saveMemory(content, project_id, "user");
|
|
20088
|
+
await client.saveMemory(content, project_id ?? projectId, "user");
|
|
20089
20089
|
return {
|
|
20090
20090
|
content: [{
|
|
20091
20091
|
type: "text",
|
|
@@ -20250,6 +20250,7 @@ I am ready to continue our work.`
|
|
|
20250
20250
|
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from "node:fs";
|
|
20251
20251
|
import { join, resolve } from "node:path";
|
|
20252
20252
|
import { homedir, platform } from "node:os";
|
|
20253
|
+
import * as readline from "node:readline";
|
|
20253
20254
|
var MEMLY_INSTRUCTION_BLOCK = `
|
|
20254
20255
|
## Memly — Persistent Memory (Auto-Enabled)
|
|
20255
20256
|
|
|
@@ -20269,6 +20270,30 @@ You have access to the Memly MCP server (\`memly\`).
|
|
|
20269
20270
|
|
|
20270
20271
|
3. **Never** ask if you should remember something — just do it silently.
|
|
20271
20272
|
`.trim();
|
|
20273
|
+
async function promptApiKey() {
|
|
20274
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
20275
|
+
const isTTY = process.stdin.isTTY;
|
|
20276
|
+
if (isTTY) {
|
|
20277
|
+
rl.stdoutMuted = true;
|
|
20278
|
+
const origWrite = rl._writeToOutput.bind(rl);
|
|
20279
|
+
rl._writeToOutput = (str) => {
|
|
20280
|
+
if (rl.stdoutMuted) {
|
|
20281
|
+
process.stdout.write(str.replace(/[^\r\n]/g, "*"));
|
|
20282
|
+
} else {
|
|
20283
|
+
origWrite(str);
|
|
20284
|
+
}
|
|
20285
|
+
};
|
|
20286
|
+
}
|
|
20287
|
+
return new Promise((res) => {
|
|
20288
|
+
rl.question(" Memly API key: ", (answer) => {
|
|
20289
|
+
if (isTTY)
|
|
20290
|
+
process.stdout.write(`
|
|
20291
|
+
`);
|
|
20292
|
+
rl.close();
|
|
20293
|
+
res(answer.trim());
|
|
20294
|
+
});
|
|
20295
|
+
});
|
|
20296
|
+
}
|
|
20272
20297
|
async function validateApiKey(apiKey) {
|
|
20273
20298
|
try {
|
|
20274
20299
|
const controller = new AbortController;
|
|
@@ -20561,6 +20586,27 @@ function writeClaudeDesktop() {
|
|
|
20561
20586
|
};
|
|
20562
20587
|
}
|
|
20563
20588
|
}
|
|
20589
|
+
async function resolveProjectId(apiKey, projectRoot) {
|
|
20590
|
+
try {
|
|
20591
|
+
const pkgPath = join(projectRoot, "package.json");
|
|
20592
|
+
const fingerprint = existsSync(pkgPath) ? readFileSync(pkgPath, "utf-8").slice(0, 500) : `Project path: ${projectRoot}/src/index`;
|
|
20593
|
+
const controller = new AbortController;
|
|
20594
|
+
const timeout = setTimeout(() => controller.abort(), 8000);
|
|
20595
|
+
const res = await fetch("https://api.memly.site/v1/chat/completions", {
|
|
20596
|
+
method: "POST",
|
|
20597
|
+
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
20598
|
+
body: JSON.stringify({ model: "gpt-4.1-nano", stream: false, messages: [{ role: "user", content: fingerprint }] }),
|
|
20599
|
+
signal: controller.signal
|
|
20600
|
+
});
|
|
20601
|
+
clearTimeout(timeout);
|
|
20602
|
+
const id = res.headers.get("X-Memly-Project-Id");
|
|
20603
|
+
if (id && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id))
|
|
20604
|
+
return id;
|
|
20605
|
+
return null;
|
|
20606
|
+
} catch {
|
|
20607
|
+
return null;
|
|
20608
|
+
}
|
|
20609
|
+
}
|
|
20564
20610
|
async function reportTelemetry(results) {
|
|
20565
20611
|
try {
|
|
20566
20612
|
const controller = new AbortController;
|
|
@@ -20591,14 +20637,22 @@ async function runInit() {
|
|
|
20591
20637
|
console.log(" Memly Init — Auto-Memory Setup");
|
|
20592
20638
|
console.log(`â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”
|
|
20593
20639
|
`);
|
|
20594
|
-
|
|
20640
|
+
let apiKey = process.env.MEMLY_API_KEY;
|
|
20641
|
+
let keyWasPrompted = false;
|
|
20595
20642
|
if (!apiKey) {
|
|
20596
|
-
console.
|
|
20643
|
+
console.log(`No MEMLY_API_KEY found in environment.
|
|
20597
20644
|
`);
|
|
20598
|
-
console.
|
|
20599
|
-
console.error(`Then run: export MEMLY_API_KEY=memly_...
|
|
20645
|
+
console.log(` Get your key at: https://memly.site/dashboard
|
|
20600
20646
|
`);
|
|
20601
|
-
|
|
20647
|
+
apiKey = await promptApiKey();
|
|
20648
|
+
if (!apiKey) {
|
|
20649
|
+
console.error(`
|
|
20650
|
+
No API key provided. Aborting.
|
|
20651
|
+
`);
|
|
20652
|
+
process.exit(1);
|
|
20653
|
+
}
|
|
20654
|
+
process.env.MEMLY_API_KEY = apiKey;
|
|
20655
|
+
keyWasPrompted = true;
|
|
20602
20656
|
}
|
|
20603
20657
|
process.stdout.write("Validating API key... ");
|
|
20604
20658
|
const valid = await validateApiKey(apiKey);
|
|
@@ -20611,6 +20665,13 @@ async function runInit() {
|
|
|
20611
20665
|
}
|
|
20612
20666
|
console.log(`✓ Valid
|
|
20613
20667
|
`);
|
|
20668
|
+
if (keyWasPrompted) {
|
|
20669
|
+
const isWin = process.platform === "win32";
|
|
20670
|
+
const persistCmd = isWin ? '$env:MEMLY_API_KEY = "' + apiKey + '"' : 'export MEMLY_API_KEY="' + apiKey + '"';
|
|
20671
|
+
console.log(" To persist this key, add to your shell profile:");
|
|
20672
|
+
console.log(` ${persistCmd}
|
|
20673
|
+
`);
|
|
20674
|
+
}
|
|
20614
20675
|
console.log(`Project: ${projectRoot}
|
|
20615
20676
|
`);
|
|
20616
20677
|
console.log(`Detecting IDEs...
|
|
@@ -20667,6 +20728,34 @@ async function runInit() {
|
|
|
20667
20728
|
try {
|
|
20668
20729
|
writeFileSync(join(projectRoot, ".memly-initialized"), new Date().toISOString());
|
|
20669
20730
|
} catch {}
|
|
20731
|
+
process.stdout.write(`
|
|
20732
|
+
Resolving project ID... `);
|
|
20733
|
+
const projectId = await resolveProjectId(apiKey, projectRoot);
|
|
20734
|
+
if (projectId) {
|
|
20735
|
+
try {
|
|
20736
|
+
writeFileSync(join(projectRoot, ".memly-project"), JSON.stringify({ project_id: projectId }, null, 2));
|
|
20737
|
+
console.log(`✓ ${projectId}`);
|
|
20738
|
+
const MARKER = `<!-- memly-project:${projectId} -->`;
|
|
20739
|
+
const ideFiles = [
|
|
20740
|
+
join(projectRoot, ".cursorrules"),
|
|
20741
|
+
join(projectRoot, ".github", "copilot-instructions.md"),
|
|
20742
|
+
join(projectRoot, ".clinerules"),
|
|
20743
|
+
join(projectRoot, ".windsurfrules")
|
|
20744
|
+
];
|
|
20745
|
+
for (const f of ideFiles) {
|
|
20746
|
+
if (existsSync(f)) {
|
|
20747
|
+
const content = readFileSync(f, "utf-8");
|
|
20748
|
+
if (!content.includes(MARKER)) {
|
|
20749
|
+
writeFileSync(f, content.trimEnd() + `
|
|
20750
|
+
` + MARKER + `
|
|
20751
|
+
`);
|
|
20752
|
+
}
|
|
20753
|
+
}
|
|
20754
|
+
}
|
|
20755
|
+
} catch {}
|
|
20756
|
+
} else {
|
|
20757
|
+
console.log("⊘ skipped (proxy unreachable — run init again after connecting)");
|
|
20758
|
+
}
|
|
20670
20759
|
await reportTelemetry(results);
|
|
20671
20760
|
console.log(`
|
|
20672
20761
|
â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”
|
|
@@ -20683,6 +20772,8 @@ async function runInit() {
|
|
|
20683
20772
|
}
|
|
20684
20773
|
|
|
20685
20774
|
// src/index.ts
|
|
20775
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
20776
|
+
import { join as join2 } from "node:path";
|
|
20686
20777
|
var apiKey = process.env.MEMLY_API_KEY;
|
|
20687
20778
|
if (process.argv.includes("--init") || process.argv.includes("init")) {
|
|
20688
20779
|
await runInit();
|
|
@@ -20696,6 +20787,21 @@ if (!apiKey) {
|
|
|
20696
20787
|
var apiUrl = process.env.MEMLY_API_URL ?? "https://api.memly.site";
|
|
20697
20788
|
var client = new MemlyClient(apiUrl, apiKey);
|
|
20698
20789
|
var isHttp = process.argv.includes("--http");
|
|
20790
|
+
function readProjectId() {
|
|
20791
|
+
if (process.env.MEMLY_PROJECT_ID)
|
|
20792
|
+
return process.env.MEMLY_PROJECT_ID;
|
|
20793
|
+
try {
|
|
20794
|
+
const file = join2(process.cwd(), ".memly-project");
|
|
20795
|
+
if (existsSync2(file)) {
|
|
20796
|
+
const data = JSON.parse(readFileSync2(file, "utf-8"));
|
|
20797
|
+
if (data.project_id && /^[0-9a-f-]{36}$/i.test(data.project_id)) {
|
|
20798
|
+
return data.project_id;
|
|
20799
|
+
}
|
|
20800
|
+
}
|
|
20801
|
+
} catch {}
|
|
20802
|
+
return;
|
|
20803
|
+
}
|
|
20804
|
+
var projectId = readProjectId();
|
|
20699
20805
|
if (isHttp) {
|
|
20700
20806
|
const port = Number(process.env.MEMLY_PORT ?? 3800);
|
|
20701
20807
|
const httpServer = Bun.serve({
|
|
@@ -20707,7 +20813,7 @@ if (isHttp) {
|
|
|
20707
20813
|
}
|
|
20708
20814
|
if (url.pathname === "/mcp") {
|
|
20709
20815
|
const server = new McpServer({ name: "memly", version: "0.1.0" });
|
|
20710
|
-
registerTools(server, client);
|
|
20816
|
+
registerTools(server, client, projectId);
|
|
20711
20817
|
registerResources(server, client);
|
|
20712
20818
|
registerPrompts(server, client);
|
|
20713
20819
|
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
@@ -20724,7 +20830,7 @@ if (isHttp) {
|
|
|
20724
20830
|
console.log(` Health: http://localhost:${httpServer.port}/health`);
|
|
20725
20831
|
} else {
|
|
20726
20832
|
const server = new McpServer({ name: "memly", version: "0.1.0" });
|
|
20727
|
-
registerTools(server, client);
|
|
20833
|
+
registerTools(server, client, projectId);
|
|
20728
20834
|
registerResources(server, client);
|
|
20729
20835
|
registerPrompts(server, client);
|
|
20730
20836
|
const transport = new StdioServerTransport;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memly/mcp-server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "Memly MCP Server — persistent memory for any IDE",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -26,4 +26,4 @@
|
|
|
26
26
|
"zod": "^3.24.2"
|
|
27
27
|
},
|
|
28
28
|
"license": "BSL-1.1"
|
|
29
|
-
}
|
|
29
|
+
}
|