@prmichaelsen/remember-mcp 2.6.13 → 2.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,55 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.7.1] - 2026-02-17
9
+
10
+ ### Fixed
11
+
12
+ - **CRITICAL: Memory_public Auto-Schema**: Let Weaviate auto-generate schema instead of manual definition
13
+ - Removed manual schema creation for Memory_public collection
14
+ - Weaviate will auto-generate schema from first insert
15
+ - Ensures schema matches user memory collections EXACTLY
16
+ - Includes nested objects (context, location) that were auto-generated in user collections
17
+ - Fixes issue where nested objects were rejected due to schema expecting flattened properties
18
+ - Spread operator now works perfectly since schemas are identical
19
+
20
+ ### Root Cause
21
+
22
+ - User memory collections have auto-generated schemas with nested `context` and `location` objects
23
+ - Memory_public had manually defined schema with flattened properties
24
+ - When spreading `...originalMemory.properties`, nested objects didn't match flattened schema
25
+ - Weaviate rejected ALL properties due to nested object schema conflict
26
+ - Auto-schema approach ensures perfect compatibility
27
+
28
+ ---
29
+
30
+ ## [2.7.0] - 2026-02-17
31
+
32
+ ### Fixed
33
+
34
+ - **CRITICAL: `remember_update_memory` Now Uses `replace()` Instead of `update()`**
35
+ - Switched from `collection.data.update()` to `collection.data.replace()`
36
+ - Fixes Weaviate bug where `update()` only persists if vectorized fields change
37
+ - Now fetches full object and merges updates before replacing
38
+ - Updates to non-vectorized fields (title, type, weight, tags, etc.) now persist correctly
39
+ - Resolves issue where updates returned success but changes weren't saved
40
+ - **Breaking Change**: This is a minor version bump due to behavior change (more reliable updates)
41
+
42
+ ### Changed
43
+
44
+ - Fetch strategy: Now fetches all properties (not just 3) since we need full object for replace
45
+ - Update strategy: Merge updates with existing properties, then replace entire object
46
+ - Logging: Changed "Calling Weaviate update" to "Calling Weaviate replace"
47
+
48
+ ### Root Cause
49
+
50
+ - Weaviate has a known bug where `update()` only persists changes if at least one vectorized field is modified
51
+ - Our schema only vectorizes `content` and `observation`
52
+ - Updates to `title`, `type`, `weight`, `tags`, etc. were silently ignored
53
+ - `replace()` doesn't have this limitation and always persists changes
54
+
55
+ ---
56
+
8
57
  ## [2.6.13] - 2026-02-17
9
58
 
10
59
  ### Changed
