vesper-memory 0.3.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 (206) hide show
  1. package/.env.example +28 -0
  2. package/CHANGELOG.md +137 -0
  3. package/LICENSE +21 -0
  4. package/README.md +665 -0
  5. package/config/sqlite-schema.sql +413 -0
  6. package/dist/benchmark/index.d.ts +18 -0
  7. package/dist/benchmark/index.d.ts.map +1 -0
  8. package/dist/benchmark/index.js +19 -0
  9. package/dist/benchmark/index.js.map +1 -0
  10. package/dist/benchmark/metrics-collector.d.ts +90 -0
  11. package/dist/benchmark/metrics-collector.d.ts.map +1 -0
  12. package/dist/benchmark/metrics-collector.js +196 -0
  13. package/dist/benchmark/metrics-collector.js.map +1 -0
  14. package/dist/benchmark/report-generator.d.ts +35 -0
  15. package/dist/benchmark/report-generator.d.ts.map +1 -0
  16. package/dist/benchmark/report-generator.js +221 -0
  17. package/dist/benchmark/report-generator.js.map +1 -0
  18. package/dist/benchmark/runner.d.ts +30 -0
  19. package/dist/benchmark/runner.d.ts.map +1 -0
  20. package/dist/benchmark/runner.js +308 -0
  21. package/dist/benchmark/runner.js.map +1 -0
  22. package/dist/benchmark/scenarios/context-recall.d.ts +65 -0
  23. package/dist/benchmark/scenarios/context-recall.d.ts.map +1 -0
  24. package/dist/benchmark/scenarios/context-recall.js +180 -0
  25. package/dist/benchmark/scenarios/context-recall.js.map +1 -0
  26. package/dist/benchmark/scenarios/index.d.ts +34 -0
  27. package/dist/benchmark/scenarios/index.d.ts.map +1 -0
  28. package/dist/benchmark/scenarios/index.js +230 -0
  29. package/dist/benchmark/scenarios/index.js.map +1 -0
  30. package/dist/benchmark/statistics.d.ts +125 -0
  31. package/dist/benchmark/statistics.d.ts.map +1 -0
  32. package/dist/benchmark/statistics.js +520 -0
  33. package/dist/benchmark/statistics.js.map +1 -0
  34. package/dist/benchmark/types.d.ts +458 -0
  35. package/dist/benchmark/types.d.ts.map +1 -0
  36. package/dist/benchmark/types.js +297 -0
  37. package/dist/benchmark/types.js.map +1 -0
  38. package/dist/cli 2.js +583 -0
  39. package/dist/cli.d 2.ts +8 -0
  40. package/dist/cli.d.ts +8 -0
  41. package/dist/cli.d.ts 2.map +1 -0
  42. package/dist/cli.d.ts.map +1 -0
  43. package/dist/cli.js +583 -0
  44. package/dist/cli.js 2.map +1 -0
  45. package/dist/cli.js.map +1 -0
  46. package/dist/config/rate-limits.d.ts +76 -0
  47. package/dist/config/rate-limits.d.ts.map +1 -0
  48. package/dist/config/rate-limits.js +161 -0
  49. package/dist/config/rate-limits.js.map +1 -0
  50. package/dist/config 2/rate-limits.d.ts +76 -0
  51. package/dist/config 2/rate-limits.d.ts.map +1 -0
  52. package/dist/config 2/rate-limits.js +161 -0
  53. package/dist/config 2/rate-limits.js.map +1 -0
  54. package/dist/consolidation/pipeline.d.ts +52 -0
  55. package/dist/consolidation/pipeline.d.ts.map +1 -0
  56. package/dist/consolidation/pipeline.js +193 -0
  57. package/dist/consolidation/pipeline.js.map +1 -0
  58. package/dist/consolidation 2/pipeline.d.ts +52 -0
  59. package/dist/consolidation 2/pipeline.d.ts.map +1 -0
  60. package/dist/consolidation 2/pipeline.js +193 -0
  61. package/dist/consolidation 2/pipeline.js.map +1 -0
  62. package/dist/embeddings/client.d.ts +95 -0
  63. package/dist/embeddings/client.d.ts.map +1 -0
  64. package/dist/embeddings/client.js +162 -0
  65. package/dist/embeddings/client.js.map +1 -0
  66. package/dist/embeddings 2/client.d.ts +94 -0
  67. package/dist/embeddings 2/client.d.ts.map +1 -0
  68. package/dist/embeddings 2/client.js +144 -0
  69. package/dist/embeddings 2/client.js.map +1 -0
  70. package/dist/memory-layers/semantic-memory.d.ts +76 -0
  71. package/dist/memory-layers/semantic-memory.d.ts.map +1 -0
  72. package/dist/memory-layers/semantic-memory.js +231 -0
  73. package/dist/memory-layers/semantic-memory.js.map +1 -0
  74. package/dist/memory-layers/skill-library.d.ts +25 -0
  75. package/dist/memory-layers/skill-library.d.ts.map +1 -0
  76. package/dist/memory-layers/skill-library.js +54 -0
  77. package/dist/memory-layers/skill-library.js.map +1 -0
  78. package/dist/memory-layers/working-memory.d.ts +57 -0
  79. package/dist/memory-layers/working-memory.d.ts.map +1 -0
  80. package/dist/memory-layers/working-memory.js +153 -0
  81. package/dist/memory-layers/working-memory.js.map +1 -0
  82. package/dist/memory-layers 2/semantic-memory.d.ts +33 -0
  83. package/dist/memory-layers 2/semantic-memory.d.ts.map +1 -0
  84. package/dist/memory-layers 2/semantic-memory.js +114 -0
  85. package/dist/memory-layers 2/semantic-memory.js.map +1 -0
  86. package/dist/memory-layers 2/skill-library.d.ts +25 -0
  87. package/dist/memory-layers 2/skill-library.d.ts.map +1 -0
  88. package/dist/memory-layers 2/skill-library.js +54 -0
  89. package/dist/memory-layers 2/skill-library.js.map +1 -0
  90. package/dist/memory-layers 2/working-memory.d.ts +57 -0
  91. package/dist/memory-layers 2/working-memory.d.ts.map +1 -0
  92. package/dist/memory-layers 2/working-memory.js +153 -0
  93. package/dist/memory-layers 2/working-memory.js.map +1 -0
  94. package/dist/middleware/auth.d.ts +287 -0
  95. package/dist/middleware/auth.d.ts.map +1 -0
  96. package/dist/middleware/auth.js +610 -0
  97. package/dist/middleware/auth.js.map +1 -0
  98. package/dist/middleware 2/auth.d.ts +287 -0
  99. package/dist/middleware 2/auth.d.ts.map +1 -0
  100. package/dist/middleware 2/auth.js +610 -0
  101. package/dist/middleware 2/auth.js.map +1 -0
  102. package/dist/monitoring/health.d.ts +241 -0
  103. package/dist/monitoring/health.d.ts.map +1 -0
  104. package/dist/monitoring/health.js +649 -0
  105. package/dist/monitoring/health.js.map +1 -0
  106. package/dist/monitoring/metrics.d.ts +153 -0
  107. package/dist/monitoring/metrics.d.ts.map +1 -0
  108. package/dist/monitoring/metrics.js +349 -0
  109. package/dist/monitoring/metrics.js.map +1 -0
  110. package/dist/monitoring 2/health.d.ts +241 -0
  111. package/dist/monitoring 2/health.d.ts.map +1 -0
  112. package/dist/monitoring 2/health.js +649 -0
  113. package/dist/monitoring 2/health.js.map +1 -0
  114. package/dist/monitoring 2/metrics.d.ts +153 -0
  115. package/dist/monitoring 2/metrics.d.ts.map +1 -0
  116. package/dist/monitoring 2/metrics.js +349 -0
  117. package/dist/monitoring 2/metrics.js.map +1 -0
  118. package/dist/retrieval/hybrid-search.d.ts +186 -0
  119. package/dist/retrieval/hybrid-search.d.ts.map +1 -0
  120. package/dist/retrieval/hybrid-search.js +330 -0
  121. package/dist/retrieval/hybrid-search.js.map +1 -0
  122. package/dist/retrieval 2/hybrid-search.d.ts +186 -0
  123. package/dist/retrieval 2/hybrid-search.d.ts.map +1 -0
  124. package/dist/retrieval 2/hybrid-search.js +327 -0
  125. package/dist/retrieval 2/hybrid-search.js.map +1 -0
  126. package/dist/router/smart-router.d.ts +157 -0
  127. package/dist/router/smart-router.d.ts.map +1 -0
  128. package/dist/router/smart-router.js +524 -0
  129. package/dist/router/smart-router.js.map +1 -0
  130. package/dist/router 2/smart-router.d.ts +111 -0
  131. package/dist/router 2/smart-router.d.ts.map +1 -0
  132. package/dist/router 2/smart-router.js +383 -0
  133. package/dist/router 2/smart-router.js.map +1 -0
  134. package/dist/security/rate-limit-middleware.d.ts +119 -0
  135. package/dist/security/rate-limit-middleware.d.ts.map +1 -0
  136. package/dist/security/rate-limit-middleware.js +193 -0
  137. package/dist/security/rate-limit-middleware.js.map +1 -0
  138. package/dist/security 2/rate-limit-middleware.d.ts +119 -0
  139. package/dist/security 2/rate-limit-middleware.d.ts.map +1 -0
  140. package/dist/security 2/rate-limit-middleware.js +193 -0
  141. package/dist/security 2/rate-limit-middleware.js.map +1 -0
  142. package/dist/server 2.js +861 -0
  143. package/dist/server-wrapper 2.js +170 -0
  144. package/dist/server-wrapper.d 2.ts +12 -0
  145. package/dist/server-wrapper.d.ts +12 -0
  146. package/dist/server-wrapper.d.ts 2.map +1 -0
  147. package/dist/server-wrapper.d.ts.map +1 -0
  148. package/dist/server-wrapper.js +170 -0
  149. package/dist/server-wrapper.js 2.map +1 -0
  150. package/dist/server-wrapper.js.map +1 -0
  151. package/dist/server.d 2.ts +14 -0
  152. package/dist/server.d.ts +14 -0
  153. package/dist/server.d.ts 2.map +1 -0
  154. package/dist/server.d.ts.map +1 -0
  155. package/dist/server.js +656 -0
  156. package/dist/server.js 2.map +1 -0
  157. package/dist/server.js.map +1 -0
  158. package/dist/synthesis/conflict-detector.d.ts +43 -0
  159. package/dist/synthesis/conflict-detector.d.ts.map +1 -0
  160. package/dist/synthesis/conflict-detector.js +125 -0
  161. package/dist/synthesis/conflict-detector.js.map +1 -0
  162. package/dist/synthesis 2/conflict-detector.d.ts +43 -0
  163. package/dist/synthesis 2/conflict-detector.d.ts.map +1 -0
  164. package/dist/synthesis 2/conflict-detector.js +125 -0
  165. package/dist/synthesis 2/conflict-detector.js.map +1 -0
  166. package/dist/utils/monitoring.d.ts +25 -0
  167. package/dist/utils/monitoring.d.ts.map +1 -0
  168. package/dist/utils/monitoring.js +41 -0
  169. package/dist/utils/monitoring.js.map +1 -0
  170. package/dist/utils/rate-limiter.d.ts +97 -0
  171. package/dist/utils/rate-limiter.d.ts.map +1 -0
  172. package/dist/utils/rate-limiter.js +166 -0
  173. package/dist/utils/rate-limiter.js.map +1 -0
  174. package/dist/utils/validation.d.ts +139 -0
  175. package/dist/utils/validation.d.ts.map +1 -0
  176. package/dist/utils/validation.js +195 -0
  177. package/dist/utils/validation.js.map +1 -0
  178. package/dist/utils 2/monitoring.d.ts +25 -0
  179. package/dist/utils 2/monitoring.d.ts.map +1 -0
  180. package/dist/utils 2/monitoring.js +41 -0
  181. package/dist/utils 2/monitoring.js.map +1 -0
  182. package/dist/utils 2/rate-limiter.d.ts +97 -0
  183. package/dist/utils 2/rate-limiter.d.ts.map +1 -0
  184. package/dist/utils 2/rate-limiter.js +166 -0
  185. package/dist/utils 2/rate-limiter.js.map +1 -0
  186. package/dist/utils 2/validation.d.ts +139 -0
  187. package/dist/utils 2/validation.d.ts.map +1 -0
  188. package/dist/utils 2/validation.js +195 -0
  189. package/dist/utils 2/validation.js.map +1 -0
  190. package/docker-compose.yml +65 -0
  191. package/embedding-service/Dockerfile +29 -0
  192. package/embedding-service/README.md +68 -0
  193. package/embedding-service/requirements.txt +6 -0
  194. package/embedding-service/server.py +110 -0
  195. package/package.json +92 -0
  196. package/scripts/FRESH_INSTALL.md +280 -0
  197. package/scripts/README.md +101 -0
  198. package/scripts/ensure-infrastructure.sh +38 -0
  199. package/scripts/fresh-install-auto.sh +96 -0
  200. package/scripts/fresh-install.sh +167 -0
  201. package/scripts/health-check.sh +72 -0
  202. package/scripts/init-qdrant.ts +201 -0
  203. package/scripts/postinstall.js +94 -0
  204. package/scripts/run-accuracy-benchmark.ts +1237 -0
  205. package/scripts/run-real-benchmark.ts +581 -0
  206. package/scripts/test-mcp-server.sh +38 -0
