@prmichaelsen/remember-mcp 2.3.1 → 2.5.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.
Files changed (34) hide show
  1. package/AGENT.md +20 -2
  2. package/CHANGELOG.md +69 -0
  3. package/README.md +50 -5
  4. package/agent/commands/acp.commit.md +511 -0
  5. package/agent/design/comment-memory-type.md +556 -0
  6. package/agent/design/unified-public-collection.md +545 -0
  7. package/agent/milestones/milestone-11-unified-public-collection.md +205 -0
  8. package/agent/progress.yaml +26 -0
  9. package/agent/scripts/install.sh +25 -1
  10. package/agent/scripts/update.sh +37 -0
  11. package/agent/tasks/task-46-update-spacememory-types.md +94 -0
  12. package/agent/tasks/task-47-update-space-schema.md +102 -0
  13. package/agent/tasks/task-48-create-memory-public-collection.md +96 -0
  14. package/agent/tasks/task-49-update-remember-publish.md +153 -0
  15. package/agent/tasks/task-50-update-remember-confirm.md +111 -0
  16. package/agent/tasks/task-51-update-remember-search-space.md +154 -0
  17. package/agent/tasks/task-52-update-remember-query-space.md +142 -0
  18. package/agent/tasks/task-53-add-multispace-tests.md +193 -0
  19. package/agent/tasks/task-54-update-documentation-multispace.md +191 -0
  20. package/dist/server-factory.js +140 -83
  21. package/dist/server.js +140 -83
  22. package/dist/tools/publish.d.ts +1 -1
  23. package/dist/tools/query-space.d.ts +1 -1
  24. package/dist/tools/search-space.d.ts +1 -1
  25. package/dist/types/space-memory.d.ts +5 -3
  26. package/dist/weaviate/space-schema.d.ts +16 -0
  27. package/package.json +1 -1
  28. package/src/tools/confirm.ts +20 -19
  29. package/src/tools/publish.ts +42 -20
  30. package/src/tools/query-space.ts +38 -24
  31. package/src/tools/search-space.ts +51 -28
  32. package/src/types/space-memory.ts +5 -3
  33. package/src/weaviate/space-schema.spec.ts +55 -0
  34. package/src/weaviate/space-schema.ts +45 -6
package/dist/server.js CHANGED
@@ -3446,6 +3446,7 @@ var confirmationTokenService = new ConfirmationTokenService();
3446
3446
  // src/weaviate/space-schema.ts
3447
3447
  init_space_memory();
3448
3448
  import weaviate3 from "weaviate-client";
3449
+ var PUBLIC_COLLECTION_NAME = "Memory_public";
3449
3450
  function getSpaceCollectionName(spaceId) {
3450
3451
  return `Memory_${spaceId}`;
3451
3452
  }
@@ -3453,7 +3454,7 @@ function isValidSpaceId(spaceId) {
3453
3454
  return SUPPORTED_SPACES.includes(spaceId);
3454
3455
  }
