getWeatherByLocation
Retrieve weather forecasts for flight planning by providing location coordinates. Returns current and next day weather data using Open-Meteo API to support travel decisions.
Instructions
天气信息查询 - 根据经纬度查询天气信息,使用Open-Meteo API。如果不提供日期,默认查询今天和明天的天气数据
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| end_date | No | ||
| latitude | Yes | ||
| longitude | Yes | ||
| start_date | No |
Implementation Reference
- Core handler function that implements the getWeatherByLocation tool. Validates inputs, queries Open-Meteo API for hourly temperature data, computes statistics, formats output, and handles errors.def getWeatherByLocation(latitude: float, longitude: float, start_date: str = None, end_date: str = None) -> Dict[str, Any]: """ 根据经纬度查询天气信息 Args: latitude: 纬度 longitude: 经度 start_date: 开始日期 (YYYY-MM-DD格式),可选,默认为前一天 end_date: 结束日期 (YYYY-MM-DD格式),可选,默认为后一天 Returns: 包含天气查询结果的字典 """ logger.info(f"开始查询天气信息: 纬度={latitude}, 经度={longitude}, 开始日期={start_date}, 结束日期={end_date}") try: # 验证输入参数 if latitude is None or longitude is None: logger.warning("经纬度参数不能为空") return { "status": "error", "message": "经纬度参数不能为空", "error_code": "INVALID_PARAMS" } # 验证经纬度范围 if not (-90 <= latitude <= 90): logger.warning(f"纬度超出有效范围: {latitude}") return { "status": "error", "message": f"纬度必须在-90到90之间,当前值: {latitude}", "error_code": "INVALID_LATITUDE" } if not (-180 <= longitude <= 180): logger.warning(f"经度超出有效范围: {longitude}") return { "status": "error", "message": f"经度必须在-180到180之间,当前值: {longitude}", "error_code": "INVALID_LONGITUDE" } # 设置默认日期(今天和明天,共两天) now = datetime.now() if start_date is None: # 默认从今天开始 start_date = now.strftime('%Y-%m-%d') if end_date is None: # 默认到明天结束 default_end = now + timedelta(days=1) end_date = default_end.strftime('%Y-%m-%d') # 验证日期格式 try: start_dt = datetime.strptime(start_date, '%Y-%m-%d') end_dt = datetime.strptime(end_date, '%Y-%m-%d') logger.debug(f"日期解析成功: {start_dt} 到 {end_dt}") except ValueError as ve: logger.warning(f"日期格式错误: start_date={start_date}, end_date={end_date}") return { "status": "error", "message": "日期格式不正确,请使用YYYY-MM-DD格式", "error_code": "INVALID_DATE_FORMAT" } # 验证日期范围 if start_dt > end_dt: logger.warning(f"开始日期晚于结束日期: {start_date} > {end_date}") return { "status": "error", "message": "开始日期不能晚于结束日期", "error_code": "INVALID_DATE_RANGE" } # 构建API请求URL base_url = "https://api.open-meteo.com/v1/forecast" params = { "latitude": latitude, "longitude": longitude, "hourly": "temperature_2m", "models": "cma_grapes_global", "timezone": "Asia/Shanghai", "start_date": start_date, "end_date": end_date } logger.info(f"请求Open-Meteo API: {base_url}") logger.debug(f"请求参数: {params}") try: # 发送HTTP请求 response = requests.get(base_url, params=params, timeout=30) response.raise_for_status() weather_data = response.json() logger.debug(f"API响应数据: {json.dumps(weather_data, indent=2, ensure_ascii=False)}") # 调试:检查温度数据质量 if "hourly" in weather_data and "temperature_2m" in weather_data["hourly"]: temps = weather_data["hourly"]["temperature_2m"] none_count = sum(1 for temp in temps if temp is None) valid_count = len(temps) - none_count logger.debug(f"温度数据质量检查: 总数据点={len(temps)}, 有效数据点={valid_count}, None值数量={none_count}") # 格式化结果 result = { "status": "success", "latitude": weather_data.get("latitude"), "longitude": weather_data.get("longitude"), "timezone": weather_data.get("timezone"), "timezone_abbreviation": weather_data.get("timezone_abbreviation"), "elevation": weather_data.get("elevation"), "start_date": start_date, "end_date": end_date, "hourly_units": weather_data.get("hourly_units", {}), "hourly_data": weather_data.get("hourly", {}), "formatted_output": _format_weather_result(weather_data, latitude, longitude, start_date, end_date), "query_time": datetime.now().isoformat() } # 添加温度统计信息 if "hourly" in weather_data and "temperature_2m" in weather_data["hourly"]: temperatures = weather_data["hourly"]["temperature_2m"] if temperatures: # 过滤掉None值 valid_temperatures = [temp for temp in temperatures if temp is not None] if valid_temperatures: result["temperature_statistics"] = { "min_temperature": min(valid_temperatures), "max_temperature": max(valid_temperatures), "avg_temperature": round(sum(valid_temperatures) / len(valid_temperatures), 1), "data_points": len(temperatures), "valid_data_points": len(valid_temperatures) } else: logger.warning("所有温度数据都为None值") result["temperature_statistics"] = { "error": "无有效温度数据", "data_points": len(temperatures), "valid_data_points": 0 } logger.info(f"天气查询成功: 纬度={latitude}, 经度={longitude}") return result except requests.exceptions.RequestException as re: logger.error(f"API请求失败: {str(re)}", exc_info=True) return { "status": "error", "message": f"天气API请求失败: {str(re)}", "error_code": "API_REQUEST_FAILED" } except json.JSONDecodeError as je: logger.error(f"API响应解析失败: {str(je)}", exc_info=True) return { "status": "error", "message": f"天气API响应格式错误: {str(je)}", "error_code": "API_RESPONSE_INVALID" } except Exception as e: logger.error(f"查询天气信息失败: {str(e)}", exc_info=True) return { "status": "error", "message": f"查询天气信息失败: {str(e)}", "error_code": "WEATHER_QUERY_FAILED" }
- flight_ticket_mcp_server/main.py:241-245 (registration)Tool registration in main.py using FastMCP @mcp.tool() decorator. This wrapper function defines the tool schema via type hints and docstring, and delegates to the core handler in weather_tools.@mcp.tool() def getWeatherByLocation(latitude: float, longitude: float, start_date: str = None, end_date: str = None): """天气信息查询 - 根据经纬度查询天气信息,使用Open-Meteo API。如果不提供日期,默认查询今天和明天的天气数据""" logger.debug(f"调用天气查询工具: latitude={latitude}, longitude={longitude}, start_date={start_date}, end_date={end_date}") return weather_tools.getWeatherByLocation(latitude, longitude, start_date, end_date)
- Supporting helper function used by the handler to format raw API weather data into a user-friendly string output with daily temperature ranges and statistics.def _format_weather_result(weather_data: Dict[str, Any], latitude: float, longitude: float, start_date: str, end_date: str) -> str: """ 格式化天气查询结果 Args: weather_data: API返回的天气数据 latitude: 纬度 longitude: 经度 start_date: 开始日期 end_date: 结束日期 Returns: 格式化后的字符串 """ try: output = [] output.append("🌤️ 天气查询结果") output.append(f"📍 位置: 纬度 {weather_data.get('latitude', latitude)}, 经度 {weather_data.get('longitude', longitude)}") output.append(f"📅 查询时间段: {start_date} 到 {end_date}") output.append(f"🌍 时区: {weather_data.get('timezone', 'N/A')} ({weather_data.get('timezone_abbreviation', 'N/A')})") if "elevation" in weather_data: output.append(f"⛰️ 海拔: {weather_data['elevation']}米") output.append("") # 处理小时温度数据 if "hourly" in weather_data and "temperature_2m" in weather_data["hourly"]: times = weather_data["hourly"].get("time", []) temperatures = weather_data["hourly"].get("temperature_2m", []) if times and temperatures: # 按日期分组显示 daily_data = {} for time_str, temp in zip(times, temperatures): try: dt = datetime.fromisoformat(time_str.replace('T', ' ')) date_key = dt.strftime('%Y-%m-%d') hour = dt.strftime('%H:%M') if date_key not in daily_data: daily_data[date_key] = [] daily_data[date_key].append((hour, temp)) except: continue # 显示每日数据 for date, hourly_temps in daily_data.items(): output.append(f"📆 {date}") # 计算当日统计 day_temps = [temp for _, temp in hourly_temps if temp is not None] if day_temps: min_temp = min(day_temps) max_temp = max(day_temps) avg_temp = sum(day_temps) / len(day_temps) output.append(f" 🌡️ 温度范围: {min_temp:.1f}°C ~ {max_temp:.1f}°C (平均: {avg_temp:.1f}°C)") else: output.append(f" ❌ 当日无有效温度数据") # 显示部分小时数据(每4小时一次) sample_data = hourly_temps[::4] # 每4小时取一个样本 for hour, temp in sample_data[:6]: # 最多显示6个时间点 if temp is not None: output.append(f" {hour}: {temp}°C") else: output.append(f" {hour}: 无数据") output.append("") # 整体统计 all_temps = [temp for _, temp in temperatures if temp is not None] if all_temps: output.append("📊 整体统计:") output.append(f" 最低温度: {min(all_temps):.1f}°C") output.append(f" 最高温度: {max(all_temps):.1f}°C") output.append(f" 平均温度: {sum(all_temps)/len(all_temps):.1f}°C") output.append(f" 数据点数: {len(times)}个") else: output.append("❌ 未获取到温度数据") return "\n".join(output) except Exception as e: logger.error(f"格式化天气结果失败: {str(e)}", exc_info=True) return f"天气数据格式化失败: {str(e)}"
- Data helper providing predefined latitude/longitude coordinates for major cities, used by getWeatherByCity (related tool).CITY_COORDINATES = { "北京": {"latitude": 39.9042, "longitude": 116.4074, "name": "北京"}, "上海": {"latitude": 31.2304, "longitude": 121.4737, "name": "上海"}, "广州": {"latitude": 23.1291, "longitude": 113.2644, "name": "广州"}, "深圳": {"latitude": 22.5431, "longitude": 114.0579, "name": "深圳"}, "成都": {"latitude": 30.5728, "longitude": 104.0668, "name": "成都"}, "武汉": {"latitude": 30.5928, "longitude": 114.3055, "name": "武汉"}, "西安": {"latitude": 34.3416, "longitude": 108.9398, "name": "西安"}, "杭州": {"latitude": 30.2741, "longitude": 120.1551, "name": "杭州"}, "重庆": {"latitude": 29.5647, "longitude": 106.5507, "name": "重庆"}, "天津": {"latitude": 39.3434, "longitude": 117.3616, "name": "天津"}, "南京": {"latitude": 32.0603, "longitude": 118.7969, "name": "南京"}, "青岛": {"latitude": 36.0986, "longitude": 120.3719, "name": "青岛"}, "大连": {"latitude": 38.9140, "longitude": 121.6147, "name": "大连"}, "宁波": {"latitude": 29.8683, "longitude": 121.5440, "name": "宁波"}, "厦门": {"latitude": 24.4798, "longitude": 118.0819, "name": "厦门"}, "福州": {"latitude": 26.0745, "longitude": 119.2965, "name": "福州"}, "无锡": {"latitude": 31.4912, "longitude": 120.3124, "name": "无锡"}, "合肥": {"latitude": 31.8206, "longitude": 117.2272, "name": "合肥"}, "昆明": {"latitude": 25.0389, "longitude": 102.7183, "name": "昆明"}, "哈尔滨": {"latitude": 45.8038, "longitude": 126.5349, "name": "哈尔滨"}, "沈阳": {"latitude": 41.8057, "longitude": 123.4315, "name": "沈阳"}, "长春": {"latitude": 43.8171, "longitude": 125.3235, "name": "长春"}, "石家庄": {"latitude": 38.0428, "longitude": 114.5149, "name": "石家庄"}, "长沙": {"latitude": 28.2282, "longitude": 112.9388, "name": "长沙"}, "郑州": {"latitude": 34.7466, "longitude": 113.6254, "name": "郑州"}, "南昌": {"latitude": 28.6820, "longitude": 115.8581, "name": "南昌"}, "贵阳": {"latitude": 26.6470, "longitude": 106.6302, "name": "贵阳"}, "兰州": {"latitude": 36.0611, "longitude": 103.8343, "name": "兰州"}, "海口": {"latitude": 20.0458, "longitude": 110.3417, "name": "海口"}, "三亚": {"latitude": 18.2528, "longitude": 109.5122, "name": "三亚"}, "银川": {"latitude": 38.4872, "longitude": 106.2309, "name": "银川"}, "西宁": {"latitude": 36.6171, "longitude": 101.7782, "name": "西宁"}, "呼和浩特": {"latitude": 40.8414, "longitude": 111.7519, "name": "呼和浩特"}, "乌鲁木齐": {"latitude": 43.8256, "longitude": 87.6168, "name": "乌鲁木齐"}, "拉萨": {"latitude": 29.6625, "longitude": 91.1112, "name": "拉萨"}, "南宁": {"latitude": 22.8170, "longitude": 108.3669, "name": "南宁"}, # 港澳台 "香港": {"latitude": 22.3193, "longitude": 114.1694, "name": "香港"}, "澳门": {"latitude": 22.1987, "longitude": 113.5439, "name": "澳门"}, "台北": {"latitude": 25.0330, "longitude": 121.5654, "name": "台北"}, }
- flight_ticket_mcp_server/main.py:285-285 (registration)Log statement confirming successful registration of tools including getWeatherByLocation.logger.info("MCP工具注册完成 - 已注册工具: searchFlightRoutes, getCurrentDate, getTransferFlightsByThreePlace, getWeatherByLocation, getWeatherByCity, getFlightInfo, getFlightStatus, getAirportFlights, getFlightsInArea, trackMultipleFlights")