@prmichaelsen/remember-mcp 2.2.1 → 2.3.1

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