@melandlabs/memory-store 0.1.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.
Files changed (46) hide show
  1. package/LICENSE +17 -0
  2. package/README.md +377 -0
  3. package/dist/config-MlK9f9CM.d.ts +147 -0
  4. package/dist/contracts.d.ts +62 -0
  5. package/dist/contracts.js +27 -0
  6. package/dist/contracts.js.map +1 -0
  7. package/dist/http.d.ts +38 -0
  8. package/dist/http.js +545 -0
  9. package/dist/http.js.map +1 -0
  10. package/dist/index.d.ts +35 -0
  11. package/dist/index.js +442 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/mcp.d.ts +29 -0
  14. package/dist/mcp.js +589 -0
  15. package/dist/mcp.js.map +1 -0
  16. package/dist/policies/memory-graph-correction-policy.d.ts +14 -0
  17. package/dist/policies/memory-graph-correction-policy.js +41 -0
  18. package/dist/policies/memory-graph-correction-policy.js.map +1 -0
  19. package/dist/policies/memory-graph-write-policy.d.ts +35 -0
  20. package/dist/policies/memory-graph-write-policy.js +78 -0
  21. package/dist/policies/memory-graph-write-policy.js.map +1 -0
  22. package/dist/search/unified-search.d.ts +81 -0
  23. package/dist/search/unified-search.js +399 -0
  24. package/dist/search/unified-search.js.map +1 -0
  25. package/dist/server/cli-http.d.ts +1 -0
  26. package/dist/server/cli-http.js +584 -0
  27. package/dist/server/cli-http.js.map +1 -0
  28. package/dist/server/cli-mcp.d.ts +1 -0
  29. package/dist/server/cli-mcp.js +603 -0
  30. package/dist/server/cli-mcp.js.map +1 -0
  31. package/dist/storage/chroma-memory-index.d.ts +76 -0
  32. package/dist/storage/chroma-memory-index.js +249 -0
  33. package/dist/storage/chroma-memory-index.js.map +1 -0
  34. package/dist/storage/postgres-raw-message-factory.d.ts +48 -0
  35. package/dist/storage/postgres-raw-message-factory.js +19 -0
  36. package/dist/storage/postgres-raw-message-factory.js.map +1 -0
  37. package/dist/storage/raw-message-store.d.ts +47 -0
  38. package/dist/storage/raw-message-store.js +140 -0
  39. package/dist/storage/raw-message-store.js.map +1 -0
  40. package/dist/storage/sqlite-raw-message-store.d.ts +19 -0
  41. package/dist/storage/sqlite-raw-message-store.js +68 -0
  42. package/dist/storage/sqlite-raw-message-store.js.map +1 -0
  43. package/dist/storage/sqlite-vector-index.d.ts +42 -0
  44. package/dist/storage/sqlite-vector-index.js +92 -0
  45. package/dist/storage/sqlite-vector-index.js.map +1 -0
  46. package/package.json +87 -0
