@prmichaelsen/remember-mcp 0.1.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 (95) hide show
  1. package/.env.example +65 -0
  2. package/AGENT.md +840 -0
  3. package/README.md +72 -0
  4. package/agent/design/.gitkeep +0 -0
  5. package/agent/design/access-control-result-pattern.md +458 -0
  6. package/agent/design/action-audit-memory-types.md +637 -0
  7. package/agent/design/common-template-fields.md +282 -0
  8. package/agent/design/complete-tool-set.md +407 -0
  9. package/agent/design/content-types-expansion.md +521 -0
  10. package/agent/design/cross-database-id-strategy.md +358 -0
  11. package/agent/design/default-template-library.md +423 -0
  12. package/agent/design/firestore-wrapper-analysis.md +606 -0
  13. package/agent/design/llm-provider-abstraction.md +691 -0
  14. package/agent/design/location-handling-architecture.md +523 -0
  15. package/agent/design/memory-templates-design.md +364 -0
  16. package/agent/design/permissions-storage-architecture.md +680 -0
  17. package/agent/design/relationship-storage-strategy.md +361 -0
  18. package/agent/design/remember-mcp-implementation-tasks.md +417 -0
  19. package/agent/design/remember-mcp-progress.yaml +141 -0
  20. package/agent/design/requirements-enhancements.md +468 -0
  21. package/agent/design/requirements.md +56 -0
  22. package/agent/design/template-storage-strategy.md +412 -0
  23. package/agent/design/template-suggestion-system.md +853 -0
  24. package/agent/design/trust-escalation-prevention.md +343 -0
  25. package/agent/design/trust-system-implementation.md +592 -0
  26. package/agent/design/user-preferences.md +683 -0
  27. package/agent/design/weaviate-collection-strategy.md +461 -0
  28. package/agent/milestones/.gitkeep +0 -0
  29. package/agent/milestones/milestone-1-project-foundation.md +121 -0
  30. package/agent/milestones/milestone-2-core-memory-system.md +150 -0
  31. package/agent/milestones/milestone-3-relationships-graph.md +116 -0
  32. package/agent/milestones/milestone-4-user-preferences.md +103 -0
  33. package/agent/milestones/milestone-5-template-system.md +126 -0
  34. package/agent/milestones/milestone-6-auth-multi-tenancy.md +124 -0
  35. package/agent/milestones/milestone-7-trust-permissions.md +133 -0
  36. package/agent/milestones/milestone-8-testing-quality.md +137 -0
  37. package/agent/milestones/milestone-9-deployment-documentation.md +147 -0
  38. package/agent/patterns/.gitkeep +0 -0
  39. package/agent/patterns/bootstrap.md +1271 -0
  40. package/agent/patterns/firebase-admin-sdk-v8-usage.md +950 -0
  41. package/agent/patterns/firestore-users-pattern-best-practices.md +347 -0
  42. package/agent/patterns/library-services.md +454 -0
  43. package/agent/patterns/testing-colocated.md +316 -0
  44. package/agent/progress.yaml +395 -0
  45. package/agent/tasks/.gitkeep +0 -0
  46. package/agent/tasks/task-1-initialize-project-structure.md +266 -0
  47. package/agent/tasks/task-2-install-dependencies.md +199 -0
  48. package/agent/tasks/task-3-setup-weaviate-client.md +330 -0
  49. package/agent/tasks/task-4-setup-firestore-client.md +362 -0
  50. package/agent/tasks/task-5-create-basic-mcp-server.md +114 -0
  51. package/agent/tasks/task-6-create-integration-tests.md +195 -0
  52. package/agent/tasks/task-7-finalize-milestone-1.md +363 -0
  53. package/agent/tasks/task-8-setup-utility-scripts.md +382 -0
  54. package/agent/tasks/task-9-create-server-factory.md +404 -0
  55. package/dist/config.d.ts +26 -0
  56. package/dist/constants/content-types.d.ts +60 -0
  57. package/dist/firestore/init.d.ts +14 -0
  58. package/dist/firestore/paths.d.ts +53 -0
  59. package/dist/firestore/paths.spec.d.ts +2 -0
  60. package/dist/server-factory.d.ts +40 -0
  61. package/dist/server-factory.js +1741 -0
  62. package/dist/server-factory.spec.d.ts +2 -0
  63. package/dist/server.d.ts +3 -0
  64. package/dist/server.js +1690 -0
  65. package/dist/tools/create-memory.d.ts +94 -0
  66. package/dist/tools/delete-memory.d.ts +47 -0
  67. package/dist/tools/search-memory.d.ts +88 -0
  68. package/dist/types/memory.d.ts +183 -0
  69. package/dist/utils/logger.d.ts +7 -0
  70. package/dist/weaviate/client.d.ts +39 -0
  71. package/dist/weaviate/client.spec.d.ts +2 -0
  72. package/dist/weaviate/schema.d.ts +29 -0
  73. package/esbuild.build.js +60 -0
  74. package/esbuild.watch.js +25 -0
  75. package/jest.config.js +31 -0
  76. package/jest.e2e.config.js +17 -0
  77. package/package.json +68 -0
  78. package/src/.gitkeep +0 -0
  79. package/src/config.ts +56 -0
  80. package/src/constants/content-types.ts +454 -0
  81. package/src/firestore/init.ts +68 -0
  82. package/src/firestore/paths.spec.ts +75 -0
  83. package/src/firestore/paths.ts +124 -0
  84. package/src/server-factory.spec.ts +60 -0
  85. package/src/server-factory.ts +215 -0
  86. package/src/server.ts +243 -0
  87. package/src/tools/create-memory.ts +198 -0
  88. package/src/tools/delete-memory.ts +126 -0
  89. package/src/tools/search-memory.ts +216 -0
  90. package/src/types/memory.ts +276 -0
  91. package/src/utils/logger.ts +42 -0
  92. package/src/weaviate/client.spec.ts +58 -0
  93. package/src/weaviate/client.ts +114 -0
  94. package/src/weaviate/schema.ts +288 -0
  95. package/tsconfig.json +26 -0
