@mnemoverse/mcp-memory-server 0.1.1 → 0.3.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.
- package/dist/index.js +179 -44
- package/dist/index.js.map +1 -1
- package/docs/configs/claude-code-cli.sh +4 -0
- package/docs/configs/claude-desktop.json +14 -0
- package/docs/configs/cursor-deep-link.txt +1 -0
- package/docs/configs/cursor.json +14 -0
- package/docs/configs/vscode-deep-link.txt +1 -0
- package/docs/configs/vscode.json +15 -0
- package/docs/configs/windsurf.json +14 -0
- package/package.json +5 -1
- package/scripts/generate-configs.mjs +306 -0
- package/server.json +28 -0
- package/smithery.yaml +24 -0
- package/src/configs/source.json +28 -0
- package/src/index.ts +227 -56
package/dist/index.js
CHANGED
|
@@ -4,6 +4,10 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
const API_URL = process.env.MNEMOVERSE_API_URL || "https://core.mnemoverse.com/api/v1";
|
|
6
6
|
const API_KEY = process.env.MNEMOVERSE_API_KEY || "";
|
|
7
|
+
// Hard cap on tool result size — required by Claude Connectors Directory
|
|
8
|
+
// (https://support.claude.com/en/articles/12922490-remote-mcp-server-submission-guide).
|
|
9
|
+
// Approximate token count = chars / 4. Cap at 24,000 tokens to leave headroom under the 25K limit.
|
|
10
|
+
const MAX_RESULT_CHARS = 24_000 * 4;
|
|
7
11
|
if (!API_KEY) {
|
|
8
12
|
console.error("Error: MNEMOVERSE_API_KEY environment variable is required.\n" +
|
|
9
13
|
"Get your free key at https://console.mnemoverse.com");
|
|
@@ -24,26 +28,47 @@ async function apiFetch(path, options = {}) {
|
|
|
24
28
|
}
|
|
25
29
|
return res.json();
|
|
26
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Truncate a result string to MAX_RESULT_CHARS, appending a notice if truncated.
|
|
33
|
+
* Required by Claude Connectors Directory submission policy.
|
|
34
|
+
*/
|
|
35
|
+
function capResult(text) {
|
|
36
|
+
if (text.length <= MAX_RESULT_CHARS)
|
|
37
|
+
return text;
|
|
38
|
+
const truncated = text.slice(0, MAX_RESULT_CHARS - 200);
|
|
39
|
+
return (truncated +
|
|
40
|
+
`\n\n[…truncated to fit 25K token limit. Use a more specific query or smaller top_k to see all results.]`);
|
|
41
|
+
}
|
|
27
42
|
// --- Server setup ---
|
|
28
43
|
const server = new McpServer({
|
|
29
44
|
name: "mnemoverse-memory",
|
|
30
|
-
version: "0.
|
|
45
|
+
version: "0.3.0",
|
|
31
46
|
});
|
|
32
47
|
// --- Tool: memory_write ---
|
|
33
|
-
server.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
server.registerTool("memory_write", {
|
|
49
|
+
description: "Store a memory that should persist across sessions — preferences, decisions, lessons learned, project facts, people and roles. Memories you store are also accessible from Claude, ChatGPT, Cursor, VS Code, and any other AI tool the user connects to Mnemoverse — write once, recall everywhere. Call this PROACTIVELY whenever the user states a preference, makes a decision, or you learn something important. Don't wait to be asked — if it's worth remembering, store it now.",
|
|
50
|
+
inputSchema: {
|
|
51
|
+
content: z
|
|
52
|
+
.string()
|
|
53
|
+
.min(1)
|
|
54
|
+
.max(10000)
|
|
55
|
+
.describe("The memory to store — what happened, what was learned"),
|
|
56
|
+
concepts: z
|
|
57
|
+
.array(z.string())
|
|
58
|
+
.optional()
|
|
59
|
+
.describe("Key concepts for linking related memories (e.g. ['deploy', 'friday', 'staging'])"),
|
|
60
|
+
domain: z
|
|
61
|
+
.string()
|
|
62
|
+
.optional()
|
|
63
|
+
.describe("Namespace to organize memories (e.g. 'engineering', 'user:alice', 'project:acme')"),
|
|
64
|
+
},
|
|
65
|
+
annotations: {
|
|
66
|
+
title: "Store Memory",
|
|
67
|
+
readOnlyHint: false,
|
|
68
|
+
destructiveHint: false,
|
|
69
|
+
idempotentHint: false,
|
|
70
|
+
openWorldHint: true,
|
|
71
|
+
},
|
|
47
72
|
}, async ({ content, concepts, domain }) => {
|
|
48
73
|
const result = await apiFetch("/memory/write", {
|
|
49
74
|
method: "POST",
|
|
@@ -74,23 +99,33 @@ server.tool("memory_write", "Store a memory that should persist across sessions
|
|
|
74
99
|
};
|
|
75
100
|
});
|
|
76
101
|
// --- Tool: memory_read ---
|
|
77
|
-
server.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
102
|
+
server.registerTool("memory_read", {
|
|
103
|
+
description: "ALWAYS call this tool first when the user asks a question about preferences, past decisions, project setup, people, or anything that might have been discussed before. This is your long-term memory — it persists across sessions and tools (Claude, ChatGPT, Cursor, VS Code, and any other AI tool the user connects). Search by natural language query. If you have any doubt whether you know something — check memory first.",
|
|
104
|
+
inputSchema: {
|
|
105
|
+
query: z
|
|
106
|
+
.string()
|
|
107
|
+
.min(1)
|
|
108
|
+
.max(5000)
|
|
109
|
+
.describe("Natural language query — what are you looking for?"),
|
|
110
|
+
top_k: z
|
|
111
|
+
.number()
|
|
112
|
+
.int()
|
|
113
|
+
.min(1)
|
|
114
|
+
.max(50)
|
|
115
|
+
.optional()
|
|
116
|
+
.describe("Max results to return (default: 5)"),
|
|
117
|
+
domain: z
|
|
118
|
+
.string()
|
|
119
|
+
.optional()
|
|
120
|
+
.describe("Filter by domain namespace"),
|
|
121
|
+
},
|
|
122
|
+
annotations: {
|
|
123
|
+
title: "Search Memories",
|
|
124
|
+
readOnlyHint: true,
|
|
125
|
+
destructiveHint: false,
|
|
126
|
+
idempotentHint: true,
|
|
127
|
+
openWorldHint: true,
|
|
128
|
+
},
|
|
94
129
|
}, async ({ query, top_k, domain }) => {
|
|
95
130
|
const result = await apiFetch("/memory/read", {
|
|
96
131
|
method: "POST",
|
|
@@ -113,26 +148,37 @@ server.tool("memory_read", "ALWAYS call this tool first when the user asks a que
|
|
|
113
148
|
(item.concepts.length > 0
|
|
114
149
|
? ` (${item.concepts.join(", ")})`
|
|
115
150
|
: ""));
|
|
151
|
+
const text = lines.join("\n\n") + `\n\n(${r.search_time_ms.toFixed(0)}ms)`;
|
|
116
152
|
return {
|
|
117
153
|
content: [
|
|
118
154
|
{
|
|
119
155
|
type: "text",
|
|
120
|
-
text:
|
|
156
|
+
text: capResult(text),
|
|
121
157
|
},
|
|
122
158
|
],
|
|
123
159
|
};
|
|
124
160
|
});
|
|
125
161
|
// --- Tool: memory_feedback ---
|
|
126
|
-
server.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
162
|
+
server.registerTool("memory_feedback", {
|
|
163
|
+
description: "Report whether a retrieved memory was helpful. Positive feedback makes memories easier to find next time across all tools. Negative feedback lets them fade. Call this after using memories from memory_read.",
|
|
164
|
+
inputSchema: {
|
|
165
|
+
atom_ids: z
|
|
166
|
+
.array(z.string())
|
|
167
|
+
.min(1)
|
|
168
|
+
.describe("IDs of memories to give feedback on (from memory_read results)"),
|
|
169
|
+
outcome: z
|
|
170
|
+
.number()
|
|
171
|
+
.min(-1)
|
|
172
|
+
.max(1)
|
|
173
|
+
.describe("How helpful was this? 1.0 = very helpful, 0 = neutral, -1.0 = harmful/wrong"),
|
|
174
|
+
},
|
|
175
|
+
annotations: {
|
|
176
|
+
title: "Rate Memory Helpfulness",
|
|
177
|
+
readOnlyHint: false,
|
|
178
|
+
destructiveHint: false,
|
|
179
|
+
idempotentHint: false,
|
|
180
|
+
openWorldHint: true,
|
|
181
|
+
},
|
|
136
182
|
}, async ({ atom_ids, outcome }) => {
|
|
137
183
|
const result = await apiFetch("/memory/feedback", {
|
|
138
184
|
method: "POST",
|
|
@@ -149,7 +195,17 @@ server.tool("memory_feedback", "Report whether a retrieved memory was helpful. P
|
|
|
149
195
|
};
|
|
150
196
|
});
|
|
151
197
|
// --- Tool: memory_stats ---
|
|
152
|
-
server.
|
|
198
|
+
server.registerTool("memory_stats", {
|
|
199
|
+
description: "Get memory statistics — how many memories are stored, which domains exist, average quality scores. These memories are shared with all AI tools the user has connected to Mnemoverse. Useful for understanding the current state of memory.",
|
|
200
|
+
inputSchema: {},
|
|
201
|
+
annotations: {
|
|
202
|
+
title: "Memory Statistics",
|
|
203
|
+
readOnlyHint: true,
|
|
204
|
+
destructiveHint: false,
|
|
205
|
+
idempotentHint: true,
|
|
206
|
+
openWorldHint: true,
|
|
207
|
+
},
|
|
208
|
+
}, async () => {
|
|
153
209
|
const result = await apiFetch("/memory/stats");
|
|
154
210
|
const r = result;
|
|
155
211
|
const text = [
|
|
@@ -160,6 +216,85 @@ server.tool("memory_stats", "Get memory statistics — how many memories are sto
|
|
|
160
216
|
].join("\n");
|
|
161
217
|
return { content: [{ type: "text", text }] };
|
|
162
218
|
});
|
|
219
|
+
// --- Tool: memory_delete ---
|
|
220
|
+
server.registerTool("memory_delete", {
|
|
221
|
+
description: "Permanently delete a single memory by its atom_id. Use when the user explicitly asks to forget something specific, or when you stored a wrong fact that needs correcting. The deletion is irreversible — the memory is gone for good. For broad cleanup of an entire topic, prefer memory_delete_domain.",
|
|
222
|
+
inputSchema: {
|
|
223
|
+
atom_id: z
|
|
224
|
+
.string()
|
|
225
|
+
.min(1)
|
|
226
|
+
.describe("The atom_id of the memory to delete (from memory_read results — each item has an id)"),
|
|
227
|
+
},
|
|
228
|
+
annotations: {
|
|
229
|
+
title: "Delete a Memory",
|
|
230
|
+
readOnlyHint: false,
|
|
231
|
+
destructiveHint: true,
|
|
232
|
+
idempotentHint: true,
|
|
233
|
+
openWorldHint: true,
|
|
234
|
+
},
|
|
235
|
+
}, async ({ atom_id }) => {
|
|
236
|
+
const result = await apiFetch(`/memory/atoms/${encodeURIComponent(atom_id)}`, {
|
|
237
|
+
method: "DELETE",
|
|
238
|
+
});
|
|
239
|
+
// Core API returns { deleted: <count>, atom_id }. count == 0 means
|
|
240
|
+
// the atom didn't exist (or was already removed). count >= 1 means it was deleted.
|
|
241
|
+
const r = result;
|
|
242
|
+
if (!r.deleted) {
|
|
243
|
+
return {
|
|
244
|
+
content: [
|
|
245
|
+
{
|
|
246
|
+
type: "text",
|
|
247
|
+
text: `No memory found with id ${atom_id}.`,
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
content: [
|
|
254
|
+
{
|
|
255
|
+
type: "text",
|
|
256
|
+
text: `Deleted memory ${atom_id}.`,
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
};
|
|
260
|
+
});
|
|
261
|
+
// --- Tool: memory_delete_domain ---
|
|
262
|
+
server.registerTool("memory_delete_domain", {
|
|
263
|
+
description: "Permanently delete ALL memories in a given domain. Use when the user wants to clean up an entire topic — e.g., 'forget everything about project X' or 'wipe my benchmark experiments'. The deletion is irreversible. List domains first with memory_stats to confirm the exact name. Refuse to call this without an explicit user request — it is much more destructive than memory_delete.",
|
|
264
|
+
inputSchema: {
|
|
265
|
+
domain: z
|
|
266
|
+
.string()
|
|
267
|
+
.min(1)
|
|
268
|
+
.max(200)
|
|
269
|
+
.describe("The domain namespace to wipe (e.g., 'project:old', 'experiments-2025'). Must match exactly."),
|
|
270
|
+
confirm: z
|
|
271
|
+
.literal(true)
|
|
272
|
+
.describe("Must be exactly true to proceed. Acts as a safety interlock against accidental invocation."),
|
|
273
|
+
},
|
|
274
|
+
annotations: {
|
|
275
|
+
title: "Delete an Entire Memory Domain",
|
|
276
|
+
readOnlyHint: false,
|
|
277
|
+
destructiveHint: true,
|
|
278
|
+
idempotentHint: true,
|
|
279
|
+
openWorldHint: true,
|
|
280
|
+
},
|
|
281
|
+
}, async ({ domain, confirm }) => {
|
|
282
|
+
if (confirm !== true) {
|
|
283
|
+
throw new Error("memory_delete_domain requires confirm=true as a safety interlock.");
|
|
284
|
+
}
|
|
285
|
+
const result = await apiFetch(`/memory/domain/${encodeURIComponent(domain)}`, {
|
|
286
|
+
method: "DELETE",
|
|
287
|
+
});
|
|
288
|
+
const r = result;
|
|
289
|
+
return {
|
|
290
|
+
content: [
|
|
291
|
+
{
|
|
292
|
+
type: "text",
|
|
293
|
+
text: `Deleted ${r.deleted} ${r.deleted === 1 ? "memory" : "memories"} from domain "${r.domain}".`,
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
};
|
|
297
|
+
});
|
|
163
298
|
// --- Start ---
|
|
164
299
|
async function main() {
|
|
165
300
|
const transport = new StdioServerTransport();
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,OAAO,GACX,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,oCAAoC,CAAC;AACzE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE,CAAC;AAErD,IAAI,CAAC,OAAO,EAAE,CAAC;IACb,OAAO,CAAC,KAAK,CACX,+DAA+D;QAC7D,qDAAqD,CACxD,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,KAAK,UAAU,QAAQ,CACrB,IAAY,EACZ,UAAuB,EAAE;IAEzB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,GAAG,IAAI,EAAE,EAAE;QAC3C,GAAG,OAAO;QACV,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,WAAW,EAAE,OAAO;YACpB,GAAG,CAAE,OAAO,CAAC,OAAkC,IAAI,EAAE,CAAC;SACvD;KACF,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC;AAED,uBAAuB;AAEvB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,mBAAmB;IACzB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,6BAA6B;AAE7B,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cursor://anysphere.cursor-deeplink/mcp/install?name=mnemoverse&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBtbmVtb3ZlcnNlL21jcC1tZW1vcnktc2VydmVyIl0sImVudiI6eyJNTkVNT1ZFUlNFX0FQSV9LRVkiOiJta19saXZlX1lPVVJfS0VZIn19
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
vscode:mcp/install?%7B%22name%22%3A%22mnemoverse%22%2C%22type%22%3A%22stdio%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40mnemoverse%2Fmcp-memory-server%22%5D%2C%22env%22%3A%7B%22MNEMOVERSE_API_KEY%22%3A%22mk_live_YOUR_KEY%22%7D%7D
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mnemoverse/mcp-memory-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "MCP server for Mnemoverse Memory API — persistent AI memory across Claude Code, Cursor, VS Code, and any MCP client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,8 +12,12 @@
|
|
|
12
12
|
"build": "tsc",
|
|
13
13
|
"dev": "tsc --watch",
|
|
14
14
|
"start": "node dist/index.js",
|
|
15
|
+
"generate:configs": "node scripts/generate-configs.mjs",
|
|
16
|
+
"verify:configs": "node scripts/generate-configs.mjs --check",
|
|
17
|
+
"prebuild": "npm run generate:configs",
|
|
15
18
|
"prepublishOnly": "npm run build"
|
|
16
19
|
},
|
|
20
|
+
"mcpName": "io.github.mnemoverse/mcp-memory-server",
|
|
17
21
|
"keywords": [
|
|
18
22
|
"mcp",
|
|
19
23
|
"memory",
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* generate-configs.mjs
|
|
4
|
+
*
|
|
5
|
+
* Single source of truth → all distribution channel configs.
|
|
6
|
+
*
|
|
7
|
+
* Reads: src/configs/source.json
|
|
8
|
+
* Writes: docs/configs/*, smithery.yaml, server.json
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* node scripts/generate-configs.mjs # generate
|
|
12
|
+
* node scripts/generate-configs.mjs --check # CI: regenerate + diff (fail if changed)
|
|
13
|
+
*
|
|
14
|
+
* When you add a new distribution channel, add a new generator function below.
|
|
15
|
+
* Never edit generated files by hand — they will be overwritten.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
19
|
+
import { dirname, resolve } from "node:path";
|
|
20
|
+
import { fileURLToPath } from "node:url";
|
|
21
|
+
import { execSync } from "node:child_process";
|
|
22
|
+
|
|
23
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
24
|
+
const __dirname = dirname(__filename);
|
|
25
|
+
const ROOT = resolve(__dirname, "..");
|
|
26
|
+
|
|
27
|
+
// ─── Load source ─────────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
const SOURCE_PATH = resolve(ROOT, "src/configs/source.json");
|
|
30
|
+
const source = JSON.parse(readFileSync(SOURCE_PATH, "utf8"));
|
|
31
|
+
|
|
32
|
+
const PACKAGE_VERSION = JSON.parse(
|
|
33
|
+
readFileSync(resolve(ROOT, "package.json"), "utf8"),
|
|
34
|
+
).version;
|
|
35
|
+
|
|
36
|
+
// Helper: extract { KEY: "value" } from source.env (which has nested {value, description, ...})
|
|
37
|
+
function envValues(envObj) {
|
|
38
|
+
const result = {};
|
|
39
|
+
for (const [key, meta] of Object.entries(envObj)) {
|
|
40
|
+
result[key] = meta.value;
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const ENV_VALUES = envValues(source.env);
|
|
46
|
+
|
|
47
|
+
// ─── Generators ──────────────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Cursor / Claude Desktop / Windsurf format (mcpServers key, stdio).
|
|
51
|
+
*/
|
|
52
|
+
function genMcpServersFormat() {
|
|
53
|
+
return {
|
|
54
|
+
mcpServers: {
|
|
55
|
+
[source.name]: {
|
|
56
|
+
command: source.command,
|
|
57
|
+
args: source.args,
|
|
58
|
+
env: ENV_VALUES,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* VS Code (Copilot Chat) format — uses `servers` key (not `mcpServers`).
|
|
66
|
+
*/
|
|
67
|
+
function genVscodeFormat() {
|
|
68
|
+
return {
|
|
69
|
+
servers: {
|
|
70
|
+
[source.name]: {
|
|
71
|
+
type: source.type,
|
|
72
|
+
command: source.command,
|
|
73
|
+
args: source.args,
|
|
74
|
+
env: ENV_VALUES,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Cursor deep link.
|
|
82
|
+
*
|
|
83
|
+
* Format: cursor://anysphere.cursor-deeplink/mcp/install?name=NAME&config=BASE64
|
|
84
|
+
* The base64 payload is the inner server object (NOT wrapped in mcpServers).
|
|
85
|
+
*/
|
|
86
|
+
function genCursorDeepLink() {
|
|
87
|
+
const inner = {
|
|
88
|
+
command: source.command,
|
|
89
|
+
args: source.args,
|
|
90
|
+
env: ENV_VALUES,
|
|
91
|
+
};
|
|
92
|
+
const config = Buffer.from(JSON.stringify(inner)).toString("base64");
|
|
93
|
+
return `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent(
|
|
94
|
+
source.name,
|
|
95
|
+
)}&config=${config}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* VS Code deep link.
|
|
100
|
+
*
|
|
101
|
+
* Format: vscode:mcp/install?{URL_ENCODED_JSON}
|
|
102
|
+
* The JSON contains name + the server object (not wrapped in `servers`).
|
|
103
|
+
*/
|
|
104
|
+
function genVscodeDeepLink() {
|
|
105
|
+
const inner = {
|
|
106
|
+
name: source.name,
|
|
107
|
+
type: source.type,
|
|
108
|
+
command: source.command,
|
|
109
|
+
args: source.args,
|
|
110
|
+
env: ENV_VALUES,
|
|
111
|
+
};
|
|
112
|
+
return `vscode:mcp/install?${encodeURIComponent(JSON.stringify(inner))}`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Claude Code CLI command.
|
|
117
|
+
*
|
|
118
|
+
* Format: claude mcp add NAME -e KEY=VAL ... -- command args...
|
|
119
|
+
*/
|
|
120
|
+
function genClaudeCodeCli() {
|
|
121
|
+
const envFlags = Object.entries(ENV_VALUES)
|
|
122
|
+
.map(([k, v]) => ` -e ${k}=${v}`)
|
|
123
|
+
.join(" \\\n");
|
|
124
|
+
return `claude mcp add ${source.name} \\\n${envFlags} \\\n -- ${source.command} ${source.args.join(" ")}\n`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Smithery.ai config — custom yaml with configSchema + commandFunction.
|
|
129
|
+
*
|
|
130
|
+
* Smithery shows configSchema fields to user as form, then calls commandFunction(config)
|
|
131
|
+
* to build the launch command.
|
|
132
|
+
*/
|
|
133
|
+
function genSmitheryYaml() {
|
|
134
|
+
// Build configSchema properties from source.env
|
|
135
|
+
const schemaProperties = {};
|
|
136
|
+
const required = [];
|
|
137
|
+
const camelCaseEnvMap = {}; // Smithery uses camelCase keys, then we map back to ENV_VAR
|
|
138
|
+
|
|
139
|
+
for (const [envKey, meta] of Object.entries(source.env)) {
|
|
140
|
+
// Convert MNEMOVERSE_API_KEY → mnemoverseApiKey
|
|
141
|
+
const camelKey = envKey
|
|
142
|
+
.toLowerCase()
|
|
143
|
+
.split("_")
|
|
144
|
+
.map((part, i) =>
|
|
145
|
+
i === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1),
|
|
146
|
+
)
|
|
147
|
+
.join("");
|
|
148
|
+
|
|
149
|
+
camelCaseEnvMap[camelKey] = envKey;
|
|
150
|
+
schemaProperties[camelKey] = {
|
|
151
|
+
type: "string",
|
|
152
|
+
description: meta.description,
|
|
153
|
+
};
|
|
154
|
+
if (meta.required) required.push(camelKey);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Build commandFunction body
|
|
158
|
+
const envAssignments = Object.entries(camelCaseEnvMap)
|
|
159
|
+
.map(([camel, envVar]) => ` ${envVar}: config.${camel},`)
|
|
160
|
+
.join("\n");
|
|
161
|
+
|
|
162
|
+
const yaml = `# Smithery configuration — AUTO-GENERATED from src/configs/source.json
|
|
163
|
+
# Do not edit by hand. Run \`npm run generate:configs\` to regenerate.
|
|
164
|
+
# Docs: https://smithery.ai/docs/build/project-config/smithery.yaml
|
|
165
|
+
|
|
166
|
+
startCommand:
|
|
167
|
+
type: ${source.type}
|
|
168
|
+
configSchema:
|
|
169
|
+
type: object
|
|
170
|
+
required:
|
|
171
|
+
${required.map((k) => ` - ${k}`).join("\n")}
|
|
172
|
+
properties:
|
|
173
|
+
${Object.entries(schemaProperties)
|
|
174
|
+
.map(
|
|
175
|
+
([key, prop]) => ` ${key}:
|
|
176
|
+
type: ${prop.type}
|
|
177
|
+
description: |
|
|
178
|
+
${prop.description}`,
|
|
179
|
+
)
|
|
180
|
+
.join("\n")}
|
|
181
|
+
commandFunction:
|
|
182
|
+
|-
|
|
183
|
+
(config) => ({
|
|
184
|
+
command: '${source.command}',
|
|
185
|
+
args: ${JSON.stringify(source.args)},
|
|
186
|
+
env: {
|
|
187
|
+
${envAssignments}
|
|
188
|
+
}
|
|
189
|
+
})
|
|
190
|
+
`;
|
|
191
|
+
return yaml;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Official MCP Registry server.json.
|
|
196
|
+
*
|
|
197
|
+
* https://registry.modelcontextprotocol.io/
|
|
198
|
+
* Schema: https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json
|
|
199
|
+
*/
|
|
200
|
+
function genServerJson() {
|
|
201
|
+
return {
|
|
202
|
+
$schema:
|
|
203
|
+
"https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
|
204
|
+
name: "io.github.mnemoverse/mcp-memory-server",
|
|
205
|
+
description: source.description,
|
|
206
|
+
repository: {
|
|
207
|
+
url: source.metadata.repository,
|
|
208
|
+
source: "github",
|
|
209
|
+
},
|
|
210
|
+
version: PACKAGE_VERSION,
|
|
211
|
+
packages: [
|
|
212
|
+
{
|
|
213
|
+
registryName: source.package.registry,
|
|
214
|
+
name: source.package.name,
|
|
215
|
+
version: PACKAGE_VERSION,
|
|
216
|
+
runtimeArguments: source.args.slice(1), // skip "-y"
|
|
217
|
+
environmentVariables: Object.entries(source.env).map(([key, meta]) => ({
|
|
218
|
+
name: key,
|
|
219
|
+
description: meta.description,
|
|
220
|
+
isRequired: meta.required ?? false,
|
|
221
|
+
isSecret: meta.secret ?? false,
|
|
222
|
+
})),
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ─── Output ──────────────────────────────────────────────────────────────────
|
|
229
|
+
|
|
230
|
+
const OUTPUTS = [
|
|
231
|
+
{
|
|
232
|
+
path: "docs/configs/cursor.json",
|
|
233
|
+
content: JSON.stringify(genMcpServersFormat(), null, 2) + "\n",
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
path: "docs/configs/claude-desktop.json",
|
|
237
|
+
content: JSON.stringify(genMcpServersFormat(), null, 2) + "\n",
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
path: "docs/configs/windsurf.json",
|
|
241
|
+
content: JSON.stringify(genMcpServersFormat(), null, 2) + "\n",
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
path: "docs/configs/vscode.json",
|
|
245
|
+
content: JSON.stringify(genVscodeFormat(), null, 2) + "\n",
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
path: "docs/configs/cursor-deep-link.txt",
|
|
249
|
+
content: genCursorDeepLink() + "\n",
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
path: "docs/configs/vscode-deep-link.txt",
|
|
253
|
+
content: genVscodeDeepLink() + "\n",
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
path: "docs/configs/claude-code-cli.sh",
|
|
257
|
+
content: "#!/usr/bin/env bash\n" + genClaudeCodeCli(),
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
path: "smithery.yaml",
|
|
261
|
+
content: genSmitheryYaml(),
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
path: "server.json",
|
|
265
|
+
content: JSON.stringify(genServerJson(), null, 2) + "\n",
|
|
266
|
+
},
|
|
267
|
+
];
|
|
268
|
+
|
|
269
|
+
// ─── Write files ─────────────────────────────────────────────────────────────
|
|
270
|
+
|
|
271
|
+
const checkMode = process.argv.includes("--check");
|
|
272
|
+
|
|
273
|
+
let written = 0;
|
|
274
|
+
let unchanged = 0;
|
|
275
|
+
|
|
276
|
+
for (const { path, content } of OUTPUTS) {
|
|
277
|
+
const fullPath = resolve(ROOT, path);
|
|
278
|
+
mkdirSync(dirname(fullPath), { recursive: true });
|
|
279
|
+
|
|
280
|
+
const existing = existsSync(fullPath) ? readFileSync(fullPath, "utf8") : "";
|
|
281
|
+
if (existing === content) {
|
|
282
|
+
unchanged++;
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (checkMode) {
|
|
287
|
+
console.error(`✗ Drift detected: ${path}`);
|
|
288
|
+
console.error(" Expected (regenerated):");
|
|
289
|
+
console.error(" " + content.slice(0, 200).split("\n").join("\n "));
|
|
290
|
+
console.error(" Got (committed):");
|
|
291
|
+
console.error(" " + existing.slice(0, 200).split("\n").join("\n "));
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
writeFileSync(fullPath, content, "utf8");
|
|
296
|
+
console.log(`✓ Generated ${path}`);
|
|
297
|
+
written++;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (checkMode) {
|
|
301
|
+
console.log(`✓ All ${OUTPUTS.length} configs in sync with source.json`);
|
|
302
|
+
} else {
|
|
303
|
+
console.log(
|
|
304
|
+
`\nDone: ${written} written, ${unchanged} unchanged (${OUTPUTS.length} total)`,
|
|
305
|
+
);
|
|
306
|
+
}
|
package/server.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
|
3
|
+
"name": "io.github.mnemoverse/mcp-memory-server",
|
|
4
|
+
"description": "Persistent memory for AI agents — write once, recall anywhere across all your AI tools",
|
|
5
|
+
"repository": {
|
|
6
|
+
"url": "https://github.com/mnemoverse/mcp-memory-server",
|
|
7
|
+
"source": "github"
|
|
8
|
+
},
|
|
9
|
+
"version": "0.3.0",
|
|
10
|
+
"packages": [
|
|
11
|
+
{
|
|
12
|
+
"registryName": "npm",
|
|
13
|
+
"name": "@mnemoverse/mcp-memory-server",
|
|
14
|
+
"version": "0.3.0",
|
|
15
|
+
"runtimeArguments": [
|
|
16
|
+
"@mnemoverse/mcp-memory-server"
|
|
17
|
+
],
|
|
18
|
+
"environmentVariables": [
|
|
19
|
+
{
|
|
20
|
+
"name": "MNEMOVERSE_API_KEY",
|
|
21
|
+
"description": "Your Mnemoverse API key (starts with mk_live_). Get one free at https://console.mnemoverse.com",
|
|
22
|
+
"isRequired": true,
|
|
23
|
+
"isSecret": true
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
package/smithery.yaml
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Smithery configuration — AUTO-GENERATED from src/configs/source.json
|
|
2
|
+
# Do not edit by hand. Run `npm run generate:configs` to regenerate.
|
|
3
|
+
# Docs: https://smithery.ai/docs/build/project-config/smithery.yaml
|
|
4
|
+
|
|
5
|
+
startCommand:
|
|
6
|
+
type: stdio
|
|
7
|
+
configSchema:
|
|
8
|
+
type: object
|
|
9
|
+
required:
|
|
10
|
+
- mnemoverseApiKey
|
|
11
|
+
properties:
|
|
12
|
+
mnemoverseApiKey:
|
|
13
|
+
type: string
|
|
14
|
+
description: |
|
|
15
|
+
Your Mnemoverse API key (starts with mk_live_). Get one free at https://console.mnemoverse.com
|
|
16
|
+
commandFunction:
|
|
17
|
+
|-
|
|
18
|
+
(config) => ({
|
|
19
|
+
command: 'npx',
|
|
20
|
+
args: ["-y","@mnemoverse/mcp-memory-server"],
|
|
21
|
+
env: {
|
|
22
|
+
MNEMOVERSE_API_KEY: config.mnemoverseApiKey,
|
|
23
|
+
}
|
|
24
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "SINGLE SOURCE OF TRUTH for all distribution channel configs. Edit this file → run `npm run generate:configs` → all 9+ artifacts regenerate. CI verifies committed artifacts match source.",
|
|
3
|
+
"name": "mnemoverse",
|
|
4
|
+
"displayName": "Mnemoverse Memory",
|
|
5
|
+
"description": "Persistent memory for AI agents — write once, recall anywhere across all your AI tools",
|
|
6
|
+
"type": "stdio",
|
|
7
|
+
"command": "npx",
|
|
8
|
+
"args": ["-y", "@mnemoverse/mcp-memory-server"],
|
|
9
|
+
"env": {
|
|
10
|
+
"MNEMOVERSE_API_KEY": {
|
|
11
|
+
"value": "mk_live_YOUR_KEY",
|
|
12
|
+
"description": "Your Mnemoverse API key (starts with mk_live_). Get one free at https://console.mnemoverse.com",
|
|
13
|
+
"required": true,
|
|
14
|
+
"secret": true
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"package": {
|
|
18
|
+
"registry": "npm",
|
|
19
|
+
"name": "@mnemoverse/mcp-memory-server"
|
|
20
|
+
},
|
|
21
|
+
"metadata": {
|
|
22
|
+
"homepage": "https://mnemoverse.com/docs/api/mcp-server",
|
|
23
|
+
"repository": "https://github.com/mnemoverse/mcp-memory-server",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"author": "Mnemoverse",
|
|
26
|
+
"tags": ["memory", "persistent", "ai-agents", "cross-tool", "oauth"]
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -8,17 +8,22 @@ const API_URL =
|
|
|
8
8
|
process.env.MNEMOVERSE_API_URL || "https://core.mnemoverse.com/api/v1";
|
|
9
9
|
const API_KEY = process.env.MNEMOVERSE_API_KEY || "";
|
|
10
10
|
|
|
11
|
+
// Hard cap on tool result size — required by Claude Connectors Directory
|
|
12
|
+
// (https://support.claude.com/en/articles/12922490-remote-mcp-server-submission-guide).
|
|
13
|
+
// Approximate token count = chars / 4. Cap at 24,000 tokens to leave headroom under the 25K limit.
|
|
14
|
+
const MAX_RESULT_CHARS = 24_000 * 4;
|
|
15
|
+
|
|
11
16
|
if (!API_KEY) {
|
|
12
17
|
console.error(
|
|
13
18
|
"Error: MNEMOVERSE_API_KEY environment variable is required.\n" +
|
|
14
|
-
"Get your free key at https://console.mnemoverse.com"
|
|
19
|
+
"Get your free key at https://console.mnemoverse.com",
|
|
15
20
|
);
|
|
16
21
|
process.exit(1);
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
async function apiFetch(
|
|
20
25
|
path: string,
|
|
21
|
-
options: RequestInit = {}
|
|
26
|
+
options: RequestInit = {},
|
|
22
27
|
): Promise<unknown> {
|
|
23
28
|
const res = await fetch(`${API_URL}${path}`, {
|
|
24
29
|
...options,
|
|
@@ -37,32 +42,59 @@ async function apiFetch(
|
|
|
37
42
|
return res.json();
|
|
38
43
|
}
|
|
39
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Truncate a result string to MAX_RESULT_CHARS, appending a notice if truncated.
|
|
47
|
+
* Required by Claude Connectors Directory submission policy.
|
|
48
|
+
*/
|
|
49
|
+
function capResult(text: string): string {
|
|
50
|
+
if (text.length <= MAX_RESULT_CHARS) return text;
|
|
51
|
+
const truncated = text.slice(0, MAX_RESULT_CHARS - 200);
|
|
52
|
+
return (
|
|
53
|
+
truncated +
|
|
54
|
+
`\n\n[…truncated to fit 25K token limit. Use a more specific query or smaller top_k to see all results.]`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
40
58
|
// --- Server setup ---
|
|
41
59
|
|
|
42
60
|
const server = new McpServer({
|
|
43
61
|
name: "mnemoverse-memory",
|
|
44
|
-
version: "0.
|
|
62
|
+
version: "0.3.0",
|
|
45
63
|
});
|
|
46
64
|
|
|
47
65
|
// --- Tool: memory_write ---
|
|
48
66
|
|
|
49
|
-
server.
|
|
67
|
+
server.registerTool(
|
|
50
68
|
"memory_write",
|
|
51
|
-
"Store a memory that should persist across sessions — preferences, decisions, lessons learned, project facts, people and roles. Call this PROACTIVELY whenever the user states a preference, makes a decision, or you learn something important. Don't wait to be asked — if it's worth remembering, store it now.",
|
|
52
69
|
{
|
|
53
|
-
|
|
54
|
-
.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
70
|
+
description:
|
|
71
|
+
"Store a memory that should persist across sessions — preferences, decisions, lessons learned, project facts, people and roles. Memories you store are also accessible from Claude, ChatGPT, Cursor, VS Code, and any other AI tool the user connects to Mnemoverse — write once, recall everywhere. Call this PROACTIVELY whenever the user states a preference, makes a decision, or you learn something important. Don't wait to be asked — if it's worth remembering, store it now.",
|
|
72
|
+
inputSchema: {
|
|
73
|
+
content: z
|
|
74
|
+
.string()
|
|
75
|
+
.min(1)
|
|
76
|
+
.max(10000)
|
|
77
|
+
.describe("The memory to store — what happened, what was learned"),
|
|
78
|
+
concepts: z
|
|
79
|
+
.array(z.string())
|
|
80
|
+
.optional()
|
|
81
|
+
.describe(
|
|
82
|
+
"Key concepts for linking related memories (e.g. ['deploy', 'friday', 'staging'])",
|
|
83
|
+
),
|
|
84
|
+
domain: z
|
|
85
|
+
.string()
|
|
86
|
+
.optional()
|
|
87
|
+
.describe(
|
|
88
|
+
"Namespace to organize memories (e.g. 'engineering', 'user:alice', 'project:acme')",
|
|
89
|
+
),
|
|
90
|
+
},
|
|
91
|
+
annotations: {
|
|
92
|
+
title: "Store Memory",
|
|
93
|
+
readOnlyHint: false,
|
|
94
|
+
destructiveHint: false,
|
|
95
|
+
idempotentHint: false,
|
|
96
|
+
openWorldHint: true,
|
|
97
|
+
},
|
|
66
98
|
},
|
|
67
99
|
async ({ content, concepts, domain }) => {
|
|
68
100
|
const result = await apiFetch("/memory/write", {
|
|
@@ -99,31 +131,41 @@ server.tool(
|
|
|
99
131
|
},
|
|
100
132
|
],
|
|
101
133
|
};
|
|
102
|
-
}
|
|
134
|
+
},
|
|
103
135
|
);
|
|
104
136
|
|
|
105
137
|
// --- Tool: memory_read ---
|
|
106
138
|
|
|
107
|
-
server.
|
|
139
|
+
server.registerTool(
|
|
108
140
|
"memory_read",
|
|
109
|
-
"ALWAYS call this tool first when the user asks a question about preferences, past decisions, project setup, people, or anything that might have been discussed before. This is your long-term memory — it persists across sessions and tools. Search by natural language query. If you have any doubt whether you know something — check memory first.",
|
|
110
141
|
{
|
|
111
|
-
|
|
112
|
-
.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
142
|
+
description:
|
|
143
|
+
"ALWAYS call this tool first when the user asks a question about preferences, past decisions, project setup, people, or anything that might have been discussed before. This is your long-term memory — it persists across sessions and tools (Claude, ChatGPT, Cursor, VS Code, and any other AI tool the user connects). Search by natural language query. If you have any doubt whether you know something — check memory first.",
|
|
144
|
+
inputSchema: {
|
|
145
|
+
query: z
|
|
146
|
+
.string()
|
|
147
|
+
.min(1)
|
|
148
|
+
.max(5000)
|
|
149
|
+
.describe("Natural language query — what are you looking for?"),
|
|
150
|
+
top_k: z
|
|
151
|
+
.number()
|
|
152
|
+
.int()
|
|
153
|
+
.min(1)
|
|
154
|
+
.max(50)
|
|
155
|
+
.optional()
|
|
156
|
+
.describe("Max results to return (default: 5)"),
|
|
157
|
+
domain: z
|
|
158
|
+
.string()
|
|
159
|
+
.optional()
|
|
160
|
+
.describe("Filter by domain namespace"),
|
|
161
|
+
},
|
|
162
|
+
annotations: {
|
|
163
|
+
title: "Search Memories",
|
|
164
|
+
readOnlyHint: true,
|
|
165
|
+
destructiveHint: false,
|
|
166
|
+
idempotentHint: true,
|
|
167
|
+
openWorldHint: true,
|
|
168
|
+
},
|
|
127
169
|
},
|
|
128
170
|
async ({ query, top_k, domain }) => {
|
|
129
171
|
const result = await apiFetch("/memory/read", {
|
|
@@ -159,35 +201,47 @@ server.tool(
|
|
|
159
201
|
`${i + 1}. [${(item.relevance * 100).toFixed(0)}%] ${item.content}` +
|
|
160
202
|
(item.concepts.length > 0
|
|
161
203
|
? ` (${item.concepts.join(", ")})`
|
|
162
|
-
: "")
|
|
204
|
+
: ""),
|
|
163
205
|
);
|
|
164
206
|
|
|
207
|
+
const text = lines.join("\n\n") + `\n\n(${r.search_time_ms.toFixed(0)}ms)`;
|
|
208
|
+
|
|
165
209
|
return {
|
|
166
210
|
content: [
|
|
167
211
|
{
|
|
168
212
|
type: "text" as const,
|
|
169
|
-
text:
|
|
213
|
+
text: capResult(text),
|
|
170
214
|
},
|
|
171
215
|
],
|
|
172
216
|
};
|
|
173
|
-
}
|
|
217
|
+
},
|
|
174
218
|
);
|
|
175
219
|
|
|
176
220
|
// --- Tool: memory_feedback ---
|
|
177
221
|
|
|
178
|
-
server.
|
|
222
|
+
server.registerTool(
|
|
179
223
|
"memory_feedback",
|
|
180
|
-
"Report whether a retrieved memory was helpful. Positive feedback makes memories easier to find next time. Negative feedback lets them fade. Call this after using memories from memory_read.",
|
|
181
224
|
{
|
|
182
|
-
|
|
183
|
-
.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
225
|
+
description:
|
|
226
|
+
"Report whether a retrieved memory was helpful. Positive feedback makes memories easier to find next time across all tools. Negative feedback lets them fade. Call this after using memories from memory_read.",
|
|
227
|
+
inputSchema: {
|
|
228
|
+
atom_ids: z
|
|
229
|
+
.array(z.string())
|
|
230
|
+
.min(1)
|
|
231
|
+
.describe("IDs of memories to give feedback on (from memory_read results)"),
|
|
232
|
+
outcome: z
|
|
233
|
+
.number()
|
|
234
|
+
.min(-1)
|
|
235
|
+
.max(1)
|
|
236
|
+
.describe("How helpful was this? 1.0 = very helpful, 0 = neutral, -1.0 = harmful/wrong"),
|
|
237
|
+
},
|
|
238
|
+
annotations: {
|
|
239
|
+
title: "Rate Memory Helpfulness",
|
|
240
|
+
readOnlyHint: false,
|
|
241
|
+
destructiveHint: false,
|
|
242
|
+
idempotentHint: false,
|
|
243
|
+
openWorldHint: true,
|
|
244
|
+
},
|
|
191
245
|
},
|
|
192
246
|
async ({ atom_ids, outcome }) => {
|
|
193
247
|
const result = await apiFetch("/memory/feedback", {
|
|
@@ -205,15 +259,25 @@ server.tool(
|
|
|
205
259
|
},
|
|
206
260
|
],
|
|
207
261
|
};
|
|
208
|
-
}
|
|
262
|
+
},
|
|
209
263
|
);
|
|
210
264
|
|
|
211
265
|
// --- Tool: memory_stats ---
|
|
212
266
|
|
|
213
|
-
server.
|
|
267
|
+
server.registerTool(
|
|
214
268
|
"memory_stats",
|
|
215
|
-
|
|
216
|
-
|
|
269
|
+
{
|
|
270
|
+
description:
|
|
271
|
+
"Get memory statistics — how many memories are stored, which domains exist, average quality scores. These memories are shared with all AI tools the user has connected to Mnemoverse. Useful for understanding the current state of memory.",
|
|
272
|
+
inputSchema: {},
|
|
273
|
+
annotations: {
|
|
274
|
+
title: "Memory Statistics",
|
|
275
|
+
readOnlyHint: true,
|
|
276
|
+
destructiveHint: false,
|
|
277
|
+
idempotentHint: true,
|
|
278
|
+
openWorldHint: true,
|
|
279
|
+
},
|
|
280
|
+
},
|
|
217
281
|
async () => {
|
|
218
282
|
const result = await apiFetch("/memory/stats");
|
|
219
283
|
|
|
@@ -235,7 +299,114 @@ server.tool(
|
|
|
235
299
|
].join("\n");
|
|
236
300
|
|
|
237
301
|
return { content: [{ type: "text" as const, text }] };
|
|
238
|
-
}
|
|
302
|
+
},
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
// --- Tool: memory_delete ---
|
|
306
|
+
|
|
307
|
+
server.registerTool(
|
|
308
|
+
"memory_delete",
|
|
309
|
+
{
|
|
310
|
+
description:
|
|
311
|
+
"Permanently delete a single memory by its atom_id. Use when the user explicitly asks to forget something specific, or when you stored a wrong fact that needs correcting. The deletion is irreversible — the memory is gone for good. For broad cleanup of an entire topic, prefer memory_delete_domain.",
|
|
312
|
+
inputSchema: {
|
|
313
|
+
atom_id: z
|
|
314
|
+
.string()
|
|
315
|
+
.min(1)
|
|
316
|
+
.describe(
|
|
317
|
+
"The atom_id of the memory to delete (from memory_read results — each item has an id)",
|
|
318
|
+
),
|
|
319
|
+
},
|
|
320
|
+
annotations: {
|
|
321
|
+
title: "Delete a Memory",
|
|
322
|
+
readOnlyHint: false,
|
|
323
|
+
destructiveHint: true,
|
|
324
|
+
idempotentHint: true,
|
|
325
|
+
openWorldHint: true,
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
async ({ atom_id }) => {
|
|
329
|
+
const result = await apiFetch(`/memory/atoms/${encodeURIComponent(atom_id)}`, {
|
|
330
|
+
method: "DELETE",
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// Core API returns { deleted: <count>, atom_id }. count == 0 means
|
|
334
|
+
// the atom didn't exist (or was already removed). count >= 1 means it was deleted.
|
|
335
|
+
const r = result as { deleted: number; atom_id?: string };
|
|
336
|
+
|
|
337
|
+
if (!r.deleted) {
|
|
338
|
+
return {
|
|
339
|
+
content: [
|
|
340
|
+
{
|
|
341
|
+
type: "text" as const,
|
|
342
|
+
text: `No memory found with id ${atom_id}.`,
|
|
343
|
+
},
|
|
344
|
+
],
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
content: [
|
|
350
|
+
{
|
|
351
|
+
type: "text" as const,
|
|
352
|
+
text: `Deleted memory ${atom_id}.`,
|
|
353
|
+
},
|
|
354
|
+
],
|
|
355
|
+
};
|
|
356
|
+
},
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
// --- Tool: memory_delete_domain ---
|
|
360
|
+
|
|
361
|
+
server.registerTool(
|
|
362
|
+
"memory_delete_domain",
|
|
363
|
+
{
|
|
364
|
+
description:
|
|
365
|
+
"Permanently delete ALL memories in a given domain. Use when the user wants to clean up an entire topic — e.g., 'forget everything about project X' or 'wipe my benchmark experiments'. The deletion is irreversible. List domains first with memory_stats to confirm the exact name. Refuse to call this without an explicit user request — it is much more destructive than memory_delete.",
|
|
366
|
+
inputSchema: {
|
|
367
|
+
domain: z
|
|
368
|
+
.string()
|
|
369
|
+
.min(1)
|
|
370
|
+
.max(200)
|
|
371
|
+
.describe(
|
|
372
|
+
"The domain namespace to wipe (e.g., 'project:old', 'experiments-2025'). Must match exactly.",
|
|
373
|
+
),
|
|
374
|
+
confirm: z
|
|
375
|
+
.literal(true)
|
|
376
|
+
.describe(
|
|
377
|
+
"Must be exactly true to proceed. Acts as a safety interlock against accidental invocation.",
|
|
378
|
+
),
|
|
379
|
+
},
|
|
380
|
+
annotations: {
|
|
381
|
+
title: "Delete an Entire Memory Domain",
|
|
382
|
+
readOnlyHint: false,
|
|
383
|
+
destructiveHint: true,
|
|
384
|
+
idempotentHint: true,
|
|
385
|
+
openWorldHint: true,
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
async ({ domain, confirm }) => {
|
|
389
|
+
if (confirm !== true) {
|
|
390
|
+
throw new Error(
|
|
391
|
+
"memory_delete_domain requires confirm=true as a safety interlock.",
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const result = await apiFetch(`/memory/domain/${encodeURIComponent(domain)}`, {
|
|
396
|
+
method: "DELETE",
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const r = result as { deleted: number; domain: string };
|
|
400
|
+
|
|
401
|
+
return {
|
|
402
|
+
content: [
|
|
403
|
+
{
|
|
404
|
+
type: "text" as const,
|
|
405
|
+
text: `Deleted ${r.deleted} ${r.deleted === 1 ? "memory" : "memories"} from domain "${r.domain}".`,
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
};
|
|
409
|
+
},
|
|
239
410
|
);
|
|
240
411
|
|
|
241
412
|
// --- Start ---
|