@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.
- package/.github/workflows/publish.yml +1 -1
- package/CHANGELOG.md +7 -0
- package/dist/server-factory.e2e.d.ts +2 -0
- package/dist/server-factory.js +69 -32
- package/dist/server.js +69 -32
- package/package.json +2 -2
- package/dist/server-factory.spec.d.ts +0 -2
- /package/src/{server-factory.spec.ts → server-factory.e2e.ts} +0 -0
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
|
package/dist/server-factory.js
CHANGED
|
@@ -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
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
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.
|
|
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
|
+
"@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"
|
|
File without changes
|