@prmichaelsen/remember-mcp 3.17.0 → 3.17.2

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.
@@ -26,7 +26,7 @@ jobs:
26
26
  run: npm run build
27
27
 
28
28
  - name: Test
29
- run: node --max-old-space-size=4096 ./node_modules/.bin/jest --runInBand
29
+ run: npm test
30
30
 
31
31
  - name: Check if version changed
32
32
  id: version
package/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@ 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
+ ## [3.17.1] - 2026-03-09
9
+
10
+ ### Fixed
11
+
12
+ - Move `server-factory.spec.ts` to `server-factory.e2e.ts` — test requires live Weaviate and was crashing CI workers
13
+ - Revert CI test command to `npm test` (OOM was a symptom of the Weaviate connection failure, not memory)
14
+
8
15
  ## [3.17.0] - 2026-03-09
9
16
 
10
17
  ### Added
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=server-factory.e2e.d.ts.map
@@ -3786,6 +3786,49 @@ var RelationshipService = class {
3786
3786
  this.logger.error(`Failed to update relationship_count for ${memoryId}:`, { error: error?.message || String(error) });
3787
3787
  }
3788
3788
  }
3789
+ /**
3790
+ * Validate that memory IDs exist, belong to user, are memories, and not deleted.
3791
+ * Returns validated entries with their current relationship_ids.
3792
+ */
3793
+ async validateMemoryIds(memoryIds) {
3794
+ const checks = await Promise.all(memoryIds.map(async (memoryId) => {
3795
+ const memory = await this.collection.query.fetchObjectById(memoryId, {
3796
+ returnProperties: ["user_id", "doc_type", "relationship_ids", "deleted_at"]
3797
+ });
3798
+ if (!memory)
3799
+ return { memoryId, error: "Memory not found" };
3800
+ if (memory.properties.user_id !== this.userId)
3801
+ return { memoryId, error: "Unauthorized" };
3802
+ if (memory.properties.doc_type !== "memory")
3803
+ return { memoryId, error: "Not a memory document" };
3804
+ if (memory.properties.deleted_at)
3805
+ return { memoryId, error: "Memory is deleted" };
3806
+ return { memoryId, relationships: memory.properties.relationship_ids || [] };
3807
+ }));
3808
+ const errors = checks.filter((c) => "error" in c && !!c.error);
3809
+ if (errors.length > 0) {
3810
+ throw new Error(`Memory validation failed: ${errors.map((e) => `${e.memoryId}: ${e.error}`).join("; ")}`);
3811
+ }
3812
+ return checks;
3813
+ }
3814
+ /**
3815
+ * Add bidirectional relationship_ids links from memories to a relationship.
3816
+ */
3817
+ async linkMemoriesToRelationship(relationshipId, validated, now) {
3818
+ await Promise.all(validated.map(async (c) => {
3819
+ try {
3820
+ await this.collection.data.update({
3821
+ id: c.memoryId,
3822
+ properties: {
3823
+ relationship_ids: [...c.relationships, relationshipId],
3824
+ updated_at: now
3825
+ }
3826
+ });
3827
+ } catch {
3828
+ this.logger.warn(`Failed to update memory ${c.memoryId} with relationship`);
3829
+ }
3830
+ }));
3831
+ }
3789
3832
  // ── Get by ID ────────────────────────────────────────────────────────
