@prmichaelsen/remember-mcp 2.2.1 → 2.3.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 (61) hide show
  1. package/AGENT.md +4 -4
  2. package/CHANGELOG.md +45 -0
  3. package/README.md +43 -3
  4. package/agent/commands/acp.init.md +376 -0
  5. package/agent/commands/acp.proceed.md +311 -0
  6. package/agent/commands/acp.status.md +280 -0
  7. package/agent/commands/acp.version-check-for-updates.md +275 -0
  8. package/agent/commands/acp.version-check.md +190 -0
  9. package/agent/commands/acp.version-update.md +288 -0
  10. package/agent/commands/command.template.md +273 -0
  11. package/agent/design/core-memory-user-profile.md +1253 -0
  12. package/agent/design/ghost-profiles-pseudonymous-identity.md +194 -0
  13. package/agent/design/publish-tools-confirmation-flow.md +922 -0
  14. package/agent/milestones/milestone-10-shared-spaces.md +169 -0
  15. package/agent/progress.yaml +90 -4
  16. package/agent/scripts/install.sh +118 -0
  17. package/agent/scripts/update.sh +22 -10
  18. package/agent/scripts/version.sh +35 -0
  19. package/agent/tasks/task-27-implement-llm-provider-interface.md +51 -0
  20. package/agent/tasks/task-28-implement-llm-provider-factory.md +64 -0
  21. package/agent/tasks/task-29-update-config-for-llm.md +71 -0
  22. package/agent/tasks/task-30-implement-bedrock-provider.md +147 -0
  23. package/agent/tasks/task-31-implement-background-job-service.md +120 -0
  24. package/agent/tasks/task-32-test-llm-provider-integration.md +152 -0
  25. package/agent/tasks/task-34-create-confirmation-token-service.md +191 -0
  26. package/agent/tasks/task-35-create-space-memory-types-schema.md +183 -0
  27. package/agent/tasks/task-36-implement-remember-publish.md +227 -0
  28. package/agent/tasks/task-37-implement-remember-confirm.md +225 -0
  29. package/agent/tasks/task-38-implement-remember-deny.md +161 -0
  30. package/agent/tasks/task-39-implement-remember-search-space.md +188 -0
  31. package/agent/tasks/task-40-implement-remember-query-space.md +193 -0
  32. package/agent/tasks/task-41-configure-firestore-ttl.md +188 -0
  33. package/agent/tasks/task-42-create-tests-shared-spaces.md +216 -0
  34. package/agent/tasks/task-43-update-documentation.md +255 -0
  35. package/dist/llm/types.d.ts +1 -0
  36. package/dist/server-factory.js +914 -1
  37. package/dist/server.js +916 -3
  38. package/dist/services/confirmation-token.service.d.ts +99 -0
  39. package/dist/services/confirmation-token.service.spec.d.ts +5 -0
  40. package/dist/tools/confirm.d.ts +20 -0
  41. package/dist/tools/deny.d.ts +19 -0
  42. package/dist/tools/publish.d.ts +22 -0
  43. package/dist/tools/query-space.d.ts +28 -0
  44. package/dist/tools/search-space.d.ts +29 -0
  45. package/dist/types/space-memory.d.ts +80 -0
  46. package/dist/weaviate/space-schema.d.ts +59 -0
  47. package/dist/weaviate/space-schema.spec.d.ts +5 -0
  48. package/package.json +1 -1
  49. package/src/llm/types.ts +0 -0
  50. package/src/server-factory.ts +33 -0
  51. package/src/server.ts +33 -0
  52. package/src/services/confirmation-token.service.spec.ts +254 -0
  53. package/src/services/confirmation-token.service.ts +232 -0
  54. package/src/tools/confirm.ts +176 -0
  55. package/src/tools/deny.ts +70 -0
  56. package/src/tools/publish.ts +167 -0
  57. package/src/tools/query-space.ts +197 -0
  58. package/src/tools/search-space.ts +189 -0
  59. package/src/types/space-memory.ts +94 -0
  60. package/src/weaviate/space-schema.spec.ts +131 -0
  61. package/src/weaviate/space-schema.ts +275 -0
package/dist/server.js CHANGED
@@ -13,6 +13,9 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
13
13
  return require.apply(this, arguments);
14
14
  throw Error('Dynamic require of "' + x + '" is not supported');
15
15
  });
16
+ var __esm = (fn, res) => function __init() {
17
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
18
+ };
16
19
  var __commonJS = (cb, mod) => function __require2() {
17
20
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
18
21
  };
@@ -391,6 +394,15 @@ var require_main = __commonJS({
391
394
  }
392
395
  });
393
396
 
397
+ // src/types/space-memory.ts
398
+ var SUPPORTED_SPACES;
399
+ var init_space_memory = __esm({
400
+ "src/types/space-memory.ts"() {
401
+ "use strict";
402
+ SUPPORTED_SPACES = ["the_void"];
403
+ }
404
+ });
405
+
394
406
  // src/server.ts
