@memly/mcp-server 0.1.1 → 0.2.0

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 (3) hide show
  1. package/README.md +13 -1
  2. package/dist/index.js +261 -25
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -56,7 +56,19 @@ Go to [memly.site/dashboard/api-keys](https://memly.site/dashboard/api-keys)
56
56
  }
57
57
  ```
58
58
 
59
- ## Available Tools
59
+ ### Automatic Context (Zero Friction) ⚡
60
+
61
+ To make your AI agent proactively load context at the start of every session, add this to your `.cursorrules`, `.windsurfrules`, or `GEMINI.md`:
62
+
63
+ ```markdown
64
+ # Memly Integration
65
+ At the start of every session, you MUST:
66
+ 1. Call `memly.search_memories({ query: "current active task", limit: 5 })`
67
+ 2. Use this context to understand the project state.
68
+ 3. When you make major decisions, call `memly.save_memory`.
69
+ ```
70
+
71
+ ### Tools
60
72
 
61
73
  | Tool | Description |
62
74
  |---|---|
package/dist/index.js CHANGED
@@ -19968,7 +19968,48 @@ class MemlyClient {
19968
19968
 
19969
19969
  // src/tools.ts
19970
19970
  function registerTools(server, client) {
19971
- server.tool("search_memories", "Search your stored code memories semantically. Returns relevant memories based on meaning, not just keywords.", {
19971
+ server.tool("load_context", [
19972
+ "ALWAYS call this tool FIRST, before responding to any message in a new conversation.",
19973
+ "It loads your persistent memory from previous sessions so you can provide accurate, context-aware assistance.",
19974
+ "Do not skip this — without it you are missing critical context about the user's project, decisions, and preferences."
19975
+ ].join(" "), {
19976
+ topic: exports_external.string().optional().describe('Optional: topic or question to focus the memory search (e.g. "authentication", "database schema"). Leave empty to load general recent context.')
19977
+ }, async ({ topic }) => {
19978
+ try {
19979
+ const query = topic ?? "recent technical decisions architecture patterns current work";
19980
+ const result = await client.searchMemories(query, undefined, 10);
19981
+ if (result.is_cold_start || result.memories.length === 0) {
19982
+ return {
19983
+ content: [{
19984
+ type: "text",
19985
+ text: "\uD83E\uDDE0 Memly: No memories yet for this project. I'll start learning from this session — important decisions and context will be saved automatically."
19986
+ }]
19987
+ };
19988
+ }
19989
+ const formatted = result.memories.map((m, i) => `[${i + 1}] ${m.chunk_type ?? "general"} | confidence: ${m.confidence ? (m.confidence * 100).toFixed(0) + "%" : "n/a"}
19990
+ ${m.content}`).join(`
19991
+
19992
+ `);
19993
+ return {
19994
+ content: [{
19995
+ type: "text",
19996
+ text: `\uD83E\uDDE0 Memly: Loaded ${result.memories.length} memories from previous sessions.
19997
+
19998
+ ${formatted}
19999
+
20000
+ Use this context to inform your responses.`
20001
+ }]
20002
+ };
20003
+ } catch (err) {
20004
+ return {
20005
+ content: [{
20006
+ type: "text",
20007
+ text: `\uD83E\uDDE0 Memly: Could not load context (${err instanceof Error ? err.message : String(err)}). Continuing without memory.`
20008
+ }]
20009
+ };
20010
+ }
20011
+ });
20012
+ 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.", {
19972
20013
  query: exports_external.string().min(1).describe("What to search for (natural language)"),
19973
20014
  project_id: exports_external.string().uuid().optional().describe("Limit search to a specific project"),
19974
20015
  limit: exports_external.number().int().min(1).max(20).optional().describe("Max results (default: 5)")
@@ -20003,8 +20044,8 @@ ${formatted}`
20003
20044
  };
20004
20045
  }
20005
20046
  });
