diff --git a/McpStudy.McpClient/Program.cs b/McpStudy.McpClient/Program.cs index 3ba0aa0..1071dc0 100644 --- a/McpStudy.McpClient/Program.cs +++ b/McpStudy.McpClient/Program.cs @@ -1,14 +1,33 @@ -namespace McpStudy.McpClient; +using Azure.Core.Pipeline; + +namespace McpStudy.McpClient; internal class Program { static async Task Main(string[] args) { Console.WriteLine("调用MCP服务示例"); - await CallStdioAsync(); + + var HttpClient = new HttpClient(new SocketsHttpHandler + { + + }) + { + BaseAddress = new Uri("http://localhost:5000/"), + Timeout = TimeSpan.FromSeconds(10), + }; + + + + + + + //await CallStdioAsync(); //请务必在调用前,启动SseServer服务 - await RequestSseServerAsync(); + //await RequestSseServerAsync(); + + await RequestStreamableHttpServerAsync(); await Task.CompletedTask; } @@ -105,4 +124,25 @@ internal class Program Console.WriteLine("调用 Studio 类型的MCP服务结束!"); } + + static async Task RequestStreamableHttpServerAsync() + { + Console.WriteLine($"开始调用 StreamableHttp 类型的MCP服务......{System.Environment.NewLine}"); + + var clientTransport = new SseClientTransport( + new SseClientTransportOptions + { + Name = "StudyMCPServer", + Endpoint = new Uri("http://localhost:5000"), + TransportMode = HttpTransportMode.StreamableHttp, + //AdditionalHeaders = new Dictionary() { { "text/event-stream", "Content-Type" } } + } + ); + + var mcpClient = await McpClientFactory.CreateAsync(clientTransport); + var clientTools = await mcpClient.ListToolsAsync(); + + //return clientTools.Select(_ => _.AsKernelFunction()); + Console.WriteLine("调用 Studio 类型的MCP服务结束!"); + } } diff --git a/McpStudy.McpServerSSE/McpStudy.McpServerSSE.csproj b/McpStudy.McpServerSSE/McpStudy.McpServerSSE.csproj index 933f213..9158f20 100644 --- a/McpStudy.McpServerSSE/McpStudy.McpServerSSE.csproj +++ b/McpStudy.McpServerSSE/McpStudy.McpServerSSE.csproj @@ -1,4 +1,4 @@ - + net9.0 diff --git a/McpStudy.McpServerSSE/Tools/StringTools.cs b/McpStudy.McpServerSSE/Tools/StringTools.cs new file mode 100644 index 0000000..50d617f --- /dev/null +++ b/McpStudy.McpServerSSE/Tools/StringTools.cs @@ -0,0 +1,30 @@ +namespace McpStudy.McpServerSSE.Tools +{ + [McpServerToolType] + public class StringTools + { + [McpServerTool,DisplayName("GetTextLength"), Description("获取文本的长度")] + public static int GetTextLength(string text) + { + if (text == null) + { + return 0; + } + + return text.Length; + } + + [McpServerTool, DisplayName("ReverseText"), Description("反转文本内容:把给定文本反序")] + public static string ReverseText(string text) + { + if (text == null) + { + return ""; + } + else + { + return string.Join("", text.Reverse()); + } + } + } +} diff --git a/McpStudy.McpServerStreamableHttp/Controllers/WeatherForecastController.cs b/McpStudy.McpServerStreamableHttp/Controllers/WeatherForecastController.cs new file mode 100644 index 0000000..c141a01 --- /dev/null +++ b/McpStudy.McpServerStreamableHttp/Controllers/WeatherForecastController.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Mvc; + +namespace McpStudy.McpServerStreamableHttp.Controllers +{ + [ApiController] + [Route("[controller]")] + public class WeatherForecastController : ControllerBase + { + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } + + [HttpGet(Name = "GetWeatherForecast")] + public IEnumerable Get() + { + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = Summaries[Random.Shared.Next(Summaries.Length)] + }) + .ToArray(); + } + } +} diff --git a/McpStudy.McpServerStreamableHttp/GlobalUsing.cs b/McpStudy.McpServerStreamableHttp/GlobalUsing.cs new file mode 100644 index 0000000..2849c8c --- /dev/null +++ b/McpStudy.McpServerStreamableHttp/GlobalUsing.cs @@ -0,0 +1,20 @@ +global using System.Linq; +global using System.ComponentModel; +global using System.Net.Http.Headers; +global using System.IO.Pipelines; + +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; + +global using ModelContextProtocol; +global using ModelContextProtocol.Protocol; +global using ModelContextProtocol.Client; +global using ModelContextProtocol.Server; +global using ModelContextProtocol.SemanticKernel; +global using ModelContextProtocol.AspNetCore; +global using ModelContextProtocolServer; +global using ModelContextProtocolServer.Sse; +global using ModelContextProtocolServer.Stdio; + + diff --git a/McpStudy.McpServerStreamableHttp/Index.html b/McpStudy.McpServerStreamableHttp/Index.html new file mode 100644 index 0000000..d85c0e9 --- /dev/null +++ b/McpStudy.McpServerStreamableHttp/Index.html @@ -0,0 +1,33 @@ + + + + + + McpStudy.McpServerSSE + + +
+

McpStudy.McpServerSSE 启动成功!

+

WebAPI参阅:Swagger 文档

+

MCP服务测试:MCP服务页

+
+ + + \ No newline at end of file diff --git a/McpStudy.McpServerStreamableHttp/McpStudy.McpServerStreamableHttp.csproj b/McpStudy.McpServerStreamableHttp/McpStudy.McpServerStreamableHttp.csproj new file mode 100644 index 0000000..9158f20 --- /dev/null +++ b/McpStudy.McpServerStreamableHttp/McpStudy.McpServerStreamableHttp.csproj @@ -0,0 +1,25 @@ + + + + net9.0 + enable + enable + + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/McpStudy.McpServerStreamableHttp/McpStudy.McpServerStreamableHttp.http b/McpStudy.McpServerStreamableHttp/McpStudy.McpServerStreamableHttp.http new file mode 100644 index 0000000..4cc7cc5 --- /dev/null +++ b/McpStudy.McpServerStreamableHttp/McpStudy.McpServerStreamableHttp.http @@ -0,0 +1,6 @@ +@McpStudy.McpServerStreamableHttp_HostAddress = http://localhost:5190 + +GET {{McpStudy.McpServerStreamableHttp_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/McpStudy.McpServerStreamableHttp/Program.cs b/McpStudy.McpServerStreamableHttp/Program.cs new file mode 100644 index 0000000..87e1d0e --- /dev/null +++ b/McpStudy.McpServerStreamableHttp/Program.cs @@ -0,0 +1,68 @@ + +namespace McpStudy.McpServerStreamableHttp; + +public class Program +{ + private static Pipe _input = new Pipe(); + private static Pipe _output = new Pipe(); + + public static void Main(string[] args) + { + var builder = WebApplication.CreateBuilder(args); + + // Add services to the container. + + builder.Services.AddControllers(); + + // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi + builder.Services.AddOpenApi(); + + builder.Services + .AddMcpServer(options => + { + options.ServerInfo = new Implementation + { + Name = "StudyMCPServer", + Version = "1.0.0" + }; + }) + .WithHttpTransport(options => options.Stateless = true) + .WithToolsFromAssembly(typeof(McpStudy.Core.TimeTools).Assembly) + .WithToolsFromAssembly(typeof(Program).Assembly); + + + var app = builder.Build(); + + // Configure the HTTP request pipeline. + app.MapOpenApi(); + + app.UseSwaggerUI(options => + { + options.SwaggerEndpoint("/openapi/v1.json", "v1"); + }); + + app.UseAuthorization(); + + + app.MapControllers(); + + //ַ(֧վ):Զм+ض + //ֻм·(app.Map): / "" ȱapp.MapMcp + app.Use(async (context, next) => + { + if (context.Request.Path == "/" || context.Request.Path == "") + { + context.Response.ContentType = "text/html"; + await context.Response.SendFileAsync(Path.Combine(app.Environment.ContentRootPath, "index.html")); + } + else + { + await next(); + } + }); + + app.MapMcp(); + + app.Run(); + } +} diff --git a/McpStudy.McpServerStreamableHttp/Properties/launchSettings.json b/McpStudy.McpServerStreamableHttp/Properties/launchSettings.json new file mode 100644 index 0000000..74652c3 --- /dev/null +++ b/McpStudy.McpServerStreamableHttp/Properties/launchSettings.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5190", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/McpStudy.McpServerStreamableHttp/WeatherForecast.cs b/McpStudy.McpServerStreamableHttp/WeatherForecast.cs new file mode 100644 index 0000000..cbb6d9d --- /dev/null +++ b/McpStudy.McpServerStreamableHttp/WeatherForecast.cs @@ -0,0 +1,13 @@ +namespace McpStudy.McpServerStreamableHttp +{ + public class WeatherForecast + { + public DateOnly Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } + } +} diff --git a/McpStudy.McpServerStreamableHttp/appsettings.Development.json b/McpStudy.McpServerStreamableHttp/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/McpStudy.McpServerStreamableHttp/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/McpStudy.McpServerStreamableHttp/appsettings.json b/McpStudy.McpServerStreamableHttp/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/McpStudy.McpServerStreamableHttp/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/McpStudy.sln b/McpStudy.sln index f8b5416..cbe2857 100644 --- a/McpStudy.sln +++ b/McpStudy.sln @@ -18,6 +18,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{208BA388-4 Docs\项目说明.md = Docs\项目说明.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "McpStudy.McpServerStreamableHttp", "McpStudy.McpServerStreamableHttp\McpStudy.McpServerStreamableHttp.csproj", "{7891DD2F-E046-4509-9A10-E5CDD93B8B45}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -44,6 +46,10 @@ Global {34A4291B-BA36-491B-914D-674CF3806314}.Debug|Any CPU.Build.0 = Debug|Any CPU {34A4291B-BA36-491B-914D-674CF3806314}.Release|Any CPU.ActiveCfg = Release|Any CPU {34A4291B-BA36-491B-914D-674CF3806314}.Release|Any CPU.Build.0 = Release|Any CPU + {7891DD2F-E046-4509-9A10-E5CDD93B8B45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7891DD2F-E046-4509-9A10-E5CDD93B8B45}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7891DD2F-E046-4509-9A10-E5CDD93B8B45}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7891DD2F-E046-4509-9A10-E5CDD93B8B45}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE