@prmichaelsen/remember-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/.env.example +65 -0
  2. package/AGENT.md +840 -0
  3. package/README.md +72 -0
  4. package/agent/design/.gitkeep +0 -0
  5. package/agent/design/access-control-result-pattern.md +458 -0
  6. package/agent/design/action-audit-memory-types.md +637 -0
  7. package/agent/design/common-template-fields.md +282 -0
  8. package/agent/design/complete-tool-set.md +407 -0
  9. package/agent/design/content-types-expansion.md +521 -0
  10. package/agent/design/cross-database-id-strategy.md +358 -0
  11. package/agent/design/default-template-library.md +423 -0
  12. package/agent/design/firestore-wrapper-analysis.md +606 -0
  13. package/agent/design/llm-provider-abstraction.md +691 -0
  14. package/agent/design/location-handling-architecture.md +523 -0
  15. package/agent/design/memory-templates-design.md +364 -0
  16. package/agent/design/permissions-storage-architecture.md +680 -0
  17. package/agent/design/relationship-storage-strategy.md +361 -0
  18. package/agent/design/remember-mcp-implementation-tasks.md +417 -0
  19. package/agent/design/remember-mcp-progress.yaml +141 -0
  20. package/agent/design/requirements-enhancements.md +468 -0
  21. package/agent/design/requirements.md +56 -0
  22. package/agent/design/template-storage-strategy.md +412 -0
  23. package/agent/design/template-suggestion-system.md +853 -0
  24. package/agent/design/trust-escalation-prevention.md +343 -0
  25. package/agent/design/trust-system-implementation.md +592 -0
  26. package/agent/design/user-preferences.md +683 -0
  27. package/agent/design/weaviate-collection-strategy.md +461 -0
  28. package/agent/milestones/.gitkeep +0 -0
  29. package/agent/milestones/milestone-1-project-foundation.md +121 -0
  30. package/agent/milestones/milestone-2-core-memory-system.md +150 -0
  31. package/agent/milestones/milestone-3-relationships-graph.md +116 -0
  32. package/agent/milestones/milestone-4-user-preferences.md +103 -0
  33. package/agent/milestones/milestone-5-template-system.md +126 -0
  34. package/agent/milestones/milestone-6-auth-multi-tenancy.md +124 -0
  35. package/agent/milestones/milestone-7-trust-permissions.md +133 -0
  36. package/agent/milestones/milestone-8-testing-quality.md +137 -0
  37. package/agent/milestones/milestone-9-deployment-documentation.md +147 -0
  38. package/agent/patterns/.gitkeep +0 -0
  39. package/agent/patterns/bootstrap.md +1271 -0
  40. package/agent/patterns/firebase-admin-sdk-v8-usage.md +950 -0
  41. package/agent/patterns/firestore-users-pattern-best-practices.md +347 -0
  42. package/agent/patterns/library-services.md +454 -0
  43. package/agent/patterns/testing-colocated.md +316 -0
  44. package/agent/progress.yaml +395 -0
  45. package/agent/tasks/.gitkeep +0 -0
  46. package/agent/tasks/task-1-initialize-project-structure.md +266 -0
  47. package/agent/tasks/task-2-install-dependencies.md +199 -0
  48. package/agent/tasks/task-3-setup-weaviate-client.md +330 -0
  49. package/agent/tasks/task-4-setup-firestore-client.md +362 -0
  50. package/agent/tasks/task-5-create-basic-mcp-server.md +114 -0
  51. package/agent/tasks/task-6-create-integration-tests.md +195 -0
  52. package/agent/tasks/task-7-finalize-milestone-1.md +363 -0
  53. package/agent/tasks/task-8-setup-utility-scripts.md +382 -0
  54. package/agent/tasks/task-9-create-server-factory.md +404 -0
  55. package/dist/config.d.ts +26 -0
  56. package/dist/constants/content-types.d.ts +60 -0
  57. package/dist/firestore/init.d.ts +14 -0
  58. package/dist/firestore/paths.d.ts +53 -0
  59. package/dist/firestore/paths.spec.d.ts +2 -0
  60. package/dist/server-factory.d.ts +40 -0
  61. package/dist/server-factory.js +1741 -0
  62. package/dist/server-factory.spec.d.ts +2 -0
  63. package/dist/server.d.ts +3 -0
  64. package/dist/server.js +1690 -0
  65. package/dist/tools/create-memory.d.ts +94 -0
  66. package/dist/tools/delete-memory.d.ts +47 -0
  67. package/dist/tools/search-memory.d.ts +88 -0
  68. package/dist/types/memory.d.ts +183 -0
  69. package/dist/utils/logger.d.ts +7 -0
  70. package/dist/weaviate/client.d.ts +39 -0
  71. package/dist/weaviate/client.spec.d.ts +2 -0
  72. package/dist/weaviate/schema.d.ts +29 -0
  73. package/esbuild.build.js +60 -0
  74. package/esbuild.watch.js +25 -0
  75. package/jest.config.js +31 -0
  76. package/jest.e2e.config.js +17 -0
  77. package/package.json +68 -0
  78. package/src/.gitkeep +0 -0
  79. package/src/config.ts +56 -0
  80. package/src/constants/content-types.ts +454 -0
  81. package/src/firestore/init.ts +68 -0
  82. package/src/firestore/paths.spec.ts +75 -0
  83. package/src/firestore/paths.ts +124 -0
  84. package/src/server-factory.spec.ts +60 -0
  85. package/src/server-factory.ts +215 -0
  86. package/src/server.ts +243 -0
  87. package/src/tools/create-memory.ts +198 -0
  88. package/src/tools/delete-memory.ts +126 -0
  89. package/src/tools/search-memory.ts +216 -0
  90. package/src/types/memory.ts +276 -0
  91. package/src/utils/logger.ts +42 -0
  92. package/src/weaviate/client.spec.ts +58 -0
  93. package/src/weaviate/client.ts +114 -0
  94. package/src/weaviate/schema.ts +288 -0
  95. package/tsconfig.json +26 -0