20006
- server.tool("remember", "Store a new memory — a fact, decision, code pattern, or important context you want to recall later.", {
20007
- content: exports_external.string().min(1).max(1e4).describe("The content to remember"),
20047
+ server.tool("remember", "Save an important fact, decision, or code pattern to persistent memory. Call this proactively whenever: the user makes an architectural decision, reveals important project context, you solve a non-trivial problem, or the user explicitly asks you to remember something.", {
20048
+ content: exports_external.string().min(1).max(1e4).describe("The content to remember — be specific and self-contained so it makes sense out of context"),
20008
20049
  project_id: exports_external.string().uuid().optional().describe("Associate with a specific project")
20009
20050
  }, async ({ content, project_id }) => {
20010
20051
  try {
@@ -20012,7 +20053,7 @@ ${formatted}`
20012
20053
  return {
20013
20054
  content: [{
20014
20055
  type: "text",
20015
- text: `✅ Memorized! Content queued for processing (${content.length} chars).`
20056
+ text: `✅ Saved to memory (${content.length} chars). This will be available in future sessions.`
20016
20057
  }]
20017
20058
  };
20018
20059
  } catch (err) {
@@ -20025,27 +20066,6 @@ ${formatted}`
20025
20066
  };
20026
20067
  }
20027
20068
  });
20028
- server.tool("forget", "Delete a specific memory by its ID.", {
20029
- memory_id: exports_external.string().uuid().describe("The ID of the memory to delete")
20030
- }, async ({ memory_id }) => {
20031
- try {
20032
- await client.deleteMemory(memory_id);
20033
- return {
20034
- content: [{
20035
- type: "text",
20036
- text: `\uD83D\uDDD1️ Memory ${memory_id} deleted.`
20037
- }]
20038
- };
20039
- } catch (err) {
20040
- return {
20041
- content: [{
20042
- type: "text",
20043
- text: `Failed to delete: ${err instanceof Error ? err.message : String(err)}`
20044
- }],
20045
- isError: true
20046
- };
20047
- }
20048
- });
20049
20069
  server.tool("list_projects", "List all your projects with memory statistics.", {}, async () => {
20050
20070
  try {
20051
20071
  const stats = await client.getStats();
@@ -20139,8 +20159,222 @@ function registerResources(server, client) {
20139
20159
  });
20140
20160
  }
20141
20161
 
20162
+ // src/prompts.ts
20163
+ function registerPrompts(server, client) {
20164
+ server.prompt("restore_context", {
20165
+ project_id: exports_external.string().uuid().optional().describe("Project ID to load context from")
20166
+ }, async (args) => {
20167
+ const project_id = args?.project_id;
20168
+ const stats = await client.getStats();
20169
+ const project = stats.projects.find((p) => p.project_id === project_id) ?? stats.projects[0];
20170
+ if (!project) {
20171
+ return {
20172
+ messages: [
20173
+ {
20174
+ role: "assistant",
20175
+ content: {
20176
+ type: "text",
20177
+ text: "No memories found. I'm ready to start learning from this session."
20178
+ }
20179
+ }
20180
+ ]
20181
+ };
20182
+ }
20183
+ const result = await client.searchMemories("context restore", project.project_id, 10);
20184
+ const memoryText = result.memories.map((m) => `[MEMORY:${m.id} (${m.created_at})]
20185
+ ${m.content}`).join(`
20186
+
20187
+ `);
20188
+ return {
20189
+ messages: [
20190
+ {
20191
+ role: "user",
20192
+ content: {
20193
+ type: "text",
20194
+ text: `Restore context for project ${project.project_id}`
20195
+ }
20196
+ },
20197
+ {
20198
+ role: "assistant",
20199
+ content: {
20200
+ type: "text",
20201
+ text: `I've retrieved the following context from Memly:
20202
+
20203
+ ${memoryText}
20204
+
20205
+ I am ready to continue our work.`
20206
+ }
20207
+ }
20208
+ ]
20209
+ };
20210
+ });
20211
+ }
20212
+
20213
+ // src/init.ts
20214
+ import { existsSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
20215
+ import { join, resolve } from "node:path";
20216
+ import { homedir, platform } from "node:os";
20217
+ var MEMLY_INSTRUCTION_BLOCK = `
20218
+ ## Memly — Persistent Memory (Auto-Enabled)
20219
+
20220
+ You have access to the Memly MCP server (\`memly\`).
20221
+
20222
+ **RULES (always follow, no exceptions):**
20223
+
20224
+ 1. **At the start of EVERY conversation**, call \`load_context\` before answering anything.
20225
+ - This loads memories from previous sessions.
20226
+ - If the user's message gives a clear topic, pass it as \`topic\` (e.g., \`topic: "authentication"\`).
20227
+ - Otherwise call with no arguments.
20228
+
20229
+ 2. **During the conversation**, call \`remember\` proactively whenever:
20230
+ - The user makes an architectural or technical decision
20231
+ - You solve a non-trivial bug or problem
20232
+ - The user shares important project context, preferences, or constraints
20233
+ - The user explicitly says "remember this"
20234
+
20235
+ 3. **Never** ask the user if you should remember something — just do it silently.
20236
+ `.trim();
20237
+ function writeCopilotInstructions(projectRoot) {
20238
+ const githubDir = join(projectRoot, ".github");
20239
+ const filePath = join(githubDir, "copilot-instructions.md");
20240
+ if (!existsSync(githubDir))
20241
+ mkdirSync(githubDir, { recursive: true });
20242
+ const MARKER = "<!-- memly-instructions -->";
20243
+ if (existsSync(filePath)) {
20244
+ const existing = readFileSync(filePath, "utf-8");
20245
+ if (existing.includes(MARKER)) {
20246
+ return { ide: "VS Code / GitHub Copilot", file: filePath, status: "skipped" };
20247
+ }
20248
+ writeFileSync(filePath, `${existing.trimEnd()}
20249
+
20250
+ ${MARKER}
20251
+ ${MEMLY_INSTRUCTION_BLOCK}
20252
+ ${MARKER}
20253
+ `);
20254
+ return { ide: "VS Code / GitHub Copilot", file: filePath, status: "updated" };
20255
+ }
20256
+ writeFileSync(filePath, `${MARKER}
20257
+ ${MEMLY_INSTRUCTION_BLOCK}
20258
+ ${MARKER}
20259
+ `);
20260
+ return { ide: "VS Code / GitHub Copilot", file: filePath, status: "created" };
20261
+ }
20262
+ function writeCursorRules(projectRoot) {
20263
+ const filePath = join(projectRoot, ".cursorrules");
20264
+ const MARKER = "# memly-instructions";
20265
+ if (existsSync(filePath)) {
20266
+ const existing = readFileSync(filePath, "utf-8");
20267
+ if (existing.includes(MARKER)) {
20268
+ return { ide: "Cursor", file: filePath, status: "skipped" };
20269
+ }
20270
+ writeFileSync(filePath, `${existing.trimEnd()}
20271
+
20272
+ ${MARKER}
20273
+ ${MEMLY_INSTRUCTION_BLOCK}
20274
+ `);
20275
+ return { ide: "Cursor", file: filePath, status: "updated" };
20276
+ }
20277
+ writeFileSync(filePath, `${MARKER}
20278
+ ${MEMLY_INSTRUCTION_BLOCK}
20279
+ `);
20280
+ return { ide: "Cursor", file: filePath, status: "created" };
20281
+ }
20282
+ function writeWindsurfRules(projectRoot) {
20283
+ const filePath = join(projectRoot, ".windsurfrules");
20284
+ const MARKER = "# memly-instructions";
20285
+ if (existsSync(filePath)) {
20286
+ const existing = readFileSync(filePath, "utf-8");
20287
+ if (existing.includes(MARKER)) {
20288
+ return { ide: "Windsurf", file: filePath, status: "skipped" };
20289
+ }
20290
+ writeFileSync(filePath, `${existing.trimEnd()}
20291
+
20292
+ ${MARKER}
20293
+ ${MEMLY_INSTRUCTION_BLOCK}
20294
+ `);
20295
+ return { ide: "Windsurf", file: filePath, status: "updated" };
20296
+ }
20297
+ writeFileSync(filePath, `${MARKER}
20298
+ ${MEMLY_INSTRUCTION_BLOCK}
20299
+ `);
20300
+ return { ide: "Windsurf", file: filePath, status: "created" };
20301
+ }
20302
+ function writeClaudeDesktop() {
20303
+ const os = platform();
20304
+ let configPath;
20305
+ if (os === "darwin") {
20306
+ configPath = join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
20307
+ } else if (os === "win32") {
20308
+ configPath = join(process.env.APPDATA ?? join(homedir(), "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
20309
+ } else {
20310
+ configPath = join(homedir(), ".config", "Claude", "claude_desktop_config.json");
20311
+ }
20312
+ if (!existsSync(configPath))
20313
+ return null;
20314
+ try {
20315
+ const raw = readFileSync(configPath, "utf-8");
20316
+ const config2 = JSON.parse(raw);
20317
+ const servers = config2["mcpServers"];
20318
+ if (!servers?.["memly"])
20319
+ return null;
20320
+ if (servers["memly"]["systemPrompt"]) {
20321
+ return { ide: "Claude Desktop", file: configPath, status: "skipped" };
20322
+ }
20323
+ servers["memly"]["systemPrompt"] = MEMLY_INSTRUCTION_BLOCK;
20324
+ writeFileSync(configPath, JSON.stringify(config2, null, 2));
20325
+ return { ide: "Claude Desktop", file: configPath, status: "updated" };
20326
+ } catch {
20327
+ return null;
20328
+ }
20329
+ }
20330
+ function runInit() {
20331
+ const projectRoot = resolve(process.cwd());
20332
+ const results = [];
20333
+ console.log(`
20334
+ \uD83E\uDDE0 Memly Init — setting up auto-context for your IDEs
20335
+ `);
20336
+ console.log(` Project root: ${projectRoot}
20337
+ `);
20338
+ results.push(writeCopilotInstructions(projectRoot));
20339
+ const hasCursor = existsSync(join(projectRoot, ".cursor")) || existsSync(join(projectRoot, ".cursorrules")) || existsSync(join(homedir(), ".cursor"));
20340
+ if (hasCursor) {
20341
+ results.push(writeCursorRules(projectRoot));
20342
+ }
20343
+ const hasWindsurf = existsSync(join(projectRoot, ".windsurfrules")) || existsSync(join(homedir(), ".codeium"));
20344
+ if (hasWindsurf) {
20345
+ results.push(writeWindsurfRules(projectRoot));
20346
+ }
20347
+ const claudeResult = writeClaudeDesktop();
20348
+ if (claudeResult)
20349
+ results.push(claudeResult);
20350
+ const icons = {
20351
+ created: "✅",
20352
+ updated: "✅",
20353
+ skipped: "⏭️ "
20354
+ };
20355
+ for (const r of results) {
20356
+ const rel = r.file.replace(projectRoot, ".").replace(homedir(), "~");
20357
+ console.log(` ${icons[r.status]} ${r.ide} — ${r.status}: ${rel}`);
20358
+ }
20359
+ if (results.length === 0) {
20360
+ console.log(" ℹ️ No supported IDEs detected automatically.");
20361
+ console.log(` Add the following to your IDE's instruction file manually:
20362
+ `);
20363
+ console.log(MEMLY_INSTRUCTION_BLOCK);
20364
+ }
20365
+ console.log(`
20366
+ ✅ Done! Memly will now load context automatically at the start of every conversation.
20367
+ `);
20368
+ console.log(` Tip: run this again after adding a new IDE to your workflow.
20369
+ `);
20370
+ }
20371
+
20142
20372
  // src/index.ts
20143
20373
  var apiKey = process.env.MEMLY_API_KEY;
20374
+ if (process.argv.includes("--init")) {
20375
+ runInit();
20376
+ process.exit(0);
20377
+ }
20144
20378
  if (!apiKey) {
20145
20379
  console.error("❌ MEMLY_API_KEY environment variable is required.");
20146
20380
  console.error(" Get your key at https://memly.site/dashboard/api-keys");
@@ -20162,6 +20396,7 @@ if (isHttp) {
20162
20396
  const server = new McpServer({ name: "memly", version: "0.1.0" });
20163
20397
  registerTools(server, client);
20164
20398
  registerResources(server, client);
20399
+ registerPrompts(server, client);
20165
20400
  const transport = new WebStandardStreamableHTTPServerTransport({
20166
20401
  sessionIdGenerator: undefined
20167
20402
  });
@@ -20178,6 +20413,7 @@ if (isHttp) {
20178
20413
  const server = new McpServer({ name: "memly", version: "0.1.0" });
20179
20414
  registerTools(server, client);
20180
20415
  registerResources(server, client);
20416
+ registerPrompts(server, client);
20181
20417
  const transport = new StdioServerTransport;
20182
20418
  await server.connect(transport);
20183
20419
  console.error("\uD83E\uDDE0 Memly MCP Server (stdio) connected");
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@memly/mcp-server",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Memly MCP Server — persistent memory for any IDE",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
- "memly-mcp": "dist/index.js"
8
+ "memly-mcp": "dist/index.js",
9
+ "memly": "dist/index.js"
9
10
  },
10
11
  "files": [
11
12
  "dist",