package/dist/mcp.js ADDED
@@ -0,0 +1,589 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { z } from 'zod';
4
+ import { buildMemoryRecordEmbeddingDocument } from '@melandlabs/ai/memory';
5
+ import { ChromaVectorStore } from '@melandlabs/rag';
6
+ import { mkdirSync } from 'fs';
7
+ import { dirname } from 'path';
8
+ import { SQLiteRawMessageManager } from '@melandlabs/sqlite';
9
+
10
+ // src/mcp.ts
11
+
12
+ // src/contracts.ts
13
+ function normalizeTimestampToMs(value) {
14
+ if (!Number.isFinite(value)) {
15
+ return 0;
16
+ }
17
+ if (value < 1e11) {
18
+ return Math.floor(value * 1e3);
19
+ }
20
+ return Math.floor(value);
21
+ }
22
+ function rawMessageToMemoryRecord(message) {
23
+ return {
24
+ id: message.messageId,
25
+ userId: message.userId,
26
+ timestamp: normalizeTimestampToMs(message.timestamp),
27
+ text: message.archivedAt ? void 0 : message.content,
28
+ mediaRefs: message.attachments?.map((item) => item.url).filter(Boolean),
29
+ embedding: message.embedding,
30
+ embeddingModel: message.embeddingModel,
31
+ embeddingContentHash: message.embeddingContentHash
32
+ };
33
+ }
34
+
35
+ // src/storage/chroma-memory-index.ts
36
+ var DEFAULT_RAW_MESSAGES_COLLECTION = "opencontext_raw_messages";
37
+ var RAW_VECTOR_BACKEND_ENV_KEYS = [
38
+ "RAW_MESSAGE_VECTOR_STORE_BACKEND",
39
+ "MEMORY_VECTOR_STORE_BACKEND",
40
+ "VECTOR_STORE_BACKEND"
41
+ ];
42
+ function getBackend(keys) {
43
+ for (const key of keys) {
44
+ const value = process.env[key]?.trim().toLowerCase();
45
+ if (value) {
46
+ return value;
47
+ }
48
+ }
49
+ return "";
50
+ }
51
+ function isRawMessageChromaEnabled() {
52
+ return getBackend(RAW_VECTOR_BACKEND_ENV_KEYS) === "chroma";
53
+ }
54
+ function getRawMessagesCollectionName() {
55
+ return process.env.CHROMA_RAW_MESSAGES_COLLECTION || process.env.CHROMA_MEMORY_COLLECTION || DEFAULT_RAW_MESSAGES_COLLECTION;
56
+ }
57
+ function getChromaStore(collectionName) {
58
+ return new ChromaVectorStore({
59
+ url: process.env.CHROMA_URL,
60
+ collectionName
61
+ });
62
+ }
63
+ function normalizeTimestampToMs2(value) {
64
+ if (!Number.isFinite(value)) {
65
+ return 0;
66
+ }
67
+ if (value < 1e11) {
68
+ return Math.floor(value * 1e3);
69
+ }
70
+ return Math.floor(value);
71
+ }
72
+ function toBooleanValue(value) {
73
+ return value === true || value === "true" || value === 1 || value === "1";
74
+ }
75
+ function rawMessageToChunk(message) {
76
+ if (!message.embedding || message.embedding.length === 0) {
77
+ return null;
78
+ }
79
+ const record = rawMessageToMemoryRecord(message);
80
+ const document = buildMemoryRecordEmbeddingDocument(record);
81
+ if (!document.content) {
82
+ return null;
83
+ }
84
+ return {
85
+ id: message.messageId,
86
+ documentId: message.messageId,
87
+ content: document.content,
88
+ embedding: message.embedding,
89
+ metadata: {
90
+ sourceType: "raw_message",
91
+ userId: message.userId,
92
+ platform: message.platform,
93
+ botId: message.botId,
94
+ channel: message.channel,
95
+ person: message.person,
96
+ timestamp: normalizeTimestampToMs2(message.timestamp),
97
+ memoryStage: message.memoryStage,
98
+ embeddingModel: message.embeddingModel,
99
+ embeddingDimensions: message.embeddingDimensions,
100
+ contentHash: message.embeddingContentHash,
101
+ archived: Boolean(message.archivedAt),
102
+ role: typeof message.metadata?.role === "string" ? message.metadata.role : void 0
103
+ }
104
+ };
105
+ }
106
+ async function upsertRawMessagesToChroma(messages) {
107
+ if (!isRawMessageChromaEnabled() || messages.length === 0) {
108
+ return 0;
109
+ }
110
+ const chunks = messages.map(rawMessageToChunk).filter((chunk) => chunk !== null);
111
+ if (chunks.length === 0) {
112
+ return 0;
113
+ }
114
+ const store = getChromaStore(getRawMessagesCollectionName());
115
+ await store.addChunks(chunks);
116
+ return chunks.length;
117
+ }
118
+ async function searchRawMessagesWithChroma(input) {
119
+ if (!isRawMessageChromaEnabled() || input.queryEmbedding.length === 0) {
120
+ return [];
121
+ }
122
+ const store = getChromaStore(getRawMessagesCollectionName());
123
+ const results = await store.similaritySearch(
124
+ input.queryEmbedding,
125
+ Math.max(input.limit * 5, input.limit),
126
+ input.userId
127
+ );
128
+ return results.filter((result) => {
129
+ const metadata = result.metadata ?? {};
130
+ if (toBooleanValue(metadata.archived)) {
131
+ return false;
132
+ }
133
+ if (input.botId && metadata.botId !== input.botId) {
134
+ return false;
135
+ }
136
+ return result.score >= input.threshold;
137
+ }).map((result) => ({
138
+ id: result.id,
139
+ content: result.content,
140
+ similarity: result.score,
141
+ metadata: result.metadata ?? {}
142
+ })).slice(0, input.limit);
143
+ }
144
+
145
+ // src/storage/postgres-raw-message-factory.ts
146
+ var factory = null;
147
+ async function resolvePostgresFactory(env) {
148
+ return null;
149
+ }
150
+ function hasPostgresFactory() {
151
+ return factory !== null;
152
+ }
153
+ var manager = null;
154
+ function isSQLiteRawMessageStorageAvailable(env) {
155
+ return resolveEnv(env).isTauriMode();
156
+ }
157
+ async function getSQLiteRawMessageManager(env) {
158
+ const e = resolveEnv(env);
159
+ if (!e.isTauriMode()) {
160
+ throw new Error("SQLite raw message storage is only available in Tauri mode.");
161
+ }
162
+ if (!manager) {
163
+ const dbPath = e.getTauriDbPath?.() ?? process.env.TAURI_DB_PATH ?? "";
164
+ mkdirSync(dirname(dbPath), { recursive: true });
165
+ manager = new SQLiteRawMessageManager({
166
+ dbPath,
167
+ enableVectorSearch: !isRawMessageChromaEnabled()
168
+ });
169
+ await manager.init();
170
+ }
171
+ return manager;
172
+ }
173
+ async function closeSQLiteRawMessageManager() {
174
+ if (!manager) {
175
+ return;
176
+ }
177
+ await manager.close();
178
+ manager = null;
179
+ }
180
+ function resolveEnv(env) {
181
+ if (env) return env;
182
+ return {
183
+ isTauriMode: () => process.env.IS_TAURI === "true" || typeof process.env.TAURI_MODE === "string",
184
+ getTauriDbPath: () => process.env.TAURI_DB_PATH ?? "",
185
+ getTauriDataDir: () => process.env.TAURI_DATA_DIR ?? ""
186
+ };
187
+ }
188
+
189
+ // src/storage/raw-message-store.ts
190
+ function resolveEnv2(env) {
191
+ if (env) return env;
192
+ return {
193
+ isTauriMode: () => process.env.IS_TAURI === "true" || typeof process.env.TAURI_MODE === "string",
194
+ getTauriDbPath: () => process.env.TAURI_DB_PATH ?? "",
195
+ getTauriDataDir: () => process.env.TAURI_DATA_DIR ?? ""
196
+ };
197
+ }
198
+ function createRawMessageStore(options = {}) {
199
+ const env = resolveEnv2(options.env);
200
+ return {
201
+ getBackend() {
202
+ return isSQLiteRawMessageStorageAvailable(env) ? "sqlite" : "postgres";
203
+ },
204
+ isAvailable() {
205
+ return isSQLiteRawMessageStorageAvailable(env) || hasPostgresFactory();
206
+ },
207
+ async getManager() {
208
+ if (isSQLiteRawMessageStorageAvailable(env)) {
209
+ return await getSQLiteRawMessageManager(env);
210
+ }
211
+ const pg = await resolvePostgresFactory();
212
+ if (!pg) {
213
+ throw new Error(
214
+ "No raw-message backend available. SQLite is disabled (not Tauri) and no Postgres factory is registered."
215
+ );
216
+ }
217
+ return pg;
218
+ },
219
+ async close() {
220
+ if (isSQLiteRawMessageStorageAvailable(env)) {
221
+ await closeSQLiteRawMessageManager();
222
+ }
223
+ }
224
+ };
225
+ }
226
+ var moduleStore = null;
227
+ var moduleConfig = null;
228
+ function isRawMessageStorageAvailable() {
229
+ if (!moduleStore) {
230
+ moduleStore = createRawMessageStore({ env: moduleConfig?.env });
231
+ }
232
+ return moduleStore.isAvailable();
233
+ }
234
+
235
+ // src/search/utilities.ts
236
+ var DEFAULT_LIMIT = 10;
237
+ var DEFAULT_THRESHOLD = 0.7;
238
+ var DEFAULT_SOURCES = ["memory", "insights", "knowledge"];
239
+ var SOURCE_SET = new Set(DEFAULT_SOURCES);
240
+ function normalizeUnifiedMemorySearchSources(sources) {
241
+ if (!Array.isArray(sources) || sources.length === 0) {
242
+ return [...DEFAULT_SOURCES];
243
+ }
244
+ const normalized = sources.filter((source) => typeof source === "string").map((source) => source.trim().toLowerCase()).filter(
245
+ (source) => SOURCE_SET.has(source)
246
+ );
247
+ return normalized.length > 0 ? Array.from(new Set(normalized)) : [...DEFAULT_SOURCES];
248
+ }
249
+ function clampUnifiedMemorySearchLimit(limit) {
250
+ const parsed = typeof limit === "number" ? limit : Number(limit ?? DEFAULT_LIMIT);
251
+ if (!Number.isFinite(parsed)) {
252
+ return DEFAULT_LIMIT;
253
+ }
254
+ return Math.min(50, Math.max(1, Math.floor(parsed)));
255
+ }
256
+ function clampUnifiedMemorySearchThreshold(threshold) {
257
+ const parsed = typeof threshold === "number" ? threshold : Number(threshold ?? DEFAULT_THRESHOLD);
258
+ if (!Number.isFinite(parsed)) {
259
+ return DEFAULT_THRESHOLD;
260
+ }
261
+ return Math.min(1, Math.max(-1, parsed));
262
+ }
263
+ function mergeUnifiedMemorySearchResults(results, limit) {
264
+ return [...results].sort((a, b) => {
265
+ const scoreDelta = b.similarity - a.similarity;
266
+ if (scoreDelta !== 0) {
267
+ return scoreDelta;
268
+ }
269
+ return a.type.localeCompare(b.type) || a.id.localeCompare(b.id);
270
+ }).slice(0, limit);
271
+ }
272
+ function toKnowledgeResult(result) {
273
+ return {
274
+ type: "knowledge",
275
+ id: result.chunkId,
276
+ content: result.content,
277
+ similarity: result.similarity,
278
+ metadata: {
279
+ documentId: result.documentId,
280
+ documentName: result.documentName,
281
+ chunkIndex: result.chunkIndex
282
+ }
283
+ };
284
+ }
285
+ function toMemoryResult(result) {
286
+ return {
287
+ type: "memory",
288
+ id: result.id,
289
+ content: result.content,
290
+ similarity: result.similarity,
291
+ metadata: result.metadata
292
+ };
293
+ }
294
+ function isRawMemorySemanticResult(result) {
295
+ if (!result || typeof result !== "object") {
296
+ return false;
297
+ }
298
+ const item = result;
299
+ return typeof item.id === "string" && typeof item.content === "string" && typeof item.similarity === "number" && Boolean(item.metadata) && typeof item.metadata === "object";
300
+ }
301
+
302
+ // src/search/unified-search.ts
303
+ function createUnifiedSearch(deps = {}) {
304
+ const logger = deps.logger ?? console;
305
+ async function searchUnifiedMemory(input) {
306
+ const query = input.query.trim();
307
+ const sources = normalizeUnifiedMemorySearchSources(input.sources);
308
+ const limit = clampUnifiedMemorySearchLimit(input.limit);
309
+ const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
310
+ const warnings = [];
311
+ const results = [];
312
+ if (!query) {
313
+ return {
314
+ query,
315
+ sources,
316
+ results: [],
317
+ count: 0,
318
+ warnings
319
+ };
320
+ }
321
+ if (sources.includes("memory")) {
322
+ if (isRawMessageStorageAvailable()) {
323
+ try {
324
+ const memoryHits = await runMemorySource(input, limit, threshold);
325
+ results.push(...memoryHits);
326
+ } catch (error) {
327
+ logger.warn?.("[memory-store] memory source failed:", error);
328
+ warnings.push({
329
+ source: "memory",
330
+ code: "memory_search_failed",
331
+ message: error.message ?? "memory_search_failed"
332
+ });
333
+ }
334
+ } else {
335
+ warnings.push({
336
+ source: "memory",
337
+ code: "raw_message_storage_unavailable",
338
+ message: "Raw memory storage is not available in this environment."
339
+ });
340
+ }
341
+ }
342
+ if (sources.includes("insights") && typeof deps.searchInsights === "function") {
343
+ try {
344
+ const insightHits = await deps.searchInsights({
345
+ userId: input.userId,
346
+ query: input.query,
347
+ limit,
348
+ threshold,
349
+ botIds: input.botIds,
350
+ includeArchived: input.includeArchivedInsights,
351
+ authToken: input.authToken
352
+ });
353
+ for (const hit of insightHits) {
354
+ results.push({
355
+ type: "insight",
356
+ id: hit.id,
357
+ content: hit.content,
358
+ similarity: hit.similarity,
359
+ metadata: hit.metadata
360
+ });
361
+ }
362
+ } catch (error) {
363
+ logger.warn?.("[memory-store] insights source failed:", error);
364
+ warnings.push({
365
+ source: "insights",
366
+ code: "insights_search_failed",
367
+ message: error.message ?? "insights_search_failed"
368
+ });
369
+ }
370
+ } else if (sources.includes("insights")) {
371
+ warnings.push({
372
+ source: "insights",
373
+ code: "insights_search_not_configured",
374
+ message: "Insights search is not configured for this host."
375
+ });
376
+ }
377
+ if (sources.includes("knowledge") && typeof deps.searchKnowledge === "function") {
378
+ try {
379
+ const knowledgeHits = await deps.searchKnowledge({
380
+ userId: input.userId,
381
+ query: input.query,
382
+ options: {
383
+ limit,
384
+ threshold,
385
+ documentIds: input.documentIds
386
+ },
387
+ authToken: input.authToken
388
+ });
389
+ for (const hit of knowledgeHits) {
390
+ results.push(toKnowledgeResult(hit));
391
+ }
392
+ } catch (error) {
393
+ logger.warn?.("[memory-store] knowledge source failed:", error);
394
+ warnings.push({
395
+ source: "knowledge",
396
+ code: "knowledge_search_failed",
397
+ message: error.message ?? "knowledge_search_failed"
398
+ });
399
+ }
400
+ } else if (sources.includes("knowledge")) {
401
+ warnings.push({
402
+ source: "knowledge",
403
+ code: "knowledge_search_not_configured",
404
+ message: "Knowledge search is not configured for this host."
405
+ });
406
+ }
407
+ const merged = mergeUnifiedMemorySearchResults(results, limit);
408
+ return {
409
+ query: input.query,
410
+ sources,
411
+ results: merged,
412
+ count: merged.length,
413
+ warnings
414
+ };
415
+ }
416
+ async function runMemorySource(input, limit, threshold) {
417
+ if (typeof deps.embedQuery !== "function") {
418
+ throw new Error("embedQuery is not configured");
419
+ }
420
+ const queryEmbedding = await deps.embedQuery({
421
+ userId: input.userId,
422
+ query: input.query,
423
+ authToken: input.authToken
424
+ });
425
+ const filters = input.botIds && input.botIds.length > 0 ? input.botIds.map((botId) => ({ botId })) : [{}];
426
+ if (isRawMessageChromaEnabled()) {
427
+ try {
428
+ const chromaHits = (await Promise.all(
429
+ filters.map((filter) => {
430
+ const botId = "botId" in filter ? filter.botId : void 0;
431
+ return searchRawMessagesWithChroma({
432
+ userId: input.userId,
433
+ queryEmbedding,
434
+ limit,
435
+ threshold,
436
+ botId
437
+ });
438
+ })
439
+ )).flat().map(toMemoryResult);
440
+ logger.log?.("[memory-store] Raw message semantic search completed", {
441
+ backend: "chroma",
442
+ dimensions: queryEmbedding.length,
443
+ count: chromaHits.length
444
+ });
445
+ return chromaHits;
446
+ } catch (error) {
447
+ logger.warn?.(
448
+ "[memory-store] Chroma raw message search failed; falling back to database search:",
449
+ error
450
+ );
451
+ }
452
+ }
453
+ if (typeof deps.searchRawMessagesAnn === "function") {
454
+ const rows = (await Promise.all(
455
+ filters.map(
456
+ (filter) => deps.searchRawMessagesAnn({
457
+ userId: input.userId,
458
+ queryEmbedding,
459
+ limit,
460
+ threshold,
461
+ botId: "botId" in filter ? filter.botId : void 0
462
+ })
463
+ )
464
+ )).flat().filter(isRawMemorySemanticResult).map(toMemoryResult);
465
+ return rows;
466
+ }
467
+ return [];
468
+ }
469
+ async function searchRawMemorySemantically(input) {
470
+ const limit = clampUnifiedMemorySearchLimit(input.limit);
471
+ const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
472
+ return runMemorySource(input, limit, threshold);
473
+ }
474
+ return { searchUnifiedMemory, searchRawMemorySemantically };
475
+ }
476
+
477
+ // src/mcp.ts
478
+ var DEFAULT_NAME = "@melandlabs/memory-store";
479
+ var DEFAULT_VERSION = "0.1.0";
480
+ async function startMcpServer(options = {}) {
481
+ const server = new McpServer({
482
+ name: options.name ?? DEFAULT_NAME,
483
+ version: options.version ?? DEFAULT_VERSION
484
+ });
485
+ const rawStore = createRawMessageStore({
486
+ env: options.env
487
+ });
488
+ const search = createUnifiedSearch(options.unified);
489
+ server.tool("memory.health", "Returns the health status of the memory store.", async () => ({
490
+ content: [{ type: "text", text: JSON.stringify({ ok: true }) }]
491
+ }));
492
+ const searchSchema = {
493
+ userId: z.string(),
494
+ query: z.string().min(1),
495
+ sources: z.array(z.enum(["memory", "insights", "knowledge"])).optional().describe("Default: ['memory','insights','knowledge']"),
496
+ limit: z.number().int().min(1).max(50).default(10),
497
+ threshold: z.number().min(-1).max(1).default(0.7),
498
+ botIds: z.array(z.string()).optional(),
499
+ documentIds: z.array(z.string()).optional(),
500
+ includeArchivedInsights: z.boolean().default(false),
501
+ authToken: z.string().optional()
502
+ };
503
+ const writeSchema = {
504
+ userId: z.string(),
505
+ message: z.object({
506
+ id: z.string().optional(),
507
+ messageId: z.string().optional(),
508
+ role: z.string(),
509
+ content: z.union([z.string(), z.array(z.unknown())]),
510
+ platform: z.string().optional(),
511
+ botId: z.string().optional(),
512
+ channel: z.string().optional(),
513
+ person: z.string().optional(),
514
+ timestamp: z.number().optional(),
515
+ metadata: z.record(z.string(), z.unknown()).optional()
516
+ }).passthrough()
517
+ };
518
+ const getSchema = {
519
+ userId: z.string(),
520
+ messageId: z.string()
521
+ };
522
+ server.registerTool(
523
+ "memory.searchUnified",
524
+ {
525
+ title: "Search Unified Memory",
526
+ description: "Semantic search across raw messages (always), and optionally insights and uploaded knowledge.",
527
+ inputSchema: searchSchema
528
+ },
529
+ async (args) => {
530
+ const a = args;
531
+ const result = await search.searchUnifiedMemory(a);
532
+ return {
533
+ content: [{ type: "text", text: JSON.stringify(result) }]
534
+ };
535
+ }
536
+ );
537
+ server.registerTool(
538
+ "memory.writeRawMessage",
539
+ {
540
+ title: "Write Raw Message",
541
+ description: "Persist a single raw message to the user's memory store.",
542
+ inputSchema: writeSchema
543
+ },
544
+ async (args) => {
545
+ const a = args;
546
+ const manager2 = await rawStore.getManager();
547
+ const result = await manager2.upsertRawMessages?.({
548
+ userId: a.userId,
549
+ messages: [a.message]
550
+ });
551
+ try {
552
+ await upsertRawMessagesToChroma([a.message]);
553
+ } catch (error) {
554
+ console.warn("[memory-store/mcp] chroma upsert failed:", error);
555
+ }
556
+ return {
557
+ content: [{ type: "text", text: JSON.stringify({ ok: true, result }) }]
558
+ };
559
+ }
560
+ );
561
+ server.registerTool(
562
+ "memory.getRawMessage",
563
+ {
564
+ title: "Get Raw Message",
565
+ description: "Fetch a single raw message by its message id.",
566
+ inputSchema: getSchema
567
+ },
568
+ async (args) => {
569
+ const a = args;
570
+ const manager2 = await rawStore.getManager();
571
+ const row = await manager2.getMessageById?.(a.messageId);
572
+ return {
573
+ content: [
574
+ {
575
+ type: "text",
576
+ text: JSON.stringify({ message: row ?? null })
577
+ }
578
+ ]
579
+ };
580
+ }
581
+ );
582
+ const transport = new StdioServerTransport();
583
+ await server.connect(transport);
584
+ return server;
585
+ }
586
+
587
+ export { startMcpServer };
588
+ //# sourceMappingURL=mcp.js.map
589
+ //# sourceMappingURL=mcp.js.map