UNPKG

@siva-sub/mcp-public-transport

Version:

A Model Context Protocol server for Singapore transport data with real-time information and routing

144 lines (143 loc) 6 kB
import { z } from 'zod'; import { BaseTool } from '../base.js'; import { SingaporeTimeService } from '../../services/time.js'; import { validateInput } from '../../utils/validation.js'; import { logger } from '../../utils/logger.js'; const TrainStatusInputSchema = z.object({ line: z.enum(['EWL', 'NSL', 'CCL', 'DTL', 'NEL', 'BPL', 'SLRT', 'PLRT', 'TEL']).optional(), }); export class TrainStatusTool extends BaseTool { ltaService; timeService; constructor(ltaService) { super(); this.ltaService = ltaService; this.timeService = new SingaporeTimeService(); } getDefinitions() { return [ { name: 'get_train_service_status', description: 'Check current train service status and any disruptions', inputSchema: this.createSchema({ line: { type: 'string', enum: ['EWL', 'NSL', 'CCL', 'DTL', 'NEL', 'BPL', 'SLRT', 'PLRT', 'TEL'], description: 'Specific train line to check (optional)', }, }), }, ]; } canHandle(toolName) { return toolName === 'get_train_service_status'; } async execute(toolName, args) { try { const { line } = validateInput(TrainStatusInputSchema, args); logger.info('Getting train service status', { line }); const alerts = await this.ltaService.getTrainServiceAlerts(); // Filter by line if specified const filteredAlerts = line ? alerts.filter(alert => alert.line === line) : alerts; const allLines = ['EWL', 'NSL', 'CCL', 'DTL', 'NEL', 'BPL', 'SLRT', 'PLRT', 'TEL']; const affectedLines = new Set(alerts.map(alert => alert.line)); const normalLines = allLines.filter(l => !affectedLines.has(l)); const overallStatus = alerts.length === 0 ? 'normal' : alerts.length < 3 ? 'minor_disruptions' : 'major_disruptions'; // Get Singapore time context const timeContext = this.timeService.getCurrentTime(); const operatingStatus = this.getOperatingStatus(timeContext); return { overallStatus, summary: this.createStatusSummary(alerts, operatingStatus), disruptions: filteredAlerts, affectedLines: Array.from(affectedLines), normalLines, recommendations: this.generateRecommendations(alerts, operatingStatus), singaporeTime: timeContext.formatted.full, operatingHours: operatingStatus, timestamp: timeContext.datetime, }; } catch (error) { logger.error(`Train status tool failed: ${toolName}`, error); return this.formatError(error, toolName); } } getOperatingStatus(timeContext) { const hour = new Date(timeContext.datetime).getHours(); // MRT/LRT typically operates from 5:30 AM to midnight/1 AM if (hour >= 6 && hour < 24) { return { status: 'operating', description: 'All train services are within normal operating hours (6:00 AM - 12:00 AM)' }; } else if (hour >= 0 && hour < 6) { return { status: 'limited', description: 'Train services are currently not operating (Normal hours: 6:00 AM - 12:00 AM)' }; } else { return { status: 'limited', description: 'Train services may be ending soon (Last trains around 12:00 AM - 1:00 AM)' }; } } createStatusSummary(alerts, operatingStatus) { if (!operatingStatus || operatingStatus.status !== 'operating') { return operatingStatus?.description || 'Train services are not currently operating'; } if (alerts.length === 0) { return 'All train services are operating normally'; } const lineCount = new Set(alerts.map(alert => alert.line)).size; return `${lineCount} line${lineCount > 1 ? 's' : ''} experiencing disruptions`; } generateRecommendations(alerts, operatingStatus) { const recommendations = []; if (alerts.length === 0) { recommendations.push('No service recommendations needed - all lines operating normally'); return recommendations; } // Group alerts by line const alertsByLine = alerts.reduce((acc, alert) => { if (!acc[alert.line]) { acc[alert.line] = []; } acc[alert.line].push(alert); return acc; }, {}); for (const [line, lineAlerts] of Object.entries(alertsByLine)) { const hasShuttle = lineAlerts.some(alert => alert.shuttleService); const hasFreeBus = lineAlerts.some(alert => alert.alternativeTransport); if (hasShuttle) { recommendations.push(`${line}: Free shuttle service available`); } if (hasFreeBus) { recommendations.push(`${line}: Free bus service operating`); } // Suggest alternative lines const alternatives = this.getAlternativeLines(line); if (alternatives.length > 0) { recommendations.push(`${line}: Consider using ${alternatives.join(' or ')} as alternatives`); } } return recommendations; } getAlternativeLines(affectedLine) { const alternatives = { 'EWL': ['NSL', 'CCL'], 'NSL': ['EWL', 'NEL'], 'CCL': ['EWL', 'NSL'], 'DTL': ['NEL', 'CCL'], 'NEL': ['NSL', 'DTL'], 'TEL': ['NSL', 'CCL'], }; return alternatives[affectedLine] || []; } }