@prmichaelsen/remember-mcp 0.2.5 → 1.0.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.
@@ -1407,22 +1407,125 @@ async function handleCreateMemory(args, userId, context) {
1407
1407
  }
1408
1408
  }
1409
1409
 
1410
+ // src/utils/weaviate-filters.ts
1411
+ function buildCombinedSearchFilters(collection, filters) {
1412
+ const memoryFilters = buildDocTypeFilters(collection, "memory", filters);
1413
+ const relationshipFilters = buildDocTypeFilters(collection, "relationship", filters);
1414
+ if (memoryFilters && relationshipFilters) {
1415
+ return combineFiltersWithOr([memoryFilters, relationshipFilters]);
1416
+ } else if (memoryFilters) {
1417
+ return memoryFilters;
1418
+ } else if (relationshipFilters) {
1419
+ return relationshipFilters;
1420
+ }
1421
+ return void 0;
1422
+ }
1423
+ function buildDocTypeFilters(collection, docType, filters) {
1424
+ const filterList = [];
1425
+ filterList.push(
1426
+ collection.filter.byProperty("doc_type").equal(docType)
1427
+ );
1428
+ if (docType === "memory" && filters?.types && filters.types.length > 0) {
1429
+ if (filters.types.length === 1) {
1430
+ filterList.push(
1431
+ collection.filter.byProperty("type").equal(filters.types[0])
1432
+ );
1433
+ } else {
1434
+ filterList.push(
1435
+ collection.filter.byProperty("type").containsAny(filters.types)
1436
+ );
1437
+ }
1438
+ }
1439
+ if (filters?.weight_min !== void 0) {
1440
+ filterList.push(
1441
+ collection.filter.byProperty("weight").greaterThanOrEqual(filters.weight_min)
1442
+ );
1443
+ }
1444
+ if (filters?.weight_max !== void 0) {
1445
+ filterList.push(
1446
+ collection.filter.byProperty("weight").lessThanOrEqual(filters.weight_max)
1447
+ );
1448
+ }
1449
+ if (filters?.trust_min !== void 0) {
1450
+ filterList.push(
1451
+ collection.filter.byProperty("trust").greaterThanOrEqual(filters.trust_min)
1452
+ );
1453
+ }
1454
+ if (filters?.trust_max !== void 0) {
1455
+ filterList.push(
1456
+ collection.filter.byProperty("trust").lessThanOrEqual(filters.trust_max)
1457
+ );
1458
+ }
1459
+ if (filters?.date_from) {
1460
+ filterList.push(
1461
+ collection.filter.byProperty("created_at").greaterThanOrEqual(new Date(filters.date_from))
1462
+ );
1463
+ }
1464
+ if (filters?.date_to) {
1465
+ filterList.push(
1466
+ collection.filter.byProperty("created_at").lessThanOrEqual(new Date(filters.date_to))
1467
+ );
1468
+ }
1469
+ if (filters?.tags && filters.tags.length > 0) {
1470
+ if (filters.tags.length === 1) {
1471
+ filterList.push(
1472
+ collection.filter.byProperty("tags").containsAny([filters.tags[0]])
1473
+ );
1474
+ } else {
1475
+ filterList.push(
1476
+ collection.filter.byProperty("tags").containsAny(filters.tags)
1477
+ );
1478
+ }
1479
+ }
1480
+ return combineFiltersWithAnd(filterList);
1481
+ }
1482
+ function buildMemoryOnlyFilters(collection, filters) {
1483
+ return buildDocTypeFilters(collection, "memory", filters);
1484
+ }
1485
+ function combineFiltersWithAnd(filters) {
1486
+ if (filters.length === 0) {
1487
+ return void 0;
1488
+ }
1489
+ if (filters.length === 1) {
1490
+ return filters[0];
1491
+ }
1492
+ return {
1493
+ operator: "And",
1494
+ operands: filters
1495
+ };
1496
+ }
1497
+ function combineFiltersWithOr(filters) {
1498
+ if (filters.length === 0) {
1499
+ return void 0;
1500
+ }
1501
+ if (filters.length === 1) {
1502
+ return filters[0];
1503
+ }
1504
+ return {
1505
+ operator: "Or",
1506
+ operands: filters
1507
+ };
1508
+ }
1509
+
1410
1510
  // src/tools/search-memory.ts
