get-weather
Fetch detailed weather data for any city, including current conditions and hourly forecasts, using structured input to ensure accurate results.
Instructions
Get detailed weather information for any city including current conditions and hourly forecast
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| city | Yes | The name of the city to get weather information for (e.g., 'New York', 'London', 'Tokyo') |
Implementation Reference
- main.ts:297-353 (handler)The core handler function `getWeatherForCity` that performs geocoding lookup, fetches current and hourly weather data from Open-Meteo API, processes it into a structured ProcessedWeatherData object with formatted strings, and handles city-not-found errors.async function getWeatherForCity(city: string): Promise<ProcessedWeatherData | string> { // Step 1: Get coordinates for the city const geoUrl = `${CONFIG.GEOCODING_API}?name=${encodeURIComponent(city)}&count=1&language=en&format=json`; const geoResponse = await fetchWithRetry(geoUrl); const geoData: GeocodingResponse = await geoResponse.json(); // Handle city not found if (!geoData.results || geoData.results.length === 0) { return `❌ Sorry, I couldn't find a city named "${city}". Please check the spelling and try again.`; } const location = geoData.results[0]; // Step 2: Get weather data using coordinates const weatherUrl = `${CONFIG.WEATHER_API}?latitude=${location.latitude}&longitude=${location.longitude}¤t=temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,weather_code&hourly=temperature_2m,precipitation&forecast_days=1&timezone=auto`; const weatherResponse = await fetchWithRetry(weatherUrl); const weatherData: WeatherResponse = await weatherResponse.json(); // Process and structure the weather data const current = weatherData.current; const locationName = location.admin1 ? `${location.name}, ${location.admin1}, ${location.country}` : `${location.name}, ${location.country}`; const processedData: ProcessedWeatherData = { location: { name: location.name, fullName: locationName, latitude: location.latitude, longitude: location.longitude, country: location.country, admin1: location.admin1 }, current: { time: current.time, formattedTime: formatTime(current.time), temperature: current.temperature_2m, formattedTemperature: formatTemperature(current.temperature_2m), feelsLike: current.apparent_temperature, formattedFeelsLike: formatTemperature(current.apparent_temperature), humidity: current.relative_humidity_2m, precipitation: current.precipitation, formattedPrecipitation: formatPrecipitation(current.precipitation), weatherCode: current.weather_code, weatherDescription: getWeatherDescription(current.weather_code) }, hourly: weatherData.hourly.time.slice(0, 24).map((time, index) => ({ time: time, formattedTime: formatTime(time), temperature: weatherData.hourly.temperature_2m[index], formattedTemperature: formatTemperature(weatherData.hourly.temperature_2m[index]), precipitation: weatherData.hourly.precipitation[index], formattedPrecipitation: formatPrecipitation(weatherData.hourly.precipitation[index]) })), raw: weatherData }; return processedData; }
- main.ts:481-532 (registration)MCP server tool registration for 'get-weather', defining name, description, input schema with Zod validation for 'city' parameter, and inline async handler that calls getWeatherForCity, handles errors, and returns text or JSON content.server.tool( 'get-weather', 'Get detailed weather information for any city including current conditions and hourly forecast', { city: z.string() .min(1, "City name cannot be empty") .max(100, "City name is too long") .describe("The name of the city to get weather information for (e.g., 'New York', 'London', 'Tokyo')") }, async({ city }) => { try { const result = await getWeatherForCity(city); // If it's an error string, return it as text if (typeof result === 'string') { return { content: [ { type: "text", text: result } ] }; } // If it's processed data, return it as JSON string for structured access return { content: [ { type: "text", text: JSON.stringify(result, null, 2) } ] }; } catch (error) { console.error('Weather fetch error:', error); const errorMessage = error instanceof Error && error.message.includes('fetch') ? `❌ Unable to fetch weather data. Please check your internet connection and try again.` : `❌ Error: ${error instanceof Error ? error.message : 'Unknown error'}`; return { content: [ { type: "text", text: errorMessage } ] }; } } );
- main.ts:484-488 (schema)Zod input schema for the 'get-weather' tool, validating the 'city' parameter as a non-empty string up to 100 characters with descriptive help text.{ city: z.string() .min(1, "City name cannot be empty") .max(100, "City name is too long") .describe("The name of the city to get weather information for (e.g., 'New York', 'London', 'Tokyo')")
- main.ts:171-197 (helper)Utility helper `fetchWithRetry` used by the weather handler for robust API calls to geocoding and weather endpoints, with timeout, retry logic for network/server errors, and AbortController.async function fetchWithRetry(url: string, options: RequestInit = {}, retries: number = CONFIG.MAX_RETRIES): Promise<Response> { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), CONFIG.REQUEST_TIMEOUT); try { const response = await fetch(url, { ...options, signal: controller.signal }); clearTimeout(timeoutId); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return response; } catch (error) { clearTimeout(timeoutId); if (retries > 0 && (error instanceof Error && (error.name === 'AbortError' || error.message.includes('HTTP 5')))) { await delay(CONFIG.RETRY_DELAY); return fetchWithRetry(url, options, retries - 1); } throw error; } }
- main.ts:132-134 (helper)Helper function `getWeatherDescription` that maps numeric weather codes to human-readable descriptions using the WEATHER_CODES lookup table (defined lines 100-125).function getWeatherDescription(code: number): string { return WEATHER_CODES[code] || 'Unknown weather condition'; }