Skip to content

Commit a0d5bbc

Browse files
committed
Refactor: Implement Robust Startup Sequence and Health Checks in Memory Core #7636
1 parent 199411c commit a0d5bbc

2 files changed

Lines changed: 107 additions & 4 deletions

File tree

ai/mcp/server/memory-core/mcp-stdio.mjs

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {CallToolRequestSchema, ListToolsRequestSchema} from '@modelcontextprotoc
44
import Neo from '../../../../src/Neo.mjs';
55
import * as core from '../../../../src/core/_export.mjs';
66
import InstanceManager from '../../../../src/manager/Instance.mjs';
7+
import HealthService from './services/HealthService.mjs';
8+
import SessionService from './services/SessionService.mjs';
79
import logger from './logger.mjs';
810
import {listTools, callTool} from './services/toolService.mjs';
911

@@ -51,7 +53,26 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
5153
const { name, arguments: args } = request.params;
5254

5355
try {
54-
logger.error(`[MCP] Calling tool: ${name} with args:`, JSON.stringify(args));
56+
logger.debug(`[MCP] Calling tool: ${name} with args:`, JSON.stringify(args));
57+
58+
const exemptFromHealthCheck = ['healthcheck', 'start_database', 'stop_database'];
59+
60+
// Perform health check before tool execution (with caching)
61+
// Skip for lifecycle and healthcheck tools to avoid circular dependencies
62+
if (!exemptFromHealthCheck.includes(name)) {
63+
try {
64+
await HealthService.ensureHealthy();
65+
} catch (healthError) {
66+
logger.error(`[MCP] Health check failed for tool ${name}:`, healthError.message);
67+
return {
68+
content: [{
69+
type: 'text',
70+
text: `Cannot execute ${name}: ${healthError.message}`
71+
}],
72+
isError: true
73+
};
74+
}
75+
}
5576

5677
const result = await callTool(name, args);
5778

@@ -95,12 +116,94 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
95116
}
96117
});
97118

98-
// Start the stdio transport
119+
/**
120+
* Proactively summarizes unsummarized sessions on startup.
121+
* Runs asynchronously to avoid blocking server startup.
122+
*
123+
* @returns {Promise<void>}
124+
*/
125+
async function summarizeSessionsOnStartup() {
126+
logger.info('[Startup] Checking for unsummarized sessions...');
127+
128+
try {
129+
const result = await SessionService.summarizeSessions({});
130+
131+
if (result.processed > 0) {
132+
logger.info(`✅ [Startup] Summarized ${result.processed} session(s):`);
133+
result.sessions.forEach(session => {
134+
logger.info(` - ${session.title} (${session.memoryCount} memories)`);
135+
});
136+
} else {
137+
logger.info('[Startup] No unsummarized sessions found');
138+
}
139+
} catch (error) {
140+
logger.warn('⚠️ [Startup] Session summarization failed:', error.message);
141+
logger.warn(' You can manually trigger summarization using the summarize_sessions tool');
142+
}
143+
}
144+
145+
/**
146+
* Main startup sequence for the Memory Core MCP server.
147+
*
148+
* Performs the following steps:
149+
* 1. Health check - verifies ChromaDB connectivity
150+
* 2. Status reporting - logs detailed diagnostics
151+
* 3. Auto-summarization - processes unsummarized sessions (if healthy)
152+
* 4. Server startup - connects stdio transport
153+
*
154+
* The server starts even if ChromaDB is unavailable, but tools will fail
155+
* gracefully with helpful error messages until dependencies are resolved.
156+
*/
99157
async function main() {
158+
// Perform initial health check (non-blocking)
159+
const health = await HealthService.healthcheck();
160+
161+
// Report status based on health check results
162+
if (health.status === 'unhealthy') {
163+
logger.warn('⚠️ [Startup] Memory Core is unhealthy. Server will start but tools will fail until resolved.');
164+
health.details.forEach(detail => logger.warn(` ${detail}`));
165+
166+
// Provide helpful guidance based on process status
167+
if (!health.database.process.running) {
168+
logger.warn(' 💡 Tip: Use the start_database tool after server starts, or run:');
169+
logger.warn(` chroma run --path ${process.env.CHROMA_DATA_PATH || './data/chroma'} --port ${process.env.CHROMA_PORT || '8000'}`);
170+
}
171+
logger.warn(' The server will periodically retry and recover automatically once dependencies are met.');
172+
} else if (health.status === 'degraded') {
173+
logger.warn('⚠️ [Startup] Memory Core is degraded. Some features may be unavailable.');
174+
health.details.forEach(detail => logger.warn(` ${detail}`));
175+
176+
// Still proceed with summarization if ChromaDB is accessible, even without API key
177+
// This allows the user to see what would be summarized
178+
logger.info('✅ [Startup] ChromaDB connectivity confirmed');
179+
if (health.database.connection.collections) {
180+
logger.info(` - Memories: ${health.database.connection.collections.memories.count}`);
181+
logger.info(` - Summaries: ${health.database.connection.collections.summaries.count}`);
182+
}
183+
} else {
184+
// Fully healthy - log success and collection stats
185+
logger.info('✅ [Startup] Memory Core health check passed');
186+
if (health.database.connection.collections) {
187+
logger.info(` - Memories: ${health.database.connection.collections.memories.count}`);
188+
logger.info(` - Summaries: ${health.database.connection.collections.summaries.count}`);
189+
}
190+
191+
// Only auto-summarize if we're fully healthy
192+
if (health.features.summarization) {
193+
// Run summarization asynchronously (non-blocking)
194+
summarizeSessionsOnStartup().catch(err => {
195+
// Error already logged in summarizeSessionsOnStartup
196+
});
197+
} else {
198+
logger.warn('⚠️ [Startup] GEMINI_API_KEY not set - skipping automatic session summarization');
199+
logger.warn(' Set GEMINI_API_KEY environment variable to enable summarization features');
200+
}
201+
}
202+
203+
// Start the stdio transport
100204
const transport = new StdioServerTransport();
101205
await server.connect(transport);
102206

103-
// Log to stderr (stdout is reserved for MCP protocol)
104207
logger.info('[neo-memory-core MCP] Server started on stdio transport');
105208
logger.info('[neo-memory-core MCP] Available tools loaded from OpenAPI spec');
106209
}

ai/mcp/server/memory-core/services/toolService.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const serviceMapping = {
1818
export_database : DatabaseService .exportDatabase .bind(DatabaseService),
1919
get_all_summaries : SummaryService .listSummaries .bind(SummaryService),
2020
get_session_memories: MemoryService .listMemories .bind(MemoryService),
21-
healthcheck : HealthService .healthcheck.bind(HealthService),
21+
healthcheck : HealthService .healthcheck .bind(HealthService),
2222
import_database : DatabaseService .importDatabase .bind(DatabaseService),
2323
query_raw_memories : MemoryService .queryMemories .bind(MemoryService),
2424
query_summaries : SummaryService .querySummaries .bind(SummaryService),

0 commit comments

Comments
 (0)