@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,9 +0,0 @@
|
|
|
1
|
-
<!-- AUTO-GENERATED from src/configs/source.json. Run `npm run generate:configs`. Do not edit by hand. -->
|
|
2
|
-
|
|
3
|
-
**Claude Code** — add via CLI:
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
claude mcp add mnemoverse \
|
|
7
|
-
-e MNEMOVERSE_API_KEY=mk_live_YOUR_KEY \
|
|
8
|
-
-- npx -y @mnemoverse/mcp-memory-server@latest
|
|
9
|
-
```
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
<!-- AUTO-GENERATED from src/configs/source.json. Run `npm run generate:configs`. Do not edit by hand. -->
|
|
2
|
-
|
|
3
|
-
**Claude Desktop** — add to `claude_desktop_config.json`:
|
|
4
|
-
|
|
5
|
-
```json
|
|
6
|
-
{
|
|
7
|
-
"mcpServers": {
|
|
8
|
-
"mnemoverse": {
|
|
9
|
-
"command": "npx",
|
|
10
|
-
"args": [
|
|
11
|
-
"-y",
|
|
12
|
-
"@mnemoverse/mcp-memory-server@latest"
|
|
13
|
-
],
|
|
14
|
-
"env": {
|
|
15
|
-
"MNEMOVERSE_API_KEY": "mk_live_YOUR_KEY"
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
```
|
package/docs/snippets/cursor.md
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
<!-- AUTO-GENERATED from src/configs/source.json. Run `npm run generate:configs`. Do not edit by hand. -->
|
|
2
|
-
|
|
3
|
-
**Cursor** — add to `.cursor/mcp.json`:
|
|
4
|
-
|
|
5
|
-
```json
|
|
6
|
-
{
|
|
7
|
-
"mcpServers": {
|
|
8
|
-
"mnemoverse": {
|
|
9
|
-
"command": "npx",
|
|
10
|
-
"args": [
|
|
11
|
-
"-y",
|
|
12
|
-
"@mnemoverse/mcp-memory-server@latest"
|
|
13
|
-
],
|
|
14
|
-
"env": {
|
|
15
|
-
"MNEMOVERSE_API_KEY": "mk_live_YOUR_KEY"
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
```
|
package/docs/snippets/vscode.md
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
<!-- AUTO-GENERATED from src/configs/source.json. Run `npm run generate:configs`. Do not edit by hand. -->
|
|
2
|
-
|
|
3
|
-
**VS Code** — add to `.vscode/mcp.json` (note: VS Code uses `servers`, not `mcpServers`):
|
|
4
|
-
|
|
5
|
-
```json
|
|
6
|
-
{
|
|
7
|
-
"servers": {
|
|
8
|
-
"mnemoverse": {
|
|
9
|
-
"type": "stdio",
|
|
10
|
-
"command": "npx",
|
|
11
|
-
"args": [
|
|
12
|
-
"-y",
|
|
13
|
-
"@mnemoverse/mcp-memory-server@latest"
|
|
14
|
-
],
|
|
15
|
-
"env": {
|
|
16
|
-
"MNEMOVERSE_API_KEY": "mk_live_YOUR_KEY"
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
```
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
<!-- AUTO-GENERATED from src/configs/source.json. Run `npm run generate:configs`. Do not edit by hand. -->
|
|
2
|
-
|
|
3
|
-
**Windsurf** — add to `~/.codeium/windsurf/mcp_config.json`:
|
|
4
|
-
|
|
5
|
-
```json
|
|
6
|
-
{
|
|
7
|
-
"mcpServers": {
|
|
8
|
-
"mnemoverse": {
|
|
9
|
-
"command": "npx",
|
|
10
|
-
"args": [
|
|
11
|
-
"-y",
|
|
12
|
-
"@mnemoverse/mcp-memory-server@latest"
|
|
13
|
-
],
|
|
14
|
-
"env": {
|
|
15
|
-
"MNEMOVERSE_API_KEY": "mk_live_YOUR_KEY"
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
```
|
package/llms.txt
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
# Mnemoverse MCP Memory Server
|
|
2
|
-
|
|
3
|
-
> Persistent AI memory via Model Context Protocol. Store and recall memories across sessions.
|
|
4
|
-
|
|
5
|
-
## Tools
|
|
6
|
-
|
|
7
|
-
### memory_write
|
|
8
|
-
Store a memory — insight, pattern, preference, or fact. Persists across sessions.
|
|
9
|
-
|
|
10
|
-
Parameters:
|
|
11
|
-
- content (string, required): The memory to store — what happened, what was learned. 1-10000 chars.
|
|
12
|
-
- concepts (string[], optional): Key concepts for linking related memories. Example: ["deploy", "friday", "staging"]
|
|
13
|
-
- domain (string, optional): Namespace to organize memories. Example: "engineering", "user:alice"
|
|
14
|
-
|
|
15
|
-
Returns: {stored: boolean, atom_id: string|null, importance: number, reason: string}
|
|
16
|
-
|
|
17
|
-
### memory_read
|
|
18
|
-
Search memories by natural language query. Returns most relevant stored memories ranked by relevance.
|
|
19
|
-
|
|
20
|
-
Parameters:
|
|
21
|
-
- query (string, required): Natural language query — what are you looking for? 1-5000 chars.
|
|
22
|
-
- top_k (integer, optional, default 5): Max results to return. Range: 1-50.
|
|
23
|
-
- domain (string, optional): Filter by domain namespace.
|
|
24
|
-
|
|
25
|
-
Returns: List of memories with relevance scores, concepts, and domain.
|
|
26
|
-
|
|
27
|
-
### memory_feedback
|
|
28
|
-
Report whether a retrieved memory was helpful. Positive feedback strengthens recall. Negative lets memories fade.
|
|
29
|
-
|
|
30
|
-
Parameters:
|
|
31
|
-
- atom_ids (string[], required): IDs of memories to give feedback on.
|
|
32
|
-
- outcome (number, required): How helpful? Range: -1.0 (harmful) to 1.0 (very helpful).
|
|
33
|
-
|
|
34
|
-
Returns: {updated_count: number}
|
|
35
|
-
|
|
36
|
-
### memory_stats
|
|
37
|
-
Get memory statistics — count, domains, quality scores.
|
|
38
|
-
|
|
39
|
-
Parameters: none
|
|
40
|
-
|
|
41
|
-
Returns: {total_atoms, episodes, prototypes, hebbian_edges, domains, avg_valence, avg_importance}
|
|
42
|
-
|
|
43
|
-
## Setup
|
|
44
|
-
Environment: MNEMOVERSE_API_KEY (required), MNEMOVERSE_API_URL (optional, default https://core.mnemoverse.com/api/v1)
|
|
45
|
-
Transport: stdio
|
|
46
|
-
Command: npx @mnemoverse/mcp-memory-server
|
|
47
|
-
|
|
48
|
-
## Links
|
|
49
|
-
- Docs: https://mnemoverse.com/docs/api/mcp-server
|
|
50
|
-
- API Reference: https://mnemoverse.com/docs/api/reference
|
|
51
|
-
- Console: https://console.mnemoverse.com
|
|
52
|
-
- Source: https://github.com/mnemoverse/mcp-memory-server
|
|
@@ -1,495 +0,0 @@
|
|
|
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
|
-
// ─── Markdown partials ────────────────────────────────────────────────────────
|
|
195
|
-
//
|
|
196
|
-
// These are the SAME install snippets that ship to:
|
|
197
|
-
// - mcp-memory-server/README.md (assembled into the INSTALL_SNIPPETS block)
|
|
198
|
-
// - mnemoverse-docs (synced via cross-repo workflow → docs/.snippets/)
|
|
199
|
-
//
|
|
200
|
-
// One channel = one partial = one place to edit (source.json). The README's
|
|
201
|
-
// install block is rebuilt from these partials in-place — never edit it by hand.
|
|
202
|
-
|
|
203
|
-
const PARTIAL_HEADER =
|
|
204
|
-
"<!-- AUTO-GENERATED from src/configs/source.json. Run `npm run generate:configs`. Do not edit by hand. -->\n\n";
|
|
205
|
-
|
|
206
|
-
function snippetClaudeCodeCli() {
|
|
207
|
-
// shell command, multiline with backslash continuations
|
|
208
|
-
return (
|
|
209
|
-
"**Claude Code** — add via CLI:\n\n" +
|
|
210
|
-
"```bash\n" +
|
|
211
|
-
genClaudeCodeCli().trim() +
|
|
212
|
-
"\n```\n"
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function snippetMcpServersJson(label, configPath) {
|
|
217
|
-
// Cursor / Claude Desktop / Windsurf — shared mcpServers shape
|
|
218
|
-
const json = JSON.stringify(genMcpServersFormat(), null, 2);
|
|
219
|
-
return (
|
|
220
|
-
`**${label}** — add to \`${configPath}\`:\n\n` +
|
|
221
|
-
"```json\n" +
|
|
222
|
-
json +
|
|
223
|
-
"\n```\n"
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function snippetVscode() {
|
|
228
|
-
// VS Code uses `servers` (not `mcpServers`) and requires `type: "stdio"`
|
|
229
|
-
const json = JSON.stringify(genVscodeFormat(), null, 2);
|
|
230
|
-
return (
|
|
231
|
-
"**VS Code** — add to `.vscode/mcp.json` (note: VS Code uses `servers`, not `mcpServers`):\n\n" +
|
|
232
|
-
"```json\n" +
|
|
233
|
-
json +
|
|
234
|
-
"\n```\n"
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const WHY_LATEST_NOTE =
|
|
239
|
-
"> Why `@latest`? Bare `npx @mnemoverse/mcp-memory-server` is cached indefinitely by npm and stops re-checking the registry. The `@latest` suffix forces a metadata lookup on every Claude Code / Cursor / VS Code session start (~100-300ms), so you always pick up new releases.";
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Build the README install block contents (without the START/END markers).
|
|
243
|
-
* The order here matches the README — change here, README rewrites itself.
|
|
244
|
-
*/
|
|
245
|
-
function readmeInstallBlock() {
|
|
246
|
-
return [
|
|
247
|
-
snippetClaudeCodeCli(),
|
|
248
|
-
snippetMcpServersJson("Cursor", ".cursor/mcp.json"),
|
|
249
|
-
snippetVscode(),
|
|
250
|
-
snippetMcpServersJson("Windsurf", "~/.codeium/windsurf/mcp_config.json"),
|
|
251
|
-
WHY_LATEST_NOTE,
|
|
252
|
-
].join("\n");
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// ─── README in-place rewriter ─────────────────────────────────────────────────
|
|
256
|
-
|
|
257
|
-
const README_START =
|
|
258
|
-
"<!-- INSTALL_SNIPPETS_START — generated from src/configs/source.json. Run `npm run generate:configs` to refresh. Do not edit by hand. -->";
|
|
259
|
-
const README_END = "<!-- INSTALL_SNIPPETS_END -->";
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Take current README content + the freshly assembled install block and return
|
|
263
|
-
* the rewritten README content. Idempotent.
|
|
264
|
-
*
|
|
265
|
-
* Throws if the markers are missing — that means a contributor stripped them
|
|
266
|
-
* out and we don't know where to inject the snippets, so we fail loudly
|
|
267
|
-
* instead of silently doing the wrong thing.
|
|
268
|
-
*/
|
|
269
|
-
function rewriteReadme(currentReadme, freshBlock) {
|
|
270
|
-
const startIdx = currentReadme.indexOf("<!-- INSTALL_SNIPPETS_START");
|
|
271
|
-
const endIdx = currentReadme.indexOf("<!-- INSTALL_SNIPPETS_END -->");
|
|
272
|
-
|
|
273
|
-
if (startIdx === -1 || endIdx === -1) {
|
|
274
|
-
throw new Error(
|
|
275
|
-
"README.md is missing the INSTALL_SNIPPETS_START / INSTALL_SNIPPETS_END markers.\n" +
|
|
276
|
-
"These markers tell the generator where to inject the install snippets.\n" +
|
|
277
|
-
"Restore them around the install section and re-run `npm run generate:configs`.",
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
if (startIdx >= endIdx) {
|
|
281
|
-
throw new Error(
|
|
282
|
-
"README.md INSTALL_SNIPPETS_END marker appears before INSTALL_SNIPPETS_START.",
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
const before = currentReadme.slice(0, startIdx);
|
|
287
|
-
const after = currentReadme.slice(endIdx + README_END.length);
|
|
288
|
-
|
|
289
|
-
return `${before}${README_START}\n\n${freshBlock}\n\n${README_END}${after}`;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Official MCP Registry server.json.
|
|
294
|
-
*
|
|
295
|
-
* https://registry.modelcontextprotocol.io/
|
|
296
|
-
* Schema: https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json
|
|
297
|
-
*
|
|
298
|
-
* The canonical shape was verified empirically by running `mcp-publisher init`
|
|
299
|
-
* in a sandbox and matching its output. Key differences vs the older
|
|
300
|
-
* 2025-09-29 schema: registryName → registryType, packages[0].name →
|
|
301
|
-
* packages[0].identifier, new required `transport` object, dropped
|
|
302
|
-
* runtimeArguments (the registry runtime builds its own launch command
|
|
303
|
-
* from identifier + version), and environmentVariables entries gained a
|
|
304
|
-
* `format` field.
|
|
305
|
-
*
|
|
306
|
-
* The `@latest` pin we use in docs/configs/* does NOT belong here — that
|
|
307
|
-
* is for direct JSON-snippet installs where users stay on the bleeding
|
|
308
|
-
* edge. Registry-installed clients get the exact pinned version from
|
|
309
|
-
* server.json.version and we re-publish on each npm release to advance
|
|
310
|
-
* them.
|
|
311
|
-
*/
|
|
312
|
-
function genServerJson() {
|
|
313
|
-
return {
|
|
314
|
-
$schema:
|
|
315
|
-
"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
316
|
-
name: "io.github.mnemoverse/mcp-memory-server",
|
|
317
|
-
description: source.description,
|
|
318
|
-
repository: {
|
|
319
|
-
url: source.metadata.repository,
|
|
320
|
-
source: "github",
|
|
321
|
-
},
|
|
322
|
-
version: PACKAGE_VERSION,
|
|
323
|
-
packages: [
|
|
324
|
-
{
|
|
325
|
-
registryType: source.package.registry,
|
|
326
|
-
identifier: source.package.name,
|
|
327
|
-
version: PACKAGE_VERSION,
|
|
328
|
-
transport: {
|
|
329
|
-
type: "stdio",
|
|
330
|
-
},
|
|
331
|
-
environmentVariables: Object.entries(source.env).map(([key, meta]) => ({
|
|
332
|
-
name: key,
|
|
333
|
-
description: meta.description,
|
|
334
|
-
isRequired: meta.required ?? false,
|
|
335
|
-
format: "string",
|
|
336
|
-
isSecret: meta.secret ?? false,
|
|
337
|
-
})),
|
|
338
|
-
},
|
|
339
|
-
],
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// ─── Output ──────────────────────────────────────────────────────────────────
|
|
344
|
-
|
|
345
|
-
const OUTPUTS = [
|
|
346
|
-
{
|
|
347
|
-
path: "docs/configs/cursor.json",
|
|
348
|
-
content: JSON.stringify(genMcpServersFormat(), null, 2) + "\n",
|
|
349
|
-
},
|
|
350
|
-
{
|
|
351
|
-
path: "docs/configs/claude-desktop.json",
|
|
352
|
-
content: JSON.stringify(genMcpServersFormat(), null, 2) + "\n",
|
|
353
|
-
},
|
|
354
|
-
{
|
|
355
|
-
path: "docs/configs/windsurf.json",
|
|
356
|
-
content: JSON.stringify(genMcpServersFormat(), null, 2) + "\n",
|
|
357
|
-
},
|
|
358
|
-
{
|
|
359
|
-
path: "docs/configs/vscode.json",
|
|
360
|
-
content: JSON.stringify(genVscodeFormat(), null, 2) + "\n",
|
|
361
|
-
},
|
|
362
|
-
{
|
|
363
|
-
path: "docs/configs/cursor-deep-link.txt",
|
|
364
|
-
content: genCursorDeepLink() + "\n",
|
|
365
|
-
},
|
|
366
|
-
{
|
|
367
|
-
path: "docs/configs/vscode-deep-link.txt",
|
|
368
|
-
content: genVscodeDeepLink() + "\n",
|
|
369
|
-
},
|
|
370
|
-
{
|
|
371
|
-
path: "docs/configs/claude-code-cli.sh",
|
|
372
|
-
content: "#!/usr/bin/env bash\n" + genClaudeCodeCli(),
|
|
373
|
-
},
|
|
374
|
-
{
|
|
375
|
-
path: "smithery.yaml",
|
|
376
|
-
content: genSmitheryYaml(),
|
|
377
|
-
},
|
|
378
|
-
{
|
|
379
|
-
path: "server.json",
|
|
380
|
-
content: JSON.stringify(genServerJson(), null, 2) + "\n",
|
|
381
|
-
},
|
|
382
|
-
// ─── Markdown partials (consumed by README + mnemoverse-docs) ──────────────
|
|
383
|
-
{
|
|
384
|
-
path: "docs/snippets/claude-code.md",
|
|
385
|
-
content: PARTIAL_HEADER + snippetClaudeCodeCli(),
|
|
386
|
-
},
|
|
387
|
-
{
|
|
388
|
-
path: "docs/snippets/cursor.md",
|
|
389
|
-
content:
|
|
390
|
-
PARTIAL_HEADER + snippetMcpServersJson("Cursor", ".cursor/mcp.json"),
|
|
391
|
-
},
|
|
392
|
-
{
|
|
393
|
-
path: "docs/snippets/claude-desktop.md",
|
|
394
|
-
content:
|
|
395
|
-
PARTIAL_HEADER +
|
|
396
|
-
snippetMcpServersJson("Claude Desktop", "claude_desktop_config.json"),
|
|
397
|
-
},
|
|
398
|
-
{
|
|
399
|
-
path: "docs/snippets/vscode.md",
|
|
400
|
-
content: PARTIAL_HEADER + snippetVscode(),
|
|
401
|
-
},
|
|
402
|
-
{
|
|
403
|
-
path: "docs/snippets/windsurf.md",
|
|
404
|
-
content:
|
|
405
|
-
PARTIAL_HEADER +
|
|
406
|
-
snippetMcpServersJson(
|
|
407
|
-
"Windsurf",
|
|
408
|
-
"~/.codeium/windsurf/mcp_config.json",
|
|
409
|
-
),
|
|
410
|
-
},
|
|
411
|
-
];
|
|
412
|
-
|
|
413
|
-
// ─── Write files ─────────────────────────────────────────────────────────────
|
|
414
|
-
|
|
415
|
-
const checkMode = process.argv.includes("--check");
|
|
416
|
-
|
|
417
|
-
let written = 0;
|
|
418
|
-
let unchanged = 0;
|
|
419
|
-
|
|
420
|
-
for (const { path, content } of OUTPUTS) {
|
|
421
|
-
const fullPath = resolve(ROOT, path);
|
|
422
|
-
mkdirSync(dirname(fullPath), { recursive: true });
|
|
423
|
-
|
|
424
|
-
const existing = existsSync(fullPath) ? readFileSync(fullPath, "utf8") : "";
|
|
425
|
-
if (existing === content) {
|
|
426
|
-
unchanged++;
|
|
427
|
-
continue;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
if (checkMode) {
|
|
431
|
-
console.error(`✗ Drift detected: ${path}`);
|
|
432
|
-
console.error(" Expected (regenerated):");
|
|
433
|
-
console.error(" " + content.slice(0, 200).split("\n").join("\n "));
|
|
434
|
-
console.error(" Got (committed):");
|
|
435
|
-
console.error(" " + existing.slice(0, 200).split("\n").join("\n "));
|
|
436
|
-
process.exit(1);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
writeFileSync(fullPath, content, "utf8");
|
|
440
|
-
console.log(`✓ Generated ${path}`);
|
|
441
|
-
written++;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// ─── README install block (in-place rewrite) ────────────────────────────────
|
|
445
|
-
//
|
|
446
|
-
// Special-cased because README is read-modify-write — we can only update the
|
|
447
|
-
// region between the INSTALL_SNIPPETS markers, the rest of the file is human-
|
|
448
|
-
// authored prose.
|
|
449
|
-
|
|
450
|
-
const README_PATH = resolve(ROOT, "README.md");
|
|
451
|
-
|
|
452
|
-
if (!existsSync(README_PATH)) {
|
|
453
|
-
console.error(`✗ README.md not found at ${README_PATH}`);
|
|
454
|
-
process.exit(1);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
const currentReadme = readFileSync(README_PATH, "utf8");
|
|
458
|
-
let freshReadme;
|
|
459
|
-
try {
|
|
460
|
-
freshReadme = rewriteReadme(currentReadme, readmeInstallBlock());
|
|
461
|
-
} catch (err) {
|
|
462
|
-
console.error("✗ Cannot rewrite README.md install block:");
|
|
463
|
-
console.error(" " + (err.message || err).split("\n").join("\n "));
|
|
464
|
-
process.exit(1);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
if (currentReadme === freshReadme) {
|
|
468
|
-
unchanged++;
|
|
469
|
-
} else if (checkMode) {
|
|
470
|
-
console.error("✗ Drift detected: README.md install block is stale");
|
|
471
|
-
console.error(
|
|
472
|
-
" The INSTALL_SNIPPETS_START/END region in README.md does not match",
|
|
473
|
-
);
|
|
474
|
-
console.error(
|
|
475
|
-
" what the generator would emit from src/configs/source.json.",
|
|
476
|
-
);
|
|
477
|
-
console.error(" Run `npm run generate:configs` and commit the result.");
|
|
478
|
-
process.exit(1);
|
|
479
|
-
} else {
|
|
480
|
-
writeFileSync(README_PATH, freshReadme, "utf8");
|
|
481
|
-
console.log("✓ Generated README.md (install block)");
|
|
482
|
-
written++;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
const totalArtifacts = OUTPUTS.length + 1; // +1 for README
|
|
486
|
-
|
|
487
|
-
if (checkMode) {
|
|
488
|
-
console.log(
|
|
489
|
-
`✓ All ${totalArtifacts} artifacts in sync with source.json`,
|
|
490
|
-
);
|
|
491
|
-
} else {
|
|
492
|
-
console.log(
|
|
493
|
-
`\nDone: ${written} written, ${unchanged} unchanged (${totalArtifacts} total)`,
|
|
494
|
-
);
|
|
495
|
-
}
|