395
407
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
396
408
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -503,6 +515,9 @@ function sanitizeUserId(userId) {
503
515
  }
504
516
  return sanitized.charAt(0).toUpperCase() + sanitized.slice(1);
505
517
  }
518
+ function getMemoryCollectionName(userId) {
519
+ return `Memory_${sanitizeUserId(userId)}`;
520
+ }
506
521
 
507
522
  // src/firestore/init.ts
508
523
  import { initializeApp } from "@prmichaelsen/firebase-admin-sdk-v8";
@@ -542,8 +557,8 @@ async function testFirestoreConnection() {
542
557
  if (!initialized) {
543
558
  throw new Error("Firestore not initialized");
544
559
  }
545
- const { getDocument: getDocument2 } = await import("@prmichaelsen/firebase-admin-sdk-v8");
546
- await getDocument2("_health_check", "test");
560
+ const { getDocument: getDocument3 } = await import("@prmichaelsen/firebase-admin-sdk-v8");
561
+ await getDocument3("_health_check", "test");
547
562
  console.log("[Firestore] Connection successful");
548
563
  return true;
549
564
  } catch (error) {
@@ -3247,6 +3262,883 @@ async function handleGetPreferences(args, userId) {
3247
3262
  }
3248
3263
  }
3249
3264
 