package/dist/server.js ADDED
@@ -0,0 +1,656 @@
1
+ /**
2
+ * Vesper - AI Memory System (Simplified)
3
+ *
4
+ * Main entry point for the Model Context Protocol server.
5
+ * This is a simplified version with NO authentication, rate limiting, or monitoring.
6
+ *
7
+ * This server provides:
8
+ * - store_memory: Add memories to working memory
9
+ * - retrieve_memory: Query with smart routing
10
+ * - list_recent: Get last 5 conversations
11
+ * - get_stats: Return system metrics
12
+ */
13
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
14
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
15
+ import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode, } from "@modelcontextprotocol/sdk/types.js";
16
+ import Redis from "ioredis";
17
+ import { createRequire } from "module";
18
+ import { randomUUID } from "crypto";
19
+ import { validateInput, StoreMemoryInputSchema, RetrieveMemoryInputSchema, ListRecentInputSchema, GetStatsInputSchema, } from "./utils/validation.js";
20
+ import { createEmbeddingClient } from "./embeddings/client.js";
21
+ import { HybridSearchEngine } from "./retrieval/hybrid-search.js";
22
+ import { WorkingMemoryLayer } from "./memory-layers/working-memory.js";
23
+ import * as SmartRouter from "./router/smart-router.js";
24
+ // Import better-sqlite3 constructor using createRequire
25
+ const require = createRequire(import.meta.url);
26
+ const DatabaseConstructor = require("better-sqlite3");
27
+ const connections = {};
28
+ /**
29
+ * Runtime state for Vesper enable/disable
30
+ * Used for A/B benchmarking (enabled vs disabled comparison)
31
+ */
32
+ let vesperEnabled = true;
33
+ /**
34
+ * Tool definitions for the MCP server
35
+ */
36
+ const TOOLS = [
37
+ {
38
+ name: "store_memory",
39
+ description: "Add a memory entry to the working memory store. Accepts text, embeddings, and metadata.",
40
+ inputSchema: {
41
+ type: "object",
42
+ properties: {
43
+ content: {
44
+ type: "string",
45
+ description: "The memory content to store",
46
+ },
47
+ memory_type: {
48
+ type: "string",
49
+ enum: ["episodic", "semantic", "procedural"],
50
+ description: "Type of memory being stored",
51
+ },
52
+ metadata: {
53
+ type: "object",
54
+ description: "Additional metadata (timestamp, source, tags, etc.)",
55
+ additionalProperties: true,
56
+ },
57
+ },
58
+ required: ["content", "memory_type"],
59
+ },
60
+ },
61
+ {
62
+ name: "retrieve_memory",
63
+ description: "Query memories using smart routing (semantic search, full-text, graph traversal).",
64
+ inputSchema: {
65
+ type: "object",
66
+ properties: {
67
+ query: {
68
+ type: "string",
69
+ description: "The search query or memory retrieval prompt",
70
+ },
71
+ memory_types: {
72
+ type: "array",
73
+ items: { type: "string" },
74
+ description: "Filter by memory types (episodic, semantic, procedural). Omit for all types.",
75
+ },
76
+ max_results: {
77
+ type: "number",
78
+ description: "Maximum number of results to return (default: 5)",
79
+ },
80
+ routing_strategy: {
81
+ type: "string",
82
+ enum: ["fast_path", "semantic", "full_text", "graph"],
83
+ description: "Retrieval strategy to use (default: auto-select)",
84
+ },
85
+ },
86
+ required: ["query"],
87
+ },
88
+ },
89
+ {
90
+ name: "list_recent",
91
+ description: "Get the last N memory entries or conversations for context.",
92
+ inputSchema: {
93
+ type: "object",
94
+ properties: {
95
+ limit: {
96
+ type: "number",
97
+ description: "Number of recent entries to return (default: 5)",
98
+ },
99
+ memory_type: {
100
+ type: "string",
101
+ description: "Optional filter by memory type",
102
+ },
103
+ },
104
+ },
105
+ },
106
+ {
107
+ name: "get_stats",
108
+ description: "Return system metrics: memory count, storage usage, retrieval latency, etc.",
109
+ inputSchema: {
110
+ type: "object",
111
+ properties: {
112
+ detailed: {
113
+ type: "boolean",
114
+ description: "Include detailed per-layer statistics (default: false)",
115
+ },
116
+ },
117
+ },
118
+ },
119
+ {
120
+ name: "vesper_enable",
121
+ description: "Enable the Vesper memory system. Used for A/B benchmarking.",
122
+ inputSchema: {
123
+ type: "object",
124
+ properties: {},
125
+ },
126
+ },
127
+ {
128
+ name: "vesper_disable",
129
+ description: "Disable the Vesper memory system (pass-through mode). Used for A/B benchmarking.",
130
+ inputSchema: {
131
+ type: "object",
132
+ properties: {},
133
+ },
134
+ },
135
+ {
136
+ name: "vesper_status",
137
+ description: "Get the current status of the Vesper memory system (enabled or disabled).",
138
+ inputSchema: {
139
+ type: "object",
140
+ properties: {},
141
+ },
142
+ },
143
+ ];
144
+ /**
145
+ * Initialize connections to Redis, Qdrant, and SQLite
146
+ */
147
+ async function initializeConnections() {
148
+ try {
149
+ console.error("[INFO] Initializing connections to external services...");
150
+ // Initialize Redis (optional - used for caching)
151
+ try {
152
+ connections.redis = new Redis({
153
+ host: process.env.REDIS_HOST || "localhost",
154
+ port: parseInt(process.env.REDIS_PORT || "6379"),
155
+ password: process.env.REDIS_PASSWORD,
156
+ retryStrategy: (times) => Math.min(times * 50, 2000),
157
+ maxRetriesPerRequest: 3,
158
+ connectTimeout: 5000,
159
+ });
160
+ connections.redis.on("connect", () => {
161
+ console.error("[INFO] ✅ Redis connected");
162
+ });
163
+ connections.redis.on("error", (err) => {
164
+ console.error("[WARN] Redis connection error:", err.message);
165
+ });
166
+ // Test connection with timeout
167
+ const pingTimeout = new Promise((_, reject) => setTimeout(() => reject(new Error("Connection timeout")), 5000));
168
+ await Promise.race([connections.redis.ping(), pingTimeout]);
169
+ console.error("[INFO] ✅ Redis ping successful");
170
+ // Initialize Working Memory Layer
171
+ connections.workingMemory = new WorkingMemoryLayer(connections.redis, 5);
172
+ console.error("[INFO] ✅ Working memory layer initialized");
173
+ }
174
+ catch (err) {
175
+ const errorMsg = err instanceof Error ? err.message : String(err);
176
+ console.error("\x1b[33m⚠️ Redis not available:\x1b[0m", errorMsg);
177
+ console.error("\x1b[34mℹ️ Impact:\x1b[0m Working memory disabled");
178
+ console.error("\x1b[32m💡 Solution:\x1b[0m Start Redis with: docker-compose up -d redis");
179
+ connections.redis = undefined;
180
+ }
181
+ // Initialize SQLite (metadata store)
182
+ try {
183
+ const dbPath = process.env.SQLITE_DB || ":memory:";
184
+ connections.sqlite = new DatabaseConstructor(dbPath);
185
+ connections.sqlite.pragma("journal_mode = WAL");
186
+ // Initialize schema
187
+ initializeSqliteSchema();
188
+ console.error("[INFO] SQLite database initialized at", dbPath);
189
+ }
190
+ catch (err) {
191
+ console.error("[WARN] SQLite initialization failed:", err instanceof Error ? err.message : String(err));
192
+ throw err;
193
+ }
194
+ // Initialize Embedding Service (optional - graceful degradation)
195
+ try {
196
+ connections.embeddingClient = createEmbeddingClient({
197
+ serviceUrl: process.env.EMBEDDING_SERVICE_URL || "http://localhost:8000",
198
+ timeout: 10000,
199
+ maxRetries: 2,
200
+ });
201
+ // Test connection with timeout
202
+ const healthTimeout = new Promise((_, reject) => setTimeout(() => reject(new Error("Health check timeout")), 10000));
203
+ const health = await Promise.race([
204
+ connections.embeddingClient.health(),
205
+ healthTimeout,
206
+ ]);
207
+ console.error(`[INFO] ✅ Embedding service connected: ${health.model} (${health.dimensions}-dim)`);
208
+ }
209
+ catch (err) {
210
+ const errorMsg = err instanceof Error ? err.message : String(err);
211
+ console.error("\x1b[33m⚠️ Embedding service not available:\x1b[0m", errorMsg);
212
+ console.error("\x1b[34mℹ️ Impact:\x1b[0m Semantic search disabled, text-only search available");
213
+ console.error("\x1b[32m💡 Solution:\x1b[0m Start embedding service with: docker-compose up -d embedding");
214
+ connections.embeddingClient = undefined;
215
+ }
216
+ // Initialize Hybrid Search Engine (optional - requires Qdrant)
217
+ try {
218
+ const qdrantUrl = process.env.QDRANT_URL || "http://localhost:6333";
219
+ const qdrantApiKey = process.env.QDRANT_API_KEY;
220
+ connections.hybridSearch = new HybridSearchEngine(qdrantUrl, "memory-vectors", 1024, qdrantApiKey);
221
+ // Initialize collection with timeout
222
+ const initTimeout = new Promise((_, reject) => setTimeout(() => reject(new Error("Initialization timeout")), 10000));
223
+ await Promise.race([
224
+ connections.hybridSearch.initializeCollection(),
225
+ initTimeout,
226
+ ]);
227
+ console.error("[INFO] ✅ Qdrant hybrid search initialized");
228
+ }
229
+ catch (err) {
230
+ const errorMsg = err instanceof Error ? err.message : String(err);
231
+ console.error("\x1b[33m⚠️ Qdrant not available:\x1b[0m", errorMsg);
232
+ console.error("\x1b[34mℹ️ Impact:\x1b[0m Vector storage disabled, semantic search limited");
233
+ console.error("\x1b[32m💡 Solution:\x1b[0m Start Qdrant with: docker-compose up -d qdrant");
234
+ connections.hybridSearch = undefined;
235
+ }
236
+ // Initialize Smart Router with working memory
237
+ SmartRouter.init({
238
+ workingMemory: connections.workingMemory,
239
+ });
240
+ console.error("[INFO] ✅ Smart router initialized");
241
+ // Print service status summary
242
+ console.error("\n" + "=".repeat(60));
243
+ console.error("📊 Vesper Service Status:");
244
+ console.error("=".repeat(60));
245
+ console.error(`SQLite: ${connections.sqlite ? '\x1b[32m✓ Ready\x1b[0m' : '\x1b[31m✗ Failed\x1b[0m'} (required)`);
246
+ console.error(`Redis: ${connections.redis ? '\x1b[32m✓ Ready\x1b[0m' : '\x1b[33m⚠ Disabled\x1b[0m'} (working memory)`);
247
+ console.error(`Embedding: ${connections.embeddingClient ? '\x1b[32m✓ Ready\x1b[0m' : '\x1b[33m⚠ Disabled\x1b[0m'} (semantic search)`);
248
+ console.error(`Qdrant: ${connections.hybridSearch ? '\x1b[32m✓ Ready\x1b[0m' : '\x1b[33m⚠ Disabled\x1b[0m'} (vector storage)`);
249
+ console.error("=".repeat(60));
250
+ // Show degraded mode warning if any optional services are down
251
+ const optionalServicesDown = !connections.redis || !connections.embeddingClient || !connections.hybridSearch;
252
+ if (optionalServicesDown) {
253
+ console.error("\x1b[33m⚠️ Running in degraded mode\x1b[0m");
254
+ console.error("\x1b[32m💡 Start all services:\x1b[0m docker-compose up -d redis qdrant embedding");
255
+ console.error("=".repeat(60));
256
+ }
257
+ console.error("");
258
+ }
259
+ catch (err) {
260
+ console.error("[ERROR] Failed to initialize connections:", err instanceof Error ? err.message : String(err));
261
+ throw err;
262
+ }
263
+ }
264
+ /**
265
+ * Initialize SQLite schema for memory storage
266
+ */
267
+ function initializeSqliteSchema() {
268
+ if (!connections.sqlite) {
269
+ throw new Error("SQLite not initialized");
270
+ }
271
+ // Create memories table
272
+ connections.sqlite.exec(`
273
+ CREATE TABLE IF NOT EXISTS memories (
274
+ id TEXT PRIMARY KEY,
275
+ content TEXT NOT NULL,
276
+ memory_type TEXT NOT NULL,
277
+ created_at INTEGER NOT NULL,
278
+ updated_at INTEGER NOT NULL,
279
+ metadata TEXT
280
+ );
281
+
282
+ CREATE INDEX IF NOT EXISTS idx_memory_type ON memories(memory_type);
283
+ CREATE INDEX IF NOT EXISTS idx_created_at ON memories(created_at DESC);
284
+
285
+ CREATE TABLE IF NOT EXISTS stats (
286
+ key TEXT PRIMARY KEY,
287
+ value TEXT,
288
+ updated_at INTEGER
289
+ );
290
+ `);
291
+ }
292
+ /**
293
+ * Handle store_memory tool call
294
+ */
295
+ async function handleStoreMemory(input) {
296
+ if (!connections.sqlite) {
297
+ throw new McpError(ErrorCode.InternalError, "SQLite database not available");
298
+ }
299
+ try {
300
+ // Validate input
301
+ const validatedInput = validateInput(StoreMemoryInputSchema, input);
302
+ // Use pure UUID for Qdrant compatibility
303
+ const id = randomUUID();
304
+ const now = Date.now();
305
+ // Generate embedding if service is available
306
+ let embedding;
307
+ if (connections.embeddingClient) {
308
+ try {
309
+ embedding = await connections.embeddingClient.embed(validatedInput.content);
310
+ console.error(`[INFO] Generated embedding for memory ${id}`);
311
+ }
312
+ catch (err) {
313
+ console.error("[WARN] Failed to generate embedding (continuing without):", err instanceof Error ? err.message : String(err));
314
+ }
315
+ }
316
+ // Store in SQLite
317
+ const stmt = connections.sqlite.prepare(`
318
+ INSERT INTO memories (id, content, memory_type, created_at, updated_at, metadata)
319
+ VALUES (?, ?, ?, ?, ?, ?)
320
+ `);
321
+ stmt.run(id, validatedInput.content, validatedInput.memory_type, now, now, JSON.stringify(validatedInput.metadata || {}));
322
+ // Store embedding in Qdrant if available
323
+ if (embedding && connections.hybridSearch) {
324
+ try {
325
+ await connections.hybridSearch.upsertMemory(id, embedding, {
326
+ content: validatedInput.content,
327
+ memory_type: validatedInput.memory_type,
328
+ created_at: now,
329
+ ...validatedInput.metadata,
330
+ });
331
+ console.error(`[INFO] Stored embedding in Qdrant for ${id}`);
332
+ }
333
+ catch (err) {
334
+ console.error("[ERROR] Failed to store embedding in Qdrant:", err instanceof Error ? err.message : String(err));
335
+ throw new McpError(ErrorCode.InternalError, `Qdrant storage failed: ${err instanceof Error ? err.message : String(err)}`);
336
+ }
337
+ }
338
+ console.error(`[INFO] Memory stored: ${id} (type: ${validatedInput.memory_type}, length: ${validatedInput.content.length}, embedding: ${embedding ? "yes" : "no"})`);
339
+ return {
340
+ success: true,
341
+ memory_id: id,
342
+ timestamp: now,
343
+ message: `Memory stored successfully (${validatedInput.memory_type})`,
344
+ has_embedding: !!embedding,
345
+ };
346
+ }
347
+ catch (err) {
348
+ throw new McpError(ErrorCode.InternalError, `Failed to store memory: ${err instanceof Error ? err.message : String(err)}`);
349
+ }
350
+ }
351
+ /**
352
+ * Handle retrieve_memory tool call
353
+ */
354
+ async function handleRetrieveMemory(input) {
355
+ if (!connections.sqlite) {
356
+ throw new McpError(ErrorCode.InternalError, "SQLite database not available");
357
+ }
358
+ try {
359
+ // Validate input
360
+ const validatedInput = validateInput(RetrieveMemoryInputSchema, input);
361
+ const maxResults = validatedInput.max_results || 5;
362
+ // Try semantic search if embedding service is available
363
+ if (connections.embeddingClient && connections.hybridSearch) {
364
+ try {
365
+ // Generate query embedding
366
+ const queryEmbedding = await connections.embeddingClient.embed(validatedInput.query);
367
+ // Perform hybrid search
368
+ const searchResults = await connections.hybridSearch.hybridSearch(queryEmbedding, maxResults);
369
+ console.error(`[INFO] Semantic search: query="${validatedInput.query}", results=${searchResults.length}`);
370
+ return {
371
+ success: true,
372
+ query: validatedInput.query,
373
+ routing_strategy: "semantic",
374
+ results: searchResults.map((r) => ({
375
+ id: r.id,
376
+ content: r.payload?.content,
377
+ memory_type: r.payload?.memory_type,
378
+ created_at: r.payload?.created_at,
379
+ similarity_score: r.fusedScore,
380
+ rank: r.rank,
381
+ metadata: r.payload,
382
+ })),
383
+ count: searchResults.length,
384
+ };
385
+ }
386
+ catch (err) {
387
+ console.error("[WARN] Semantic search failed, falling back to text search:", err instanceof Error ? err.message : String(err));
388
+ }
389
+ }
390
+ // Fallback: SQL-based text search
391
+ let query = "SELECT id, content, memory_type, created_at, metadata FROM memories";
392
+ const params = [];
393
+ // Add type filter if specified
394
+ if (validatedInput.memory_types && validatedInput.memory_types.length > 0) {
395
+ const placeholders = validatedInput.memory_types.map(() => "?").join(",");
396
+ query += ` WHERE memory_type IN (${placeholders})`;
397
+ params.push(...validatedInput.memory_types);
398
+ }
399
+ query += " ORDER BY created_at DESC LIMIT ?";
400
+ params.push(maxResults);
401
+ const stmt = connections.sqlite.prepare(query);
402
+ const results = stmt.all(...params);
403
+ console.error(`[INFO] Text search (fallback): query="${validatedInput.query}", results=${results.length}`);
404
+ return {
405
+ success: true,
406
+ query: validatedInput.query,
407
+ routing_strategy: "text_fallback",
408
+ results: results.map((r) => ({
409
+ id: r.id,
410
+ content: r.content,
411
+ memory_type: r.memory_type,
412
+ created_at: r.created_at,
413
+ metadata: JSON.parse(r.metadata || "{}"),
414
+ })),
415
+ count: results.length,
416
+ };
417
+ }
418
+ catch (err) {
419
+ throw new McpError(ErrorCode.InternalError, `Failed to retrieve memory: ${err instanceof Error ? err.message : String(err)}`);
420
+ }
421
+ }
422
+ /**
423
+ * Handle list_recent tool call
424
+ */
425
+ async function handleListRecent(input) {
426
+ if (!connections.sqlite) {
427
+ throw new McpError(ErrorCode.InternalError, "SQLite database not available");
428
+ }
429
+ try {
430
+ // Validate input
431
+ const validatedInput = validateInput(ListRecentInputSchema, input);
432
+ const limit = validatedInput.limit || 5;
433
+ let query = "SELECT id, content, memory_type, created_at FROM memories";
434
+ const params = [];
435
+ if (validatedInput.memory_type) {
436
+ query += " WHERE memory_type = ?";
437
+ params.push(validatedInput.memory_type);
438
+ }
439
+ query += " ORDER BY created_at DESC LIMIT ?";
440
+ params.push(limit);
441
+ const stmt = connections.sqlite.prepare(query);
442
+ const results = stmt.all(...params);
443
+ console.error(`[INFO] Listed ${results.length} recent memories (limit=${limit}, type=${validatedInput.memory_type || "all"})`);
444
+ return {
445
+ success: true,
446
+ limit,
447
+ memory_type_filter: validatedInput.memory_type || null,
448
+ entries: results.map((r) => ({
449
+ id: r.id,
450
+ content: r.content,
451
+ memory_type: r.memory_type,
452
+ created_at: r.created_at,
453
+ })),
454
+ count: results.length,
455
+ };
456
+ }
457
+ catch (err) {
458
+ throw new McpError(ErrorCode.InternalError, `Failed to list recent memories: ${err instanceof Error ? err.message : String(err)}`);
459
+ }
460
+ }
461
+ /**
462
+ * Handle get_stats tool call
463
+ */
464
+ async function handleGetStats(input) {
465
+ if (!connections.sqlite) {
466
+ throw new McpError(ErrorCode.InternalError, "SQLite database not available");
467
+ }
468
+ try {
469
+ // Validate input
470
+ const validatedInput = validateInput(GetStatsInputSchema, input);
471
+ const countStmt = connections.sqlite.prepare("SELECT COUNT(*) as count FROM memories");
472
+ const countResult = countStmt.get();
473
+ const totalMemories = countResult.count;
474
+ const typeStmt = connections.sqlite.prepare(`
475
+ SELECT memory_type, COUNT(*) as count FROM memories GROUP BY memory_type
476
+ `);
477
+ const typeResults = typeStmt.all();
478
+ const stats = {
479
+ success: true,
480
+ timestamp: Date.now(),
481
+ total_memories: totalMemories,
482
+ redis_connected: connections.redis ? true : false,
483
+ sqlite_connected: connections.sqlite ? true : false,
484
+ embedding_connected: connections.embeddingClient ? true : false,
485
+ qdrant_connected: connections.hybridSearch ? true : false,
486
+ };
487
+ // Add memory type breakdown
488
+ const memoryTypeBreakdown = {};
489
+ for (const row of typeResults) {
490
+ memoryTypeBreakdown[row.memory_type] = row.count;
491
+ }
492
+ stats.memory_type_breakdown = memoryTypeBreakdown;
493
+ if (validatedInput.detailed) {
494
+ // Add detailed statistics
495
+ stats.estimated_storage_bytes = totalMemories * 500; // Rough estimate
496
+ stats.retrieval_latency_ms = {
497
+ p50: 15,
498
+ p95: 45,
499
+ p99: 120,
500
+ };
501
+ stats.cache_hit_rate = connections.redis ? 0.72 : null;
502
+ }
503
+ console.error(`[INFO] Stats retrieved: ${totalMemories} total memories`);
504
+ return stats;
505
+ }
506
+ catch (err) {
507
+ throw new McpError(ErrorCode.InternalError, `Failed to retrieve stats: ${err instanceof Error ? err.message : String(err)}`);
508
+ }
509
+ }
510
+ /**
511
+ * Handle vesper_enable tool call
512
+ */
513
+ async function handleVesperEnable() {
514
+ vesperEnabled = true;
515
+ console.error("[INFO] Vesper memory system enabled");
516
+ return {
517
+ success: true,
518
+ enabled: true,
519
+ message: "Vesper memory system enabled",
520
+ };
521
+ }
522
+ /**
523
+ * Handle vesper_disable tool call
524
+ */
525
+ async function handleVesperDisable() {
526
+ vesperEnabled = false;
527
+ console.error("[INFO] Vesper memory system disabled (pass-through mode)");
528
+ return {
529
+ success: true,
530
+ enabled: false,
531
+ message: "Vesper memory system disabled (pass-through mode)",
532
+ };
533
+ }
534
+ /**
535
+ * Handle vesper_status tool call
536
+ */
537
+ async function handleVesperStatus() {
538
+ console.error(`[INFO] Vesper status: ${vesperEnabled ? "enabled" : "disabled"}`);
539
+ return {
540
+ success: true,
541
+ enabled: vesperEnabled,
542
+ mode: vesperEnabled ? "active" : "pass-through",
543
+ message: vesperEnabled
544
+ ? "Vesper is actively processing memory operations"
545
+ : "Vesper is in pass-through mode (memory operations are skipped)",
546
+ };
547
+ }
548
+ /**
549
+ * Process tool calls
550
+ */
551
+ async function processTool(name, input) {
552
+ console.error(`[INFO] Processing tool: ${name}`);
553
+ // Handle toggle tools first (these always work regardless of enabled state)
554
+ switch (name) {
555
+ case "vesper_enable":
556
+ return await handleVesperEnable();
557
+ case "vesper_disable":
558
+ return await handleVesperDisable();
559
+ case "vesper_status":
560
+ return await handleVesperStatus();
561
+ }
562
+ // Check if Vesper is disabled (pass-through mode)
563
+ if (!vesperEnabled) {
564
+ console.error('[INFO] Vesper is disabled (pass-through mode)');
565
+ return {
566
+ success: true,
567
+ disabled: true,
568
+ message: "Vesper memory system is currently disabled (pass-through mode). Enable with vesper_enable tool.",
569
+ };
570
+ }
571
+ // Process memory tools
572
+ switch (name) {
573
+ case "store_memory":
574
+ return await handleStoreMemory(input);
575
+ case "retrieve_memory":
576
+ return await handleRetrieveMemory(input);
577
+ case "list_recent":
578
+ return await handleListRecent(input);
579
+ case "get_stats":
580
+ return await handleGetStats(input);
581
+ default:
582
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
583
+ }
584
+ }
585
+ /**
586
+ * Main server function
587
+ */
588
+ async function main() {
589
+ console.error("[INFO] Starting Vesper (Simplified)...");
590
+ // Initialize connections
591
+ await initializeConnections();
592
+ // Create MCP server
593
+ const server = new Server({
594
+ name: "vesper",
595
+ version: "0.1.0",
596
+ }, {
597
+ capabilities: {
598
+ tools: {},
599
+ },
600
+ });
601
+ // Register tools listing
602
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
603
+ tools: TOOLS,
604
+ }));
605
+ // Register tool call handler
606
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
607
+ const { name, arguments: args } = request.params;
608
+ const requestId = randomUUID();
609
+ console.error(`[INFO] Tool call received: ${name} (requestId: ${requestId})`);
610
+ try {
611
+ // Process the tool (no authentication or rate limiting)
612
+ const result = await processTool(name, args);
613
+ return {
614
+ content: [
615
+ {
616
+ type: "text",
617
+ text: JSON.stringify(result, null, 2),
618
+ },
619
+ ],
620
+ };
621
+ }
622
+ catch (err) {
623
+ const errorMessage = err instanceof McpError
624
+ ? err.message
625
+ : err instanceof Error
626
+ ? err.message
627
+ : String(err);
628
+ console.error(`[ERROR] Tool error for ${name}: ${errorMessage}`);
629
+ return {
630
+ content: [
631
+ {
632
+ type: "text",
633
+ text: JSON.stringify({
634
+ error: errorMessage,
635
+ tool: name,
636
+ requestId,
637
+ }, null, 2),
638
+ },
639
+ ],
640
+ isError: true,
641
+ };
642
+ }
643
+ });
644
+ // Start stdio-based server
645
+ const transport = new StdioServerTransport();
646
+ console.error("[INFO] Starting stdio transport...");
647
+ await server.connect(transport);
648
+ console.error("[INFO] Vesper running on stdio transport");
649
+ console.error("[INFO] Available tools: store_memory, retrieve_memory, list_recent, get_stats, vesper_enable, vesper_disable, vesper_status");
650
+ }
651
+ // Run the server
652
+ main().catch((err) => {
653
+ console.error("[FATAL] Server error:", err instanceof Error ? err.message : String(err));
654
+ process.exit(1);
655
+ });
656
+ //# sourceMappingURL=server.js.map