3790
3833
  async getById(relationshipId) {
3791
3834
  const result = await this.collection.query.fetchObjectById(relationshipId, {
@@ -3817,24 +3860,7 @@ var RelationshipService = class {
3817
3860
  if (input.memory_ids.length < 2) {
3818
3861
  throw new Error("At least 2 memory IDs are required to create a relationship");
3819
3862
  }
3820
- const checks = await Promise.all(input.memory_ids.map(async (memoryId) => {
3821
- const memory = await this.collection.query.fetchObjectById(memoryId, {
3822
- returnProperties: ["user_id", "doc_type", "relationship_ids", "deleted_at"]
3823
- });
3824
- if (!memory)
3825
- return { memoryId, error: "Memory not found" };
3826
- if (memory.properties.user_id !== this.userId)
3827
- return { memoryId, error: "Unauthorized" };
3828
- if (memory.properties.doc_type !== "memory")
3829
- return { memoryId, error: "Not a memory document" };
3830
- if (memory.properties.deleted_at)
3831
- return { memoryId, error: "Memory is deleted" };
3832
- return { memoryId, memory, relationships: memory.properties.relationship_ids || [] };
3833
- }));
3834
- const errors = checks.filter((c) => c.error);
3835
- if (errors.length > 0) {
3836
- throw new Error(`Memory validation failed: ${errors.map((e) => `${e.memoryId}: ${e.error}`).join("; ")}`);
3837
- }
3863
+ const validated = await this.validateMemoryIds(input.memory_ids);
3838
3864
  const now = (/* @__PURE__ */ new Date()).toISOString();
3839
3865
  const properties = {
3840
3866
  user_id: this.userId,
@@ -3853,26 +3879,18 @@ var RelationshipService = class {
3853
3879
  };
3854
3880
  const relationshipId = await this.collection.data.insert({ properties });
3855
3881
  await Promise.all(input.memory_ids.map((memoryId) => this.updateRelationshipCount(memoryId, 1)));
3856
- await Promise.all(checks.filter((c) => !c.error && c.memory).map(async (c) => {
3857
- try {
3858
- await this.collection.data.update({
3859
- id: c.memoryId,
3860
- properties: {
3861
- relationship_ids: [...c.relationships || [], relationshipId],
3862
- updated_at: now
3863
- }
3864
- });
3865
- } catch {
3866
- this.logger.warn(`Failed to update memory ${c.memoryId} with relationship`);
3867
- }
3868
- }));
3882
+ await this.linkMemoriesToRelationship(relationshipId, validated, now);
3869
3883
  this.logger.info("Relationship created", { relationshipId, memoryCount: input.memory_ids.length });
3870
3884
  return { relationship_id: relationshipId, memory_ids: input.memory_ids, created_at: now };
3871
3885
  }
3872
3886
  // ── Update ──────────────────────────────────────────────────────────
3873
3887
  async update(input) {
3888
+ const fetchProps = ["user_id", "doc_type", "version"];
3889
+ const needMemoryIds = input.add_memory_ids && input.add_memory_ids.length > 0;
3890
+ if (needMemoryIds)
3891
+ fetchProps.push("related_memory_ids");
3874
3892
  const existing = await this.collection.query.fetchObjectById(input.relationship_id, {
3875
- returnProperties: ["user_id", "doc_type", "version"]
3893
+ returnProperties: fetchProps
3876
3894
  });
3877
3895
  if (!existing)
3878
3896
  throw new Error(`Relationship not found: ${input.relationship_id}`);
@@ -3906,12 +3924,31 @@ var RelationshipService = class {
3906
3924
  updates.tags = input.tags;
3907
3925
  updatedFields.push("tags");
3908
3926
  }
3927
+ let newMemoryIds = [];
3928
+ let validated = [];
3929
+ if (needMemoryIds) {
3930
+ const existingMemoryIds = new Set(existing.properties.related_memory_ids || []);
3931
+ newMemoryIds = input.add_memory_ids.filter((id) => !existingMemoryIds.has(id));
3932
+ if (newMemoryIds.length > 0) {
3933
+ validated = await this.validateMemoryIds(newMemoryIds);
3934
+ const allMemoryIds = [...existingMemoryIds, ...newMemoryIds];
3935
+ updates.related_memory_ids = allMemoryIds;
3936
+ updates.member_count = allMemoryIds.length;
3937
+ updatedFields.push("related_memory_ids", "member_count");
3938
+ }
3939
+ }
3909
3940
  if (updatedFields.length === 0)
3910
3941
  throw new Error("No fields provided for update");
3911
3942
  const now = (/* @__PURE__ */ new Date()).toISOString();
3912
3943
  updates.updated_at = now;
3913
3944
  updates.version = existing.properties.version + 1;
3914
3945
  await this.collection.data.update({ id: input.relationship_id, properties: updates });
3946
+ if (newMemoryIds.length > 0) {
3947
+ await Promise.all([
3948
+ this.linkMemoriesToRelationship(input.relationship_id, validated, now),
3949
+ ...newMemoryIds.map((memoryId) => this.updateRelationshipCount(memoryId, 1))
3950
+ ]);
3951
+ }
3915
3952
  this.logger.info("Relationship updated", { relationshipId: input.relationship_id, updatedFields });
3916
3953
  return {
3917
3954
  relationship_id: input.relationship_id,
package/dist/server.js CHANGED
@@ -3790,6 +3790,49 @@ var RelationshipService = class {
3790
3790
  this.logger.error(`Failed to update relationship_count for ${memoryId}:`, { error: error?.message || String(error) });
3791
3791
  }
3792
3792
  }
3793
+ /**
3794
+ * Validate that memory IDs exist, belong to user, are memories, and not deleted.
3795
+ * Returns validated entries with their current relationship_ids.
3796
+ */
3797
+ async validateMemoryIds(memoryIds) {
3798
+ const checks = await Promise.all(memoryIds.map(async (memoryId) => {
3799
+ const memory = await this.collection.query.fetchObjectById(memoryId, {
3800
+ returnProperties: ["user_id", "doc_type", "relationship_ids", "deleted_at"]
3801
+ });
3802
+ if (!memory)
3803
+ return { memoryId, error: "Memory not found" };
3804
+ if (memory.properties.user_id !== this.userId)
3805
+ return { memoryId, error: "Unauthorized" };
3806
+ if (memory.properties.doc_type !== "memory")
3807
+ return { memoryId, error: "Not a memory document" };
3808
+ if (memory.properties.deleted_at)
3809
+ return { memoryId, error: "Memory is deleted" };
3810
+ return { memoryId, relationships: memory.properties.relationship_ids || [] };
3811
+ }));
3812
+ const errors = checks.filter((c) => "error" in c && !!c.error);
3813
+ if (errors.length > 0) {
3814
+ throw new Error(`Memory validation failed: ${errors.map((e) => `${e.memoryId}: ${e.error}`).join("; ")}`);
3815
+ }
3816
+ return checks;
3817
+ }
3818
+ /**
3819
+ * Add bidirectional relationship_ids links from memories to a relationship.
3820
+ */
3821
+ async linkMemoriesToRelationship(relationshipId, validated, now) {
3822
+ await Promise.all(validated.map(async (c) => {
3823
+ try {
3824
+ await this.collection.data.update({
3825
+ id: c.memoryId,
3826
+ properties: {
3827
+ relationship_ids: [...c.relationships, relationshipId],
3828
+ updated_at: now
3829
+ }
3830
+ });
3831
+ } catch {
3832
+ this.logger.warn(`Failed to update memory ${c.memoryId} with relationship`);
3833
+ }
3834
+ }));
3835
+ }
3793
3836
  // ── Get by ID ────────────────────────────────────────────────────────
3794
3837
  async getById(relationshipId) {
3795
3838
  const result = await this.collection.query.fetchObjectById(relationshipId, {
@@ -3821,24 +3864,7 @@ var RelationshipService = class {
3821
3864
  if (input.memory_ids.length < 2) {
3822
3865
  throw new Error("At least 2 memory IDs are required to create a relationship");
3823
3866
  }
3824
- const checks = await Promise.all(input.memory_ids.map(async (memoryId) => {
3825
- const memory = await this.collection.query.fetchObjectById(memoryId, {
3826
- returnProperties: ["user_id", "doc_type", "relationship_ids", "deleted_at"]
3827
- });
3828
- if (!memory)
3829
- return { memoryId, error: "Memory not found" };
3830
- if (memory.properties.user_id !== this.userId)
3831
- return { memoryId, error: "Unauthorized" };
3832
- if (memory.properties.doc_type !== "memory")
3833
- return { memoryId, error: "Not a memory document" };
3834
- if (memory.properties.deleted_at)
3835
- return { memoryId, error: "Memory is deleted" };
3836
- return { memoryId, memory, relationships: memory.properties.relationship_ids || [] };
3837
- }));
3838
- const errors = checks.filter((c) => c.error);
3839
- if (errors.length > 0) {
3840
- throw new Error(`Memory validation failed: ${errors.map((e) => `${e.memoryId}: ${e.error}`).join("; ")}`);
3841
- }
3867
+ const validated = await this.validateMemoryIds(input.memory_ids);
3842
3868
  const now = (/* @__PURE__ */ new Date()).toISOString();
3843
3869
  const properties = {
3844
3870
  user_id: this.userId,
@@ -3857,26 +3883,18 @@ var RelationshipService = class {
3857
3883
  };
3858
3884
  const relationshipId = await this.collection.data.insert({ properties });
3859
3885
  await Promise.all(input.memory_ids.map((memoryId) => this.updateRelationshipCount(memoryId, 1)));
3860
- await Promise.all(checks.filter((c) => !c.error && c.memory).map(async (c) => {
3861
- try {
3862
- await this.collection.data.update({
3863
- id: c.memoryId,
3864
- properties: {
3865
- relationship_ids: [...c.relationships || [], relationshipId],
3866
- updated_at: now
3867
- }
3868
- });
3869
- } catch {
3870
- this.logger.warn(`Failed to update memory ${c.memoryId} with relationship`);
3871
- }
3872
- }));
3886
+ await this.linkMemoriesToRelationship(relationshipId, validated, now);
3873
3887
  this.logger.info("Relationship created", { relationshipId, memoryCount: input.memory_ids.length });
3874
3888
  return { relationship_id: relationshipId, memory_ids: input.memory_ids, created_at: now };
3875
3889
  }
3876
3890
  // ── Update ──────────────────────────────────────────────────────────
3877
3891
  async update(input) {
3892
+ const fetchProps = ["user_id", "doc_type", "version"];
3893
+ const needMemoryIds = input.add_memory_ids && input.add_memory_ids.length > 0;
3894
+ if (needMemoryIds)
3895
+ fetchProps.push("related_memory_ids");
3878
3896
  const existing = await this.collection.query.fetchObjectById(input.relationship_id, {
3879
- returnProperties: ["user_id", "doc_type", "version"]
3897
+ returnProperties: fetchProps
3880
3898
  });
3881
3899
  if (!existing)
3882
3900
  throw new Error(`Relationship not found: ${input.relationship_id}`);
@@ -3910,12 +3928,31 @@ var RelationshipService = class {
3910
3928
  updates.tags = input.tags;
3911
3929
  updatedFields.push("tags");
3912
3930
  }
3931
+ let newMemoryIds = [];
3932
+ let validated = [];
3933
+ if (needMemoryIds) {
3934
+ const existingMemoryIds = new Set(existing.properties.related_memory_ids || []);
3935
+ newMemoryIds = input.add_memory_ids.filter((id) => !existingMemoryIds.has(id));
3936
+ if (newMemoryIds.length > 0) {
3937
+ validated = await this.validateMemoryIds(newMemoryIds);
3938
+ const allMemoryIds = [...existingMemoryIds, ...newMemoryIds];
3939
+ updates.related_memory_ids = allMemoryIds;
3940
+ updates.member_count = allMemoryIds.length;
3941
+ updatedFields.push("related_memory_ids", "member_count");
3942
+ }
3943
+ }
3913
3944
  if (updatedFields.length === 0)
3914
3945
  throw new Error("No fields provided for update");
3915
3946
  const now = (/* @__PURE__ */ new Date()).toISOString();
3916
3947
  updates.updated_at = now;
3917
3948
  updates.version = existing.properties.version + 1;
3918
3949
  await this.collection.data.update({ id: input.relationship_id, properties: updates });
3950
+ if (newMemoryIds.length > 0) {
3951
+ await Promise.all([
3952
+ this.linkMemoriesToRelationship(input.relationship_id, validated, now),
3953
+ ...newMemoryIds.map((memoryId) => this.updateRelationshipCount(memoryId, 1))
3954
+ ]);
3955
+ }
3919
3956
  this.logger.info("Relationship updated", { relationshipId: input.relationship_id, updatedFields });
3920
3957
  return {
3921
3958
  relationship_id: input.relationship_id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prmichaelsen/remember-mcp",
3
- "version": "3.17.0",
3
+ "version": "3.17.2",
4
4
  "description": "Multi-tenant memory system MCP server with vector search and relationships",
5
5
  "main": "dist/server.js",
6
6
  "type": "module",
@@ -51,7 +51,7 @@
51
51
  "@google-cloud/vision": "^5.3.4",
52
52
  "@modelcontextprotocol/sdk": "^1.0.4",
53
53
  "@prmichaelsen/firebase-admin-sdk-v8": "^2.2.0",
54
- "@prmichaelsen/remember-core": "^0.54.0",
54
+ "@prmichaelsen/remember-core": "^0.55.0",
55
55
  "dotenv": "^16.4.5",
56
56
  "uuid": "^13.0.0",
57
57
  "weaviate-client": "^3.2.0"
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=server-factory.spec.d.ts.map