3265
+ // src/services/confirmation-token.service.ts
3266
+ import { randomUUID } from "crypto";
3267
+ var ConfirmationTokenService = class {
3268
+ EXPIRY_MINUTES = 5;
3269
+ /**
3270
+ * Create a new confirmation request
3271
+ *
3272
+ * @param userId - User ID who initiated the request
3273
+ * @param action - Action type (e.g., 'publish_memory')
3274
+ * @param payload - Data to store with the request
3275
+ * @param targetCollection - Optional target collection (e.g., 'the_void')
3276
+ * @returns Request ID and token
3277
+ */
3278
+ async createRequest(userId, action, payload, targetCollection) {
3279
+ const token = randomUUID();
3280
+ const now = /* @__PURE__ */ new Date();
3281
+ const expiresAt = new Date(now.getTime() + this.EXPIRY_MINUTES * 60 * 1e3);
3282
+ const request = {
3283
+ user_id: userId,
3284
+ token,
3285
+ action,
3286
+ target_collection: targetCollection,
3287
+ payload,
3288
+ created_at: now.toISOString(),
3289
+ expires_at: expiresAt.toISOString(),
3290
+ status: "pending"
3291
+ };
3292
+ const collectionPath = `users/${userId}/requests`;
3293
+ const docRef = await addDocument(collectionPath, request);
3294
+ return { requestId: docRef.id, token };
3295
+ }
3296
+ /**
3297
+ * Validate and retrieve a confirmation request
3298
+ *
3299
+ * @param userId - User ID
3300
+ * @param token - Confirmation token
3301
+ * @returns Request with request_id if valid, null otherwise
3302
+ */
3303
+ async validateToken(userId, token) {
3304
+ const collectionPath = `users/${userId}/requests`;
3305
+ const queryOptions = {
3306
+ where: [
3307
+ { field: "token", op: "==", value: token },
3308
+ { field: "status", op: "==", value: "pending" }
3309
+ ],
3310
+ limit: 1
3311
+ };
3312
+ const results = await queryDocuments(collectionPath, queryOptions);
3313
+ if (results.length === 0) {
3314
+ return null;
3315
+ }
3316
+ const doc = results[0];
3317
+ const request = doc.data;
3318
+ const expiresAt = new Date(request.expires_at);
3319
+ if (expiresAt.getTime() < Date.now()) {
3320
+ await this.updateStatus(userId, doc.id, "expired");
3321
+ return null;
3322
+ }
3323
+ return {
3324
+ ...request,
3325
+ request_id: doc.id
3326
+ };
3327
+ }
3328
+ /**
3329
+ * Confirm a request
3330
+ *
3331
+ * @param userId - User ID
3332
+ * @param token - Confirmation token
3333
+ * @returns Confirmed request if valid, null otherwise
3334
+ */
3335
+ async confirmRequest(userId, token) {
3336
+ const request = await this.validateToken(userId, token);
3337
+ if (!request) {
3338
+ return null;
3339
+ }
3340
+ await this.updateStatus(userId, request.request_id, "confirmed");
3341
+ return {
3342
+ ...request,
3343
+ status: "confirmed",
3344
+ confirmed_at: (/* @__PURE__ */ new Date()).toISOString()
3345
+ };
3346
+ }
3347
+ /**
3348
+ * Deny a request
3349
+ *
3350
+ * @param userId - User ID
3351
+ * @param token - Confirmation token
3352
+ * @returns True if denied successfully, false otherwise
3353
+ */
3354
+ async denyRequest(userId, token) {
3355
+ const request = await this.validateToken(userId, token);
3356
+ if (!request) {
3357
+ return false;
3358
+ }
3359
+ await this.updateStatus(userId, request.request_id, "denied");
3360
+ return true;
3361
+ }
3362
+ /**
3363
+ * Retract a request
3364
+ *
3365
+ * @param userId - User ID
3366
+ * @param token - Confirmation token
3367
+ * @returns True if retracted successfully, false otherwise
3368
+ */
3369
+ async retractRequest(userId, token) {
3370
+ const request = await this.validateToken(userId, token);
3371
+ if (!request) {
3372
+ return false;
3373
+ }
3374
+ await this.updateStatus(userId, request.request_id, "retracted");
3375
+ return true;
3376
+ }
3377
+ /**
3378
+ * Update request status
3379
+ *
3380
+ * @param userId - User ID
3381
+ * @param requestId - Request document ID
3382
+ * @param status - New status
3383
+ */
3384
+ async updateStatus(userId, requestId, status) {
3385
+ const collectionPath = `users/${userId}/requests`;
3386
+ const updateData = {
3387
+ status
3388
+ };
3389
+ if (status === "confirmed") {
3390
+ updateData.confirmed_at = (/* @__PURE__ */ new Date()).toISOString();
3391
+ }
3392
+ await updateDocument(collectionPath, requestId, updateData);
3393
+ }
3394
+ /**
3395
+ * Clean up expired requests (optional - Firestore TTL handles deletion)
3396
+ *
3397
+ * Note: Configure Firestore TTL policy on 'requests' collection group
3398
+ * with 'expires_at' field for automatic deletion within 24 hours.
3399
+ *
3400
+ * This method is optional for immediate cleanup if needed.
3401
+ *
3402
+ * @returns Count of deleted requests
3403
+ */
3404
+ async cleanupExpired() {
3405
+ console.warn("[ConfirmationTokenService] cleanupExpired not implemented - rely on Firestore TTL");
3406
+ return 0;
3407
+ }
3408
+ };
3409
+ var confirmationTokenService = new ConfirmationTokenService();
3410
+
3411
+ // src/weaviate/space-schema.ts
3412
+ init_space_memory();
3413
+ import weaviate3 from "weaviate-client";
3414
+ function getSpaceCollectionName(spaceId) {
3415
+ return `Memory_${spaceId}`;
3416
+ }
3417
+ function isValidSpaceId(spaceId) {
3418
+ return SUPPORTED_SPACES.includes(spaceId);
3419
+ }
3420
+ async function createSpaceCollection(client2, spaceId) {
3421
+ const collectionName = getSpaceCollectionName(spaceId);
3422
+ console.log(`[Weaviate] Creating space collection ${collectionName}...`);
3423
+ await client2.collections.create({
3424
+ name: collectionName,
3425
+ // Vectorizer configuration
3426
+ vectorizers: weaviate3.configure.vectorizer.text2VecOpenAI({
3427
+ model: "text-embedding-3-small",
3428
+ // Vectorize content for semantic search
3429
+ sourceProperties: ["content", "observation"]
3430
+ }),
3431
+ properties: [
3432
+ // Discriminator
3433
+ {
3434
+ name: "doc_type",
3435
+ dataType: "text",
3436
+ description: 'Document type: "space_memory"'
3437
+ },
3438
+ // Space identity
3439
+ {
3440
+ name: "space_id",
3441
+ dataType: "text",
3442
+ description: 'Space identifier (e.g., "the_void")'
3443
+ },
3444
+ {
3445
+ name: "author_id",
3446
+ dataType: "text",
3447
+ description: "Original author user_id (for permissions)"
3448
+ },
3449
+ {
3450
+ name: "ghost_id",
3451
+ dataType: "text",
3452
+ description: "Optional ghost profile ID for pseudonymous publishing"
3453
+ },
3454
+ {
3455
+ name: "attribution",
3456
+ dataType: "text",
3457
+ description: 'Attribution type: "user" or "ghost"'
3458
+ },
3459
+ // Discovery metadata
3460
+ {
3461
+ name: "published_at",
3462
+ dataType: "text",
3463
+ description: "When published to space (ISO 8601)"
3464
+ },
3465
+ {
3466
+ name: "discovery_count",
3467
+ dataType: "number",
3468
+ description: "How many times discovered"
3469
+ },
3470
+ // Memory fields (same as personal memories)
3471
+ {
3472
+ name: "content",
3473
+ dataType: "text",
3474
+ description: "Main memory content (vectorized)"
3475
+ },
3476
+ {
3477
+ name: "title",
3478
+ dataType: "text",
3479
+ description: "Optional short title"
3480
+ },
3481
+ {
3482
+ name: "summary",
3483
+ dataType: "text",
3484
+ description: "Optional brief summary"
3485
+ },
3486
+ {
3487
+ name: "type",
3488
+ dataType: "text",
3489
+ description: "Content type (note, event, person, etc.)"
3490
+ },
3491
+ // Scoring fields
3492
+ {
3493
+ name: "weight",
3494
+ dataType: "number",
3495
+ description: "Significance/priority (0-1)"
3496
+ },
3497
+ {
3498
+ name: "trust",
3499
+ dataType: "number",
3500
+ description: "Access control level (0-1)"
3501
+ },
3502
+ {
3503
+ name: "confidence",
3504
+ dataType: "number",
3505
+ description: "System confidence in accuracy (0-1)"
3506
+ },
3507
+ // Location fields (flattened)
3508
+ {
3509
+ name: "location_gps_latitude",
3510
+ dataType: "number",
3511
+ description: "GPS latitude"
3512
+ },
3513
+ {
3514
+ name: "location_gps_longitude",
3515
+ dataType: "number",
3516
+ description: "GPS longitude"
3517
+ },
3518
+ {
3519
+ name: "location_address_formatted",
3520
+ dataType: "text",
3521
+ description: "Formatted address"
3522
+ },
3523
+ {
3524
+ name: "location_address_city",
3525
+ dataType: "text",
3526
+ description: "City"
3527
+ },
3528
+ {
3529
+ name: "location_address_country",
3530
+ dataType: "text",
3531
+ description: "Country"
3532
+ },
3533
+ // Context fields (flattened)
3534
+ {
3535
+ name: "context_conversation_id",
3536
+ dataType: "text",
3537
+ description: "Conversation ID"
3538
+ },
3539
+ {
3540
+ name: "context_platform",
3541
+ dataType: "text",
3542
+ description: "Platform where created"
3543
+ },
3544
+ // Tags and relationships
3545
+ {
3546
+ name: "tags",
3547
+ dataType: "text[]",
3548
+ description: "Tags for categorization"
3549
+ },
3550
+ {
3551
+ name: "related_memory_ids",
3552
+ dataType: "text[]",
3553
+ description: "IDs of related memories"
3554
+ },
3555
+ // Timestamps
3556
+ {
3557
+ name: "created_at",
3558
+ dataType: "text",
3559
+ description: "Original creation timestamp (ISO 8601)"
3560
+ },
3561
+ {
3562
+ name: "updated_at",
3563
+ dataType: "text",
3564
+ description: "Last update timestamp (ISO 8601)"
3565
+ },
3566
+ // Versioning
3567
+ {
3568
+ name: "version",
3569
+ dataType: "number",
3570
+ description: "Version number (increments on update)"
3571
+ }
3572
+ ]
3573
+ });
3574
+ console.log(`[Weaviate] Space collection ${collectionName} created successfully`);
3575
+ }
3576
+ async function ensureSpaceCollection(client2, spaceId) {
3577
+ if (!isValidSpaceId(spaceId)) {
3578
+ throw new Error(`Invalid space ID: ${spaceId}. Supported spaces: ${SUPPORTED_SPACES.join(", ")}`);
3579
+ }
3580
+ const collectionName = getSpaceCollectionName(spaceId);
3581
+ const exists = await client2.collections.exists(collectionName);
3582
+ if (!exists) {
3583
+ await createSpaceCollection(client2, spaceId);
3584
+ }
3585
+ return client2.collections.get(collectionName);
3586
+ }
3587
+
3588
+ // src/tools/publish.ts
3589
+ init_space_memory();
3590
+ var publishTool = {
3591
+ name: "remember_publish",
3592
+ 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.',
3593
+ inputSchema: {
3594
+ type: "object",
3595
+ properties: {
3596
+ memory_id: {
3597
+ type: "string",
3598
+ description: "ID of the memory from your personal collection to publish"
3599
+ },
3600
+ target: {
3601
+ type: "string",
3602
+ description: "Target space to publish to (snake_case ID)",
3603
+ enum: SUPPORTED_SPACES,
3604
+ default: "the_void"
3605
+ },
3606
+ additional_tags: {
3607
+ type: "array",
3608
+ items: { type: "string" },
3609
+ description: "Additional tags for discovery (merged with original tags)",
3610
+ default: []
3611
+ }
3612
+ },
3613
+ required: ["memory_id", "target"]
3614
+ }
3615
+ };
3616
+ async function handlePublish(args, userId) {
3617
+ try {
3618
+ if (!isValidSpaceId(args.target)) {
3619
+ return JSON.stringify(
3620
+ {
3621
+ success: false,
3622
+ error: "Invalid space ID",
3623
+ message: `Space "${args.target}" is not supported. Supported spaces: ${SUPPORTED_SPACES.join(", ")}`,
3624
+ context: {
3625
+ provided_space: args.target,
3626
+ supported_spaces: SUPPORTED_SPACES
3627
+ }
3628
+ },
3629
+ null,
3630
+ 2
3631
+ );
3632
+ }
3633
+ const weaviateClient = getWeaviateClient();
3634
+ const userCollection = weaviateClient.collections.get(
3635
+ getMemoryCollectionName(userId)
3636
+ );
3637
+ const memory = await userCollection.query.fetchObjectById(args.memory_id);
3638
+ if (!memory) {
3639
+ return JSON.stringify(
3640
+ {
3641
+ success: false,
3642
+ error: "Memory not found",
3643
+ message: `No memory found with ID: ${args.memory_id}`,
3644
+ context: {
3645
+ collection_name: getMemoryCollectionName(userId),
3646
+ memory_id: args.memory_id
3647
+ }
3648
+ },
3649
+ null,
3650
+ 2
3651
+ );
3652
+ }
3653
+ if (memory.properties.user_id !== userId) {
3654
+ return JSON.stringify(
3655
+ {
3656
+ success: false,
3657
+ error: "Permission denied",
3658
+ message: "You can only publish your own memories",
3659
+ context: {
3660
+ memory_id: args.memory_id,
3661
+ memory_owner: memory.properties.user_id,
3662
+ requesting_user: userId
3663
+ }
3664
+ },
3665
+ null,
3666
+ 2
3667
+ );
3668
+ }
3669
+ if (memory.properties.doc_type !== "memory") {
3670
+ return JSON.stringify(
3671
+ {
3672
+ success: false,
3673
+ error: "Invalid document type",
3674
+ message: "Only memories can be published (not relationships)",
3675
+ context: {
3676
+ memory_id: args.memory_id,
3677
+ doc_type: memory.properties.doc_type
3678
+ }
3679
+ },
3680
+ null,
3681
+ 2
3682
+ );
3683
+ }
3684
+ const payload = {
3685
+ memory_id: args.memory_id,
3686
+ additional_tags: args.additional_tags || []
3687
+ };
3688
+ const { requestId, token } = await confirmationTokenService.createRequest(
3689
+ userId,
3690
+ "publish_memory",
3691
+ payload,
3692
+ args.target
3693
+ );
3694
+ return JSON.stringify(
3695
+ {
3696
+ success: true,
3697
+ token
3698
+ },
3699
+ null,
3700
+ 2
3701
+ );
3702
+ } catch (error) {
3703
+ handleToolError(error, {
3704
+ toolName: "remember_publish",
3705
+ userId,
3706
+ operation: "publish memory",
3707
+ memory_id: args.memory_id,
3708
+ target: args.target
3709
+ });
3710
+ }
3711
+ }
3712
+
3713
+ // src/tools/confirm.ts
3714
+ var confirmTool = {
3715
+ name: "remember_confirm",
3716
+ description: "Confirm and execute a pending action using the token. Works for any action that requires confirmation (publish, delete, etc.).",
3717
+ inputSchema: {
3718
+ type: "object",
3719
+ properties: {
3720
+ token: {
3721
+ type: "string",
3722
+ description: "The confirmation token from the action tool"
3723
+ }
3724
+ },
3725
+ required: ["token"]
3726
+ }
3727
+ };
3728
+ async function handleConfirm(args, userId) {
3729
+ try {
3730
+ const request = await confirmationTokenService.confirmRequest(userId, args.token);
3731
+ if (!request) {
3732
+ return JSON.stringify(
3733
+ {
3734
+ success: false,
3735
+ error: "Invalid or expired token",
3736
+ message: "The confirmation token is invalid, expired, or has already been used."
3737
+ },
3738
+ null,
3739
+ 2
3740
+ );
3741
+ }
3742
+ if (request.action === "publish_memory") {
3743
+ return await executePublishMemory(request, userId);
3744
+ }
3745
+ throw new Error(`Unknown action type: ${request.action}`);
3746
+ } catch (error) {
3747
+ handleToolError(error, {
3748
+ toolName: "remember_confirm",
3749
+ userId,
3750
+ operation: "confirm action",
3751
+ token: args.token
3752
+ });
3753
+ }
3754
+ }
3755
+ async function executePublishMemory(request, userId) {
3756
+ try {
3757
+ const weaviateClient = getWeaviateClient();
3758
+ const userCollection = weaviateClient.collections.get(
3759
+ getMemoryCollectionName(userId)
3760
+ );
3761
+ const originalMemory = await userCollection.query.fetchObjectById(
3762
+ request.payload.memory_id
3763
+ );
3764
+ if (!originalMemory) {
3765
+ return JSON.stringify(
3766
+ {
3767
+ success: false,
3768
+ error: "Memory not found",
3769
+ message: `Original memory ${request.payload.memory_id} no longer exists`
3770
+ },
3771
+ null,
3772
+ 2
3773
+ );
3774
+ }
3775
+ if (originalMemory.properties.user_id !== userId) {
3776
+ return JSON.stringify(
3777
+ {
3778
+ success: false,
3779
+ error: "Permission denied",
3780
+ message: "You can only publish your own memories"
3781
+ },
3782
+ null,
3783
+ 2
3784
+ );
3785
+ }
3786
+ const targetCollection = await ensureSpaceCollection(
3787
+ weaviateClient,
3788
+ request.target_collection || "the_void"
3789
+ );
3790
+ const originalTags = Array.isArray(originalMemory.properties.tags) ? originalMemory.properties.tags : [];
3791
+ const additionalTags = Array.isArray(request.payload.additional_tags) ? request.payload.additional_tags : [];
3792
+ const publishedMemory = {
3793
+ ...originalMemory.properties,
3794
+ // Override specific fields
3795
+ space_id: request.target_collection || "the_void",
3796
+ author_id: userId,
3797
+ // Always attributed
3798
+ published_at: (/* @__PURE__ */ new Date()).toISOString(),
3799
+ discovery_count: 0,
3800
+ doc_type: "space_memory",
3801
+ attribution: "user",
3802
+ // Merge additional tags
3803
+ tags: [...originalTags, ...additionalTags],
3804
+ // Update timestamps
3805
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
3806
+ updated_at: (/* @__PURE__ */ new Date()).toISOString(),
3807
+ version: 1
3808
+ };
3809
+ const result = await targetCollection.data.insert({
3810
+ properties: publishedMemory
3811
+ });
3812
+ return JSON.stringify(
3813
+ {
3814
+ success: true,
3815
+ space_memory_id: result
3816
+ },
3817
+ null,
3818
+ 2
3819
+ );
3820
+ } catch (error) {
3821
+ handleToolError(error, {
3822
+ toolName: "remember_confirm",
3823
+ userId,
3824
+ operation: "execute publish_memory",
3825
+ action: "publish_memory"
3826
+ });
3827
+ }
3828
+ }
3829
+
3830
+ // src/tools/deny.ts
3831
+ var denyTool = {
3832
+ name: "remember_deny",
3833
+ description: "Deny a pending action. The request will be marked as denied and the token invalidated. Works for any action that requires confirmation.",
3834
+ inputSchema: {
3835
+ type: "object",
3836
+ properties: {
3837
+ token: {
3838
+ type: "string",
3839
+ description: "The confirmation token from the action tool"
3840
+ }
3841
+ },
3842
+ required: ["token"]
3843
+ }
3844
+ };
3845
+ async function handleDeny(args, userId) {
3846
+ try {
3847
+ const success = await confirmationTokenService.denyRequest(userId, args.token);
3848
+ if (!success) {
3849
+ return JSON.stringify(
3850
+ {
3851
+ success: false,
3852
+ error: "Invalid token",
3853
+ message: "Token not found or already used"
3854
+ },
3855
+ null,
3856
+ 2
3857
+ );
3858
+ }
3859
+ return JSON.stringify(
3860
+ {
3861
+ success: true
3862
+ },
3863
+ null,
3864
+ 2
3865
+ );
3866
+ } catch (error) {
3867
+ handleToolError(error, {
3868
+ toolName: "remember_deny",
3869
+ userId,
3870
+ operation: "deny action",
3871
+ token: args.token
3872
+ });
3873
+ }
3874
+ }
3875
+
3876
+ // src/tools/search-space.ts
3877
+ import { Filters as Filters3 } from "weaviate-client";
3878
+ init_space_memory();
3879
+ var searchSpaceTool = {
3880
+ name: "remember_search_space",
3881
+ description: "Search shared spaces to discover thoughts, ideas, and memories. Works like remember_search_memory but searches shared spaces instead of personal memories.",
3882
+ inputSchema: {
3883
+ type: "object",
3884
+ properties: {
3885
+ query: {
3886
+ type: "string",
3887
+ description: "Search query (semantic + keyword hybrid)"
3888
+ },
3889
+ space: {
3890
+ type: "string",
3891
+ description: "Which space to search",
3892
+ enum: SUPPORTED_SPACES,
3893
+ default: "the_void"
3894
+ },
3895
+ content_type: {
3896
+ type: "string",
3897
+ description: "Filter by content type"
3898
+ },
3899
+ tags: {
3900
+ type: "array",
3901
+ items: { type: "string" },
3902
+ description: "Filter by tags (must have all specified tags)"
3903
+ },
3904
+ min_weight: {
3905
+ type: "number",
3906
+ minimum: 0,
3907
+ maximum: 1,
3908
+ description: "Minimum weight/significance (0-1)"
3909
+ },
3910
+ max_weight: {
3911
+ type: "number",
3912
+ minimum: 0,
3913
+ maximum: 1,
3914
+ description: "Maximum weight/significance (0-1)"
3915
+ },
3916
+ date_from: {
3917
+ type: "string",
3918
+ description: "Filter memories created after this date (ISO 8601)"
3919
+ },
3920
+ date_to: {
3921
+ type: "string",
3922
+ description: "Filter memories created before this date (ISO 8601)"
3923
+ },
3924
+ limit: {
3925
+ type: "number",
3926
+ default: 10,
3927
+ description: "Maximum number of results"
3928
+ },
3929
+ offset: {
3930
+ type: "number",
3931
+ default: 0,
3932
+ description: "Offset for pagination"
3933
+ }
3934
+ },
3935
+ required: ["query", "space"]
3936
+ }
3937
+ };
3938
+ async function handleSearchSpace(args, userId) {
3939
+ try {
3940
+ if (!isValidSpaceId(args.space)) {
3941
+ return JSON.stringify(
3942
+ {
3943
+ success: false,
3944
+ error: "Invalid space ID",
3945
+ message: `Space "${args.space}" is not supported. Supported spaces: ${SUPPORTED_SPACES.join(", ")}`
3946
+ },
3947
+ null,
3948
+ 2
3949
+ );
3950
+ }
3951
+ const weaviateClient = getWeaviateClient();
3952
+ const spaceCollection = await ensureSpaceCollection(weaviateClient, args.space);
3953
+ const filterList = [];
3954
+ filterList.push(spaceCollection.filter.byProperty("space_id").equal(args.space));
3955
+ filterList.push(spaceCollection.filter.byProperty("doc_type").equal("space_memory"));
3956
+ if (args.content_type) {
3957
+ filterList.push(spaceCollection.filter.byProperty("type").equal(args.content_type));
3958
+ }
3959
+ if (args.tags && args.tags.length > 0) {
3960
+ args.tags.forEach((tag) => {
3961
+ filterList.push(spaceCollection.filter.byProperty("tags").containsAny([tag]));
3962
+ });
3963
+ }
3964
+ if (args.min_weight !== void 0) {
3965
+ filterList.push(spaceCollection.filter.byProperty("weight").greaterOrEqual(args.min_weight));
3966
+ }
3967
+ if (args.max_weight !== void 0) {
3968
+ filterList.push(spaceCollection.filter.byProperty("weight").lessOrEqual(args.max_weight));
3969
+ }
3970
+ if (args.date_from) {
3971
+ filterList.push(spaceCollection.filter.byProperty("created_at").greaterOrEqual(new Date(args.date_from)));
3972
+ }
3973
+ if (args.date_to) {
3974
+ filterList.push(spaceCollection.filter.byProperty("created_at").lessOrEqual(new Date(args.date_to)));
3975
+ }
3976
+ const whereFilter = filterList.length > 0 ? Filters3.and(...filterList) : void 0;
3977
+ const searchResults = await spaceCollection.query.hybrid(args.query, {
3978
+ limit: args.limit || 10,
3979
+ offset: args.offset || 0,
3980
+ ...whereFilter && { where: whereFilter }
3981
+ });
3982
+ const memories = searchResults.objects.map((obj) => ({
3983
+ id: obj.uuid,
3984
+ ...obj.properties,
3985
+ _score: obj.metadata?.score
3986
+ }));
3987
+ const result = {
3988
+ space: args.space,
3989
+ query: args.query,
3990
+ memories,
3991
+ total: memories.length,
3992
+ offset: args.offset || 0,
3993
+ limit: args.limit || 10
3994
+ };
3995
+ return JSON.stringify(result, null, 2);
3996
+ } catch (error) {
3997
+ handleToolError(error, {
3998
+ toolName: "remember_search_space",
3999
+ operation: "search space",
4000
+ space: args.space,
4001
+ query: args.query
4002
+ });
4003
+ }
4004
+ }
4005
+
4006
+ // src/tools/query-space.ts
4007
+ import { Filters as Filters4 } from "weaviate-client";
4008
+ init_space_memory();
4009
+ var querySpaceTool = {
4010
+ name: "remember_query_space",
4011
+ description: "Ask natural language questions about memories in shared spaces. Works like remember_query_memory but queries shared spaces.",
4012
+ inputSchema: {
4013
+ type: "object",
4014
+ properties: {
4015
+ question: {
4016
+ type: "string",
4017
+ description: "Natural language question"
4018
+ },
4019
+ space: {
4020
+ type: "string",
4021
+ description: "Which space to query",
4022
+ enum: SUPPORTED_SPACES,
4023
+ default: "the_void"
4024
+ },
4025
+ content_type: {
4026
+ type: "string",
4027
+ description: "Filter by content type"
4028
+ },
4029
+ tags: {
4030
+ type: "array",
4031
+ items: { type: "string" },
4032
+ description: "Filter by tags"
4033
+ },
4034
+ min_weight: {
4035
+ type: "number",
4036
+ minimum: 0,
4037
+ maximum: 1,
4038
+ description: "Minimum weight/significance (0-1)"
4039
+ },
4040
+ date_from: {
4041
+ type: "string",
4042
+ description: "Filter memories created after this date (ISO 8601)"
4043
+ },
4044
+ date_to: {
4045
+ type: "string",
4046
+ description: "Filter memories created before this date (ISO 8601)"
4047
+ },
4048
+ limit: {
4049
+ type: "number",
4050
+ default: 10,
4051
+ description: "Maximum number of results"
4052
+ },
4053
+ format: {
4054
+ type: "string",
4055
+ enum: ["detailed", "compact"],
4056
+ default: "detailed",
4057
+ description: "Output format: detailed (full objects) or compact (text summary)"
4058
+ }
4059
+ },
4060
+ required: ["question", "space"]
4061
+ }
4062
+ };
4063
+ async function handleQuerySpace(args, userId) {
4064
+ try {
4065
+ if (!isValidSpaceId(args.space)) {
4066
+ return JSON.stringify(
4067
+ {
4068
+ success: false,
4069
+ error: "Invalid space ID",
4070
+ message: `Space "${args.space}" is not supported. Supported spaces: ${SUPPORTED_SPACES.join(", ")}`
4071
+ },
4072
+ null,
4073
+ 2
4074
+ );
4075
+ }
4076
+ const weaviateClient = getWeaviateClient();
4077
+ const spaceCollection = await ensureSpaceCollection(weaviateClient, args.space);
4078
+ const filterList = [];
4079
+ filterList.push(spaceCollection.filter.byProperty("space_id").equal(args.space));
4080
+ filterList.push(spaceCollection.filter.byProperty("doc_type").equal("space_memory"));
4081
+ if (args.content_type) {
4082
+ filterList.push(spaceCollection.filter.byProperty("type").equal(args.content_type));
4083
+ }
4084
+ if (args.tags && args.tags.length > 0) {
4085
+ args.tags.forEach((tag) => {
4086
+ filterList.push(spaceCollection.filter.byProperty("tags").containsAny([tag]));
4087
+ });
4088
+ }
4089
+ if (args.min_weight !== void 0) {
4090
+ filterList.push(spaceCollection.filter.byProperty("weight").greaterOrEqual(args.min_weight));
4091
+ }
4092
+ if (args.date_from) {
4093
+ filterList.push(spaceCollection.filter.byProperty("created_at").greaterOrEqual(new Date(args.date_from)));
4094
+ }
4095
+ if (args.date_to) {
4096
+ filterList.push(spaceCollection.filter.byProperty("created_at").lessOrEqual(new Date(args.date_to)));
4097
+ }
4098
+ const whereFilter = filterList.length > 0 ? Filters4.and(...filterList) : void 0;
4099
+ const searchResults = await spaceCollection.query.nearText(args.question, {
4100
+ limit: args.limit || 10,
4101
+ ...whereFilter && { where: whereFilter }
4102
+ });
4103
+ const format = args.format || "detailed";
4104
+ if (format === "compact") {
4105
+ const summaries = searchResults.objects.map((obj, idx) => {
4106
+ const props = obj.properties;
4107
+ return `${idx + 1}. ${props.title || props.content?.substring(0, 100) || "Untitled"}`;
4108
+ });
4109
+ const result = {
4110
+ question: args.question,
4111
+ space: args.space,
4112
+ format: "compact",
4113
+ summary: summaries.join("\n"),
4114
+ count: searchResults.objects.length
4115
+ };
4116
+ return JSON.stringify(result, null, 2);
4117
+ } else {
4118
+ const memories = searchResults.objects.map((obj) => ({
4119
+ id: obj.uuid,
4120
+ ...obj.properties,
4121
+ _distance: obj.metadata?.distance
4122
+ }));
4123
+ const result = {
4124
+ question: args.question,
4125
+ space: args.space,
4126
+ format: "detailed",
4127
+ memories,
4128
+ total: memories.length
4129
+ };
4130
+ return JSON.stringify(result, null, 2);
4131
+ }
4132
+ } catch (error) {
4133
+ handleToolError(error, {
4134
+ toolName: "remember_query_space",
4135
+ operation: "query space",
4136
+ space: args.space,
4137
+ question: args.question
4138
+ });
4139
+ }
4140
+ }
4141
+
3250
4142
  // src/server.ts
