@j0hanz/memdb 1.1.5 → 1.2.1

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 (143) hide show
  1. package/dist/config.d.ts +3 -1
  2. package/dist/config.js +27 -3
  3. package/dist/core/db.d.ts +1 -1
  4. package/dist/core/db.js +11 -0
  5. package/dist/core/memory-read.js +18 -16
  6. package/dist/core/memory-write.d.ts +5 -1
  7. package/dist/core/memory-write.js +33 -22
  8. package/dist/core/relationships.d.ts +15 -0
  9. package/dist/core/relationships.js +93 -0
  10. package/dist/core/search.d.ts +5 -1
  11. package/dist/core/search.js +93 -7
  12. package/dist/index.js +8 -4
  13. package/dist/protocol-version-guard.js +16 -0
  14. package/dist/schemas.d.ts +44 -0
  15. package/dist/schemas.js +58 -0
  16. package/dist/stdio-transport.d.ts +29 -0
  17. package/dist/stdio-transport.js +250 -0
  18. package/dist/tools.d.ts +11 -2
  19. package/dist/tools.js +101 -3
  20. package/dist/types.d.ts +29 -5
  21. package/dist/types.js +10 -1
  22. package/package.json +3 -3
  23. package/dist/core/database-schema.d.ts +0 -2
  24. package/dist/core/database-schema.d.ts.map +0 -1
  25. package/dist/core/database-schema.js +0 -64
  26. package/dist/core/database-schema.js.map +0 -1
  27. package/dist/core/database.d.ts +0 -3
  28. package/dist/core/database.d.ts.map +0 -1
  29. package/dist/core/database.js +0 -43
  30. package/dist/core/database.js.map +0 -1
  31. package/dist/core/memory-create.d.ts +0 -7
  32. package/dist/core/memory-create.d.ts.map +0 -1
  33. package/dist/core/memory-create.js +0 -40
  34. package/dist/core/memory-create.js.map +0 -1
  35. package/dist/core/memory-db.d.ts +0 -2
  36. package/dist/core/memory-db.d.ts.map +0 -1
  37. package/dist/core/memory-db.js +0 -31
  38. package/dist/core/memory-db.js.map +0 -1
  39. package/dist/core/memory-read.d.ts.map +0 -1
  40. package/dist/core/memory-read.js.map +0 -1
  41. package/dist/core/memory-relations.d.ts +0 -10
  42. package/dist/core/memory-relations.d.ts.map +0 -1
  43. package/dist/core/memory-relations.js +0 -57
  44. package/dist/core/memory-relations.js.map +0 -1
  45. package/dist/core/memory-search.d.ts +0 -10
  46. package/dist/core/memory-search.d.ts.map +0 -1
  47. package/dist/core/memory-search.js +0 -23
  48. package/dist/core/memory-search.js.map +0 -1
  49. package/dist/core/memory-stats.d.ts +0 -2
  50. package/dist/core/memory-stats.d.ts.map +0 -1
  51. package/dist/core/memory-stats.js +0 -51
  52. package/dist/core/memory-stats.js.map +0 -1
  53. package/dist/core/memory-updates.d.ts +0 -10
  54. package/dist/core/memory-updates.d.ts.map +0 -1
  55. package/dist/core/memory-updates.js +0 -115
  56. package/dist/core/memory-updates.js.map +0 -1
  57. package/dist/core/relation-queries.d.ts +0 -7
  58. package/dist/core/relation-queries.d.ts.map +0 -1
  59. package/dist/core/relation-queries.js +0 -125
  60. package/dist/core/relation-queries.js.map +0 -1
  61. package/dist/core/relations.d.ts +0 -10
  62. package/dist/core/relations.js +0 -177
  63. package/dist/core/row-mappers.d.ts +0 -6
  64. package/dist/core/row-mappers.d.ts.map +0 -1
  65. package/dist/core/row-mappers.js +0 -52
  66. package/dist/core/row-mappers.js.map +0 -1
  67. package/dist/core/search-errors.d.ts +0 -1
  68. package/dist/core/search-errors.d.ts.map +0 -1
  69. package/dist/core/search-errors.js +0 -30
  70. package/dist/core/search-errors.js.map +0 -1
  71. package/dist/core/search.d.ts.map +0 -1
  72. package/dist/core/search.js.map +0 -1
  73. package/dist/core/sqlite.d.ts +0 -10
  74. package/dist/core/sqlite.d.ts.map +0 -1
  75. package/dist/core/sqlite.js +0 -71
  76. package/dist/core/sqlite.js.map +0 -1
  77. package/dist/core/tags.d.ts +0 -1
  78. package/dist/core/tags.d.ts.map +0 -1
  79. package/dist/core/tags.js +0 -27
  80. package/dist/core/tags.js.map +0 -1
  81. package/dist/index.d.ts.map +0 -1
  82. package/dist/index.js.map +0 -1
  83. package/dist/lib/errors.d.ts +0 -19
  84. package/dist/lib/errors.d.ts.map +0 -1
  85. package/dist/lib/errors.js +0 -22
  86. package/dist/lib/errors.js.map +0 -1
  87. package/dist/schemas/inputs.d.ts +0 -44
  88. package/dist/schemas/inputs.d.ts.map +0 -1
  89. package/dist/schemas/inputs.js +0 -97
  90. package/dist/schemas/inputs.js.map +0 -1
  91. package/dist/schemas/outputs.d.ts +0 -9
  92. package/dist/schemas/outputs.d.ts.map +0 -1
  93. package/dist/schemas/outputs.js +0 -28
  94. package/dist/schemas/outputs.js.map +0 -1
  95. package/dist/tools/definitions/memory-core.d.ts +0 -2
  96. package/dist/tools/definitions/memory-core.d.ts.map +0 -1
  97. package/dist/tools/definitions/memory-core.js +0 -79
  98. package/dist/tools/definitions/memory-core.js.map +0 -1
  99. package/dist/tools/definitions/memory-relations.d.ts +0 -2
  100. package/dist/tools/definitions/memory-relations.d.ts.map +0 -1
  101. package/dist/tools/definitions/memory-relations.js +0 -43
  102. package/dist/tools/definitions/memory-relations.js.map +0 -1
  103. package/dist/tools/definitions/memory-search.d.ts +0 -2
  104. package/dist/tools/definitions/memory-search.d.ts.map +0 -1
  105. package/dist/tools/definitions/memory-search.js +0 -20
  106. package/dist/tools/definitions/memory-search.js.map +0 -1
  107. package/dist/tools/definitions/memory-stats.d.ts +0 -2
  108. package/dist/tools/definitions/memory-stats.d.ts.map +0 -1
  109. package/dist/tools/definitions/memory-stats.js +0 -20
  110. package/dist/tools/definitions/memory-stats.js.map +0 -1
  111. package/dist/tools/index.d.ts +0 -2
  112. package/dist/tools/index.d.ts.map +0 -1
  113. package/dist/tools/index.js +0 -15
  114. package/dist/tools/index.js.map +0 -1
  115. package/dist/tools/tool-handlers.d.ts +0 -3
  116. package/dist/tools/tool-handlers.d.ts.map +0 -1
  117. package/dist/tools/tool-handlers.js +0 -19
  118. package/dist/tools/tool-handlers.js.map +0 -1
  119. package/dist/tools/tool-types.d.ts +0 -14
  120. package/dist/tools/tool-types.d.ts.map +0 -1
  121. package/dist/tools/tool-types.js +0 -1
  122. package/dist/tools/tool-types.js.map +0 -1
  123. package/dist/tsconfig.tsbuildinfo +0 -1
  124. package/dist/types/index.d.ts +0 -37
  125. package/dist/types/index.d.ts.map +0 -1
  126. package/dist/types/index.js +0 -1
  127. package/dist/types/index.js.map +0 -1
  128. package/dist/utils/config.d.ts +0 -6
  129. package/dist/utils/config.d.ts.map +0 -1
  130. package/dist/utils/config.js +0 -99
  131. package/dist/utils/config.js.map +0 -1
  132. package/dist/utils/logger.d.ts +0 -5
  133. package/dist/utils/logger.d.ts.map +0 -1
  134. package/dist/utils/logger.js +0 -20
  135. package/dist/utils/logger.js.map +0 -1
  136. package/dist/utils.d.ts +0 -11
  137. package/dist/utils.js +0 -118
  138. package/dist/worker/db-worker-client.d.ts +0 -9
  139. package/dist/worker/db-worker-client.js +0 -93
  140. package/dist/worker/db-worker.d.ts +0 -1
  141. package/dist/worker/db-worker.js +0 -174
  142. package/dist/worker/protocol.d.ts +0 -9
  143. package/dist/worker/protocol.js +0 -14
