@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.
Files changed (2) hide show
  1. package/dist/index.js +118 -12
  2. 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, undefined, 20);
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 that may not have been loaded by load_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("Limit search to a specific project"),
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
- const apiKey = process.env.MEMLY_API_KEY;
20640
+ let apiKey = process.env.MEMLY_API_KEY;
20641
+ let keyWasPrompted = false;
20595
20642
  if (!apiKey) {
20596
- console.error(`Error: MEMLY_API_KEY not set.
20643
+ console.log(`No MEMLY_API_KEY found in environment.
20597
20644
  `);
20598
- console.error("Get your API key at: https://memly.site/dashboard");
20599
- console.error(`Then run: export MEMLY_API_KEY=memly_...
20645
+ console.log(` Get your key at: https://memly.site/dashboard
20600
20646
  `);
20601
- process.exit(1);
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.6",
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
+ }