1411
1511
  var searchMemoryTool = {
1412
1512
  name: "remember_search_memory",
1413
- description: `Search memories using hybrid semantic and keyword search.
1513
+ description: `Search memories AND relationships using hybrid semantic and keyword search.
1514
+
1515
+ By default, searches BOTH memories and relationships to provide comprehensive results.
1516
+ Relationships contain valuable context in their observations.
1414
1517
 
1415
1518
  Supports:
1416
- - Semantic search (meaning-based)
1519
+ - Semantic search (meaning-based) across memory content and relationship observations
1417
1520
  - Keyword search (exact matches)
1418
1521
  - Hybrid search (balanced with alpha parameter)
1419
1522
  - Filtering by type, tags, weight, trust, date range
1420
- - Location-based search
1523
+ - Returns both memories and relationships in separate arrays
1421
1524
 
1422
1525
  Examples:
1423
- - "Find memories about camping trips"
1424
- - "Search for recipes I saved"
1425
- - "Show me notes from last week"
1526
+ - "Find memories about camping trips" \u2192 returns memories + relationships about camping
1527
+ - "Search for recipes I saved" \u2192 returns recipe memories + related relationships
1528
+ - "Show me notes from last week" \u2192 returns notes + any relationships created that week
1426
1529
  `,
1427
1530
  inputSchema: {
1428
1531
  type: "object",
@@ -1485,8 +1588,8 @@ var searchMemoryTool = {
1485
1588
  },
1486
1589
  include_relationships: {
1487
1590
  type: "boolean",
1488
- description: "Include relationships in results. Default: false",
1489
- default: false
1591
+ description: "Include relationships in results. Default: true (searches both memories and relationships)",
1592
+ default: true
1490
1593
  }
1491
1594
  },
1492
1595
  required: ["query"]
@@ -1494,83 +1597,53 @@ var searchMemoryTool = {
1494
1597
  };
1495
1598
  async function handleSearchMemory(args, userId) {
1496
1599
  try {
1497
- logger.info("Searching memories", { userId, query: args.query });
1600
+ const includeRelationships = args.include_relationships !== false;
1601
+ logger.info("Searching memories and relationships", {
1602
+ userId,
1603
+ query: args.query,
1604
+ includeRelationships
1605
+ });
1498
1606
  const collection = getMemoryCollection(userId);
1499
1607
  const alpha = args.alpha ?? 0.7;
1500
1608
  const limit = args.limit ?? 10;
1501
1609
  const offset = args.offset ?? 0;
1502
- const whereFilters = [
1503
- {
1504
- path: "doc_type",
1505
- operator: "Equal",
1506
- valueText: "memory"
1507
- }
1508
- ];
1509
- if (args.filters?.types && args.filters.types.length > 0) {
1510
- whereFilters.push({
1511
- path: "type",
1512
- operator: "ContainsAny",
1513
- valueTextArray: args.filters.types
1514
- });
1515
- }
1516
- if (args.filters?.weight_min !== void 0) {
1517
- whereFilters.push({
1518
- path: "weight",
1519
- operator: "GreaterThanEqual",
1520
- valueNumber: args.filters.weight_min
1521
- });
1522
- }
1523
- if (args.filters?.trust_min !== void 0) {
1524
- whereFilters.push({
1525
- path: "trust",
1526
- operator: "GreaterThanEqual",
1527
- valueNumber: args.filters.trust_min
1528
- });
1529
- }
1530
- if (args.filters?.date_from) {
1531
- whereFilters.push({
1532
- path: "created_at",
1533
- operator: "GreaterThanEqual",
1534
- valueDate: new Date(args.filters.date_from)
1535
- });
1536
- }
1537
- if (args.filters?.date_to) {
1538
- whereFilters.push({
1539
- path: "created_at",
1540
- operator: "LessThanEqual",
1541
- valueDate: new Date(args.filters.date_to)
1542
- });
1543
- }
1610
+ const filters = includeRelationships ? buildCombinedSearchFilters(collection, args.filters) : buildMemoryOnlyFilters(collection, args.filters);
1544
1611
  const searchOptions = {
1545
1612
  alpha,
1546
1613
  limit: limit + offset
1547
1614
  // Get extra for offset
1548
1615
  };
1549
- if (whereFilters.length > 0) {
1550
- searchOptions.filters = whereFilters.length > 1 ? {
1551
- operator: "And",
1552
- operands: whereFilters
1553
- } : whereFilters[0];
1616
+ if (filters) {
1617
+ searchOptions.filters = filters;
1554
1618
  }
1555
1619
  const results = await collection.query.hybrid(args.query, searchOptions);
1556
1620
  const paginatedResults = results.objects.slice(offset);
1557
- const memories = paginatedResults.map((obj) => ({
1558
- id: obj.uuid,
1559
- ...obj.properties
1560
- }));
1621
+ const memories = [];
1622
+ const relationships = [];
1623
+ for (const obj of paginatedResults) {
1624
+ const doc = {
1625
+ id: obj.uuid,
1626
+ ...obj.properties
1627
+ };
1628
+ if (doc.doc_type === "memory") {
1629
+ memories.push(doc);
1630
+ } else if (doc.doc_type === "relationship") {
1631
+ relationships.push(doc);
1632
+ }
1633
+ }
1561
1634
  const searchResult = {
1562
1635
  memories,
1563
- total: memories.length,
1636
+ relationships: includeRelationships ? relationships : void 0,
1637
+ total: memories.length + relationships.length,
1564
1638
  offset,
1565
1639
  limit
1566
1640
  };
1567
- if (args.include_relationships) {
1568
- searchResult.relationships = [];
1569
- }
1570
1641
  logger.info("Search completed", {
1571
1642
  userId,
1572
1643
  query: args.query,
1573
- results: memories.length
1644
+ memoriesFound: memories.length,
1645
+ relationshipsFound: relationships.length,
1646
+ total: searchResult.total
1574
1647
  });
1575
1648
  return JSON.stringify(searchResult, null, 2);
1576
1649
  } catch (error) {
@@ -2030,59 +2103,15 @@ async function handleQueryMemory(args, userId) {
2030
2103
  const minRelevance = args.min_relevance ?? 0.6;
2031
2104
  const includeContext = args.include_context ?? true;
2032
2105
  const format = args.format ?? "detailed";
2033
- const whereFilters = [
2034
- {
2035
- path: "doc_type",
2036
- operator: "Equal",
2037
- valueText: "memory"
2038
- }
2039
- ];
2040
- if (args.filters?.types && args.filters.types.length > 0) {
2041
- whereFilters.push({
2042
- path: "type",
2043
- operator: "ContainsAny",
2044
- valueTextArray: args.filters.types
2045
- });
2046
- }
2047
- if (args.filters?.weight_min !== void 0) {
2048
- whereFilters.push({
2049
- path: "weight",
2050
- operator: "GreaterThanEqual",
2051
- valueNumber: args.filters.weight_min
2052
- });
2053
- }
2054
- if (args.filters?.trust_min !== void 0) {
2055
- whereFilters.push({
2056
- path: "trust",
2057
- operator: "GreaterThanEqual",
2058
- valueNumber: args.filters.trust_min
2059
- });
2060
- }
2061
- if (args.filters?.date_from) {
2062
- whereFilters.push({
2063
- path: "created_at",
2064
- operator: "GreaterThanEqual",
2065
- valueDate: new Date(args.filters.date_from)
2066
- });
2067
- }
2068
- if (args.filters?.date_to) {
2069
- whereFilters.push({
2070
- path: "created_at",
2071
- operator: "LessThanEqual",
2072
- valueDate: new Date(args.filters.date_to)
2073
- });
2074
- }
2106
+ const filters = buildCombinedSearchFilters(collection, args.filters);
2075
2107
  const searchOptions = {
2076
2108
  limit,
2077
2109
  distance: 1 - minRelevance,
2078
2110
  // Convert relevance to distance
2079
2111
  returnMetadata: ["distance"]
2080
2112
  };
2081
- if (whereFilters.length > 0) {
2082
- searchOptions.filters = whereFilters.length > 1 ? {
2083
- operator: "And",
2084
- operands: whereFilters
2085
- } : whereFilters[0];
2113
+ if (filters) {
2114
+ searchOptions.filters = filters;
2086
2115
  }
2087
2116
  const results = await collection.query.nearText(args.query, searchOptions);
2088
2117
  const relevantMemories = results.objects.map((obj) => {
@@ -2698,6 +2727,437 @@ async function handleDeleteRelationship(args, userId) {
2698
2727
  }
2699
2728
  }
2700
2729
 
2730
+ // src/services/preferences-database.service.ts
2731
+ init_init();
2732
+
2733
+ // src/firestore/paths.ts
2734
+ var APP_NAME = "remember-mcp";
2735
+ function getBasePrefix() {
2736
+ const environment = process.env.ENVIRONMENT;
2737
+ if (environment && environment !== "production" && environment !== "prod") {
2738
+ return `${environment}.${APP_NAME}`;
2739
+ }
2740
+ const isDevelopment = process.env.NODE_ENV === "development";
2741
+ if (isDevelopment) {
2742
+ const customPrefix = process.env.DB_PREFIX;
2743
+ if (customPrefix) {
2744
+ return customPrefix;
2745
+ }
2746
+ return `e0.${APP_NAME}`;
2747
+ }
2748
+ return APP_NAME;
2749
+ }
2750
+ var BASE = getBasePrefix();
2751
+ function getUserPreferencesPath(userId) {
2752
+ return `${BASE}.users/${userId}/preferences`;
2753
+ }
2754
+
2755
+ // src/types/preferences.ts
2756
+ var DEFAULT_PREFERENCES = {
2757
+ templates: {
2758
+ auto_suggest: true,
2759
+ suggestion_threshold: 0.6,
2760
+ max_suggestions: 3,
2761
+ show_preview: true,
2762
+ remember_choice: true,
2763
+ suppressed_categories: [],
2764
+ suppressed_templates: [],
2765
+ always_suggest: []
2766
+ },
2767
+ search: {
2768
+ default_limit: 10,
2769
+ include_low_trust: false,
2770
+ weight_by_access: true,
2771
+ weight_by_recency: true,
2772
+ default_alpha: 0.7
2773
+ },
2774
+ location: {
2775
+ auto_capture: true,
2776
+ precision: "approximate",
2777
+ share_with_memories: true
2778
+ },
2779
+ privacy: {
2780
+ default_trust_level: 0.5,
2781
+ allow_cross_user_access: false,
2782
+ auto_approve_requests: false,
2783
+ audit_logging: true
2784
+ },
2785
+ notifications: {
2786
+ trust_violations: true,
2787
+ access_requests: true,
2788
+ memory_reminders: false,
2789
+ relationship_suggestions: true
2790
+ },
2791
+ display: {
2792
+ date_format: "MM/DD/YYYY",
2793
+ time_format: "12h",
2794
+ timezone: "America/Los_Angeles",
2795
+ language: "en"
2796
+ }
2797
+ };
2798
+ var PREFERENCE_CATEGORIES = [
2799
+ "templates",
2800
+ "search",
2801
+ "location",
2802
+ "privacy",
2803
+ "notifications",
2804
+ "display"
2805
+ ];
2806
+ var PREFERENCE_DESCRIPTIONS = {
2807
+ templates: {
2808
+ auto_suggest: "Automatically suggest templates when creating memories (default: true)",
2809
+ suggestion_threshold: "Minimum confidence to show template suggestion, 0-1 (default: 0.6)",
2810
+ max_suggestions: "Maximum number of templates to suggest, 1-5 (default: 3)",
2811
+ show_preview: "Show template preview in suggestion (default: true)",
2812
+ remember_choice: `Remember "don't suggest for this type" choices (default: true)`,
2813
+ suppressed_categories: "Categories to never suggest templates for (default: [])",
2814
+ suppressed_templates: "Specific templates to never suggest (default: [])",
2815
+ always_suggest: "Templates to always suggest regardless of confidence (default: [])"
2816
+ },
2817
+ search: {
2818
+ default_limit: "Default number of search results to return, 1-100 (default: 10)",
2819
+ include_low_trust: "Include low-trust memories in search results (default: false)",
2820
+ weight_by_access: "Use access count in search ranking (default: true)",
2821
+ weight_by_recency: "Use recency in search ranking (default: true)",
2822
+ default_alpha: "Default hybrid search alpha (0=keyword, 1=semantic, default: 0.7)"
2823
+ },
2824
+ location: {
2825
+ auto_capture: "Automatically capture location for memories (default: true)",
2826
+ precision: "Location precision level: exact, approximate, city, none (default: approximate)",
2827
+ share_with_memories: "Include location data in memories (default: true)"
2828
+ },
2829
+ privacy: {
2830
+ default_trust_level: "Default trust level for new memories, 0-1 (default: 0.5)",
2831
+ allow_cross_user_access: "Allow other users to request access to memories (default: false)",
2832
+ auto_approve_requests: "Automatically approve access requests (default: false)",
2833
+ audit_logging: "Enable audit logging for preference changes (default: true)"
2834
+ },
2835
+ notifications: {
2836
+ trust_violations: "Notify on trust violations (default: true)",
2837
+ access_requests: "Notify on access requests from other users (default: true)",
2838
+ memory_reminders: "Send reminders about important memories (default: false)",
2839
+ relationship_suggestions: "Suggest new relationships between memories (default: true)"
2840
+ },
2841
+ display: {
2842
+ date_format: "Date format string (default: MM/DD/YYYY)",
2843
+ time_format: "Time format: 12h or 24h (default: 12h)",
2844
+ timezone: "Timezone identifier (default: America/Los_Angeles)",
2845
+ language: "Language code (default: en)"
2846
+ }
2847
+ };
2848
+ function getPreferenceDescription() {
2849
+ const categories = Object.entries(PREFERENCE_DESCRIPTIONS).map(([category, fields]) => {
2850
+ const fieldList = Object.entries(fields).map(([field, desc]) => ` - ${field}: ${desc}`).join("\n");
2851
+ return `${category}:
2852
+ ${fieldList}`;
2853
+ }).join("\n\n");
2854
+ return `User preferences control system behavior. Available preference categories and fields:
2855
+
2856
+ ${categories}`;
2857
+ }
2858
+ function getPreferencesSchema() {
2859
+ return {
2860
+ type: "object",
2861
+ properties: {
2862
+ templates: {
2863
+ type: "object",
2864
+ description: "Template suggestion preferences",
2865
+ properties: {
2866
+ auto_suggest: { type: "boolean" },
2867
+ suggestion_threshold: { type: "number", minimum: 0, maximum: 1 },
2868
+ max_suggestions: { type: "number", minimum: 1, maximum: 5 },
2869
+ show_preview: { type: "boolean" },
2870
+ remember_choice: { type: "boolean" },
2871
+ suppressed_categories: { type: "array", items: { type: "string" } },
2872
+ suppressed_templates: { type: "array", items: { type: "string" } },
2873
+ always_suggest: { type: "array", items: { type: "string" } }
2874
+ }
2875
+ },
2876
+ search: {
2877
+ type: "object",
2878
+ description: "Search behavior preferences",
2879
+ properties: {
2880
+ default_limit: { type: "number", minimum: 1, maximum: 100 },
2881
+ include_low_trust: { type: "boolean" },
2882
+ weight_by_access: { type: "boolean" },
2883
+ weight_by_recency: { type: "boolean" },
2884
+ default_alpha: { type: "number", minimum: 0, maximum: 1 }
2885
+ }
2886
+ },
2887
+ location: {
2888
+ type: "object",
2889
+ description: "Location capture preferences",
2890
+ properties: {
2891
+ auto_capture: { type: "boolean" },
2892
+ precision: { type: "string", enum: ["exact", "approximate", "city", "none"] },
2893
+ share_with_memories: { type: "boolean" }
2894
+ }
2895
+ },
2896
+ privacy: {
2897
+ type: "object",
2898
+ description: "Privacy and trust preferences",
2899
+ properties: {
2900
+ default_trust_level: { type: "number", minimum: 0, maximum: 1 },
2901
+ allow_cross_user_access: { type: "boolean" },
2902
+ auto_approve_requests: { type: "boolean" },
2903
+ audit_logging: { type: "boolean" }
2904
+ }
2905
+ },
2906
+ notifications: {
2907
+ type: "object",
2908
+ description: "Notification preferences",
2909
+ properties: {
2910
+ trust_violations: { type: "boolean" },
2911
+ access_requests: { type: "boolean" },
2912
+ memory_reminders: { type: "boolean" },
2913
+ relationship_suggestions: { type: "boolean" }
2914
+ }
2915
+ },
2916
+ display: {
2917
+ type: "object",
2918
+ description: "Display format preferences",
2919
+ properties: {
2920
+ date_format: { type: "string" },
2921
+ time_format: { type: "string", enum: ["12h", "24h"] },
2922
+ timezone: { type: "string" },
2923
+ language: { type: "string" }
2924
+ }
2925
+ }
2926
+ }
2927
+ };
2928
+ }
2929
+
2930
+ // src/services/preferences-database.service.ts
2931
+ var PreferencesDatabaseService = class {
2932
+ /**
2933
+ * Get user preferences
2934
+ * Returns defaults if preferences don't exist
2935
+ */
2936
+ static async getPreferences(userId) {
2937
+ try {
2938
+ const pathParts = getUserPreferencesPath(userId).split("/");
2939
+ const docId = pathParts.pop();
2940
+ const collectionPath = pathParts.join("/");
2941
+ const doc = await getDocument(collectionPath, docId);
2942
+ if (!doc) {
2943
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2944
+ return {
2945
+ user_id: userId,
2946
+ ...DEFAULT_PREFERENCES,
2947
+ created_at: now,
2948
+ updated_at: now
2949
+ };
2950
+ }
2951
+ return doc;
2952
+ } catch (error) {
2953
+ logger.error("Failed to get preferences:", error);
2954
+ throw new Error(`Failed to get preferences: ${error instanceof Error ? error.message : String(error)}`);
2955
+ }
2956
+ }
2957
+ /**
2958
+ * Update user preferences (partial update with merge)
2959
+ * Creates with defaults if preferences don't exist
2960
+ */
2961
+ static async updatePreferences(userId, updates) {
2962
+ try {
2963
+ const pathParts = getUserPreferencesPath(userId).split("/");
2964
+ const docId = pathParts.pop();
2965
+ const collectionPath = pathParts.join("/");
2966
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2967
+ const doc = await getDocument(collectionPath, docId);
2968
+ if (!doc) {
2969
+ const newPrefs = {
2970
+ user_id: userId,
2971
+ ...DEFAULT_PREFERENCES,
2972
+ ...updates,
2973
+ created_at: now,
2974
+ updated_at: now
2975
+ };
2976
+ await setDocument(collectionPath, docId, newPrefs);
2977
+ logger.info("Preferences created with defaults", { userId });
2978
+ return newPrefs;
2979
+ }
2980
+ const updateData = {
2981
+ ...updates,
2982
+ updated_at: now
2983
+ };
2984
+ await setDocument(collectionPath, docId, updateData, { merge: true });
2985
+ logger.info("Preferences updated", { userId });
2986
+ const updatedDoc = await getDocument(collectionPath, docId);
2987
+ return updatedDoc;
2988
+ } catch (error) {
2989
+ logger.error("Failed to update preferences:", error);
2990
+ throw new Error(`Failed to update preferences: ${error instanceof Error ? error.message : String(error)}`);
2991
+ }
2992
+ }
2993
+ /**
2994
+ * Create preferences with defaults
2995
+ */
2996
+ static async createPreferences(userId) {
2997
+ try {
2998
+ const pathParts = getUserPreferencesPath(userId).split("/");
2999
+ const docId = pathParts.pop();
3000
+ const collectionPath = pathParts.join("/");
3001
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3002
+ const preferences = {
3003
+ user_id: userId,
3004
+ ...DEFAULT_PREFERENCES,
3005
+ created_at: now,
3006
+ updated_at: now
3007
+ };
3008
+ await setDocument(collectionPath, docId, preferences);
3009
+ logger.info("Preferences created", { userId });
3010
+ return preferences;
3011
+ } catch (error) {
3012
+ logger.error("Failed to create preferences:", error);
3013
+ throw new Error(`Failed to create preferences: ${error instanceof Error ? error.message : String(error)}`);
3014
+ }
3015
+ }
3016
+ };
3017
+
3018
+ // src/tools/set-preference.ts
3019
+ var setPreferenceTool = {
3020
+ name: "remember_set_preference",
3021
+ description: `Update user preferences for system behavior through natural conversation.
3022
+
3023
+ This tool allows bulk updates to user preferences. Provide a partial preferences object
3024
+ with only the fields you want to update. All updates are merged with existing preferences.
3025
+
3026
+ ${getPreferenceDescription()}
3027
+
3028
+ Common examples:
3029
+ - Disable template suggestions: { templates: { auto_suggest: false } }
3030
+ - Change search defaults: { search: { default_limit: 20, default_alpha: 0.8 } }
3031
+ - Update privacy: { privacy: { default_trust_level: 0.8 } }
3032
+ - Suppress categories: { templates: { suppressed_categories: ["work", "personal"] } }
3033
+ `,
3034
+ inputSchema: {
3035
+ type: "object",
3036
+ properties: {
3037
+ preferences: {
3038
+ ...getPreferencesSchema(),
3039
+ description: "Partial preferences object with fields to update"
3040
+ }
3041
+ },
3042
+ required: ["preferences"]
3043
+ }
3044
+ };
3045
+ function formatPreferenceChangeMessage(updates) {
3046
+ const changes = [];
3047
+ if (updates.templates) {
3048
+ if (updates.templates.auto_suggest !== void 0) {
3049
+ changes.push(
3050
+ updates.templates.auto_suggest ? "Template suggestions enabled" : "Template suggestions disabled"
3051
+ );
3052
+ }
3053
+ if (updates.templates.suppressed_categories) {
3054
+ changes.push(`Suppressed categories: ${updates.templates.suppressed_categories.join(", ")}`);
3055
+ }
3056
+ }
3057
+ if (updates.search) {
3058
+ if (updates.search.default_limit !== void 0) {
3059
+ changes.push(`Search limit set to ${updates.search.default_limit}`);
3060
+ }
3061
+ if (updates.search.default_alpha !== void 0) {
3062
+ changes.push(`Search alpha set to ${updates.search.default_alpha}`);
3063
+ }
3064
+ }
3065
+ if (updates.privacy) {
3066
+ if (updates.privacy.default_trust_level !== void 0) {
3067
+ changes.push(`Default trust level set to ${updates.privacy.default_trust_level}`);
3068
+ }
3069
+ }
3070
+ if (updates.location) {
3071
+ if (updates.location.auto_capture !== void 0) {
3072
+ changes.push(
3073
+ updates.location.auto_capture ? "Location auto-capture enabled" : "Location auto-capture disabled"
3074
+ );
3075
+ }
3076
+ }
3077
+ if (changes.length === 0) {
3078
+ return "Preferences updated successfully";
3079
+ }
3080
+ return `Preferences updated: ${changes.join(", ")}`;
3081
+ }
3082
+ async function handleSetPreference(args, userId) {
3083
+ try {
3084
+ const { preferences } = args;
3085
+ logger.info("Setting preferences", { userId, updates: Object.keys(preferences) });
3086
+ const updatedPreferences = await PreferencesDatabaseService.updatePreferences(
3087
+ userId,
3088
+ preferences
3089
+ );
3090
+ const message = formatPreferenceChangeMessage(preferences);
3091
+ const result = {
3092
+ success: true,
3093
+ updated_preferences: updatedPreferences,
3094
+ message
3095
+ };
3096
+ logger.info("Preferences set successfully", { userId });
3097
+ return JSON.stringify(result, null, 2);
3098
+ } catch (error) {
3099
+ logger.error("Failed to set preferences:", error);
3100
+ throw new Error(`Failed to set preferences: ${error instanceof Error ? error.message : String(error)}`);
3101
+ }
3102
+ }
3103
+
3104
+ // src/tools/get-preferences.ts
3105
+ var getPreferencesTool = {
3106
+ name: "remember_get_preferences",
3107
+ description: `Get current user preferences.
3108
+
3109
+ Use this to understand user's current settings before suggesting changes
3110
+ or to explain why system is behaving a certain way.
3111
+
3112
+ Returns the complete preferences object or filtered by category.
3113
+ If preferences don't exist, returns defaults.
3114
+
3115
+ ${getPreferenceDescription()}
3116
+ `,
3117
+ inputSchema: {
3118
+ type: "object",
3119
+ properties: {
3120
+ category: {
3121
+ type: "string",
3122
+ enum: ["templates", "search", "location", "privacy", "notifications", "display"],
3123
+ description: "Optional category to filter preferences"
3124
+ }
3125
+ }
3126
+ }
3127
+ };
3128
+ async function handleGetPreferences(args, userId) {
3129
+ try {
3130
+ const { category } = args;
3131
+ logger.info("Getting preferences", { userId, category });
3132
+ const preferences = await PreferencesDatabaseService.getPreferences(userId);
3133
+ const isDefault = !preferences.created_at || preferences.created_at === preferences.updated_at;
3134
+ let result;
3135
+ let message;
3136
+ if (category) {
3137
+ if (!PREFERENCE_CATEGORIES.includes(category)) {
3138
+ throw new Error(`Invalid category: ${category}. Valid categories: ${PREFERENCE_CATEGORIES.join(", ")}`);
3139
+ }
3140
+ result = {
3141
+ [category]: preferences[category]
3142
+ };
3143
+ message = isDefault ? `Showing default ${category} preferences (user has not customized preferences yet).` : `Showing current ${category} preferences.`;
3144
+ } else {
3145
+ result = preferences;
3146
+ message = isDefault ? "Showing default preferences (user has not customized preferences yet)." : "Showing current user preferences.";
3147
+ }
3148
+ const response = {
3149
+ preferences: result,
3150
+ is_default: isDefault,
3151
+ message
3152
+ };
3153
+ logger.info("Preferences retrieved successfully", { userId, category, isDefault });
3154
+ return JSON.stringify(response, null, 2);
3155
+ } catch (error) {
3156
+ logger.error("Failed to get preferences:", error);
3157
+ throw new Error(`Failed to get preferences: ${error instanceof Error ? error.message : String(error)}`);
3158
+ }
3159
+ }
3160
+
2701
3161
  // src/server-factory.ts
2702
3162
  var databasesInitialized = false;
2703
3163
  var initializationPromise = null;
@@ -2730,14 +3190,12 @@ async function ensureDatabasesInitialized() {
2730
3190
  })();
2731
3191
  return initializationPromise;
2732
3192
  }
2733
- function createServer(accessToken, userId, options = {}) {
3193
+ async function createServer(accessToken, userId, options = {}) {
2734
3194
  if (!userId) {
2735
3195
  throw new Error("userId is required");
2736
3196
  }
2737
3197
  logger.debug("Creating server instance", { userId });
2738
- ensureDatabasesInitialized().catch((error) => {
2739
- logger.error("Failed to initialize databases:", error);
2740
- });
3198
+ await ensureDatabasesInitialized();
2741
3199
  const server = new Server(
2742
3200
  {
2743
3201
  name: options.name || "remember-mcp",
@@ -2775,7 +3233,10 @@ function registerHandlers(server, userId, accessToken) {
2775
3233
  createRelationshipTool,
2776
3234
  updateRelationshipTool,
2777
3235
  searchRelationshipTool,
2778
- deleteRelationshipTool
3236
+ deleteRelationshipTool,
3237
+ // Preference tools
3238
+ setPreferenceTool,
3239
+ getPreferencesTool
2779
3240
  ]
2780
3241
  };
2781
3242
  });
@@ -2817,6 +3278,12 @@ function registerHandlers(server, userId, accessToken) {
2817
3278
  case "remember_delete_relationship":
2818
3279
  result = await handleDeleteRelationship(args, userId);
2819
3280
  break;
3281
+ case "remember_set_preference":
3282
+ result = await handleSetPreference(args, userId);
3283
+ break;
3284
+ case "remember_get_preferences":
3285
+ result = await handleGetPreferences(args, userId);
3286
+ break;
2820
3287
  default:
2821
3288
  throw new McpError(
2822
3289
  ErrorCode.MethodNotFound,