diff --git a/.gitignore b/.gitignore
index add57be..8454050 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@ bin/
obj/
/packages/
riderModule.iml
-/_ReSharper.Caches/
\ No newline at end of file
+/_ReSharper.Caches/
+/*.user
diff --git a/.idea/.idea.McpServerCSharp/.idea/.gitignore b/.idea/.idea.McpServerCSharp/.idea/.gitignore
new file mode 100644
index 0000000..ce6a1da
--- /dev/null
+++ b/.idea/.idea.McpServerCSharp/.idea/.gitignore
@@ -0,0 +1,13 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# Rider 忽略的文件
+/projectSettingsUpdater.xml
+/modules.xml
+/.idea.McpServerCSharp.iml
+/contentModel.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/.idea.McpServerCSharp/.idea/encodings.xml b/.idea/.idea.McpServerCSharp/.idea/encodings.xml
new file mode 100644
index 0000000..df87cf9
--- /dev/null
+++ b/.idea/.idea.McpServerCSharp/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.McpServerCSharp/.idea/indexLayout.xml b/.idea/.idea.McpServerCSharp/.idea/indexLayout.xml
new file mode 100644
index 0000000..7b08163
--- /dev/null
+++ b/.idea/.idea.McpServerCSharp/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.McpServerCSharp/.idea/vcs.xml b/.idea/.idea.McpServerCSharp/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/.idea.McpServerCSharp/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AspNetCoreMcpServer/AspNetCoreMcpServer.csproj b/AspNetCoreMcpServer/AspNetCoreMcpServer.csproj
new file mode 100644
index 0000000..1914d45
--- /dev/null
+++ b/AspNetCoreMcpServer/AspNetCoreMcpServer.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net9.0
+ enable
+ enable
+
+
+
+
+
+
+
+
diff --git a/AspNetCoreMcpServer/AspNetCoreMcpServer.http b/AspNetCoreMcpServer/AspNetCoreMcpServer.http
new file mode 100644
index 0000000..662e71c
--- /dev/null
+++ b/AspNetCoreMcpServer/AspNetCoreMcpServer.http
@@ -0,0 +1,6 @@
+@AspNetCoreMcpServer_HostAddress = http://localhost:5283
+
+GET {{AspNetCoreMcpServer_HostAddress}}/weatherforecast/
+Accept: application/json
+
+###
diff --git a/AspNetCoreMcpServer/Program.cs b/AspNetCoreMcpServer/Program.cs
new file mode 100644
index 0000000..50cd99b
--- /dev/null
+++ b/AspNetCoreMcpServer/Program.cs
@@ -0,0 +1,20 @@
+using AspNetCoreMcpServer.Resources;
+using AspNetCoreMcpServer.Tools;
+
+var builder = WebApplication.CreateBuilder(args);
+
+// 添加 HttpClientFactory 支持
+builder.Services.AddHttpClient();
+
+builder.Services.AddMcpServer()
+ .WithHttpTransport()
+ .WithTools()
+ .WithTools()
+ .WithTools()
+ .WithTools()
+ .WithResources();
+
+
+var app = builder.Build();
+app.MapMcp();
+app.Run();
\ No newline at end of file
diff --git a/AspNetCoreMcpServer/Properties/launchSettings.json b/AspNetCoreMcpServer/Properties/launchSettings.json
new file mode 100644
index 0000000..97436a0
--- /dev/null
+++ b/AspNetCoreMcpServer/Properties/launchSettings.json
@@ -0,0 +1,23 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": false,
+ "applicationUrl": "http://0.0.0.0:5284",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": false,
+ "applicationUrl": "https://0.0.0.0:7154;http://0.0.0.0:5284",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/AspNetCoreMcpServer/Resources/SimpleResourceType.cs b/AspNetCoreMcpServer/Resources/SimpleResourceType.cs
new file mode 100644
index 0000000..60dfe62
--- /dev/null
+++ b/AspNetCoreMcpServer/Resources/SimpleResourceType.cs
@@ -0,0 +1,11 @@
+using ModelContextProtocol.Server;
+using System.ComponentModel;
+
+namespace AspNetCoreMcpServer.Resources;
+
+[McpServerResourceType]
+public class SimpleResourceType
+{
+ [McpServerResource, Description("A direct text resource")]
+ public static string DirectTextResource() => "This is a direct resource";
+}
\ No newline at end of file
diff --git a/AspNetCoreMcpServer/Tools/EchoTool.cs b/AspNetCoreMcpServer/Tools/EchoTool.cs
new file mode 100644
index 0000000..6e6c179
--- /dev/null
+++ b/AspNetCoreMcpServer/Tools/EchoTool.cs
@@ -0,0 +1,14 @@
+using System.ComponentModel;
+using ModelContextProtocol.Server;
+
+namespace AspNetCoreMcpServer.Tools;
+
+[McpServerToolType]
+public sealed class EchoTool
+{
+ [McpServerTool, Description("Echoes the message back to the client.")]
+ public static string Echo(string message) => $"Hello from C#: {message}";
+
+ [McpServerTool, Description("Echoes in reverse the message sent.")]
+ public static string ReverseEcho(string message) => string.Concat(message.Reverse());
+}
\ No newline at end of file
diff --git a/AspNetCoreMcpServer/Tools/IotDeviceTools.cs b/AspNetCoreMcpServer/Tools/IotDeviceTools.cs
new file mode 100644
index 0000000..180944e
--- /dev/null
+++ b/AspNetCoreMcpServer/Tools/IotDeviceTools.cs
@@ -0,0 +1,526 @@
+using ModelContextProtocol;
+using ModelContextProtocol.Server;
+using System.ComponentModel;
+using System.Globalization;
+using System.Text.Json;
+
+namespace AspNetCoreMcpServer.Tools;
+
+[McpServerToolType]
+public sealed class IotDeviceTools
+{
+// 模拟设备状态存储
+ private static readonly Dictionary _deviceStates = new();
+
+ public IotDeviceTools()
+ {
+ // 初始化一些默认设备
+ InitializeDefaultDevices();
+ }
+
+ private void InitializeDefaultDevices()
+ {
+ _deviceStates["living_room_light"] = new DeviceState
+ {
+ Id = "living_room_light",
+ Name = "客厅灯",
+ Type = "light",
+ IsOn = false,
+ Brightness = 100,
+ Temperature = 4000
+ };
+
+ _deviceStates["bedroom_curtain"] = new DeviceState
+ {
+ Id = "bedroom_curtain",
+ Name = "卧室窗帘",
+ Type = "curtain",
+ IsOn = false,
+ Position = 0
+ };
+
+ _deviceStates["living_room_ac"] = new DeviceState
+ {
+ Id = "living_room_ac",
+ Name = "客厅空调",
+ Type = "air_conditioner",
+ IsOn = false,
+ Temperature = 26,
+ Mode = "cool",
+ FanSpeed = "medium"
+ };
+ }
+
+ // ==================== 灯光控制 ====================
+
+ [McpServerTool, Description("控制灯光的开关状态")]
+ [McpMeta("category", "iot")]
+ [McpMeta("device", "light")]
+ public async Task ControlLight(
+ [Description("设备ID,例如: living_room_light, bedroom_light")] string deviceId,
+ [Description("开关状态: on 或 off")] string action)
+ {
+ await Task.Delay(100); // 模拟网络延迟
+
+ if (!_deviceStates.ContainsKey(deviceId))
+ {
+ return $"错误: 找不到设备 {deviceId}";
+ }
+
+ var device = _deviceStates[deviceId];
+ if (device.Type != "light")
+ {
+ return $"错误: {deviceId} 不是灯光设备";
+ }
+
+ bool turnOn = action.ToLower() == "on";
+ device.IsOn = turnOn;
+
+ return $"""
+ 设备控制成功
+ 设备: {device.Name} ({deviceId})
+ 状态: {(turnOn ? "已开启" : "已关闭")}
+ 亮度: {device.Brightness}%
+ 色温: {device.Temperature}K
+ """;
+ }
+
+ [McpServerTool, Description("调节灯光亮度")]
+ [McpMeta("category", "iot")]
+ [McpMeta("device", "light")]
+ public async Task SetLightBrightness(
+ [Description("设备ID")] string deviceId,
+ [Description("亮度值 (0-100)")] int brightness)
+ {
+ await Task.Delay(100);
+
+ if (!_deviceStates.ContainsKey(deviceId))
+ {
+ return $"错误: 找不到设备 {deviceId}";
+ }
+
+ var device = _deviceStates[deviceId];
+ if (device.Type != "light")
+ {
+ return $"错误: {deviceId} 不是灯光设备";
+ }
+
+ if (brightness < 0 || brightness > 100)
+ {
+ return "错误: 亮度值必须在 0-100 之间";
+ }
+
+ device.Brightness = brightness;
+ if (brightness > 0 && !device.IsOn)
+ {
+ device.IsOn = true;
+ }
+
+ return $"""
+ 亮度调节成功
+ 设备: {device.Name}
+ 当前亮度: {brightness}%
+ 状态: {(device.IsOn ? "开启" : "关闭")}
+ """;
+ }
+
+ [McpServerTool, Description("调节灯光色温")]
+ [McpMeta("category", "iot")]
+ [McpMeta("device", "light")]
+ public async Task SetLightTemperature(
+ [Description("设备ID")] string deviceId,
+ [Description("色温值 (2700-6500K)")] int temperature)
+ {
+ await Task.Delay(100);
+
+ if (!_deviceStates.ContainsKey(deviceId))
+ {
+ return $"错误: 找不到设备 {deviceId}";
+ }
+
+ var device = _deviceStates[deviceId];
+ if (device.Type != "light")
+ {
+ return $"错误: {deviceId} 不是灯光设备";
+ }
+
+ if (temperature < 2700 || temperature > 6500)
+ {
+ return "错误: 色温值必须在 2700-6500K 之间";
+ }
+
+ device.Temperature = temperature;
+
+ string tempDesc = temperature < 3500 ? "暖光" : temperature < 5000 ? "自然光" : "冷光";
+
+ return $"""
+ 色温调节成功
+ 设备: {device.Name}
+ 当前色温: {temperature}K ({tempDesc})
+ 亮度: {device.Brightness}%
+ """;
+ }
+
+ // ==================== 窗帘控制 ====================
+
+ [McpServerTool, Description("控制窗帘的开关")]
+ [McpMeta("category", "iot")]
+ [McpMeta("device", "curtain")]
+ public async Task ControlCurtain(
+ [Description("设备ID,例如: bedroom_curtain")] string deviceId,
+ [Description("操作: open(打开), close(关闭), stop(停止)")] string action)
+ {
+ await Task.Delay(200); // 模拟窗帘运行时间
+
+ if (!_deviceStates.ContainsKey(deviceId))
+ {
+ return $"错误: 找不到设备 {deviceId}";
+ }
+
+ var device = _deviceStates[deviceId];
+ if (device.Type != "curtain")
+ {
+ return $"错误: {deviceId} 不是窗帘设备";
+ }
+
+ string result = action.ToLower() switch
+ {
+ "open" => HandleCurtainOpen(device),
+ "close" => HandleCurtainClose(device),
+ "stop" => HandleCurtainStop(device),
+ _ => "错误: 无效的操作,请使用 open, close 或 stop"
+ };
+
+ return result;
+ }
+
+ [McpServerTool, Description("设置窗帘打开的位置")]
+ [McpMeta("category", "iot")]
+ [McpMeta("device", "curtain")]
+ public async Task SetCurtainPosition(
+ [Description("设备ID")] string deviceId,
+ [Description("位置百分比 (0-100),0表示完全关闭,100表示完全打开")] int position)
+ {
+ await Task.Delay(150);
+
+ if (!_deviceStates.ContainsKey(deviceId))
+ {
+ return $"错误: 找不到设备 {deviceId}";
+ }
+
+ var device = _deviceStates[deviceId];
+ if (device.Type != "curtain")
+ {
+ return $"错误: {deviceId} 不是窗帘设备";
+ }
+
+ if (position < 0 || position > 100)
+ {
+ return "错误: 位置值必须在 0-100 之间";
+ }
+
+ device.Position = position;
+ device.IsOn = position > 0;
+
+ return $"""
+ 窗帘位置调节成功
+ 设备: {device.Name}
+ 当前位置: {position}%
+ 状态: {(position == 0 ? "完全关闭" : position == 100 ? "完全打开" : "部分打开")}
+ """;
+ }
+
+ // ==================== 空调控制 ====================
+
+ [McpServerTool, Description("控制空调的开关")]
+ [McpMeta("category", "iot")]
+ [McpMeta("device", "air_conditioner")]
+ public async Task ControlAirConditioner(
+ [Description("设备ID,例如: living_room_ac")] string deviceId,
+ [Description("开关状态: on 或 off")] string action)
+ {
+ await Task.Delay(100);
+
+ if (!_deviceStates.ContainsKey(deviceId))
+ {
+ return $"错误: 找不到设备 {deviceId}";
+ }
+
+ var device = _deviceStates[deviceId];
+ if (device.Type != "air_conditioner")
+ {
+ return $"错误: {deviceId} 不是空调设备";
+ }
+
+ bool turnOn = action.ToLower() == "on";
+ device.IsOn = turnOn;
+
+ return $"""
+ 空调控制成功
+ 设备: {device.Name}
+ 状态: {(turnOn ? "已开启" : "已关闭")}
+ {(turnOn ? $"温度: {device.Temperature}°C\n模式: {GetModeDescription(device.Mode)}\n风速: {GetFanSpeedDescription(device.FanSpeed)}" : "")}
+ """;
+ }
+
+ [McpServerTool, Description("设置空调温度")]
+ [McpMeta("category", "iot")]
+ [McpMeta("device", "air_conditioner")]
+ public async Task SetAirConditionerTemperature(
+ [Description("设备ID")] string deviceId,
+ [Description("目标温度 (16-30°C)")] int temperature)
+ {
+ await Task.Delay(100);
+
+ if (!_deviceStates.ContainsKey(deviceId))
+ {
+ return $"错误: 找不到设备 {deviceId}";
+ }
+
+ var device = _deviceStates[deviceId];
+ if (device.Type != "air_conditioner")
+ {
+ return $"错误: {deviceId} 不是空调设备";
+ }
+
+ if (temperature < 16 || temperature > 30)
+ {
+ return "错误: 温度必须在 16-30°C 之间";
+ }
+
+ device.Temperature = temperature;
+ if (!device.IsOn)
+ {
+ device.IsOn = true;
+ }
+
+ return $"""
+ 空调温度设置成功
+ 设备: {device.Name}
+ 目标温度: {temperature}°C
+ 当前模式: {GetModeDescription(device.Mode)}
+ 风速: {GetFanSpeedDescription(device.FanSpeed)}
+ """;
+ }
+
+ [McpServerTool, Description("设置空调模式")]
+ [McpMeta("category", "iot")]
+ [McpMeta("device", "air_conditioner")]
+ public async Task SetAirConditionerMode(
+ [Description("设备ID")] string deviceId,
+ [Description("模式: cool(制冷), heat(制热), fan(送风), dry(除湿), auto(自动)")] string mode)
+ {
+ await Task.Delay(100);
+
+ if (!_deviceStates.ContainsKey(deviceId))
+ {
+ return $"错误: 找不到设备 {deviceId}";
+ }
+
+ var device = _deviceStates[deviceId];
+ if (device.Type != "air_conditioner")
+ {
+ return $"错误: {deviceId} 不是空调设备";
+ }
+
+ var validModes = new[] { "cool", "heat", "fan", "dry", "auto" };
+ if (!validModes.Contains(mode.ToLower()))
+ {
+ return $"错误: 无效的模式。有效模式: {string.Join(", ", validModes)}";
+ }
+
+ device.Mode = mode.ToLower();
+
+ return $"""
+ 空调模式设置成功
+ 设备: {device.Name}
+ 当前模式: {GetModeDescription(device.Mode)}
+ 温度: {device.Temperature}°C
+ 风速: {GetFanSpeedDescription(device.FanSpeed)}
+ """;
+ }
+
+ [McpServerTool, Description("设置空调风速")]
+ [McpMeta("category", "iot")]
+ [McpMeta("device", "air_conditioner")]
+ public async Task SetAirConditionerFanSpeed(
+ [Description("设备ID")] string deviceId,
+ [Description("风速: low(低), medium(中), high(高), auto(自动)")] string fanSpeed)
+ {
+ await Task.Delay(100);
+
+ if (!_deviceStates.ContainsKey(deviceId))
+ {
+ return $"错误: 找不到设备 {deviceId}";
+ }
+
+ var device = _deviceStates[deviceId];
+ if (device.Type != "air_conditioner")
+ {
+ return $"错误: {deviceId} 不是空调设备";
+ }
+
+ var validSpeeds = new[] { "low", "medium", "high", "auto" };
+ if (!validSpeeds.Contains(fanSpeed.ToLower()))
+ {
+ return $"错误: 无效的风速。有效风速: {string.Join(", ", validSpeeds)}";
+ }
+
+ device.FanSpeed = fanSpeed.ToLower();
+
+ return $"""
+ 空调风速设置成功
+ 设备: {device.Name}
+ 当前风速: {GetFanSpeedDescription(device.FanSpeed)}
+ 模式: {GetModeDescription(device.Mode)}
+ 温度: {device.Temperature}°C
+ """;
+ }
+
+ // ==================== 通用查询 ====================
+
+ [McpServerTool, Description("获取所有IoT设备的状态")]
+ [McpMeta("category", "iot")]
+ [McpMeta("device", "all")]
+ public async Task GetAllDevicesStatus()
+ {
+ await Task.Delay(50);
+
+ var statusList = _deviceStates.Values.Select(device =>
+ {
+ string status = device.Type switch
+ {
+ "light" => $"开关: {(device.IsOn ? "开" : "关")}, 亮度: {device.Brightness}%, 色温: {device.Temperature}K",
+ "curtain" => $"位置: {device.Position}%, 状态: {(device.Position == 0 ? "关闭" : device.Position == 100 ? "打开" : "部分打开")}",
+ "air_conditioner" => $"开关: {(device.IsOn ? "开" : "关")}, 温度: {device.Temperature}°C, 模式: {GetModeDescription(device.Mode)}, 风速: {GetFanSpeedDescription(device.FanSpeed)}",
+ _ => "未知设备类型"
+ };
+
+ return $"""
+ [{device.Type.ToUpper()}] {device.Name} ({device.Id})
+ {status}
+ """;
+ });
+
+ return $"当前设备总数: {_deviceStates.Count}\n\n" + string.Join("\n---\n", statusList);
+ }
+
+ [McpServerTool, Description("获取指定设备的详细状态")]
+ [McpMeta("category", "iot")]
+ [McpMeta("device", "all")]
+ public async Task GetDeviceStatus(
+ [Description("设备ID")] string deviceId)
+ {
+ await Task.Delay(50);
+
+ if (!_deviceStates.ContainsKey(deviceId))
+ {
+ return $"错误: 找不到设备 {deviceId}\n\n可用设备: {string.Join(", ", _deviceStates.Keys)}";
+ }
+
+ var device = _deviceStates[deviceId];
+
+ string details = device.Type switch
+ {
+ "light" => $"""
+ 开关状态: {(device.IsOn ? "开启" : "关闭")}
+ 亮度: {device.Brightness}%
+ 色温: {device.Temperature}K ({(device.Temperature < 3500 ? "暖光" : device.Temperature < 5000 ? "自然光" : "冷光")})
+ """,
+ "curtain" => $"""
+ 位置: {device.Position}%
+ 状态: {(device.Position == 0 ? "完全关闭" : device.Position == 100 ? "完全打开" : "部分打开")}
+ """,
+ "air_conditioner" => $"""
+ 开关状态: {(device.IsOn ? "开启" : "关闭")}
+ 温度设置: {device.Temperature}°C
+ 运行模式: {GetModeDescription(device.Mode)}
+ 风速: {GetFanSpeedDescription(device.FanSpeed)}
+ """,
+ _ => "未知设备类型"
+ };
+
+ return $"""
+ 设备信息
+ 名称: {device.Name}
+ ID: {device.Id}
+ 类型: {device.Type}
+
+ {details}
+ """;
+ }
+
+ // ==================== 辅助方法 ====================
+
+ private string HandleCurtainOpen(DeviceState device)
+ {
+ device.Position = 100;
+ device.IsOn = true;
+ return $"""
+ 窗帘正在打开...
+ 设备: {device.Name}
+ 目标位置: 100% (完全打开)
+ 预计时间: 5秒
+ """;
+ }
+
+ private string HandleCurtainClose(DeviceState device)
+ {
+ device.Position = 0;
+ device.IsOn = false;
+ return $"""
+ 窗帘正在关闭...
+ 设备: {device.Name}
+ 目标位置: 0% (完全关闭)
+ 预计时间: 5秒
+ """;
+ }
+
+ private string HandleCurtainStop(DeviceState device)
+ {
+ return $"""
+ 窗帘已停止
+ 设备: {device.Name}
+ 当前位置: {device.Position}%
+ """;
+ }
+
+ private static string GetModeDescription(string? mode) => mode switch
+ {
+ "cool" => "制冷",
+ "heat" => "制热",
+ "fan" => "送风",
+ "dry" => "除湿",
+ "auto" => "自动",
+ _ => "未知"
+ };
+
+ private static string GetFanSpeedDescription(string? speed) => speed switch
+ {
+ "low" => "低速",
+ "medium" => "中速",
+ "high" => "高速",
+ "auto" => "自动",
+ _ => "未知"
+ };
+}
+
+// 设备状态数据模型
+internal class DeviceState
+{
+ public string Id { get; set; } = "";
+ public string Name { get; set; } = "";
+ public string Type { get; set; } = "";
+ public bool IsOn { get; set; }
+
+ // 灯光属性
+ public int Brightness { get; set; }
+ public int Temperature { get; set; }
+
+ // 窗帘属性
+ public int Position { get; set; }
+
+ // 空调属性
+ public string? Mode { get; set; }
+ public string? FanSpeed { get; set; }
+}
\ No newline at end of file
diff --git a/AspNetCoreMcpServer/Tools/SampleLlmTool.cs b/AspNetCoreMcpServer/Tools/SampleLlmTool.cs
new file mode 100644
index 0000000..7d06ec8
--- /dev/null
+++ b/AspNetCoreMcpServer/Tools/SampleLlmTool.cs
@@ -0,0 +1,31 @@
+using Microsoft.Extensions.AI;
+using ModelContextProtocol.Server;
+using System.ComponentModel;
+
+namespace AspNetCoreMcpServer.Tools;
+
+///
+/// This tool uses dependency injection and async method
+///
+[McpServerToolType]
+public sealed class SampleLlmTool
+{
+ [McpServerTool(Name = "sampleLLM"), Description("Samples from an LLM using MCP's sampling feature")]
+ public static async Task SampleLLM(
+ McpServer thisServer,
+ [Description("The prompt to send to the LLM")] string prompt,
+ [Description("Maximum number of tokens to generate")] int maxTokens,
+ CancellationToken cancellationToken)
+ {
+ ChatOptions options = new()
+ {
+ Instructions = "You are a helpful test server.",
+ MaxOutputTokens = maxTokens,
+ Temperature = 0.7f,
+ };
+
+ var samplingResponse = await thisServer.AsSamplingChatClient().GetResponseAsync(prompt, options, cancellationToken);
+
+ return $"LLM sampling result: {samplingResponse}";
+ }
+}
\ No newline at end of file
diff --git a/AspNetCoreMcpServer/Tools/WeatherTools.cs b/AspNetCoreMcpServer/Tools/WeatherTools.cs
new file mode 100644
index 0000000..e2ef8dc
--- /dev/null
+++ b/AspNetCoreMcpServer/Tools/WeatherTools.cs
@@ -0,0 +1,366 @@
+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 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 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 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 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 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(content, options);
+ }
+
+ private static string GetWeatherAdvice(WeatherData weather)
+ {
+ var advice = new List();
+
+ // 根据天气状况给建议
+ 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 weatherList)
+ {
+ var adviceList = new List();
+
+ // 检查是否有连续雨天
+ 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 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; } = "";
+}
\ No newline at end of file
diff --git a/AspNetCoreMcpServer/appsettings.Development.json b/AspNetCoreMcpServer/appsettings.Development.json
new file mode 100644
index 0000000..0c208ae
--- /dev/null
+++ b/AspNetCoreMcpServer/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/AspNetCoreMcpServer/appsettings.json b/AspNetCoreMcpServer/appsettings.json
new file mode 100644
index 0000000..10f68b8
--- /dev/null
+++ b/AspNetCoreMcpServer/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/ConsoleEchoMcpServer/ConsoleEchoMcpServer.csproj b/ConsoleEchoMcpServer/ConsoleEchoMcpServer.csproj
index 0fd0cbb..39db7b6 100644
--- a/ConsoleEchoMcpServer/ConsoleEchoMcpServer.csproj
+++ b/ConsoleEchoMcpServer/ConsoleEchoMcpServer.csproj
@@ -2,12 +2,13 @@
Exe
- net8.0
+ net9.0
enable
enable
+
diff --git a/ConsoleEchoMcpServer/Program.cs b/ConsoleEchoMcpServer/Program.cs
index e5dff12..65c2849 100644
--- a/ConsoleEchoMcpServer/Program.cs
+++ b/ConsoleEchoMcpServer/Program.cs
@@ -1,3 +1,14 @@
-// See https://aka.ms/new-console-template for more information
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using System.ComponentModel;
+using ConsoleEchoMcpServer.Tools;
+
+var builder = Host.CreateEmptyApplicationBuilder(settings: null);
+builder.Services
+ .AddMcpServer()
+ .WithStdioServerTransport()
+ .WithTools();
+
+await builder.Build().RunAsync();
+
-Console.WriteLine("Hello, World!");
\ No newline at end of file
diff --git a/ConsoleEchoMcpServer/Tools/EchoTool.cs b/ConsoleEchoMcpServer/Tools/EchoTool.cs
new file mode 100644
index 0000000..24673b1
--- /dev/null
+++ b/ConsoleEchoMcpServer/Tools/EchoTool.cs
@@ -0,0 +1,14 @@
+using System.ComponentModel;
+using ModelContextProtocol.Server;
+
+namespace ConsoleEchoMcpServer.Tools;
+
+[McpServerToolType]
+public sealed class EchoTool
+{
+ [McpServerTool, Description("Echoes the message back to the client.")]
+ public static string Echo(string message) => $"Hello from C#: {message}";
+
+ [McpServerTool, Description("Echoes in reverse the message sent.")]
+ public static string ReverseEcho(string message) => string.Concat(message.Reverse());
+}
\ No newline at end of file
diff --git a/McpServerCSharp.sln b/McpServerCSharp.sln
index 1847fd2..3b7c021 100644
--- a/McpServerCSharp.sln
+++ b/McpServerCSharp.sln
@@ -2,6 +2,8 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleEchoMcpServer", "ConsoleEchoMcpServer\ConsoleEchoMcpServer.csproj", "{D9A9A1BA-D847-4083-A085-DCBD43B5E1DC}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCoreMcpServer", "AspNetCoreMcpServer\AspNetCoreMcpServer.csproj", "{6001BAA3-90C0-4C77-BEEF-0CF788E7898E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -12,5 +14,9 @@ Global
{D9A9A1BA-D847-4083-A085-DCBD43B5E1DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9A9A1BA-D847-4083-A085-DCBD43B5E1DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D9A9A1BA-D847-4083-A085-DCBD43B5E1DC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6001BAA3-90C0-4C77-BEEF-0CF788E7898E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6001BAA3-90C0-4C77-BEEF-0CF788E7898E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6001BAA3-90C0-4C77-BEEF-0CF788E7898E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6001BAA3-90C0-4C77-BEEF-0CF788E7898E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal