@frontmcp/plugin-remember 0.0.1 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/esm/index.mjs +1289 -0
  2. package/esm/package.json +65 -0
  3. package/index.d.ts +53 -0
  4. package/index.d.ts.map +1 -0
  5. package/index.js +1316 -0
  6. package/package.json +3 -3
  7. package/providers/index.d.ts +7 -0
  8. package/providers/index.d.ts.map +1 -0
  9. package/providers/remember-accessor.provider.d.ts +124 -0
  10. package/providers/remember-accessor.provider.d.ts.map +1 -0
  11. package/providers/remember-memory.provider.d.ts +53 -0
  12. package/providers/remember-memory.provider.d.ts.map +1 -0
  13. package/providers/remember-redis.provider.d.ts +62 -0
  14. package/providers/remember-redis.provider.d.ts.map +1 -0
  15. package/providers/remember-storage.provider.d.ts +126 -0
  16. package/providers/remember-storage.provider.d.ts.map +1 -0
  17. package/providers/remember-store.interface.d.ts +42 -0
  18. package/providers/remember-store.interface.d.ts.map +1 -0
  19. package/providers/remember-vercel-kv.provider.d.ts +52 -0
  20. package/providers/remember-vercel-kv.provider.d.ts.map +1 -0
  21. package/remember.context-extension.d.ts +65 -0
  22. package/remember.context-extension.d.ts.map +1 -0
  23. package/remember.crypto.d.ts +81 -0
  24. package/remember.crypto.d.ts.map +1 -0
  25. package/remember.plugin.d.ts +41 -0
  26. package/remember.plugin.d.ts.map +1 -0
  27. package/remember.secret-persistence.d.ts +77 -0
  28. package/remember.secret-persistence.d.ts.map +1 -0
  29. package/remember.symbols.d.ts +28 -0
  30. package/remember.symbols.d.ts.map +1 -0
  31. package/remember.types.d.ts +206 -0
  32. package/remember.types.d.ts.map +1 -0
  33. package/tools/forget.tool.d.ts +32 -0
  34. package/tools/forget.tool.d.ts.map +1 -0
  35. package/tools/index.d.ts +9 -0
  36. package/tools/index.d.ts.map +1 -0
  37. package/tools/list-memories.tool.d.ts +33 -0
  38. package/tools/list-memories.tool.d.ts.map +1 -0
  39. package/tools/recall.tool.d.ts +38 -0
  40. package/tools/recall.tool.d.ts.map +1 -0
  41. package/tools/remember-this.tool.d.ts +42 -0
  42. package/tools/remember-this.tool.d.ts.map +1 -0
