Files
McpServerCSharp/AspNetCoreMcpServer/Tools/WeatherTools.cs
2025-11-13 16:07:34 +08:00

366 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using ModelContextProtocol;
using ModelContextProtocol.Server;
using System.ComponentModel;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AspNetCoreMcpServer.Tools;
[McpServerToolType]
public sealed class WeatherTools
{
private readonly IHttpClientFactory _httpClientFactory;
private const string WeatherApiUrl = "http://10.1.57.124:8002/ledclock/weather.txt";
public WeatherTools(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
[McpServerTool, Description("获取今天的天气信息")]
[McpMeta("category", "weather")]
[McpMeta("dataSource", "local")]
public async Task<string> GetTodayWeather()
{
try
{
var weatherData = await FetchWeatherDataAsync();
if (weatherData?.Data == null || weatherData.Data.Count == 0)
{
return "无法获取天气数据";
}
// 获取今天的日期
var today = DateTime.Now.ToString("yyyy-MM-dd");
// 查找今天的天气数据
var todayWeather = weatherData.Data.FirstOrDefault(d => d.Date == today);
if (todayWeather == null)
{
// 如果没有今天的数据,返回第一天的数据
todayWeather = weatherData.Data.First();
return $"""
今天天气信息 (数据日期: {todayWeather.Date})
地区: {weatherData.Province}/{weatherData.City}
天气: {todayWeather.WeatherDescription}
温度: {todayWeather.MinTemperature}°C ~ {todayWeather.MaxTemperature}°C
风向风力: {todayWeather.WindDescription}
注意: 未找到今日准确数据,显示的是最近日期的天气信息。
""";
}
return $"""
今天天气信息
地区: {weatherData.Province}/{weatherData.City}
日期: {todayWeather.Date}
天气: {todayWeather.WeatherDescription}
温度: {todayWeather.MinTemperature}°C ~ {todayWeather.MaxTemperature}°C
风向风力: {todayWeather.WindDescription}
{GetWeatherAdvice(todayWeather)}
""";
}
catch (HttpRequestException ex)
{
return $"网络请求失败: {ex.Message}";
}
catch (JsonException ex)
{
return $"数据解析失败: {ex.Message}";
}
catch (Exception ex)
{
return $"获取天气信息时发生错误: {ex.Message}";
}
}
[McpServerTool, Description("获取未来几天的天气预报")]
[McpMeta("category", "weather")]
[McpMeta("dataSource", "local")]
public async Task<string> GetWeatherForecast(
[Description("预报天数默认为3天最多15天")] int days = 3)
{
try
{
if (days < 1 || days > 15)
{
return "预报天数必须在1-15天之间";
}
var weatherData = await FetchWeatherDataAsync();
if (weatherData?.Data == null || weatherData.Data.Count == 0)
{
return "无法获取天气数据";
}
var forecasts = weatherData.Data.Take(days).Select((weather, index) =>
{
var dateLabel = index == 0 ? "今天" : index == 1 ? "明天" : index == 2 ? "后天" : weather.Date;
return $"""
{dateLabel} ({weather.Date})
天气: {weather.WeatherDescription}
温度: {weather.MinTemperature}°C ~ {weather.MaxTemperature}°C
风向风力: {weather.WindDescription}
""";
});
return $"""
{weatherData.Province}/{weatherData.City} 未来{days}天天气预报
{string.Join("\n---\n", forecasts)}
""";
}
catch (Exception ex)
{
return $"获取天气预报时发生错误: {ex.Message}";
}
}
[McpServerTool, Description("获取指定日期的天气信息")]
[McpMeta("category", "weather")]
[McpMeta("dataSource", "local")]
public async Task<string> GetWeatherByDate(
[Description("日期,格式: yyyy-MM-dd例如: 2025-11-15")] string date)
{
try
{
// 验证日期格式
if (!DateTime.TryParseExact(date, "yyyy-MM-dd", null,
System.Globalization.DateTimeStyles.None, out var parsedDate))
{
return "日期格式错误,请使用 yyyy-MM-dd 格式,例如: 2025-11-15";
}
var weatherData = await FetchWeatherDataAsync();
if (weatherData?.Data == null || weatherData.Data.Count == 0)
{
return "无法获取天气数据";
}
var targetWeather = weatherData.Data.FirstOrDefault(d => d.Date == date);
if (targetWeather == null)
{
var availableDates = string.Join(", ", weatherData.Data.Select(d => d.Date).Take(5));
return $"未找到日期 {date} 的天气数据\n\n可用的日期: {availableDates}...";
}
var daysFromNow = (parsedDate - DateTime.Now).Days;
var dateLabel = daysFromNow switch
{
0 => "今天",
1 => "明天",
2 => "后天",
_ => daysFromNow > 0 ? $"{daysFromNow}天后" : $"{-daysFromNow}天前"
};
return $"""
{dateLabel}的天气信息
地区: {weatherData.Province}/{weatherData.City}
日期: {targetWeather.Date}
天气: {targetWeather.WeatherDescription}
温度: {targetWeather.MinTemperature}°C ~ {targetWeather.MaxTemperature}°C
风向风力: {targetWeather.WindDescription}
{GetWeatherAdvice(targetWeather)}
""";
}
catch (Exception ex)
{
return $"获取天气信息时发生错误: {ex.Message}";
}
}
[McpServerTool, Description("获取完整的天气数据概览")]
[McpMeta("category", "weather")]
[McpMeta("dataSource", "local")]
public async Task<string> GetWeatherOverview()
{
try
{
var weatherData = await FetchWeatherDataAsync();
if (weatherData?.Data == null || weatherData.Data.Count == 0)
{
return "无法获取天气数据";
}
// 统计信息
var totalDays = weatherData.Data.Count;
var avgMaxTemp = weatherData.Data.Average(d => int.Parse(d.MaxTemperature));
var avgMinTemp = weatherData.Data.Average(d => int.Parse(d.MinTemperature));
var rainyDays = weatherData.Data.Count(d => d.WeatherDescription.Contains("雨"));
var sunnyDays = weatherData.Data.Count(d => d.WeatherDescription.Contains("晴"));
// 今天的天气
var today = DateTime.Now.ToString("yyyy-MM-dd");
var todayWeather = weatherData.Data.FirstOrDefault(d => d.Date == today)
?? weatherData.Data.First();
return $"""
天气数据概览
地区: {weatherData.Province}/{weatherData.City}
数据范围: {weatherData.Data.First().Date} 至 {weatherData.Data.Last().Date}
今日天气:
{todayWeather.WeatherDescription} | {todayWeather.MinTemperature}°C ~ {todayWeather.MaxTemperature}°C | {todayWeather.WindDescription}
统计信息 (未来{totalDays}天):
- 平均最高温度: {avgMaxTemp:F1}°C
- 平均最低温度: {avgMinTemp:F1}°C
- 晴天: {sunnyDays}天
- 雨天: {rainyDays}天
温馨提示: {GetOverviewAdvice(weatherData.Data)}
""";
}
catch (Exception ex)
{
return $"获取天气概览时发生错误: {ex.Message}";
}
}
// ==================== 私有辅助方法 ====================
private async Task<WeatherResponse?> FetchWeatherDataAsync()
{
var client = _httpClientFactory.CreateClient();
client.Timeout = TimeSpan.FromSeconds(10);
using var response = await client.GetAsync(WeatherApiUrl);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
return JsonSerializer.Deserialize<WeatherResponse>(content, options);
}
private static string GetWeatherAdvice(WeatherData weather)
{
var advice = new List<string>();
// 根据天气状况给建议
if (weather.WeatherDescription.Contains("雨"))
{
advice.Add("☔ 记得带伞");
}
else if (weather.WeatherDescription.Contains("晴"))
{
advice.Add("☀️ 天气晴朗,适合外出");
}
else if (weather.WeatherDescription.Contains("多云"))
{
advice.Add("⛅ 天气多云,可以外出活动");
}
// 根据温度给建议
var maxTemp = int.Parse(weather.MaxTemperature);
var minTemp = int.Parse(weather.MinTemperature);
var tempDiff = maxTemp - minTemp;
if (maxTemp > 30)
{
advice.Add("🌡️ 高温天气,注意防暑降温");
}
else if (minTemp < 10)
{
advice.Add("🧥 气温较低,注意保暖");
}
if (tempDiff > 10)
{
advice.Add("👔 昼夜温差大,适当增减衣物");
}
return advice.Count > 0 ? $"温馨提示: {string.Join("", advice)}" : "";
}
private static string GetOverviewAdvice(List<WeatherData> weatherList)
{
var adviceList = new List<string>();
// 检查是否有连续雨天
var consecutiveRainyDays = 0;
var maxConsecutiveRainy = 0;
foreach (var weather in weatherList)
{
if (weather.WeatherDescription.Contains("雨"))
{
consecutiveRainyDays++;
maxConsecutiveRainy = Math.Max(maxConsecutiveRainy, consecutiveRainyDays);
}
else
{
consecutiveRainyDays = 0;
}
}
if (maxConsecutiveRainy >= 3)
{
adviceList.Add($"未来有连续{maxConsecutiveRainy}天降雨,注意出行安全");
}
// 检查温度变化
var firstTemp = int.Parse(weatherList.First().MaxTemperature);
var lastTemp = int.Parse(weatherList.Last().MaxTemperature);
var tempChange = lastTemp - firstTemp;
if (Math.Abs(tempChange) > 5)
{
adviceList.Add(tempChange > 0 ? "未来气温逐渐上升" : "未来气温逐渐下降");
}
return adviceList.Count > 0 ? string.Join("", adviceList) : "天气状况平稳";
}
}
// ==================== 数据模型 ====================
internal class WeatherResponse
{
[JsonPropertyName("code")]
public int Code { get; set; }
[JsonPropertyName("message")]
public string Message { get; set; } = "";
[JsonPropertyName("province")]
public string Province { get; set; } = "";
[JsonPropertyName("city")]
public string City { get; set; } = "";
[JsonPropertyName("data")]
public List<WeatherData> Data { get; set; } = new();
}
internal class WeatherData
{
[JsonPropertyName("date")]
public string Date { get; set; } = "";
[JsonPropertyName("weatherDescription")]
public string WeatherDescription { get; set; } = "";
[JsonPropertyName("maxTemperature")]
public string MaxTemperature { get; set; } = "";
[JsonPropertyName("minTemperature")]
public string MinTemperature { get; set; } = "";
[JsonPropertyName("wea_img")]
public string WeaImg { get; set; } = "";
[JsonPropertyName("windDescription")]
public string WindDescription { get; set; } = "";
}