@@ -1948,9 +1948,7 @@ async function handleUpdateMemory(args, userId) {
1948
1948
  const collection = getMemoryCollection(userId);
1949
1949
  let existingMemory;
1950
1950
  try {
1951
- existingMemory = await collection.query.fetchObjectById(args.memory_id, {
1952
- returnProperties: ["user_id", "doc_type", "version"]
1953
- });
1951
+ existingMemory = await collection.query.fetchObjectById(args.memory_id);
1954
1952
  } catch (fetchError) {
1955
1953
  const fetchErrorMsg = fetchError instanceof Error ? fetchError.message : String(fetchError);
1956
1954
  logger.error("Failed to fetch memory for update:", {
@@ -1961,7 +1959,7 @@ async function handleUpdateMemory(args, userId) {
1961
1959
  });
1962
1960
  throw new Error(`Failed to fetch memory ${args.memory_id}: ${fetchErrorMsg}`);
1963
1961
  }
1964
- if (!existingMemory) {
1962
+ if (!existingMemory || !existingMemory.properties) {
1965
1963
  throw new Error(`Memory not found: ${args.memory_id}. It may have been deleted or never existed.`);
1966
1964
  }
1967
1965
  if (existingMemory.properties.user_id !== userId) {
@@ -2034,26 +2032,31 @@ async function handleUpdateMemory(args, userId) {
2034
2032
  const now = (/* @__PURE__ */ new Date()).toISOString();
2035
2033
  updates.updated_at = now;
2036
2034
  updates.version = existingMemory.properties.version + 1;
2037
- logger.info("Calling Weaviate update", {
2035
+ const mergedProperties = {
2036
+ ...existingMemory.properties,
2037
+ ...updates
2038
+ };
2039
+ logger.info("Calling Weaviate replace", {
2038
2040
  userId,
2039
2041
  memoryId: args.memory_id,
2040
2042
  updateFields: Object.keys(updates),
2041
2043
  updateValues: updates,
2042
- collectionName: `Memory_${userId}`
2044
+ collectionName: `Memory_${userId}`,
2045
+ totalProperties: Object.keys(mergedProperties).length
2043
2046
  });
2044
2047
  try {
2045
- await collection.data.update({
2048
+ await collection.data.replace({
2046
2049
  id: args.memory_id,
2047
- properties: updates
2050
+ properties: mergedProperties
2048
2051
  });
2049
- logger.info("Weaviate update completed (no error thrown)", {
2052
+ logger.info("Weaviate replace completed (no error thrown)", {
2050
2053
  userId,
2051
2054
  memoryId: args.memory_id,
2052
2055
  updatedFields: Object.keys(updates)
2053
2056
  });
2054
2057
  } catch (updateError) {
2055
2058
  const updateErrorMsg = updateError instanceof Error ? updateError.message : String(updateError);
2056
- logger.error("Failed to perform Weaviate update:", {
2059
+ logger.error("Failed to perform Weaviate replace:", {
2057
2060
  error: updateErrorMsg,
2058
2061
  userId,
2059
2062
  memoryId: args.memory_id,
@@ -3684,283 +3687,11 @@ init_space_memory();
3684
3687
  init_logger();
3685
3688
  import weaviate3 from "weaviate-client";
3686
3689
  var PUBLIC_COLLECTION_NAME = "Memory_public";
3687
- function getSpaceCollectionName(spaceId) {
3688
- return `Memory_${spaceId}`;
3689
- }
3690
3690
  function isValidSpaceId(spaceId) {
3691
3691
  return SUPPORTED_SPACES.includes(spaceId);
3692
3692
  }
3693
- async function createSpaceCollection(client2, spaceId) {
3694
- const collectionName = spaceId === "public" ? PUBLIC_COLLECTION_NAME : getSpaceCollectionName(spaceId);
3695
- logger.info("Creating space collection", {
3696
- module: "weaviate-space-schema",
3697
- collectionName,
3698
- spaceId
3699
- });
3700
- await client2.collections.create({
3701
- name: collectionName,
3702
- // Vectorizer configuration
3703
- vectorizers: weaviate3.configure.vectorizer.text2VecOpenAI({
3704
- model: "text-embedding-3-small",
3705
- // Vectorize content for semantic search
3706
- sourceProperties: ["content", "observation"]
3707
- }),
3708
- properties: [
3709
- // Discriminator
3710
- {
3711
- name: "doc_type",
3712
- dataType: "text",
3713
- description: 'Document type: "space_memory"'
3714
- },
3715
- // Space identity
3716
- {
3717
- name: "spaces",
3718
- dataType: "text[]",
3719
- description: 'Spaces this memory is published to (e.g., ["the_void", "dogs"])'
3720
- },
3721
- {
3722
- name: "space_id",
3723
- dataType: "text",
3724
- description: "DEPRECATED: Use spaces array instead. Will be removed in v3.0.0."
3725
- },
3726
- {
3727
- name: "author_id",
3728
- dataType: "text",
3729
- description: "Original author user_id (for permissions)"
3730
- },
3731
- {
3732
- name: "ghost_id",
3733
- dataType: "text",
3734
- description: "Optional ghost profile ID for pseudonymous publishing"
3735
- },
3736
- {
3737
- name: "attribution",
3738
- dataType: "text",
3739
- description: 'Attribution type: "user" or "ghost"'
3740
- },
3741
- // Discovery metadata
3742
- {
3743
- name: "published_at",
3744
- dataType: "text",
3745
- description: "When published to space (ISO 8601)"
3746
- },
3747
- {
3748
- name: "discovery_count",
3749
- dataType: "number",
3750
- description: "How many times discovered"
3751
- },
3752
- // Memory fields (same as personal memories)
3753
- {
3754
- name: "content",
3755
- dataType: "text",
3756
- description: "Main memory content (vectorized)"
3757
- },
3758
- {
3759
- name: "title",
3760
- dataType: "text",
3761
- description: "Optional short title"
3762
- },
3763
- {
3764
- name: "summary",
3765
- dataType: "text",
3766
- description: "Optional brief summary"
3767
- },
3768
- {
3769
- name: "type",
3770
- dataType: "text",
3771
- description: "Content type (note, event, person, etc.)"
3772
- },
3773
- // Scoring fields
3774
- {
3775
- name: "weight",
3776
- dataType: "number",
3777
- description: "Significance/priority (0-1)"
3778
- },
3779
- {
3780
- name: "trust",
3781
- dataType: "number",
3782
- description: "Access control level (0-1)"
3783
- },
3784
- {
3785
- name: "confidence",
3786
- dataType: "number",
3787
- description: "System confidence in accuracy (0-1)"
3788
- },
3789
- // Location fields (flattened) - MUST match user memory schema exactly
3790
- {
3791
- name: "location_gps_lat",
3792
- dataType: "number",
3793
- description: "GPS latitude"
3794
- },
3795
- {
3796
- name: "location_gps_lng",
3797
- dataType: "number",
3798
- description: "GPS longitude"
3799
- },
3800
- {
3801
- name: "location_address",
3802
- dataType: "text",
3803
- description: "Formatted address"
3804
- },
3805
- {
3806
- name: "location_city",
3807
- dataType: "text",
3808
- description: "City name"
3809
- },
3810
- {
3811
- name: "location_country",
3812
- dataType: "text",
3813
- description: "Country name"
3814
- },
3815
- {
3816
- name: "location_source",
3817
- dataType: "text",
3818
- description: "Location source (gps, ip, manual, etc.)"
3819
- },
3820
- // Context fields (flattened) - MUST match user memory schema exactly
3821
- {
3822
- name: "context_conversation_id",
3823
- dataType: "text",
3824
- description: "Conversation ID"
3825
- },
3826
- {
3827
- name: "context_summary",
3828
- dataType: "text",
3829
- description: "Brief context summary"
3830
- },
3831
- {
3832
- name: "context_timestamp",
3833
- dataType: "date",
3834
- description: "Context timestamp"
3835
- },
3836
- // Locale fields - MUST match user memory schema exactly
3837
- {
3838
- name: "locale_language",
3839
- dataType: "text",
3840
- description: "Language code (e.g., en, es, fr)"
3841
- },
3842
- {
3843
- name: "locale_timezone",
3844
- dataType: "text",
3845
- description: "Timezone (e.g., America/Los_Angeles)"
3846
- },
3847
- // Relationships - MUST match user memory schema exactly
3848
- {
3849
- name: "relationships",
3850
- dataType: "text[]",
3851
- description: "Array of relationship IDs"
3852
- },
3853
- // Access tracking - MUST match user memory schema exactly
3854
- {
3855
- name: "access_count",
3856
- dataType: "number",
3857
- description: "Total times accessed"
3858
- },
3859
- {
3860
- name: "last_accessed_at",
3861
- dataType: "date",
3862
- description: "Most recent access timestamp"
3863
- },
3864
- // Metadata - MUST match user memory schema exactly
3865
- {
3866
- name: "tags",
3867
- dataType: "text[]",
3868
- description: "Tags for organization"
3869
- },
3870
- {
3871
- name: "references",
3872
- dataType: "text[]",
3873
- description: "Source URLs"
3874
- },
3875
- {
3876
- name: "template_id",
3877
- dataType: "text",
3878
- description: "Template ID if using template"
3879
- },
3880
- // Relationship-specific fields (for relationships in public space)
3881
- {
3882
- name: "memory_ids",
3883
- dataType: "text[]",
3884
- description: "Connected memory IDs (for relationships)"
3885
- },
3886
- {
3887
- name: "relationship_type",
3888
- dataType: "text",
3889
- description: "Relationship type (for relationships)"
3890
- },
3891
- {
3892
- name: "observation",
3893
- dataType: "text",
3894
- description: "Relationship observation (vectorized)"
3895
- },
3896
- {
3897
- name: "strength",
3898
- dataType: "number",
3899
- description: "Relationship strength (0-1)"
3900
- },
3901
- // Computed fields - MUST match user memory schema exactly
3902
- {
3903
- name: "base_weight",
3904
- dataType: "number",
3905
- description: "User-specified weight"
3906
- },
3907
- {
3908
- name: "computed_weight",
3909
- dataType: "number",
3910
- description: "Calculated effective weight"
3911
- },
3912
- // User ID field (for backwards compatibility with spread operator)
3913
- {
3914
- name: "user_id",
3915
- dataType: "text",
3916
- description: "User ID (copied from original memory, same as author_id)"
3917
- },
3918
- // Timestamps
3919
- {
3920
- name: "created_at",
3921
- dataType: "text",
3922
- description: "Original creation timestamp (ISO 8601)"
3923
- },
3924
- {
3925
- name: "updated_at",
3926
- dataType: "text",
3927
- description: "Last update timestamp (ISO 8601)"
3928
- },
3929
- // Versioning
3930
- {
3931
- name: "version",
3932
- dataType: "number",
3933
- description: "Version number (increments on update)"
3934
- },
3935
- // Comment/threading fields (for threaded discussions in shared spaces)
3936
- {
3937
- name: "parent_id",
3938
- dataType: "text",
3939
- description: "ID of parent memory or comment (for threading)"
3940
- },
3941
- {
3942
- name: "thread_root_id",
3943
- dataType: "text",
3944
- description: "Root memory ID for fetching entire thread"
3945
- },
3946
- {
3947
- name: "moderation_flags",
3948
- dataType: "text[]",
3949
- description: 'Per-space moderation flags (format: "{space_id}:{flag_type}")'
3950
- }
3951
- ]
3952
- });
3953
- logger.info("Space collection created successfully", {
3954
- module: "weaviate-space-schema",
3955
- collectionName
3956
- });
3957
- }
3958
3693
  async function ensurePublicCollection(client2) {
3959
3694
  const collectionName = PUBLIC_COLLECTION_NAME;
3960
- const exists = await client2.collections.exists(collectionName);
3961
- if (!exists) {
3962
- await createSpaceCollection(client2, "public");
3963
- }
3964
3695
  return client2.collections.get(collectionName);
3965
3696
  }
3966
3697
 
package/dist/server.js CHANGED
@@ -2016,9 +2016,7 @@ async function handleUpdateMemory(args, userId) {
2016
2016
  const collection = getMemoryCollection(userId);
2017
2017
  let existingMemory;
2018
2018
  try {
2019
- existingMemory = await collection.query.fetchObjectById(args.memory_id, {
2020
- returnProperties: ["user_id", "doc_type", "version"]
2021
- });
2019
+ existingMemory = await collection.query.fetchObjectById(args.memory_id);
2022
2020
  } catch (fetchError) {
2023
2021
  const fetchErrorMsg = fetchError instanceof Error ? fetchError.message : String(fetchError);
2024
2022
  logger.error("Failed to fetch memory for update:", {
@@ -2029,7 +2027,7 @@ async function handleUpdateMemory(args, userId) {
2029
2027
  });
2030
2028
  throw new Error(`Failed to fetch memory ${args.memory_id}: ${fetchErrorMsg}`);
2031
2029
  }
2032
- if (!existingMemory) {
2030
+ if (!existingMemory || !existingMemory.properties) {
2033
2031
  throw new Error(`Memory not found: ${args.memory_id}. It may have been deleted or never existed.`);
2034
2032
  }
2035
2033
  if (existingMemory.properties.user_id !== userId) {
@@ -2102,26 +2100,31 @@ async function handleUpdateMemory(args, userId) {
2102
2100
  const now = (/* @__PURE__ */ new Date()).toISOString();
2103
2101
  updates.updated_at = now;
2104
2102
  updates.version = existingMemory.properties.version + 1;
2105
- logger.info("Calling Weaviate update", {
2103
+ const mergedProperties = {
2104
+ ...existingMemory.properties,
2105
+ ...updates
2106
+ };
2107
+ logger.info("Calling Weaviate replace", {
2106
2108
  userId,
2107
2109
  memoryId: args.memory_id,
2108
2110
  updateFields: Object.keys(updates),
2109
2111
  updateValues: updates,
2110
- collectionName: `Memory_${userId}`
2112
+ collectionName: `Memory_${userId}`,
2113
+ totalProperties: Object.keys(mergedProperties).length
2111
2114
  });
2112
2115
  try {
2113
- await collection.data.update({
2116
+ await collection.data.replace({
2114
2117
  id: args.memory_id,
2115
- properties: updates
2118
+ properties: mergedProperties
2116
2119
  });
2117
- logger.info("Weaviate update completed (no error thrown)", {
2120
+ logger.info("Weaviate replace completed (no error thrown)", {
2118
2121
  userId,
2119
2122
  memoryId: args.memory_id,
2120
2123
  updatedFields: Object.keys(updates)
2121
2124
  });
2122
2125
  } catch (updateError) {
2123
2126
  const updateErrorMsg = updateError instanceof Error ? updateError.message : String(updateError);
2124
- logger.error("Failed to perform Weaviate update:", {
2127
+ logger.error("Failed to perform Weaviate replace:", {
2125
2128
  error: updateErrorMsg,
2126
2129
  userId,
2127
2130
  memoryId: args.memory_id,
@@ -3752,283 +3755,11 @@ init_space_memory();
3752
3755
  init_logger();
3753
3756
  import weaviate3 from "weaviate-client";
3754
3757
  var PUBLIC_COLLECTION_NAME = "Memory_public";
3755
- function getSpaceCollectionName(spaceId) {
3756
- return `Memory_${spaceId}`;
3757
- }
3758
3758
  function isValidSpaceId(spaceId) {
3759
3759
  return SUPPORTED_SPACES.includes(spaceId);
3760
3760
  }
3761
- async function createSpaceCollection(client2, spaceId) {
3762
- const collectionName = spaceId === "public" ? PUBLIC_COLLECTION_NAME : getSpaceCollectionName(spaceId);
3763
- logger.info("Creating space collection", {
3764
- module: "weaviate-space-schema",
3765
- collectionName,
3766
- spaceId
3767
- });
3768
- await client2.collections.create({
3769
- name: collectionName,
3770
- // Vectorizer configuration
3771
- vectorizers: weaviate3.configure.vectorizer.text2VecOpenAI({
3772
- model: "text-embedding-3-small",
3773
- // Vectorize content for semantic search
3774
- sourceProperties: ["content", "observation"]
3775
- }),
3776
- properties: [
3777
- // Discriminator
3778
- {
3779
- name: "doc_type",
3780
- dataType: "text",
3781
- description: 'Document type: "space_memory"'
3782
- },
3783
- // Space identity
3784
- {
3785
- name: "spaces",
3786
- dataType: "text[]",
3787
- description: 'Spaces this memory is published to (e.g., ["the_void", "dogs"])'
3788
- },
3789
- {
3790
- name: "space_id",
3791
- dataType: "text",
3792
- description: "DEPRECATED: Use spaces array instead. Will be removed in v3.0.0."
3793
- },
3794
- {
3795
- name: "author_id",
3796
- dataType: "text",
3797
- description: "Original author user_id (for permissions)"
3798
- },
3799
- {
3800
- name: "ghost_id",
3801
- dataType: "text",
3802
- description: "Optional ghost profile ID for pseudonymous publishing"
3803
- },
3804
- {
3805
- name: "attribution",
3806
- dataType: "text",
3807
- description: 'Attribution type: "user" or "ghost"'
3808
- },
3809
- // Discovery metadata
3810
- {
3811
- name: "published_at",
3812
- dataType: "text",
3813
- description: "When published to space (ISO 8601)"
3814
- },
3815
- {
3816
- name: "discovery_count",
3817
- dataType: "number",
3818
- description: "How many times discovered"
3819
- },
3820
- // Memory fields (same as personal memories)
3821
- {
3822
- name: "content",
3823
- dataType: "text",
3824
- description: "Main memory content (vectorized)"
3825
- },
3826
- {
3827
- name: "title",
3828
- dataType: "text",
3829
- description: "Optional short title"
3830
- },
3831
- {
3832
- name: "summary",
3833
- dataType: "text",
3834
- description: "Optional brief summary"
3835
- },
3836
- {
3837
- name: "type",
3838
- dataType: "text",
3839
- description: "Content type (note, event, person, etc.)"
3840
- },
3841
- // Scoring fields
3842
- {
3843
- name: "weight",
3844
- dataType: "number",
3845
- description: "Significance/priority (0-1)"
3846
- },
3847
- {
3848
- name: "trust",
3849
- dataType: "number",
3850
- description: "Access control level (0-1)"
3851
- },
3852
- {
3853
- name: "confidence",
3854
- dataType: "number",
3855
- description: "System confidence in accuracy (0-1)"
3856
- },
3857
- // Location fields (flattened) - MUST match user memory schema exactly
3858
- {
3859
- name: "location_gps_lat",
3860
- dataType: "number",
3861
- description: "GPS latitude"
3862
- },
3863
- {
3864
- name: "location_gps_lng",
3865
- dataType: "number",
3866
- description: "GPS longitude"
3867
- },
3868
- {
3869
- name: "location_address",
3870
- dataType: "text",
3871
- description: "Formatted address"
3872
- },
3873
- {
3874
- name: "location_city",
3875
- dataType: "text",
3876
- description: "City name"
3877
- },
3878
- {
3879
- name: "location_country",
3880
- dataType: "text",
3881
- description: "Country name"
3882
- },
3883
- {
3884
- name: "location_source",
3885
- dataType: "text",
3886
- description: "Location source (gps, ip, manual, etc.)"
3887
- },
3888
- // Context fields (flattened) - MUST match user memory schema exactly
3889
- {
3890
- name: "context_conversation_id",
3891
- dataType: "text",
3892
- description: "Conversation ID"
3893
- },
3894
- {
3895
- name: "context_summary",
3896
- dataType: "text",
3897
- description: "Brief context summary"
3898
- },
3899
- {
3900
- name: "context_timestamp",
3901
- dataType: "date",
3902
- description: "Context timestamp"
3903
- },
3904
- // Locale fields - MUST match user memory schema exactly
3905
- {
3906
- name: "locale_language",
3907
- dataType: "text",
3908
- description: "Language code (e.g., en, es, fr)"
3909
- },
3910
- {
3911
- name: "locale_timezone",
3912
- dataType: "text",
3913
- description: "Timezone (e.g., America/Los_Angeles)"
3914
- },
3915
- // Relationships - MUST match user memory schema exactly
3916
- {
3917
- name: "relationships",
3918
- dataType: "text[]",
3919
- description: "Array of relationship IDs"
3920
- },
3921
- // Access tracking - MUST match user memory schema exactly
3922
- {
3923
- name: "access_count",
3924
- dataType: "number",
3925
- description: "Total times accessed"
3926
- },
3927
- {
3928
- name: "last_accessed_at",
3929
- dataType: "date",
3930
- description: "Most recent access timestamp"
3931
- },
3932
- // Metadata - MUST match user memory schema exactly
3933
- {
3934
- name: "tags",
3935
- dataType: "text[]",
3936
- description: "Tags for organization"
3937
- },
3938
- {
3939
- name: "references",
3940
- dataType: "text[]",
3941
- description: "Source URLs"
3942
- },
3943
- {
3944
- name: "template_id",
3945
- dataType: "text",
3946
- description: "Template ID if using template"
3947
- },
3948
- // Relationship-specific fields (for relationships in public space)
3949
- {
3950
- name: "memory_ids",
3951
- dataType: "text[]",
3952
- description: "Connected memory IDs (for relationships)"
3953
- },
3954
- {
3955
- name: "relationship_type",
3956
- dataType: "text",
3957
- description: "Relationship type (for relationships)"
3958
- },
3959
- {
3960
- name: "observation",
3961
- dataType: "text",
3962
- description: "Relationship observation (vectorized)"
3963
- },
3964
- {
3965
- name: "strength",
3966
- dataType: "number",
3967
- description: "Relationship strength (0-1)"
3968
- },
3969
- // Computed fields - MUST match user memory schema exactly
3970
- {
3971
- name: "base_weight",
3972
- dataType: "number",
3973
- description: "User-specified weight"
3974
- },
3975
- {
3976
- name: "computed_weight",
3977
- dataType: "number",
3978
- description: "Calculated effective weight"
3979
- },
3980
- // User ID field (for backwards compatibility with spread operator)
3981
- {
3982
- name: "user_id",
3983
- dataType: "text",
3984
- description: "User ID (copied from original memory, same as author_id)"
3985
- },
3986
- // Timestamps
3987
- {
3988
- name: "created_at",
3989
- dataType: "text",
3990
- description: "Original creation timestamp (ISO 8601)"
3991
- },
3992
- {
3993
- name: "updated_at",
3994
- dataType: "text",
3995
- description: "Last update timestamp (ISO 8601)"
3996
- },
3997
- // Versioning
3998
- {
3999
- name: "version",
4000
- dataType: "number",
4001
- description: "Version number (increments on update)"
4002
- },
4003
- // Comment/threading fields (for threaded discussions in shared spaces)
4004
- {
4005
- name: "parent_id",
4006
- dataType: "text",
4007
- description: "ID of parent memory or comment (for threading)"
4008
- },
4009
- {
4010
- name: "thread_root_id",
4011
- dataType: "text",
4012
- description: "Root memory ID for fetching entire thread"
4013
- },
4014
- {
4015
- name: "moderation_flags",
4016
- dataType: "text[]",
4017
- description: 'Per-space moderation flags (format: "{space_id}:{flag_type}")'
4018
- }
4019
- ]
4020
- });
4021
- logger.info("Space collection created successfully", {
4022
- module: "weaviate-space-schema",
4023
- collectionName
4024
- });
4025
- }
4026
3761
  async function ensurePublicCollection(client2) {
4027
3762
  const collectionName = PUBLIC_COLLECTION_NAME;
4028
- const exists = await client2.collections.exists(collectionName);
4029
- if (!exists) {
4030
- await createSpaceCollection(client2, "public");
4031
- }
4032
3763
  return client2.collections.get(collectionName);
4033
3764
  }
4034
3765
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prmichaelsen/remember-mcp",
3
- "version": "2.6.13",
3
+ "version": "2.7.1",
4
4
  "description": "Multi-tenant memory system MCP server with vector search and relationships",
5
5
  "main": "dist/server.js",
6
6
  "type": "module",
@@ -130,14 +130,12 @@ export async function handleUpdateMemory(
130
130
 
131
131
  const collection = getMemoryCollection(userId);
132
132
 
133
- // Get existing memory to verify ownership and get current version
134
- // Only fetch minimal properties needed for validation - don't query optional properties
135
- // that may not exist on all records (causes Weaviate gRPC errors)
133
+ // Get existing memory - fetch ALL properties for replace operation
134
+ // We need the full object to use replace() instead of update()
135
+ // (Weaviate bug: update() only persists if vectorized fields change)
136
136
  let existingMemory;
137
137
  try {
138
- existingMemory = await collection.query.fetchObjectById(args.memory_id, {
139
- returnProperties: ['user_id', 'doc_type', 'version'],
140
- });
138
+ existingMemory = await collection.query.fetchObjectById(args.memory_id);
141
139
  } catch (fetchError) {
142
140
  const fetchErrorMsg = fetchError instanceof Error ? fetchError.message : String(fetchError);
143
141
  logger.error('Failed to fetch memory for update:', {
@@ -149,7 +147,7 @@ export async function handleUpdateMemory(
149
147
  throw new Error(`Failed to fetch memory ${args.memory_id}: ${fetchErrorMsg}`);
150
148
  }
151
149
 
152
- if (!existingMemory) {
150
+ if (!existingMemory || !existingMemory.properties) {
153
151
  throw new Error(`Memory not found: ${args.memory_id}. It may have been deleted or never existed.`);
154
152
  }
155
153
 
@@ -249,29 +247,37 @@ export async function handleUpdateMemory(
249
247
  updates.updated_at = now;
250
248
  updates.version = (existingMemory.properties.version as number) + 1;
251
249
 
252
- // Perform update in Weaviate
253
- logger.info('Calling Weaviate update', {
250
+ // Merge updates with existing properties
251
+ // Use replace() instead of update() due to Weaviate bug where update() only
252
+ // persists if vectorized fields (content/observation) are changed
253
+ const mergedProperties = {
254
+ ...existingMemory.properties,
255
+ ...updates,
256
+ };
257
+
258
+ logger.info('Calling Weaviate replace', {
254
259
  userId,
255
260
  memoryId: args.memory_id,
256
261
  updateFields: Object.keys(updates),
257
262
  updateValues: updates,
258
263
  collectionName: `Memory_${userId}`,
264
+ totalProperties: Object.keys(mergedProperties).length,
259
265
  });
260
266
 
261
267
  try {
262
- await collection.data.update({
268
+ await collection.data.replace({
263
269
  id: args.memory_id,
264
- properties: updates,
270
+ properties: mergedProperties,
265
271
  });
266
272
 
267
- logger.info('Weaviate update completed (no error thrown)', {
273
+ logger.info('Weaviate replace completed (no error thrown)', {
268
274
  userId,
269
275
  memoryId: args.memory_id,
270
276
  updatedFields: Object.keys(updates),
271
277
  });
272
278
  } catch (updateError) {
273
279
  const updateErrorMsg = updateError instanceof Error ? updateError.message : String(updateError);
274
- logger.error('Failed to perform Weaviate update:', {
280
+ logger.error('Failed to perform Weaviate replace:', {
275
281
  error: updateErrorMsg,
276
282
  userId,
277
283
  memoryId: args.memory_id,
@@ -383,12 +383,16 @@ export async function ensurePublicCollection(
383
383
  ): Promise<Collection<any>> {
384
384
  const collectionName = PUBLIC_COLLECTION_NAME;
385
385
 
386
- // Check if collection exists
387
- const exists = await client.collections.exists(collectionName);
388
-
389
- if (!exists) {
390
- await createSpaceCollection(client, 'public');
391
- }
386
+ // DON'T manually create the collection!
387
+ // Let Weaviate auto-generate the schema from the first insert.
388
+ // This ensures the schema matches user memory collections exactly,
389
+ // including nested objects (context, location) that were auto-generated.
390
+ //
391
+ // When we spread {...originalMemory.properties}, the nested objects
392
+ // will be preserved and Weaviate will auto-create matching schema.
393
+ //
394
+ // NOTE: Collection will be created automatically on first insert.
395
+ // Weaviate's auto-schema feature handles this transparently.
392
396
 
393
397
  return client.collections.get(collectionName);
394
398
  }