@prmichaelsen/remember-mcp 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,17 +4,49 @@ Multi-tenant memory system MCP server with vector search, relationships, and tru
4
4
 
5
5
  ## Features
6
6
 
7
- - 24 MCP tools for memory management
7
+ - 10 MCP tools for memory and relationship management
8
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
9
+ - Vector search with Weaviate (semantic + keyword hybrid search)
10
+ - Knowledge graph with relationship tracking
11
+ - RAG queries with natural language
12
+ - 45 content types (notes, events, people, recipes, etc.)
13
+ - Trust-based access control (planned for M7)
14
14
 
15
15
  ## Quick Start
16
16
 
17
- ### Standalone (stdio transport)
17
+ ### Option 1: Use with Claude Desktop (Recommended)
18
+
19
+ Add to your Claude Desktop MCP configuration:
20
+
21
+ **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
22
+ **Linux**: `~/.config/Claude/claude_desktop_config.json`
23
+ **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "remember": {
29
+ "command": "npx",
30
+ "args": ["-y", "@prmichaelsen/remember-mcp"],
31
+ "env": {
32
+ "WEAVIATE_REST_URL": "https://your-instance.weaviate.cloud",
33
+ "WEAVIATE_API_KEY": "your-weaviate-api-key",
34
+ "OPENAI_EMBEDDINGS_API_KEY": "sk-...",
35
+ "FIREBASE_ADMIN_SERVICE_ACCOUNT_KEY": "{\"type\":\"service_account\",\"project_id\":\"your-project\",\"private_key\":\"-----BEGIN PRIVATE KEY-----\\nYOUR_KEY\\n-----END PRIVATE KEY-----\\n\",\"client_email\":\"firebase-adminsdk@your-project.iam.gserviceaccount.com\"}",
36
+ "FIREBASE_PROJECT_ID": "your-project-id"
37
+ }
38
+ }
39
+ }
40
+ }
41
+ ```
42
+
43
+ **Important**:
44
+ - Use `\\n` (double backslash) for newlines in private_key
45
+ - Escape all quotes with `\"`
46
+ - Get Weaviate Cloud at https://console.weaviate.cloud
47
+ - Get Firebase service account from Firebase Console → Project Settings → Service Accounts
48
+
49
+ ### Option 2: Standalone (stdio transport)
18
50
 
19
51
  ```bash
20
52
  # Install dependencies
@@ -32,10 +64,10 @@ npm run build
32
64
  npm start
33
65
  ```
34
66
 
35
- ### With mcp-auth (multi-tenant production)
67
+ ### Option 3: With mcp-auth (multi-tenant production)
36
68
 
