@mnemoverse/mcp-memory-server 0.3.1 → 0.3.3
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/LICENSE +21 -0
- package/README.md +4 -1
- package/dist/index.js +82 -40
- package/dist/index.js.map +1 -1
- package/package.json +12 -1
- package/.github/workflows/verify-configs.yml +0 -55
- package/CONTRIBUTING.md +0 -159
- package/docs/configs/claude-code-cli.sh +0 -4
- package/docs/configs/claude-desktop.json +0 -14
- package/docs/configs/cursor-deep-link.txt +0 -1
- package/docs/configs/cursor.json +0 -14
- package/docs/configs/vscode-deep-link.txt +0 -1
- package/docs/configs/vscode.json +0 -15
- package/docs/configs/windsurf.json +0 -14
- package/docs/snippets/claude-code.md +0 -9
- package/docs/snippets/claude-desktop.md +0 -20
- package/docs/snippets/cursor.md +0 -20
- package/docs/snippets/vscode.md +0 -21
- package/docs/snippets/windsurf.md +0 -20
- package/llms.txt +0 -52
- package/scripts/generate-configs.mjs +0 -495
- package/scripts/install-hooks.mjs +0 -76
- package/server.json +0 -29
- package/smithery.yaml +0 -24
- package/src/configs/source.json +0 -28
- package/src/index.ts +0 -422
- package/tsconfig.json +0 -15
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* install-hooks.mjs
|
|
4
|
-
*
|
|
5
|
-
* Installs the optional pre-push hook for local drift detection. The hook
|
|
6
|
-
* runs `npm run verify:configs` before every push and aborts if any of the
|
|
7
|
-
* 15 generated artifacts is out of sync with src/configs/source.json.
|
|
8
|
-
*
|
|
9
|
-
* The hook is per-clone (.git/hooks/ is not committed) and opt-in. CI is
|
|
10
|
-
* the authoritative gate — see .github/workflows/verify-configs.yml.
|
|
11
|
-
*
|
|
12
|
-
* Usage: npm run install-hooks
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import {
|
|
16
|
-
writeFileSync,
|
|
17
|
-
chmodSync,
|
|
18
|
-
existsSync,
|
|
19
|
-
mkdirSync,
|
|
20
|
-
} from "node:fs";
|
|
21
|
-
import { resolve, dirname } from "node:path";
|
|
22
|
-
import { fileURLToPath } from "node:url";
|
|
23
|
-
|
|
24
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
25
|
-
const ROOT = resolve(__dirname, "..");
|
|
26
|
-
const HOOKS_DIR = resolve(ROOT, ".git/hooks");
|
|
27
|
-
const HOOK_PATH = resolve(HOOKS_DIR, "pre-push");
|
|
28
|
-
|
|
29
|
-
if (!existsSync(resolve(ROOT, ".git"))) {
|
|
30
|
-
console.error(
|
|
31
|
-
"✗ Not a git repo (no .git directory found at " + ROOT + ").",
|
|
32
|
-
);
|
|
33
|
-
console.error(" Cannot install pre-push hook.");
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
mkdirSync(HOOKS_DIR, { recursive: true });
|
|
38
|
-
|
|
39
|
-
const HOOK_BODY = `#!/usr/bin/env bash
|
|
40
|
-
# Auto-generated by scripts/install-hooks.mjs.
|
|
41
|
-
# Aborts the push if any of the 15 distribution artifacts has drifted from
|
|
42
|
-
# src/configs/source.json. Re-run install-hooks after pulling generator changes.
|
|
43
|
-
#
|
|
44
|
-
# To bypass for a one-off (NOT recommended for branches targeting main):
|
|
45
|
-
# git push --no-verify
|
|
46
|
-
|
|
47
|
-
set -e
|
|
48
|
-
|
|
49
|
-
# Find repo root so the hook works regardless of where 'git push' was invoked.
|
|
50
|
-
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
|
51
|
-
cd "$REPO_ROOT"
|
|
52
|
-
|
|
53
|
-
if ! command -v node >/dev/null 2>&1; then
|
|
54
|
-
echo "pre-push: node not found in PATH; skipping drift check"
|
|
55
|
-
exit 0
|
|
56
|
-
fi
|
|
57
|
-
|
|
58
|
-
echo "pre-push: verifying distribution config drift..."
|
|
59
|
-
if ! node scripts/generate-configs.mjs --check; then
|
|
60
|
-
echo ""
|
|
61
|
-
echo "✗ Push aborted: distribution configs are out of sync with src/configs/source.json"
|
|
62
|
-
echo " Run \\\`npm run generate:configs\\\` to fix, commit the result, then push again."
|
|
63
|
-
echo " See CONTRIBUTING.md for details."
|
|
64
|
-
exit 1
|
|
65
|
-
fi
|
|
66
|
-
`;
|
|
67
|
-
|
|
68
|
-
writeFileSync(HOOK_PATH, HOOK_BODY, "utf8");
|
|
69
|
-
chmodSync(HOOK_PATH, 0o755);
|
|
70
|
-
|
|
71
|
-
console.log("✓ Installed pre-push hook at " + HOOK_PATH);
|
|
72
|
-
console.log(" It will run `node scripts/generate-configs.mjs --check`");
|
|
73
|
-
console.log(" before every push and abort on drift.");
|
|
74
|
-
console.log("");
|
|
75
|
-
console.log(" To remove: rm " + HOOK_PATH);
|
|
76
|
-
console.log(" To bypass once: git push --no-verify");
|
package/server.json
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
-
"name": "io.github.mnemoverse/mcp-memory-server",
|
|
4
|
-
"description": "Shared AI memory across Claude, Cursor, VS Code, ChatGPT. Write once, recall anywhere.",
|
|
5
|
-
"repository": {
|
|
6
|
-
"url": "https://github.com/mnemoverse/mcp-memory-server",
|
|
7
|
-
"source": "github"
|
|
8
|
-
},
|
|
9
|
-
"version": "0.3.1",
|
|
10
|
-
"packages": [
|
|
11
|
-
{
|
|
12
|
-
"registryType": "npm",
|
|
13
|
-
"identifier": "@mnemoverse/mcp-memory-server",
|
|
14
|
-
"version": "0.3.1",
|
|
15
|
-
"transport": {
|
|
16
|
-
"type": "stdio"
|
|
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
|
-
"format": "string",
|
|
24
|
-
"isSecret": true
|
|
25
|
-
}
|
|
26
|
-
]
|
|
27
|
-
}
|
|
28
|
-
]
|
|
29
|
-
}
|
package/smithery.yaml
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
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@latest"],
|
|
21
|
-
env: {
|
|
22
|
-
MNEMOVERSE_API_KEY: config.mnemoverseApiKey,
|
|
23
|
-
}
|
|
24
|
-
})
|
package/src/configs/source.json
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
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": "Shared AI memory across Claude, Cursor, VS Code, ChatGPT. Write once, recall anywhere.",
|
|
6
|
-
"type": "stdio",
|
|
7
|
-
"command": "npx",
|
|
8
|
-
"args": ["-y", "@mnemoverse/mcp-memory-server@latest"],
|
|
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
DELETED
|
@@ -1,422 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
-
import { z } from "zod";
|
|
6
|
-
|
|
7
|
-
const API_URL =
|
|
8
|
-
process.env.MNEMOVERSE_API_URL || "https://core.mnemoverse.com/api/v1";
|
|
9
|
-
const API_KEY = process.env.MNEMOVERSE_API_KEY || "";
|
|
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
|
-
|
|
16
|
-
if (!API_KEY) {
|
|
17
|
-
console.error(
|
|
18
|
-
"Error: MNEMOVERSE_API_KEY environment variable is required.\n" +
|
|
19
|
-
"Get your free key at https://console.mnemoverse.com",
|
|
20
|
-
);
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async function apiFetch(
|
|
25
|
-
path: string,
|
|
26
|
-
options: RequestInit = {},
|
|
27
|
-
): Promise<unknown> {
|
|
28
|
-
const res = await fetch(`${API_URL}${path}`, {
|
|
29
|
-
...options,
|
|
30
|
-
headers: {
|
|
31
|
-
"Content-Type": "application/json",
|
|
32
|
-
"X-Api-Key": API_KEY,
|
|
33
|
-
...((options.headers as Record<string, string>) || {}),
|
|
34
|
-
},
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
if (!res.ok) {
|
|
38
|
-
const text = await res.text();
|
|
39
|
-
throw new Error(`Mnemoverse API error ${res.status}: ${text}`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return res.json();
|
|
43
|
-
}
|
|
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
|
-
|
|
58
|
-
// --- Server setup ---
|
|
59
|
-
|
|
60
|
-
const server = new McpServer({
|
|
61
|
-
name: "mnemoverse-memory",
|
|
62
|
-
version: "0.3.1",
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// --- Tool: memory_write ---
|
|
66
|
-
|
|
67
|
-
server.registerTool(
|
|
68
|
-
"memory_write",
|
|
69
|
-
{
|
|
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
|
-
},
|
|
98
|
-
},
|
|
99
|
-
async ({ content, concepts, domain }) => {
|
|
100
|
-
const result = await apiFetch("/memory/write", {
|
|
101
|
-
method: "POST",
|
|
102
|
-
body: JSON.stringify({
|
|
103
|
-
content,
|
|
104
|
-
concepts: concepts || [],
|
|
105
|
-
domain: domain || "general",
|
|
106
|
-
}),
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
const r = result as {
|
|
110
|
-
stored: boolean;
|
|
111
|
-
atom_id: string | null;
|
|
112
|
-
importance: number;
|
|
113
|
-
reason: string;
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
if (r.stored) {
|
|
117
|
-
return {
|
|
118
|
-
content: [
|
|
119
|
-
{
|
|
120
|
-
type: "text" as const,
|
|
121
|
-
text: `Stored (importance: ${r.importance.toFixed(2)}). ID: ${r.atom_id}`,
|
|
122
|
-
},
|
|
123
|
-
],
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
return {
|
|
127
|
-
content: [
|
|
128
|
-
{
|
|
129
|
-
type: "text" as const,
|
|
130
|
-
text: `Filtered — ${r.reason} (importance: ${r.importance.toFixed(2)})`,
|
|
131
|
-
},
|
|
132
|
-
],
|
|
133
|
-
};
|
|
134
|
-
},
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
// --- Tool: memory_read ---
|
|
138
|
-
|
|
139
|
-
server.registerTool(
|
|
140
|
-
"memory_read",
|
|
141
|
-
{
|
|
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
|
-
},
|
|
169
|
-
},
|
|
170
|
-
async ({ query, top_k, domain }) => {
|
|
171
|
-
const result = await apiFetch("/memory/read", {
|
|
172
|
-
method: "POST",
|
|
173
|
-
body: JSON.stringify({
|
|
174
|
-
query,
|
|
175
|
-
top_k: top_k || 5,
|
|
176
|
-
domain: domain || undefined,
|
|
177
|
-
include_associations: true,
|
|
178
|
-
}),
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
const r = result as {
|
|
182
|
-
items: Array<{
|
|
183
|
-
content: string;
|
|
184
|
-
relevance: number;
|
|
185
|
-
concepts: string[];
|
|
186
|
-
domain: string;
|
|
187
|
-
}>;
|
|
188
|
-
search_time_ms: number;
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
if (r.items.length === 0) {
|
|
192
|
-
return {
|
|
193
|
-
content: [
|
|
194
|
-
{ type: "text" as const, text: "No memories found for this query." },
|
|
195
|
-
],
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const lines = r.items.map(
|
|
200
|
-
(item, i) =>
|
|
201
|
-
`${i + 1}. [${(item.relevance * 100).toFixed(0)}%] ${item.content}` +
|
|
202
|
-
(item.concepts.length > 0
|
|
203
|
-
? ` (${item.concepts.join(", ")})`
|
|
204
|
-
: ""),
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
const text = lines.join("\n\n") + `\n\n(${r.search_time_ms.toFixed(0)}ms)`;
|
|
208
|
-
|
|
209
|
-
return {
|
|
210
|
-
content: [
|
|
211
|
-
{
|
|
212
|
-
type: "text" as const,
|
|
213
|
-
text: capResult(text),
|
|
214
|
-
},
|
|
215
|
-
],
|
|
216
|
-
};
|
|
217
|
-
},
|
|
218
|
-
);
|
|
219
|
-
|
|
220
|
-
// --- Tool: memory_feedback ---
|
|
221
|
-
|
|
222
|
-
server.registerTool(
|
|
223
|
-
"memory_feedback",
|
|
224
|
-
{
|
|
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
|
-
},
|
|
245
|
-
},
|
|
246
|
-
async ({ atom_ids, outcome }) => {
|
|
247
|
-
const result = await apiFetch("/memory/feedback", {
|
|
248
|
-
method: "POST",
|
|
249
|
-
body: JSON.stringify({ atom_ids, outcome }),
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
const r = result as { updated_count: number };
|
|
253
|
-
|
|
254
|
-
return {
|
|
255
|
-
content: [
|
|
256
|
-
{
|
|
257
|
-
type: "text" as const,
|
|
258
|
-
text: `Feedback recorded for ${r.updated_count} memor${r.updated_count === 1 ? "y" : "ies"}.`,
|
|
259
|
-
},
|
|
260
|
-
],
|
|
261
|
-
};
|
|
262
|
-
},
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
// --- Tool: memory_stats ---
|
|
266
|
-
|
|
267
|
-
server.registerTool(
|
|
268
|
-
"memory_stats",
|
|
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
|
-
},
|
|
281
|
-
async () => {
|
|
282
|
-
const result = await apiFetch("/memory/stats");
|
|
283
|
-
|
|
284
|
-
const r = result as {
|
|
285
|
-
total_atoms: number;
|
|
286
|
-
episodes: number;
|
|
287
|
-
prototypes: number;
|
|
288
|
-
hebbian_edges: number;
|
|
289
|
-
domains: string[];
|
|
290
|
-
avg_valence: number;
|
|
291
|
-
avg_importance: number;
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
const text = [
|
|
295
|
-
`Memories: ${r.total_atoms} (${r.episodes} episodes, ${r.prototypes} prototypes)`,
|
|
296
|
-
`Associations: ${r.hebbian_edges} Hebbian edges`,
|
|
297
|
-
`Domains: ${r.domains.length > 0 ? r.domains.join(", ") : "general"}`,
|
|
298
|
-
`Avg quality: valence ${r.avg_valence.toFixed(2)}, importance ${r.avg_importance.toFixed(2)}`,
|
|
299
|
-
].join("\n");
|
|
300
|
-
|
|
301
|
-
return { content: [{ type: "text" as const, text }] };
|
|
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
|
-
},
|
|
410
|
-
);
|
|
411
|
-
|
|
412
|
-
// --- Start ---
|
|
413
|
-
|
|
414
|
-
async function main() {
|
|
415
|
-
const transport = new StdioServerTransport();
|
|
416
|
-
await server.connect(transport);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
main().catch((err) => {
|
|
420
|
-
console.error("MCP server error:", err);
|
|
421
|
-
process.exit(1);
|
|
422
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "Node16",
|
|
5
|
-
"moduleResolution": "Node16",
|
|
6
|
-
"outDir": "./dist",
|
|
7
|
-
"rootDir": "./src",
|
|
8
|
-
"strict": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
"declaration": true,
|
|
12
|
-
"sourceMap": true
|
|
13
|
-
},
|
|
14
|
-
"include": ["src/**/*"]
|
|
15
|
-
}
|