3455
3456
  async function createSpaceCollection(client2, spaceId) {
3456
- const collectionName = getSpaceCollectionName(spaceId);
3457
+ const collectionName = spaceId === "public" ? PUBLIC_COLLECTION_NAME : getSpaceCollectionName(spaceId);
3457
3458
  console.log(`[Weaviate] Creating space collection ${collectionName}...`);
3458
3459
  await client2.collections.create({
3459
3460
  name: collectionName,
@@ -3471,10 +3472,15 @@ async function createSpaceCollection(client2, spaceId) {
3471
3472
  description: 'Document type: "space_memory"'
3472
3473
  },
3473
3474
  // Space identity
3475
+ {
3476
+ name: "spaces",
3477
+ dataType: "text[]",
3478
+ description: 'Spaces this memory is published to (e.g., ["the_void", "dogs"])'
3479
+ },
3474
3480
  {
3475
3481
  name: "space_id",
3476
3482
  dataType: "text",
3477
- description: 'Space identifier (e.g., "the_void")'
3483
+ description: "DEPRECATED: Use spaces array instead. Will be removed in v3.0.0."
3478
3484
  },
3479
3485
  {
3480
3486
  name: "author_id",
@@ -3608,14 +3614,11 @@ async function createSpaceCollection(client2, spaceId) {
3608
3614
  });
3609
3615
  console.log(`[Weaviate] Space collection ${collectionName} created successfully`);
3610
3616
  }
3611
- async function ensureSpaceCollection(client2, spaceId) {
3612
- if (!isValidSpaceId(spaceId)) {
3613
- throw new Error(`Invalid space ID: ${spaceId}. Supported spaces: ${SUPPORTED_SPACES.join(", ")}`);
3614
- }
3615
- const collectionName = getSpaceCollectionName(spaceId);
3617
+ async function ensurePublicCollection(client2) {
3618
+ const collectionName = PUBLIC_COLLECTION_NAME;
3616
3619
  const exists = await client2.collections.exists(collectionName);
3617
3620
  if (!exists) {
3618
- await createSpaceCollection(client2, spaceId);
3621
+ await createSpaceCollection(client2, "public");
3619
3622
  }
3620
3623
  return client2.collections.get(collectionName);
3621
3624
  }
@@ -3624,7 +3627,7 @@ async function ensureSpaceCollection(client2, spaceId) {
3624
3627
  init_space_memory();
3625
3628
  var publishTool = {
3626
3629
  name: "remember_publish",
3627
- description: 'Publish a memory to a shared space (like "The Void"). The memory will be COPIED (not moved) from your personal collection. Generates a confirmation token. Use remember_confirm to execute.',
3630
+ description: 'Publish a memory to one or more shared spaces (like "The Void"). The memory will be COPIED (not moved) from your personal collection. Generates a confirmation token. Use remember_confirm to execute.',
3628
3631
  inputSchema: {
3629
3632
  type: "object",
3630
3633
  properties: {
@@ -3632,11 +3635,15 @@ var publishTool = {
3632
3635
  type: "string",
3633
3636
  description: "ID of the memory from your personal collection to publish"
3634
3637
  },
3635
- target: {
3636
- type: "string",
3637
- description: "Target space to publish to (snake_case ID)",
3638
- enum: SUPPORTED_SPACES,
3639
- default: "the_void"
3638
+ spaces: {
3639
+ type: "array",
3640
+ items: {
3641
+ type: "string",
3642
+ enum: SUPPORTED_SPACES
3643
+ },
3644
+ description: 'Spaces to publish to (e.g., ["the_void", "dogs"]). Can publish to multiple spaces at once.',
3645
+ minItems: 1,
3646
+ default: ["the_void"]
3640
3647
  },
3641
3648
  additional_tags: {
3642
3649
  type: "array",
@@ -3645,7 +3652,7 @@ var publishTool = {
3645
3652
  default: []
3646
3653
  }
3647
3654
  },
3648
- required: ["memory_id", "target"]
3655
+ required: ["memory_id", "spaces"]
3649
3656
  }
3650
3657
  };
3651
3658
  async function handlePublish(args, userId) {
@@ -3653,18 +3660,21 @@ async function handlePublish(args, userId) {
3653
3660
  console.log("[remember_publish] Starting publish request:", {
3654
3661
  userId,
3655
3662
  memoryId: args.memory_id,
3656
- target: args.target,
3663
+ spaces: args.spaces,
3664
+ spaceCount: args.spaces.length,
3657
3665
  additionalTags: args.additional_tags?.length || 0
3658
3666
  });
3659
- if (!isValidSpaceId(args.target)) {
3660
- console.log("[remember_publish] Invalid space ID:", args.target);
3667
+ const invalidSpaces = args.spaces.filter((s) => !isValidSpaceId(s));
3668
+ if (invalidSpaces.length > 0) {
3669
+ console.log("[remember_publish] Invalid space IDs:", invalidSpaces);
3661
3670
  return JSON.stringify(
3662
3671
  {
3663
3672
  success: false,
3664
- error: "Invalid space ID",
3665
- message: `Space "${args.target}" is not supported. Supported spaces: ${SUPPORTED_SPACES.join(", ")}`,
3673
+ error: "Invalid space IDs",
3674
+ message: `Invalid spaces: ${invalidSpaces.join(", ")}. Supported spaces: ${SUPPORTED_SPACES.join(", ")}`,
3666
3675
  context: {
3667
- provided_space: args.target,
3676
+ invalid_spaces: invalidSpaces,
3677
+ provided_spaces: args.spaces,
3668
3678
  supported_spaces: SUPPORTED_SPACES
3669
3679
  }
3670
3680
  },
@@ -3672,6 +3682,18 @@ async function handlePublish(args, userId) {
3672
3682
  2
3673
3683
  );
3674
3684
  }
3685
+ if (args.spaces.length === 0) {
3686
+ console.log("[remember_publish] Empty spaces array");
3687
+ return JSON.stringify(
3688
+ {
3689
+ success: false,
3690
+ error: "Empty spaces array",
3691
+ message: "Must specify at least one space to publish to"
3692
+ },
3693
+ null,
3694
+ 2
3695
+ );
3696
+ }
3675
3697
  const weaviateClient = getWeaviateClient();
3676
3698
  const collectionName = getMemoryCollectionName(userId);
3677
3699
  console.log("[remember_publish] Fetching memory from collection:", collectionName);
@@ -3730,6 +3752,7 @@ async function handlePublish(args, userId) {
3730
3752
  }
3731
3753
  const payload = {
3732
3754
  memory_id: args.memory_id,
3755
+ spaces: args.spaces,
3733
3756
  additional_tags: args.additional_tags || []
3734
3757
  };
3735
3758
  console.log("[remember_publish] Generating confirmation token");
@@ -3737,7 +3760,8 @@ async function handlePublish(args, userId) {
3737
3760
  userId,
3738
3761
  "publish_memory",
3739
3762
  payload,
3740
- args.target
3763
+ void 0
3764
+ // No single target_collection anymore
3741
3765
  );
3742
3766
  console.log("[remember_publish] Token generated:", {
3743
3767
  requestId,
@@ -3753,12 +3777,12 @@ async function handlePublish(args, userId) {
3753
3777
  2
3754
3778
  );
3755
3779
  } catch (error) {
3756
- handleToolError(error, {
3780
+ return handleToolError(error, {
3757
3781
  toolName: "remember_publish",
3758
3782
  userId,
3759
3783
  operation: "publish memory",
3760
3784
  memory_id: args.memory_id,
3761
- target: args.target
3785
+ spaces: args.spaces
3762
3786
  });
3763
3787
  }
3764
3788
  }
@@ -3820,7 +3844,8 @@ async function executePublishMemory(request, userId) {
3820
3844
  console.log("[executePublishMemory] Starting execution:", {
3821
3845
  userId,
3822
3846
  memoryId: request.payload.memory_id,
3823
- targetSpace: request.target_collection
3847
+ spaces: request.payload.spaces,
3848
+ spaceCount: request.payload.spaces?.length || 0
3824
3849
  });
3825
3850
  const weaviateClient = getWeaviateClient();
3826
3851
  const userCollection = weaviateClient.collections.get(
@@ -3858,20 +3883,18 @@ async function executePublishMemory(request, userId) {
3858
3883
  2
3859
3884
  );
3860
3885
  }
3861
- console.log("[executePublishMemory] Ensuring space collection:", request.target_collection || "the_void");
3862
- const targetCollection = await ensureSpaceCollection(
3863
- weaviateClient,
3864
- request.target_collection || "the_void"
3865
- );
3866
- console.log("[executePublishMemory] Space collection ready");
3886
+ console.log("[executePublishMemory] Ensuring public collection");
3887
+ const publicCollection = await ensurePublicCollection(weaviateClient);
3888
+ console.log("[executePublishMemory] Public collection ready");
3867
3889
  const originalTags = Array.isArray(originalMemory.properties.tags) ? originalMemory.properties.tags : [];
3868
3890
  const additionalTags = Array.isArray(request.payload.additional_tags) ? request.payload.additional_tags : [];
3869
3891
  const publishedMemory = {
3870
3892
  ...originalMemory.properties,
3871
- // Override specific fields
3872
- space_id: request.target_collection || "the_void",
3893
+ // Add space-specific fields
3894
+ spaces: request.payload.spaces || ["the_void"],
3895
+ // ✅ Array of spaces!
3873
3896
  author_id: userId,
3874
- // Always attributed
3897
+ // Track original author
3875
3898
  published_at: (/* @__PURE__ */ new Date()).toISOString(),
3876
3899
  discovery_count: 0,
3877
3900
  doc_type: "space_memory",
@@ -3883,14 +3906,14 @@ async function executePublishMemory(request, userId) {
3883
3906
  updated_at: (/* @__PURE__ */ new Date()).toISOString(),
3884
3907
  version: 1
3885
3908
  };
3886
- console.log("[executePublishMemory] Inserting into space collection:", {
3887
- spaceId: request.target_collection || "the_void",
3909
+ console.log("[executePublishMemory] Inserting into Memory_public:", {
3910
+ spaces: request.payload.spaces,
3911
+ spaceCount: request.payload.spaces?.length || 0,
3888
3912
  memoryId: request.payload.memory_id,
3889
- hasProperties: !!publishedMemory
3890
- });
3891
- const result = await targetCollection.data.insert({
3892
- properties: publishedMemory
3913
+ hasUserId: !!publishedMemory.user_id,
3914
+ hasAuthorId: !!publishedMemory.author_id
3893
3915
  });
3916
+ const result = await publicCollection.data.insert(publishedMemory);
3894
3917
  console.log("[executePublishMemory] Insert result:", {
3895
3918
  success: !!result,
3896
3919
  spaceMemoryId: result
@@ -3898,7 +3921,8 @@ async function executePublishMemory(request, userId) {
3898
3921
  return JSON.stringify(
3899
3922
  {
3900
3923
  success: true,
3901
- space_memory_id: result
3924
+ space_memory_id: result,
3925
+ spaces: request.payload.spaces || ["the_void"]
3902
3926
  },
3903
3927
  null,
3904
3928
  2
@@ -3964,7 +3988,7 @@ import { Filters as Filters3 } from "weaviate-client";
3964
3988
  init_space_memory();
3965
3989
  var searchSpaceTool = {
3966
3990
  name: "remember_search_space",
3967
- description: "Search shared spaces to discover thoughts, ideas, and memories. Works like remember_search_memory but searches shared spaces instead of personal memories.",
3991
+ description: "Search one or more shared spaces to discover thoughts, ideas, and memories. Works like remember_search_memory but searches shared spaces instead of personal memories. Can search multiple spaces in a single query.",
3968
3992
  inputSchema: {
3969
3993
  type: "object",
3970
3994
  properties: {
@@ -3972,11 +3996,15 @@ var searchSpaceTool = {
3972
3996
  type: "string",
3973
3997
  description: "Search query (semantic + keyword hybrid)"
3974
3998
  },
3975
- space: {
3976
- type: "string",
3977
- description: "Which space to search",
3978
- enum: SUPPORTED_SPACES,
3979
- default: "the_void"
3999
+ spaces: {
4000
+ type: "array",
4001
+ items: {
4002
+ type: "string",
4003
+ enum: SUPPORTED_SPACES
4004
+ },
4005
+ description: 'Spaces to search (e.g., ["the_void", "dogs"]). Can search multiple spaces at once.',
4006
+ minItems: 1,
4007
+ default: ["the_void"]
3980
4008
  },
3981
4009
  content_type: {
3982
4010
  type: "string",
@@ -4018,49 +4046,66 @@ var searchSpaceTool = {
4018
4046
  description: "Offset for pagination"
4019
4047
  }
4020
4048
  },
4021
- required: ["query", "space"]
4049
+ required: ["query", "spaces"]
4022
4050
  }
4023
4051
  };
4024
4052
  async function handleSearchSpace(args, userId) {
4025
4053
  try {
4026
- if (!isValidSpaceId(args.space)) {
4054
+ const invalidSpaces = args.spaces.filter((s) => !isValidSpaceId(s));
4055
+ if (invalidSpaces.length > 0) {
4027
4056
  return JSON.stringify(
4028
4057
  {
4029
4058
  success: false,
4030
- error: "Invalid space ID",
4031
- message: `Space "${args.space}" is not supported. Supported spaces: ${SUPPORTED_SPACES.join(", ")}`
4059
+ error: "Invalid space IDs",
4060
+ message: `Invalid spaces: ${invalidSpaces.join(", ")}. Supported spaces: ${SUPPORTED_SPACES.join(", ")}`,
4061
+ context: {
4062
+ invalid_spaces: invalidSpaces,
4063
+ provided_spaces: args.spaces,
4064
+ supported_spaces: SUPPORTED_SPACES
4065
+ }
4066
+ },
4067
+ null,
4068
+ 2
4069
+ );
4070
+ }
4071
+ if (args.spaces.length === 0) {
4072
+ return JSON.stringify(
4073
+ {
4074
+ success: false,
4075
+ error: "Empty spaces array",
4076
+ message: "Must specify at least one space to search"
4032
4077
  },
4033
4078
  null,
4034
4079
  2
4035
4080
  );
4036
4081
  }
4037
4082
  const weaviateClient = getWeaviateClient();
4038
- const spaceCollection = await ensureSpaceCollection(weaviateClient, args.space);
4083
+ const publicCollection = await ensurePublicCollection(weaviateClient);
4039
4084
  const filterList = [];
4040
- filterList.push(spaceCollection.filter.byProperty("space_id").equal(args.space));
4041
- filterList.push(spaceCollection.filter.byProperty("doc_type").equal("space_memory"));
4085
+ filterList.push(publicCollection.filter.byProperty("spaces").containsAny(args.spaces));
4086
+ filterList.push(publicCollection.filter.byProperty("doc_type").equal("space_memory"));
4042
4087
  if (args.content_type) {
4043
- filterList.push(spaceCollection.filter.byProperty("type").equal(args.content_type));
4088
+ filterList.push(publicCollection.filter.byProperty("type").equal(args.content_type));
4044
4089
  }
4045
4090
  if (args.tags && args.tags.length > 0) {
4046
4091
  args.tags.forEach((tag) => {
4047
- filterList.push(spaceCollection.filter.byProperty("tags").containsAny([tag]));
4092
+ filterList.push(publicCollection.filter.byProperty("tags").containsAny([tag]));
4048
4093
  });
4049
4094
  }
4050
4095
  if (args.min_weight !== void 0) {
4051
- filterList.push(spaceCollection.filter.byProperty("weight").greaterOrEqual(args.min_weight));
4096
+ filterList.push(publicCollection.filter.byProperty("weight").greaterOrEqual(args.min_weight));
4052
4097
  }
4053
4098
  if (args.max_weight !== void 0) {
4054
- filterList.push(spaceCollection.filter.byProperty("weight").lessOrEqual(args.max_weight));
4099
+ filterList.push(publicCollection.filter.byProperty("weight").lessOrEqual(args.max_weight));
4055
4100
  }
4056
4101
  if (args.date_from) {
4057
- filterList.push(spaceCollection.filter.byProperty("created_at").greaterOrEqual(new Date(args.date_from)));
4102
+ filterList.push(publicCollection.filter.byProperty("created_at").greaterOrEqual(new Date(args.date_from)));
4058
4103
  }
4059
4104
  if (args.date_to) {
4060
- filterList.push(spaceCollection.filter.byProperty("created_at").lessOrEqual(new Date(args.date_to)));
4105
+ filterList.push(publicCollection.filter.byProperty("created_at").lessOrEqual(new Date(args.date_to)));
4061
4106
  }
4062
4107
  const whereFilter = filterList.length > 0 ? Filters3.and(...filterList) : void 0;
4063
- const searchResults = await spaceCollection.query.hybrid(args.query, {
4108
+ const searchResults = await publicCollection.query.hybrid(args.query, {
4064
4109
  limit: args.limit || 10,
4065
4110
  offset: args.offset || 0,
4066
4111
  ...whereFilter && { where: whereFilter }
@@ -4071,7 +4116,7 @@ async function handleSearchSpace(args, userId) {
4071
4116
  _score: obj.metadata?.score
4072
4117
  }));
4073
4118
  const result = {
4074
- space: args.space,
4119
+ spaces_searched: args.spaces,
4075
4120
  query: args.query,
4076
4121
  memories,
4077
4122
  total: memories.length,
@@ -4080,10 +4125,10 @@ async function handleSearchSpace(args, userId) {
4080
4125
  };
4081
4126
  return JSON.stringify(result, null, 2);
4082
4127
  } catch (error) {
4083
- handleToolError(error, {
4128
+ return handleToolError(error, {
4084
4129
  toolName: "remember_search_space",
4085
- operation: "search space",
4086
- space: args.space,
4130
+ operation: "search spaces",
4131
+ spaces: args.spaces,
4087
4132
  query: args.query
4088
4133
  });
4089
4134
  }
@@ -4143,46 +4188,58 @@ var querySpaceTool = {
4143
4188
  description: "Output format: detailed (full objects) or compact (text summary)"
4144
4189
  }
4145
4190
  },
4146
- required: ["question", "space"]
4191
+ required: ["question", "spaces"]
4147
4192
  }
4148
4193
  };
4149
4194
  async function handleQuerySpace(args, userId) {
4150
4195
  try {
4151
- if (!isValidSpaceId(args.space)) {
4196
+ const invalidSpaces = args.spaces.filter((s) => !isValidSpaceId(s));
4197
+ if (invalidSpaces.length > 0) {
4152
4198
  return JSON.stringify(
4153
4199
  {
4154
4200
  success: false,
4155
- error: "Invalid space ID",
4156
- message: `Space "${args.space}" is not supported. Supported spaces: ${SUPPORTED_SPACES.join(", ")}`
4201
+ error: "Invalid space IDs",
4202
+ message: `Invalid spaces: ${invalidSpaces.join(", ")}. Supported spaces: ${SUPPORTED_SPACES.join(", ")}`
4203
+ },
4204
+ null,
4205
+ 2
4206
+ );
4207
+ }
4208
+ if (args.spaces.length === 0) {
4209
+ return JSON.stringify(
4210
+ {
4211
+ success: false,
4212
+ error: "Empty spaces array",
4213
+ message: "Must specify at least one space to query"
4157
4214
  },
4158
4215
  null,
4159
4216
  2
4160
4217
  );
4161
4218
  }
4162
4219
  const weaviateClient = getWeaviateClient();
4163
- const spaceCollection = await ensureSpaceCollection(weaviateClient, args.space);
4220
+ const publicCollection = await ensurePublicCollection(weaviateClient);
4164
4221
  const filterList = [];
4165
- filterList.push(spaceCollection.filter.byProperty("space_id").equal(args.space));
4166
- filterList.push(spaceCollection.filter.byProperty("doc_type").equal("space_memory"));
4222
+ filterList.push(publicCollection.filter.byProperty("spaces").containsAny(args.spaces));
4223
+ filterList.push(publicCollection.filter.byProperty("doc_type").equal("space_memory"));
4167
4224
  if (args.content_type) {
4168
- filterList.push(spaceCollection.filter.byProperty("type").equal(args.content_type));
4225
+ filterList.push(publicCollection.filter.byProperty("type").equal(args.content_type));
4169
4226
  }
4170
4227
  if (args.tags && args.tags.length > 0) {
4171
4228
  args.tags.forEach((tag) => {
4172
- filterList.push(spaceCollection.filter.byProperty("tags").containsAny([tag]));
4229
+ filterList.push(publicCollection.filter.byProperty("tags").containsAny([tag]));
4173
4230
  });
4174
4231
  }
4175
4232
  if (args.min_weight !== void 0) {
4176
- filterList.push(spaceCollection.filter.byProperty("weight").greaterOrEqual(args.min_weight));
4233
+ filterList.push(publicCollection.filter.byProperty("weight").greaterOrEqual(args.min_weight));
4177
4234
  }
4178
4235
  if (args.date_from) {
4179
- filterList.push(spaceCollection.filter.byProperty("created_at").greaterOrEqual(new Date(args.date_from)));
4236
+ filterList.push(publicCollection.filter.byProperty("created_at").greaterOrEqual(new Date(args.date_from)));
4180
4237
  }
4181
4238
  if (args.date_to) {
4182
- filterList.push(spaceCollection.filter.byProperty("created_at").lessOrEqual(new Date(args.date_to)));
4239
+ filterList.push(publicCollection.filter.byProperty("created_at").lessOrEqual(new Date(args.date_to)));
4183
4240
  }
4184
4241
  const whereFilter = filterList.length > 0 ? Filters4.and(...filterList) : void 0;
4185
- const searchResults = await spaceCollection.query.nearText(args.question, {
4242
+ const searchResults = await publicCollection.query.nearText(args.question, {
4186
4243
  limit: args.limit || 10,
4187
4244
  ...whereFilter && { where: whereFilter }
4188
4245
  });
@@ -4194,7 +4251,7 @@ async function handleQuerySpace(args, userId) {
4194
4251
  });
4195
4252
  const result = {
4196
4253
  question: args.question,
4197
- space: args.space,
4254
+ spaces_queried: args.spaces,
4198
4255
  format: "compact",
4199
4256
  summary: summaries.join("\n"),
4200
4257
  count: searchResults.objects.length
@@ -4208,7 +4265,7 @@ async function handleQuerySpace(args, userId) {
4208
4265
  }));
4209
4266
  const result = {
4210
4267
  question: args.question,
4211
- space: args.space,
4268
+ spaces_queried: args.spaces,
4212
4269
  format: "detailed",
4213
4270
  memories,
4214
4271
  total: memories.length
@@ -4216,10 +4273,10 @@ async function handleQuerySpace(args, userId) {
4216
4273
  return JSON.stringify(result, null, 2);
4217
4274
  }
4218
4275
  } catch (error) {
4219
- handleToolError(error, {
4276
+ return handleToolError(error, {
4220
4277
  toolName: "remember_query_space",
4221
- operation: "query space",
4222
- space: args.space,
4278
+ operation: "query spaces",
4279
+ spaces: args.spaces,
4223
4280
  question: args.question
4224
4281
  });
4225
4282
  }
@@ -11,7 +11,7 @@ import type { Tool } from '@modelcontextprotocol/sdk/types.js';
11
11
  export declare const publishTool: Tool;
12
12
  interface PublishArgs {
13
13
  memory_id: string;
14
- target: string;
14
+ spaces: string[];
15
15
  additional_tags?: string[];
16
16
  }
17
17
  /**
@@ -11,7 +11,7 @@ import type { Tool } from '@modelcontextprotocol/sdk/types.js';
11
11
  export declare const querySpaceTool: Tool;
12
12
  interface QuerySpaceArgs {
13
13
  question: string;
14
- space: string;
14
+ spaces: string[];
15
15
  content_type?: string;
16
16
  tags?: string[];
17
17
  min_weight?: number;
@@ -11,7 +11,7 @@ import type { Tool } from '@modelcontextprotocol/sdk/types.js';
11
11
  export declare const searchSpaceTool: Tool;
12
12
  interface SearchSpaceArgs {
13
13
  query: string;
14
- space: string;
14
+ spaces: string[];
15
15
  content_type?: string;
16
16
  tags?: string[];
17
17
  min_weight?: number;
@@ -12,10 +12,12 @@ import type { Memory, SearchOptions, SearchResult } from './memory.js';
12
12
  */
13
13
  export interface SpaceMemory extends Omit<Memory, 'user_id' | 'doc_type'> {
14
14
  /**
15
- * Space identifier (snake_case)
16
- * Examples: 'the_void', 'public_space'
15
+ * Spaces this memory is published to (snake_case array)
16
+ * Examples: ['the_void'], ['dogs', 'cats'], ['the_void', 'dogs']
17
+ *
18
+ * A memory can belong to multiple spaces simultaneously.
17
19
  */
18
- space_id: string;
20
+ spaces: string[];
19
21
  /**
20
22
  * Original author's user_id (for permissions)
21
23
  * This is private and not shown publicly
@@ -5,9 +5,14 @@
5
5
  * for discovery by other users.
6
6
  */
7
7
  import { type WeaviateClient, type Collection } from 'weaviate-client';
8
+ /**
9
+ * Unified public collection name for all public spaces
10
+ */
11
+ export declare const PUBLIC_COLLECTION_NAME = "Memory_public";
8
12
  /**
9
13
  * Get collection name for a space
10
14
  *
15
+ * @deprecated Use PUBLIC_COLLECTION_NAME instead. Will be removed in v3.0.0.
11
16
  * @param spaceId - Space identifier (snake_case)
12
17
  * @returns Collection name in format Memory_{space_id}
13
18
  *
@@ -45,9 +50,20 @@ export declare function getSpaceDisplayName(spaceId: string): string;
45
50
  * @returns True if valid, false otherwise
46
51
  */
47
52
  export declare function isValidSpaceId(spaceId: string): boolean;
53
+ /**
54
+ * Ensure the unified public collection exists, creating it if needed
55
+ *
56
+ * @param client - Weaviate client
57
+ * @returns Collection reference to Memory_public
58
+ *
59
+ * @example
60
+ * const collection = await ensurePublicCollection(client);
61
+ */
62
+ export declare function ensurePublicCollection(client: WeaviateClient): Promise<Collection<any>>;
48
63
  /**
49
64
  * Ensure a space collection exists, creating it if needed
50
65
  *
66
+ * @deprecated Use ensurePublicCollection() instead. Will be removed in v3.0.0.
51
67
  * @param client - Weaviate client
52
68
  * @param spaceId - Space identifier
53
69
  * @returns Collection reference
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prmichaelsen/remember-mcp",
3
- "version": "2.3.1",
3
+ "version": "2.5.0",
4
4
  "description": "Multi-tenant memory system MCP server with vector search and relationships",
5
5
  "main": "dist/server.js",
6
6
  "type": "module",
@@ -8,7 +8,7 @@
8
8
  import type { Tool } from '@modelcontextprotocol/sdk/types.js';
9
9
  import { confirmationTokenService, type ConfirmationRequest } from '../services/confirmation-token.service.js';
10
10
  import { getWeaviateClient, getMemoryCollectionName } from '../weaviate/client.js';
11
- import { ensureSpaceCollection } from '../weaviate/space-schema.js';
11
+ import { ensurePublicCollection } from '../weaviate/space-schema.js';
12
12
  import { handleToolError } from '../utils/error-handler.js';
13
13
 
14
14
  /**
@@ -102,7 +102,8 @@ async function executePublishMemory(
102
102
  console.log('[executePublishMemory] Starting execution:', {
103
103
  userId,
104
104
  memoryId: request.payload.memory_id,
105
- targetSpace: request.target_collection,
105
+ spaces: request.payload.spaces,
106
+ spaceCount: request.payload.spaces?.length || 0,
106
107
  });
107
108
 
108
109
  // Fetch the memory NOW (during confirmation, not from stored payload)
@@ -149,15 +150,12 @@ async function executePublishMemory(
149
150
  );
150
151
  }
151
152
 
152
- console.log('[executePublishMemory] Ensuring space collection:', request.target_collection || 'the_void');
153
+ console.log('[executePublishMemory] Ensuring public collection');
153
154
 
154
- // Get target collection
155
- const targetCollection = await ensureSpaceCollection(
156
- weaviateClient,
157
- request.target_collection || 'the_void'
158
- );
155
+ // Get unified public collection
156
+ const publicCollection = await ensurePublicCollection(weaviateClient);
159
157
 
160
- console.log('[executePublishMemory] Space collection ready');
158
+ console.log('[executePublishMemory] Public collection ready');
161
159
 
162
160
  // Create published memory (copy with modifications)
163
161
  const originalTags = Array.isArray(originalMemory.properties.tags)
@@ -167,11 +165,12 @@ async function executePublishMemory(
167
165
  ? request.payload.additional_tags
168
166
  : [];
169
167
 
168
+ // Create published memory with space-specific fields
170
169
  const publishedMemory = {
171
170
  ...originalMemory.properties,
172
- // Override specific fields
173
- space_id: request.target_collection || 'the_void',
174
- author_id: userId, // Always attributed
171
+ // Add space-specific fields
172
+ spaces: request.payload.spaces || ['the_void'], // ✅ Array of spaces!
173
+ author_id: userId, // Track original author
175
174
  published_at: new Date().toISOString(),
176
175
  discovery_count: 0,
177
176
  doc_type: 'space_memory',
@@ -184,26 +183,28 @@ async function executePublishMemory(
184
183
  version: 1,
185
184
  };
186
185
 
187
- console.log('[executePublishMemory] Inserting into space collection:', {
188
- spaceId: request.target_collection || 'the_void',
186
+ console.log('[executePublishMemory] Inserting into Memory_public:', {
187
+ spaces: request.payload.spaces,
188
+ spaceCount: request.payload.spaces?.length || 0,
189
189
  memoryId: request.payload.memory_id,
190
- hasProperties: !!publishedMemory,
190
+ hasUserId: !!(publishedMemory as any).user_id,
191
+ hasAuthorId: !!publishedMemory.author_id,
191
192
  });
192
193
 
193
- const result = await targetCollection.data.insert({
194
- properties: publishedMemory as any,
195
- });
194
+ // Insert directly into unified public collection
195
+ const result = await publicCollection.data.insert(publishedMemory as any);
196
196
 
197
197
  console.log('[executePublishMemory] Insert result:', {
198
198
  success: !!result,
199
199
  spaceMemoryId: result,
200
200
  });
201
201
 
202
- // Return minimal response - agent already knows original memory
202
+ // Return minimal response with spaces array
203
203
  return JSON.stringify(
204
204
  {
205
205
  success: true,
206
206
  space_memory_id: result,
207
+ spaces: request.payload.spaces || ['the_void'],
207
208
  },
208
209
  null,
209
210
  2