AspNetCoreMcpServer 能加到 dify 中
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ obj/
|
||||
/packages/
|
||||
riderModule.iml
|
||||
/_ReSharper.Caches/
|
||||
/*.user
|
||||
|
||||
13
.idea/.idea.McpServerCSharp/.idea/.gitignore
generated
vendored
Normal file
13
.idea/.idea.McpServerCSharp/.idea/.gitignore
generated
vendored
Normal file
@@ -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
|
||||
4
.idea/.idea.McpServerCSharp/.idea/encodings.xml
generated
Normal file
4
.idea/.idea.McpServerCSharp/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||
</project>
|
||||
8
.idea/.idea.McpServerCSharp/.idea/indexLayout.xml
generated
Normal file
8
.idea/.idea.McpServerCSharp/.idea/indexLayout.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders />
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/.idea.McpServerCSharp/.idea/vcs.xml
generated
Normal file
6
.idea/.idea.McpServerCSharp/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
14
AspNetCoreMcpServer/AspNetCoreMcpServer.csproj
Normal file
14
AspNetCoreMcpServer/AspNetCoreMcpServer.csproj
Normal file
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ModelContextProtocol" Version="0.4.0-preview.3" />
|
||||
<PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.4.0-preview.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
6
AspNetCoreMcpServer/AspNetCoreMcpServer.http
Normal file
6
AspNetCoreMcpServer/AspNetCoreMcpServer.http
Normal file
@@ -0,0 +1,6 @@
|
||||
@AspNetCoreMcpServer_HostAddress = http://localhost:5283
|
||||
|
||||
GET {{AspNetCoreMcpServer_HostAddress}}/weatherforecast/
|
||||
Accept: application/json
|
||||
|
||||
###
|
||||
20
AspNetCoreMcpServer/Program.cs
Normal file
20
AspNetCoreMcpServer/Program.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using AspNetCoreMcpServer.Resources;
|
||||
using AspNetCoreMcpServer.Tools;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// 添加 HttpClientFactory 支持
|
||||
builder.Services.AddHttpClient();
|
||||
|
||||
builder.Services.AddMcpServer()
|
||||
.WithHttpTransport()
|
||||
.WithTools<EchoTool>()
|
||||
.WithTools<SampleLlmTool>()
|
||||
.WithTools<WeatherTools>()
|
||||
.WithTools<IotDeviceTools>()
|
||||
.WithResources<SimpleResourceType>();
|
||||
|
||||
|
||||
var app = builder.Build();
|
||||
app.MapMcp();
|
||||
app.Run();
|
||||
23
AspNetCoreMcpServer/Properties/launchSettings.json
Normal file
23
AspNetCoreMcpServer/Properties/launchSettings.json
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
AspNetCoreMcpServer/Resources/SimpleResourceType.cs
Normal file
11
AspNetCoreMcpServer/Resources/SimpleResourceType.cs
Normal file
@@ -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";
|
||||
}
|
||||
14
AspNetCoreMcpServer/Tools/EchoTool.cs
Normal file
14
AspNetCoreMcpServer/Tools/EchoTool.cs
Normal file
@@ -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());
|
||||
}
|
||||
526
AspNetCoreMcpServer/Tools/IotDeviceTools.cs
Normal file
526
AspNetCoreMcpServer/Tools/IotDeviceTools.cs
Normal file
@@ -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<string, DeviceState> _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<string> 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<string> 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<string> 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<string> 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<string> 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<string> 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<string> 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<string> 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<string> 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<string> 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<string> 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; }
|
||||
}
|
||||
31
AspNetCoreMcpServer/Tools/SampleLlmTool.cs
Normal file
31
AspNetCoreMcpServer/Tools/SampleLlmTool.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Microsoft.Extensions.AI;
|
||||
using ModelContextProtocol.Server;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace AspNetCoreMcpServer.Tools;
|
||||
|
||||
/// <summary>
|
||||
/// This tool uses dependency injection and async method
|
||||
/// </summary>
|
||||
[McpServerToolType]
|
||||
public sealed class SampleLlmTool
|
||||
{
|
||||
[McpServerTool(Name = "sampleLLM"), Description("Samples from an LLM using MCP's sampling feature")]
|
||||
public static async Task<string> 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}";
|
||||
}
|
||||
}
|
||||
366
AspNetCoreMcpServer/Tools/WeatherTools.cs
Normal file
366
AspNetCoreMcpServer/Tools/WeatherTools.cs
Normal file
@@ -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<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; } = "";
|
||||
}
|
||||
8
AspNetCoreMcpServer/appsettings.Development.json
Normal file
8
AspNetCoreMcpServer/appsettings.Development.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
9
AspNetCoreMcpServer/appsettings.json
Normal file
9
AspNetCoreMcpServer/appsettings.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
|
||||
<PackageReference Include="ModelContextProtocol" Version="0.4.0-preview.3" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -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<EchoTool>();
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
|
||||
|
||||
Console.WriteLine("Hello, World!");
|
||||
14
ConsoleEchoMcpServer/Tools/EchoTool.cs
Normal file
14
ConsoleEchoMcpServer/Tools/EchoTool.cs
Normal file
@@ -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());
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user