@@ -0,0 +1,1741 @@
1
+ import { createRequire } from 'module'; const require = createRequire(import.meta.url);
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
9
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
10
+ }) : x)(function(x) {
11
+ if (typeof require !== "undefined")
12
+ return require.apply(this, arguments);
13
+ throw Error('Dynamic require of "' + x + '" is not supported');
14
+ });
15
+ var __esm = (fn, res) => function __init() {
16
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
17
+ };
18
+ var __commonJS = (cb, mod) => function __require2() {
19
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
20
+ };
21
+ var __export = (target, all) => {
22
+ for (var name in all)
23
+ __defProp(target, name, { get: all[name], enumerable: true });
24
+ };
25
+ var __copyProps = (to, from, except, desc) => {
26
+ if (from && typeof from === "object" || typeof from === "function") {
27
+ for (let key of __getOwnPropNames(from))
28
+ if (!__hasOwnProp.call(to, key) && key !== except)
29
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
30
+ }
31
+ return to;
32
+ };
33
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
34
+ // If the importer is in node compatibility mode or this is not an ESM
35
+ // file that has been converted to a CommonJS file using a Babel-
36
+ // compatible transform (i.e. "__esModule" has not been set), then set
37
+ // "default" to the CommonJS "module.exports" for node compatibility.
38
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
39
+ mod
40
+ ));
41
+
42
+ // node_modules/dotenv/package.json
43
+ var require_package = __commonJS({
44
+ "node_modules/dotenv/package.json"(exports, module) {
45
+ module.exports = {
46
+ name: "dotenv",
47
+ version: "16.6.1",
48
+ description: "Loads environment variables from .env file",
49
+ main: "lib/main.js",
50
+ types: "lib/main.d.ts",
51
+ exports: {
52
+ ".": {
53
+ types: "./lib/main.d.ts",
54
+ require: "./lib/main.js",
55
+ default: "./lib/main.js"
56
+ },
57
+ "./config": "./config.js",
58
+ "./config.js": "./config.js",
59
+ "./lib/env-options": "./lib/env-options.js",
60
+ "./lib/env-options.js": "./lib/env-options.js",
61
+ "./lib/cli-options": "./lib/cli-options.js",
62
+ "./lib/cli-options.js": "./lib/cli-options.js",
63
+ "./package.json": "./package.json"
64
+ },
65
+ scripts: {
66
+ "dts-check": "tsc --project tests/types/tsconfig.json",
67
+ lint: "standard",
68
+ pretest: "npm run lint && npm run dts-check",
69
+ test: "tap run --allow-empty-coverage --disable-coverage --timeout=60000",
70
+ "test:coverage": "tap run --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov",
71
+ prerelease: "npm test",
72
+ release: "standard-version"
73
+ },
74
+ repository: {
75
+ type: "git",
76
+ url: "git://github.com/motdotla/dotenv.git"
77
+ },
78
+ homepage: "https://github.com/motdotla/dotenv#readme",
79
+ funding: "https://dotenvx.com",
80
+ keywords: [
81
+ "dotenv",
82
+ "env",
83
+ ".env",
84
+ "environment",
85
+ "variables",
86
+ "config",
87
+ "settings"
88
+ ],
89
+ readmeFilename: "README.md",
90
+ license: "BSD-2-Clause",
91
+ devDependencies: {
92
+ "@types/node": "^18.11.3",
93
+ decache: "^4.6.2",
94
+ sinon: "^14.0.1",
95
+ standard: "^17.0.0",
96
+ "standard-version": "^9.5.0",
97
+ tap: "^19.2.0",
98
+ typescript: "^4.8.4"
99
+ },
100
+ engines: {
101
+ node: ">=12"
102
+ },
103
+ browser: {
104
+ fs: false
105
+ }
106
+ };
107
+ }
108
+ });
109
+
110
+ // node_modules/dotenv/lib/main.js
111
+ var require_main = __commonJS({
112
+ "node_modules/dotenv/lib/main.js"(exports, module) {
113
+ var fs = __require("fs");
114
+ var path = __require("path");
115
+ var os = __require("os");
116
+ var crypto = __require("crypto");
117
+ var packageJson = require_package();
118
+ var version = packageJson.version;
119
+ var LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg;
120
+ function parse(src) {
121
+ const obj = {};
122
+ let lines = src.toString();
123
+ lines = lines.replace(/\r\n?/mg, "\n");
124
+ let match;
125
+ while ((match = LINE.exec(lines)) != null) {
126
+ const key = match[1];
127
+ let value = match[2] || "";
128
+ value = value.trim();
129
+ const maybeQuote = value[0];
130
+ value = value.replace(/^(['"`])([\s\S]*)\1$/mg, "$2");
131
+ if (maybeQuote === '"') {
132
+ value = value.replace(/\\n/g, "\n");
133
+ value = value.replace(/\\r/g, "\r");
134
+ }
135
+ obj[key] = value;
136
+ }
137
+ return obj;
138
+ }
139
+ function _parseVault(options) {
140
+ options = options || {};
141
+ const vaultPath = _vaultPath(options);
142
+ options.path = vaultPath;
143
+ const result = DotenvModule.configDotenv(options);
144
+ if (!result.parsed) {
145
+ const err = new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`);
146
+ err.code = "MISSING_DATA";
147
+ throw err;
148
+ }
149
+ const keys = _dotenvKey(options).split(",");
150
+ const length = keys.length;
151
+ let decrypted;
152
+ for (let i = 0; i < length; i++) {
153
+ try {
154
+ const key = keys[i].trim();
155
+ const attrs = _instructions(result, key);
156
+ decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key);
157
+ break;
158
+ } catch (error) {
159
+ if (i + 1 >= length) {
160
+ throw error;
161
+ }
162
+ }
163
+ }
164
+ return DotenvModule.parse(decrypted);
165
+ }
166
+ function _warn(message) {
167
+ console.log(`[dotenv@${version}][WARN] ${message}`);
168
+ }
169
+ function _debug(message) {
170
+ console.log(`[dotenv@${version}][DEBUG] ${message}`);
171
+ }
172
+ function _log(message) {
173
+ console.log(`[dotenv@${version}] ${message}`);
174
+ }
175
+ function _dotenvKey(options) {
176
+ if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) {
177
+ return options.DOTENV_KEY;
178
+ }
179
+ if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) {
180
+ return process.env.DOTENV_KEY;
181
+ }
182
+ return "";
183
+ }
184
+ function _instructions(result, dotenvKey) {
185
+ let uri;
186
+ try {
187
+ uri = new URL(dotenvKey);
188
+ } catch (error) {
189
+ if (error.code === "ERR_INVALID_URL") {
190
+ const err = new Error("INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development");
191
+ err.code = "INVALID_DOTENV_KEY";
192
+ throw err;
193
+ }
194
+ throw error;
195
+ }
196
+ const key = uri.password;
197
+ if (!key) {
198
+ const err = new Error("INVALID_DOTENV_KEY: Missing key part");
199
+ err.code = "INVALID_DOTENV_KEY";
200
+ throw err;
201
+ }
202
+ const environment = uri.searchParams.get("environment");
203
+ if (!environment) {
204
+ const err = new Error("INVALID_DOTENV_KEY: Missing environment part");
205
+ err.code = "INVALID_DOTENV_KEY";
206
+ throw err;
207
+ }
208
+ const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`;
209
+ const ciphertext = result.parsed[environmentKey];
210
+ if (!ciphertext) {
211
+ const err = new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`);
212
+ err.code = "NOT_FOUND_DOTENV_ENVIRONMENT";
213
+ throw err;
214
+ }
215
+ return { ciphertext, key };
216
+ }
217
+ function _vaultPath(options) {
218
+ let possibleVaultPath = null;
219
+ if (options && options.path && options.path.length > 0) {
220
+ if (Array.isArray(options.path)) {
221
+ for (const filepath of options.path) {
222
+ if (fs.existsSync(filepath)) {
223
+ possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
224
+ }
225
+ }
226
+ } else {
227
+ possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
228
+ }
229
+ } else {
230
+ possibleVaultPath = path.resolve(process.cwd(), ".env.vault");
231
+ }
232
+ if (fs.existsSync(possibleVaultPath)) {
233
+ return possibleVaultPath;
234
+ }
235
+ return null;
236
+ }
237
+ function _resolveHome(envPath) {
238
+ return envPath[0] === "~" ? path.join(os.homedir(), envPath.slice(1)) : envPath;
239
+ }
240
+ function _configVault(options) {
241
+ const debug = Boolean(options && options.debug);
242
+ const quiet = options && "quiet" in options ? options.quiet : true;
243
+ if (debug || !quiet) {
244
+ _log("Loading env from encrypted .env.vault");
245
+ }
246
+ const parsed = DotenvModule._parseVault(options);
247
+ let processEnv = process.env;
248
+ if (options && options.processEnv != null) {
249
+ processEnv = options.processEnv;
250
+ }
251
+ DotenvModule.populate(processEnv, parsed, options);
252
+ return { parsed };
253
+ }
254
+ function configDotenv(options) {
255
+ const dotenvPath = path.resolve(process.cwd(), ".env");
256
+ let encoding = "utf8";
257
+ const debug = Boolean(options && options.debug);
258
+ const quiet = options && "quiet" in options ? options.quiet : true;
259
+ if (options && options.encoding) {
260
+ encoding = options.encoding;
261
+ } else {
262
+ if (debug) {
263
+ _debug("No encoding is specified. UTF-8 is used by default");
264
+ }
265
+ }
266
+ let optionPaths = [dotenvPath];
267
+ if (options && options.path) {
268
+ if (!Array.isArray(options.path)) {
269
+ optionPaths = [_resolveHome(options.path)];
270
+ } else {
271
+ optionPaths = [];
272
+ for (const filepath of options.path) {
273
+ optionPaths.push(_resolveHome(filepath));
274
+ }
275
+ }
276
+ }
277
+ let lastError;
278
+ const parsedAll = {};
279
+ for (const path2 of optionPaths) {
280
+ try {
281
+ const parsed = DotenvModule.parse(fs.readFileSync(path2, { encoding }));
282
+ DotenvModule.populate(parsedAll, parsed, options);
283
+ } catch (e) {
284
+ if (debug) {
285
+ _debug(`Failed to load ${path2} ${e.message}`);
286
+ }
287
+ lastError = e;
288
+ }
289
+ }
290
+ let processEnv = process.env;
291
+ if (options && options.processEnv != null) {
292
+ processEnv = options.processEnv;
293
+ }
294
+ DotenvModule.populate(processEnv, parsedAll, options);
295
+ if (debug || !quiet) {
296
+ const keysCount = Object.keys(parsedAll).length;
297
+ const shortPaths = [];
298
+ for (const filePath of optionPaths) {
299
+ try {
300
+ const relative = path.relative(process.cwd(), filePath);
301
+ shortPaths.push(relative);
302
+ } catch (e) {
303
+ if (debug) {
304
+ _debug(`Failed to load ${filePath} ${e.message}`);
305
+ }
306
+ lastError = e;
307
+ }
308
+ }
309
+ _log(`injecting env (${keysCount}) from ${shortPaths.join(",")}`);
310
+ }
311
+ if (lastError) {
312
+ return { parsed: parsedAll, error: lastError };
313
+ } else {
314
+ return { parsed: parsedAll };
315
+ }
316
+ }
317
+ function config2(options) {
318
+ if (_dotenvKey(options).length === 0) {
319
+ return DotenvModule.configDotenv(options);
320
+ }
321
+ const vaultPath = _vaultPath(options);
322
+ if (!vaultPath) {
323
+ _warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`);
324
+ return DotenvModule.configDotenv(options);
325
+ }
326
+ return DotenvModule._configVault(options);
327
+ }
328
+ function decrypt(encrypted, keyStr) {
329
+ const key = Buffer.from(keyStr.slice(-64), "hex");
330
+ let ciphertext = Buffer.from(encrypted, "base64");
331
+ const nonce = ciphertext.subarray(0, 12);
332
+ const authTag = ciphertext.subarray(-16);
333
+ ciphertext = ciphertext.subarray(12, -16);
334
+ try {
335
+ const aesgcm = crypto.createDecipheriv("aes-256-gcm", key, nonce);
336
+ aesgcm.setAuthTag(authTag);
337
+ return `${aesgcm.update(ciphertext)}${aesgcm.final()}`;
338
+ } catch (error) {
339
+ const isRange = error instanceof RangeError;
340
+ const invalidKeyLength = error.message === "Invalid key length";
341
+ const decryptionFailed = error.message === "Unsupported state or unable to authenticate data";
342
+ if (isRange || invalidKeyLength) {
343
+ const err = new Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)");
344
+ err.code = "INVALID_DOTENV_KEY";
345
+ throw err;
346
+ } else if (decryptionFailed) {
347
+ const err = new Error("DECRYPTION_FAILED: Please check your DOTENV_KEY");
348
+ err.code = "DECRYPTION_FAILED";
349
+ throw err;
350
+ } else {
351
+ throw error;
352
+ }
353
+ }
354
+ }
355
+ function populate(processEnv, parsed, options = {}) {
356
+ const debug = Boolean(options && options.debug);
357
+ const override = Boolean(options && options.override);
358
+ if (typeof parsed !== "object") {
359
+ const err = new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");
360
+ err.code = "OBJECT_REQUIRED";
361
+ throw err;
362
+ }
363
+ for (const key of Object.keys(parsed)) {
364
+ if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
365
+ if (override === true) {
366
+ processEnv[key] = parsed[key];
367
+ }
368
+ if (debug) {
369
+ if (override === true) {
370
+ _debug(`"${key}" is already defined and WAS overwritten`);
371
+ } else {
372
+ _debug(`"${key}" is already defined and was NOT overwritten`);
373
+ }
374
+ }
375
+ } else {
376
+ processEnv[key] = parsed[key];
377
+ }
378
+ }
379
+ }
380
+ var DotenvModule = {
381
+ configDotenv,
382
+ _configVault,
383
+ _parseVault,
384
+ config: config2,
385
+ decrypt,
386
+ parse,
387
+ populate
388
+ };
389
+ module.exports.configDotenv = DotenvModule.configDotenv;
390
+ module.exports._configVault = DotenvModule._configVault;
391
+ module.exports._parseVault = DotenvModule._parseVault;
392
+ module.exports.config = DotenvModule.config;
393
+ module.exports.decrypt = DotenvModule.decrypt;
394
+ module.exports.parse = DotenvModule.parse;
395
+ module.exports.populate = DotenvModule.populate;
396
+ module.exports = DotenvModule;
397
+ }
398
+ });
399
+
400
+ // src/config.ts
401
+ var import_dotenv, config;
402
+ var init_config = __esm({
403
+ "src/config.ts"() {
404
+ "use strict";
405
+ import_dotenv = __toESM(require_main(), 1);
406
+ import_dotenv.default.config();
407
+ config = {
408
+ // Weaviate
409
+ weaviate: {
410
+ url: process.env.WEAVIATE_URL || "http://localhost:8080",
411
+ apiKey: process.env.WEAVIATE_API_KEY || ""
412
+ },
413
+ // OpenAI
414
+ openai: {
415
+ apiKey: process.env.OPENAI_APIKEY || ""
416
+ },
417
+ // Firebase (using firebase-admin-sdk-v8)
418
+ firebase: {
419
+ serviceAccount: process.env.FIREBASE_ADMIN_SERVICE_ACCOUNT_KEY || "",
420
+ projectId: process.env.FIREBASE_PROJECT_ID || ""
421
+ },
422
+ // Server
423
+ server: {
424
+ port: parseInt(process.env.PORT || "3000", 10),
425
+ nodeEnv: process.env.NODE_ENV || "development",
426
+ logLevel: process.env.LOG_LEVEL || "info"
427
+ },
428
+ // MCP
429
+ mcp: {
430
+ transport: process.env.MCP_TRANSPORT || "sse"
431
+ }
432
+ };
433
+ }
434
+ });
435
+
436
+ // src/weaviate/client.ts
437
+ var client_exports = {};
438
+ __export(client_exports, {
439
+ closeWeaviateClient: () => closeWeaviateClient,
440
+ collectionExists: () => collectionExists,
441
+ getAuditCollectionName: () => getAuditCollectionName,
442
+ getMemoryCollectionName: () => getMemoryCollectionName,
443
+ getTemplateCollectionName: () => getTemplateCollectionName,
444
+ getWeaviateClient: () => getWeaviateClient,
445
+ initWeaviateClient: () => initWeaviateClient,
446
+ sanitizeUserId: () => sanitizeUserId,
447
+ testWeaviateConnection: () => testWeaviateConnection
448
+ });
449
+ import weaviate from "weaviate-client";
450
+ async function initWeaviateClient() {
451
+ if (client) {
452
+ return client;
453
+ }
454
+ client = await weaviate.connectToWeaviateCloud(config.weaviate.url, {
455
+ authCredentials: config.weaviate.apiKey ? new weaviate.ApiKey(config.weaviate.apiKey) : void 0,
456
+ headers: config.openai.apiKey ? { "X-OpenAI-Api-Key": config.openai.apiKey } : void 0
457
+ });
458
+ console.log("[Weaviate] Client initialized");
459
+ return client;
460
+ }
461
+ function getWeaviateClient() {
462
+ if (!client) {
463
+ throw new Error("Weaviate client not initialized. Call initWeaviateClient() first.");
464
+ }
465
+ return client;
466
+ }
467
+ async function testWeaviateConnection() {
468
+ try {
469
+ const weaviateClient = getWeaviateClient();
470
+ const isReady = await weaviateClient.isReady();
471
+ console.log("[Weaviate] Connection successful, ready:", isReady);
472
+ return isReady;
473
+ } catch (error) {
474
+ console.error("[Weaviate] Connection failed:", error);
475
+ return false;
476
+ }
477
+ }
478
+ function sanitizeUserId(userId) {
479
+ let sanitized = userId.replace(/[^a-zA-Z0-9]/g, "_");
480
+ if (/^[0-9]/.test(sanitized)) {
481
+ sanitized = "_" + sanitized;
482
+ }
483
+ return sanitized.charAt(0).toUpperCase() + sanitized.slice(1);
484
+ }
485
+ function getMemoryCollectionName(userId) {
486
+ return `Memory_${sanitizeUserId(userId)}`;
487
+ }
488
+ function getTemplateCollectionName(userId) {
489
+ return `Template_${sanitizeUserId(userId)}`;
490
+ }
491
+ function getAuditCollectionName(userId) {
492
+ return `Audit_${sanitizeUserId(userId)}`;
493
+ }
494
+ async function collectionExists(collectionName) {
495
+ try {
496
+ const weaviateClient = getWeaviateClient();
497
+ const exists = await weaviateClient.collections.exists(collectionName);
498
+ return exists;
499
+ } catch (error) {
500
+ console.error(`[Weaviate] Error checking collection ${collectionName}:`, error);
501
+ return false;
502
+ }
503
+ }
504
+ async function closeWeaviateClient() {
505
+ if (client) {
506
+ client = null;
507
+ console.log("[Weaviate] Client closed");
508
+ }
509
+ }
510
+ var client;
511
+ var init_client = __esm({
512
+ "src/weaviate/client.ts"() {
513
+ "use strict";
514
+ init_config();
515
+ client = null;
516
+ }
517
+ });
518
+
519
+ // src/firestore/init.ts
520
+ var init_exports = {};
521
+ __export(init_exports, {
522
+ FieldValue: () => FieldValue,
523
+ addDocument: () => addDocument,
524
+ batchWrite: () => batchWrite,
525
+ deleteDocument: () => deleteDocument,
526
+ getDocument: () => getDocument,
527
+ initFirestore: () => initFirestore,
528
+ isFirestoreInitialized: () => isFirestoreInitialized,
529
+ queryDocuments: () => queryDocuments,
530
+ setDocument: () => setDocument,
531
+ testFirestoreConnection: () => testFirestoreConnection,
532
+ updateDocument: () => updateDocument,
533
+ verifyIdToken: () => verifyIdToken
534
+ });
535
+ import { initializeApp } from "@prmichaelsen/firebase-admin-sdk-v8";
536
+ import {
537
+ getDocument,
538
+ setDocument,
539
+ addDocument,
540
+ updateDocument,
541
+ deleteDocument,
542
+ queryDocuments,
543
+ batchWrite,
544
+ FieldValue,
545
+ verifyIdToken
546
+ } from "@prmichaelsen/firebase-admin-sdk-v8";
547
+ function initFirestore() {
548
+ if (initialized) {
549
+ return;
550
+ }
551
+ try {
552
+ initializeApp({
553
+ serviceAccount: JSON.parse(config.firebase.serviceAccount),
554
+ projectId: config.firebase.projectId
555
+ });
556
+ initialized = true;
557
+ console.log("[Firestore] Initialized");
558
+ } catch (error) {
559
+ console.error("[Firestore] Initialization failed:", error);
560
+ throw error;
561
+ }
562
+ }
563
+ function isFirestoreInitialized() {
564
+ return initialized;
565
+ }
566
+ async function testFirestoreConnection() {
567
+ try {
568
+ if (!initialized) {
569
+ throw new Error("Firestore not initialized");
570
+ }
571
+ const { getDocument: getDocument2 } = await import("@prmichaelsen/firebase-admin-sdk-v8");
572
+ await getDocument2("_health_check", "test");
573
+ console.log("[Firestore] Connection successful");
574
+ return true;
575
+ } catch (error) {
576
+ console.error("[Firestore] Connection test failed:", error);
577
+ return false;
578
+ }
579
+ }
580
+ var initialized;
581
+ var init_init = __esm({
582
+ "src/firestore/init.ts"() {
583
+ "use strict";
584
+ init_config();
585
+ initialized = false;
586
+ }
587
+ });
588
+
589
+ // src/server-factory.ts
590
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
591
+ import {
592
+ CallToolRequestSchema,
593
+ ListToolsRequestSchema,
594
+ ErrorCode,
595
+ McpError
596
+ } from "@modelcontextprotocol/sdk/types.js";
597
+
598
+ // src/utils/logger.ts
599
+ init_config();
600
+ var LOG_LEVELS = {
601
+ debug: 0,
602
+ info: 1,
603
+ warn: 2,
604
+ error: 3
605
+ };
606
+ var currentLevel = LOG_LEVELS[config.server.logLevel] ?? LOG_LEVELS.info;
607
+ function shouldLog(level) {
608
+ return LOG_LEVELS[level] >= currentLevel;
609
+ }
610
+ var logger = {
611
+ debug: (message, ...args) => {
612
+ if (shouldLog("debug")) {
613
+ console.debug(`[DEBUG] ${message}`, ...args);
614
+ }
615
+ },
616
+ info: (message, ...args) => {
617
+ if (shouldLog("info")) {
618
+ console.info(`[INFO] ${message}`, ...args);
619
+ }
620
+ },
621
+ warn: (message, ...args) => {
622
+ if (shouldLog("warn")) {
623
+ console.warn(`[WARN] ${message}`, ...args);
624
+ }
625
+ },
626
+ error: (message, ...args) => {
627
+ if (shouldLog("error")) {
628
+ console.error(`[ERROR] ${message}`, ...args);
629
+ }
630
+ }
631
+ };
632
+
633
+ // src/weaviate/schema.ts
634
+ init_client();
635
+ import weaviate2 from "weaviate-client";
636
+ async function createMemoryCollection(userId) {
637
+ const client2 = getWeaviateClient();
638
+ const collectionName = `Memory_${sanitizeUserId(userId)}`;
639
+ const exists = await client2.collections.exists(collectionName);
640
+ if (exists) {
641
+ console.log(`[Weaviate] Collection ${collectionName} already exists`);
642
+ return;
643
+ }
644
+ console.log(`[Weaviate] Creating collection ${collectionName}...`);
645
+ await client2.collections.create({
646
+ name: collectionName,
647
+ // Vectorizer configuration
648
+ vectorizers: weaviate2.configure.vectorizer.text2VecOpenAI({
649
+ model: "text-embedding-3-small",
650
+ // Vectorize both memory content and relationship observations
651
+ sourceProperties: ["content", "observation"]
652
+ }),
653
+ properties: [
654
+ // Discriminator
655
+ {
656
+ name: "doc_type",
657
+ dataType: "text",
658
+ description: 'Document type: "memory" or "relationship"'
659
+ },
660
+ // Core identity
661
+ {
662
+ name: "user_id",
663
+ dataType: "text",
664
+ description: "User who owns this document"
665
+ },
666
+ // Memory fields
667
+ {
668
+ name: "content",
669
+ dataType: "text",
670
+ description: "Main memory content (vectorized)"
671
+ },
672
+ {
673
+ name: "title",
674
+ dataType: "text",
675
+ description: "Optional short title"
676
+ },
677
+ {
678
+ name: "summary",
679
+ dataType: "text",
680
+ description: "Optional brief summary"
681
+ },
682
+ {
683
+ name: "type",
684
+ dataType: "text",
685
+ description: "Content type (note, event, person, etc.)"
686
+ },
687
+ // Scoring fields
688
+ {
689
+ name: "weight",
690
+ dataType: "number",
691
+ description: "Significance/priority (0-1)"
692
+ },
693
+ {
694
+ name: "trust",
695
+ dataType: "number",
696
+ description: "Access control level (0-1)"
697
+ },
698
+ {
699
+ name: "confidence",
700
+ dataType: "number",
701
+ description: "System confidence in accuracy (0-1)"
702
+ },
703
+ // Location fields (flattened for Weaviate)
704
+ {
705
+ name: "location_gps_lat",
706
+ dataType: "number",
707
+ description: "GPS latitude"
708
+ },
709
+ {
710
+ name: "location_gps_lng",
711
+ dataType: "number",
712
+ description: "GPS longitude"
713
+ },
714
+ {
715
+ name: "location_address",
716
+ dataType: "text",
717
+ description: "Formatted address"
718
+ },
719
+ {
720
+ name: "location_city",
721
+ dataType: "text",
722
+ description: "City name"
723
+ },
724
+ {
725
+ name: "location_country",
726
+ dataType: "text",
727
+ description: "Country name"
728
+ },
729
+ {
730
+ name: "location_source",
731
+ dataType: "text",
732
+ description: "Location source (gps, ip, manual, etc.)"
733
+ },
734
+ // Locale fields
735
+ {
736
+ name: "locale_language",
737
+ dataType: "text",
738
+ description: "Language code (e.g., en, es, fr)"
739
+ },
740
+ {
741
+ name: "locale_timezone",
742
+ dataType: "text",
743
+ description: "Timezone (e.g., America/Los_Angeles)"
744
+ },
745
+ // Context fields
746
+ {
747
+ name: "context_conversation_id",
748
+ dataType: "text",
749
+ description: "Conversation ID"
750
+ },
751
+ {
752
+ name: "context_summary",
753
+ dataType: "text",
754
+ description: "Brief context summary"
755
+ },
756
+ {
757
+ name: "context_timestamp",
758
+ dataType: "date",
759
+ description: "Context timestamp"
760
+ },
761
+ // Relationships
762
+ {
763
+ name: "relationships",
764
+ dataType: "text[]",
765
+ description: "Array of relationship IDs"
766
+ },
767
+ // Access tracking
768
+ {
769
+ name: "access_count",
770
+ dataType: "number",
771
+ description: "Total times accessed"
772
+ },
773
+ {
774
+ name: "last_accessed_at",
775
+ dataType: "date",
776
+ description: "Most recent access timestamp"
777
+ },
778
+ // Metadata
779
+ {
780
+ name: "tags",
781
+ dataType: "text[]",
782
+ description: "Tags for organization"
783
+ },
784
+ {
785
+ name: "references",
786
+ dataType: "text[]",
787
+ description: "Source URLs"
788
+ },
789
+ {
790
+ name: "created_at",
791
+ dataType: "date",
792
+ description: "Creation timestamp"
793
+ },
794
+ {
795
+ name: "updated_at",
796
+ dataType: "date",
797
+ description: "Last update timestamp"
798
+ },
799
+ {
800
+ name: "version",
801
+ dataType: "number",
802
+ description: "Version number"
803
+ },
804
+ // Template fields
805
+ {
806
+ name: "template_id",
807
+ dataType: "text",
808
+ description: "Template ID if using template"
809
+ },
810
+ // Relationship-specific fields
811
+ {
812
+ name: "memory_ids",
813
+ dataType: "text[]",
814
+ description: "Connected memory IDs (for relationships)"
815
+ },
816
+ {
817
+ name: "relationship_type",
818
+ dataType: "text",
819
+ description: "Relationship type (for relationships)"
820
+ },
821
+ {
822
+ name: "observation",
823
+ dataType: "text",
824
+ description: "Relationship observation (vectorized)"
825
+ },
826
+ {
827
+ name: "strength",
828
+ dataType: "number",
829
+ description: "Relationship strength (0-1)"
830
+ },
831
+ // Computed fields
832
+ {
833
+ name: "base_weight",
834
+ dataType: "number",
835
+ description: "User-specified weight"
836
+ },
837
+ {
838
+ name: "computed_weight",
839
+ dataType: "number",
840
+ description: "Calculated effective weight"
841
+ }
842
+ ]
843
+ });
844
+ console.log(`[Weaviate] Collection ${collectionName} created successfully`);
845
+ }
846
+ async function ensureMemoryCollection(userId) {
847
+ const client2 = getWeaviateClient();
848
+ const collectionName = `Memory_${sanitizeUserId(userId)}`;
849
+ const exists = await client2.collections.exists(collectionName);
850
+ if (!exists) {
851
+ await createMemoryCollection(userId);
852
+ }
853
+ }
854
+ function getMemoryCollection(userId) {
855
+ const client2 = getWeaviateClient();
856
+ const collectionName = `Memory_${sanitizeUserId(userId)}`;
857
+ return client2.collections.get(collectionName);
858
+ }
859
+
860
+ // src/constants/content-types.ts
861
+ var CONTENT_TYPES = [
862
+ // Core types
863
+ "code",
864
+ "note",
865
+ "documentation",
866
+ "reference",
867
+ // Task & Planning
868
+ "todo",
869
+ "checklist",
870
+ "project",
871
+ "goal",
872
+ "habit",
873
+ // Communication
874
+ "email",
875
+ "conversation",
876
+ "meeting",
877
+ "person",
878
+ // Unified person template (replaces both 'contact' and 'person')
879
+ // Content & Media
880
+ "article",
881
+ "webpage",
882
+ "social",
883
+ "image",
884
+ "video",
885
+ "audio",
886
+ "transcript",
887
+ "presentation",
888
+ "spreadsheet",
889
+ "pdf",
890
+ // Creative
891
+ "screenplay",
892
+ "recipe",
893
+ "idea",
894
+ "quote",
895
+ // Personal
896
+ "journal",
897
+ "memory",
898
+ "event",
899
+ // Organizational
900
+ "bookmark",
901
+ "form",
902
+ "location",
903
+ // Business
904
+ "invoice",
905
+ "contract",
906
+ // System
907
+ "system",
908
+ "action",
909
+ "audit",
910
+ "history"
911
+ ];
912
+ var CONTENT_TYPE_METADATA = {
913
+ // Core Types
914
+ code: {
915
+ name: "code",
916
+ category: "core",
917
+ description: "Source code files and programming content",
918
+ examples: ["Code snippets", "Functions", "Scripts", "Configuration files"],
919
+ common_fields: ["language", "framework", "purpose"]
920
+ },
921
+ note: {
922
+ name: "note",
923
+ category: "core",
924
+ description: "Personal notes and quick documentation",
925
+ examples: ["Quick notes", "Reminders", "Observations", "Thoughts"]
926
+ },
927
+ documentation: {
928
+ name: "documentation",
929
+ category: "core",
930
+ description: "Technical documentation and guides",
931
+ examples: ["API docs", "User guides", "Technical specs", "How-to guides"]
932
+ },
933
+ reference: {
934
+ name: "reference",
935
+ category: "core",
936
+ description: "Quick reference guides and cheat sheets",
937
+ examples: ["Command references", "Keyboard shortcuts", "API references", "Cheat sheets"]
938
+ },
939
+ // Task & Planning
940
+ todo: {
941
+ name: "todo",
942
+ category: "task",
943
+ description: "Individual tasks with due dates and priorities",
944
+ examples: ["Task items", "Action items", "Assignments"],
945
+ common_fields: ["due_date", "priority", "status", "assignee"]
946
+ },
947
+ checklist: {
948
+ name: "checklist",
949
+ category: "task",
950
+ description: "Reusable checklists and sequential steps",
951
+ examples: ["Grocery lists", "Packing lists", "Process checklists", "Preparation lists"],
952
+ common_fields: ["items", "completion_percentage"]
953
+ },
954
+ project: {
955
+ name: "project",
956
+ category: "task",
957
+ description: "Project plans and overviews",
958
+ examples: ["Project documentation", "Project plans", "Milestones"],
959
+ common_fields: ["status", "start_date", "end_date", "stakeholders"]
960
+ },
961
+ goal: {
962
+ name: "goal",
963
+ category: "task",
964
+ description: "Goals, objectives, and milestones",
965
+ examples: ["Personal goals", "Professional objectives", "KPIs"],
966
+ common_fields: ["target_date", "progress", "milestones"]
967
+ },
968
+ habit: {
969
+ name: "habit",
970
+ category: "task",
971
+ description: "Routines and habit tracking",
972
+ examples: ["Daily habits", "Routines", "Recurring activities"],
973
+ common_fields: ["frequency", "streak", "trigger"]
974
+ },
975
+ // Communication
976
+ email: {
977
+ name: "email",
978
+ category: "communication",
979
+ description: "Email messages and threads",
980
+ examples: ["Email messages", "Email threads", "Drafts"],
981
+ common_fields: ["from", "to", "subject", "date"]
982
+ },
983
+ conversation: {
984
+ name: "conversation",
985
+ category: "communication",
986
+ description: "Chat logs and conversations",
987
+ examples: ["Chat messages", "Conversation logs", "Discussions"],
988
+ common_fields: ["participants", "platform"]
989
+ },
990
+ meeting: {
991
+ name: "meeting",
992
+ category: "communication",
993
+ description: "Meeting notes and action items",
994
+ examples: ["Meeting notes", "Standup notes", "Conference calls"],
995
+ common_fields: ["attendees", "agenda", "decisions", "action_items"]
996
+ },
997
+ person: {
998
+ name: "person",
999
+ category: "communication",
1000
+ description: "Track information about people - personal, professional, or both",
1001
+ examples: ["Friends", "Family", "Colleagues", "Professional contacts", "Business partners"],
1002
+ common_fields: ["name", "relationship", "company", "job_title", "how_we_met", "contact_info", "birthday", "interests"]
1003
+ },
1004
+ // Content & Media
1005
+ article: {
1006
+ name: "article",
1007
+ category: "content",
1008
+ description: "Articles and blog posts",
1009
+ examples: ["Blog posts", "News articles", "Long-form content"],
1010
+ common_fields: ["author", "publication", "url"]
1011
+ },
1012
+ webpage: {
1013
+ name: "webpage",
1014
+ category: "content",
1015
+ description: "Saved web pages and HTML content",
1016
+ examples: ["Web pages", "HTML documents", "Web content"],
1017
+ common_fields: ["url", "domain", "archived_at"]
1018
+ },
1019
+ social: {
1020
+ name: "social",
1021
+ category: "content",
1022
+ description: "Social media posts and updates",
1023
+ examples: ["Tweets", "Posts", "Status updates"],
1024
+ common_fields: ["platform", "author", "url"]
1025
+ },
1026
+ image: {
1027
+ name: "image",
1028
+ category: "media",
1029
+ description: "Image files and visual content",
1030
+ examples: ["Photos", "Screenshots", "Diagrams", "Illustrations"],
1031
+ common_fields: ["file_path", "dimensions", "format"]
1032
+ },
1033
+ video: {
1034
+ name: "video",
1035
+ category: "media",
1036
+ description: "Video files and recordings",
1037
+ examples: ["Videos", "Recordings", "Tutorials"],
1038
+ common_fields: ["duration", "format", "url"]
1039
+ },
1040
+ audio: {
1041
+ name: "audio",
1042
+ category: "media",
1043
+ description: "Audio files and recordings",
1044
+ examples: ["Voice notes", "Podcasts", "Music", "Recordings"],
1045
+ common_fields: ["duration", "format"]
1046
+ },
1047
+ transcript: {
1048
+ name: "transcript",
1049
+ category: "media",
1050
+ description: "Transcriptions of audio or video",
1051
+ examples: ["Meeting transcripts", "Podcast transcripts", "Video captions"],
1052
+ common_fields: ["source_media", "speakers"]
1053
+ },
1054
+ presentation: {
1055
+ name: "presentation",
1056
+ category: "content",
1057
+ description: "Presentation slides and decks",
1058
+ examples: ["Slide decks", "Pitch decks", "Presentations"],
1059
+ common_fields: ["slide_count", "format"]
1060
+ },
1061
+ spreadsheet: {
1062
+ name: "spreadsheet",
1063
+ category: "content",
1064
+ description: "Data tables and spreadsheet content",
1065
+ examples: ["Spreadsheets", "Data tables", "CSV content"],
1066
+ common_fields: ["rows", "columns", "format"]
1067
+ },
1068
+ pdf: {
1069
+ name: "pdf",
1070
+ category: "content",
1071
+ description: "PDF documents and scanned files",
1072
+ examples: ["PDF documents", "Scanned documents", "Reports"],
1073
+ common_fields: ["pages", "file_size"]
1074
+ },
1075
+ // Creative
1076
+ screenplay: {
1077
+ name: "screenplay",
1078
+ category: "creative",
1079
+ description: "Screenplay and script content",
1080
+ examples: ["Screenplays", "Scripts", "Dialogue"],
1081
+ common_fields: ["characters", "scenes"]
1082
+ },
1083
+ recipe: {
1084
+ name: "recipe",
1085
+ category: "creative",
1086
+ description: "Cooking recipes and instructions",
1087
+ examples: ["Recipes", "Cooking instructions", "Meal plans"],
1088
+ common_fields: ["ingredients", "instructions", "servings", "prep_time", "cook_time"]
1089
+ },
1090
+ idea: {
1091
+ name: "idea",
1092
+ category: "creative",
1093
+ description: "Brainstorming and concepts",
1094
+ examples: ["Ideas", "Brainstorms", "Concepts", "Inspiration"],
1095
+ common_fields: ["category", "potential_impact"]
1096
+ },
1097
+ quote: {
1098
+ name: "quote",
1099
+ category: "creative",
1100
+ description: "Memorable quotes and excerpts",
1101
+ examples: ["Quotes", "Excerpts", "Highlights", "Citations"],
1102
+ common_fields: ["author", "source"]
1103
+ },
1104
+ // Personal
1105
+ journal: {
1106
+ name: "journal",
1107
+ category: "personal",
1108
+ description: "Daily journal entries and reflections",
1109
+ examples: ["Journal entries", "Diary entries", "Reflections"],
1110
+ common_fields: ["date", "mood", "highlights"]
1111
+ },
1112
+ memory: {
1113
+ name: "memory",
1114
+ category: "personal",
1115
+ description: "Personal memories and significant moments",
1116
+ examples: ["Life events", "Significant moments", "Memories"],
1117
+ common_fields: ["date", "people_involved", "location"]
1118
+ },
1119
+ event: {
1120
+ name: "event",
1121
+ category: "personal",
1122
+ description: "Calendar events and activities",
1123
+ examples: ["Events", "Activities", "Appointments"],
1124
+ common_fields: ["date", "time", "location", "attendees"]
1125
+ },
1126
+ // Organizational
1127
+ bookmark: {
1128
+ name: "bookmark",
1129
+ category: "organizational",
1130
+ description: "Web bookmarks and resource collections",
1131
+ examples: ["Bookmarks", "Resource links", "Reading lists"],
1132
+ common_fields: ["url", "domain", "read_later"]
1133
+ },
1134
+ form: {
1135
+ name: "form",
1136
+ category: "organizational",
1137
+ description: "Forms and surveys",
1138
+ examples: ["Questionnaires", "Feedback forms", "Surveys"],
1139
+ common_fields: ["fields", "responses"]
1140
+ },
1141
+ location: {
1142
+ name: "location",
1143
+ category: "organizational",
1144
+ description: "Place information and recommendations",
1145
+ examples: ["Places", "Venues", "Destinations", "Locations"],
1146
+ common_fields: ["address", "gps", "rating"]
1147
+ },
1148
+ // Business
1149
+ invoice: {
1150
+ name: "invoice",
1151
+ category: "business",
1152
+ description: "Invoices and receipts",
1153
+ examples: ["Invoices", "Receipts", "Bills"],
1154
+ common_fields: ["amount", "date", "vendor", "items"]
1155
+ },
1156
+ contract: {
1157
+ name: "contract",
1158
+ category: "business",
1159
+ description: "Contracts and agreements",
1160
+ examples: ["Contracts", "Agreements", "Terms of service"],
1161
+ common_fields: ["parties", "effective_date", "terms"]
1162
+ },
1163
+ // System
1164
+ system: {
1165
+ name: "system",
1166
+ category: "system",
1167
+ description: "Agent instructions (reserved for internal use only)",
1168
+ examples: ["System prompts", "Agent instructions", "Configuration"]
1169
+ },
1170
+ action: {
1171
+ name: "action",
1172
+ category: "system",
1173
+ description: "Agent actions and operations",
1174
+ examples: ["Actions taken", "Operations performed", "Commands executed"],
1175
+ common_fields: ["action_type", "status", "result"]
1176
+ },
1177
+ audit: {
1178
+ name: "audit",
1179
+ category: "system",
1180
+ description: "Audit logs and compliance records",
1181
+ examples: ["Audit logs", "Access logs", "Security events"],
1182
+ common_fields: ["event_type", "actor", "target", "result"]
1183
+ },
1184
+ history: {
1185
+ name: "history",
1186
+ category: "system",
1187
+ description: "Change history and version tracking",
1188
+ examples: ["Edit history", "Version history", "Change logs"],
1189
+ common_fields: ["target_id", "change_type", "previous_value", "new_value"]
1190
+ }
1191
+ };
1192
+ function isValidContentType(type) {
1193
+ return CONTENT_TYPES.includes(type);
1194
+ }
1195
+ function getContentTypeDescription() {
1196
+ const categoryNames = {
1197
+ core: "Core Types",
1198
+ task: "Task & Planning",
1199
+ communication: "Communication",
1200
+ content: "Content & Media",
1201
+ media: "Content & Media",
1202
+ creative: "Creative",
1203
+ personal: "Personal",
1204
+ organizational: "Organizational",
1205
+ business: "Business",
1206
+ system: "System (Internal Use)"
1207
+ };
1208
+ const lines = ["Type of content:", ""];
1209
+ const categorized = /* @__PURE__ */ new Map();
1210
+ for (const type of CONTENT_TYPES) {
1211
+ const metadata = CONTENT_TYPE_METADATA[type];
1212
+ const categoryKey = metadata.category;
1213
+ if (!categorized.has(categoryKey)) {
1214
+ categorized.set(categoryKey, []);
1215
+ }
1216
+ categorized.get(categoryKey).push(type);
1217
+ }
1218
+ for (const [categoryKey, types] of categorized) {
1219
+ const categoryName = categoryNames[categoryKey] || categoryKey;
1220
+ lines.push(`${categoryName}:`);
1221
+ for (const type of types) {
1222
+ const metadata = CONTENT_TYPE_METADATA[type];
1223
+ lines.push(` - '${type}': ${metadata.description}`);
1224
+ }
1225
+ lines.push("");
1226
+ }
1227
+ return lines.join("\n").trim();
1228
+ }
1229
+ var DEFAULT_CONTENT_TYPE = "note";
1230
+
1231
+ // src/tools/create-memory.ts
1232
+ var createMemoryTool = {
1233
+ name: "remember_create_memory",
1234
+ description: `Create a new memory with optional template.
1235
+
1236
+ Memories can store any type of information: notes, events, people, recipes, etc.
1237
+ Each memory has a weight (significance 0-1) and trust level (access control 0-1).
1238
+ Location and context are automatically captured from the request.
1239
+
1240
+ Examples:
1241
+ - "Remember that I met Sarah at the conference"
1242
+ - "Save this recipe for chocolate chip cookies"
1243
+ - "Note that my tent is stored in garage bin A4"
1244
+ `,
1245
+ inputSchema: {
1246
+ type: "object",
1247
+ properties: {
1248
+ content: {
1249
+ type: "string",
1250
+ description: "Memory content (main text)"
1251
+ },
1252
+ title: {
1253
+ type: "string",
1254
+ description: "Optional short title"
1255
+ },
1256
+ type: {
1257
+ type: "string",
1258
+ description: getContentTypeDescription(),
1259
+ default: DEFAULT_CONTENT_TYPE
1260
+ },
1261
+ weight: {
1262
+ type: "number",
1263
+ description: "Significance/priority (0-1, default: 0.5)",
1264
+ minimum: 0,
1265
+ maximum: 1
1266
+ },
1267
+ trust: {
1268
+ type: "number",
1269
+ description: "Access control level (0-1, default: 0.5)",
1270
+ minimum: 0,
1271
+ maximum: 1
1272
+ },
1273
+ tags: {
1274
+ type: "array",
1275
+ items: { type: "string" },
1276
+ description: "Tags for organization"
1277
+ },
1278
+ references: {
1279
+ type: "array",
1280
+ items: { type: "string" },
1281
+ description: "Source URLs"
1282
+ },
1283
+ template_id: {
1284
+ type: "string",
1285
+ description: "Template ID to use (optional)"
1286
+ },
1287
+ skip_template_suggestion: {
1288
+ type: "boolean",
1289
+ description: "Skip automatic template suggestion",
1290
+ default: false
1291
+ }
1292
+ },
1293
+ required: ["content"]
1294
+ }
1295
+ };
1296
+ async function handleCreateMemory(args, userId, context) {
1297
+ try {
1298
+ logger.info("Creating memory", { userId, type: args.type });
1299
+ await ensureMemoryCollection(userId);
1300
+ const collection = getMemoryCollection(userId);
1301
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1302
+ const memory = {
1303
+ // Core identity
1304
+ user_id: userId,
1305
+ doc_type: "memory",
1306
+ // Content
1307
+ content: args.content,
1308
+ title: args.title,
1309
+ summary: args.title,
1310
+ // Use title as summary for now
1311
+ type: args.type && isValidContentType(args.type) ? args.type : DEFAULT_CONTENT_TYPE,
1312
+ // Scoring
1313
+ weight: args.weight ?? 0.5,
1314
+ trust: args.trust ?? 0.5,
1315
+ confidence: 1,
1316
+ // Location (from context or default)
1317
+ location: {
1318
+ gps: null,
1319
+ address: null,
1320
+ source: "unavailable",
1321
+ confidence: 0,
1322
+ is_approximate: true
1323
+ },
1324
+ // Context
1325
+ context: {
1326
+ timestamp: now,
1327
+ source: {
1328
+ type: "api",
1329
+ platform: "mcp"
1330
+ },
1331
+ summary: context?.summary || "Memory created via MCP",
1332
+ conversation_id: context?.conversation_id,
1333
+ ...context
1334
+ },
1335
+ // Relationships
1336
+ relationships: [],
1337
+ // Access tracking
1338
+ access_count: 0,
1339
+ last_accessed_at: now,
1340
+ access_frequency: 0,
1341
+ // Metadata
1342
+ created_at: now,
1343
+ updated_at: now,
1344
+ version: 1,
1345
+ tags: args.tags || [],
1346
+ references: args.references || [],
1347
+ // Template
1348
+ template_id: args.template_id,
1349
+ structured_content: args.structured_content,
1350
+ // Computed weight
1351
+ base_weight: args.weight ?? 0.5,
1352
+ computed_weight: args.weight ?? 0.5
1353
+ };
1354
+ const result = await collection.data.insert(memory);
1355
+ logger.info("Memory created successfully", { memoryId: result, userId });
1356
+ const response = {
1357
+ memory_id: result,
1358
+ created_at: now,
1359
+ message: `Memory created successfully with ID: ${result}`
1360
+ };
1361
+ return JSON.stringify(response, null, 2);
1362
+ } catch (error) {
1363
+ logger.error("Failed to create memory:", error);
1364
+ throw new Error(`Failed to create memory: ${error instanceof Error ? error.message : String(error)}`);
1365
+ }
1366
+ }
1367
+
1368
+ // src/tools/search-memory.ts
1369
+ var searchMemoryTool = {
1370
+ name: "remember_search_memory",
1371
+ description: `Search memories using hybrid semantic and keyword search.
1372
+
1373
+ Supports:
1374
+ - Semantic search (meaning-based)
1375
+ - Keyword search (exact matches)
1376
+ - Hybrid search (balanced with alpha parameter)
1377
+ - Filtering by type, tags, weight, trust, date range
1378
+ - Location-based search
1379
+
1380
+ Examples:
1381
+ - "Find memories about camping trips"
1382
+ - "Search for recipes I saved"
1383
+ - "Show me notes from last week"
1384
+ `,
1385
+ inputSchema: {
1386
+ type: "object",
1387
+ properties: {
1388
+ query: {
1389
+ type: "string",
1390
+ description: "Search query"
1391
+ },
1392
+ alpha: {
1393
+ type: "number",
1394
+ description: "Balance between semantic (1.0) and keyword (0.0) search. Default: 0.7",
1395
+ minimum: 0,
1396
+ maximum: 1,
1397
+ default: 0.7
1398
+ },
1399
+ limit: {
1400
+ type: "number",
1401
+ description: "Maximum number of results. Default: 10",
1402
+ minimum: 1,
1403
+ maximum: 100,
1404
+ default: 10
1405
+ },
1406
+ offset: {
1407
+ type: "number",
1408
+ description: "Pagination offset. Default: 0",
1409
+ minimum: 0,
1410
+ default: 0
1411
+ },
1412
+ filters: {
1413
+ type: "object",
1414
+ description: "Optional filters",
1415
+ properties: {
1416
+ types: {
1417
+ type: "array",
1418
+ items: { type: "string" },
1419
+ description: "Filter by content types"
1420
+ },
1421
+ tags: {
1422
+ type: "array",
1423
+ items: { type: "string" },
1424
+ description: "Filter by tags"
1425
+ },
1426
+ weight_min: {
1427
+ type: "number",
1428
+ description: "Minimum weight (0-1)"
1429
+ },
1430
+ trust_min: {
1431
+ type: "number",
1432
+ description: "Minimum trust level (0-1)"
1433
+ },
1434
+ date_from: {
1435
+ type: "string",
1436
+ description: "Start date (ISO 8601)"
1437
+ },
1438
+ date_to: {
1439
+ type: "string",
1440
+ description: "End date (ISO 8601)"
1441
+ }
1442
+ }
1443
+ },
1444
+ include_relationships: {
1445
+ type: "boolean",
1446
+ description: "Include relationships in results. Default: false",
1447
+ default: false
1448
+ }
1449
+ },
1450
+ required: ["query"]
1451
+ }
1452
+ };
1453
+ async function handleSearchMemory(args, userId) {
1454
+ try {
1455
+ logger.info("Searching memories", { userId, query: args.query });
1456
+ const collection = getMemoryCollection(userId);
1457
+ const alpha = args.alpha ?? 0.7;
1458
+ const limit = args.limit ?? 10;
1459
+ const offset = args.offset ?? 0;
1460
+ const whereFilters = [
1461
+ {
1462
+ path: "doc_type",
1463
+ operator: "Equal",
1464
+ valueText: "memory"
1465
+ }
1466
+ ];
1467
+ if (args.filters?.types && args.filters.types.length > 0) {
1468
+ whereFilters.push({
1469
+ path: "type",
1470
+ operator: "ContainsAny",
1471
+ valueTextArray: args.filters.types
1472
+ });
1473
+ }
1474
+ if (args.filters?.weight_min !== void 0) {
1475
+ whereFilters.push({
1476
+ path: "weight",
1477
+ operator: "GreaterThanEqual",
1478
+ valueNumber: args.filters.weight_min
1479
+ });
1480
+ }
1481
+ if (args.filters?.trust_min !== void 0) {
1482
+ whereFilters.push({
1483
+ path: "trust",
1484
+ operator: "GreaterThanEqual",
1485
+ valueNumber: args.filters.trust_min
1486
+ });
1487
+ }
1488
+ if (args.filters?.date_from) {
1489
+ whereFilters.push({
1490
+ path: "created_at",
1491
+ operator: "GreaterThanEqual",
1492
+ valueDate: new Date(args.filters.date_from)
1493
+ });
1494
+ }
1495
+ if (args.filters?.date_to) {
1496
+ whereFilters.push({
1497
+ path: "created_at",
1498
+ operator: "LessThanEqual",
1499
+ valueDate: new Date(args.filters.date_to)
1500
+ });
1501
+ }
1502
+ const searchOptions = {
1503
+ alpha,
1504
+ limit: limit + offset
1505
+ // Get extra for offset
1506
+ };
1507
+ if (whereFilters.length > 0) {
1508
+ searchOptions.filters = whereFilters.length > 1 ? {
1509
+ operator: "And",
1510
+ operands: whereFilters
1511
+ } : whereFilters[0];
1512
+ }
1513
+ const results = await collection.query.hybrid(args.query, searchOptions);
1514
+ const paginatedResults = results.objects.slice(offset);
1515
+ const memories = paginatedResults.map((obj) => ({
1516
+ id: obj.uuid,
1517
+ ...obj.properties
1518
+ }));
1519
+ const searchResult = {
1520
+ memories,
1521
+ total: memories.length,
1522
+ offset,
1523
+ limit
1524
+ };
1525
+ if (args.include_relationships) {
1526
+ searchResult.relationships = [];
1527
+ }
1528
+ logger.info("Search completed", {
1529
+ userId,
1530
+ query: args.query,
1531
+ results: memories.length
1532
+ });
1533
+ return JSON.stringify(searchResult, null, 2);
1534
+ } catch (error) {
1535
+ logger.error("Failed to search memories:", error);
1536
+ throw new Error(`Failed to search memories: ${error instanceof Error ? error.message : String(error)}`);
1537
+ }
1538
+ }
1539
+
1540
+ // src/tools/delete-memory.ts
1541
+ var deleteMemoryTool = {
1542
+ name: "remember_delete_memory",
1543
+ description: `Delete a memory from your collection.
1544
+
1545
+ Optionally delete connected relationships as well.
1546
+ This action cannot be undone.
1547
+
1548
+ Examples:
1549
+ - "Delete that old camping note"
1550
+ - "Remove the recipe I saved yesterday"
1551
+ `,
1552
+ inputSchema: {
1553
+ type: "object",
1554
+ properties: {
1555
+ memory_id: {
1556
+ type: "string",
1557
+ description: "ID of the memory to delete"
1558
+ },
1559
+ delete_relationships: {
1560
+ type: "boolean",
1561
+ description: "Also delete connected relationships. Default: false",
1562
+ default: false
1563
+ }
1564
+ },
1565
+ required: ["memory_id"]
1566
+ }
1567
+ };
1568
+ async function handleDeleteMemory(args, userId) {
1569
+ try {
1570
+ logger.info("Deleting memory", { userId, memoryId: args.memory_id });
1571
+ const collection = getMemoryCollection(userId);
1572
+ const memory = await collection.query.fetchObjectById(args.memory_id, {
1573
+ returnProperties: ["user_id", "doc_type", "relationships"]
1574
+ });
1575
+ if (!memory) {
1576
+ throw new Error(`Memory not found: ${args.memory_id}`);
1577
+ }
1578
+ if (memory.properties.user_id !== userId) {
1579
+ throw new Error("Unauthorized: Cannot delete another user's memory");
1580
+ }
1581
+ if (memory.properties.doc_type !== "memory") {
1582
+ throw new Error("Cannot delete relationships using this tool. Use remember_delete_relationship instead.");
1583
+ }
1584
+ let relationshipsDeleted = 0;
1585
+ if (args.delete_relationships && memory.properties.relationships) {
1586
+ const relationshipIds = memory.properties.relationships;
1587
+ for (const relId of relationshipIds) {
1588
+ try {
1589
+ await collection.data.deleteById(relId);
1590
+ relationshipsDeleted++;
1591
+ } catch (error) {
1592
+ logger.warn(`Failed to delete relationship ${relId}:`, error);
1593
+ }
1594
+ }
1595
+ }
1596
+ await collection.data.deleteById(args.memory_id);
1597
+ logger.info("Memory deleted successfully", {
1598
+ userId,
1599
+ memoryId: args.memory_id,
1600
+ relationshipsDeleted
1601
+ });
1602
+ const result = {
1603
+ memory_id: args.memory_id,
1604
+ deleted: true,
1605
+ relationships_deleted: relationshipsDeleted,
1606
+ message: `Memory deleted successfully${relationshipsDeleted > 0 ? ` (${relationshipsDeleted} relationships also deleted)` : ""}`
1607
+ };
1608
+ return JSON.stringify(result, null, 2);
1609
+ } catch (error) {
1610
+ logger.error("Failed to delete memory:", error);
1611
+ throw new Error(`Failed to delete memory: ${error instanceof Error ? error.message : String(error)}`);
1612
+ }
1613
+ }
1614
+
1615
+ // src/server-factory.ts
1616
+ function createServer(accessToken, userId, options = {}) {
1617
+ if (!accessToken) {
1618
+ throw new Error("accessToken is required");
1619
+ }
1620
+ if (!userId) {
1621
+ throw new Error("userId is required");
1622
+ }
1623
+ logger.debug("Creating server instance", { userId });
1624
+ const server = new Server(
1625
+ {
1626
+ name: options.name || "remember-mcp",
1627
+ version: options.version || "0.1.0"
1628
+ },
1629
+ {
1630
+ capabilities: {
1631
+ tools: {}
1632
+ }
1633
+ }
1634
+ );
1635
+ registerHandlers(server, userId, accessToken);
1636
+ return server;
1637
+ }
1638
+ function registerHandlers(server, userId, accessToken) {
1639
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
1640
+ return {
1641
+ tools: [
1642
+ {
1643
+ name: "health_check",
1644
+ description: "Check server health and database connections",
1645
+ inputSchema: {
1646
+ type: "object",
1647
+ properties: {}
1648
+ }
1649
+ },
1650
+ createMemoryTool,
1651
+ searchMemoryTool,
1652
+ deleteMemoryTool
1653
+ ]
1654
+ };
1655
+ });
1656
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1657
+ const { name, arguments: args } = request.params;
1658
+ try {
1659
+ let result;
1660
+ switch (name) {
1661
+ case "health_check":
1662
+ result = await handleHealthCheck(userId);
1663
+ break;
1664
+ case "remember_create_memory":
1665
+ result = await handleCreateMemory(args, userId);
1666
+ break;
1667
+ case "remember_search_memory":
1668
+ result = await handleSearchMemory(args, userId);
1669
+ break;
1670
+ case "remember_delete_memory":
1671
+ result = await handleDeleteMemory(args, userId);
1672
+ break;
1673
+ default:
1674
+ throw new McpError(
1675
+ ErrorCode.MethodNotFound,
1676
+ `Unknown tool: ${name}`
1677
+ );
1678
+ }
1679
+ return {
1680
+ content: [
1681
+ {
1682
+ type: "text",
1683
+ text: result
1684
+ }
1685
+ ]
1686
+ };
1687
+ } catch (error) {
1688
+ if (error instanceof McpError) {
1689
+ throw error;
1690
+ }
1691
+ logger.error(`Tool execution failed for ${name}:`, error);
1692
+ throw new McpError(
1693
+ ErrorCode.InternalError,
1694
+ `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`
1695
+ );
1696
+ }
1697
+ });
1698
+ }
1699
+ async function handleHealthCheck(userId) {
1700
+ const health = {
1701
+ status: "healthy",
1702
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1703
+ userId,
1704
+ server: {
1705
+ name: "remember-mcp",
1706
+ version: "0.1.0"
1707
+ },
1708
+ databases: {
1709
+ weaviate: {
1710
+ connected: false,
1711
+ userCollection: `Memory_${userId}`
1712
+ },
1713
+ firestore: {
1714
+ connected: false,
1715
+ userPath: `users/${userId}`
1716
+ }
1717
+ }
1718
+ };
1719
+ try {
1720
+ const { getWeaviateClient: getWeaviateClient2 } = await Promise.resolve().then(() => (init_client(), client_exports));
1721
+ const weaviateClient = getWeaviateClient2();
1722
+ health.databases.weaviate.connected = await weaviateClient.isReady();
1723
+ } catch (error) {
1724
+ logger.error("Weaviate health check failed:", error);
1725
+ health.databases.weaviate.connected = false;
1726
+ }
1727
+ try {
1728
+ const { testFirestoreConnection: testFirestoreConnection2 } = await Promise.resolve().then(() => (init_init(), init_exports));
1729
+ health.databases.firestore.connected = await testFirestoreConnection2();
1730
+ } catch (error) {
1731
+ logger.error("Firestore health check failed:", error);
1732
+ health.databases.firestore.connected = false;
1733
+ }
1734
+ const allHealthy = health.databases.weaviate.connected && health.databases.firestore.connected;
1735
+ health.status = allHealthy ? "healthy" : "degraded";
1736
+ return JSON.stringify(health, null, 2);
1737
+ }
1738
+ export {
1739
+ createServer
1740
+ };
1741
+ //# sourceMappingURL=server-factory.js.map