package/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # remember-mcp
2
+
3
+ Multi-tenant memory system MCP server with vector search, relationships, and trust-based access control.
4
+
5
+ ## Features
6
+
7
+ - 24 MCP tools for memory management
8
+ - Multi-tenant with per-user isolation
9
+ - Vector search with Weaviate
10
+ - Relationship graph between memories
11
+ - Template system with auto-suggestion
12
+ - Trust-based cross-user access control
13
+ - User preferences via conversation
14
+
15
+ ## Quick Start
16
+
17
+ ### Standalone (stdio transport)
18
+
19
+ ```bash
20
+ # Install dependencies
21
+ npm install
22
+
23
+ # Set up environment
24
+ cp .env.example .env
25
+ # Edit .env with your configuration
26
+
27
+ # Run in development
28
+ npm run dev
29
+
30
+ # Build for production
31
+ npm run build
32
+ npm start
33
+ ```
34
+
35
+ ### With mcp-auth (multi-tenant production)
36
+
37
+ ```typescript
38
+ import { wrapServer, JWTAuthProvider, APITokenResolver } from '@prmichaelsen/mcp-auth';
39
+ import { createServer } from '@prmichaelsen/remember-mcp/factory';
40
+
41
+ const wrapped = wrapServer({
42
+ serverFactory: createServer,
43
+ authProvider: new JWTAuthProvider({
44
+ jwtSecret: process.env.JWT_SECRET
45
+ }),
46
+ tokenResolver: new APITokenResolver({
47
+ tenantManagerUrl: process.env.TENANT_MANAGER_URL,
48
+ serviceToken: process.env.SERVICE_TOKEN
49
+ }),
50
+ resourceType: 'remember',
51
+ transport: { type: 'sse', port: 3000 }
52
+ });
53
+
54
+ await wrapped.start();
55
+ ```
56
+
57
+ ## Architecture
58
+
59
+ - **Weaviate**: Vector storage for memories, relationships, templates
60
+ - **Firestore**: Permissions, preferences, metadata
61
+ - **Firebase Auth**: User authentication
62
+
63
+ ## Documentation
64
+
65
+ See `agent/` directory for:
66
+ - Design documents (`agent/design/`)
67
+ - Milestones (`agent/milestones/`)
68
+ - Implementation tasks (`agent/tasks/`)
69
+
70
+ ## License
71
+
72
+ MIT
File without changes
@@ -0,0 +1,458 @@
1
+ # Access Control Result Pattern
2
+
3
+ **Concept**: Use discriminated unions instead of exceptions for access control
4
+ **Created**: 2026-02-11
5
+ **Status**: Design Specification
6
+
7
+ ---
8
+
9
+ ## Problem with Throwing Errors
10
+
11
+ **Bad Pattern**:
12
+ ```typescript
13
+ // ❌ Using exceptions for control flow
14
+ async function checkMemoryAccess(memory_id, accessor_user_id) {
15
+ if (blocked) {
16
+ throw new Error('Access blocked');
17
+ }
18
+ if (insufficient_trust) {
19
+ throw new Error('Insufficient trust');
20
+ }
21
+ return memory;
22
+ }
23
+
24
+ // Caller has to catch and parse error messages
25
+ try {
26
+ const memory = await checkMemoryAccess(id, user);
27
+ } catch (error) {
28
+ // Hard to distinguish between different failure types
29
+ if (error.message.includes('blocked')) {
30
+ // Handle block
31
+ } else if (error.message.includes('trust')) {
32
+ // Handle trust failure
33
+ }
34
+ }
35
+ ```
36
+
37
+ **Problems**:
38
+ - Exceptions for expected cases (not exceptional)
39
+ - Hard to distinguish failure types
40
+ - Error message parsing is brittle
41
+ - No type safety
42
+ - Poor developer experience
43
+
44
+ ---
45
+
46
+ ## Solution: Discriminated Union (Result Type)
47
+
48
+ **Good Pattern**:
49
+ ```typescript
50
+ // ✅ Using discriminated union
51
+ type AccessResult =
52
+ | { status: 'granted'; memory: Memory }
53
+ | { status: 'insufficient_trust'; required: number; actual: number; attempts_remaining: number }
54
+ | { status: 'blocked'; reason: string; blocked_at: Date }
55
+ | { status: 'no_permission'; owner_user_id: string }
56
+ | { status: 'not_found'; memory_id: string };
57
+
58
+ async function checkMemoryAccess(
59
+ memory_id: string,
60
+ accessor_user_id: string
61
+ ): Promise<AccessResult> {
62
+ // Implementation returns appropriate result
63
+ }
64
+
65
+ // Caller has type-safe handling
66
+ const result = await checkMemoryAccess(id, user);
67
+
68
+ switch (result.status) {
69
+ case 'granted':
70
+ // TypeScript knows result.memory exists
71
+ return result.memory;
72
+
73
+ case 'insufficient_trust':
74
+ // TypeScript knows these fields exist
75
+ console.log(`Need ${result.required}, have ${result.actual}`);
76
+ console.log(`${result.attempts_remaining} attempts remaining`);
77
+ return null;
78
+
79
+ case 'blocked':
80
+ console.log(`Blocked: ${result.reason} at ${result.blocked_at}`);
81
+ return null;
82
+
83
+ case 'no_permission':
84
+ console.log(`No permission to access ${result.owner_user_id}'s memories`);
85
+ return null;
86
+
87
+ case 'not_found':
88
+ console.log(`Memory ${result.memory_id} not found`);
89
+ return null;
90
+ }
91
+ ```
92
+
93
+ **Benefits**:
94
+ - ✅ Type-safe
95
+ - ✅ Exhaustive checking (TypeScript ensures all cases handled)
96
+ - ✅ Clear failure reasons
97
+ - ✅ Rich context for each failure type
98
+ - ✅ Better developer experience
99
+ - ✅ No exception handling needed
100
+
101
+ ---
102
+
103
+ ## Complete Implementation
104
+
105
+ ### Access Result Types
106
+
107
+ ```typescript
108
+ // Success case
109
+ interface AccessGranted {
110
+ status: 'granted';
111
+ memory: Memory;
112
+ access_level: 'owner' | 'trusted'; // Was this owner or cross-user access?
113
+ }
114
+
115
+ // Trust insufficient (but not blocked yet)
116
+ interface AccessInsufficientTrust {
117
+ status: 'insufficient_trust';
118
+ memory_id: string;
119
+ required_trust: number;
120
+ actual_trust: number;
121
+ trust_deficit: number; // How much more trust needed
122
+ attempts_made: number;
123
+ attempts_remaining: number;
124
+ new_trust_level: number; // After penalty applied
125
+ }
126
+
127
+ // Access blocked (after 3 attempts)
128
+ interface AccessBlocked {
129
+ status: 'blocked';
130
+ memory_id: string;
131
+ reason: string;
132
+ blocked_at: Date;
133
+ attempt_count: number;
134
+ contact_owner: boolean; // Should user contact owner?
135
+ }
136
+
137
+ // No permission granted at all
138
+ interface AccessNoPermission {
139
+ status: 'no_permission';
140
+ owner_user_id: string;
141
+ accessor_user_id: string;
142
+ message: string;
143
+ }
144
+
145
+ // Memory doesn't exist
146
+ interface AccessNotFound {
147
+ status: 'not_found';
148
+ memory_id: string;
149
+ }
150
+
151
+ // Memory was deleted
152
+ interface AccessDeleted {
153
+ status: 'deleted';
154
+ memory_id: string;
155
+ deleted_at: Date;
156
+ }
157
+
158
+ // Union type
159
+ type AccessResult =
160
+ | AccessGranted
161
+ | AccessInsufficientTrust
162
+ | AccessBlocked
163
+ | AccessNoPermission
164
+ | AccessNotFound
165
+ | AccessDeleted;
166
+ ```
167
+
168
+ ### Implementation
169
+
170
+ ```typescript
171
+ async function checkMemoryAccess(
172
+ memory_id: string,
173
+ accessor_user_id: string
174
+ ): Promise<AccessResult> {
175
+ // Get memory
176
+ const memory = await getMemory(memory_id);
177
+
178
+ if (!memory) {
179
+ return {
180
+ status: 'not_found',
181
+ memory_id
182
+ };
183
+ }
184
+
185
+ if (memory.deleted) {
186
+ return {
187
+ status: 'deleted',
188
+ memory_id,
189
+ deleted_at: memory.deleted_at
190
+ };
191
+ }
192
+
193
+ // Owner always has access
194
+ if (accessor_user_id === memory.user_id) {
195
+ return {
196
+ status: 'granted',
197
+ memory,
198
+ access_level: 'owner'
199
+ };
200
+ }
201
+
202
+ // Cross-user access - check permission
203
+ const permission = await getPermission(memory.user_id, accessor_user_id);
204
+
205
+ if (!permission || !permission.can_access) {
206
+ return {
207
+ status: 'no_permission',
208
+ owner_user_id: memory.user_id,
209
+ accessor_user_id,
210
+ message: 'No permission granted to access this user\'s memories'
211
+ };
212
+ }
213
+
214
+ // Check if blocked
215
+ const blockKey = `${accessor_user_id}:${memory_id}`;
216
+ const block = await getBlock(blockKey);
217
+
218
+ if (block) {
219
+ return {
220
+ status: 'blocked',
221
+ memory_id,
222
+ reason: 'Access blocked due to repeated unauthorized attempts',
223
+ blocked_at: block.blocked_at,
224
+ attempt_count: block.attempt_count,
225
+ contact_owner: true
226
+ };
227
+ }
228
+
229
+ // Check trust level
230
+ if (permission.trust_level < memory.trust) {
231
+ // Apply penalty
232
+ const attemptCount = await incrementAttemptCount(blockKey);
233
+ const newTrust = Math.max(0, permission.trust_level - 0.1);
234
+
235
+ await updateTrustLevel(
236
+ memory.user_id,
237
+ accessor_user_id,
238
+ newTrust,
239
+ `Unauthorized access attempt to memory ${memory_id}`
240
+ );
241
+
242
+ // Block after 3 attempts
243
+ if (attemptCount >= 3) {
244
+ await blockMemoryAccess(blockKey, attemptCount);
245
+
246
+ return {
247
+ status: 'blocked',
248
+ memory_id,
249
+ reason: 'Access blocked after 3 unauthorized attempts',
250
+ blocked_at: new Date(),
251
+ attempt_count: attemptCount,
252
+ contact_owner: true
253
+ };
254
+ }
255
+
256
+ return {
257
+ status: 'insufficient_trust',
258
+ memory_id,
259
+ required_trust: memory.trust,
260
+ actual_trust: permission.trust_level,
261
+ trust_deficit: memory.trust - permission.trust_level,
262
+ attempts_made: attemptCount,
263
+ attempts_remaining: 3 - attemptCount,
264
+ new_trust_level: newTrust
265
+ };
266
+ }
267
+
268
+ // Access granted
269
+ return {
270
+ status: 'granted',
271
+ memory,
272
+ access_level: 'trusted'
273
+ };
274
+ }
275
+ ```
276
+
277
+ ### Usage in Tools
278
+
279
+ ```typescript
280
+ // remember_search_memory tool
281
+ async function searchMemory(args: SearchArgs, context: RequestContext) {
282
+ const memories = await weaviate.search(args.query);
283
+
284
+ // Filter by access
285
+ const accessible = [];
286
+ const access_denied = [];
287
+
288
+ for (const memory of memories) {
289
+ const result = await checkMemoryAccess(memory.id, context.user_id);
290
+
291
+ switch (result.status) {
292
+ case 'granted':
293
+ accessible.push(result.memory);
294
+ break;
295
+
296
+ case 'insufficient_trust':
297
+ access_denied.push({
298
+ memory_id: memory.id,
299
+ reason: 'insufficient_trust',
300
+ details: `Need trust ${result.required_trust.toFixed(2)}, have ${result.actual_trust.toFixed(2)}`
301
+ });
302
+ break;
303
+
304
+ case 'blocked':
305
+ access_denied.push({
306
+ memory_id: memory.id,
307
+ reason: 'blocked',
308
+ details: result.reason
309
+ });
310
+ break;
311
+
312
+ // Other cases...
313
+ }
314
+ }
315
+
316
+ return {
317
+ results: accessible,
318
+ access_denied: access_denied,
319
+ total: accessible.length
320
+ };
321
+ }
322
+ ```
323
+
324
+ ### User-Friendly Messages
325
+
326
+ ```typescript
327
+ function formatAccessResult(result: AccessResult): string {
328
+ switch (result.status) {
329
+ case 'granted':
330
+ return 'Access granted';
331
+
332
+ case 'insufficient_trust':
333
+ return `Insufficient trust level. You need ${result.required_trust.toFixed(2)} but have ${result.actual_trust.toFixed(2)}. Your trust has been reduced to ${result.new_trust_level.toFixed(2)}. ${result.attempts_remaining} attempts remaining before access is blocked.`;
334
+
335
+ case 'blocked':
336
+ return `Access to this memory has been blocked due to ${result.attempt_count} unauthorized access attempts. Please contact the memory owner to reset access.`;
337
+
338
+ case 'no_permission':
339
+ return `You don't have permission to access this user's memories. Please request access from the owner.`;
340
+
341
+ case 'not_found':
342
+ return `Memory not found.`;
343
+
344
+ case 'deleted':
345
+ return `This memory was deleted on ${result.deleted_at.toLocaleDateString()}.`;
346
+ }
347
+ }
348
+ ```
349
+
350
+ ---
351
+
352
+ ## Benefits Summary
353
+
354
+ ### 1. **Type Safety**
355
+ ```typescript
356
+ const result = await checkMemoryAccess(id, user);
357
+
358
+ if (result.status === 'granted') {
359
+ // TypeScript knows result.memory exists
360
+ console.log(result.memory.content);
361
+
362
+ // TypeScript error: Property 'required_trust' does not exist
363
+ // console.log(result.required_trust); ❌
364
+ }
365
+ ```
366
+
367
+ ### 2. **Exhaustive Checking**
368
+ ```typescript
369
+ function handleAccess(result: AccessResult) {
370
+ switch (result.status) {
371
+ case 'granted':
372
+ return result.memory;
373
+ case 'insufficient_trust':
374
+ return null;
375
+ case 'blocked':
376
+ return null;
377
+ // TypeScript error if we forget a case!
378
+ }
379
+ }
380
+ ```
381
+
382
+ ### 3. **Rich Context**
383
+ ```typescript
384
+ // Each failure type has specific, relevant information
385
+ if (result.status === 'insufficient_trust') {
386
+ console.log(`Need ${result.trust_deficit.toFixed(2)} more trust`);
387
+ console.log(`${result.attempts_remaining} attempts left`);
388
+ }
389
+ ```
390
+
391
+ ### 4. **Better Error Handling**
392
+ ```typescript
393
+ // No try/catch needed for expected failures
394
+ const result = await checkMemoryAccess(id, user);
395
+
396
+ // Handle each case appropriately
397
+ if (result.status !== 'granted') {
398
+ logAccessDenied(result);
399
+ return null;
400
+ }
401
+
402
+ // Continue with granted access
403
+ processMemory(result.memory);
404
+ ```
405
+
406
+ ---
407
+
408
+ ## Comparison
409
+
410
+ ### Exceptions (Bad)
411
+ ```typescript
412
+ try {
413
+ const memory = await checkMemoryAccess(id, user);
414
+ return memory;
415
+ } catch (error) {
416
+ // String parsing, no type safety
417
+ if (error.message.includes('blocked')) {
418
+ // How many attempts? When blocked? Unknown!
419
+ return null;
420
+ }
421
+ }
422
+ ```
423
+
424
+ ### Result Type (Good)
425
+ ```typescript
426
+ const result = await checkMemoryAccess(id, user);
427
+
428
+ if (result.status === 'blocked') {
429
+ // All info available, type-safe
430
+ console.log(`Blocked at ${result.blocked_at}`);
431
+ console.log(`After ${result.attempt_count} attempts`);
432
+ return null;
433
+ }
434
+ ```
435
+
436
+ ---
437
+
438
+ ## Recommendation
439
+
440
+ **Use discriminated unions (Result types) for all access control operations**:
441
+
442
+ ✅ `checkMemoryAccess()` → `AccessResult`
443
+ ✅ `checkPermission()` → `PermissionResult`
444
+ ✅ `validateTrust()` → `TrustResult`
445
+ ✅ `createMemory()` → `CreateResult`
446
+ ✅ `updateMemory()` → `UpdateResult`
447
+
448
+ **Reserve exceptions for truly exceptional cases**:
449
+ - Database connection failures
450
+ - Network errors
451
+ - Programming errors (bugs)
452
+ - System failures
453
+
454
+ ---
455
+
456
+ **Status**: Design Specification
457
+ **Pattern**: Discriminated Union (Result Type)
458
+ **Benefit**: Type-safe, exhaustive, rich context for each failure case