@199-bio/engram 0.3.0 → 0.3.2

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/dist/index.js CHANGED
@@ -42,23 +42,24 @@ async function initialize() {
42
42
  // ============ MCP Server ============
43
43
  const server = new Server({
44
44
  name: "engram",
45
- version: "0.3.0",
45
+ version: "0.3.2",
46
46
  }, {
47
47
  capabilities: {
48
48
  tools: {},
49
49
  },
50
50
  });
51
51
  // Tool definitions with MCP 2025-06-18 annotations
52
+ // Descriptions are carefully written to guide Claude on when to use each tool
52
53
  const TOOLS = [
53
54
  {
54
55
  name: "remember",
55
- description: "Store a new memory. Automatically extracts entities and relationships for the knowledge graph.",
56
+ description: "PRIMARY STORAGE TOOL. Use this for ALL new information - conversations, facts, observations, notes. Automatically extracts people, organizations, and places as entities and creates relationships. Do NOT also call create_entity/observe/relate - remember handles entity extraction automatically. Only use remember once per piece of information.",
56
57
  inputSchema: {
57
58
  type: "object",
58
59
  properties: {
59
60
  content: {
60
61
  type: "string",
61
- description: "The memory content to store",
62
+ description: "The information to store - can be a conversation snippet, fact, observation, or note",
62
63
  },
63
64
  source: {
64
65
  type: "string",
@@ -67,7 +68,7 @@ const TOOLS = [
67
68
  },
68
69
  importance: {
69
70
  type: "number",
70
- description: "Importance score from 0 to 1 (higher = more important)",
71
+ description: "Importance score from 0 to 1 (higher = more important). Use 0.7+ for key facts, 0.3- for casual mentions",
71
72
  minimum: 0,
72
73
  maximum: 1,
73
74
  default: 0.5,
@@ -85,7 +86,7 @@ const TOOLS = [
85
86
  },
86
87
  {
87
88
  name: "recall",
88
- description: "Retrieve relevant memories using hybrid search (BM25 keywords + semantic + knowledge graph). Returns the most relevant memories for a query.",
89
+ description: "PRIMARY SEARCH TOOL. Use this FIRST when answering any question about stored information. Searches across all memories using semantic understanding, keywords, and knowledge graph connections. Returns relevant memories with context.",
89
90
  inputSchema: {
90
91
  type: "object",
91
92
  properties: {
@@ -116,7 +117,7 @@ const TOOLS = [
116
117
  },
117
118
  {
118
119
  name: "forget",
119
- description: "Remove a specific memory by its ID",
120
+ description: "Delete a specific memory by its ID. Use only when explicitly asked to remove information.",
120
121
  inputSchema: {
121
122
  type: "object",
122
123
  properties: {
@@ -135,94 +136,9 @@ const TOOLS = [
135
136
  openWorldHint: false,
136
137
  },
137
138
  },
138
- {
139
- name: "create_entity",
140
- description: "Create a new entity in the knowledge graph (person, place, concept, etc.)",
141
- inputSchema: {
142
- type: "object",
143
- properties: {
144
- name: {
145
- type: "string",
146
- description: "Entity name (e.g., 'John', 'Paris', 'Machine Learning')",
147
- },
148
- type: {
149
- type: "string",
150
- enum: ["person", "place", "concept", "event", "organization"],
151
- description: "Type of entity",
152
- },
153
- },
154
- required: ["name", "type"],
155
- },
156
- annotations: {
157
- title: "Create Entity",
158
- readOnlyHint: false,
159
- destructiveHint: false,
160
- idempotentHint: true,
161
- openWorldHint: false,
162
- },
163
- },
164
- {
165
- name: "observe",
166
- description: "Add an observation or fact about an entity",
167
- inputSchema: {
168
- type: "object",
169
- properties: {
170
- entity: {
171
- type: "string",
172
- description: "Entity name to add observation to",
173
- },
174
- observation: {
175
- type: "string",
176
- description: "The fact or observation to record",
177
- },
178
- confidence: {
179
- type: "number",
180
- description: "Confidence in this observation (0-1)",
181
- default: 1.0,
182
- },
183
- },
184
- required: ["entity", "observation"],
185
- },
186
- annotations: {
187
- title: "Add Observation",
188
- readOnlyHint: false,
189
- destructiveHint: false,
190
- idempotentHint: false,
191
- openWorldHint: false,
192
- },
193
- },
194
- {
195
- name: "relate",
196
- description: "Create a relationship between two entities in the knowledge graph",
197
- inputSchema: {
198
- type: "object",
199
- properties: {
200
- from: {
201
- type: "string",
202
- description: "Source entity name",
203
- },
204
- to: {
205
- type: "string",
206
- description: "Target entity name",
207
- },
208
- relation: {
209
- type: "string",
210
- description: "Type of relationship (e.g., 'sibling', 'works_at', 'knows', 'located_in')",
211
- },
212
- },
213
- required: ["from", "to", "relation"],
214
- },
215
- annotations: {
216
- title: "Create Relationship",
217
- readOnlyHint: false,
218
- destructiveHint: false,
219
- idempotentHint: true,
220
- openWorldHint: false,
221
- },
222
- },
223
139
  {
224
140
  name: "query_entity",
225
- description: "Get detailed information about an entity including all observations and relationships",
141
+ description: "Get all stored information about a specific person, place, or organization. Use after recall to get deeper details about an entity mentioned in search results.",
226
142
  inputSchema: {
227
143
  type: "object",
228
144
  properties: {
@@ -243,7 +159,7 @@ const TOOLS = [
243
159
  },
244
160
  {
245
161
  name: "list_entities",
246
- description: "List all entities, optionally filtered by type",
162
+ description: "List all known entities (people, places, organizations, etc.). Use to browse the knowledge graph or find entity names for query_entity.",
247
163
  inputSchema: {
248
164
  type: "object",
249
165
  properties: {
@@ -391,68 +307,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
391
307
  ],
392
308
  };
393
309
  }
394
- case "create_entity": {
395
- const { name: entityName, type } = args;
396
- const entity = graph.getOrCreateEntity(entityName, type);
397
- return {
398
- content: [
399
- {
400
- type: "text",
401
- text: JSON.stringify({
402
- success: true,
403
- entity: {
404
- id: entity.id,
405
- name: entity.name,
406
- type: entity.type,
407
- },
408
- }, null, 2),
409
- },
410
- ],
411
- };
412
- }
413
- case "observe": {
414
- const { entity: entityName, observation, confidence = 1.0 } = args;
415
- // Ensure entity exists
416
- const entity = graph.getOrCreateEntity(entityName, "person");
417
- // Add observation
418
- const obs = graph.addObservation(entity.id, observation, undefined, confidence);
419
- return {
420
- content: [
421
- {
422
- type: "text",
423
- text: JSON.stringify({
424
- success: true,
425
- entity: entity.name,
426
- observation_id: obs.id,
427
- }, null, 2),
428
- },
429
- ],
430
- };
431
- }
432
- case "relate": {
433
- const { from, to, relation } = args;
434
- // Ensure both entities exist
435
- const fromEntity = graph.getOrCreateEntity(from, "person");
436
- const toEntity = graph.getOrCreateEntity(to, "person");
437
- // Create relation
438
- const rel = graph.relate(fromEntity.id, toEntity.id, relation);
439
- return {
440
- content: [
441
- {
442
- type: "text",
443
- text: JSON.stringify({
444
- success: true,
445
- relation: {
446
- id: rel.id,
447
- from: fromEntity.name,
448
- to: toEntity.name,
449
- type: rel.type,
450
- },
451
- }, null, 2),
452
- },
453
- ],
454
- };
455
- }
456
310
  case "query_entity": {
457
311
  const { entity: entityName } = args;
458
312
  const details = graph.getEntityDetails(entityName);
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@199-bio/engram",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Give Claude a perfect memory. Local-first MCP server with hybrid search.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -60,7 +60,7 @@ async function initialize(): Promise<void> {
60
60
  const server = new Server(
61
61
  {
62
62
  name: "engram",
63
- version: "0.3.0",
63
+ version: "0.3.2",
64
64
  },
65
65
  {
66
66
  capabilities: {
@@ -70,17 +70,18 @@ const server = new Server(
70
70
  );
71
71
 
72
72
  // Tool definitions with MCP 2025-06-18 annotations
73
+ // Descriptions are carefully written to guide Claude on when to use each tool
73
74
  const TOOLS = [
74
75
  {
75
76
  name: "remember",
76
77
  description:
77
- "Store a new memory. Automatically extracts entities and relationships for the knowledge graph.",
78
+ "PRIMARY STORAGE TOOL. Use this for ALL new information - conversations, facts, observations, notes. Automatically extracts people, organizations, and places as entities and creates relationships. Do NOT also call create_entity/observe/relate - remember handles entity extraction automatically. Only use remember once per piece of information.",
78
79
  inputSchema: {
79
80
  type: "object" as const,
80
81
  properties: {
81
82
  content: {
82
83
  type: "string",
83
- description: "The memory content to store",
84
+ description: "The information to store - can be a conversation snippet, fact, observation, or note",
84
85
  },
85
86
  source: {
86
87
  type: "string",
@@ -89,7 +90,7 @@ const TOOLS = [
89
90
  },
90
91
  importance: {
91
92
  type: "number",
92
- description: "Importance score from 0 to 1 (higher = more important)",
93
+ description: "Importance score from 0 to 1 (higher = more important). Use 0.7+ for key facts, 0.3- for casual mentions",
93
94
  minimum: 0,
94
95
  maximum: 1,
95
96
  default: 0.5,
@@ -108,7 +109,7 @@ const TOOLS = [
108
109
  {
109
110
  name: "recall",
110
111
  description:
111
- "Retrieve relevant memories using hybrid search (BM25 keywords + semantic + knowledge graph). Returns the most relevant memories for a query.",
112
+ "PRIMARY SEARCH TOOL. Use this FIRST when answering any question about stored information. Searches across all memories using semantic understanding, keywords, and knowledge graph connections. Returns relevant memories with context.",
112
113
  inputSchema: {
113
114
  type: "object" as const,
114
115
  properties: {
@@ -139,7 +140,7 @@ const TOOLS = [
139
140
  },
140
141
  {
141
142
  name: "forget",
142
- description: "Remove a specific memory by its ID",
143
+ description: "Delete a specific memory by its ID. Use only when explicitly asked to remove information.",
143
144
  inputSchema: {
144
145
  type: "object" as const,
145
146
  properties: {
@@ -158,94 +159,9 @@ const TOOLS = [
158
159
  openWorldHint: false,
159
160
  },
160
161
  },
161
- {
162
- name: "create_entity",
163
- description: "Create a new entity in the knowledge graph (person, place, concept, etc.)",
164
- inputSchema: {
165
- type: "object" as const,
166
- properties: {
167
- name: {
168
- type: "string",
169
- description: "Entity name (e.g., 'John', 'Paris', 'Machine Learning')",
170
- },
171
- type: {
172
- type: "string",
173
- enum: ["person", "place", "concept", "event", "organization"],
174
- description: "Type of entity",
175
- },
176
- },
177
- required: ["name", "type"],
178
- },
179
- annotations: {
180
- title: "Create Entity",
181
- readOnlyHint: false,
182
- destructiveHint: false,
183
- idempotentHint: true,
184
- openWorldHint: false,
185
- },
186
- },
187
- {
188
- name: "observe",
189
- description: "Add an observation or fact about an entity",
190
- inputSchema: {
191
- type: "object" as const,
192
- properties: {
193
- entity: {
194
- type: "string",
195
- description: "Entity name to add observation to",
196
- },
197
- observation: {
198
- type: "string",
199
- description: "The fact or observation to record",
200
- },
201
- confidence: {
202
- type: "number",
203
- description: "Confidence in this observation (0-1)",
204
- default: 1.0,
205
- },
206
- },
207
- required: ["entity", "observation"],
208
- },
209
- annotations: {
210
- title: "Add Observation",
211
- readOnlyHint: false,
212
- destructiveHint: false,
213
- idempotentHint: false,
214
- openWorldHint: false,
215
- },
216
- },
217
- {
218
- name: "relate",
219
- description: "Create a relationship between two entities in the knowledge graph",
220
- inputSchema: {
221
- type: "object" as const,
222
- properties: {
223
- from: {
224
- type: "string",
225
- description: "Source entity name",
226
- },
227
- to: {
228
- type: "string",
229
- description: "Target entity name",
230
- },
231
- relation: {
232
- type: "string",
233
- description: "Type of relationship (e.g., 'sibling', 'works_at', 'knows', 'located_in')",
234
- },
235
- },
236
- required: ["from", "to", "relation"],
237
- },
238
- annotations: {
239
- title: "Create Relationship",
240
- readOnlyHint: false,
241
- destructiveHint: false,
242
- idempotentHint: true,
243
- openWorldHint: false,
244
- },
245
- },
246
162
  {
247
163
  name: "query_entity",
248
- description: "Get detailed information about an entity including all observations and relationships",
164
+ description: "Get all stored information about a specific person, place, or organization. Use after recall to get deeper details about an entity mentioned in search results.",
249
165
  inputSchema: {
250
166
  type: "object" as const,
251
167
  properties: {
@@ -266,7 +182,7 @@ const TOOLS = [
266
182
  },
267
183
  {
268
184
  name: "list_entities",
269
- description: "List all entities, optionally filtered by type",
185
+ description: "List all known entities (people, places, organizations, etc.). Use to browse the knowledge graph or find entity names for query_entity.",
270
186
  inputSchema: {
271
187
  type: "object" as const,
272
188
  properties: {
@@ -439,90 +355,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
439
355
  };
440
356
  }
441
357
 
442
- case "create_entity": {
443
- const { name: entityName, type } = args as {
444
- name: string;
445
- type: "person" | "place" | "concept" | "event" | "organization";
446
- };
447
-
448
- const entity = graph.getOrCreateEntity(entityName, type);
449
-
450
- return {
451
- content: [
452
- {
453
- type: "text" as const,
454
- text: JSON.stringify({
455
- success: true,
456
- entity: {
457
- id: entity.id,
458
- name: entity.name,
459
- type: entity.type,
460
- },
461
- }, null, 2),
462
- },
463
- ],
464
- };
465
- }
466
-
467
- case "observe": {
468
- const { entity: entityName, observation, confidence = 1.0 } = args as {
469
- entity: string;
470
- observation: string;
471
- confidence?: number;
472
- };
473
-
474
- // Ensure entity exists
475
- const entity = graph.getOrCreateEntity(entityName, "person");
476
-
477
- // Add observation
478
- const obs = graph.addObservation(entity.id, observation, undefined, confidence);
479
-
480
- return {
481
- content: [
482
- {
483
- type: "text" as const,
484
- text: JSON.stringify({
485
- success: true,
486
- entity: entity.name,
487
- observation_id: obs.id,
488
- }, null, 2),
489
- },
490
- ],
491
- };
492
- }
493
-
494
- case "relate": {
495
- const { from, to, relation } = args as {
496
- from: string;
497
- to: string;
498
- relation: string;
499
- };
500
-
501
- // Ensure both entities exist
502
- const fromEntity = graph.getOrCreateEntity(from, "person");
503
- const toEntity = graph.getOrCreateEntity(to, "person");
504
-
505
- // Create relation
506
- const rel = graph.relate(fromEntity.id, toEntity.id, relation);
507
-
508
- return {
509
- content: [
510
- {
511
- type: "text" as const,
512
- text: JSON.stringify({
513
- success: true,
514
- relation: {
515
- id: rel.id,
516
- from: fromEntity.name,
517
- to: toEntity.name,
518
- type: rel.type,
519
- },
520
- }, null, 2),
521
- },
522
- ],
523
- };
524
- }
525
-
526
358
  case "query_entity": {
527
359
  const { entity: entityName } = args as { entity: string };
528
360
 
@@ -266,12 +266,140 @@ export class EngramDatabase {
266
266
  }
267
267
 
268
268
  findEntityByName(name: string): Entity | null {
269
+ // First try exact match (case-insensitive)
269
270
  const row = this.stmt("SELECT * FROM entities WHERE LOWER(name) = LOWER(?)").get(name) as Record<string, unknown> | undefined;
270
- return row ? this.rowToEntity(row) : null;
271
+ if (row) return this.rowToEntity(row);
272
+
273
+ // If no exact match, try fuzzy match for potential duplicates
274
+ const fuzzyMatch = this.findSimilarEntity(name);
275
+ return fuzzyMatch;
276
+ }
277
+
278
+ /**
279
+ * Find a similar entity using fuzzy matching
280
+ * Catches "Boris D" matching "Boris Djordjevic", "John" matching "John Smith"
281
+ */
282
+ findSimilarEntity(name: string, threshold: number = 0.8): Entity | null {
283
+ const normalizedName = name.toLowerCase().trim();
284
+ const nameWords = normalizedName.split(/\s+/);
285
+
286
+ // Get candidates: entities that share at least one word with the query
287
+ const candidates = this.stmt(`
288
+ SELECT * FROM entities
289
+ WHERE LOWER(name) LIKE ?
290
+ LIMIT 100
291
+ `).all(`%${nameWords[0]}%`) as Record<string, unknown>[];
292
+
293
+ let bestMatch: Entity | null = null;
294
+ let bestScore = 0;
295
+
296
+ for (const row of candidates) {
297
+ const entity = this.rowToEntity(row);
298
+ const entityName = entity.name.toLowerCase();
299
+ const entityWords = entityName.split(/\s+/);
300
+
301
+ // Calculate similarity score
302
+ const score = this.calculateNameSimilarity(nameWords, entityWords, normalizedName, entityName);
303
+
304
+ if (score >= threshold && score > bestScore) {
305
+ bestScore = score;
306
+ bestMatch = entity;
307
+ }
308
+ }
309
+
310
+ return bestMatch;
311
+ }
312
+
313
+ /**
314
+ * Calculate similarity between two names
315
+ * Returns 0-1 score (1 = identical)
316
+ */
317
+ private calculateNameSimilarity(
318
+ words1: string[],
319
+ words2: string[],
320
+ full1: string,
321
+ full2: string
322
+ ): number {
323
+ // Exact match
324
+ if (full1 === full2) return 1.0;
325
+
326
+ // One is prefix of the other (e.g., "Boris" vs "Boris Djordjevic")
327
+ if (full1.startsWith(full2 + " ") || full2.startsWith(full1 + " ")) {
328
+ return 0.9;
329
+ }
330
+
331
+ // First word matches (e.g., "John" vs "John Smith")
332
+ if (words1[0] === words2[0]) {
333
+ // Same first word, different lengths
334
+ const longer = Math.max(words1.length, words2.length);
335
+ const shorter = Math.min(words1.length, words2.length);
336
+ return 0.7 + (0.2 * shorter / longer);
337
+ }
338
+
339
+ // Check for abbreviated names (e.g., "Boris D" vs "Boris Djordjevic")
340
+ if (words1.length >= 2 && words2.length >= 2) {
341
+ const last1 = words1[words1.length - 1];
342
+ const last2 = words2[words2.length - 1];
343
+
344
+ // Check if one is abbreviation of the other
345
+ if (last1.length === 1 && last2.startsWith(last1)) {
346
+ return 0.85;
347
+ }
348
+ if (last2.length === 1 && last1.startsWith(last2)) {
349
+ return 0.85;
350
+ }
351
+ }
352
+
353
+ // Count shared words
354
+ const set1 = new Set(words1);
355
+ const shared = words2.filter(w => set1.has(w)).length;
356
+ const total = Math.max(words1.length, words2.length);
357
+
358
+ return shared / total * 0.7;
359
+ }
360
+
361
+ /**
362
+ * Find all potential duplicate entities
363
+ */
364
+ findDuplicateEntities(): Array<{ entity: Entity; potentialDuplicates: Entity[] }> {
365
+ const entities = this.listEntities(undefined, 1000);
366
+ const duplicates: Array<{ entity: Entity; potentialDuplicates: Entity[] }> = [];
367
+ const processed = new Set<string>();
368
+
369
+ for (const entity of entities) {
370
+ if (processed.has(entity.id)) continue;
371
+
372
+ const potentialDupes: Entity[] = [];
373
+ const words = entity.name.toLowerCase().split(/\s+/);
374
+
375
+ for (const other of entities) {
376
+ if (other.id === entity.id || processed.has(other.id)) continue;
377
+
378
+ const otherWords = other.name.toLowerCase().split(/\s+/);
379
+ const score = this.calculateNameSimilarity(
380
+ words, otherWords,
381
+ entity.name.toLowerCase(),
382
+ other.name.toLowerCase()
383
+ );
384
+
385
+ if (score >= 0.8) {
386
+ potentialDupes.push(other);
387
+ processed.add(other.id);
388
+ }
389
+ }
390
+
391
+ if (potentialDupes.length > 0) {
392
+ duplicates.push({ entity, potentialDuplicates: potentialDupes });
393
+ processed.add(entity.id);
394
+ }
395
+ }
396
+
397
+ return duplicates;
271
398
  }
272
399
 
273
400
  searchEntities(query: string, type?: Entity["type"]): Entity[] {
274
- let sql = "SELECT * FROM entities WHERE name LIKE ?";
401
+ // Case-insensitive search
402
+ let sql = "SELECT * FROM entities WHERE LOWER(name) LIKE LOWER(?)";
275
403
  const params: unknown[] = [`%${query}%`];
276
404
 
277
405
  if (type) {