37
69
  ```typescript
38
- import { wrapServer, JWTAuthProvider, APITokenResolver } from '@prmichaelsen/mcp-auth';
70
+ import { wrapServer, JWTAuthProvider } from '@prmichaelsen/mcp-auth';
39
71
  import { createServer } from '@prmichaelsen/remember-mcp/factory';
40
72
 
41
73
  const wrapped = wrapServer({
@@ -43,10 +75,7 @@ const wrapped = wrapServer({
43
75
  authProvider: new JWTAuthProvider({
44
76
  jwtSecret: process.env.JWT_SECRET
45
77
  }),
46
- tokenResolver: new APITokenResolver({
47
- tenantManagerUrl: process.env.TENANT_MANAGER_URL,
48
- serviceToken: process.env.SERVICE_TOKEN
49
- }),
78
+ // tokenResolver not needed - remember-mcp is self-managed
50
79
  resourceType: 'remember',
51
80
  transport: { type: 'sse', port: 3000 }
52
81
  });
@@ -630,24 +630,40 @@ function shouldLog(level) {
630
630
  return LOG_LEVELS[level] >= currentLevel;
631
631
  }
632
632
  var logger = {
633
- debug: (message, ...args) => {
633
+ debug: (message, data) => {
634
634
  if (shouldLog("debug")) {
635
- console.debug(`[DEBUG] ${message}`, ...args);
635
+ if (data) {
636
+ console.debug(JSON.stringify({ level: "DEBUG", message, ...data }));
637
+ } else {
638
+ console.debug(`[DEBUG] ${message}`);
639
+ }
636
640
  }
637
641
  },
638
- info: (message, ...args) => {
642
+ info: (message, data) => {
639
643
  if (shouldLog("info")) {
640
- console.info(`[INFO] ${message}`, ...args);
644
+ if (data) {
645
+ console.info(JSON.stringify({ level: "INFO", message, ...data }));
646
+ } else {
647
+ console.info(`[INFO] ${message}`);
648
+ }
641
649
  }
642
650
  },
643
- warn: (message, ...args) => {
651
+ warn: (message, data) => {
644
652
  if (shouldLog("warn")) {
645
- console.warn(`[WARN] ${message}`, ...args);
653
+ if (data) {
654
+ console.warn(JSON.stringify({ level: "WARN", message, ...data }));
655
+ } else {
656
+ console.warn(`[WARN] ${message}`);
657
+ }
646
658
  }
647
659
  },
648
- error: (message, ...args) => {
660
+ error: (message, data) => {
649
661
  if (shouldLog("error")) {
650
- console.error(`[ERROR] ${message}`, ...args);
662
+ if (data) {
663
+ console.error(JSON.stringify({ level: "ERROR", message, ...data }));
664
+ } else {
665
+ console.error(`[ERROR] ${message}`);
666
+ }
651
667
  }
652
668
  }
653
669
  };
@@ -2700,8 +2716,14 @@ async function ensureDatabasesInitialized() {
2700
2716
  databasesInitialized = true;
2701
2717
  logger.info("Databases initialized successfully");
2702
2718
  } catch (error) {
2703
- logger.error("Database initialization failed:", error);
2704
- throw error;
2719
+ const errorMessage = error instanceof Error ? error.message : String(error);
2720
+ const errorStack = error instanceof Error ? error.stack : void 0;
2721
+ logger.error("Database initialization failed", {
2722
+ error: errorMessage,
2723
+ stack: errorStack,
2724
+ type: error instanceof Error ? error.constructor.name : "Unknown"
2725
+ });
2726
+ throw new Error(`Database initialization failed: ${errorMessage}`);
2705
2727
  } finally {
2706
2728
  initializationPromise = null;
2707
2729
  }
@@ -2709,9 +2731,6 @@ async function ensureDatabasesInitialized() {
2709
2731
  return initializationPromise;
2710
2732
  }
2711
2733
  function createServer(accessToken, userId, options = {}) {
2712
- if (!accessToken) {
2713
- throw new Error("accessToken is required");
2714
- }
2715
2734
  if (!userId) {
2716
2735
  throw new Error("userId is required");
2717
2736
  }
package/dist/server.js CHANGED
@@ -308,7 +308,7 @@ var require_main = __commonJS({
308
308
  return { parsed: parsedAll };
309
309
  }
310
310
  }
311
- function config2(options) {
311
+ function config3(options) {
312
312
  if (_dotenvKey(options).length === 0) {
313
313
  return DotenvModule.configDotenv(options);
314
314
  }
@@ -375,7 +375,7 @@ var require_main = __commonJS({
375
375
  configDotenv,
376
376
  _configVault,
377
377
  _parseVault,
378
- config: config2,
378
+ config: config3,
379
379
  decrypt,
380
380
  parse,
381
381
  populate
@@ -564,24 +564,40 @@ function shouldLog(level) {
564
564
  return LOG_LEVELS[level] >= currentLevel;
565
565
  }
566
566
  var logger = {
567
- debug: (message, ...args) => {
567
+ debug: (message, data) => {
568
568
  if (shouldLog("debug")) {
569
- console.debug(`[DEBUG] ${message}`, ...args);
569
+ if (data) {
570
+ console.debug(JSON.stringify({ level: "DEBUG", message, ...data }));
571
+ } else {
572
+ console.debug(`[DEBUG] ${message}`);
573
+ }
570
574
  }
571
575
  },
572
- info: (message, ...args) => {
576
+ info: (message, data) => {
573
577
  if (shouldLog("info")) {
574
- console.info(`[INFO] ${message}`, ...args);
578
+ if (data) {
579
+ console.info(JSON.stringify({ level: "INFO", message, ...data }));
580
+ } else {
581
+ console.info(`[INFO] ${message}`);
582
+ }
575
583
  }
576
584
  },
577
- warn: (message, ...args) => {
585
+ warn: (message, data) => {
578
586
  if (shouldLog("warn")) {
579
- console.warn(`[WARN] ${message}`, ...args);
587
+ if (data) {
588
+ console.warn(JSON.stringify({ level: "WARN", message, ...data }));
589
+ } else {
590
+ console.warn(`[WARN] ${message}`);
591
+ }
580
592
  }
581
593
  },
582
- error: (message, ...args) => {
594
+ error: (message, data) => {
583
595
  if (shouldLog("error")) {
584
- console.error(`[ERROR] ${message}`, ...args);
596
+ if (data) {
597
+ console.error(JSON.stringify({ level: "ERROR", message, ...data }));
598
+ } else {
599
+ console.error(`[ERROR] ${message}`);
600
+ }
585
601
  }
586
602
  }
587
603
  };
@@ -2643,14 +2659,6 @@ function registerHandlers(server) {
2643
2659
  server.setRequestHandler(ListToolsRequestSchema, async () => {
2644
2660
  return {
2645
2661
  tools: [
2646
- {
2647
- name: "health_check",
2648
- description: "Check server health and database connections",
2649
- inputSchema: {
2650
- type: "object",
2651
- properties: {}
2652
- }
2653
- },
2654
2662
  // Memory tools
2655
2663
  createMemoryTool,
2656
2664
  searchMemoryTool,
@@ -2672,9 +2680,6 @@ function registerHandlers(server) {
2672
2680
  let result;
2673
2681
  const userId = args.user_id || "default_user";
2674
2682
  switch (name) {
2675
- case "health_check":
2676
- result = await handleHealthCheck();
2677
- break;
2678
2683
  case "remember_create_memory":
2679
2684
  result = await handleCreateMemory(args, userId);
2680
2685
  break;
@@ -2731,42 +2736,6 @@ function registerHandlers(server) {
2731
2736
  }
2732
2737
  });
2733
2738
  }
2734
- async function handleHealthCheck() {
2735
- const health = {
2736
- status: "healthy",
2737
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2738
- server: {
2739
- name: "remember-mcp",
2740
- version: "0.1.0"
2741
- },
2742
- databases: {
2743
- weaviate: {
2744
- connected: false,
2745
- url: config.weaviate.url
2746
- },
2747
- firestore: {
2748
- connected: false,
2749
- projectId: config.firebase.projectId
2750
- }
2751
- }
2752
- };
2753
- try {
2754
- const weaviateClient = getWeaviateClient();
2755
- health.databases.weaviate.connected = await weaviateClient.isReady();
2756
- } catch (error) {
2757
- logger.error("Weaviate health check failed:", error);
2758
- health.databases.weaviate.connected = false;
2759
- }
2760
- try {
2761
- health.databases.firestore.connected = await testFirestoreConnection();
2762
- } catch (error) {
2763
- logger.error("Firestore health check failed:", error);
2764
- health.databases.firestore.connected = false;
2765
- }
2766
- const allHealthy = health.databases.weaviate.connected && health.databases.firestore.connected;
2767
- health.status = allHealthy ? "healthy" : "degraded";
2768
- return JSON.stringify(health, null, 2);
2769
- }
2770
2739
  async function main() {
2771
2740
  try {
2772
2741
  logger.info("Starting remember-mcp server...");
@@ -1,7 +1,7 @@
1
1
  export declare const logger: {
2
- debug: (message: string, ...args: any[]) => void;
3
- info: (message: string, ...args: any[]) => void;
4
- warn: (message: string, ...args: any[]) => void;
5
- error: (message: string, ...args: any[]) => void;
2
+ debug: (message: string, data?: any) => void;
3
+ info: (message: string, data?: any) => void;
4
+ warn: (message: string, data?: any) => void;
5
+ error: (message: string, data?: any) => void;
6
6
  };
7
7
  //# sourceMappingURL=logger.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prmichaelsen/remember-mcp",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Multi-tenant memory system MCP server with vector search and relationships",
5
5
  "main": "dist/server.js",
6
6
  "type": "module",
@@ -49,6 +49,7 @@
49
49
  "dependencies": {
50
50
  "@modelcontextprotocol/sdk": "^1.0.4",
51
51
  "@prmichaelsen/firebase-admin-sdk-v8": "^2.2.0",
52
+ "@prmichaelsen/mcp-auth": "^7.0.4",
52
53
  "dotenv": "^16.4.5",
53
54
  "weaviate-client": "^3.2.0"
54
55
  },
@@ -9,8 +9,10 @@ describe('Server Factory', () => {
9
9
  expect(server).toHaveProperty('setRequestHandler');
10
10
  });
11
11
 
12
- it('should require accessToken', () => {
13
- expect(() => createServer('', 'user123')).toThrow('accessToken is required');
12
+ it('should allow empty accessToken (not used by remember-mcp)', () => {
13
+ // accessToken is not used by remember-mcp (self-managed data)
14
+ // Should not throw even with empty string
15
+ expect(() => createServer('', 'user123')).not.toThrow();
14
16
  });
15
17
 
16
18
  it('should require userId', () => {
@@ -59,8 +59,17 @@ async function ensureDatabasesInitialized(): Promise<void> {
59
59
  databasesInitialized = true;
60
60
  logger.info('Databases initialized successfully');
61
61
  } catch (error) {
62
- logger.error('Database initialization failed:', error);
63
- throw error;
62
+ // Format error as single-line JSON for better cloud logging
63
+ const errorMessage = error instanceof Error ? error.message : String(error);
64
+ const errorStack = error instanceof Error ? error.stack : undefined;
65
+
66
+ logger.error('Database initialization failed', {
67
+ error: errorMessage,
68
+ stack: errorStack,
69
+ type: error instanceof Error ? error.constructor.name : 'Unknown'
70
+ });
71
+
72
+ throw new Error(`Database initialization failed: ${errorMessage}`);
64
73
  } finally {
65
74
  initializationPromise = null;
66
75
  }
@@ -105,9 +114,8 @@ export function createServer(
105
114
  userId: string,
106
115
  options: ServerOptions = {}
107
116
  ): Server {
108
- if (!accessToken) {
109
- throw new Error('accessToken is required');
110
- }
117
+ // Note: accessToken is not used by remember-mcp (self-managed data)
118
+ // but required by mcp-auth contract. Can be any value including empty string.
111
119
 
112
120
  if (!userId) {
113
121
  throw new Error('userId is required');
package/src/server.ts CHANGED
@@ -79,14 +79,6 @@ function registerHandlers(server: Server): void {
79
79
  server.setRequestHandler(ListToolsRequestSchema, async () => {
80
80
  return {
81
81
  tools: [
82
- {
83
- name: 'health_check',
84
- description: 'Check server health and database connections',
85
- inputSchema: {
86
- type: 'object',
87
- properties: {},
88
- },
89
- },
90
82
  // Memory tools
91
83
  createMemoryTool,
92
84
  searchMemoryTool,
@@ -115,10 +107,6 @@ function registerHandlers(server: Server): void {
115
107
  const userId = (args as any).user_id || 'default_user';
116
108
 
117
109
  switch (name) {
118
- case 'health_check':
119
- result = await handleHealthCheck();
120
- break;
121
-
122
110
  case 'remember_create_memory':
123
111
  result = await handleCreateMemory(args as any, userId);
124
112
  break;
@@ -188,56 +176,6 @@ function registerHandlers(server: Server): void {
188
176
  });
189
177
  }
190
178
 
191
- /**
192
- * Health check handler
193
- */
194
- async function handleHealthCheck(): Promise<string> {
195
- const health = {
196
- status: 'healthy',
197
- timestamp: new Date().toISOString(),
198
- server: {
199
- name: 'remember-mcp',
200
- version: '0.1.0',
201
- },
202
- databases: {
203
- weaviate: {
204
- connected: false,
205
- url: config.weaviate.url,
206
- },
207
- firestore: {
208
- connected: false,
209
- projectId: config.firebase.projectId,
210
- },
211
- },
212
- };
213
-
214
- try {
215
- // Test Weaviate connection
216
- const weaviateClient = getWeaviateClient();
217
- health.databases.weaviate.connected = await weaviateClient.isReady();
218
- } catch (error) {
219
- logger.error('Weaviate health check failed:', error);
220
- health.databases.weaviate.connected = false;
221
- }
222
-
223
- try {
224
- // Test Firestore connection
225
- health.databases.firestore.connected = await testFirestoreConnection();
226
- } catch (error) {
227
- logger.error('Firestore health check failed:', error);
228
- health.databases.firestore.connected = false;
229
- }
230
-
231
- // Overall status
232
- const allHealthy =
233
- health.databases.weaviate.connected &&
234
- health.databases.firestore.connected;
235
-
236
- health.status = allHealthy ? 'healthy' : 'degraded';
237
-
238
- return JSON.stringify(health, null, 2);
239
- }
240
-
241
179
  /**
242
180
  * Main server startup
243
181
  */
@@ -16,27 +16,44 @@ function shouldLog(level: LogLevel): boolean {
16
16
  }
17
17
 
18
18
  export const logger = {
19
- debug: (message: string, ...args: any[]) => {
19
+ debug: (message: string, data?: any) => {
20
20
  if (shouldLog('debug')) {
21
- console.debug(`[DEBUG] ${message}`, ...args);
21
+ if (data) {
22
+ console.debug(JSON.stringify({ level: 'DEBUG', message, ...data }));
23
+ } else {
24
+ console.debug(`[DEBUG] ${message}`);
25
+ }
22
26
  }
23
27
  },
24
28
 
25
- info: (message: string, ...args: any[]) => {
29
+ info: (message: string, data?: any) => {
26
30
  if (shouldLog('info')) {
27
- console.info(`[INFO] ${message}`, ...args);
31
+ if (data) {
32
+ console.info(JSON.stringify({ level: 'INFO', message, ...data }));
33
+ } else {
34
+ console.info(`[INFO] ${message}`);
35
+ }
28
36
  }
29
37
  },
30
38
 
31
- warn: (message: string, ...args: any[]) => {
39
+ warn: (message: string, data?: any) => {
32
40
  if (shouldLog('warn')) {
33
- console.warn(`[WARN] ${message}`, ...args);
41
+ if (data) {
42
+ console.warn(JSON.stringify({ level: 'WARN', message, ...data }));
43
+ } else {
44
+ console.warn(`[WARN] ${message}`);
45
+ }
34
46
  }
35
47
  },
36
48
 
37
- error: (message: string, ...args: any[]) => {
49
+ error: (message: string, data?: any) => {
38
50
  if (shouldLog('error')) {
39
- console.error(`[ERROR] ${message}`, ...args);
51
+ if (data) {
52
+ // Structured logging for cloud environments
53
+ console.error(JSON.stringify({ level: 'ERROR', message, ...data }));
54
+ } else {
55
+ console.error(`[ERROR] ${message}`);
56
+ }
40
57
  }
41
58
  },
42
59
  };