@prmichaelsen/remember-mcp 0.2.5 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -1336,22 +1336,125 @@ async function handleCreateMemory(args, userId, context) {
1336
1336
  }
1337
1337
  }
1338
1338
 
1339
+ // src/utils/weaviate-filters.ts
1340
+ function buildCombinedSearchFilters(collection, filters) {
1341
+ const memoryFilters = buildDocTypeFilters(collection, "memory", filters);
1342
+ const relationshipFilters = buildDocTypeFilters(collection, "relationship", filters);
1343
+ if (memoryFilters && relationshipFilters) {
1344
+ return combineFiltersWithOr([memoryFilters, relationshipFilters]);
1345
+ } else if (memoryFilters) {
1346
+ return memoryFilters;
1347
+ } else if (relationshipFilters) {
1348
+ return relationshipFilters;
1349
+ }
1350
+ return void 0;
1351
+ }
1352
+ function buildDocTypeFilters(collection, docType, filters) {
1353
+ const filterList = [];
1354
+ filterList.push(
1355
+ collection.filter.byProperty("doc_type").equal(docType)
1356
+ );
1357
+ if (docType === "memory" && filters?.types && filters.types.length > 0) {
1358
+ if (filters.types.length === 1) {
1359
+ filterList.push(
1360
+ collection.filter.byProperty("type").equal(filters.types[0])
1361
+ );
1362
+ } else {
1363
+ filterList.push(
1364
+ collection.filter.byProperty("type").containsAny(filters.types)
1365
+ );
1366
+ }
1367
+ }
1368
+ if (filters?.weight_min !== void 0) {
1369
+ filterList.push(
1370
+ collection.filter.byProperty("weight").greaterThanOrEqual(filters.weight_min)
1371
+ );
1372
+ }
1373
+ if (filters?.weight_max !== void 0) {
1374
+ filterList.push(
1375
+ collection.filter.byProperty("weight").lessThanOrEqual(filters.weight_max)
1376
+ );
1377
+ }
1378
+ if (filters?.trust_min !== void 0) {
1379
+ filterList.push(
1380
+ collection.filter.byProperty("trust").greaterThanOrEqual(filters.trust_min)
1381
+ );
1382
+ }
1383
+ if (filters?.trust_max !== void 0) {
1384
+ filterList.push(
1385
+ collection.filter.byProperty("trust").lessThanOrEqual(filters.trust_max)
1386
+ );
1387
+ }
1388
+ if (filters?.date_from) {
1389
+ filterList.push(
1390
+ collection.filter.byProperty("created_at").greaterThanOrEqual(new Date(filters.date_from))
1391
+ );
1392
+ }
1393
+ if (filters?.date_to) {
1394
+ filterList.push(
1395
+ collection.filter.byProperty("created_at").lessThanOrEqual(new Date(filters.date_to))
1396
+ );
1397
+ }
1398
+ if (filters?.tags && filters.tags.length > 0) {
1399
+ if (filters.tags.length === 1) {
1400
+ filterList.push(
1401
+ collection.filter.byProperty("tags").containsAny([filters.tags[0]])
1402
+ );
1403
+ } else {
1404
+ filterList.push(
1405
+ collection.filter.byProperty("tags").containsAny(filters.tags)
1406
+ );
1407
+ }
1408
+ }
1409
+ return combineFiltersWithAnd(filterList);
1410
+ }
1411
+ function buildMemoryOnlyFilters(collection, filters) {
1412
+ return buildDocTypeFilters(collection, "memory", filters);
1413
+ }
1414
+ function combineFiltersWithAnd(filters) {
1415
+ if (filters.length === 0) {
1416
+ return void 0;
1417
+ }
1418
+ if (filters.length === 1) {
1419
+ return filters[0];
1420
+ }
1421
+ return {
1422
+ operator: "And",
1423
+ operands: filters
1424
+ };
1425
+ }
1426
+ function combineFiltersWithOr(filters) {
1427
+ if (filters.length === 0) {
1428
+ return void 0;
1429
+ }
1430
+ if (filters.length === 1) {
1431
+ return filters[0];
1432
+ }
1433
+ return {
1434
+ operator: "Or",
1435
+ operands: filters
1436
+ };
1437
+ }
1438
+
1339
1439
  // src/tools/search-memory.ts
