UNPKG

@siva-sub/mcp-public-transport

Version:

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

446 lines (445 loc) 19.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WeatherConditionsTool = void 0; const zod_1 = require("zod"); const base_js_1 = require("../base.js"); const logger_js_1 = require("../../utils/logger.js"); const WeatherInputSchema = zod_1.z.object({ location: zod_1.z.object({ latitude: zod_1.z.number().min(1.0).max(1.5), longitude: zod_1.z.number().min(103.0).max(104.5), name: zod_1.z.string().optional(), }).optional(), includeAdvisories: zod_1.z.boolean().default(true), includeForecast: zod_1.z.boolean().default(false), includeHistorical: zod_1.z.boolean().default(false), timeRange: zod_1.z.enum(['current', 'hourly', 'daily']).default('current'), }); class WeatherConditionsTool extends base_js_1.BaseTool { constructor(weatherService) { super(); this.weatherService = weatherService; } getDefinitions() { return [ { name: 'get_weather_conditions', description: 'Get comprehensive real-time weather conditions for Singapore with travel impact analysis and advisories.', inputSchema: this.createSchema({ location: { type: 'object', description: 'Location coordinates (defaults to Singapore center if not provided)', properties: { latitude: { type: 'number', minimum: 1.0, maximum: 1.5, description: 'Latitude coordinate within Singapore bounds', }, longitude: { type: 'number', minimum: 103.0, maximum: 104.5, description: 'Longitude coordinate within Singapore bounds', }, name: { type: 'string', description: 'Location name for reference', }, }, }, includeAdvisories: { type: 'boolean', default: true, description: 'Include weather advisories and travel recommendations', }, includeForecast: { type: 'boolean', default: false, description: 'Include short-term forecast (next few hours)', }, includeHistorical: { type: 'boolean', default: false, description: 'Include recent weather trends', }, timeRange: { type: 'string', enum: ['current', 'hourly', 'daily'], default: 'current', description: 'Time range for weather data', }, }), }, { name: 'get_weather_advisory', description: 'Get weather-based travel and activity advisories for Singapore with specific recommendations.', inputSchema: this.createSchema({ location: { type: 'object', description: 'Location coordinates (defaults to Singapore center if not provided)', properties: { latitude: { type: 'number', minimum: 1.0, maximum: 1.5, description: 'Latitude coordinate within Singapore bounds', }, longitude: { type: 'number', minimum: 103.0, maximum: 104.5, description: 'Longitude coordinate within Singapore bounds', }, name: { type: 'string', description: 'Location name for reference', }, }, }, activityType: { type: 'string', enum: ['walking', 'cycling', 'outdoor_dining', 'sports', 'photography', 'general'], default: 'general', description: 'Type of activity for specific recommendations', }, }), }, ]; } canHandle(toolName) { return ['get_weather_conditions', 'get_weather_advisory'].includes(toolName); } async execute(toolName, args) { try { switch (toolName) { case 'get_weather_conditions': return await this.getWeatherConditions(args); case 'get_weather_advisory': return await this.getWeatherAdvisory(args); default: throw new Error(`Unknown tool: ${toolName}`); } } catch (error) { logger_js_1.logger.error(`Weather tool failed: ${toolName}`, error); return this.formatError(error, toolName); } } async getWeatherConditions(args) { const { location = { latitude: 1.3521, longitude: 103.8198, name: 'Singapore' }, // Default to Singapore center includeAdvisories, includeForecast, includeHistorical, timeRange, } = WeatherInputSchema.parse(args); logger_js_1.logger.info('Getting weather conditions', { location: location.name || `${location.latitude},${location.longitude}`, timeRange, }); // Get current weather conditions const conditions = await this.weatherService.getWeatherConditionsForLocation(location.latitude, location.longitude); // Generate advisories const advisories = includeAdvisories ? this.weatherService.generateWeatherAdvisory(conditions) : []; // Analyze comfort level const comfort = this.analyzeComfortLevel(conditions); // Assess travel impact const travelImpact = this.assessTravelImpact(conditions, advisories); // Generate weather summary const summary = this.generateWeatherSummary(conditions); return { location: { latitude: location.latitude, longitude: location.longitude, name: location.name, nearestStation: 'Singapore Weather Station', // Would be determined from actual data }, current: { conditions, summary, comfort, }, advisories, travelImpact, timestamp: new Date().toISOString(), }; } async getWeatherAdvisory(args) { const input = zod_1.z.object({ location: zod_1.z.object({ latitude: zod_1.z.number().min(1.0).max(1.5), longitude: zod_1.z.number().min(103.0).max(104.5), name: zod_1.z.string().optional(), }).default({ latitude: 1.3521, longitude: 103.8198, name: 'Singapore' }), activityType: zod_1.z.enum(['walking', 'cycling', 'outdoor_dining', 'sports', 'photography', 'general']).default('general'), }).parse(args); const conditions = await this.weatherService.getWeatherConditionsForLocation(input.location.latitude, input.location.longitude); const advisories = this.weatherService.generateWeatherAdvisory(conditions); const activityRecommendations = this.generateActivityRecommendations(conditions, input.activityType); return { location: input.location, conditions: { temperature: `${conditions.temperature}°C`, rainfall: `${conditions.rainfall}mm`, humidity: `${conditions.humidity}%`, windSpeed: `${conditions.windSpeed} km/h`, }, advisories, activityRecommendations, timestamp: new Date().toISOString(), }; } analyzeComfortLevel(conditions) { const { temperature, humidity, rainfall, windSpeed } = conditions; // Heavy rain makes it uncomfortable regardless of other factors if (rainfall > 10) { return { level: 'uncomfortable', description: `Heavy rain (${rainfall}mm) making outdoor activities difficult`, }; } // Very hot conditions if (temperature > 34) { return { level: 'uncomfortable', description: `Very hot (${temperature}°C) with potential heat stress risk`, }; } // Hot and humid combination if (temperature > 32 && humidity > 80) { return { level: 'uncomfortable', description: `Hot and humid (${temperature}°C, ${humidity}%) - feels much hotter`, }; } // Hot conditions if (temperature > 30) { return { level: 'hot', description: `Hot weather (${temperature}°C) - stay hydrated and seek shade`, }; } // High humidity if (humidity > 85) { return { level: 'humid', description: `Very humid (${humidity}%) - may feel sticky and uncomfortable`, }; } // Warm but manageable if (temperature > 28) { return { level: 'warm', description: `Warm and pleasant (${temperature}°C) - good for most activities`, }; } // Comfortable conditions return { level: 'comfortable', description: `Comfortable conditions (${temperature}°C) - ideal for outdoor activities`, }; } assessTravelImpact(conditions, advisories) { const { rainfall, temperature, humidity, windSpeed } = conditions; const recommendedActions = []; let walkingConditions = 'good'; let walkingTimeMultiplier = 1.0; // Heavy rain impact if (rainfall > 15) { walkingConditions = 'avoid'; walkingTimeMultiplier = 2.0; recommendedActions.push('Avoid outdoor walking - seek shelter'); recommendedActions.push('Use covered walkways and MRT stations'); recommendedActions.push('Consider postponing non-essential trips'); } else if (rainfall > 5) { walkingConditions = 'poor'; walkingTimeMultiplier = 1.5; recommendedActions.push('Use umbrella and waterproof clothing'); recommendedActions.push('Prefer covered routes and MRT connections'); recommendedActions.push('Allow extra time for travel'); } else if (rainfall > 0) { walkingConditions = 'fair'; walkingTimeMultiplier = 1.2; recommendedActions.push('Light rain - carry umbrella as precaution'); } // Temperature impact if (temperature > 34) { if (walkingConditions === 'good') walkingConditions = 'poor'; walkingTimeMultiplier = Math.max(walkingTimeMultiplier, 1.4); recommendedActions.push('Very hot - minimize outdoor exposure'); recommendedActions.push('Seek air-conditioned transport options'); recommendedActions.push('Stay hydrated and take frequent breaks'); } else if (temperature > 32) { if (walkingConditions === 'good') walkingConditions = 'fair'; walkingTimeMultiplier = Math.max(walkingTimeMultiplier, 1.2); recommendedActions.push('Hot weather - stay hydrated'); recommendedActions.push('Seek shade when possible'); } // Humidity impact if (humidity > 90) { walkingTimeMultiplier = Math.max(walkingTimeMultiplier, 1.1); recommendedActions.push('Very humid - expect to feel warmer than actual temperature'); } // Wind impact if (windSpeed > 25) { recommendedActions.push('Strong winds - be cautious near tall buildings'); recommendedActions.push('Secure loose items and clothing'); } // Excellent conditions if (rainfall === 0 && temperature >= 24 && temperature <= 30 && humidity < 75) { walkingConditions = 'excellent'; recommendedActions.push('Excellent weather for outdoor activities'); } const waitingTimeRecommendation = this.generateWaitingTimeRecommendation(conditions); return { walkingConditions, recommendedActions, timeAdjustments: { walkingTimeMultiplier, waitingTimeRecommendation, }, }; } generateWeatherSummary(conditions) { const { temperature, rainfall, humidity, windSpeed } = conditions; if (rainfall > 10) { return `Heavy rain with ${temperature}°C temperature - indoor activities recommended`; } if (rainfall > 2.5) { return `Light to moderate rain with ${temperature}°C temperature - carry umbrella`; } if (temperature > 32) { return `Hot and ${humidity > 80 ? 'humid' : 'dry'} weather at ${temperature}°C - stay cool and hydrated`; } if (temperature < 26) { return `Pleasant ${temperature}°C weather with ${humidity}% humidity - great for outdoor activities`; } return `Warm ${temperature}°C weather with ${humidity}% humidity - comfortable for most activities`; } generateWaitingTimeRecommendation(conditions) { const { rainfall, temperature } = conditions; if (rainfall > 10) { return 'Seek covered waiting areas - avoid outdoor bus stops'; } if (rainfall > 2.5) { return 'Use covered bus stops and MRT stations when possible'; } if (temperature > 32) { return 'Seek air-conditioned or shaded waiting areas'; } return 'Standard waiting times apply - conditions are manageable'; } generateActivityRecommendations(conditions, activityType) { const { temperature, rainfall, humidity, windSpeed } = conditions; const recommendations = []; const bestTimes = []; let suitability = 'good'; switch (activityType) { case 'walking': if (rainfall > 5) { suitability = 'poor'; recommendations.push('Heavy rain - postpone if possible'); } else if (temperature > 32) { suitability = 'fair'; recommendations.push('Hot weather - walk in shade, stay hydrated'); bestTimes.push('Early morning (6-8 AM)', 'Evening (6-8 PM)'); } else { suitability = 'excellent'; recommendations.push('Great conditions for walking'); } break; case 'cycling': if (rainfall > 2.5) { suitability = 'not_recommended'; recommendations.push('Rain makes cycling dangerous'); } else if (windSpeed > 20) { suitability = 'poor'; recommendations.push('Strong winds - be extra cautious'); } else if (temperature > 30) { suitability = 'fair'; recommendations.push('Hot - cycle early morning or evening'); bestTimes.push('Early morning (6-8 AM)', 'Evening (6-8 PM)'); } else { suitability = 'excellent'; recommendations.push('Perfect cycling weather'); } break; case 'outdoor_dining': if (rainfall > 0) { suitability = 'poor'; recommendations.push('Rain - choose covered outdoor areas'); } else if (temperature > 32) { suitability = 'fair'; recommendations.push('Hot - seek shaded areas with fans'); bestTimes.push('Evening (6-9 PM)'); } else { suitability = 'excellent'; recommendations.push('Perfect for outdoor dining'); } break; case 'sports': if (rainfall > 2.5) { suitability = 'not_recommended'; recommendations.push('Rain - move to indoor facilities'); } else if (temperature > 34) { suitability = 'not_recommended'; recommendations.push('Too hot for outdoor sports - risk of heat exhaustion'); } else if (temperature > 30) { suitability = 'poor'; recommendations.push('Very hot - take frequent breaks, stay hydrated'); bestTimes.push('Early morning (6-8 AM)', 'Evening (6-8 PM)'); } else { suitability = 'good'; recommendations.push('Good conditions for sports'); } break; case 'photography': if (rainfall > 5) { suitability = 'poor'; recommendations.push('Heavy rain - protect equipment'); } else if (rainfall > 0) { suitability = 'fair'; recommendations.push('Light rain can create interesting effects'); } else { suitability = 'excellent'; recommendations.push('Clear conditions great for photography'); bestTimes.push('Golden hour (6-7 AM, 6-7 PM)'); } break; default: if (rainfall > 5) { suitability = 'poor'; recommendations.push('Heavy rain - indoor activities recommended'); } else if (temperature > 32) { suitability = 'fair'; recommendations.push('Hot weather - seek air-conditioned spaces'); } else { suitability = 'good'; recommendations.push('Pleasant conditions for most activities'); } } return { suitability, recommendations, bestTimes, }; } } exports.WeatherConditionsTool = WeatherConditionsTool;