import express from 'express';
import { Request, Response } from 'express';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import dotenv from 'dotenv';
import { RedisClient } from './redis-client.js';
import { AIService } from './ai-service.js';
import { MCPTools } from './mcp-tools.js';
dotenv.config();
const app = express();
app.use(express.json());
const PORT = parseInt(process.env.SERVER_PORT || '3000', 10);
const REDIS_URL = process.env.REDIS_URL || 'redis://localhost:6379';
const OPENAI_API_KEY = process.env.OPENAI_API_KEY || '';
const NODE_ID = process.env.NODE_ID || 'node-unknown';
const redisClient = new RedisClient(REDIS_URL);
const aiService = new AIService(OPENAI_API_KEY);
const mcpTools = new MCPTools({
redisClient,
aiService,
});
await redisClient.connect();
console.log(`[${NODE_ID}] Connected to Redis at ${REDIS_URL}`);
app.get('/health', async (_req: Request, res: Response) => {
try {
const redisHealthy = await redisClient.ping();
if (redisHealthy) {
res.status(200).json({
status: 'healthy',
node: NODE_ID,
redis: 'connected',
timestamp: new Date().toISOString(),
});
} else {
res.status(503).json({
status: 'unhealthy',
node: NODE_ID,
redis: 'disconnected',
timestamp: new Date().toISOString(),
});
}
} catch (error) {
res.status(503).json({
status: 'unhealthy',
node: NODE_ID,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString(),
});
}
});
app.get('/', (_req: Request, res: Response) => {
res.json({
name: 'MCP Todo Server',
node: NODE_ID,
version: '1.0.0',
endpoints: {
health: '/health',
mcp: '/mcp',
},
});
});
app.get('/mcp', async (req: Request, res: Response) => {
console.log(`[${NODE_ID}] New MCP connection via GET /mcp`);
const transport = new SSEServerTransport('/mcp', res);
await mcpTools.getServer().connect(transport);
req.on('close', () => {
console.log(`[${NODE_ID}] MCP connection closed`);
});
});
app.post('/mcp', async (_req: Request, res: Response) => {
console.log(`[${NODE_ID}] New MCP message via POST /mcp`);
const transport = new SSEServerTransport('/mcp', res);
await mcpTools.getServer().connect(transport);
});
process.on('SIGTERM', async () => {
console.log(`[${NODE_ID}] SIGTERM received, shutting down gracefully...`);
await redisClient.disconnect();
process.exit(0);
});
process.on('SIGINT', async () => {
console.log(`[${NODE_ID}] SIGINT received, shutting down gracefully...`);
await redisClient.disconnect();
process.exit(0);
});
app.listen(PORT, () => {
console.log(`[${NODE_ID}] MCP Todo Server listening on port ${PORT}`);
console.log(`[${NODE_ID}] Health endpoint: http://localhost:${PORT}/health`);
console.log(`[${NODE_ID}] MCP endpoint: http://localhost:${PORT}/mcp`);
});