1340
1440
  var searchMemoryTool = {
1341
1441
  name: "remember_search_memory",
1342
- description: `Search memories using hybrid semantic and keyword search.
1442
+ description: `Search memories AND relationships using hybrid semantic and keyword search.
1443
+
1444
+ By default, searches BOTH memories and relationships to provide comprehensive results.
1445
+ Relationships contain valuable context in their observations.
1343
1446
 
1344
1447
  Supports:
1345
- - Semantic search (meaning-based)
1448
+ - Semantic search (meaning-based) across memory content and relationship observations
1346
1449
  - Keyword search (exact matches)
1347
1450
  - Hybrid search (balanced with alpha parameter)
1348
1451
  - Filtering by type, tags, weight, trust, date range
1349
- - Location-based search
1452
+ - Returns both memories and relationships in separate arrays
1350
1453
 
1351
1454
  Examples:
1352
- - "Find memories about camping trips"
1353
- - "Search for recipes I saved"
1354
- - "Show me notes from last week"
1455
+ - "Find memories about camping trips" \u2192 returns memories + relationships about camping
1456
+ - "Search for recipes I saved" \u2192 returns recipe memories + related relationships
1457
+ - "Show me notes from last week" \u2192 returns notes + any relationships created that week
1355
1458
  `,
1356
1459
  inputSchema: {
1357
1460
  type: "object",
@@ -1414,8 +1517,8 @@ var searchMemoryTool = {
1414
1517
  },
1415
1518
  include_relationships: {
1416
1519
  type: "boolean",
1417
- description: "Include relationships in results. Default: false",
1418
- default: false
1520
+ description: "Include relationships in results. Default: true (searches both memories and relationships)",
1521
+ default: true
1419
1522
  }
1420
1523
  },
1421
1524
  required: ["query"]
@@ -1423,83 +1526,53 @@ var searchMemoryTool = {
1423
1526
  };
1424
1527
  async function handleSearchMemory(args, userId) {
1425
1528
  try {
1426
- logger.info("Searching memories", { userId, query: args.query });
1529
+ const includeRelationships = args.include_relationships !== false;
1530
+ logger.info("Searching memories and relationships", {
1531
+ userId,
1532
+ query: args.query,
1533
+ includeRelationships
1534
+ });
1427
1535
  const collection = getMemoryCollection(userId);
1428
1536
  const alpha = args.alpha ?? 0.7;
1429
1537
  const limit = args.limit ?? 10;
1430
1538
  const offset = args.offset ?? 0;
1431
- const whereFilters = [
1432
- {
1433
- path: "doc_type",
1434
- operator: "Equal",
1435
- valueText: "memory"
1436
- }
1437
- ];
1438
- if (args.filters?.types && args.filters.types.length > 0) {
1439
- whereFilters.push({
1440
- path: "type",
1441
- operator: "ContainsAny",
1442
- valueTextArray: args.filters.types
1443
- });
1444
- }
1445
- if (args.filters?.weight_min !== void 0) {
1446
- whereFilters.push({
1447
- path: "weight",
1448
- operator: "GreaterThanEqual",
1449
- valueNumber: args.filters.weight_min
1450
- });
1451
- }
1452
- if (args.filters?.trust_min !== void 0) {
1453
- whereFilters.push({
1454
- path: "trust",
1455
- operator: "GreaterThanEqual",
1456
- valueNumber: args.filters.trust_min
1457
- });
1458
- }
1459
- if (args.filters?.date_from) {
1460
- whereFilters.push({
1461
- path: "created_at",
1462
- operator: "GreaterThanEqual",
1463
- valueDate: new Date(args.filters.date_from)
1464
- });
1465
- }
1466
- if (args.filters?.date_to) {
1467
- whereFilters.push({
1468
- path: "created_at",
1469
- operator: "LessThanEqual",
1470
- valueDate: new Date(args.filters.date_to)
1471
- });
1472
- }
1539
+ const filters = includeRelationships ? buildCombinedSearchFilters(collection, args.filters) : buildMemoryOnlyFilters(collection, args.filters);
1473
1540
  const searchOptions = {
1474
1541
  alpha,
1475
1542
  limit: limit + offset
1476
1543
  // Get extra for offset
1477
1544
  };
1478
- if (whereFilters.length > 0) {
1479
- searchOptions.filters = whereFilters.length > 1 ? {
1480
- operator: "And",
1481
- operands: whereFilters
1482
- } : whereFilters[0];
1545
+ if (filters) {
1546
+ searchOptions.filters = filters;
1483
1547
  }
1484
1548
  const results = await collection.query.hybrid(args.query, searchOptions);
1485
1549
  const paginatedResults = results.objects.slice(offset);
1486
- const memories = paginatedResults.map((obj) => ({
1487
- id: obj.uuid,
1488
- ...obj.properties
1489
- }));
1550
+ const memories = [];
1551
+ const relationships = [];
1552
+ for (const obj of paginatedResults) {
1553
+ const doc = {
1554
+ id: obj.uuid,
1555
+ ...obj.properties
1556
+ };
1557
+ if (doc.doc_type === "memory") {
1558
+ memories.push(doc);
1559
+ } else if (doc.doc_type === "relationship") {
1560
+ relationships.push(doc);
1561
+ }
1562
+ }
1490
1563
  const searchResult = {
1491
1564
  memories,
1492
- total: memories.length,
1565
+ relationships: includeRelationships ? relationships : void 0,
1566
+ total: memories.length + relationships.length,
1493
1567
  offset,
1494
1568
  limit
1495
1569
  };
1496
- if (args.include_relationships) {
1497
- searchResult.relationships = [];
1498
- }
1499
1570
  logger.info("Search completed", {
1500
1571
  userId,
1501
1572
  query: args.query,
1502
- results: memories.length
1573
+ memoriesFound: memories.length,
1574
+ relationshipsFound: relationships.length,
1575
+ total: searchResult.total
1503
1576
  });
1504
1577
  return JSON.stringify(searchResult, null, 2);
1505
1578
  } catch (error) {
@@ -1959,59 +2032,15 @@ async function handleQueryMemory(args, userId) {
1959
2032
  const minRelevance = args.min_relevance ?? 0.6;
1960
2033
  const includeContext = args.include_context ?? true;
1961
2034
  const format = args.format ?? "detailed";
1962
- const whereFilters = [
1963
- {
1964
- path: "doc_type",
1965
- operator: "Equal",
1966
- valueText: "memory"
1967
- }
1968
- ];
1969
- if (args.filters?.types && args.filters.types.length > 0) {
1970
- whereFilters.push({
1971
- path: "type",
1972
- operator: "ContainsAny",
1973
- valueTextArray: args.filters.types
1974
- });
1975
- }
1976
- if (args.filters?.weight_min !== void 0) {
1977
- whereFilters.push({
1978
- path: "weight",
1979
- operator: "GreaterThanEqual",
1980
- valueNumber: args.filters.weight_min
1981
- });
1982
- }
1983
- if (args.filters?.trust_min !== void 0) {
1984
- whereFilters.push({
1985
- path: "trust",
1986
- operator: "GreaterThanEqual",
1987
- valueNumber: args.filters.trust_min
1988
- });
1989
- }
1990
- if (args.filters?.date_from) {
1991
- whereFilters.push({
1992
- path: "created_at",
1993
- operator: "GreaterThanEqual",
1994
- valueDate: new Date(args.filters.date_from)
1995
- });
1996
- }
1997
- if (args.filters?.date_to) {
1998
- whereFilters.push({
1999
- path: "created_at",
2000
- operator: "LessThanEqual",
2001
- valueDate: new Date(args.filters.date_to)
2002
- });
2003
- }
2035
+ const filters = buildCombinedSearchFilters(collection, args.filters);
2004
2036
  const searchOptions = {
2005
2037
  limit,
2006
2038
  distance: 1 - minRelevance,
2007
2039
  // Convert relevance to distance
2008
2040
  returnMetadata: ["distance"]
2009
2041
  };
2010
- if (whereFilters.length > 0) {
2011
- searchOptions.filters = whereFilters.length > 1 ? {
2012
- operator: "And",
2013
- operands: whereFilters
2014
- } : whereFilters[0];
2042
+ if (filters) {
2043
+ searchOptions.filters = filters;
2015
2044
  }
2016
2045
  const results = await collection.query.nearText(args.query, searchOptions);
2017
2046
  const relevantMemories = results.objects.map((obj) => {
@@ -2627,6 +2656,434 @@ async function handleDeleteRelationship(args, userId) {
2627
2656
  }
2628
2657
  }
2629
2658
 
2659
+ // src/firestore/paths.ts
2660
+ var APP_NAME = "remember-mcp";
2661
+ function getBasePrefix() {
2662
+ const environment = process.env.ENVIRONMENT;
2663
+ if (environment && environment !== "production" && environment !== "prod") {
2664
+ return `${environment}.${APP_NAME}`;
2665
+ }
2666
+ const isDevelopment = process.env.NODE_ENV === "development";
2667
+ if (isDevelopment) {
2668
+ const customPrefix = process.env.DB_PREFIX;
2669
+ if (customPrefix) {
2670
+ return customPrefix;
2671
+ }
2672
+ return `e0.${APP_NAME}`;
2673
+ }
2674
+ return APP_NAME;
2675
+ }
2676
+ var BASE = getBasePrefix();
2677
+ function getUserPreferencesPath(userId) {
2678
+ return `${BASE}.users/${userId}/preferences`;
2679
+ }
2680
+
2681
+ // src/types/preferences.ts
2682
+ var DEFAULT_PREFERENCES = {
2683
+ templates: {
2684
+ auto_suggest: true,
2685
+ suggestion_threshold: 0.6,
2686
+ max_suggestions: 3,
2687
+ show_preview: true,
2688
+ remember_choice: true,
2689
+ suppressed_categories: [],
2690
+ suppressed_templates: [],
2691
+ always_suggest: []
2692
+ },
2693
+ search: {
2694
+ default_limit: 10,
2695
+ include_low_trust: false,
2696
+ weight_by_access: true,
2697
+ weight_by_recency: true,
2698
+ default_alpha: 0.7
2699
+ },
2700
+ location: {
2701
+ auto_capture: true,
2702
+ precision: "approximate",
2703
+ share_with_memories: true
2704
+ },
2705
+ privacy: {
2706
+ default_trust_level: 0.5,
2707
+ allow_cross_user_access: false,
2708
+ auto_approve_requests: false,
2709
+ audit_logging: true
2710
+ },
2711
+ notifications: {
2712
+ trust_violations: true,
2713
+ access_requests: true,
2714
+ memory_reminders: false,
2715
+ relationship_suggestions: true
2716
+ },
2717
+ display: {
2718
+ date_format: "MM/DD/YYYY",
2719
+ time_format: "12h",
2720
+ timezone: "America/Los_Angeles",
2721
+ language: "en"
2722
+ }
2723
+ };
2724
+ var PREFERENCE_CATEGORIES = [
2725
+ "templates",
2726
+ "search",
2727
+ "location",
2728
+ "privacy",
2729
+ "notifications",
2730
+ "display"
2731
+ ];
2732
+ var PREFERENCE_DESCRIPTIONS = {
2733
+ templates: {
2734
+ auto_suggest: "Automatically suggest templates when creating memories (default: true)",
2735
+ suggestion_threshold: "Minimum confidence to show template suggestion, 0-1 (default: 0.6)",
2736
+ max_suggestions: "Maximum number of templates to suggest, 1-5 (default: 3)",
2737
+ show_preview: "Show template preview in suggestion (default: true)",
2738
+ remember_choice: `Remember "don't suggest for this type" choices (default: true)`,
2739
+ suppressed_categories: "Categories to never suggest templates for (default: [])",
2740
+ suppressed_templates: "Specific templates to never suggest (default: [])",
2741
+ always_suggest: "Templates to always suggest regardless of confidence (default: [])"
2742
+ },
2743
+ search: {
2744
+ default_limit: "Default number of search results to return, 1-100 (default: 10)",
2745
+ include_low_trust: "Include low-trust memories in search results (default: false)",
2746
+ weight_by_access: "Use access count in search ranking (default: true)",
2747
+ weight_by_recency: "Use recency in search ranking (default: true)",
2748
+ default_alpha: "Default hybrid search alpha (0=keyword, 1=semantic, default: 0.7)"
2749
+ },
2750
+ location: {
2751
+ auto_capture: "Automatically capture location for memories (default: true)",
2752
+ precision: "Location precision level: exact, approximate, city, none (default: approximate)",
2753
+ share_with_memories: "Include location data in memories (default: true)"
2754
+ },
2755
+ privacy: {
2756
+ default_trust_level: "Default trust level for new memories, 0-1 (default: 0.5)",
2757
+ allow_cross_user_access: "Allow other users to request access to memories (default: false)",
2758
+ auto_approve_requests: "Automatically approve access requests (default: false)",
2759
+ audit_logging: "Enable audit logging for preference changes (default: true)"
2760
+ },
2761
+ notifications: {
2762
+ trust_violations: "Notify on trust violations (default: true)",
2763
+ access_requests: "Notify on access requests from other users (default: true)",
2764
+ memory_reminders: "Send reminders about important memories (default: false)",
2765
+ relationship_suggestions: "Suggest new relationships between memories (default: true)"
2766
+ },
2767
+ display: {
2768
+ date_format: "Date format string (default: MM/DD/YYYY)",
2769
+ time_format: "Time format: 12h or 24h (default: 12h)",
2770
+ timezone: "Timezone identifier (default: America/Los_Angeles)",
2771
+ language: "Language code (default: en)"
2772
+ }
2773
+ };
2774
+ function getPreferenceDescription() {
2775
+ const categories = Object.entries(PREFERENCE_DESCRIPTIONS).map(([category, fields]) => {
2776
+ const fieldList = Object.entries(fields).map(([field, desc]) => ` - ${field}: ${desc}`).join("\n");
2777
+ return `${category}:
2778
+ ${fieldList}`;
2779
+ }).join("\n\n");
2780
+ return `User preferences control system behavior. Available preference categories and fields:
2781
+
2782
+ ${categories}`;
2783
+ }
2784
+ function getPreferencesSchema() {
2785
+ return {
2786
+ type: "object",
2787
+ properties: {
2788
+ templates: {
2789
+ type: "object",
2790
+ description: "Template suggestion preferences",
2791
+ properties: {
2792
+ auto_suggest: { type: "boolean" },
2793
+ suggestion_threshold: { type: "number", minimum: 0, maximum: 1 },
2794
+ max_suggestions: { type: "number", minimum: 1, maximum: 5 },
2795
+ show_preview: { type: "boolean" },
2796
+ remember_choice: { type: "boolean" },
2797
+ suppressed_categories: { type: "array", items: { type: "string" } },
2798
+ suppressed_templates: { type: "array", items: { type: "string" } },
2799
+ always_suggest: { type: "array", items: { type: "string" } }
2800
+ }
2801
+ },
2802
+ search: {
2803
+ type: "object",
2804
+ description: "Search behavior preferences",
2805
+ properties: {
2806
+ default_limit: { type: "number", minimum: 1, maximum: 100 },
2807
+ include_low_trust: { type: "boolean" },
2808
+ weight_by_access: { type: "boolean" },
2809
+ weight_by_recency: { type: "boolean" },
2810
+ default_alpha: { type: "number", minimum: 0, maximum: 1 }
2811
+ }
2812
+ },
2813
+ location: {
2814
+ type: "object",
2815
+ description: "Location capture preferences",
2816
+ properties: {
2817
+ auto_capture: { type: "boolean" },
2818
+ precision: { type: "string", enum: ["exact", "approximate", "city", "none"] },
2819
+ share_with_memories: { type: "boolean" }
2820
+ }
2821
+ },
2822
+ privacy: {
2823
+ type: "object",
2824
+ description: "Privacy and trust preferences",
2825
+ properties: {
2826
+ default_trust_level: { type: "number", minimum: 0, maximum: 1 },
2827
+ allow_cross_user_access: { type: "boolean" },
2828
+ auto_approve_requests: { type: "boolean" },
2829
+ audit_logging: { type: "boolean" }
2830
+ }
2831
+ },
2832
+ notifications: {
2833
+ type: "object",
2834
+ description: "Notification preferences",
2835
+ properties: {
2836
+ trust_violations: { type: "boolean" },
2837
+ access_requests: { type: "boolean" },
2838
+ memory_reminders: { type: "boolean" },
2839
+ relationship_suggestions: { type: "boolean" }
2840
+ }
2841
+ },
2842
+ display: {
2843
+ type: "object",
2844
+ description: "Display format preferences",
2845
+ properties: {
2846
+ date_format: { type: "string" },
2847
+ time_format: { type: "string", enum: ["12h", "24h"] },
2848
+ timezone: { type: "string" },
2849
+ language: { type: "string" }
2850
+ }
2851
+ }
2852
+ }
2853
+ };
2854
+ }
2855
+
2856
+ // src/services/preferences-database.service.ts
2857
+ var PreferencesDatabaseService = class {
2858
+ /**
2859
+ * Get user preferences
2860
+ * Returns defaults if preferences don't exist
2861
+ */
2862
+ static async getPreferences(userId) {
2863
+ try {
2864
+ const pathParts = getUserPreferencesPath(userId).split("/");
2865
+ const docId = pathParts.pop();
2866
+ const collectionPath = pathParts.join("/");
2867
+ const doc = await getDocument(collectionPath, docId);
2868
+ if (!doc) {
2869
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2870
+ return {
2871
+ user_id: userId,
2872
+ ...DEFAULT_PREFERENCES,
2873
+ created_at: now,
2874
+ updated_at: now
2875
+ };
2876
+ }
2877
+ return doc;
2878
+ } catch (error) {
2879
+ logger.error("Failed to get preferences:", error);
2880
+ throw new Error(`Failed to get preferences: ${error instanceof Error ? error.message : String(error)}`);
2881
+ }
2882
+ }
2883
+ /**
2884
+ * Update user preferences (partial update with merge)
2885
+ * Creates with defaults if preferences don't exist
2886
+ */
2887
+ static async updatePreferences(userId, updates) {
2888
+ try {
2889
+ const pathParts = getUserPreferencesPath(userId).split("/");
2890
+ const docId = pathParts.pop();
2891
+ const collectionPath = pathParts.join("/");
2892
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2893
+ const doc = await getDocument(collectionPath, docId);
2894
+ if (!doc) {
2895
+ const newPrefs = {
2896
+ user_id: userId,
2897
+ ...DEFAULT_PREFERENCES,
2898
+ ...updates,
2899
+ created_at: now,
2900
+ updated_at: now
2901
+ };
2902
+ await setDocument(collectionPath, docId, newPrefs);
2903
+ logger.info("Preferences created with defaults", { userId });
2904
+ return newPrefs;
2905
+ }
2906
+ const updateData = {
2907
+ ...updates,
2908
+ updated_at: now
2909
+ };
2910
+ await setDocument(collectionPath, docId, updateData, { merge: true });
2911
+ logger.info("Preferences updated", { userId });
2912
+ const updatedDoc = await getDocument(collectionPath, docId);
2913
+ return updatedDoc;
2914
+ } catch (error) {
2915
+ logger.error("Failed to update preferences:", error);
2916
+ throw new Error(`Failed to update preferences: ${error instanceof Error ? error.message : String(error)}`);
2917
+ }
2918
+ }
2919
+ /**
2920
+ * Create preferences with defaults
2921
+ */
2922
+ static async createPreferences(userId) {
2923
+ try {
2924
+ const pathParts = getUserPreferencesPath(userId).split("/");
2925
+ const docId = pathParts.pop();
2926
+ const collectionPath = pathParts.join("/");
2927
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2928
+ const preferences = {
2929
+ user_id: userId,
2930
+ ...DEFAULT_PREFERENCES,
2931
+ created_at: now,
2932
+ updated_at: now
2933
+ };
2934
+ await setDocument(collectionPath, docId, preferences);
2935
+ logger.info("Preferences created", { userId });
2936
+ return preferences;
2937
+ } catch (error) {
2938
+ logger.error("Failed to create preferences:", error);
2939
+ throw new Error(`Failed to create preferences: ${error instanceof Error ? error.message : String(error)}`);
2940
+ }
2941
+ }
2942
+ };
2943
+
2944
+ // src/tools/set-preference.ts
2945
+ var setPreferenceTool = {
2946
+ name: "remember_set_preference",
2947
+ description: `Update user preferences for system behavior through natural conversation.
2948
+
2949
+ This tool allows bulk updates to user preferences. Provide a partial preferences object
2950
+ with only the fields you want to update. All updates are merged with existing preferences.
2951
+
2952
+ ${getPreferenceDescription()}
2953
+
2954
+ Common examples:
2955
+ - Disable template suggestions: { templates: { auto_suggest: false } }
2956
+ - Change search defaults: { search: { default_limit: 20, default_alpha: 0.8 } }
2957
+ - Update privacy: { privacy: { default_trust_level: 0.8 } }
2958
+ - Suppress categories: { templates: { suppressed_categories: ["work", "personal"] } }
2959
+ `,
2960
+ inputSchema: {
2961
+ type: "object",
2962
+ properties: {
2963
+ preferences: {
2964
+ ...getPreferencesSchema(),
2965
+ description: "Partial preferences object with fields to update"
2966
+ }
2967
+ },
2968
+ required: ["preferences"]
2969
+ }
2970
+ };
2971
+ function formatPreferenceChangeMessage(updates) {
2972
+ const changes = [];
2973
+ if (updates.templates) {
2974
+ if (updates.templates.auto_suggest !== void 0) {
2975
+ changes.push(
2976
+ updates.templates.auto_suggest ? "Template suggestions enabled" : "Template suggestions disabled"
2977
+ );
2978
+ }
2979
+ if (updates.templates.suppressed_categories) {
2980
+ changes.push(`Suppressed categories: ${updates.templates.suppressed_categories.join(", ")}`);
2981
+ }
2982
+ }
2983
+ if (updates.search) {
2984
+ if (updates.search.default_limit !== void 0) {
2985
+ changes.push(`Search limit set to ${updates.search.default_limit}`);
2986
+ }
2987
+ if (updates.search.default_alpha !== void 0) {
2988
+ changes.push(`Search alpha set to ${updates.search.default_alpha}`);
2989
+ }
2990
+ }
2991
+ if (updates.privacy) {
2992
+ if (updates.privacy.default_trust_level !== void 0) {
2993
+ changes.push(`Default trust level set to ${updates.privacy.default_trust_level}`);
2994
+ }
2995
+ }
2996
+ if (updates.location) {
2997
+ if (updates.location.auto_capture !== void 0) {
2998
+ changes.push(
2999
+ updates.location.auto_capture ? "Location auto-capture enabled" : "Location auto-capture disabled"
3000
+ );
3001
+ }
3002
+ }
3003
+ if (changes.length === 0) {
3004
+ return "Preferences updated successfully";
3005
+ }
3006
+ return `Preferences updated: ${changes.join(", ")}`;
3007
+ }
3008
+ async function handleSetPreference(args, userId) {
3009
+ try {
3010
+ const { preferences } = args;
3011
+ logger.info("Setting preferences", { userId, updates: Object.keys(preferences) });
3012
+ const updatedPreferences = await PreferencesDatabaseService.updatePreferences(
3013
+ userId,
3014
+ preferences
3015
+ );
3016
+ const message = formatPreferenceChangeMessage(preferences);
3017
+ const result = {
3018
+ success: true,
3019
+ updated_preferences: updatedPreferences,
3020
+ message
3021
+ };
3022
+ logger.info("Preferences set successfully", { userId });
3023
+ return JSON.stringify(result, null, 2);
3024
+ } catch (error) {
3025
+ logger.error("Failed to set preferences:", error);
3026
+ throw new Error(`Failed to set preferences: ${error instanceof Error ? error.message : String(error)}`);
3027
+ }
3028
+ }
3029
+
3030
+ // src/tools/get-preferences.ts
3031
+ var getPreferencesTool = {
3032
+ name: "remember_get_preferences",
3033
+ description: `Get current user preferences.
3034
+
3035
+ Use this to understand user's current settings before suggesting changes
3036
+ or to explain why system is behaving a certain way.
3037
+
3038
+ Returns the complete preferences object or filtered by category.
3039
+ If preferences don't exist, returns defaults.
3040
+
3041
+ ${getPreferenceDescription()}
3042
+ `,
3043
+ inputSchema: {
3044
+ type: "object",
3045
+ properties: {
3046
+ category: {
3047
+ type: "string",
3048
+ enum: ["templates", "search", "location", "privacy", "notifications", "display"],
3049
+ description: "Optional category to filter preferences"
3050
+ }
3051
+ }
3052
+ }
3053
+ };
3054
+ async function handleGetPreferences(args, userId) {
3055
+ try {
3056
+ const { category } = args;
3057
+ logger.info("Getting preferences", { userId, category });
3058
+ const preferences = await PreferencesDatabaseService.getPreferences(userId);
3059
+ const isDefault = !preferences.created_at || preferences.created_at === preferences.updated_at;
3060
+ let result;
3061
+ let message;
3062
+ if (category) {
3063
+ if (!PREFERENCE_CATEGORIES.includes(category)) {
3064
+ throw new Error(`Invalid category: ${category}. Valid categories: ${PREFERENCE_CATEGORIES.join(", ")}`);
3065
+ }
3066
+ result = {
3067
+ [category]: preferences[category]
3068
+ };
3069
+ message = isDefault ? `Showing default ${category} preferences (user has not customized preferences yet).` : `Showing current ${category} preferences.`;
3070
+ } else {
3071
+ result = preferences;
3072
+ message = isDefault ? "Showing default preferences (user has not customized preferences yet)." : "Showing current user preferences.";
3073
+ }
3074
+ const response = {
3075
+ preferences: result,
3076
+ is_default: isDefault,
3077
+ message
3078
+ };
3079
+ logger.info("Preferences retrieved successfully", { userId, category, isDefault });
3080
+ return JSON.stringify(response, null, 2);
3081
+ } catch (error) {
3082
+ logger.error("Failed to get preferences:", error);
3083
+ throw new Error(`Failed to get preferences: ${error instanceof Error ? error.message : String(error)}`);
3084
+ }
3085
+ }
3086
+
2630
3087
  // src/server.ts
2631
3088
  async function initServer() {
2632
3089
  logger.info("Initializing remember-mcp server...");
@@ -2670,7 +3127,10 @@ function registerHandlers(server) {
2670
3127
  createRelationshipTool,
2671
3128
  updateRelationshipTool,
2672
3129
  searchRelationshipTool,
2673
- deleteRelationshipTool
3130
+ deleteRelationshipTool,
3131
+ // Preference tools
3132
+ setPreferenceTool,
3133
+ getPreferencesTool
2674
3134
  ]
2675
3135
  };
2676
3136
  });
@@ -2710,6 +3170,12 @@ function registerHandlers(server) {
2710
3170
  case "remember_delete_relationship":
2711
3171
  result = await handleDeleteRelationship(args, userId);
2712
3172
  break;
3173
+ case "remember_set_preference":
3174
+ result = await handleSetPreference(args, userId);
3175
+ break;
3176
+ case "remember_get_preferences":
3177
+ result = await handleGetPreferences(args, userId);
3178
+ break;
2713
3179
  default:
2714
3180
  throw new McpError(
2715
3181
  ErrorCode.MethodNotFound,