package/index.js ADDED
@@ -0,0 +1,1316 @@
1
+ "use strict";
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 __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+ var __decorateClass = (decorators, target, key, kind) => {
31
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
32
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
33
+ if (decorator = decorators[i])
34
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
35
+ if (kind && result) __defProp(target, key, result);
36
+ return result;
37
+ };
38
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
39
+
40
+ // plugins/plugin-remember/src/index.ts
41
+ var index_exports = {};
42
+ __export(index_exports, {
43
+ ForgetTool: () => ForgetTool,
44
+ ListMemoriesTool: () => ListMemoriesTool,
45
+ RecallTool: () => RecallTool,
46
+ RememberAccessor: () => RememberAccessor,
47
+ RememberAccessorToken: () => RememberAccessorToken,
48
+ RememberConfigToken: () => RememberConfigToken,
49
+ RememberMemoryProvider: () => RememberMemoryProvider,
50
+ RememberPlugin: () => RememberPlugin,
51
+ RememberRedisProvider: () => RememberRedisProvider,
52
+ RememberStoreToken: () => RememberStoreToken,
53
+ RememberThisTool: () => RememberThisTool,
54
+ RememberVercelKvProvider: () => RememberVercelKvProvider,
55
+ brand: () => brand,
56
+ default: () => RememberPlugin,
57
+ getRemember: () => getRemember,
58
+ tryGetRemember: () => tryGetRemember
59
+ });
60
+ module.exports = __toCommonJS(index_exports);
61
+
62
+ // plugins/plugin-remember/src/remember.plugin.ts
63
+ var import_sdk5 = require("@frontmcp/sdk");
64
+
65
+ // plugins/plugin-remember/src/remember.symbols.ts
66
+ var RememberStoreToken = /* @__PURE__ */ Symbol(
67
+ "plugin:remember:store"
68
+ );
69
+ var RememberConfigToken = /* @__PURE__ */ Symbol(
70
+ "plugin:remember:config"
71
+ );
72
+ var RememberAccessorToken = /* @__PURE__ */ Symbol(
73
+ "plugin:remember:accessor"
74
+ );
75
+
76
+ // plugins/plugin-remember/src/providers/remember-memory.provider.ts
77
+ var import_sdk = require("@frontmcp/sdk");
78
+ var MAX_TIMEOUT_MS = 2 ** 31 - 1;
79
+ var RememberMemoryProvider = class {
80
+ memory = /* @__PURE__ */ new Map();
81
+ sweeper;
82
+ constructor(sweepIntervalSeconds = 60) {
83
+ this.sweeper = setInterval(() => this.sweep(), sweepIntervalSeconds * 1e3);
84
+ this.sweeper.unref?.();
85
+ }
86
+ /**
87
+ * Store a value with optional TTL.
88
+ * Always JSON-serializes the value for consistent storage/retrieval.
89
+ */
90
+ async setValue(key, value, ttlSeconds) {
91
+ const strValue = JSON.stringify(value);
92
+ const existing = this.memory.get(key);
93
+ if (existing?.timeout) clearTimeout(existing.timeout);
94
+ const entry = { value: strValue };
95
+ if (ttlSeconds && ttlSeconds > 0) {
96
+ const ttlMs = ttlSeconds * 1e3;
97
+ entry.expiresAt = Date.now() + ttlMs;
98
+ if (ttlMs <= MAX_TIMEOUT_MS) {
99
+ entry.timeout = setTimeout(() => {
100
+ const e = this.memory.get(key);
101
+ if (e && e.expiresAt && e.expiresAt <= Date.now()) {
102
+ this.memory.delete(key);
103
+ }
104
+ }, ttlMs);
105
+ entry.timeout.unref?.();
106
+ }
107
+ }
108
+ this.memory.set(key, entry);
109
+ }
110
+ /**
111
+ * Retrieve a value by key.
112
+ * JSON-parses the stored value to match the setValue serialization.
113
+ */
114
+ async getValue(key, defaultValue) {
115
+ const entry = this.memory.get(key);
116
+ if (!entry) return defaultValue;
117
+ if (this.isExpired(entry)) {
118
+ await this.delete(key);
119
+ return defaultValue;
120
+ }
121
+ try {
122
+ return JSON.parse(entry.value);
123
+ } catch {
124
+ return entry.value;
125
+ }
126
+ }
127
+ /**
128
+ * Delete a key.
129
+ */
130
+ async delete(key) {
131
+ const entry = this.memory.get(key);
132
+ if (entry?.timeout) clearTimeout(entry.timeout);
133
+ this.memory.delete(key);
134
+ }
135
+ /**
136
+ * Check if a key exists (and is not expired).
137
+ */
138
+ async exists(key) {
139
+ const entry = this.memory.get(key);
140
+ if (!entry) return false;
141
+ if (this.isExpired(entry)) {
142
+ await this.delete(key);
143
+ return false;
144
+ }
145
+ return true;
146
+ }
147
+ /**
148
+ * List keys matching a glob-style pattern.
149
+ * Supports * and ? wildcards.
150
+ */
151
+ async keys(pattern) {
152
+ const result = [];
153
+ const regex = pattern ? this.patternToRegex(pattern) : null;
154
+ const expiredKeys = [];
155
+ for (const [key, entry] of this.memory) {
156
+ if (this.isExpired(entry)) {
157
+ expiredKeys.push(key);
158
+ }
159
+ }
160
+ for (const key of expiredKeys) {
161
+ await this.delete(key);
162
+ }
163
+ for (const [key, entry] of this.memory) {
164
+ if (this.isExpired(entry)) {
165
+ continue;
166
+ }
167
+ if (regex && !regex.test(key)) {
168
+ continue;
169
+ }
170
+ result.push(key);
171
+ }
172
+ return result;
173
+ }
174
+ /**
175
+ * Gracefully close the provider.
176
+ */
177
+ async close() {
178
+ if (this.sweeper) clearInterval(this.sweeper);
179
+ for (const [, entry] of this.memory) {
180
+ if (entry.timeout) clearTimeout(entry.timeout);
181
+ }
182
+ this.memory.clear();
183
+ }
184
+ // ─────────────────────────────────────────────────────────────────────────────
185
+ // Private Methods
186
+ // ─────────────────────────────────────────────────────────────────────────────
187
+ isExpired(entry) {
188
+ return entry.expiresAt !== void 0 && entry.expiresAt <= Date.now();
189
+ }
190
+ /**
191
+ * Periodically remove expired keys to keep memory tidy.
192
+ */
193
+ sweep() {
194
+ const now = Date.now();
195
+ for (const [key, entry] of this.memory) {
196
+ if (entry.expiresAt !== void 0 && entry.expiresAt <= now) {
197
+ if (entry.timeout) clearTimeout(entry.timeout);
198
+ this.memory.delete(key);
199
+ }
200
+ }
201
+ }
202
+ /**
203
+ * Convert a glob-style pattern to a RegExp.
204
+ * * matches any sequence of characters
205
+ * ? matches any single character
206
+ *
207
+ * Includes ReDoS protection: validates pattern length and wildcard count.
208
+ */
209
+ patternToRegex(pattern) {
210
+ if (pattern.length > RememberMemoryProvider.MAX_PATTERN_LENGTH) {
211
+ throw new Error(`Pattern too long: ${pattern.length} chars (max ${RememberMemoryProvider.MAX_PATTERN_LENGTH})`);
212
+ }
213
+ const wildcardCount = (pattern.match(/[*?]/g) || []).length;
214
+ if (wildcardCount > RememberMemoryProvider.MAX_WILDCARDS) {
215
+ throw new Error(`Too many wildcards in pattern: ${wildcardCount} (max ${RememberMemoryProvider.MAX_WILDCARDS})`);
216
+ }
217
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
218
+ return new RegExp(`^${escaped}$`);
219
+ }
220
+ };
221
+ // ReDoS protection constants
222
+ // Pattern length allows for long prefixes (e.g., remember:session:{uuid}:user:*)
223
+ __publicField(RememberMemoryProvider, "MAX_PATTERN_LENGTH", 500);
224
+ __publicField(RememberMemoryProvider, "MAX_WILDCARDS", 20);
225
+ RememberMemoryProvider = __decorateClass([
226
+ (0, import_sdk.Provider)({
227
+ name: "provider:remember:memory",
228
+ description: "In-memory storage provider for RememberPlugin",
229
+ scope: import_sdk.ProviderScope.GLOBAL
230
+ })
231
+ ], RememberMemoryProvider);
232
+
233
+ // plugins/plugin-remember/src/providers/remember-redis.provider.ts
234
+ var import_ioredis = __toESM(require("ioredis"));
235
+ var import_sdk2 = require("@frontmcp/sdk");
236
+ var RememberRedisProvider = class {
237
+ client;
238
+ /**
239
+ * Prefix prepended to all Redis keys.
240
+ * Include any separator (e.g., "myapp:" or "user:123:") as part of the prefix.
241
+ */
242
+ keyPrefix;
243
+ /** True if this provider created the client (and should close it), false if externally provided */
244
+ ownsClient;
245
+ constructor(options) {
246
+ this.keyPrefix = options.keyPrefix ?? "";
247
+ if (options.type === "redis-client") {
248
+ this.client = options.client;
249
+ this.ownsClient = false;
250
+ return;
251
+ }
252
+ this.ownsClient = true;
253
+ this.client = new import_ioredis.default({
254
+ lazyConnect: false,
255
+ maxRetriesPerRequest: 3,
256
+ ...options.config
257
+ });
258
+ this.client.on("connect", () => {
259
+ if (process.env["DEBUG"] === "true") {
260
+ console.log("[RememberPlugin:Redis] Connected");
261
+ }
262
+ });
263
+ this.client.on("error", (err) => {
264
+ console.error("[RememberPlugin:Redis] Error:", err.message);
265
+ });
266
+ }
267
+ /**
268
+ * Store a value with optional TTL.
269
+ *
270
+ * @param key - The key to store under
271
+ * @param value - The value to store (must not be undefined)
272
+ * @param ttlSeconds - Optional TTL in seconds (must be positive integer if provided)
273
+ * @throws Error if value is undefined or ttlSeconds is invalid
274
+ */
275
+ async setValue(key, value, ttlSeconds) {
276
+ if (value === void 0) {
277
+ throw new Error("Cannot store undefined value. Use null or delete the key instead.");
278
+ }
279
+ if (ttlSeconds !== void 0) {
280
+ if (typeof ttlSeconds !== "number" || !Number.isFinite(ttlSeconds)) {
281
+ throw new Error(`Invalid TTL: expected a number, got ${typeof ttlSeconds}`);
282
+ }
283
+ if (ttlSeconds <= 0) {
284
+ throw new Error(`Invalid TTL: must be positive, got ${ttlSeconds}`);
285
+ }
286
+ if (!Number.isInteger(ttlSeconds)) {
287
+ throw new Error(`Invalid TTL: must be an integer, got ${ttlSeconds}`);
288
+ }
289
+ }
290
+ const fullKey = this.keyPrefix + key;
291
+ const strValue = typeof value === "string" ? value : JSON.stringify(value);
292
+ if (ttlSeconds !== void 0 && ttlSeconds > 0) {
293
+ await this.client.set(fullKey, strValue, "EX", ttlSeconds);
294
+ } else {
295
+ await this.client.set(fullKey, strValue);
296
+ }
297
+ }
298
+ /**
299
+ * Retrieve a value by key.
300
+ *
301
+ * Returns the parsed JSON value if successful, or `defaultValue` if:
302
+ * - The key does not exist
303
+ * - The stored value is not valid JSON (malformed or legacy data)
304
+ *
305
+ * @param key - The key to retrieve
306
+ * @param defaultValue - Value to return if key doesn't exist or parsing fails
307
+ * @returns The parsed value as T, or defaultValue/undefined
308
+ */
309
+ async getValue(key, defaultValue) {
310
+ const fullKey = this.keyPrefix + key;
311
+ const raw = await this.client.get(fullKey);
312
+ if (raw === null) return defaultValue;
313
+ try {
314
+ return JSON.parse(raw);
315
+ } catch {
316
+ return defaultValue;
317
+ }
318
+ }
319
+ /**
320
+ * Delete a key.
321
+ */
322
+ async delete(key) {
323
+ const fullKey = this.keyPrefix + key;
324
+ await this.client.del(fullKey);
325
+ }
326
+ /**
327
+ * Check if a key exists.
328
+ */
329
+ async exists(key) {
330
+ const fullKey = this.keyPrefix + key;
331
+ return await this.client.exists(fullKey) === 1;
332
+ }
333
+ /**
334
+ * List keys matching a pattern.
335
+ * Uses Redis SCAN for efficient iteration.
336
+ */
337
+ async keys(pattern) {
338
+ const searchPattern = this.keyPrefix + (pattern ?? "*");
339
+ const result = [];
340
+ let cursor = "0";
341
+ do {
342
+ const [nextCursor, keys] = await this.client.scan(cursor, "MATCH", searchPattern, "COUNT", 100);
343
+ cursor = nextCursor;
344
+ for (const key of keys) {
345
+ result.push(key.slice(this.keyPrefix.length));
346
+ }
347
+ } while (cursor !== "0");
348
+ return result;
349
+ }
350
+ /**
351
+ * Gracefully close the Redis connection.
352
+ * Only closes if this provider owns the client (created it internally).
353
+ * Externally-provided clients are left open for the caller to manage.
354
+ */
355
+ async close() {
356
+ if (this.ownsClient) {
357
+ await this.client.quit();
358
+ }
359
+ }
360
+ };
361
+ RememberRedisProvider = __decorateClass([
362
+ (0, import_sdk2.Provider)({
363
+ name: "provider:remember:redis",
364
+ description: "Redis-based storage provider for RememberPlugin",
365
+ scope: import_sdk2.ProviderScope.GLOBAL
366
+ })
367
+ ], RememberRedisProvider);
368
+
369
+ // plugins/plugin-remember/src/providers/remember-vercel-kv.provider.ts
370
+ var import_sdk3 = require("@frontmcp/sdk");
371
+ var RememberVercelKvProvider = class {
372
+ kv;
373
+ keyPrefix;
374
+ defaultTTL;
375
+ constructor(options = {}) {
376
+ const vercelKv = require("@vercel/kv");
377
+ const hasUrl = options.url !== void 0;
378
+ const hasToken = options.token !== void 0;
379
+ if (hasUrl !== hasToken) {
380
+ throw new Error(
381
+ `RememberVercelKvProvider: Both 'url' and 'token' must be provided together, or neither. Received: url=${hasUrl ? "provided" : "missing"}, token=${hasToken ? "provided" : "missing"}`
382
+ );
383
+ }
384
+ if (options.url && options.token) {
385
+ this.kv = vercelKv.createClient({
386
+ url: options.url,
387
+ token: options.token
388
+ });
389
+ } else {
390
+ this.kv = vercelKv.kv;
391
+ }
392
+ this.keyPrefix = options.keyPrefix ?? "remember:";
393
+ this.defaultTTL = options.defaultTTL;
394
+ }
395
+ prefixKey(key) {
396
+ return `${this.keyPrefix}${key}`;
397
+ }
398
+ /**
399
+ * Store a value with optional TTL.
400
+ */
401
+ async setValue(key, value, ttlSeconds) {
402
+ const fullKey = this.prefixKey(key);
403
+ const strValue = typeof value === "string" ? value : JSON.stringify(value);
404
+ const ttl = ttlSeconds ?? this.defaultTTL;
405
+ if (ttl && ttl > 0) {
406
+ await this.kv.set(fullKey, strValue, { ex: ttl });
407
+ } else {
408
+ await this.kv.set(fullKey, strValue);
409
+ }
410
+ }
411
+ /**
412
+ * Retrieve a value by key.
413
+ */
414
+ async getValue(key, defaultValue) {
415
+ const fullKey = this.prefixKey(key);
416
+ const raw = await this.kv.get(fullKey);
417
+ if (raw === null || raw === void 0) return defaultValue;
418
+ if (typeof raw === "string") {
419
+ try {
420
+ return JSON.parse(raw);
421
+ } catch {
422
+ return raw;
423
+ }
424
+ }
425
+ return raw;
426
+ }
427
+ /**
428
+ * Delete a key.
429
+ */
430
+ async delete(key) {
431
+ const fullKey = this.prefixKey(key);
432
+ await this.kv.del(fullKey);
433
+ }
434
+ /**
435
+ * Check if a key exists.
436
+ */
437
+ async exists(key) {
438
+ const fullKey = this.prefixKey(key);
439
+ return await this.kv.exists(fullKey) === 1;
440
+ }
441
+ /**
442
+ * List keys matching a pattern.
443
+ * Uses SCAN for efficient iteration.
444
+ */
445
+ async keys(pattern) {
446
+ const searchPattern = this.prefixKey(pattern ?? "*");
447
+ const result = [];
448
+ try {
449
+ let cursor = 0;
450
+ do {
451
+ const [nextCursor, keys] = await this.kv.scan(cursor, {
452
+ match: searchPattern,
453
+ count: 100
454
+ });
455
+ cursor = nextCursor;
456
+ for (const key of keys) {
457
+ result.push(key.slice(this.keyPrefix.length));
458
+ }
459
+ } while (cursor !== 0);
460
+ } catch {
461
+ try {
462
+ const keys = await this.kv.keys(searchPattern);
463
+ for (const key of keys) {
464
+ result.push(key.slice(this.keyPrefix.length));
465
+ }
466
+ } catch {
467
+ console.warn("[RememberPlugin:VercelKV] keys() operation not supported");
468
+ }
469
+ }
470
+ return result;
471
+ }
472
+ /**
473
+ * Gracefully close the provider.
474
+ * No-op for Vercel KV as it uses stateless REST API.
475
+ */
476
+ async close() {
477
+ }
478
+ };
479
+ RememberVercelKvProvider = __decorateClass([
480
+ (0, import_sdk3.Provider)({
481
+ name: "provider:remember:vercel-kv",
482
+ description: "Vercel KV-based storage provider for RememberPlugin",
483
+ scope: import_sdk3.ProviderScope.GLOBAL
484
+ })
485
+ ], RememberVercelKvProvider);
486
+
487
+ // plugins/plugin-remember/src/providers/remember-accessor.provider.ts
488
+ var import_sdk4 = require("@frontmcp/sdk");
489
+
490
+ // plugins/plugin-remember/src/remember.crypto.ts
491
+ var import_utils2 = require("@frontmcp/utils");
492
+
493
+ // plugins/plugin-remember/src/remember.secret-persistence.ts
494
+ var path = __toESM(require("path"));
495
+ var import_zod = require("zod");
496
+ var import_utils = require("@frontmcp/utils");
497
+ var DEFAULT_SECRET_FILE_PATH = ".frontmcp/remember-secret.json";
498
+ var SECRET_BYTES = 32;
499
+ var rememberSecretDataSchema = import_zod.z.object({
500
+ secret: import_zod.z.string().min(32),
501
+ // base64url of 32 bytes is ~43 chars
502
+ createdAt: import_zod.z.number().positive().int(),
503
+ version: import_zod.z.literal(1)
504
+ });
505
+ function validateSecretData(data) {
506
+ const result = rememberSecretDataSchema.safeParse(data);
507
+ if (!result.success) {
508
+ return { valid: false, error: result.error.issues[0]?.message ?? "Invalid secret structure" };
509
+ }
510
+ const parsed = result.data;
511
+ const now = Date.now();
512
+ if (parsed.createdAt > now + 6e4) {
513
+ return { valid: false, error: "createdAt is in the future" };
514
+ }
515
+ const hundredYearsMs = 100 * 365 * 24 * 60 * 60 * 1e3;
516
+ if (parsed.createdAt < now - hundredYearsMs) {
517
+ return { valid: false, error: "createdAt is too old" };
518
+ }
519
+ return { valid: true };
520
+ }
521
+ function isSecretPersistenceEnabled(options) {
522
+ const isProduction = process.env["NODE_ENV"] === "production";
523
+ if (isProduction) {
524
+ return options?.forceEnable === true;
525
+ }
526
+ return true;
527
+ }
528
+ function resolveSecretPath(options) {
529
+ const secretPath = options?.secretPath ?? DEFAULT_SECRET_FILE_PATH;
530
+ if (path.isAbsolute(secretPath)) {
531
+ return secretPath;
532
+ }
533
+ return path.resolve(process.cwd(), secretPath);
534
+ }
535
+ async function loadRememberSecret(options) {
536
+ if (!isSecretPersistenceEnabled(options)) {
537
+ return null;
538
+ }
539
+ const secretPath = resolveSecretPath(options);
540
+ try {
541
+ const content = await (0, import_utils.readFile)(secretPath, "utf8");
542
+ const data = JSON.parse(content);
543
+ const validation = validateSecretData(data);
544
+ if (!validation.valid) {
545
+ console.warn(
546
+ `[RememberSecretPersistence] Invalid secret file format at ${secretPath}: ${validation.error}, will regenerate`
547
+ );
548
+ return null;
549
+ }
550
+ return data;
551
+ } catch (error) {
552
+ if (error.code === "ENOENT") {
553
+ return null;
554
+ }
555
+ console.warn(`[RememberSecretPersistence] Failed to load secret from ${secretPath}: ${error.message}`);
556
+ return null;
557
+ }
558
+ }
559
+ async function saveRememberSecret(secretData, options) {
560
+ if (!isSecretPersistenceEnabled(options)) {
561
+ return true;
562
+ }
563
+ const secretPath = resolveSecretPath(options);
564
+ const dir = path.dirname(secretPath);
565
+ const tempPath = `${secretPath}.tmp.${Date.now()}.${(0, import_utils.base64urlEncode)((0, import_utils.randomBytes)(8))}`;
566
+ try {
567
+ await (0, import_utils.mkdir)(dir, { recursive: true, mode: 448 });
568
+ const content = JSON.stringify(secretData, null, 2);
569
+ await (0, import_utils.writeFile)(tempPath, content, { mode: 384 });
570
+ await (0, import_utils.rename)(tempPath, secretPath);
571
+ return true;
572
+ } catch (error) {
573
+ console.error(`[RememberSecretPersistence] Failed to save secret to ${secretPath}: ${error.message}`);
574
+ try {
575
+ await (0, import_utils.unlink)(tempPath);
576
+ } catch {
577
+ }
578
+ return false;
579
+ }
580
+ }
581
+ var cachedSecret = null;
582
+ var secretGenerationPromise = null;
583
+ function generateSecret() {
584
+ return (0, import_utils.base64urlEncode)((0, import_utils.randomBytes)(SECRET_BYTES));
585
+ }
586
+ async function getOrCreatePersistedSecret(options) {
587
+ if (cachedSecret) {
588
+ return cachedSecret;
589
+ }
590
+ if (secretGenerationPromise) {
591
+ return secretGenerationPromise;
592
+ }
593
+ secretGenerationPromise = (async () => {
594
+ try {
595
+ const loaded = await loadRememberSecret(options);
596
+ if (loaded) {
597
+ cachedSecret = loaded.secret;
598
+ return cachedSecret;
599
+ }
600
+ const newSecret = generateSecret();
601
+ const secretData = {
602
+ secret: newSecret,
603
+ createdAt: Date.now(),
604
+ version: 1
605
+ };
606
+ if (isSecretPersistenceEnabled(options)) {
607
+ await saveRememberSecret(secretData, options);
608
+ }
609
+ cachedSecret = newSecret;
610
+ return cachedSecret;
611
+ } finally {
612
+ secretGenerationPromise = null;
613
+ }
614
+ })();
615
+ return secretGenerationPromise;
616
+ }
617
+
618
+ // plugins/plugin-remember/src/remember.crypto.ts
619
+ async function getBaseSecret() {
620
+ const envSecret = process.env["REMEMBER_SECRET"] || process.env["MCP_MEMORY_SECRET"] || process.env["MCP_SESSION_SECRET"];
621
+ if (envSecret) {
622
+ return envSecret;
623
+ }
624
+ return getOrCreatePersistedSecret();
625
+ }
626
+ var textEncoder = new TextEncoder();
627
+ async function deriveEncryptionKey(source) {
628
+ const salt = textEncoder.encode("remember-plugin-v1");
629
+ let ikm;
630
+ let context;
631
+ switch (source.type) {
632
+ case "session":
633
+ ikm = source.sessionId;
634
+ context = `remember:session:${source.sessionId}`;
635
+ break;
636
+ case "tool":
637
+ ikm = source.sessionId;
638
+ context = `remember:tool:${source.toolName}:${source.sessionId}`;
639
+ break;
640
+ case "user":
641
+ ikm = await getBaseSecret() + source.userId;
642
+ context = `remember:user:${source.userId}`;
643
+ break;
644
+ case "global":
645
+ ikm = await getBaseSecret();
646
+ context = "remember:global";
647
+ break;
648
+ case "custom":
649
+ ikm = source.key;
650
+ context = `remember:custom:${source.key}`;
651
+ break;
652
+ }
653
+ const ikmBytes = textEncoder.encode(ikm);
654
+ const info = textEncoder.encode(context);
655
+ return (0, import_utils2.hkdfSha256)(ikmBytes, salt, info, 32);
656
+ }
657
+ function getKeySourceForScope(scope, context) {
658
+ switch (scope) {
659
+ case "session":
660
+ return { type: "session", sessionId: context.sessionId };
661
+ case "user":
662
+ return { type: "user", userId: context.userId ?? "anonymous" };
663
+ case "tool":
664
+ return {
665
+ type: "tool",
666
+ toolName: context.toolName ?? "unknown",
667
+ sessionId: context.sessionId
668
+ };
669
+ case "global":
670
+ return { type: "global" };
671
+ }
672
+ }
673
+ var textDecoder = new TextDecoder();
674
+ async function encryptValue(data, keySource) {
675
+ const key = await deriveEncryptionKey(keySource);
676
+ const plaintext = textEncoder.encode(JSON.stringify(data));
677
+ const iv = (0, import_utils2.randomBytes)(12);
678
+ const { ciphertext, tag } = (0, import_utils2.encryptAesGcm)(key, plaintext, iv);
679
+ return {
680
+ alg: "A256GCM",
681
+ iv: (0, import_utils2.base64urlEncode)(iv),
682
+ tag: (0, import_utils2.base64urlEncode)(tag),
683
+ data: (0, import_utils2.base64urlEncode)(ciphertext)
684
+ };
685
+ }
686
+ async function decryptValue(blob, keySource) {
687
+ try {
688
+ const key = await deriveEncryptionKey(keySource);
689
+ const iv = (0, import_utils2.base64urlDecode)(blob.iv);
690
+ const tag = (0, import_utils2.base64urlDecode)(blob.tag);
691
+ const ciphertext = (0, import_utils2.base64urlDecode)(blob.data);
692
+ const decrypted = (0, import_utils2.decryptAesGcm)(key, ciphertext, iv, tag);
693
+ return JSON.parse(textDecoder.decode(decrypted));
694
+ } catch {
695
+ return null;
696
+ }
697
+ }
698
+ function serializeBlob(blob) {
699
+ return JSON.stringify(blob);
700
+ }
701
+ function deserializeBlob(str) {
702
+ try {
703
+ const parsed = JSON.parse(str);
704
+ if (parsed && typeof parsed === "object" && parsed.alg === "A256GCM" && typeof parsed.iv === "string" && typeof parsed.tag === "string" && typeof parsed.data === "string") {
705
+ return parsed;
706
+ }
707
+ return null;
708
+ } catch {
709
+ return null;
710
+ }
711
+ }
712
+ async function encryptAndSerialize(data, keySource) {
713
+ const blob = await encryptValue(data, keySource);
714
+ return serializeBlob(blob);
715
+ }
716
+ async function deserializeAndDecrypt(str, keySource) {
717
+ const blob = deserializeBlob(str);
718
+ if (!blob) return null;
719
+ return decryptValue(blob, keySource);
720
+ }
721
+
722
+ // plugins/plugin-remember/src/providers/remember-accessor.provider.ts
723
+ var RememberAccessor = class {
724
+ store;
725
+ ctx;
726
+ config;
727
+ keyPrefix;
728
+ encryptionEnabled;
729
+ constructor(store, ctx, config) {
730
+ this.store = store;
731
+ this.ctx = ctx;
732
+ this.config = config;
733
+ this.keyPrefix = config.keyPrefix ?? "remember:";
734
+ this.encryptionEnabled = config.encryption?.enabled !== false;
735
+ }
736
+ // ─────────────────────────────────────────────────────────────────────────────
737
+ // Public API
738
+ // ─────────────────────────────────────────────────────────────────────────────
739
+ /**
740
+ * Store a value in memory.
741
+ *
742
+ * @param key - The key to store under
743
+ * @param value - The value to store (any JSON-serializable data)
744
+ * @param options - Storage options (scope, ttl, brand, metadata)
745
+ */
746
+ async set(key, value, options = {}) {
747
+ const scope = options.scope ?? "session";
748
+ const storageKey = this.buildStorageKey(key, scope);
749
+ const entry = {
750
+ value,
751
+ brand: options.brand,
752
+ createdAt: Date.now(),
753
+ updatedAt: Date.now(),
754
+ expiresAt: options.ttl ? Date.now() + options.ttl * 1e3 : void 0,
755
+ metadata: options.metadata
756
+ };
757
+ const serialized = this.encryptionEnabled ? await encryptAndSerialize(entry, this.getKeySource(scope)) : JSON.stringify(entry);
758
+ await this.store.setValue(storageKey, serialized, options.ttl);
759
+ }
760
+ /**
761
+ * Retrieve a value from memory.
762
+ *
763
+ * @param key - The key to retrieve
764
+ * @param options - Retrieval options (scope, defaultValue)
765
+ * @returns The stored value or defaultValue if not found
766
+ */
767
+ async get(key, options = {}) {
768
+ const scope = options.scope ?? "session";
769
+ const storageKey = this.buildStorageKey(key, scope);
770
+ const raw = await this.store.getValue(storageKey);
771
+ if (!raw) return options.defaultValue;
772
+ const entry = this.encryptionEnabled ? await deserializeAndDecrypt(raw, this.getKeySource(scope)) : this.parseEntry(raw);
773
+ if (!entry) return options.defaultValue;
774
+ if (entry.expiresAt && Date.now() > entry.expiresAt) {
775
+ await this.forget(key, { scope });
776
+ return options.defaultValue;
777
+ }
778
+ return entry.value;
779
+ }
780
+ /**
781
+ * Get the full entry including metadata.
782
+ *
783
+ * @param key - The key to retrieve
784
+ * @param options - Retrieval options (scope)
785
+ * @returns The full entry or undefined if not found
786
+ */
787
+ async getEntry(key, options = {}) {
788
+ const scope = options.scope ?? "session";
789
+ const storageKey = this.buildStorageKey(key, scope);
790
+ const raw = await this.store.getValue(storageKey);
791
+ if (!raw) return void 0;
792
+ const entry = this.encryptionEnabled ? await deserializeAndDecrypt(raw, this.getKeySource(scope)) : this.parseEntry(raw);
793
+ if (!entry) return void 0;
794
+ if (entry.expiresAt && Date.now() > entry.expiresAt) {
795
+ await this.forget(key, { scope });
796
+ return void 0;
797
+ }
798
+ return entry;
799
+ }
800
+ /**
801
+ * Forget (delete) a value from memory.
802
+ *
803
+ * @param key - The key to forget
804
+ * @param options - Options (scope)
805
+ */
806
+ async forget(key, options = {}) {
807
+ const scope = options.scope ?? "session";
808
+ const storageKey = this.buildStorageKey(key, scope);
809
+ await this.store.delete(storageKey);
810
+ }
811
+ /**
812
+ * Check if a key is remembered (exists and not expired).
813
+ *
814
+ * @param key - The key to check
815
+ * @param options - Options (scope)
816
+ * @returns true if the key exists
817
+ */
818
+ async knows(key, options = {}) {
819
+ const scope = options.scope ?? "session";
820
+ const storageKey = this.buildStorageKey(key, scope);
821
+ return this.store.exists(storageKey);
822
+ }
823
+ /**
824
+ * List all remembered keys for a scope.
825
+ *
826
+ * @param options - Options (scope, pattern)
827
+ * @returns Array of keys (without the scope prefix)
828
+ */
829
+ async list(options = {}) {
830
+ const scope = options.scope ?? "session";
831
+ const scopePrefix = this.buildScopePrefix(scope);
832
+ const fullPattern = scopePrefix + (options.pattern ?? "*");
833
+ const keys = await this.store.keys(fullPattern);
834
+ return keys.map((k) => k.slice(scopePrefix.length));
835
+ }
836
+ /**
837
+ * Update an existing entry's value while preserving metadata.
838
+ *
839
+ * @param key - The key to update
840
+ * @param value - The new value
841
+ * @param options - Options (scope, ttl)
842
+ */
843
+ async update(key, value, options = {}) {
844
+ const scope = options.scope ?? "session";
845
+ const existing = await this.getEntry(key, { scope });
846
+ if (!existing) return false;
847
+ const entry = {
848
+ ...existing,
849
+ value,
850
+ updatedAt: Date.now(),
851
+ expiresAt: options.ttl ? Date.now() + options.ttl * 1e3 : existing.expiresAt
852
+ };
853
+ const storageKey = this.buildStorageKey(key, scope);
854
+ const serialized = this.encryptionEnabled ? await encryptAndSerialize(entry, this.getKeySource(scope)) : JSON.stringify(entry);
855
+ await this.store.setValue(storageKey, serialized, options.ttl);
856
+ return true;
857
+ }
858
+ // ─────────────────────────────────────────────────────────────────────────────
859
+ // Convenience Methods
860
+ // ─────────────────────────────────────────────────────────────────────────────
861
+ /**
862
+ * Get the session ID from context.
863
+ */
864
+ get sessionId() {
865
+ return this.ctx.sessionId;
866
+ }
867
+ /**
868
+ * Get the user ID from context (if available).
869
+ */
870
+ get userId() {
871
+ const authInfo = this.ctx.authInfo;
872
+ return authInfo?.extra?.["userId"] ?? authInfo?.extra?.["sub"] ?? authInfo?.clientId ?? void 0;
873
+ }
874
+ // ─────────────────────────────────────────────────────────────────────────────
875
+ // Private Methods
876
+ // ─────────────────────────────────────────────────────────────────────────────
877
+ /**
878
+ * Build the full storage key including scope prefix.
879
+ */
880
+ buildStorageKey(key, scope) {
881
+ return this.buildScopePrefix(scope) + key;
882
+ }
883
+ /**
884
+ * Build the scope-specific prefix.
885
+ */
886
+ buildScopePrefix(scope) {
887
+ switch (scope) {
888
+ case "session":
889
+ return `${this.keyPrefix}session:${this.ctx.sessionId}:`;
890
+ case "user":
891
+ return `${this.keyPrefix}user:${this.userId ?? "anonymous"}:`;
892
+ case "tool": {
893
+ const toolName = this.ctx.flow?.name ?? "unknown";
894
+ return `${this.keyPrefix}tool:${toolName}:${this.ctx.sessionId}:`;
895
+ }
896
+ case "global":
897
+ return `${this.keyPrefix}global:`;
898
+ }
899
+ }
900
+ /**
901
+ * Get the encryption key source for a scope.
902
+ */
903
+ getKeySource(scope) {
904
+ return getKeySourceForScope(scope, {
905
+ sessionId: this.ctx.sessionId,
906
+ userId: this.userId,
907
+ toolName: this.ctx.flow?.name
908
+ });
909
+ }
910
+ /**
911
+ * Parse a JSON entry (when encryption is disabled).
912
+ */
913
+ parseEntry(raw) {
914
+ try {
915
+ return JSON.parse(raw);
916
+ } catch {
917
+ return null;
918
+ }
919
+ }
920
+ };
921
+ RememberAccessor = __decorateClass([
922
+ (0, import_sdk4.Provider)({
923
+ name: "provider:remember:accessor",
924
+ description: "Context-scoped accessor for RememberPlugin storage",
925
+ scope: import_sdk4.ProviderScope.CONTEXT
926
+ })
927
+ ], RememberAccessor);
928
+ function createRememberAccessor(store, ctx, config) {
929
+ return new RememberAccessor(store, ctx, config);
930
+ }
931
+
932
+ // plugins/plugin-remember/src/remember.plugin.ts
933
+ var RememberPlugin = class extends import_sdk5.DynamicPlugin {
934
+ options;
935
+ constructor(options = {}) {
936
+ super();
937
+ this.options = {
938
+ ...RememberPlugin.defaultOptions,
939
+ ...options
940
+ };
941
+ }
942
+ };
943
+ __publicField(RememberPlugin, "defaultOptions", {
944
+ type: "memory",
945
+ keyPrefix: "remember:",
946
+ encryption: { enabled: true }
947
+ });
948
+ /**
949
+ * Dynamic providers based on plugin options.
950
+ */
951
+ __publicField(RememberPlugin, "dynamicProviders", (options) => {
952
+ const providers = [];
953
+ const config = {
954
+ ...RememberPlugin.defaultOptions,
955
+ ...options
956
+ };
957
+ switch (config.type) {
958
+ case "global-store":
959
+ providers.push({
960
+ name: "remember:store:global",
961
+ provide: RememberStoreToken,
962
+ inject: () => [import_sdk5.FrontMcpConfig],
963
+ useFactory: (frontmcpConfig) => {
964
+ const storeConfig = (0, import_sdk5.getGlobalStoreConfig)("RememberPlugin", frontmcpConfig);
965
+ if ((0, import_sdk5.isVercelKvProvider)(storeConfig)) {
966
+ return new RememberVercelKvProvider({
967
+ url: storeConfig.url,
968
+ token: storeConfig.token,
969
+ keyPrefix: config.keyPrefix,
970
+ defaultTTL: config.defaultTTL
971
+ });
972
+ }
973
+ return new RememberRedisProvider({
974
+ type: "redis",
975
+ config: {
976
+ host: storeConfig.host ?? "localhost",
977
+ port: storeConfig.port ?? 6379,
978
+ password: storeConfig.password,
979
+ db: storeConfig.db
980
+ },
981
+ keyPrefix: config.keyPrefix,
982
+ defaultTTL: config.defaultTTL
983
+ });
984
+ }
985
+ });
986
+ break;
987
+ case "redis":
988
+ if ("config" in config && config.config) {
989
+ providers.push({
990
+ name: "remember:store:redis",
991
+ provide: RememberStoreToken,
992
+ useValue: new RememberRedisProvider({
993
+ type: "redis",
994
+ config: config.config,
995
+ keyPrefix: config.keyPrefix,
996
+ defaultTTL: config.defaultTTL
997
+ })
998
+ });
999
+ }
1000
+ break;
1001
+ case "redis-client":
1002
+ if ("client" in config && config.client) {
1003
+ providers.push({
1004
+ name: "remember:store:redis-client",
1005
+ provide: RememberStoreToken,
1006
+ useValue: new RememberRedisProvider({
1007
+ type: "redis-client",
1008
+ client: config.client,
1009
+ keyPrefix: config.keyPrefix,
1010
+ defaultTTL: config.defaultTTL
1011
+ })
1012
+ });
1013
+ }
1014
+ break;
1015
+ case "vercel-kv":
1016
+ providers.push({
1017
+ name: "remember:store:vercel-kv",
1018
+ provide: RememberStoreToken,
1019
+ useValue: new RememberVercelKvProvider({
1020
+ url: "url" in config ? config.url : void 0,
1021
+ token: "token" in config ? config.token : void 0,
1022
+ keyPrefix: config.keyPrefix,
1023
+ defaultTTL: config.defaultTTL
1024
+ })
1025
+ });
1026
+ break;
1027
+ case "memory":
1028
+ default:
1029
+ providers.push({
1030
+ name: "remember:store:memory",
1031
+ provide: RememberStoreToken,
1032
+ useValue: new RememberMemoryProvider()
1033
+ });
1034
+ break;
1035
+ }
1036
+ providers.push({
1037
+ name: "remember:config",
1038
+ provide: RememberConfigToken,
1039
+ useValue: config
1040
+ });
1041
+ providers.push({
1042
+ name: "remember:accessor",
1043
+ provide: RememberAccessorToken,
1044
+ scope: import_sdk5.ProviderScope.CONTEXT,
1045
+ inject: () => [RememberStoreToken, import_sdk5.FRONTMCP_CONTEXT, RememberConfigToken],
1046
+ useFactory: (store, ctx, cfg) => createRememberAccessor(store, ctx, cfg)
1047
+ });
1048
+ return providers;
1049
+ });
1050
+ RememberPlugin = __decorateClass([
1051
+ (0, import_sdk5.Plugin)({
1052
+ name: "remember",
1053
+ description: "Help your LLM remember things across sessions",
1054
+ providers: [
1055
+ // Default in-memory provider (overridden by dynamicProviders if configured)
1056
+ {
1057
+ name: "remember:store:memory",
1058
+ provide: RememberStoreToken,
1059
+ useValue: new RememberMemoryProvider()
1060
+ }
1061
+ ],
1062
+ // Context extensions - SDK handles runtime installation
1063
+ // TypeScript types are declared in remember.context-extension.ts
1064
+ contextExtensions: [
1065
+ {
1066
+ property: "remember",
1067
+ token: RememberAccessorToken,
1068
+ errorMessage: "RememberPlugin is not installed. Add RememberPlugin.init() to your plugins array."
1069
+ }
1070
+ ]
1071
+ })
1072
+ ], RememberPlugin);
1073
+
1074
+ // plugins/plugin-remember/src/remember.types.ts
1075
+ var import_zod2 = require("zod");
1076
+ var rememberScopeSchema = import_zod2.z.enum(["session", "user", "tool", "global"]);
1077
+ function brand(data, _brand) {
1078
+ return data;
1079
+ }
1080
+ var rememberEntrySchema = import_zod2.z.object({
1081
+ value: import_zod2.z.unknown(),
1082
+ brand: import_zod2.z.enum(["approval", "preference", "cache", "state", "conversation", "custom"]).optional(),
1083
+ createdAt: import_zod2.z.number(),
1084
+ updatedAt: import_zod2.z.number(),
1085
+ expiresAt: import_zod2.z.number().optional(),
1086
+ metadata: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).optional()
1087
+ });
1088
+
1089
+ // plugins/plugin-remember/src/tools/remember-this.tool.ts
1090
+ var import_sdk6 = require("@frontmcp/sdk");
1091
+ var import_zod3 = require("zod");
1092
+ var rememberThisInputSchema = import_zod3.z.object({
1093
+ key: import_zod3.z.string().min(1).describe('What to call this memory (e.g., "user_preference", "last_action")'),
1094
+ value: import_zod3.z.unknown().describe("The value to remember (any JSON-serializable data)"),
1095
+ scope: import_zod3.z.enum(["session", "user", "tool", "global"]).optional().describe(
1096
+ "How long to remember: session (until disconnect), user (forever), tool (for this tool), global (shared)"
1097
+ ),
1098
+ ttl: import_zod3.z.number().positive().optional().describe("Forget after this many seconds"),
1099
+ brand: import_zod3.z.enum(["preference", "cache", "state", "conversation", "custom"]).optional().describe("Type hint for the stored value")
1100
+ });
1101
+ var rememberThisOutputSchema = import_zod3.z.object({
1102
+ success: import_zod3.z.boolean(),
1103
+ key: import_zod3.z.string(),
1104
+ scope: import_zod3.z.string(),
1105
+ expiresAt: import_zod3.z.number().optional()
1106
+ });
1107
+ var RememberThisTool = class extends import_sdk6.ToolContext {
1108
+ async execute(input) {
1109
+ const remember = this.get(RememberAccessorToken);
1110
+ const config = this.get(RememberConfigToken);
1111
+ const scope = input.scope ?? "session";
1112
+ const allowedScopes = config.tools?.allowedScopes ?? ["session", "user", "tool", "global"];
1113
+ if (!allowedScopes.includes(scope)) {
1114
+ throw this.fail(new Error(`Scope '${scope}' is not allowed. Allowed scopes: ${allowedScopes.join(", ")}`));
1115
+ }
1116
+ await remember.set(input.key, input.value, {
1117
+ scope,
1118
+ ttl: input.ttl,
1119
+ brand: input.brand
1120
+ });
1121
+ const expiresAt = input.ttl ? Date.now() + input.ttl * 1e3 : void 0;
1122
+ return {
1123
+ success: true,
1124
+ key: input.key,
1125
+ scope,
1126
+ expiresAt
1127
+ };
1128
+ }
1129
+ };
1130
+ RememberThisTool = __decorateClass([
1131
+ (0, import_sdk6.Tool)({
1132
+ name: "remember_this",
1133
+ description: "Remember something for later. Store a value that can be recalled in future conversations. Use this when the user asks you to remember preferences, settings, or any information they want to persist.",
1134
+ inputSchema: rememberThisInputSchema,
1135
+ outputSchema: rememberThisOutputSchema,
1136
+ annotations: {
1137
+ readOnlyHint: false
1138
+ }
1139
+ })
1140
+ ], RememberThisTool);
1141
+
1142
+ // plugins/plugin-remember/src/tools/recall.tool.ts
1143
+ var import_sdk7 = require("@frontmcp/sdk");
1144
+ var import_zod4 = require("zod");
1145
+ var recallInputSchema = {
1146
+ key: import_zod4.z.string().min(1).describe("What memory to recall"),
1147
+ scope: import_zod4.z.enum(["session", "user", "tool", "global"]).optional().describe("Which scope to look in (default: session)")
1148
+ };
1149
+ var recallOutputSchema = import_zod4.z.object({
1150
+ found: import_zod4.z.boolean(),
1151
+ key: import_zod4.z.string(),
1152
+ value: import_zod4.z.unknown().optional(),
1153
+ scope: import_zod4.z.string(),
1154
+ createdAt: import_zod4.z.number().optional(),
1155
+ expiresAt: import_zod4.z.number().optional()
1156
+ });
1157
+ var RecallTool = class extends import_sdk7.ToolContext {
1158
+ async execute(input) {
1159
+ const remember = this.get(RememberAccessorToken);
1160
+ const config = this.get(RememberConfigToken);
1161
+ const scope = input.scope ?? "session";
1162
+ const allowedScopes = config.tools?.allowedScopes ?? ["session", "user", "tool", "global"];
1163
+ if (!allowedScopes.includes(scope)) {
1164
+ this.fail(new Error(`Scope '${scope}' is not allowed. Allowed scopes: ${allowedScopes.join(", ")}`));
1165
+ }
1166
+ const entry = await remember.getEntry(input.key, { scope });
1167
+ if (!entry) {
1168
+ return {
1169
+ found: false,
1170
+ key: input.key,
1171
+ scope
1172
+ };
1173
+ }
1174
+ return {
1175
+ found: true,
1176
+ key: input.key,
1177
+ value: entry.value,
1178
+ scope,
1179
+ createdAt: entry.createdAt,
1180
+ expiresAt: entry.expiresAt
1181
+ };
1182
+ }
1183
+ };
1184
+ RecallTool = __decorateClass([
1185
+ (0, import_sdk7.Tool)({
1186
+ name: "recall",
1187
+ description: "Recall something that was previously remembered. Use this to retrieve stored preferences, settings, or any information that was saved with remember_this.",
1188
+ inputSchema: recallInputSchema,
1189
+ outputSchema: recallOutputSchema,
1190
+ annotations: {
1191
+ readOnlyHint: true
1192
+ }
1193
+ })
1194
+ ], RecallTool);
1195
+
1196
+ // plugins/plugin-remember/src/tools/forget.tool.ts
1197
+ var import_sdk8 = require("@frontmcp/sdk");
1198
+ var import_zod5 = require("zod");
1199
+ var forgetInputSchema = import_zod5.z.object({
1200
+ key: import_zod5.z.string().min(1).describe("What memory to forget"),
1201
+ scope: import_zod5.z.enum(["session", "user", "tool", "global"]).optional().describe("Which scope to forget from (default: session)")
1202
+ });
1203
+ var forgetOutputSchema = import_zod5.z.object({
1204
+ success: import_zod5.z.boolean(),
1205
+ key: import_zod5.z.string(),
1206
+ scope: import_zod5.z.string(),
1207
+ existed: import_zod5.z.boolean()
1208
+ });
1209
+ var ForgetTool = class extends import_sdk8.ToolContext {
1210
+ async execute(input) {
1211
+ const remember = this.get(RememberAccessorToken);
1212
+ const config = this.get(RememberConfigToken);
1213
+ const scope = input.scope ?? "session";
1214
+ const allowedScopes = config.tools?.allowedScopes ?? ["session", "user", "tool", "global"];
1215
+ if (!allowedScopes.includes(scope)) {
1216
+ throw this.fail(new Error(`Scope '${scope}' is not allowed. Allowed scopes: ${allowedScopes.join(", ")}`));
1217
+ }
1218
+ const existed = await remember.knows(input.key, { scope });
1219
+ await remember.forget(input.key, { scope });
1220
+ return {
1221
+ success: true,
1222
+ key: input.key,
1223
+ scope,
1224
+ existed
1225
+ };
1226
+ }
1227
+ };
1228
+ ForgetTool = __decorateClass([
1229
+ (0, import_sdk8.Tool)({
1230
+ name: "forget",
1231
+ description: "Forget a previously remembered value. Use this when the user wants to delete stored preferences or information.",
1232
+ inputSchema: forgetInputSchema,
1233
+ outputSchema: forgetOutputSchema,
1234
+ annotations: {
1235
+ readOnlyHint: false
1236
+ }
1237
+ })
1238
+ ], ForgetTool);
1239
+
1240
+ // plugins/plugin-remember/src/tools/list-memories.tool.ts
1241
+ var import_sdk9 = require("@frontmcp/sdk");
1242
+ var import_zod6 = require("zod");
1243
+ var listMemoriesInputSchema = import_zod6.z.object({
1244
+ scope: import_zod6.z.enum(["session", "user", "tool", "global"]).optional().describe("Which scope to list from (default: session)"),
1245
+ pattern: import_zod6.z.string().optional().describe('Pattern to filter keys (e.g., "user_*")'),
1246
+ limit: import_zod6.z.number().positive().max(100).optional().describe("Maximum number of keys to return (default: 50)")
1247
+ });
1248
+ var listMemoriesOutputSchema = import_zod6.z.object({
1249
+ keys: import_zod6.z.array(import_zod6.z.string()),
1250
+ scope: import_zod6.z.string(),
1251
+ count: import_zod6.z.number(),
1252
+ truncated: import_zod6.z.boolean()
1253
+ });
1254
+ var ListMemoriesTool = class extends import_sdk9.ToolContext {
1255
+ async execute(input) {
1256
+ const remember = this.get(RememberAccessorToken);
1257
+ const config = this.get(RememberConfigToken);
1258
+ const scope = input.scope ?? "session";
1259
+ const allowedScopes = config.tools?.allowedScopes ?? ["session", "user", "tool", "global"];
1260
+ if (!allowedScopes.includes(scope)) {
1261
+ throw this.fail(new Error(`Scope '${scope}' is not allowed. Allowed scopes: ${allowedScopes.join(", ")}`));
1262
+ }
1263
+ const limit = input.limit ?? 50;
1264
+ let keys = await remember.list({ scope, pattern: input.pattern });
1265
+ const truncated = keys.length > limit;
1266
+ if (truncated) {
1267
+ keys = keys.slice(0, limit);
1268
+ }
1269
+ return {
1270
+ keys,
1271
+ scope,
1272
+ count: keys.length,
1273
+ truncated
1274
+ };
1275
+ }
1276
+ };
1277
+ ListMemoriesTool = __decorateClass([
1278
+ (0, import_sdk9.Tool)({
1279
+ name: "list_memories",
1280
+ description: "List all remembered keys in a scope. Use this to see what memories are stored for the current session or user.",
1281
+ inputSchema: listMemoriesInputSchema,
1282
+ outputSchema: listMemoriesOutputSchema,
1283
+ annotations: {
1284
+ readOnlyHint: true
1285
+ }
1286
+ })
1287
+ ], ListMemoriesTool);
1288
+
1289
+ // plugins/plugin-remember/src/remember.context-extension.ts
1290
+ function getRemember(ctx) {
1291
+ return ctx.get(RememberAccessorToken);
1292
+ }
1293
+ function tryGetRemember(ctx) {
1294
+ if (typeof ctx.tryGet === "function") {
1295
+ return ctx.tryGet(RememberAccessorToken);
1296
+ }
1297
+ return void 0;
1298
+ }
1299
+ // Annotate the CommonJS export names for ESM import in node:
1300
+ 0 && (module.exports = {
1301
+ ForgetTool,
1302
+ ListMemoriesTool,
1303
+ RecallTool,
1304
+ RememberAccessor,
1305
+ RememberAccessorToken,
1306
+ RememberConfigToken,
1307
+ RememberMemoryProvider,
1308
+ RememberPlugin,
1309
+ RememberRedisProvider,
1310
+ RememberStoreToken,
1311
+ RememberThisTool,
1312
+ RememberVercelKvProvider,
1313
+ brand,
1314
+ getRemember,
1315
+ tryGetRemember
1316
+ });