@memorycode/mcp-server 1.0.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/README.md ADDED
@@ -0,0 +1,317 @@
1
+ # MemoryCode MCP Server
2
+
3
+ 将 MemoryCode 记忆配置通过 MCP 暴露给 **Claude Desktop**、**Cursor** 等客户端。**Claude 桌面版**的 MCP 交互方式为:**自然语言**(如「请使用职场配置」)与点击输入框旁的 **「+」** 展开后选择记忆配置;**@ 在 Claude 桌面版中对 MCP 无任何作用**(参见 YIS-20 研究)。其他 AI 客户端的引用方式以各产品为准,本 Server 均支持通过自然语言(Tool)及 Resources 供客户端加载。
4
+
5
+ ---
6
+
7
+ ## 环境要求
8
+
9
+ - **Node.js 18+**
10
+ - 已从 MemoryCode Web 端导出的 **`memorycode-mcp.json`**(设置 → MCP Server → 导出到 MCP)
11
+
12
+ ---
13
+
14
+ ## 安装
15
+
16
+ ```bash
17
+ cd memorycode-mcp-server
18
+ npm install
19
+ npm run build
20
+ ```
21
+
22
+ 构建完成后,可执行文件为 `dist/index.js`。
23
+
24
+ ---
25
+
26
+ ## 快速开始
27
+
28
+ 1. **导出配置**
29
+ 在 MemoryCode 应用中:设置 → MCP Server → 导出到 MCP,将 `memorycode-mcp.json` 保存到本机(例如 `~/memorycode/` 或 `~/Desktop/MCP/`)。
30
+
31
+ 2. **启动 Server**(二选一)
32
+ - 使用默认路径 `~/memorycode/memorycode-mcp.json`:
33
+ ```bash
34
+ node dist/index.js
35
+ ```
36
+ - 或指定文件:
37
+ ```bash
38
+ node dist/index.js --file /path/to/memorycode-mcp.json
39
+ ```
40
+
41
+ 3. **在 Claude Desktop 或 Cursor 中配置 MCP**(见下方「Cursor / Claude Desktop 配置」)。
42
+
43
+ 4. **使用方式**
44
+ - **Claude Desktop**:用**自然语言**(如「请使用职场配置」「有哪些配置可用」),或点击输入框旁的 **「+」** 展开后选择记忆配置加载。其他客户端(如 Cursor)若支持 @ 或类似引用方式,可按其界面操作;否则同样通过自然语言或该客户端的资源/附件入口选择配置。
45
+
46
+ ---
47
+
48
+ ## 命令行与配置文件
49
+
50
+ | 选项 | 说明 |
51
+ |------|------|
52
+ | `-f, --file <path>` | 监听的 `memorycode-mcp.json` 路径 |
53
+ | `-c, --config <path>` | 使用 JSON 配置文件(见下) |
54
+ | `-h, --help` | 显示帮助 |
55
+
56
+ **默认监听路径:**
57
+
58
+ - macOS / Linux:`~/memorycode/memorycode-mcp.json`
59
+ - Windows:`%USERPROFILE%\memorycode\memorycode-mcp.json`
60
+
61
+ **配置文件示例**(`--config /path/to/config.json`):
62
+
63
+ ```json
64
+ {
65
+ "watchFile": "/path/to/memorycode-mcp.json",
66
+ "logLevel": "info"
67
+ }
68
+ ```
69
+
70
+ `logLevel` 可选:`info`、`warn`、`error`。
71
+
72
+ ---
73
+
74
+ ## Cursor / Claude Desktop 配置
75
+
76
+ ### Claude Desktop
77
+
78
+ 1. 打开 MCP 配置目录:
79
+ ```bash
80
+ open ~/Library/Application\ Support/Claude/
81
+ ```
82
+ 2. 编辑 `claude_desktop_config.json`,在 `mcpServers` 中增加一项(将 `command` 和 `args` 中的路径改为你本机实际路径):
83
+
84
+ ```json
85
+ "memorycode": {
86
+ "command": "/usr/local/bin/node",
87
+ "args": [
88
+ "/Users/你的用户名/ai-identity-wallet/memorycode-mcp-server/dist/index.js",
89
+ "--file",
90
+ "/Users/你的用户名/Desktop/MCP/memorycode-mcp.json"
91
+ ]
92
+ }
93
+ ```
94
+
95
+ 3. 查询本机 `node` 路径:终端执行 `which node`,将得到的路径填到 `command`。
96
+ 4. 完全退出 Claude Desktop 后重新打开。**在 Claude 桌面版中**:通过**自然语言**(如「请使用职场配置」「有哪些配置可用」)或点击输入框旁的 **「+」** 展开附件/资源菜单并选择 MemoryCode 记忆配置加载。**注意:@ 在 Claude 桌面版中对 MCP 无任何功能,请勿依赖 @ 调用记忆配置。**
97
+
98
+ ### Cursor
99
+
100
+ **重要:** 在 Cursor 中请**不要**使用 `npx @memorycode/mcp-server`。该方式容易触发 "No server info found" / "Server not yet created"。请改用**本仓库本地构建**的 server:`command` 填本机 **node 路径**,`args` 填 **[本机 dist/index.js 绝对路径, "--file", memorycode-mcp.json 绝对路径]**,并在 `memorycode` 配置里**必须**写 **`"type": "stdio"`**。可直接复制 [cursor-mcp-example.json](./cursor-mcp-example.json) 中 `memorycode` 一段,把路径改成你的本机路径后粘贴到 `~/.cursor/mcp.json` 的 `mcpServers` 中。
101
+
102
+ 以下配置格式已在 Cursor 中验证通过,按此填写即可正常接入。Cursor 通过 **`mcp.json`** 配置 MCP Server,需手动编辑 JSON 并填写本机路径。根据 [Cursor MCP 文档](https://cursor.com/docs/context/mcp),STDIO 类型服务器**必须**包含 `"type": "stdio"`,且 `memorycode` 必须写在 **`mcpServers`** 对象**内部**(与其它 MCP 并列),不能写在 `mcpServers` 外面。
103
+
104
+ **配置位置(二选一):**
105
+
106
+ - **全局配置**(所有项目可用):`~/.cursor/mcp.json`(macOS/Linux)或 `%USERPROFILE%\.cursor\mcp.json`(Windows)
107
+ - **项目配置**(仅当前项目):项目根目录下的 `.cursor/mcp.json`
108
+
109
+ **步骤:**
110
+
111
+ 1. **确认已构建**
112
+ 在 `memorycode-mcp-server` 目录执行过 `npm run build`,且存在 `dist/index.js`。
113
+
114
+ 2. **确认 Node 路径**
115
+ 终端执行 `which node`(Windows 可用 `where node`),记下路径,例如 `/usr/local/bin/node`。
116
+
117
+ 3. **编辑 mcp.json(已验证格式)**
118
+ - 若文件不存在:在 `~/.cursor/` 下新建 `mcp.json`。
119
+ - 在 **`mcpServers`** 对象中增加 **`memorycode`** 一项;若已有其他 server(如 linear),保留它们,只在 **`mcpServers` 里**新增 `memorycode`。完整示例(仅 memorycode 时整文件如下):
120
+
121
+ ```json
122
+ {
123
+ "mcpServers": {
124
+ "memorycode": {
125
+ "type": "stdio",
126
+ "command": "/usr/local/bin/node",
127
+ "args": [
128
+ "/你的本机路径/memorycode-mcp-server/dist/index.js",
129
+ "--file",
130
+ "/你的本机路径/memorycode-mcp.json"
131
+ ]
132
+ }
133
+ }
134
+ }
135
+ ```
136
+
137
+ 若已有其他 MCP,则只在 `mcpServers` 中多加一段,例如:
138
+
139
+ ```json
140
+ {
141
+ "mcpServers": {
142
+ "linear": { "command": "npx", "args": ["-y", "mcp-remote", "https://mcp.linear.app/mcp"] },
143
+ "memorycode": {
144
+ "type": "stdio",
145
+ "command": "/usr/local/bin/node",
146
+ "args": [
147
+ "/你的本机路径/memorycode-mcp-server/dist/index.js",
148
+ "--file",
149
+ "/你的本机路径/memorycode-mcp.json"
150
+ ]
151
+ }
152
+ }
153
+ }
154
+ ```
155
+
156
+ **必填说明:**
157
+ - **`"type": "stdio"`**:Cursor 要求 STDIO 服务器必须写明,缺此项会无法正确识别。
158
+ - **`command`**:上一步得到的 node 可执行文件路径(如 `/usr/local/bin/node`)。
159
+ - **`args`**:严格为 3 项——① `dist/index.js` 的绝对路径;② `"--file"`;③ `memorycode-mcp.json` 的绝对路径。
160
+
161
+ 4. **(可选)使用变量便于多机共用**
162
+ Cursor 支持在 `mcp.json` 中使用 `${userHome}` 等变量,例如:
163
+ ```json
164
+ "args": [
165
+ "${userHome}/ai-identity-wallet/memorycode-mcp-server/dist/index.js",
166
+ "--file",
167
+ "${userHome}/Desktop/MCP/memorycode-mcp.json"
168
+ ]
169
+ ```
170
+ 路径按你本机实际目录调整。
171
+
172
+ 5. **重启 Cursor**
173
+ 保存 `mcp.json` 后完全退出 Cursor 再打开,使配置生效。
174
+
175
+ 6. **验证与使用**
176
+ - 打开 **Settings → Features → Model Context Protocol**,确认 `memorycode` 已列出且为开启状态,并能看到 `load_config`、`list_configs` 及你的配置名(如职场、创作者、学生等)。
177
+ - 在对话中用**自然语言**(如「请使用职场配置」「有哪些配置可用」)使用,或直接点选界面中展示的配置按钮。
178
+
179
+ **排查:** 若连接失败,打开 **Output → MCP Logs**(Cmd+Shift+U 后切换下拉)查看报错;常见原因:路径错误、未先执行 `npm run build`、漏写 `"type": "stdio"`、或将 `memorycode` 写到了 `mcpServers` 外面。
180
+
181
+ **若出现 "Error - Show Output" / "No server info found":** 多为 Cursor 与 MCP 的启动竞态。请:① 确认已用最新构建(在 `memorycode-mcp-server` 目录执行 `npm run build`);② 完全退出 Cursor 后重新打开;③ 确认 `mcp.json` 中 `memorycode` 在 `mcpServers` 内且含 `"type": "stdio"`。若仍报错,可尝试暂时关闭其他 MCP 仅保留 memorycode 再试。
182
+
183
+ **说明:** 应用内「完整教程」中有更详细的图文步骤,建议一并查看。
184
+
185
+ ### Windsurf(Codeium)
186
+
187
+ MemoryCode 应用内 MCP Connect 支持为 Windsurf 一键写入配置,配置文件路径为:
188
+
189
+ - macOS / Linux:`~/.codeium/windsurf/mcp_config.json`
190
+ - Windows:`%USERPROFILE%\.codeium\windsurf\mcp_config.json`
191
+
192
+ 写入内容使用 `command: "npx"`、`args: ["-y", "@memorycode/mcp-server"]`,并通过环境变量 `MEMORYCODE_FILE` 指定本机 `memorycode-mcp.json` 路径(默认 `~/memorycode/memorycode-mcp.json`)。Server 会优先读取该环境变量。
193
+
194
+ **若出现「MCP server initialization timed out」:**
195
+
196
+ 1. **确认配置文件存在**:先在 MemoryCode 中「导出 MCP 配置」或通过 MCP Connect 导出,确保 `~/memorycode/memorycode-mcp.json`(或你设置的路径)存在。
197
+ 2. **首次运行较慢**:`npx -y @memorycode/mcp-server` 第一次会拉取 npm 包,可能超过 60 秒。可在终端先执行一次以预拉取(将 `MEMORYCODE_FILE` 换成你的实际路径):
198
+ ```bash
199
+ MEMORYCODE_FILE=~/memorycode/memorycode-mcp.json npx -y @memorycode/mcp-server
200
+ ```
201
+ 看到 “MemoryCode MCP Server running” 后按 Ctrl+C 结束,再在 Windsurf 中重新加载 MCP。
202
+ 3. **若仍超时:改用本地 node 启动(推荐)**
203
+ 在项目内构建后,用 `node` 直接运行可执行文件,避免 npx 拉包与网络延迟,启动更快、不易超时。编辑 `~/.codeium/windsurf/mcp_config.json`,将 `memorycode` 一项改为(路径按本机实际修改):
204
+ ```json
205
+ "memorycode": {
206
+ "name": "memorycode",
207
+ "command": "/usr/local/bin/node",
208
+ "args": [
209
+ "/你的路径/ai-identity-wallet/memorycode-mcp-server/dist/index.js",
210
+ "--file",
211
+ "/你的路径/memorycode/memorycode-mcp.json"
212
+ ]
213
+ }
214
+ ```
215
+ 其中 `command` 填本机 node 路径(终端执行 `which node` 可得),`args[0]` 填 `memorycode-mcp-server/dist/index.js` 的绝对路径,`args[2]` 填 `memorycode-mcp.json` 的绝对路径。保存后重启 Windsurf。
216
+ 4. **检查路径**:在 Windsurf 的 memorycode 配置页中确认「Configuration File」显示的路径与 MemoryCode 中导出的文件路径一致。
217
+
218
+ **若 @memorycode 提示「MCP server not found / 服务器名称为空」:** 通常表示该 MCP 未成功启动或未在 Cascade 中正确注册。请先解决「initialization timed out」(按上三步),确保 memorycode 在「Installed MCPs」中为已启用且无报错;配置中已包含 `"name": "memorycode"` 时部分客户端会正确显示 ServerName。若为手动编辑的 `mcp_config.json`,请确保 `mcpServers.memorycode` 存在且含 `command`、`args`、`env`(及可选 `name: "memorycode"`)。
219
+
220
+ ---
221
+
222
+ ## 云盘同步(可选)
223
+
224
+ 若希望多台设备共用同一份配置,或将配置备份到云盘:
225
+
226
+ 1. 将 **`memorycode-mcp.json`** 放在云盘同步目录中(如 iCloud Drive、Dropbox、OneDrive 等)。
227
+ 2. 在本机启动 MCP Server 时,用 **`--file`** 指向该同步路径,例如:
228
+ ```bash
229
+ node dist/index.js --file "$HOME/Library/Mobile Documents/com~apple~CloudDocs/MemoryCode/memorycode-mcp.json"
230
+ ```
231
+ 3. 在 Claude Desktop / Cursor 的 MCP 配置里,`args` 中填写同一路径。
232
+
233
+ **注意:** 云盘冲突或同步延迟可能导致文件短暂不可用,若出现「加载 0 个资源」,请检查文件是否已同步完成、路径是否正确。
234
+
235
+ ---
236
+
237
+ ## Windsurf 问题与修复方向(参考)
238
+
239
+ 若后续需重新研究文档或调整实现,可参考以下结论:
240
+
241
+ **1. 应用内「测试连接」对 Windsurf 无反应**
242
+ - **原因**:前端 `handleTestConnection` 曾仅允许 `claude` / `cursor`,`toolId === 'windsurf'` 时直接 return,未发请求。
243
+ - **修复**:已去掉该限制,Windsurf 与 Claude/Cursor 共用同一测试连接逻辑;并增加 15 秒请求超时与 20 秒兜底清除,避免按钮长期处于「测试中…」。
244
+
245
+ **2. Windsurf 内 MCP「initialization timed out after 60 seconds」**
246
+ - **原因**:Cascade 用 stdio 启动我们配置的 command(默认 `npx -y @memorycode/mcp-server`)。首次运行 npx 会拉取 npm 包,网络或磁盘慢时易超过 Cascade 的 60 秒等待。
247
+ - **文档**:Windsurf 官方 [Cascade MCP](https://docs.windsurf.com/windsurf/cascade/mcp) 仅描述 `mcp_config.json` 的 command/args/env 格式,未写超时时间;实际观测为约 60 秒。
248
+ - **修复方向**:优先推荐用户改用**本地 node + 绝对路径**(见上方「若仍超时:改用本地 node 启动」),避免 npx;或先在终端预运行一次 npx 完成拉包后再在 Windsurf 中启用。后续若官方提供更长超时或「后台预加载」能力,可再对接。
249
+
250
+ ---
251
+
252
+ ## 故障排查
253
+
254
+ ### 1. MCP 开关无法打开 / 看不到 MemoryCode 配置
255
+
256
+ - **检查**:Claude Desktop 会请求 `tools/list`,Server 必须响应(本实现已支持)。
257
+ - **建议**:确认使用最新构建(`npm run build`),并完全退出后重新打开 Claude Desktop。
258
+
259
+ ### 2. 提示「Loaded 0 resources」
260
+
261
+ - **原因**:未找到文件,或文件不是 MemoryCode 导出的有效 JSON(需包含 `meta.origin_app === 'MemoryCode'` 且 `mcp.resources` 存在)。
262
+ - **处理**:确认 `--file` 路径正确;在 MemoryCode 中重新「导出到 MCP」并覆盖该文件。
263
+
264
+ ### 3. 自然语言不触发配置加载
265
+
266
+ - **原因**:需通过 Tool(`load_config`)实现,本 Server 已提供。
267
+ - **处理**:确认 MCP 连接正常(开关为开启状态);可说「请使用职场配置」「有哪些配置可用」等,由 Claude 调用对应 Tool。
268
+
269
+ ### 4. 第一次打开 Claude 白屏,第二次才正常
270
+
271
+ - **可能原因**:与 MCP 首次连接或其它扩展有关。
272
+ - **建议**:暂时移除 MemoryCode MCP 配置后重启,若不再白屏,则多为 MCP 启动顺序问题;可保留当前配置,后续启动通常正常。
273
+
274
+ ### 5. 修改 JSON 后列表未更新
275
+
276
+ - **说明**:Server 会监听文件变化并防抖约 500ms 后重载。
277
+ - **处理**:保存文件后稍等片刻;若仍不更新,可重启 MCP Server 或重启 Claude / Cursor。
278
+
279
+ ### 6. Windsurf 提示「MCP server initialization timed out after 60 seconds」
280
+
281
+ - **原因**:首次通过 `npx -y @memorycode/mcp-server` 启动时会从 npm 拉包,耗时可能超过 60 秒;或 `MEMORYCODE_FILE` 指向的文件不存在导致启动异常。
282
+ - **处理**:见上方 **Windsurf(Codeium)** 小节:先确保 `memorycode-mcp.json` 已导出并存在;可选在终端预运行一次 `npx -y @memorycode/mcp-server` 完成拉包后再在 Windsurf 中重试。
283
+
284
+ ---
285
+
286
+ ## 使用场景
287
+
288
+ - **Claude Desktop**:用**自然语言**(如「请使用职场配置」「有哪些配置可用」)由模型调用 Tool 加载;或点击输入框旁 **「+」**,在展开的菜单中选择 MemoryCode 记忆配置加载。@ 在桌面版中对 MCP 无作用。
289
+ - **其他客户端**(如 Cursor):以该客户端实际支持的引用方式为准(可能包含 @ 或菜单入口);自然语言在两端均可用。
290
+ - **多配置组合**:不同对话选用不同配置,或在同一对话中先选一个再自然语言切换另一个,按需切换身份/风格。
291
+ - **热更新**:在 MemoryCode 中调整配置后重新导出并覆盖 `memorycode-mcp.json`,Server 会自动重载,无需重启 Claude/Cursor。
292
+
293
+ ---
294
+
295
+ ## 开发
296
+
297
+ ```bash
298
+ npm run build # 编译到 dist/
299
+ npm run dev # 监听源码变更并重新编译
300
+ npm start # 运行 dist/index.js
301
+ npm test # 集成测试(导出→加载、热更新、错误恢复)
302
+ ```
303
+
304
+ 集成测试见 `tests/integration.test.mjs`;Cursor / Claude Desktop 的手动验证步骤见 **`tests/MANUAL.md`**。
305
+
306
+ ---
307
+
308
+ ## 相关链接
309
+
310
+ - 应用内 **完整教程**:安装、Cursor/Claude 配置、云盘同步、FAQ 等。
311
+ - Resource URI 格式:`memorycode://profile/{id}`。
312
+
313
+ ---
314
+
315
+ ## License
316
+
317
+ MIT
@@ -0,0 +1,22 @@
1
+ /**
2
+ * YIS-15 Task 2.3: 文件监听与热更新
3
+ * chokidar 监听单文件,防抖 500ms,解析 meta/mcp.resources,错误保留上次有效状态
4
+ */
5
+ import type { McpResource } from './types.js';
6
+ export interface FileWatcherOptions {
7
+ filePath: string;
8
+ onChange: (resources: McpResource[], payload: unknown) => void;
9
+ onError: (error: Error) => void;
10
+ }
11
+ export declare class FileWatcher {
12
+ private options;
13
+ private lastValidResources;
14
+ private debounceTimer;
15
+ private watcher;
16
+ constructor(options: FileWatcherOptions);
17
+ private scheduleReload;
18
+ private loadFile;
19
+ start(): Promise<void>;
20
+ stop(): void;
21
+ }
22
+ //# sourceMappingURL=fileWatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileWatcher.d.ts","sourceRoot":"","sources":["../src/fileWatcher.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAO9C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,CAAC,SAAS,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/D,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACjC;AAED,qBAAa,WAAW;IAKV,OAAO,CAAC,OAAO;IAJ3B,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,aAAa,CAA8C;IACnE,OAAO,CAAC,OAAO,CAAkD;gBAE7C,OAAO,EAAE,kBAAkB;IAE/C,OAAO,CAAC,cAAc;YAQR,QAAQ;IAoBhB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA6B5B,IAAI,IAAI,IAAI;CAUb"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * YIS-15 Task 2.3: 文件监听与热更新
3
+ * chokidar 监听单文件,防抖 500ms,解析 meta/mcp.resources,错误保留上次有效状态
4
+ */
5
+ import chokidar from 'chokidar';
6
+ import { readFile } from 'node:fs/promises';
7
+ import { isMemoryCodeExport, parseResources } from './utils/validator.js';
8
+ import { logInfo, logWarn } from './utils/logger.js';
9
+ const DEBOUNCE_MS = 500;
10
+ const INITIAL_LOAD_TIMEOUT_MS = 8000;
11
+ export class FileWatcher {
12
+ options;
13
+ lastValidResources = [];
14
+ debounceTimer = null;
15
+ watcher = null;
16
+ constructor(options) {
17
+ this.options = options;
18
+ }
19
+ scheduleReload() {
20
+ if (this.debounceTimer)
21
+ clearTimeout(this.debounceTimer);
22
+ this.debounceTimer = setTimeout(() => {
23
+ this.debounceTimer = null;
24
+ this.loadFile().catch(() => { });
25
+ }, DEBOUNCE_MS);
26
+ }
27
+ async loadFile() {
28
+ const { filePath, onChange, onError } = this.options;
29
+ try {
30
+ const content = await readFile(filePath, 'utf-8');
31
+ const data = JSON.parse(content);
32
+ if (!isMemoryCodeExport(data)) {
33
+ logWarn(`File is not a MemoryCode export (meta.origin_app): ${filePath}`);
34
+ return;
35
+ }
36
+ const resources = parseResources(data);
37
+ this.lastValidResources = resources;
38
+ onChange(resources, data);
39
+ logInfo(`Loaded ${resources.length} resources from ${filePath}`);
40
+ }
41
+ catch (err) {
42
+ const error = err instanceof Error ? err : new Error(String(err));
43
+ onError(error);
44
+ logWarn(`Failed to load ${filePath}, keeping previous ${this.lastValidResources.length} resources`);
45
+ }
46
+ }
47
+ async start() {
48
+ const { filePath, onChange } = this.options;
49
+ const loadWithTimeout = () => Promise.race([
50
+ this.loadFile(),
51
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Initial load timeout')), INITIAL_LOAD_TIMEOUT_MS)),
52
+ ]).catch((err) => {
53
+ this.options.onError(err instanceof Error ? err : new Error(String(err)));
54
+ });
55
+ await loadWithTimeout();
56
+ this.watcher = chokidar.watch(filePath, {
57
+ persistent: true,
58
+ ignoreInitial: true,
59
+ });
60
+ this.watcher.on('add', () => this.scheduleReload());
61
+ this.watcher.on('change', () => this.scheduleReload());
62
+ this.watcher.on('unlink', () => {
63
+ this.lastValidResources = [];
64
+ onChange([], null);
65
+ logInfo(`File removed, cleared resources: ${filePath}`);
66
+ });
67
+ logInfo(`Watching ${filePath} (debounce ${DEBOUNCE_MS}ms)`);
68
+ }
69
+ stop() {
70
+ if (this.debounceTimer) {
71
+ clearTimeout(this.debounceTimer);
72
+ this.debounceTimer = null;
73
+ }
74
+ if (this.watcher) {
75
+ this.watcher.close();
76
+ this.watcher = null;
77
+ }
78
+ }
79
+ }
80
+ //# sourceMappingURL=fileWatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileWatcher.js","sourceRoot":"","sources":["../src/fileWatcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * YIS-15: MemoryCode MCP Server 入口
4
+ * Task 2.1 项目初始化;Task 2.4 完善 CLI 与配置
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG"}
package/dist/index.js ADDED
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * YIS-15: MemoryCode MCP Server 入口
4
+ * Task 2.1 项目初始化;Task 2.4 完善 CLI 与配置
5
+ */
6
+ import path from 'node:path';
7
+ import os from 'node:os';
8
+ import { MemoryCodeMCPServer } from './server.js';
9
+ import { FileWatcher } from './fileWatcher.js';
10
+ import { setLogLevel } from './utils/logger.js';
11
+ const DEFAULT_WATCH_FILE = path.join(os.homedir(), 'memorycode', 'memorycode-mcp.json');
12
+ function printHelp() {
13
+ const defaultPath = DEFAULT_WATCH_FILE;
14
+ console.error(`MemoryCode MCP Server
15
+
16
+ Usage: memorycode-mcp-server [options]
17
+
18
+ Options:
19
+ -f, --file <path> Path to memorycode-mcp.json (default: ${defaultPath})
20
+ -c, --config <path> Path to JSON config file (watchFile, logLevel)
21
+ -h, --help Show this help
22
+
23
+ Config file example:
24
+ { "watchFile": "/path/to/memorycode-mcp.json", "logLevel": "info" }
25
+ `);
26
+ }
27
+ function parseArgs() {
28
+ const args = process.argv.slice(2);
29
+ let watchFile = null;
30
+ let configFile = null;
31
+ let help = false;
32
+ for (let i = 0; i < args.length; i++) {
33
+ if (args[i] === '--file' || args[i] === '-f') {
34
+ watchFile = args[i + 1] ?? null;
35
+ i++;
36
+ }
37
+ else if (args[i] === '--config' || args[i] === '-c') {
38
+ configFile = args[i + 1] ?? null;
39
+ i++;
40
+ }
41
+ else if (args[i] === '--help' || args[i] === '-h') {
42
+ help = true;
43
+ }
44
+ }
45
+ return { watchFile, configFile, help };
46
+ }
47
+ function resolveWatchPath(raw) {
48
+ if (raw.startsWith('~/') || raw === '~') {
49
+ return path.join(os.homedir(), raw === '~' ? '' : raw.slice(2));
50
+ }
51
+ if (raw.startsWith('~' + path.sep)) {
52
+ return path.join(os.homedir(), raw.slice(2));
53
+ }
54
+ return raw;
55
+ }
56
+ async function loadConfig() {
57
+ const { watchFile, configFile } = parseArgs();
58
+ const envFile = process.env.MEMORYCODE_FILE?.trim();
59
+ const rawWatchFile = watchFile ?? (envFile && envFile.length > 0 ? envFile : null) ?? DEFAULT_WATCH_FILE;
60
+ const resolvedWatchFile = resolveWatchPath(rawWatchFile);
61
+ let config = {
62
+ watchFile: resolvedWatchFile,
63
+ logLevel: 'info',
64
+ };
65
+ if (configFile) {
66
+ try {
67
+ const fs = await import('node:fs/promises');
68
+ const content = await fs.readFile(configFile, 'utf-8');
69
+ const parsed = JSON.parse(content);
70
+ if (parsed.watchFile != null && watchFile === null)
71
+ config.watchFile = resolveWatchPath(String(parsed.watchFile));
72
+ if (parsed.logLevel)
73
+ config.logLevel = parsed.logLevel;
74
+ }
75
+ catch (err) {
76
+ console.error('Failed to load config file:', err);
77
+ }
78
+ }
79
+ return config;
80
+ }
81
+ async function main() {
82
+ if (parseArgs().help) {
83
+ printHelp();
84
+ process.exit(0);
85
+ }
86
+ const config = await loadConfig();
87
+ setLogLevel(config.logLevel);
88
+ const server = new MemoryCodeMCPServer();
89
+ const watcher = new FileWatcher({
90
+ filePath: config.watchFile,
91
+ onChange: (resources, payload) => server.updateMemorySnapshot(resources, payload),
92
+ onError: (err) => console.error('FileWatcher error:', err),
93
+ });
94
+ // 先连接 stdio 再启动 watcher,让 Cursor 等客户端尽快收到 initialize 响应,减少 "No server info found" 竞态
95
+ await server.run();
96
+ await watcher.start();
97
+ }
98
+ main().catch((err) => {
99
+ console.error(err);
100
+ process.exit(1);
101
+ });
102
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * YIS-15 Task 2.2 / YIS-20 / YIS-21: MCP Server 实现
3
+ * YIS-22: 添加 load_config、list_configs Tool,支持自然语言唤起配置切换
4
+ */
5
+ import type { McpResource } from './types.js';
6
+ export declare class MemoryCodeMCPServer {
7
+ private resources;
8
+ private memorySnapshot;
9
+ private server;
10
+ private transport;
11
+ constructor();
12
+ updateMemorySnapshot(resources: McpResource[], payload: unknown): void;
13
+ private getUserProfile;
14
+ private getExpertise;
15
+ private getActiveProfile;
16
+ private asRecord;
17
+ private toSafeString;
18
+ private toSafeStringArray;
19
+ private toDepthValue;
20
+ private mapDepth;
21
+ private findResourceByConfigName;
22
+ private normalizeConfigName;
23
+ run(): Promise<void>;
24
+ }
25
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA6E9C,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAqC;;IAoItD,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAQtE,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,YAAY;IA0BpB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,QAAQ;IAMhB,OAAO,CAAC,wBAAwB;IAUhC,OAAO,CAAC,mBAAmB;IAOrB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAK3B"}
package/dist/server.js ADDED
@@ -0,0 +1,277 @@
1
+ /**
2
+ * YIS-15 Task 2.2 / YIS-20 / YIS-21: MCP Server 实现
3
+ * YIS-22: 添加 load_config、list_configs Tool,支持自然语言唤起配置切换
4
+ */
5
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
6
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
7
+ import { ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListToolsRequestSchema, CallToolRequestSchema, ReadResourceRequestSchema, McpError, ErrorCode, } from '@modelcontextprotocol/sdk/types.js';
8
+ import { logInfo, logWarn } from './utils/logger.js';
9
+ const RESOURCE_URI_PREFIX = 'memorycode://profile/';
10
+ const MIME_TYPE = 'text/markdown';
11
+ const TOOLS = [
12
+ {
13
+ name: 'load_config',
14
+ description: '加载指定的 MemoryCode 记忆配置,使后续回复使用该身份/风格。用户说「请使用职场配置」「切换到学生模式」等时调用。',
15
+ inputSchema: {
16
+ type: 'object',
17
+ properties: {
18
+ config_name: {
19
+ type: 'string',
20
+ description: '要加载的配置名称,如:空白、职场、创作者、自由职业、学生(以当前可用列表为准)',
21
+ },
22
+ },
23
+ required: ['config_name'],
24
+ },
25
+ },
26
+ {
27
+ name: 'list_configs',
28
+ description: '列出当前可用的所有 MemoryCode 记忆配置名称,供用户选择或确认。',
29
+ inputSchema: {
30
+ type: 'object',
31
+ properties: {},
32
+ },
33
+ },
34
+ {
35
+ name: 'get_user_profile',
36
+ description: 'Get current user identity profile from MemoryCode (nickname, roles, about). Use this when personalization needs user background.',
37
+ inputSchema: {
38
+ type: 'object',
39
+ properties: {},
40
+ },
41
+ },
42
+ {
43
+ name: 'get_expertise',
44
+ description: 'Get current user expertise from MemoryCode. Optional domain filters skills by case-insensitive substring match.',
45
+ inputSchema: {
46
+ type: 'object',
47
+ properties: {
48
+ domain: {
49
+ type: 'string',
50
+ description: 'Optional domain keyword, e.g. frontend, design.',
51
+ },
52
+ },
53
+ },
54
+ },
55
+ ];
56
+ export class MemoryCodeMCPServer {
57
+ resources = [];
58
+ memorySnapshot = null;
59
+ server;
60
+ transport = null;
61
+ constructor() {
62
+ this.server = new Server({
63
+ name: 'memorycode',
64
+ version: '1.0.0',
65
+ }, {
66
+ capabilities: {
67
+ resources: {
68
+ listChanged: true,
69
+ subscribe: true,
70
+ },
71
+ tools: { listChanged: true },
72
+ },
73
+ /** Cursor 等客户端通过 GetInstructions/initialize 获取此说明,避免 "No server info found" 类问题 */
74
+ instructions: [
75
+ 'MemoryCode MCP: 提供记忆配置供 AI 使用。',
76
+ 'Resources: 通过 @ 或资源列表加载 memorycode://profile/<id> 获取某配置的完整 prompt。',
77
+ 'Tools: list_configs 列出可用配置;load_config 加载指定配置名称(如「职场」「创作者」)使后续回复使用该身份。',
78
+ 'Tools: get_user_profile 返回当前身份信息;get_expertise 支持按 domain 过滤技能深度。',
79
+ ].join(' '),
80
+ });
81
+ this.server.setRequestHandler(ListToolsRequestSchema, () => ({
82
+ tools: TOOLS.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })),
83
+ }));
84
+ this.server.setRequestHandler(CallToolRequestSchema, (request) => {
85
+ const { name, arguments: args } = request.params;
86
+ if (name === 'load_config') {
87
+ const configName = typeof args?.config_name === 'string' ? args.config_name.trim() : '';
88
+ if (!configName) {
89
+ return {
90
+ content: [{ type: 'text', text: '请提供 config_name 参数,例如:职场、创作者、学生。' }],
91
+ isError: true,
92
+ };
93
+ }
94
+ const resource = this.findResourceByConfigName(configName);
95
+ if (!resource) {
96
+ const names = this.resources.map((r) => r.name).join('、') || '(暂无)';
97
+ return {
98
+ content: [
99
+ {
100
+ type: 'text',
101
+ text: `未找到配置「${configName}」。当前可用:${names}`,
102
+ },
103
+ ],
104
+ isError: true,
105
+ };
106
+ }
107
+ return {
108
+ content: [
109
+ {
110
+ type: 'text',
111
+ text: `已加载配置:${resource.name}\n\n${resource.prompt}`,
112
+ },
113
+ ],
114
+ };
115
+ }
116
+ if (name === 'list_configs') {
117
+ const names = this.resources.map((r) => r.name);
118
+ const text = names.length > 0
119
+ ? `当前可用配置:\n${names.map((c) => `- ${c}`).join('\n')}`
120
+ : '当前暂无可用配置,请先在 MemoryCode 中导出 memorycode-mcp.json 并放在 MCP 监听的路径。';
121
+ return {
122
+ content: [{ type: 'text', text }],
123
+ };
124
+ }
125
+ if (name === 'get_user_profile') {
126
+ const result = this.getUserProfile();
127
+ return {
128
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
129
+ };
130
+ }
131
+ if (name === 'get_expertise') {
132
+ const domain = typeof args?.domain === 'string' ? args.domain : undefined;
133
+ const result = this.getExpertise(domain);
134
+ return {
135
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
136
+ };
137
+ }
138
+ throw new McpError(ErrorCode.InvalidParams, `Unknown tool: ${name}`);
139
+ });
140
+ this.server.setRequestHandler(ListResourcesRequestSchema, (_request) => {
141
+ const resources = this.resources.map((r) => {
142
+ const desc = r.description || r.name;
143
+ const description = desc.startsWith('Memory - ') ? desc : `Memory - ${desc}`;
144
+ return {
145
+ uri: RESOURCE_URI_PREFIX + encodeURIComponent(r.id),
146
+ name: r.name,
147
+ description,
148
+ mimeType: MIME_TYPE,
149
+ };
150
+ });
151
+ return { resources };
152
+ });
153
+ this.server.setRequestHandler(ListResourceTemplatesRequestSchema, () => {
154
+ return { resourceTemplates: [] };
155
+ });
156
+ this.server.setRequestHandler(ReadResourceRequestSchema, (request) => {
157
+ const uri = request.params.uri;
158
+ if (!uri.startsWith(RESOURCE_URI_PREFIX)) {
159
+ throw new McpError(ErrorCode.InvalidParams, `Invalid resource URI: ${uri}`);
160
+ }
161
+ const id = decodeURIComponent(uri.slice(RESOURCE_URI_PREFIX.length));
162
+ const resource = this.resources.find((r) => r.id === id);
163
+ if (!resource) {
164
+ throw new McpError(ErrorCode.InvalidParams, `Resource not found: ${uri}`);
165
+ }
166
+ return {
167
+ contents: [
168
+ {
169
+ uri,
170
+ mimeType: MIME_TYPE,
171
+ text: resource.prompt,
172
+ },
173
+ ],
174
+ };
175
+ });
176
+ logInfo('[MemoryCode] MCP Server initialized, capabilities: resources (listChanged, subscribe), tools (load_config, list_configs, get_user_profile, get_expertise)');
177
+ }
178
+ updateMemorySnapshot(resources, payload) {
179
+ this.resources = resources;
180
+ this.memorySnapshot = payload;
181
+ this.server.sendResourceListChanged().catch((err) => {
182
+ logWarn(`Failed to send resources/list_changed: ${err}`);
183
+ });
184
+ }
185
+ getUserProfile() {
186
+ const profile = this.getActiveProfile();
187
+ return {
188
+ nickname: this.toSafeString(profile?.nickname),
189
+ roles: this.toSafeStringArray(profile?.roles),
190
+ about: this.toSafeString(profile?.role),
191
+ source: 'memorycode-local',
192
+ schema_version: '1.0',
193
+ };
194
+ }
195
+ getExpertise(domainRaw) {
196
+ const profile = this.getActiveProfile();
197
+ const domain = typeof domainRaw === 'string' ? domainRaw.trim().toLowerCase() : '';
198
+ const skills = Array.isArray(profile?.skills) ? profile.skills : [];
199
+ const expertise = skills
200
+ .map((item) => {
201
+ const skill = this.toSafeString(item?.name);
202
+ const depthValue = this.toDepthValue(item?.depth);
203
+ if (!skill)
204
+ return null;
205
+ if (domain && !skill.toLowerCase().includes(domain))
206
+ return null;
207
+ const depth = this.mapDepth(depthValue);
208
+ return {
209
+ skill,
210
+ depth,
211
+ depthLabel: depth,
212
+ };
213
+ })
214
+ .filter((item) => item !== null);
215
+ return {
216
+ expertise,
217
+ total: expertise.length,
218
+ source: 'memorycode-local',
219
+ };
220
+ }
221
+ getActiveProfile() {
222
+ if (!this.memorySnapshot || typeof this.memorySnapshot !== 'object')
223
+ return null;
224
+ const root = this.memorySnapshot;
225
+ const data = this.asRecord(root.data);
226
+ const state = this.asRecord(data?.state);
227
+ const profiles = Array.isArray(state?.profiles) ? state?.profiles : [];
228
+ const activeProfileId = this.toSafeString(state?.activeProfileId);
229
+ if (!activeProfileId)
230
+ return null;
231
+ const profile = profiles.find((item) => this.toSafeString(this.asRecord(item)?.id) === activeProfileId);
232
+ return this.asRecord(profile);
233
+ }
234
+ asRecord(v) {
235
+ return v && typeof v === 'object' ? v : null;
236
+ }
237
+ toSafeString(v) {
238
+ return typeof v === 'string' ? v : '';
239
+ }
240
+ toSafeStringArray(v) {
241
+ if (!Array.isArray(v))
242
+ return [];
243
+ return v.filter((item) => typeof item === 'string');
244
+ }
245
+ toDepthValue(v) {
246
+ return v === 2 || v === 3 ? v : 1;
247
+ }
248
+ mapDepth(depth) {
249
+ if (depth === 3)
250
+ return 'expert';
251
+ if (depth === 2)
252
+ return 'proficient';
253
+ return 'familiar';
254
+ }
255
+ findResourceByConfigName(configName) {
256
+ const directMatch = this.resources.find((r) => r.name === configName || r.name.trim() === configName);
257
+ if (directMatch)
258
+ return directMatch;
259
+ // 容错匹配:允许用户输入去掉 emoji/符号后的名称(如「职场」匹配「💼 职场」)
260
+ const normalizedInput = this.normalizeConfigName(configName);
261
+ if (!normalizedInput)
262
+ return undefined;
263
+ return this.resources.find((r) => this.normalizeConfigName(r.name) === normalizedInput);
264
+ }
265
+ normalizeConfigName(name) {
266
+ return name
267
+ .trim()
268
+ .toLowerCase()
269
+ .replace(/[^\p{L}\p{N}\p{Script=Han}]+/gu, '');
270
+ }
271
+ async run() {
272
+ this.transport = new StdioServerTransport();
273
+ await this.server.connect(this.transport);
274
+ logInfo('MemoryCode MCP Server running (stdio)');
275
+ }
276
+ }
277
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * YIS-15: MCP Server 类型定义
3
+ * 与 Web 端导出的 mcp.resources[] 结构一致
4
+ */
5
+ export interface McpResource {
6
+ id: string;
7
+ name: string;
8
+ description: string;
9
+ prompt: string;
10
+ }
11
+ export interface MemoryCodeExportJson {
12
+ meta?: {
13
+ origin_app?: string;
14
+ schema_version?: string;
15
+ };
16
+ mcp?: {
17
+ schema_version?: string;
18
+ default_model?: string;
19
+ resources?: McpResourceRaw[];
20
+ };
21
+ }
22
+ export interface McpResourceRaw {
23
+ id?: string;
24
+ name?: string;
25
+ description?: string;
26
+ prompt?: string;
27
+ }
28
+ export interface Config {
29
+ watchFile: string;
30
+ logLevel: 'info' | 'warn' | 'error';
31
+ }
32
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE;QACL,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,GAAG,CAAC,EAAE;QACJ,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,SAAS,CAAC,EAAE,cAAc,EAAE,CAAC;KAC9B,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,MAAM;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;CACrC"}
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * YIS-15: MCP Server 类型定义
3
+ * 与 Web 端导出的 mcp.resources[] 结构一致
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * YIS-15: 简单日志工具,输出到 stderr
3
+ */
4
+ export type LogLevel = 'info' | 'warn' | 'error';
5
+ export declare function setLogLevel(level: LogLevel): void;
6
+ export declare function getLogLevel(): LogLevel;
7
+ export declare function logInfo(message: string): void;
8
+ export declare function logWarn(message: string): void;
9
+ export declare function logError(message: string, err?: unknown): void;
10
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAIjD,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAEjD;AAED,wBAAgB,WAAW,IAAI,QAAQ,CAEtC;AAuBD,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAI7C;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAI7C;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,GAAG,IAAI,CAO7D"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * YIS-15: 简单日志工具,输出到 stderr
3
+ */
4
+ let currentLevel = 'info';
5
+ export function setLogLevel(level) {
6
+ currentLevel = level;
7
+ }
8
+ export function getLogLevel() {
9
+ return currentLevel;
10
+ }
11
+ function levelPriority(level) {
12
+ switch (level) {
13
+ case 'error':
14
+ return 3;
15
+ case 'warn':
16
+ return 2;
17
+ case 'info':
18
+ default:
19
+ return 1;
20
+ }
21
+ }
22
+ function shouldLog(level) {
23
+ return levelPriority(level) >= levelPriority(currentLevel);
24
+ }
25
+ function format(level, message) {
26
+ const ts = new Date().toISOString();
27
+ return `[${ts}] ${message}`;
28
+ }
29
+ export function logInfo(message) {
30
+ if (shouldLog('info')) {
31
+ console.error(format('info', message));
32
+ }
33
+ }
34
+ export function logWarn(message) {
35
+ if (shouldLog('warn')) {
36
+ console.error(format('warn', message));
37
+ }
38
+ }
39
+ export function logError(message, err) {
40
+ if (shouldLog('error')) {
41
+ console.error(format('error', message));
42
+ if (err !== undefined) {
43
+ console.error(err);
44
+ }
45
+ }
46
+ }
47
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,IAAI,YAAY,GAAa,MAAM,CAAC;AAEpC,MAAM,UAAU,WAAW,CAAC,KAAe;IACzC,YAAY,GAAG,KAAK,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,SAAS,aAAa,CAAC,KAAe;IACpC,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,OAAO;YACV,OAAO,CAAC,CAAC;QACX,KAAK,MAAM;YACT,OAAO,CAAC,CAAC;QACX,KAAK,MAAM,CAAC;QACZ;YACE,OAAO,CAAC,CAAC;IACb,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,KAAe;IAChC,OAAO,aAAa,CAAC,KAAK,CAAC,IAAI,aAAa,CAAC,YAAY,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,MAAM,CAAC,KAAa,EAAE,OAAe;IAC5C,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACpC,OAAO,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,OAAe;IACrC,IAAI,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,OAAe;IACrC,IAAI,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,GAAa;IACrD,IAAI,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QACxC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * YIS-15: JSON 校验与 mcp.resources 解析
3
+ * PRD 6.4: meta.origin_app 不匹配则忽略;超长 prompt 跳过
4
+ */
5
+ import type { McpResource, MemoryCodeExportJson } from '../types.js';
6
+ export declare function isMemoryCodeExport(data: unknown): data is MemoryCodeExportJson;
7
+ export declare function parseResources(data: MemoryCodeExportJson): McpResource[];
8
+ //# sourceMappingURL=validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/utils/validator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAkB,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAMrF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,oBAAoB,CAK9E;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,oBAAoB,GAAG,WAAW,EAAE,CAyBxE"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * YIS-15: JSON 校验与 mcp.resources 解析
3
+ * PRD 6.4: meta.origin_app 不匹配则忽略;超长 prompt 跳过
4
+ */
5
+ import { logWarn } from './logger.js';
6
+ const MAX_PROMPT_LENGTH = 10_000;
7
+ const ORIGIN_APP = 'MemoryCode';
8
+ export function isMemoryCodeExport(data) {
9
+ if (!data || typeof data !== 'object')
10
+ return false;
11
+ const obj = data;
12
+ const meta = obj.meta;
13
+ return meta?.origin_app === ORIGIN_APP;
14
+ }
15
+ export function parseResources(data) {
16
+ const rawList = data.mcp?.resources;
17
+ if (!Array.isArray(rawList))
18
+ return [];
19
+ const result = [];
20
+ for (const r of rawList) {
21
+ const item = r;
22
+ if (!item?.id || typeof item.name !== 'string')
23
+ continue;
24
+ const prompt = typeof item.prompt === 'string' ? item.prompt : '';
25
+ if (prompt.length > MAX_PROMPT_LENGTH) {
26
+ logWarn(`Profile "${item.name}" prompt too long (${prompt.length} chars), skipped`);
27
+ continue;
28
+ }
29
+ result.push({
30
+ id: String(item.id),
31
+ name: String(item.name),
32
+ description: typeof item.description === 'string' ? item.description : '',
33
+ prompt,
34
+ });
35
+ }
36
+ return result;
37
+ }
38
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/utils/validator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AACjC,MAAM,UAAU,GAAG,YAAY,CAAC;AAEhC,MAAM,UAAU,kBAAkB,CAAC,IAAa;IAC9C,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACpD,MAAM,GAAG,GAAG,IAA+B,CAAC;IAC5C,MAAM,IAAI,GAAG,GAAG,CAAC,IAA2C,CAAC;IAC7D,OAAO,IAAI,EAAE,UAAU,KAAK,UAAU,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAA0B;IACvD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC;IACpC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IAEvC,MAAM,MAAM,GAAkB,EAAE,CAAC;IAEjC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,CAAmB,CAAC;QACjC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ;YAAE,SAAS;QAEzD,MAAM,MAAM,GAAG,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QAClE,IAAI,MAAM,CAAC,MAAM,GAAG,iBAAiB,EAAE,CAAC;YACtC,OAAO,CAAC,YAAY,IAAI,CAAC,IAAI,sBAAsB,MAAM,CAAC,MAAM,kBAAkB,CAAC,CAAC;YACpF,SAAS;QACX,CAAC;QAED,MAAM,CAAC,IAAI,CAAC;YACV,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YACnB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;YACvB,WAAW,EAAE,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE;YACzE,MAAM;SACP,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@memorycode/mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "MCP Server for MemoryCode - Use your memory configs in Claude Desktop/Cursor",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "memorycode-mcp-server": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "dev": "tsc --watch",
19
+ "start": "node dist/index.js",
20
+ "test": "node --test tests/integration.test.mjs",
21
+ "test:integration": "npm run build && node --test tests/integration.test.mjs"
22
+ },
23
+ "engines": {
24
+ "node": ">=18.0.0"
25
+ },
26
+ "dependencies": {
27
+ "@modelcontextprotocol/sdk": "^1.0.0",
28
+ "chokidar": "^3.5.3"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^20.0.0",
32
+ "typescript": "^5.0.0"
33
+ },
34
+ "keywords": ["mcp", "memorycode", "cursor", "claude", "model-context-protocol"],
35
+ "license": "MIT"
36
+ }