package/dist/schemas.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { MEMORY_TYPES } from './types.js';
2
3
  const hashSchema = z.string().regex(/^[a-f0-9]{32}$/i);
3
4
  const tagSchema = z
4
5
  .string()
@@ -8,17 +9,36 @@ const tagSchema = z
8
9
  const tagsSchema = z.array(tagSchema);
9
10
  const contentSchema = z.string().min(1).max(100000);
10
11
  const querySchema = z.string().trim().min(1).max(1000);
12
+ const importanceSchema = z.number().int().min(0).max(10);
13
+ const memoryTypeSchema = z.enum(MEMORY_TYPES);
14
+ const relationTypeSchema = z
15
+ .string()
16
+ .min(1)
17
+ .max(50)
18
+ .regex(/^\S+$/, 'Relation type must not contain whitespace');
11
19
  export const StoreMemoryInputSchema = z.strictObject({
12
20
  content: contentSchema.meta({ description: 'The content of the memory' }),
13
21
  tags: tagsSchema.min(1).max(100).meta({
14
22
  description: 'Tags to categorize the memory (1-100 tags, no whitespace, max 50 chars each)',
15
23
  }),
24
+ importance: importanceSchema.optional().meta({
25
+ description: 'Priority level 0-10 (0=lowest, 10=critical). Higher importance memories surface first in search.',
26
+ }),
27
+ memory_type: memoryTypeSchema.optional().meta({
28
+ description: 'Category: general, fact, plan, decision, reflection, lesson, error, gradient',
29
+ }),
16
30
  });
