UNPKG

@siva-sub/mcp-public-transport

Version:

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

150 lines (149 loc) 7.19 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SingaporeTransportServer = void 0; const types_js_1 = require("@modelcontextprotocol/sdk/types.js"); const lta_js_1 = require("./services/lta.js"); const onemap_js_1 = require("./services/onemap.js"); const cache_js_1 = require("./services/cache.js"); const logger_js_1 = require("./utils/logger.js"); const errors_js_1 = require("./utils/errors.js"); // Import all tools const arrival_js_1 = require("./tools/bus/arrival.js"); const stops_js_1 = require("./tools/bus/stops.js"); const search_js_1 = require("./tools/bus/search.js"); const details_js_1 = require("./tools/bus/details.js"); const status_js_1 = require("./tools/train/status.js"); const comprehensive_js_1 = require("./tools/routing/comprehensive.js"); const availability_js_1 = require("./tools/taxi/availability.js"); const search_js_2 = require("./tools/location/search.js"); const landmarks_js_1 = require("./tools/location/landmarks.js"); const conditions_js_1 = require("./tools/weather/conditions.js"); // Import enhanced services const postalCode_js_1 = require("./services/postalCode.js"); const time_js_1 = require("./services/time.js"); const fuzzySearch_js_1 = require("./services/fuzzySearch.js"); const weather_js_1 = require("./services/weather.js"); const themes_js_1 = require("./services/themes.js"); class SingaporeTransportServer { constructor(config) { this.config = config; this.tools = []; this.cacheService = new cache_js_1.CacheService(config.cacheDuration); this.ltaService = new lta_js_1.LTAService(config.ltaAccountKey, this.cacheService, config.requestTimeout); this.oneMapService = new onemap_js_1.OneMapService(undefined, // No static token needed config.oneMapEmail, config.oneMapPassword, this.cacheService, config.requestTimeout); } async setupTools(server) { // Initialize enhanced services const postalCodeService = new postalCode_js_1.PostalCodeService(this.oneMapService, this.cacheService); const timeService = new time_js_1.SingaporeTimeService(); const fuzzySearchService = new fuzzySearch_js_1.FuzzySearchService(); const weatherService = new weather_js_1.WeatherService(this.cacheService); const themesService = new themes_js_1.ThemesService(this.oneMapService, this.cacheService); // Initialize all tools this.tools = [ new arrival_js_1.BusArrivalTool(this.ltaService), new stops_js_1.BusStopsTool(this.ltaService, this.oneMapService), new search_js_1.BusStopSearchTool(this.ltaService, this.oneMapService, fuzzySearchService), new details_js_1.BusStopDetailsTool(this.ltaService, this.oneMapService), new status_js_1.TrainStatusTool(this.ltaService), new comprehensive_js_1.ComprehensiveJourneyTool(this.oneMapService, this.ltaService, weatherService), new availability_js_1.TaxiAvailabilityTool(this.ltaService, this.oneMapService), new search_js_2.LocationSearchTool(this.oneMapService, postalCodeService, timeService, fuzzySearchService), new landmarks_js_1.LandmarksDiscoveryTool(themesService, this.oneMapService), new conditions_js_1.WeatherConditionsTool(weatherService), ]; // Get all tool definitions const toolDefinitions = this.tools.flatMap(tool => tool.getDefinitions()); // Set up tool list handler server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => { logger_js_1.logger.debug('Listing available tools'); return { tools: toolDefinitions, }; }); // Set up tool call handler server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { logger_js_1.logger.info(`Executing tool: ${name}`, { arguments: args ? Object.keys(args) : [], }); // Find and execute the appropriate tool for (const tool of this.tools) { if (tool.canHandle(name)) { const startTime = Date.now(); const result = await tool.execute(name, args || {}); const duration = Date.now() - startTime; logger_js_1.logger.info(`Tool executed successfully: ${name}`, { duration: `${duration}ms`, hasError: !!result.error, }); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } } throw new errors_js_1.TransportError(`Unknown tool: ${name}`, 'TOOL_NOT_FOUND'); } catch (error) { logger_js_1.logger.error(`Tool execution failed: ${name}`, error); if (error instanceof errors_js_1.TransportError) { throw error; } throw new errors_js_1.TransportError(`Tool execution failed: ${error instanceof Error ? error.message : 'Unknown error'}`, 'TOOL_EXECUTION_ERROR'); } }); logger_js_1.logger.info(`Singapore Transport MCP Server initialized with ${toolDefinitions.length} tools`); } async healthCheck() { const checks = { lta_api: false, onemap_api: false, cache: false, }; try { // Test LTA API await this.ltaService.getBusStops(0, 1); checks.lta_api = true; } catch (error) { logger_js_1.logger.warn('LTA API health check failed', error); } try { // Test OneMap API await this.oneMapService.geocode('Singapore'); checks.onemap_api = true; } catch (error) { logger_js_1.logger.warn('OneMap API health check failed', error); } // Test cache try { const testKey = 'health_check_test'; const testValue = { status: 'ok' }; this.cacheService.set(testKey, testValue, 10); const retrievedValue = this.cacheService.get(testKey); if (retrievedValue?.status === 'ok') { checks.cache = true; } this.cacheService.del(testKey); } catch (error) { logger_js_1.logger.warn('Cache health check failed', error); } const isHealthy = Object.values(checks).every(Boolean); return { status: isHealthy ? 'healthy' : 'degraded', checks, cache_stats: this.cacheService.getStats(), timestamp: new Date().toISOString(), }; } } exports.SingaporeTransportServer = SingaporeTransportServer;