@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
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Firestore collection path helpers
3
+ * Following the environment-based prefix + users subcollection pattern
4
+ *
5
+ * Pattern from agentbase.me:
6
+ * - Environment prefix: e0.remember-mcp (dev), remember-mcp (prod)
7
+ * - User-scoped data: {BASE}.users/{user_id}/* (per agent/patterns/firestore-users-pattern-best-practices.md)
8
+ * - Shared data: {BASE}.templates/default, {BASE}.user-permissions
9
+ */
10
+
11
+ const APP_NAME = 'remember-mcp';
12
+
13
+ /**
14
+ * Get the database collection prefix based on environment
15
+ * - Development: Uses ENVIRONMENT env var or DB_PREFIX, defaults to 'e0.remember-mcp'
16
+ * - Production: Uses base 'remember-mcp'
17
+ *
18
+ * This allows developers to use their own database entries as a sandbox per dev or branch.
19
+ */
20
+ function getBasePrefix(): string {
21
+ // Check for explicit environment variable (e0, e1, e2, etc.)
22
+ const environment = process.env.ENVIRONMENT;
23
+ if (environment && environment !== 'production' && environment !== 'prod') {
24
+ return `${environment}.${APP_NAME}`;
25
+ }
26
+
27
+ // Check if we're in development mode
28
+ const isDevelopment = process.env.NODE_ENV === 'development';
29
+
30
+ if (isDevelopment) {
31
+ // Check for custom DB_PREFIX env var first
32
+ const customPrefix = process.env.DB_PREFIX;
33
+ if (customPrefix) {
34
+ return customPrefix;
35
+ }
36
+ // Default to e0.{APP_NAME} in development
37
+ return `e0.${APP_NAME}`;
38
+ }
39
+
40
+ // Production uses the base APP_NAME
41
+ return APP_NAME;
42
+ }
43
+
44
+ export const BASE = getBasePrefix();
45
+
46
+ // ============================================================================
47
+ // USER-SCOPED COLLECTIONS (under users/{user_id}/)
48
+ // Per agent/patterns/firestore-users-pattern-best-practices.md
49
+ // ============================================================================
50
+
51
+ /**
52
+ * Get path to user preferences document
53
+ * Pattern: {BASE}.users/{user_id}/preferences
54
+ */
55
+ export function getUserPreferencesPath(userId: string): string {
56
+ return `${BASE}.users/${userId}/preferences`;
57
+ }
58
+
59
+ /**
60
+ * Get path to user's templates collection
61
+ * Pattern: {BASE}.users/{user_id}/templates
62
+ */
63
+ export function getUserTemplatesPath(userId: string): string {
64
+ return `${BASE}.users/${userId}/templates`;
65
+ }
66
+
67
+ /**
68
+ * Get path to user's access logs collection
69
+ * Pattern: {BASE}.users/{user_id}/access-logs
70
+ */
71
+ export function getUserAccessLogsPath(userId: string): string {
72
+ return `${BASE}.users/${userId}/access-logs`;
73
+ }
74
+
75
+ /**
76
+ * Get path to user's trust relationships collection
77
+ * Pattern: {BASE}.users/{user_id}/trust-relationships
78
+ */
79
+ export function getUserTrustRelationshipsPath(userId: string): string {
80
+ return `${BASE}.users/${userId}/trust-relationships`;
81
+ }
82
+
83
+ // ============================================================================
84
+ // CROSS-USER COLLECTIONS (outside users/)
85
+ // These involve multiple users, so can't be under users/{user_id}/
86
+ // ============================================================================
87
+
88
+ /**
89
+ * Get path to user's allowed accessors collection (permissions)
90
+ * Pattern: {BASE}.user-permissions/{owner_user_id}/allowed-accessors
91
+ *
92
+ * Note: Outside users/ because it involves two users (owner + accessor)
93
+ */
94
+ export function getUserPermissionsPath(ownerUserId: string): string {
95
+ return `${BASE}.user-permissions/${ownerUserId}/allowed-accessors`;
96
+ }
97
+
98
+ /**
99
+ * Get path to specific permission document
100
+ * Pattern: {BASE}.user-permissions/{owner_user_id}/allowed-accessors/{accessor_user_id}
101
+ */
102
+ export function getUserPermissionPath(ownerUserId: string, accessorUserId: string): string {
103
+ return `${BASE}.user-permissions/${ownerUserId}/allowed-accessors/${accessorUserId}`;
104
+ }
105
+
106
+ // ============================================================================
107
+ // SHARED/GLOBAL COLLECTIONS
108
+ // ============================================================================
109
+
110
+ /**
111
+ * Get path to default templates collection
112
+ * Pattern: {BASE}.templates/default
113
+ */
114
+ export function getDefaultTemplatesPath(): string {
115
+ return `${BASE}.templates/default`;
116
+ }
117
+
118
+ /**
119
+ * Get path to specific default template
120
+ * Pattern: {BASE}.templates/default/{template_id}
121
+ */
122
+ export function getDefaultTemplatePath(templateId: string): string {
123
+ return `${BASE}.templates/default/${templateId}`;
124
+ }
@@ -0,0 +1,60 @@
1
+ import { describe, it, expect } from '@jest/globals';
2
+ import { createServer } from './server-factory.js';
3
+
4
+ describe('Server Factory', () => {
5
+ describe('Parameter Validation', () => {
6
+ it('should create server instance with valid parameters', () => {
7
+ const server = createServer('test-token', 'user123');
8
+ expect(server).toBeDefined();
9
+ expect(server).toHaveProperty('setRequestHandler');
10
+ });
11
+
12
+ it('should require accessToken', () => {
13
+ expect(() => createServer('', 'user123')).toThrow('accessToken is required');
14
+ });
15
+
16
+ it('should require userId', () => {
17
+ expect(() => createServer('token', '')).toThrow('userId is required');
18
+ });
19
+
20
+ it('should accept custom options', () => {
21
+ const server = createServer('token', 'user123', {
22
+ name: 'custom-name',
23
+ version: '2.0.0',
24
+ });
25
+ expect(server).toBeDefined();
26
+ });
27
+ });
28
+
29
+ describe('Server Isolation', () => {
30
+ it('should create separate instances for different users', () => {
31
+ const server1 = createServer('token1', 'user1');
32
+ const server2 = createServer('token2', 'user2');
33
+
34
+ expect(server1).not.toBe(server2);
35
+ });
36
+
37
+ it('should scope operations to userId', () => {
38
+ const server = createServer('token', 'user123');
39
+ expect(server).toBeDefined();
40
+ // Note: Actual scoping tested in integration tests
41
+ });
42
+ });
43
+
44
+ describe('Options', () => {
45
+ it('should use default name if not provided', () => {
46
+ const server = createServer('token', 'user123');
47
+ expect(server).toBeDefined();
48
+ });
49
+
50
+ it('should use custom name if provided', () => {
51
+ const server = createServer('token', 'user123', { name: 'custom' });
52
+ expect(server).toBeDefined();
53
+ });
54
+
55
+ it('should use custom version if provided', () => {
56
+ const server = createServer('token', 'user123', { version: '2.0.0' });
57
+ expect(server).toBeDefined();
58
+ });
59
+ });
60
+ });
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Server factory for mcp-auth compatibility
3
+ * Creates isolated server instances per user/tenant
4
+ */
5
+
6
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
7
+ import {
8
+ CallToolRequestSchema,
9
+ ListToolsRequestSchema,
10
+ ErrorCode,
11
+ McpError,
12
+ } from '@modelcontextprotocol/sdk/types.js';
13
+ import { logger } from './utils/logger.js';
14
+
15
+ // Import memory tools
16
+ import { createMemoryTool, handleCreateMemory } from './tools/create-memory.js';
17
+ import { searchMemoryTool, handleSearchMemory } from './tools/search-memory.js';
18
+ import { deleteMemoryTool, handleDeleteMemory } from './tools/delete-memory.js';
19
+
20
+ export interface ServerOptions {
21
+ name?: string;
22
+ version?: string;
23
+ }
24
+
25
+ /**
26
+ * Create a server instance for a specific user/tenant
27
+ *
28
+ * This factory function is compatible with mcp-auth wrapping pattern.
29
+ * It creates isolated server instances with no shared state.
30
+ *
31
+ * @param accessToken - User's access token (reserved for future external APIs)
32
+ * @param userId - User identifier for scoping operations
33
+ * @param options - Optional server configuration
34
+ * @returns Configured MCP Server instance (not connected to transport)
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * // Direct usage
39
+ * const server = createServer('token', 'user123');
40
+ * const transport = new StdioServerTransport();
41
+ * await server.connect(transport);
42
+ *
43
+ * // With mcp-auth
44
+ * import { wrapServer } from '@prmichaelsen/mcp-auth';
45
+ * const wrapped = wrapServer({
46
+ * serverFactory: createServer,
47
+ * authProvider: new JWTAuthProvider({ ... }),
48
+ * tokenResolver: new APITokenResolver({ ... }),
49
+ * resourceType: 'remember',
50
+ * transport: { type: 'sse', port: 3000 }
51
+ * });
52
+ * ```
53
+ */
54
+ export function createServer(
55
+ accessToken: string,
56
+ userId: string,
57
+ options: ServerOptions = {}
58
+ ): Server {
59
+ if (!accessToken) {
60
+ throw new Error('accessToken is required');
61
+ }
62
+
63
+ if (!userId) {
64
+ throw new Error('userId is required');
65
+ }
66
+
67
+ logger.debug('Creating server instance', { userId });
68
+
69
+ // Create MCP server
70
+ const server = new Server(
71
+ {
72
+ name: options.name || 'remember-mcp',
73
+ version: options.version || '0.1.0',
74
+ },
75
+ {
76
+ capabilities: {
77
+ tools: {},
78
+ },
79
+ }
80
+ );
81
+
82
+ // Register handlers with userId scope
83
+ registerHandlers(server, userId, accessToken);
84
+
85
+ return server;
86
+ }
87
+
88
+ /**
89
+ * Register MCP handlers scoped to userId
90
+ */
91
+ function registerHandlers(server: Server, userId: string, accessToken: string): void {
92
+ // List available tools
93
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
94
+ return {
95
+ tools: [
96
+ {
97
+ name: 'health_check',
98
+ description: 'Check server health and database connections',
99
+ inputSchema: {
100
+ type: 'object',
101
+ properties: {},
102
+ },
103
+ },
104
+ createMemoryTool,
105
+ searchMemoryTool,
106
+ deleteMemoryTool,
107
+ ],
108
+ };
109
+ });
110
+
111
+ // Handle tool calls
112
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
113
+ const { name, arguments: args } = request.params;
114
+
115
+ try {
116
+ let result: string;
117
+
118
+ switch (name) {
119
+ case 'health_check':
120
+ result = await handleHealthCheck(userId);
121
+ break;
122
+
123
+ case 'remember_create_memory':
124
+ result = await handleCreateMemory(args as any, userId);
125
+ break;
126
+
127
+ case 'remember_search_memory':
128
+ result = await handleSearchMemory(args as any, userId);
129
+ break;
130
+
131
+ case 'remember_delete_memory':
132
+ result = await handleDeleteMemory(args as any, userId);
133
+ break;
134
+
135
+ default:
136
+ throw new McpError(
137
+ ErrorCode.MethodNotFound,
138
+ `Unknown tool: ${name}`
139
+ );
140
+ }
141
+
142
+ return {
143
+ content: [
144
+ {
145
+ type: 'text',
146
+ text: result,
147
+ },
148
+ ],
149
+ };
150
+ } catch (error) {
151
+ if (error instanceof McpError) {
152
+ throw error;
153
+ }
154
+
155
+ logger.error(`Tool execution failed for ${name}:`, error);
156
+ throw new McpError(
157
+ ErrorCode.InternalError,
158
+ `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`
159
+ );
160
+ }
161
+ });
162
+ }
163
+
164
+ /**
165
+ * Health check handler (scoped to userId)
166
+ */
167
+ async function handleHealthCheck(userId: string): Promise<string> {
168
+ const health = {
169
+ status: 'healthy',
170
+ timestamp: new Date().toISOString(),
171
+ userId: userId,
172
+ server: {
173
+ name: 'remember-mcp',
174
+ version: '0.1.0',
175
+ },
176
+ databases: {
177
+ weaviate: {
178
+ connected: false,
179
+ userCollection: `Memory_${userId}`,
180
+ },
181
+ firestore: {
182
+ connected: false,
183
+ userPath: `users/${userId}`,
184
+ },
185
+ },
186
+ };
187
+
188
+ try {
189
+ // Test Weaviate connection
190
+ const { getWeaviateClient } = await import('./weaviate/client.js');
191
+ const weaviateClient = getWeaviateClient();
192
+ health.databases.weaviate.connected = await weaviateClient.isReady();
193
+ } catch (error) {
194
+ logger.error('Weaviate health check failed:', error);
195
+ health.databases.weaviate.connected = false;
196
+ }
197
+
198
+ try {
199
+ // Test Firestore connection
200
+ const { testFirestoreConnection } = await import('./firestore/init.js');
201
+ health.databases.firestore.connected = await testFirestoreConnection();
202
+ } catch (error) {
203
+ logger.error('Firestore health check failed:', error);
204
+ health.databases.firestore.connected = false;
205
+ }
206
+
207
+ // Overall status
208
+ const allHealthy =
209
+ health.databases.weaviate.connected &&
210
+ health.databases.firestore.connected;
211
+
212
+ health.status = allHealthy ? 'healthy' : 'degraded';
213
+
214
+ return JSON.stringify(health, null, 2);
215
+ }
package/src/server.ts ADDED
@@ -0,0 +1,243 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
4
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
+ import {
6
+ CallToolRequestSchema,
7
+ ListToolsRequestSchema,
8
+ ErrorCode,
9
+ McpError,
10
+ } from '@modelcontextprotocol/sdk/types.js';
11
+ import { config, validateConfig } from './config.js';
12
+ import { initWeaviateClient, testWeaviateConnection, getWeaviateClient } from './weaviate/client.js';
13
+ import { initFirestore, testFirestoreConnection } from './firestore/init.js';
14
+ import { logger } from './utils/logger.js';
15
+
16
+ // Import memory tools
17
+ import { createMemoryTool, handleCreateMemory } from './tools/create-memory.js';
18
+ import { searchMemoryTool, handleSearchMemory } from './tools/search-memory.js';
19
+ import { deleteMemoryTool, handleDeleteMemory } from './tools/delete-memory.js';
20
+
21
+ /**
22
+ * Initialize remember-mcp server
23
+ */
24
+ async function initServer(): Promise<Server> {
25
+ logger.info('Initializing remember-mcp server...');
26
+
27
+ // Validate configuration
28
+ validateConfig();
29
+
30
+ // Initialize databases
31
+ logger.info('Connecting to databases...');
32
+ await initWeaviateClient();
33
+ initFirestore();
34
+
35
+ // Test connections
36
+ const weaviateOk = await testWeaviateConnection();
37
+ const firestoreOk = await testFirestoreConnection();
38
+
39
+ if (!weaviateOk || !firestoreOk) {
40
+ throw new Error('Database connection failed');
41
+ }
42
+
43
+ logger.info('Database connections established');
44
+
45
+ // Create MCP server
46
+ const server = new Server(
47
+ {
48
+ name: 'remember-mcp',
49
+ version: '0.1.0',
50
+ },
51
+ {
52
+ capabilities: {
53
+ tools: {},
54
+ },
55
+ }
56
+ );
57
+
58
+ // Register handlers
59
+ registerHandlers(server);
60
+
61
+ logger.info('Server initialized successfully');
62
+ return server;
63
+ }
64
+
65
+ /**
66
+ * Register MCP handlers
67
+ */
68
+ function registerHandlers(server: Server): void {
69
+ // List available tools
70
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
71
+ return {
72
+ tools: [
73
+ {
74
+ name: 'health_check',
75
+ description: 'Check server health and database connections',
76
+ inputSchema: {
77
+ type: 'object',
78
+ properties: {},
79
+ },
80
+ },
81
+ createMemoryTool,
82
+ searchMemoryTool,
83
+ deleteMemoryTool,
84
+ ],
85
+ };
86
+ });
87
+
88
+ // Handle tool calls
89
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
90
+ const { name, arguments: args } = request.params;
91
+
92
+ try {
93
+ let result: string;
94
+
95
+ // Extract userId from args or use default for now
96
+ // TODO: Get userId from authentication context in M6
97
+ const userId = (args as any).user_id || 'default_user';
98
+
99
+ switch (name) {
100
+ case 'health_check':
101
+ result = await handleHealthCheck();
102
+ break;
103
+
104
+ case 'remember_create_memory':
105
+ result = await handleCreateMemory(args as any, userId);
106
+ break;
107
+
108
+ case 'remember_search_memory':
109
+ result = await handleSearchMemory(args as any, userId);
110
+ break;
111
+
112
+ case 'remember_delete_memory':
113
+ result = await handleDeleteMemory(args as any, userId);
114
+ break;
115
+
116
+ default:
117
+ throw new McpError(
118
+ ErrorCode.MethodNotFound,
119
+ `Unknown tool: ${name}`
120
+ );
121
+ }
122
+
123
+ return {
124
+ content: [
125
+ {
126
+ type: 'text',
127
+ text: result,
128
+ },
129
+ ],
130
+ };
131
+ } catch (error) {
132
+ if (error instanceof McpError) {
133
+ throw error;
134
+ }
135
+
136
+ logger.error(`Tool execution failed for ${name}:`, error);
137
+ throw new McpError(
138
+ ErrorCode.InternalError,
139
+ `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`
140
+ );
141
+ }
142
+ });
143
+ }
144
+
145
+ /**
146
+ * Health check handler
147
+ */
148
+ async function handleHealthCheck(): Promise<string> {
149
+ const health = {
150
+ status: 'healthy',
151
+ timestamp: new Date().toISOString(),
152
+ server: {
153
+ name: 'remember-mcp',
154
+ version: '0.1.0',
155
+ },
156
+ databases: {
157
+ weaviate: {
158
+ connected: false,
159
+ url: config.weaviate.url,
160
+ },
161
+ firestore: {
162
+ connected: false,
163
+ projectId: config.firebase.projectId,
164
+ },
165
+ },
166
+ };
167
+
168
+ try {
169
+ // Test Weaviate connection
170
+ const weaviateClient = getWeaviateClient();
171
+ health.databases.weaviate.connected = await weaviateClient.isReady();
172
+ } catch (error) {
173
+ logger.error('Weaviate health check failed:', error);
174
+ health.databases.weaviate.connected = false;
175
+ }
176
+
177
+ try {
178
+ // Test Firestore connection
179
+ health.databases.firestore.connected = await testFirestoreConnection();
180
+ } catch (error) {
181
+ logger.error('Firestore health check failed:', error);
182
+ health.databases.firestore.connected = false;
183
+ }
184
+
185
+ // Overall status
186
+ const allHealthy =
187
+ health.databases.weaviate.connected &&
188
+ health.databases.firestore.connected;
189
+
190
+ health.status = allHealthy ? 'healthy' : 'degraded';
191
+
192
+ return JSON.stringify(health, null, 2);
193
+ }
194
+
195
+ /**
196
+ * Main server startup
197
+ */
198
+ async function main(): Promise<void> {
199
+ try {
200
+ logger.info('Starting remember-mcp server...');
201
+
202
+ // Initialize server
203
+ const server = await initServer();
204
+
205
+ // Start with stdio transport
206
+ const transport = new StdioServerTransport();
207
+ await server.connect(transport);
208
+
209
+ logger.info('Server running on stdio transport');
210
+
211
+ // Note: When using stdio transport, avoid console.log as it interferes with JSON-RPC
212
+ // The logger is configured to handle this appropriately
213
+ } catch (error) {
214
+ logger.error('Server startup failed:', error);
215
+ process.exit(1);
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Graceful shutdown handler
221
+ */
222
+ function setupShutdownHandlers(server: Server): void {
223
+ const shutdown = async (signal: string) => {
224
+ logger.info(`Received ${signal}, shutting down gracefully...`);
225
+ try {
226
+ await server.close();
227
+ logger.info('Server closed successfully');
228
+ process.exit(0);
229
+ } catch (error) {
230
+ logger.error('Error during shutdown:', error);
231
+ process.exit(1);
232
+ }
233
+ };
234
+
235
+ process.on('SIGINT', () => shutdown('SIGINT'));
236
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
237
+ }
238
+
239
+ // Start the server
240
+ main().catch((error) => {
241
+ logger.error('Fatal error:', error);
242
+ process.exit(1);
243
+ });