17
31
  const StoreMemoryItemSchema = z.strictObject({
18
32
  content: contentSchema.meta({ description: 'The content of the memory' }),
19
33
  tags: tagsSchema.min(1).max(100).meta({
20
34
  description: 'Tags to categorize the memory',
21
35
  }),
36
+ importance: importanceSchema.optional().meta({
37
+ description: 'Priority level 0-10 (0=lowest, 10=critical)',
38
+ }),
39
+ memory_type: memoryTypeSchema.optional().meta({
40
+ description: 'Category: general, fact, plan, decision, reflection, lesson, error, gradient',
41
+ }),
22
42
  });
23
43
  export const StoreMemoriesInputSchema = z.strictObject({
24
44
  items: z.array(StoreMemoryItemSchema).min(1).max(50).meta({
@@ -52,6 +72,44 @@ export const UpdateMemoryInputSchema = z.strictObject({
52
72
  export const MemoryStatsInputSchema = z
53
73
  .strictObject({})
54
74
  .meta({ description: 'No parameters required' });
75
+ export const CreateRelationshipInputSchema = z.strictObject({
76
+ from_hash: hashSchema.meta({
77
+ description: 'MD5 hash of the source memory',
78
+ }),
79
+ to_hash: hashSchema.meta({
80
+ description: 'MD5 hash of the target memory',
81
+ }),
82
+ relation_type: relationTypeSchema.meta({
83
+ description: 'Type of relationship (e.g., "related_to", "causes", "depends_on", "part_of", "follows")',
84
+ }),
85
+ });
86
+ export const GetRelationshipsInputSchema = z.strictObject({
87
+ hash: hashSchema.meta({
88
+ description: 'MD5 hash of the memory to get relationships for',
89
+ }),
90
+ direction: z.enum(['outgoing', 'incoming', 'both']).optional().meta({
91
+ description: 'Direction: outgoing (from this memory), incoming (to this memory), both (default)',
92
+ }),
93
+ });
94
+ export const DeleteRelationshipInputSchema = z.strictObject({
95
+ from_hash: hashSchema.meta({
96
+ description: 'MD5 hash of the source memory',
97
+ }),
98
+ to_hash: hashSchema.meta({
99
+ description: 'MD5 hash of the target memory',
100
+ }),
101
+ relation_type: relationTypeSchema.meta({
102
+ description: 'Type of relationship to delete',
103
+ }),
104
+ });
105
+ export const RecallInputSchema = z.strictObject({
106
+ query: querySchema.meta({
107
+ description: 'Search query to find initial memories',
108
+ }),
109
+ depth: z.number().int().min(0).max(3).optional().meta({
110
+ description: 'How many relationship hops to follow (0-3, default 1). 0 = search only, no graph traversal.',
111
+ }),
112
+ });
55
113
  const ErrorSchema = z.strictObject({
56
114
  code: z.string(),
57
115
  message: z.string(),
@@ -0,0 +1,29 @@
1
+ import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
2
+ import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
3
+ /**
4
+ * Stdio transport that explicitly rejects JSON-RPC batch arrays.
5
+ *
6
+ * MCP protocol revisions >= 2025-06-18 removed JSON-RPC batching; servers must reject array payloads.
7
+ */
8
+ export declare class BatchRejectingStdioServerTransport implements Transport {
9
+ onclose: () => void;
10
+ onerror: (error: Error) => void;
11
+ onmessage: NonNullable<Transport['onmessage']>;
12
+ private readonly stdin;
13
+ private readonly stdout;
14
+ private readonly readBuffer;
15
+ private started;
16
+ private readonly onData;
17
+ private readonly onStdinError;
18
+ constructor(stdin?: NodeJS.ReadableStream, stdout?: NodeJS.WritableStream);
19
+ start(): Promise<void>;
20
+ close(): Promise<void>;
21
+ send(message: JSONRPCMessage): Promise<void>;
22
+ private sendInvalidRequest;
23
+ private sendInvalidRequestUnknownId;
24
+ private sendParseError;
25
+ private handleBatch;
26
+ private handleNonBatch;
27
+ private handleLine;
28
+ private processReadBuffer;
29
+ }
@@ -0,0 +1,250 @@
1
+ import process from 'node:process';
2
+ import { JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';
3
+ const MAX_LINE_BYTES = 16 * 1024 * 1024;
4
+ class LineBuffer {
5
+ maxBytes;
6
+ chunks = [];
7
+ totalLength = 0;
8
+ constructor(maxBytes) {
9
+ this.maxBytes = maxBytes;
10
+ }
11
+ append(chunk) {
12
+ this.chunks.push(chunk);
13
+ this.totalLength += chunk.length;
14
+ }
15
+ readLine() {
16
+ if (this.totalLength === 0)
17
+ return null;
18
+ const found = this.findNewline();
19
+ if (!found) {
20
+ this.assertLineLength(this.totalLength);
21
+ return null;
22
+ }
23
+ const lineLength = found.offset + found.newlineIndex;
24
+ this.assertLineLength(lineLength);
25
+ const lineBuffer = this.buildLineBuffer(found.chunkIndex, found.newlineIndex, lineLength);
26
+ this.consumeLine(found.chunkIndex, found.newlineIndex, lineLength);
27
+ return lineBuffer.toString('utf8').replace(/\r$/, '');
28
+ }
29
+ assertLineLength(length) {
30
+ if (length > this.maxBytes) {
31
+ throw new Error('Input line exceeds maximum size');
32
+ }
33
+ }
34
+ findNewline() {
35
+ let offset = 0;
36
+ for (let i = 0; i < this.chunks.length; i++) {
37
+ const chunk = this.chunks[i];
38
+ if (!chunk)
39
+ continue;
40
+ const newlineIndex = chunk.indexOf('\n');
41
+ if (newlineIndex !== -1) {
42
+ return { chunkIndex: i, newlineIndex, offset };
43
+ }
44
+ offset += chunk.length;
45
+ }
46
+ return null;
47
+ }
48
+ buildLineBuffer(chunkIndex, newlineIndex, lineLength) {
49
+ const lineBuffer = Buffer.allocUnsafe(lineLength);
50
+ let writeOffset = 0;
51
+ for (let i = 0; i < chunkIndex; i++) {
52
+ const part = this.chunks[i];
53
+ if (!part)
54
+ continue;
55
+ part.copy(lineBuffer, writeOffset);
56
+ writeOffset += part.length;
57
+ }
58
+ const chunk = this.chunks[chunkIndex];
59
+ if (chunk && newlineIndex > 0) {
60
+ chunk.copy(lineBuffer, writeOffset, 0, newlineIndex);
61
+ }
62
+ return lineBuffer;
63
+ }
64
+ consumeLine(chunkIndex, newlineIndex, lineLength) {
65
+ const remaining = [];
66
+ const chunk = this.chunks[chunkIndex];
67
+ if (chunk) {
68
+ const rest = chunk.subarray(newlineIndex + 1);
69
+ if (rest.length > 0)
70
+ remaining.push(rest);
71
+ }
72
+ for (let i = chunkIndex + 1; i < this.chunks.length; i++) {
73
+ const tail = this.chunks[i];
74
+ if (!tail)
75
+ continue;
76
+ remaining.push(tail);
77
+ }
78
+ this.chunks = remaining;
79
+ this.totalLength -= lineLength + 1;
80
+ }
81
+ clear() {
82
+ this.chunks = [];
83
+ this.totalLength = 0;
84
+ }
85
+ }
86
+ const isObject = (value) => typeof value === 'object' && value !== null;
87
+ const getRequestIdResult = (value) => {
88
+ if (!isObject(value))
89
+ return { ok: false, reason: 'missing' };
90
+ const candidate = Reflect.get(value, 'id');
91
+ if (candidate === undefined)
92
+ return { ok: false, reason: 'missing' };
93
+ if (typeof candidate === 'string' || typeof candidate === 'number') {
94
+ return { ok: true, id: candidate };
95
+ }
96
+ return { ok: false, reason: 'invalid-type' };
97
+ };
98
+ const invalidRequestError = (id) => id === undefined
99
+ ? {
100
+ jsonrpc: '2.0',
101
+ error: {
102
+ code: -32600,
103
+ message: 'Invalid request',
104
+ },
105
+ }
106
+ : {
107
+ jsonrpc: '2.0',
108
+ id,
109
+ error: {
110
+ code: -32600,
111
+ message: 'Invalid request',
112
+ },
113
+ };
114
+ const parseError = () => ({
115
+ jsonrpc: '2.0',
116
+ error: {
117
+ code: -32700,
118
+ message: 'Parse error',
119
+ },
120
+ });
121
+ /**
122
+ * Stdio transport that explicitly rejects JSON-RPC batch arrays.
123
+ *
124
+ * MCP protocol revisions >= 2025-06-18 removed JSON-RPC batching; servers must reject array payloads.
125
+ */
126
+ export class BatchRejectingStdioServerTransport {
127
+ onclose = () => { };
128
+ onerror = () => { };
129
+ onmessage = () => { };
130
+ stdin;
131
+ stdout;
132
+ readBuffer = new LineBuffer(MAX_LINE_BYTES);
133
+ started = false;
134
+ // Arrow functions keep identity for off().
135
+ onData = (chunk) => {
136
+ try {
137
+ this.readBuffer.append(chunk);
138
+ this.processReadBuffer();
139
+ }
140
+ catch (error) {
141
+ this.onerror(error instanceof Error ? error : new Error(String(error)));
142
+ }
143
+ };
144
+ onStdinError = (error) => {
145
+ this.onerror(error);
146
+ };
147
+ constructor(stdin = process.stdin, stdout = process.stdout) {
148
+ this.stdin = stdin;
149
+ this.stdout = stdout;
150
+ }
151
+ start() {
152
+ if (this.started) {
153
+ throw new Error('BatchRejectingStdioServerTransport already started! If using McpServer.connect(), note that connect() calls start() automatically.');
154
+ }
155
+ this.started = true;
156
+ this.stdin.on('data', this.onData);
157
+ this.stdin.on('error', this.onStdinError);
158
+ return Promise.resolve();
159
+ }
160
+ close() {
161
+ this.stdin.off('data', this.onData);
162
+ this.stdin.off('error', this.onStdinError);
163
+ const remainingDataListeners = this.stdin.listenerCount('data');
164
+ if (remainingDataListeners === 0) {
165
+ this.stdin.pause();
166
+ }
167
+ this.readBuffer.clear();
168
+ this.onclose();
169
+ return Promise.resolve();
170
+ }
171
+ send(message) {
172
+ return new Promise((resolve) => {
173
+ const json = `${JSON.stringify(message)}\n`;
174
+ if (this.stdout.write(json)) {
175
+ resolve();
176
+ }
177
+ else {
178
+ this.stdout.once('drain', resolve);
179
+ }
180
+ });
181
+ }
182
+ sendInvalidRequest(id) {
183
+ void this.send(invalidRequestError(id));
184
+ }
185
+ sendInvalidRequestUnknownId() {
186
+ void this.send(invalidRequestError());
187
+ }
188
+ sendParseError() {
189
+ void this.send(parseError());
190
+ }
191
+ handleBatch(raw) {
192
+ let sentAny = false;
193
+ for (const item of raw) {
194
+ const idResult = getRequestIdResult(item);
195
+ if (!idResult.ok)
196
+ continue;
197
+ this.sendInvalidRequest(idResult.id);
198
+ sentAny = true;
199
+ }
200
+ if (!sentAny) {
201
+ this.sendInvalidRequestUnknownId();
202
+ }
203
+ }
204
+ handleNonBatch(raw) {
205
+ const idResult = getRequestIdResult(raw);
206
+ if (idResult.ok) {
207
+ this.sendInvalidRequest(idResult.id);
208
+ return;
209
+ }
210
+ if (idResult.reason === 'invalid-type') {
211
+ this.sendInvalidRequestUnknownId();
212
+ }
213
+ }
214
+ handleLine(line) {
215
+ let raw;
216
+ try {
217
+ raw = JSON.parse(line);
218
+ }
219
+ catch {
220
+ this.sendParseError();
221
+ return;
222
+ }
223
+ if (Array.isArray(raw)) {
224
+ this.handleBatch(raw);
225
+ return;
226
+ }
227
+ const parsed = JSONRPCMessageSchema.safeParse(raw);
228
+ if (!parsed.success) {
229
+ this.handleNonBatch(raw);
230
+ return;
231
+ }
232
+ this.onmessage(parsed.data);
233
+ }
234
+ processReadBuffer() {
235
+ try {
236
+ for (let line = this.readBuffer.readLine(); line !== null; line = this.readBuffer.readLine()) {
237
+ try {
238
+ this.handleLine(line);
239
+ }
240
+ catch (error) {
241
+ this.onerror(error instanceof Error ? error : new Error(String(error)));
242
+ }
243
+ }
244
+ }
245
+ catch {
246
+ this.readBuffer.clear();
247
+ this.sendParseError();
248
+ }
249
+ }
250
+ }
package/dist/tools.d.ts CHANGED
@@ -1,14 +1,19 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { deleteMemories } from './core/memory-read.js';
3
3
  import { createMemories, createMemory, updateMemory } from './core/memory-write.js';
4
- import { searchMemories } from './core/search.js';
5
- import type { BatchDeleteResult, BatchStoreResult, Memory, MemoryInsertResult, MemoryStats, MemoryUpdateResult, SearchResult, StatementResult } from './types.js';
4
+ import { createRelationship, deleteRelationship, getRelationships } from './core/relationships.js';
5
+ import { recallMemories, searchMemories } from './core/search.js';
6
+ import type { BatchDeleteResult, BatchStoreResult, CreateRelationshipResult, Memory, MemoryInsertResult, MemoryStats, MemoryUpdateResult, RecallResult, Relationship, SearchResult, StatementResult } from './types.js';
6
7
  type MaybePromise<T> = T | Promise<T>;
7
8
  type CreateMemoryInput = Parameters<typeof createMemory>[0];
8
9
  type CreateMemoriesInput = Parameters<typeof createMemories>[0];
9
10
  type UpdateMemoryArgs = Parameters<typeof updateMemory>;
10
11
  type SearchInput = Parameters<typeof searchMemories>[0];
11
12
  type DeleteMemoriesInput = Parameters<typeof deleteMemories>[0];
13
+ type CreateRelationshipInput = Parameters<typeof createRelationship>[0];
14
+ type GetRelationshipsInput = Parameters<typeof getRelationships>[0];
15
+ type DeleteRelationshipInput = Parameters<typeof deleteRelationship>[0];
16
+ type RecallInput = Parameters<typeof recallMemories>[0];
12
17
  export interface ToolDependencies {
13
18
  createMemory: (input: CreateMemoryInput) => MaybePromise<MemoryInsertResult>;
14
19
  createMemories: (input: CreateMemoriesInput) => MaybePromise<BatchStoreResult>;
@@ -18,6 +23,10 @@ export interface ToolDependencies {
18
23
  deleteMemories: (input: DeleteMemoriesInput) => MaybePromise<BatchDeleteResult>;
19
24
  searchMemories: (input: SearchInput) => MaybePromise<SearchResult[]>;
20
25
  getStats: () => MaybePromise<MemoryStats>;
26
+ createRelationship: (input: CreateRelationshipInput) => MaybePromise<CreateRelationshipResult>;
27
+ getRelationships: (input: GetRelationshipsInput) => MaybePromise<Relationship[]>;
28
+ deleteRelationship: (input: DeleteRelationshipInput) => MaybePromise<StatementResult>;
29
+ recallMemories: (input: RecallInput) => MaybePromise<RecallResult>;
21
30
  }
22
31
  export declare function registerAllTools(server: McpServer, deps?: ToolDependencies): void;
23
32
  export {};
package/dist/tools.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { deleteMemories, deleteMemory, getMemory, getStats, } from './core/memory-read.js';
2
2
  import { createMemories, createMemory, updateMemory, } from './core/memory-write.js';
3
- import { searchMemories } from './core/search.js';
4
- import { DefaultOutputSchema, DeleteMemoriesInputSchema, DeleteMemoryInputSchema, GetMemoryInputSchema, MemoryStatsInputSchema, SearchMemoriesInputSchema, StoreMemoriesInputSchema, StoreMemoryInputSchema, UpdateMemoryInputSchema, } from './schemas.js';
3
+ import { createRelationship, deleteRelationship, getRelationships, } from './core/relationships.js';
4
+ import { recallMemories, searchMemories } from './core/search.js';
5
+ import { CreateRelationshipInputSchema, DefaultOutputSchema, DeleteMemoriesInputSchema, DeleteMemoryInputSchema, DeleteRelationshipInputSchema, GetMemoryInputSchema, GetRelationshipsInputSchema, MemoryStatsInputSchema, RecallInputSchema, SearchMemoriesInputSchema, StoreMemoriesInputSchema, StoreMemoryInputSchema, UpdateMemoryInputSchema, } from './schemas.js';
5
6
  const defaultDeps = {
6
7
  createMemory,
7
8
  createMemories,
@@ -11,6 +12,10 @@ const defaultDeps = {
11
12
  deleteMemories,
12
13
  searchMemories,
13
14
  getStats,
15
+ createRelationship,
16
+ getRelationships,
17
+ deleteRelationship,
18
+ recallMemories,
14
19
  };
15
20
  const getErrorMessage = (error) => {
16
21
  if (error instanceof Error)
@@ -64,6 +69,10 @@ const buildCoreTools = (deps) => [
64
69
  const result = await deps.createMemory({
65
70
  content: input.content,
66
71
  tags: input.tags,
72
+ ...(input.importance !== undefined && { importance: input.importance }),
73
+ ...(input.memory_type !== undefined && {
74
+ memory_type: input.memory_type,
75
+ }),
67
76
  });
68
77
  return ok(result);
69
78
  }),
@@ -79,7 +88,15 @@ const buildCoreTools = (deps) => [
79
88
  },
80
89
  handler: wrapHandler('E_STORE_MEMORIES', async (params) => {
81
90
  const input = StoreMemoriesInputSchema.parse(params);
82
- const result = await deps.createMemories(input.items);
91
+ const items = input.items.map((item) => ({
92
+ content: item.content,
93
+ tags: item.tags,
94
+ ...(item.importance !== undefined && { importance: item.importance }),
95
+ ...(item.memory_type !== undefined && {
96
+ memory_type: item.memory_type,
97
+ }),
98
+ }));
99
+ const result = await deps.createMemories(items);
83
100
  return ok(result);
84
101
  }),
85
102
  },
@@ -169,6 +186,86 @@ const buildSearchTools = (deps) => [
169
186
  return ok(result);
170
187
  }),
171
188
  },
189
+ {
190
+ name: 'recall',
191
+ options: {
192
+ title: 'Recall Memories',
193
+ description: 'Search for memories and traverse relationships to return a connected graph cluster. ' +
194
+ 'Use this for deeper context retrieval that follows knowledge graph connections.',
195
+ inputSchema: RecallInputSchema,
196
+ outputSchema: DefaultOutputSchema,
197
+ annotations: { readOnlyHint: true },
198
+ },
199
+ handler: wrapHandler('E_RECALL', async (params) => {
200
+ const input = RecallInputSchema.parse(params);
201
+ const result = await deps.recallMemories({
202
+ query: input.query,
203
+ ...(input.depth !== undefined && { depth: input.depth }),
204
+ });
205
+ return ok(result);
206
+ }),
207
+ },
208
+ ];
209
+ const buildRelationshipTools = (deps) => [
210
+ {
211
+ name: 'create_relationship',
212
+ options: {
213
+ title: 'Create Relationship',
214
+ description: 'Link two memories with a typed relationship. Creates a knowledge graph edge between memories.',
215
+ inputSchema: CreateRelationshipInputSchema,
216
+ outputSchema: DefaultOutputSchema,
217
+ annotations: { idempotentHint: true },
218
+ },
219
+ handler: wrapHandler('E_CREATE_RELATIONSHIP', async (params) => {
220
+ const input = CreateRelationshipInputSchema.parse(params);
221
+ const result = await deps.createRelationship({
222
+ from_hash: normalizeHash(input.from_hash),
223
+ to_hash: normalizeHash(input.to_hash),
224
+ relation_type: input.relation_type,
225
+ });
226
+ return ok(result);
227
+ }),
228
+ },
229
+ {
230
+ name: 'get_relationships',
231
+ options: {
232
+ title: 'Get Relationships',
233
+ description: 'Get all relationships for a memory. Returns linked memories with relationship types.',
234
+ inputSchema: GetRelationshipsInputSchema,
235
+ outputSchema: DefaultOutputSchema,
236
+ annotations: { readOnlyHint: true },
237
+ },
238
+ handler: wrapHandler('E_GET_RELATIONSHIPS', async (params) => {
239
+ const input = GetRelationshipsInputSchema.parse(params);
240
+ const result = await deps.getRelationships({
241
+ hash: normalizeHash(input.hash),
242
+ ...(input.direction !== undefined && { direction: input.direction }),
243
+ });
244
+ return ok(result);
245
+ }),
246
+ },
247
+ {
248
+ name: 'delete_relationship',
249
+ options: {
250
+ title: 'Delete Relationship',
251
+ description: 'Remove a relationship between two memories.',
252
+ inputSchema: DeleteRelationshipInputSchema,
253
+ outputSchema: DefaultOutputSchema,
254
+ annotations: { destructiveHint: true },
255
+ },
256
+ handler: wrapHandler('E_DELETE_RELATIONSHIP', async (params) => {
257
+ const input = DeleteRelationshipInputSchema.parse(params);
258
+ const result = await deps.deleteRelationship({
259
+ from_hash: normalizeHash(input.from_hash),
260
+ to_hash: normalizeHash(input.to_hash),
261
+ relation_type: input.relation_type,
262
+ });
263
+ if (result.changes === 0) {
264
+ return createErrorResponse('E_NOT_FOUND', 'Relationship not found');
265
+ }
266
+ return ok({ deleted: true });
267
+ }),
268
+ },
172
269
  ];
173
270
  const buildStatsTools = (deps) => [
174
271
  {
@@ -190,6 +287,7 @@ const buildStatsTools = (deps) => [
190
287
  const buildTools = (deps) => [
191
288
  ...buildCoreTools(deps),
192
289
  ...buildSearchTools(deps),
290
+ ...buildRelationshipTools(deps),
193
291
  ...buildStatsTools(deps),
194
292
  ];
195
293
  export function registerAllTools(server, deps = defaultDeps) {
package/dist/types.d.ts CHANGED
@@ -1,7 +1,11 @@
1
+ export declare const MEMORY_TYPES: readonly ["general", "fact", "plan", "decision", "reflection", "lesson", "error", "gradient"];
2
+ export type MemoryType = (typeof MEMORY_TYPES)[number];
1
3
  export interface Memory {
2
4
  readonly id: number;
3
5
  readonly content: string;
4
6
  readonly summary: string | undefined;
7
+ readonly importance: number;
8
+ readonly memory_type: MemoryType;
5
9
  readonly created_at: string;
6
10
  readonly accessed_at: string;
7
11
  readonly hash: string;
@@ -28,12 +32,16 @@ export interface MemoryStats {
28
32
  readonly oldestMemory: string | null;
29
33
  readonly newestMemory: string | null;
30
34
  }
31
- export interface BatchStoreItemResult {
35
+ export type BatchStoreItemResult = {
36
+ readonly ok: true;
32
37
  readonly index: number;
33
- readonly hash?: string;
34
- readonly isNew?: boolean;
35
- readonly error?: string;
36
- }
38
+ readonly hash: string;
39
+ readonly isNew: boolean;
40
+ } | {
41
+ readonly ok: false;
42
+ readonly index: number;
43
+ readonly error: string;
44
+ };
37
45
  export interface BatchStoreResult {
38
46
  readonly results: BatchStoreItemResult[];
39
47
  readonly succeeded: number;
@@ -49,3 +57,19 @@ export interface BatchDeleteResult {
49
57
  readonly succeeded: number;
50
58
  readonly failed: number;
51
59
  }
60
+ export interface Relationship {
61
+ readonly id: number;
62
+ readonly from_hash: string;
63
+ readonly to_hash: string;
64
+ readonly relation_type: string;
65
+ readonly created_at: string;
66
+ }
67
+ export interface CreateRelationshipResult {
68
+ readonly id: number;
69
+ readonly isNew: boolean;
70
+ }
71
+ export interface RecallResult {
72
+ readonly memories: SearchResult[];
73
+ readonly relationships: Relationship[];
74
+ readonly depth: number;
75
+ }
package/dist/types.js CHANGED
@@ -1 +1,10 @@
1
- export {};
1
+ export const MEMORY_TYPES = [
2
+ 'general',
3
+ 'fact',
4
+ 'plan',
5
+ 'decision',
6
+ 'reflection',
7
+ 'lesson',
8
+ 'error',
9
+ 'gradient',
10
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@j0hanz/memdb",
3
- "version": "1.1.5",
3
+ "version": "1.2.1",
4
4
  "mcpName": "io.github.j0hanz/memdb",
5
5
  "description": "A SQLite-backed MCP memory server with local workspace storage.",
6
6
  "type": "module",
@@ -18,7 +18,6 @@
18
18
  "README.md"
19
19
  ],
20
20
  "scripts": {
21
- "clean": "node -e \"require('fs').rmSync('dist', {recursive: true, force: true})\"",
22
21
  "build": "tsc -p tsconfig.build.json && node -e \"require('fs').chmodSync('dist/index.js', '755')\"",
23
22
  "prepare": "npm run build",
24
23
  "dev": "tsx watch src/index.ts",
@@ -29,7 +28,7 @@
29
28
  "lint": "eslint .",
30
29
  "format": "prettier --write .",
31
30
  "format:check": "prettier --check .",
32
- "type-check": "tsc --noEmit",
31
+ "type-check": "tsc -p tsconfig.json --noEmit",
33
32
  "type-check:test": "tsc -p tsconfig.test.json --noEmit",
34
33
  "inspector": "npx @modelcontextprotocol/inspector",
35
34
  "prepublishOnly": "npm run lint && npm run type-check && npm run build"
@@ -62,6 +61,7 @@
62
61
  "eslint-plugin-depend": "^1.4.0",
63
62
  "eslint-plugin-sonarjs": "^3.0.5",
64
63
  "eslint-plugin-unused-imports": "^4.3.0",
64
+ "knip": "^5.80.2",
65
65
  "prettier": "^3.7.4",
66
66
  "tsx": "^4.21.0",
67
67
  "typescript": "^5.9.3",
@@ -1,2 +0,0 @@
1
- export declare const SCHEMA_SQL = "\n PRAGMA foreign_keys = ON;\n PRAGMA journal_mode = WAL;\n PRAGMA synchronous = NORMAL;\n\n CREATE TABLE IF NOT EXISTS memories (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n content TEXT NOT NULL,\n summary TEXT,\n importance INTEGER DEFAULT 0,\n memory_type TEXT DEFAULT 'general',\n created_at TEXT DEFAULT CURRENT_TIMESTAMP,\n accessed_at TEXT DEFAULT CURRENT_TIMESTAMP,\n hash TEXT UNIQUE NOT NULL\n ) STRICT;\n\n CREATE TABLE IF NOT EXISTS tags (\n memory_id INTEGER NOT NULL,\n tag TEXT NOT NULL,\n PRIMARY KEY (memory_id, tag),\n FOREIGN KEY (memory_id) REFERENCES memories(id) ON DELETE CASCADE\n ) STRICT;\n\n CREATE INDEX IF NOT EXISTS idx_tags_tag_memory_id ON tags(tag, memory_id);\n\n CREATE TABLE IF NOT EXISTS relationships (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n from_memory_id INTEGER NOT NULL,\n to_memory_id INTEGER NOT NULL,\n relation_type TEXT NOT NULL,\n created_at TEXT DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (from_memory_id) REFERENCES memories(id) ON DELETE CASCADE,\n FOREIGN KEY (to_memory_id) REFERENCES memories(id) ON DELETE CASCADE,\n UNIQUE(from_memory_id, to_memory_id, relation_type)\n ) STRICT;\n\n CREATE INDEX IF NOT EXISTS idx_relationships_to_memory_id ON relationships(to_memory_id);\n\n CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(\n content,\n summary,\n content_rowid='id'\n );\n\n CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN\n INSERT INTO memories_fts(rowid, content, summary)\n VALUES (new.id, new.content, new.summary);\n END;\n\n CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN\n DELETE FROM memories_fts WHERE rowid = old.id;\n INSERT INTO memories_fts(rowid, content, summary)\n VALUES (new.id, new.content, new.summary);\n END;\n\n CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN\n DELETE FROM memories_fts WHERE rowid = old.id;\n END;\n";
2
- export declare const FTS_SYNC_SQL = "\n INSERT INTO memories_fts(rowid, content, summary)\n SELECT id, content, summary FROM memories\n WHERE id NOT IN (SELECT rowid FROM memories_fts);\n";
@@ -1 +0,0 @@
1
- {"version":3,"file":"database-schema.d.ts","sourceRoot":"","sources":["../../src/core/database-schema.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,28DA0DtB,CAAC;AAEF,eAAO,MAAM,YAAY,8JAIxB,CAAC"}