@199-bio/engram 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.
- package/.env.example +19 -0
- package/LICENSE +21 -0
- package/LIVING_PLAN.md +180 -0
- package/PLAN.md +514 -0
- package/README.md +304 -0
- package/dist/graph/extractor.d.ts.map +1 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/knowledge-graph.d.ts.map +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +473 -0
- package/dist/retrieval/colbert.d.ts.map +1 -0
- package/dist/retrieval/hybrid.d.ts.map +1 -0
- package/dist/retrieval/index.d.ts.map +1 -0
- package/dist/storage/database.d.ts.map +1 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/package.json +62 -0
- package/src/graph/extractor.ts +441 -0
- package/src/graph/index.ts +2 -0
- package/src/graph/knowledge-graph.ts +263 -0
- package/src/index.ts +558 -0
- package/src/retrieval/colbert-bridge.py +222 -0
- package/src/retrieval/colbert.ts +317 -0
- package/src/retrieval/hybrid.ts +218 -0
- package/src/retrieval/index.ts +2 -0
- package/src/storage/database.ts +527 -0
- package/src/storage/index.ts +1 -0
- package/tests/test-interactive.js +218 -0
- package/tests/test-mcp.sh +81 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Interactive test for Engram MCP server
|
|
4
|
+
* Sends multiple requests and captures responses
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { spawn } from "child_process";
|
|
8
|
+
import { createInterface } from "readline";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
|
|
15
|
+
const requests = [
|
|
16
|
+
// 1. Stats (empty)
|
|
17
|
+
{
|
|
18
|
+
name: "Stats (empty)",
|
|
19
|
+
request: { method: "tools/call", params: { name: "stats", arguments: {} } },
|
|
20
|
+
},
|
|
21
|
+
// 2. Remember Sarah
|
|
22
|
+
{
|
|
23
|
+
name: "Remember Sarah background",
|
|
24
|
+
request: {
|
|
25
|
+
method: "tools/call",
|
|
26
|
+
params: {
|
|
27
|
+
name: "remember",
|
|
28
|
+
arguments: {
|
|
29
|
+
content: "Sarah Chen is the VP of Engineering at Acme Corp. She's allergic to shellfish, prefers window seats on flights, and is leading the Q1 product launch.",
|
|
30
|
+
importance: 0.9,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
// 3. Remember Sarah preferences
|
|
36
|
+
{
|
|
37
|
+
name: "Remember Sarah preferences",
|
|
38
|
+
request: {
|
|
39
|
+
method: "tools/call",
|
|
40
|
+
params: {
|
|
41
|
+
name: "remember",
|
|
42
|
+
arguments: {
|
|
43
|
+
content: "Sarah prefers async communication over meetings. She's most productive in the mornings and usually blocks her calendar before 10am for deep work.",
|
|
44
|
+
importance: 0.8,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
// 4. Remember John
|
|
50
|
+
{
|
|
51
|
+
name: "Remember John",
|
|
52
|
+
request: {
|
|
53
|
+
method: "tools/call",
|
|
54
|
+
params: {
|
|
55
|
+
name: "remember",
|
|
56
|
+
arguments: {
|
|
57
|
+
content: "John Martinez is a senior developer who reports to Sarah. He's an expert in backend systems and recently led the API redesign project.",
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
// 5. Create relationship
|
|
63
|
+
{
|
|
64
|
+
name: "Relate Sarah and John",
|
|
65
|
+
request: {
|
|
66
|
+
method: "tools/call",
|
|
67
|
+
params: {
|
|
68
|
+
name: "relate",
|
|
69
|
+
arguments: { from: "John Martinez", to: "Sarah Chen", relation: "reports_to" },
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
// 6. Add observation
|
|
74
|
+
{
|
|
75
|
+
name: "Observe Sarah allergy",
|
|
76
|
+
request: {
|
|
77
|
+
method: "tools/call",
|
|
78
|
+
params: {
|
|
79
|
+
name: "observe",
|
|
80
|
+
arguments: {
|
|
81
|
+
entity: "Sarah Chen",
|
|
82
|
+
observation: "Has severe shellfish allergy - avoid all seafood restaurants for team events",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
// 7. Query entity
|
|
88
|
+
{
|
|
89
|
+
name: "Query Sarah entity",
|
|
90
|
+
request: {
|
|
91
|
+
method: "tools/call",
|
|
92
|
+
params: { name: "query_entity", arguments: { entity: "Sarah Chen" } },
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
// 8. Recall semantic
|
|
96
|
+
{
|
|
97
|
+
name: "Recall: Sarah's work preferences",
|
|
98
|
+
request: {
|
|
99
|
+
method: "tools/call",
|
|
100
|
+
params: {
|
|
101
|
+
name: "recall",
|
|
102
|
+
arguments: { query: "How does Sarah prefer to work and communicate?" },
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
// 9. Recall by context
|
|
107
|
+
{
|
|
108
|
+
name: "Recall: Team lunch planning",
|
|
109
|
+
request: {
|
|
110
|
+
method: "tools/call",
|
|
111
|
+
params: {
|
|
112
|
+
name: "recall",
|
|
113
|
+
arguments: { query: "What should I know when planning a team lunch?" },
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
// 10. List entities
|
|
118
|
+
{
|
|
119
|
+
name: "List person entities",
|
|
120
|
+
request: {
|
|
121
|
+
method: "tools/call",
|
|
122
|
+
params: { name: "list_entities", arguments: { type: "person" } },
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
// 11. Final stats
|
|
126
|
+
{
|
|
127
|
+
name: "Final stats",
|
|
128
|
+
request: { method: "tools/call", params: { name: "stats", arguments: {} } },
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
async function main() {
|
|
133
|
+
console.log("=== Engram MCP Test Suite ===\n");
|
|
134
|
+
|
|
135
|
+
// Start the MCP server
|
|
136
|
+
const serverPath = path.join(__dirname, "..", "dist", "index.js");
|
|
137
|
+
const server = spawn("node", [serverPath], {
|
|
138
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Capture stderr for logging
|
|
142
|
+
server.stderr.on("data", (data) => {
|
|
143
|
+
const msg = data.toString().trim();
|
|
144
|
+
if (msg) console.log(`[Server] ${msg}`);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const rl = createInterface({
|
|
148
|
+
input: server.stdout,
|
|
149
|
+
crlfDelay: Infinity,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
let responseResolve;
|
|
153
|
+
let responsePromise;
|
|
154
|
+
|
|
155
|
+
rl.on("line", (line) => {
|
|
156
|
+
try {
|
|
157
|
+
const response = JSON.parse(line);
|
|
158
|
+
if (responseResolve) {
|
|
159
|
+
responseResolve(response);
|
|
160
|
+
}
|
|
161
|
+
} catch (e) {
|
|
162
|
+
// Ignore non-JSON lines
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Wait for server to be ready
|
|
167
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
168
|
+
|
|
169
|
+
// Run each test
|
|
170
|
+
let id = 1;
|
|
171
|
+
for (const test of requests) {
|
|
172
|
+
console.log(`\n--- Test ${id}: ${test.name} ---`);
|
|
173
|
+
|
|
174
|
+
responsePromise = new Promise((resolve) => {
|
|
175
|
+
responseResolve = resolve;
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const request = {
|
|
179
|
+
jsonrpc: "2.0",
|
|
180
|
+
id: id++,
|
|
181
|
+
...test.request,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
server.stdin.write(JSON.stringify(request) + "\n");
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
const response = await Promise.race([
|
|
188
|
+
responsePromise,
|
|
189
|
+
new Promise((_, reject) =>
|
|
190
|
+
setTimeout(() => reject(new Error("Timeout")), 10000)
|
|
191
|
+
),
|
|
192
|
+
]);
|
|
193
|
+
|
|
194
|
+
if (response.error) {
|
|
195
|
+
console.log(`Error: ${JSON.stringify(response.error)}`);
|
|
196
|
+
} else if (response.result?.content?.[0]?.text) {
|
|
197
|
+
const text = response.result.content[0].text;
|
|
198
|
+
try {
|
|
199
|
+
const parsed = JSON.parse(text);
|
|
200
|
+
console.log(`Result: ${JSON.stringify(parsed, null, 2)}`);
|
|
201
|
+
} catch {
|
|
202
|
+
console.log(`Result: ${text}`);
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
console.log(`Response: ${JSON.stringify(response, null, 2)}`);
|
|
206
|
+
}
|
|
207
|
+
} catch (e) {
|
|
208
|
+
console.log(`Error: ${e.message}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Cleanup
|
|
213
|
+
console.log("\n=== Tests Complete ===");
|
|
214
|
+
server.kill();
|
|
215
|
+
process.exit(0);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Test Engram MCP server with fictional examples
|
|
3
|
+
|
|
4
|
+
cd "$(dirname "$0")/.."
|
|
5
|
+
|
|
6
|
+
echo "=== Testing Engram MCP Server ==="
|
|
7
|
+
echo ""
|
|
8
|
+
|
|
9
|
+
# Function to send JSON-RPC request
|
|
10
|
+
send_request() {
|
|
11
|
+
local request="$1"
|
|
12
|
+
echo "$request" | node dist/index.js 2>/dev/null | grep -v '^\[' | head -1
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
# Test 1: Stats (empty)
|
|
16
|
+
echo "1. Testing stats (empty database)..."
|
|
17
|
+
STATS=$(send_request '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"stats","arguments":{}}}')
|
|
18
|
+
echo "Response: $STATS"
|
|
19
|
+
echo ""
|
|
20
|
+
|
|
21
|
+
# Test 2: Remember a memory about Sarah
|
|
22
|
+
echo "2. Testing remember (Sarah memory)..."
|
|
23
|
+
REMEMBER1=$(send_request '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"remember","arguments":{"content":"Sarah Chen is the VP of Engineering at Acme Corp. She is allergic to shellfish and prefers window seats on flights.","importance":0.9}}}')
|
|
24
|
+
echo "Response: $REMEMBER1"
|
|
25
|
+
echo ""
|
|
26
|
+
|
|
27
|
+
# Test 3: Remember another memory
|
|
28
|
+
echo "3. Testing remember (Sarah work preferences)..."
|
|
29
|
+
REMEMBER2=$(send_request '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"remember","arguments":{"content":"Sarah prefers async communication over meetings. She blocks her calendar before 10am for deep work.","importance":0.8}}}')
|
|
30
|
+
echo "Response: $REMEMBER2"
|
|
31
|
+
echo ""
|
|
32
|
+
|
|
33
|
+
# Test 4: Remember about John
|
|
34
|
+
echo "4. Testing remember (John memory)..."
|
|
35
|
+
REMEMBER3=$(send_request '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"remember","arguments":{"content":"John Martinez is a senior developer who reports to Sarah. He is an expert in backend systems."}}}')
|
|
36
|
+
echo "Response: $REMEMBER3"
|
|
37
|
+
echo ""
|
|
38
|
+
|
|
39
|
+
# Test 5: Create explicit relationship
|
|
40
|
+
echo "5. Testing relate (John and Sarah)..."
|
|
41
|
+
RELATE=$(send_request '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"relate","arguments":{"from":"John Martinez","to":"Sarah Chen","relation":"reports_to"}}}')
|
|
42
|
+
echo "Response: $RELATE"
|
|
43
|
+
echo ""
|
|
44
|
+
|
|
45
|
+
# Test 6: Add observation
|
|
46
|
+
echo "6. Testing observe (Sarah allergy)..."
|
|
47
|
+
OBSERVE=$(send_request '{"jsonrpc":"2.0","id":6,"method":"tools/call","params":{"name":"observe","arguments":{"entity":"Sarah Chen","observation":"Has severe shellfish allergy - avoid seafood restaurants"}}}')
|
|
48
|
+
echo "Response: $OBSERVE"
|
|
49
|
+
echo ""
|
|
50
|
+
|
|
51
|
+
# Test 7: Query entity
|
|
52
|
+
echo "7. Testing query_entity (Sarah)..."
|
|
53
|
+
QUERY=$(send_request '{"jsonrpc":"2.0","id":7,"method":"tools/call","params":{"name":"query_entity","arguments":{"entity":"Sarah Chen"}}}')
|
|
54
|
+
echo "Response: $QUERY"
|
|
55
|
+
echo ""
|
|
56
|
+
|
|
57
|
+
# Test 8: Recall - semantic search
|
|
58
|
+
echo "8. Testing recall (Sarah work preferences)..."
|
|
59
|
+
RECALL1=$(send_request '{"jsonrpc":"2.0","id":8,"method":"tools/call","params":{"name":"recall","arguments":{"query":"How does Sarah prefer to work and communicate?"}}}')
|
|
60
|
+
echo "Response: $RECALL1"
|
|
61
|
+
echo ""
|
|
62
|
+
|
|
63
|
+
# Test 9: Recall - context search
|
|
64
|
+
echo "9. Testing recall (team lunch planning)..."
|
|
65
|
+
RECALL2=$(send_request '{"jsonrpc":"2.0","id":9,"method":"tools/call","params":{"name":"recall","arguments":{"query":"What should I know when planning a team lunch?"}}}')
|
|
66
|
+
echo "Response: $RECALL2"
|
|
67
|
+
echo ""
|
|
68
|
+
|
|
69
|
+
# Test 10: List entities
|
|
70
|
+
echo "10. Testing list_entities..."
|
|
71
|
+
LIST=$(send_request '{"jsonrpc":"2.0","id":10,"method":"tools/call","params":{"name":"list_entities","arguments":{"type":"person"}}}')
|
|
72
|
+
echo "Response: $LIST"
|
|
73
|
+
echo ""
|
|
74
|
+
|
|
75
|
+
# Test 11: Final stats
|
|
76
|
+
echo "11. Testing stats (after adding data)..."
|
|
77
|
+
FINAL_STATS=$(send_request '{"jsonrpc":"2.0","id":11,"method":"tools/call","params":{"name":"stats","arguments":{}}}')
|
|
78
|
+
echo "Response: $FINAL_STATS"
|
|
79
|
+
echo ""
|
|
80
|
+
|
|
81
|
+
echo "=== All tests completed ==="
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"declaration": true,
|
|
15
|
+
"declarationMap": true,
|
|
16
|
+
"sourceMap": true
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"],
|
|
19
|
+
"exclude": ["node_modules", "dist", "tests"]
|
|
20
|
+
}
|