3251
4143
  async function initServer() {
3252
4144
  logger.info("Initializing remember-mcp server...");
@@ -3293,7 +4185,13 @@ function registerHandlers(server) {
3293
4185
  deleteRelationshipTool,
3294
4186
  // Preference tools
3295
4187
  setPreferenceTool,
3296
- getPreferencesTool
4188
+ getPreferencesTool,
4189
+ // Space tools
4190
+ publishTool,
4191
+ confirmTool,
4192
+ denyTool,
4193
+ searchSpaceTool,
4194
+ querySpaceTool
3297
4195
  ]
3298
4196
  };
3299
4197
  });
@@ -3339,6 +4237,21 @@ function registerHandlers(server) {
3339
4237
  case "remember_get_preferences":
3340
4238
  result = await handleGetPreferences(args, userId);
3341
4239
  break;
4240
+ case "remember_publish":
4241
+ result = await handlePublish(args, userId);
4242
+ break;
4243
+ case "remember_confirm":
4244
+ result = await handleConfirm(args, userId);
4245
+ break;
4246
+ case "remember_deny":
4247
+ result = await handleDeny(args, userId);
4248
+ break;
4249
+ case "remember_search_space":
4250
+ result = await handleSearchSpace(args, userId);
4251
+ break;
4252
+ case "remember_query_space":
4253
+ result = await handleQuerySpace(args, userId);
4254
+ break;
3342
4255
  default:
3343
4256
  throw new McpError(
3344
4257
  ErrorCode.MethodNotFound,