oh-memos-mcp 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/.env.example +27 -0
- package/README.md +71 -0
- package/dist/api-client.d.ts +22 -0
- package/dist/api-client.js +127 -0
- package/dist/config.d.ts +30 -0
- package/dist/config.js +163 -0
- package/dist/cube-manager.d.ts +16 -0
- package/dist/cube-manager.js +357 -0
- package/dist/formatters.d.ts +16 -0
- package/dist/formatters.js +174 -0
- package/dist/handlers/admin.d.ts +13 -0
- package/dist/handlers/admin.js +331 -0
- package/dist/handlers/calendar.d.ts +8 -0
- package/dist/handlers/calendar.js +281 -0
- package/dist/handlers/graph.d.ts +11 -0
- package/dist/handlers/graph.js +502 -0
- package/dist/handlers/index.d.ts +7 -0
- package/dist/handlers/index.js +71 -0
- package/dist/handlers/memory.d.ts +12 -0
- package/dist/handlers/memory.js +270 -0
- package/dist/handlers/search.d.ts +11 -0
- package/dist/handlers/search.js +334 -0
- package/dist/handlers/utils.d.ts +22 -0
- package/dist/handlers/utils.js +75 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +18 -0
- package/dist/keyword-enhancer.d.ts +18 -0
- package/dist/keyword-enhancer.js +335 -0
- package/dist/memory-analysis.d.ts +7 -0
- package/dist/memory-analysis.js +45 -0
- package/dist/models.d.ts +21 -0
- package/dist/models.js +101 -0
- package/dist/query-processing.d.ts +20 -0
- package/dist/query-processing.js +265 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.js +114 -0
- package/dist/tools-registry.d.ts +303 -0
- package/dist/tools-registry.js +369 -0
- package/dist/types.d.ts +118 -0
- package/dist/types.js +5 -0
- package/package.json +53 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query Processing Module
|
|
3
|
+
*
|
|
4
|
+
* Intent detection, keyword extraction, reranking, type extraction.
|
|
5
|
+
*/
|
|
6
|
+
import type { SearchData, MemoryNode, QueryIntent } from "./types.js";
|
|
7
|
+
export declare const INTENT_TO_GRAPHS: Record<string, string[]>;
|
|
8
|
+
export declare function detectQueryIntent(query: string): QueryIntent;
|
|
9
|
+
export declare function getGraphsForIntent(intent: QueryIntent): string[];
|
|
10
|
+
export declare function getIntentDescription(intent: QueryIntent): string;
|
|
11
|
+
export declare function parseMemoryTypePrefix(query: string): [string | null, string];
|
|
12
|
+
export declare function extractKeywords(query: string): string[];
|
|
13
|
+
export declare function keywordMatchScore(text: string, keywords: string[], metadata?: Record<string, unknown>, enableFuzzy?: boolean): number;
|
|
14
|
+
export declare function applyKeywordRerank(data: SearchData, query: string, enableFuzzy?: boolean): SearchData;
|
|
15
|
+
export declare function filterEdgesByIntent(data: SearchData, intent: QueryIntent): SearchData;
|
|
16
|
+
export declare function filterMemoriesByType(data: SearchData, memType: string | null): SearchData;
|
|
17
|
+
export declare function extractMemoriesFromResponse(data: SearchData): MemoryNode[];
|
|
18
|
+
export declare function extractMcpType(memory: MemoryNode | Record<string, unknown>): string;
|
|
19
|
+
export declare function computeMemoryStats(data: SearchData): [Record<string, number>, number];
|
|
20
|
+
//# sourceMappingURL=query-processing.d.ts.map
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query Processing Module
|
|
3
|
+
*
|
|
4
|
+
* Intent detection, keyword extraction, reranking, type extraction.
|
|
5
|
+
*/
|
|
6
|
+
import { MEMORY_TYPES } from "./config.js";
|
|
7
|
+
import { ALL_STOPWORDS, extractKeywordsEnhanced, keywordMatchScoreEnhanced } from "./keyword-enhancer.js";
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Intent to Graph Type Mapping
|
|
10
|
+
// ============================================================================
|
|
11
|
+
export const INTENT_TO_GRAPHS = {
|
|
12
|
+
causal: ["CAUSE", "CONDITION"],
|
|
13
|
+
related: ["RELATE"],
|
|
14
|
+
conflict: ["CONFLICT"],
|
|
15
|
+
temporal: ["FOLLOWS"],
|
|
16
|
+
default: ["CAUSE", "RELATE", "CONDITION"],
|
|
17
|
+
};
|
|
18
|
+
export function detectQueryIntent(query) {
|
|
19
|
+
const q = query.toLowerCase();
|
|
20
|
+
const causalPatterns = [
|
|
21
|
+
/为什么/, /why/, /原因/, /cause/, /导致/, /因为/,
|
|
22
|
+
/根本原因/, /root\s*cause/, /怎么.*出错/, /how.*fail/,
|
|
23
|
+
/什么.*引起/, /what.*caused/, /出了什么问题/,
|
|
24
|
+
];
|
|
25
|
+
if (causalPatterns.some((p) => p.test(q)))
|
|
26
|
+
return "causal";
|
|
27
|
+
const conflictPatterns = [
|
|
28
|
+
/冲突/, /conflict/, /矛盾/, /contradict/, /不一致/,
|
|
29
|
+
/inconsisten/, /问题.*之间/, /clash/,
|
|
30
|
+
];
|
|
31
|
+
if (conflictPatterns.some((p) => p.test(q)))
|
|
32
|
+
return "conflict";
|
|
33
|
+
const temporalPatterns = [
|
|
34
|
+
/什么时候/, /when/, /之前/, /before/, /之后/, /after/,
|
|
35
|
+
/最近/, /recent/, /先.*后/, /顺序/, /timeline/, /历史/,
|
|
36
|
+
/上次/, /last\s*time/, /earlier/, /previously/,
|
|
37
|
+
];
|
|
38
|
+
if (temporalPatterns.some((p) => p.test(q)))
|
|
39
|
+
return "temporal";
|
|
40
|
+
const relatedPatterns = [
|
|
41
|
+
/相关/, /related/, /关联/, /有关/, /涉及/,
|
|
42
|
+
/association/, /connect/, /link/,
|
|
43
|
+
];
|
|
44
|
+
if (relatedPatterns.some((p) => p.test(q)))
|
|
45
|
+
return "related";
|
|
46
|
+
return "default";
|
|
47
|
+
}
|
|
48
|
+
export function getGraphsForIntent(intent) {
|
|
49
|
+
return INTENT_TO_GRAPHS[intent] ?? INTENT_TO_GRAPHS.default;
|
|
50
|
+
}
|
|
51
|
+
export function getIntentDescription(intent) {
|
|
52
|
+
const descriptions = {
|
|
53
|
+
causal: "🔍 因果分析 (Causal Analysis)",
|
|
54
|
+
related: "🔗 关联查询 (Related Search)",
|
|
55
|
+
conflict: "⚠️ 冲突检测 (Conflict Detection)",
|
|
56
|
+
temporal: "📅 时序查询 (Temporal Search)",
|
|
57
|
+
default: "📚 综合查询 (General Search)",
|
|
58
|
+
};
|
|
59
|
+
return descriptions[intent] ?? descriptions.default;
|
|
60
|
+
}
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// Memory Type Prefix Parsing
|
|
63
|
+
// ============================================================================
|
|
64
|
+
export function parseMemoryTypePrefix(query) {
|
|
65
|
+
if (!query)
|
|
66
|
+
return [null, ""];
|
|
67
|
+
const m = query.match(/^\s*\[?([A-Z_]+)\]?\s*[:\-]?\s*(.*)$/);
|
|
68
|
+
if (!m)
|
|
69
|
+
return [null, query.trim()];
|
|
70
|
+
const memType = m[1];
|
|
71
|
+
const rest = (m[2] ?? "").trim();
|
|
72
|
+
if (MEMORY_TYPES.has(memType)) {
|
|
73
|
+
return [memType, rest];
|
|
74
|
+
}
|
|
75
|
+
return [null, query.trim()];
|
|
76
|
+
}
|
|
77
|
+
// ============================================================================
|
|
78
|
+
// Keyword Extraction
|
|
79
|
+
// ============================================================================
|
|
80
|
+
export function extractKeywords(query) {
|
|
81
|
+
return extractKeywordsEnhanced(query, ALL_STOPWORDS);
|
|
82
|
+
}
|
|
83
|
+
// ============================================================================
|
|
84
|
+
// Keyword Match Score
|
|
85
|
+
// ============================================================================
|
|
86
|
+
export function keywordMatchScore(text, keywords, metadata, enableFuzzy = true) {
|
|
87
|
+
return keywordMatchScoreEnhanced(text, keywords, metadata, enableFuzzy);
|
|
88
|
+
}
|
|
89
|
+
// ============================================================================
|
|
90
|
+
// Reranking
|
|
91
|
+
// ============================================================================
|
|
92
|
+
export function applyKeywordRerank(data, query, enableFuzzy = true) {
|
|
93
|
+
const keywords = extractKeywords(query);
|
|
94
|
+
if (keywords.length === 0)
|
|
95
|
+
return data;
|
|
96
|
+
const textMems = data.text_mem ?? [];
|
|
97
|
+
for (const cubeData of textMems) {
|
|
98
|
+
const memData = cubeData.memories;
|
|
99
|
+
if (memData && !Array.isArray(memData) && memData.nodes) {
|
|
100
|
+
memData.nodes = [...memData.nodes].sort((a, b) => {
|
|
101
|
+
const scoreA = (a.metadata?.relativity ?? 0) + keywordMatchScore(a.memory ?? "", keywords, a.metadata, enableFuzzy);
|
|
102
|
+
const scoreB = (b.metadata?.relativity ?? 0) + keywordMatchScore(b.memory ?? "", keywords, b.metadata, enableFuzzy);
|
|
103
|
+
return scoreB - scoreA;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
else if (Array.isArray(memData)) {
|
|
107
|
+
memData.sort((a, b) => {
|
|
108
|
+
const scoreA = (a.metadata?.relativity ?? 0) + keywordMatchScore(a.memory ?? "", keywords, a.metadata, enableFuzzy);
|
|
109
|
+
const scoreB = (b.metadata?.relativity ?? 0) + keywordMatchScore(b.memory ?? "", keywords, b.metadata, enableFuzzy);
|
|
110
|
+
return scoreB - scoreA;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return data;
|
|
115
|
+
}
|
|
116
|
+
// ============================================================================
|
|
117
|
+
// Edge Filtering by Intent
|
|
118
|
+
// ============================================================================
|
|
119
|
+
export function filterEdgesByIntent(data, intent) {
|
|
120
|
+
const allowedEdgeTypes = getGraphsForIntent(intent);
|
|
121
|
+
const textMems = data.text_mem ?? [];
|
|
122
|
+
for (const cubeData of textMems) {
|
|
123
|
+
const memData = cubeData.memories;
|
|
124
|
+
if (memData && !Array.isArray(memData) && memData.edges && memData.nodes) {
|
|
125
|
+
const filteredEdges = memData.edges.filter((edge) => edge.type && allowedEdgeTypes.includes(edge.type));
|
|
126
|
+
memData.edges = filteredEdges;
|
|
127
|
+
if (filteredEdges.length > 0) {
|
|
128
|
+
const boostedIds = new Set();
|
|
129
|
+
for (const edge of filteredEdges) {
|
|
130
|
+
if (edge.source)
|
|
131
|
+
boostedIds.add(edge.source);
|
|
132
|
+
if (edge.target)
|
|
133
|
+
boostedIds.add(edge.target);
|
|
134
|
+
}
|
|
135
|
+
for (const node of memData.nodes) {
|
|
136
|
+
if (node.id && boostedIds.has(node.id) && node.metadata) {
|
|
137
|
+
node.metadata.relativity = (node.metadata.relativity ?? 0) + 0.5;
|
|
138
|
+
node.metadata.intent_matched = true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return data;
|
|
145
|
+
}
|
|
146
|
+
// ============================================================================
|
|
147
|
+
// Memory Type Filter
|
|
148
|
+
// ============================================================================
|
|
149
|
+
export function filterMemoriesByType(data, memType) {
|
|
150
|
+
if (!memType)
|
|
151
|
+
return data;
|
|
152
|
+
const textMems = data.text_mem ?? [];
|
|
153
|
+
for (const cubeData of textMems) {
|
|
154
|
+
const memData = cubeData.memories;
|
|
155
|
+
if (memData && !Array.isArray(memData) && memData.nodes) {
|
|
156
|
+
const filtered = memData.nodes.filter((n) => new RegExp(`^\\[${memType}\\]`).test(n.memory ?? ""));
|
|
157
|
+
if (memData.edges) {
|
|
158
|
+
const keptIds = new Set(filtered.map((n) => n.id));
|
|
159
|
+
memData.edges = memData.edges.filter((e) => e.source && keptIds.has(e.source) && e.target && keptIds.has(e.target));
|
|
160
|
+
}
|
|
161
|
+
memData.nodes = filtered;
|
|
162
|
+
}
|
|
163
|
+
else if (Array.isArray(memData)) {
|
|
164
|
+
cubeData.memories = memData.filter((m) => new RegExp(`^\\[${memType}\\]`).test(m.memory ?? ""));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return data;
|
|
168
|
+
}
|
|
169
|
+
// ============================================================================
|
|
170
|
+
// Memory Extraction
|
|
171
|
+
// ============================================================================
|
|
172
|
+
export function extractMemoriesFromResponse(data) {
|
|
173
|
+
const memories = [];
|
|
174
|
+
const textMems = data.text_mem ?? [];
|
|
175
|
+
for (const cubeData of textMems) {
|
|
176
|
+
const memData = cubeData.memories;
|
|
177
|
+
if (memData && !Array.isArray(memData) && memData.nodes) {
|
|
178
|
+
memories.push(...memData.nodes);
|
|
179
|
+
}
|
|
180
|
+
else if (Array.isArray(memData)) {
|
|
181
|
+
memories.push(...memData);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return memories;
|
|
185
|
+
}
|
|
186
|
+
// ============================================================================
|
|
187
|
+
// MCP Type Extraction
|
|
188
|
+
// ============================================================================
|
|
189
|
+
const VALID_MEMORY_TYPES = new Set([
|
|
190
|
+
"BUGFIX", "ERROR_PATTERN", "DECISION", "GOTCHA", "CODE_PATTERN",
|
|
191
|
+
"CONFIG", "FEATURE", "MILESTONE", "PROGRESS",
|
|
192
|
+
]);
|
|
193
|
+
const TYPE_PATTERN = /^\[([A-Z_]+)\]/;
|
|
194
|
+
function extractTypeFromText(text) {
|
|
195
|
+
const m = TYPE_PATTERN.exec(text);
|
|
196
|
+
if (m && VALID_MEMORY_TYPES.has(m[1]))
|
|
197
|
+
return m[1];
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
function decodeSourceEntry(entry) {
|
|
201
|
+
if (entry && typeof entry === "object")
|
|
202
|
+
return entry;
|
|
203
|
+
if (typeof entry !== "string")
|
|
204
|
+
return null;
|
|
205
|
+
try {
|
|
206
|
+
let result = JSON.parse(entry);
|
|
207
|
+
if (typeof result === "string")
|
|
208
|
+
result = JSON.parse(result);
|
|
209
|
+
return typeof result === "object" && result !== null ? result : null;
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function extractTypeFromSources(sources) {
|
|
216
|
+
if (!sources || sources.length === 0)
|
|
217
|
+
return null;
|
|
218
|
+
for (const entry of sources) {
|
|
219
|
+
const decoded = decodeSourceEntry(entry);
|
|
220
|
+
if (decoded) {
|
|
221
|
+
const content = String(decoded.content ?? "");
|
|
222
|
+
const found = extractTypeFromText(content);
|
|
223
|
+
if (found)
|
|
224
|
+
return found;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
export function extractMcpType(memory) {
|
|
230
|
+
// 1. [TYPE] prefix in memory text
|
|
231
|
+
const memText = memory.memory ?? "";
|
|
232
|
+
const fromText = extractTypeFromText(memText);
|
|
233
|
+
if (fromText)
|
|
234
|
+
return fromText;
|
|
235
|
+
// 2. [TYPE] prefix in sources (tree_text mode)
|
|
236
|
+
const sources = memory.sources ??
|
|
237
|
+
(memory.metadata?.sources) ??
|
|
238
|
+
[];
|
|
239
|
+
const fromSources = extractTypeFromSources(sources);
|
|
240
|
+
if (fromSources)
|
|
241
|
+
return fromSources;
|
|
242
|
+
// 3. LLM-inferred nodes
|
|
243
|
+
const meta = memory.metadata ?? memory;
|
|
244
|
+
const nodeType = typeof meta === "object" ? meta.type : null;
|
|
245
|
+
const key = memory.key ?? (typeof meta === "object" ? String(meta.key ?? "") : "");
|
|
246
|
+
if (nodeType === "reasoning" || String(key).startsWith("InferredFact")) {
|
|
247
|
+
return "INFERRED";
|
|
248
|
+
}
|
|
249
|
+
return "PROGRESS";
|
|
250
|
+
}
|
|
251
|
+
// ============================================================================
|
|
252
|
+
// Stats
|
|
253
|
+
// ============================================================================
|
|
254
|
+
export function computeMemoryStats(data) {
|
|
255
|
+
const memories = extractMemoriesFromResponse(data);
|
|
256
|
+
const stats = {};
|
|
257
|
+
let total = 0;
|
|
258
|
+
for (const mem of memories) {
|
|
259
|
+
total++;
|
|
260
|
+
const memType = extractMcpType(mem);
|
|
261
|
+
stats[memType] = (stats[memType] ?? 0) + 1;
|
|
262
|
+
}
|
|
263
|
+
return [stats, total];
|
|
264
|
+
}
|
|
265
|
+
//# sourceMappingURL=query-processing.js.map
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemOS MCP Server — McpServer setup + background init
|
|
3
|
+
*/
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { MEMOS_DEFAULT_CUBE, MEMOS_ENABLE_DELETE, logger } from "./config.js";
|
|
7
|
+
import { waitForApiReady } from "./api-client.js";
|
|
8
|
+
import { ensureCubeRegistered } from "./cube-manager.js";
|
|
9
|
+
import { toolSchemas } from "./tools-registry.js";
|
|
10
|
+
import { dispatchTool, handleApiUnreachable } from "./handlers/index.js";
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Register All Tools
|
|
13
|
+
// ============================================================================
|
|
14
|
+
function registerTools(server) {
|
|
15
|
+
for (const [name, schema] of Object.entries(toolSchemas)) {
|
|
16
|
+
// Skip delete tool if not enabled
|
|
17
|
+
if (name === "memos_delete" && !MEMOS_ENABLE_DELETE) {
|
|
18
|
+
logger.debug("Delete tool skipped (MEMOS_ENABLE_DELETE=false)");
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
server.registerTool(name, {
|
|
22
|
+
description: schema.description,
|
|
23
|
+
inputSchema: schema.inputSchema,
|
|
24
|
+
}, async (args) => {
|
|
25
|
+
try {
|
|
26
|
+
const result = await dispatchTool(name, args);
|
|
27
|
+
return {
|
|
28
|
+
content: result.map((r) => ({ type: "text", text: r.text })),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
const errStr = String(err);
|
|
33
|
+
if (errStr.includes("ECONNREFUSED") || errStr.includes("fetch failed")) {
|
|
34
|
+
const unreachable = await handleApiUnreachable();
|
|
35
|
+
return {
|
|
36
|
+
content: unreachable.map((r) => ({ type: "text", text: r.text })),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
logger.exception("Tool call failed", err);
|
|
40
|
+
return {
|
|
41
|
+
content: [
|
|
42
|
+
{
|
|
43
|
+
type: "text",
|
|
44
|
+
text: [
|
|
45
|
+
`❌ [UNEXPECTED_ERROR] ${errStr}`,
|
|
46
|
+
"",
|
|
47
|
+
"💡 Suggestions:",
|
|
48
|
+
"- Check MCP server logs for details",
|
|
49
|
+
"- Verify MemOS API is healthy: `curl http://localhost:18000/health/detail`",
|
|
50
|
+
].join("\n"),
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (MEMOS_ENABLE_DELETE) {
|
|
58
|
+
logger.debug("Delete tool enabled (MEMOS_ENABLE_DELETE=true)");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// Background Init
|
|
63
|
+
// ============================================================================
|
|
64
|
+
async function backgroundInit() {
|
|
65
|
+
try {
|
|
66
|
+
const apiReady = await waitForApiReady();
|
|
67
|
+
if (apiReady) {
|
|
68
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
69
|
+
try {
|
|
70
|
+
const [regSuccess, regError] = await ensureCubeRegistered(MEMOS_DEFAULT_CUBE, true);
|
|
71
|
+
if (regSuccess) {
|
|
72
|
+
logger.debug(`Default cube '${MEMOS_DEFAULT_CUBE}' ready`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
logger.warning(`Cube registration attempt ${attempt + 1} failed: ${regError}`);
|
|
76
|
+
await sleep(2000);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
logger.warning(`Registration attempt ${attempt + 1} error: ${err}`);
|
|
80
|
+
await sleep(2000);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
logger.warning("API not ready, will register cube on first tool call");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
logger.warning(`Background init failed: ${err}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// ============================================================================
|
|
93
|
+
// Run Server
|
|
94
|
+
// ============================================================================
|
|
95
|
+
export async function runServer() {
|
|
96
|
+
const server = new McpServer({
|
|
97
|
+
name: "memos-memory",
|
|
98
|
+
version: "1.0.0",
|
|
99
|
+
});
|
|
100
|
+
registerTools(server);
|
|
101
|
+
const transport = new StdioServerTransport();
|
|
102
|
+
// Connect FIRST (completes MCP handshake immediately),
|
|
103
|
+
// then do background init to prevent Claude Code timeout.
|
|
104
|
+
await server.connect(transport);
|
|
105
|
+
// Background init — don't await
|
|
106
|
+
backgroundInit().catch((err) => {
|
|
107
|
+
logger.error(`Background init error: ${err}`);
|
|
108
|
+
});
|
|
109
|
+
logger.debug("MemOS MCP Server (Node.js) started");
|
|
110
|
+
}
|
|
111
|
+
function sleep(ms) {
|
|
112
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=server.js.map
|