Semantic Kernel:开启MCP

MCP越来越火了,各大模型公司也在陆陆续续支持MCP了,OpenAI在前不久声明对MCP的支持,同时社区的SDK也来了,今天就用ModelContextProtocol来创建服务端和客户端,并且找通他们。与此同时,也不能落下SK,看看SK怎么使用MCP。先看Server端:项目文件如下:

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>    <TargetFramework>net10.0</TargetFramework>    <Nullable>enable</Nullable>    <ImplicitUsings>enable</ImplicitUsings>  </PropertyGroup>
  <ItemGroup>    <PackageReference Include="ModelContextProtocol" Version="0.1.0-preview.2" />  </ItemGroup></Project>

Program.cs:

using ModelContextProtocol;
var builder = WebApplication.CreateBuilder(args);builder.Services.AddMcpServer()        .WithStdioServerTransport()    .WithToolsFromAssembly();var app = builder.Build();app.MapGet("/", () => "Hello World!");app.MapMcpSse();app.Run();

工具定义:

using ModelContextProtocol.Server;using System.ComponentModel;
namespace MCPOrderTool.Tools;
[McpServerToolType]public static class OrderTool{    [McpServerTool("queryOrder"), Description("按开始日期和结速日期查询订单")]    public static List<Order> QueryList(DateTime? beginTime, DateTime? endTime)    {        Console.WriteLine("-------------参数-------------");        Console.ForegroundColor = ConsoleColor.Green;        Console.WriteLine($"BeginTime:{beginTime},EndTime:{endTime}");        Console.ResetColor();        Console.WriteLine("-------------参数-------------");        if (beginTime is null || endTime is null)        {            beginTime = DateTime.Now;            endTime = DateTime.Now;        }        return new List<Order>        {            new Order("NO000001", "Order1", DateTime.Now.ToString("yyyy-MM-dd"), "Pending"),            new Order("NO000002", "Order2", DateTime.Now.ToString("yyyy-MM-dd"), "Completed"),            new Order("NO000003", "Order3", DateTime.Now.ToString("yyyy-MM-dd"), "Pending"),            new Order("NO000004", "Order4", DateTime.Now.ToString("yyyy-MM-dd"), "Completed"),            new Order("NO000005", "Order5", DateTime.Now.ToString("yyyy-MM-dd"), "Pending"),            new Order("NO000006", "Order6", DateTime.Now.ToString("yyyy-MM-dd"), "Completed"),            new Order("NO000007", "Order7", DateTime.Now.ToString("yyyy-MM-dd"), "Pending"),            new Order("NO000008", "Order8", DateTime.Now.ToString("yyyy-MM-dd"), "Completed"),            new Order("NO000009", "Order9", DateTime.Now.ToString("yyyy-MM-dd"), "Pending"),            new Order("NO000010", "Order10",  DateTime.Now.ToString("yyyy-MM-dd"), "Completed"),        };    }}
public record Order(string OrderId, string OrderName, string OrderTime, string OrderStatus);

MCP服务端Map定义:

using ModelContextProtocol.Protocol.Messages;using ModelContextProtocol.Server;using ModelContextProtocol.Utils.Json;using Microsoft.Extensions.Options;using ModelContextProtocol.Protocol.Transport;
public static class McpEndpointRouteBuilderExtensions{    public static IEndpointConventionBuilder MapMcpSse(this IEndpointRouteBuilder endpoints)    {        IMcpServer? server = null;        SseResponseStreamTransport? transport = null;        var loggerFactory = endpoints.ServiceProvider.GetRequiredService<ILoggerFactory>();        var mcpServerOptions = endpoints.ServiceProvider.GetRequiredService<IOptions<McpServerOptions>>();
        var routeGroup = endpoints.MapGroup("");
        routeGroup.MapGet("/sse", async (HttpResponse response, CancellationToken requestAborted) =>        {                    await using var localTransport = transport = new SseResponseStreamTransport(response.Body);            await using var localServer = server = McpServerFactory.Create(transport, mcpServerOptions.Value, loggerFactory, endpoints.ServiceProvider);
            await localServer.StartAsync(requestAborted);            response.Headers.ContentType = "text/event-stream";            response.Headers.CacheControl = "no-cache";            try            {                await transport.RunAsync(requestAborted);            }            catch (OperationCanceledException) when (requestAborted.IsCancellationRequested)            {                               Console.WriteLine("closed");            }        });
        routeGroup.MapPost("/message", async context =>        {                       if (transport is null)            {                await Results.BadRequest("Connect to the /sse endpoint before sending messages.").ExecuteAsync(context);                return;            }            var message = await context.Request.ReadFromJsonAsync<IJsonRpcMessage>(McpJsonUtilities.DefaultOptions, context.RequestAborted);            if (message is null)            {                await Results.BadRequest("No message in request body.").ExecuteAsync(context);                return;            }            await transport.OnMessageReceivedAsync(message, context.RequestAborted);            context.Response.StatusCode = StatusCodes.Status202Accepted;            await context.Response.WriteAsync("Accepted");        });        return routeGroup;    }}

下面是Client使用:项目文件:

<Project Sdk="Microsoft.NET.Sdk">  <PropertyGroup>    <OutputType>Exe</OutputType>    <TargetFramework>net10.0</TargetFramework>    <ImplicitUsings>enable</ImplicitUsings>    <Nullable>enable</Nullable>  </PropertyGroup>  <ItemGroup>    <PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.3.0-preview.1.25161.3" />    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.0-preview.2.25163.2" />    <PackageReference Include="Microsoft.SemanticKernel" Version="1.44.0" />    <PackageReference Include="Microsoft.SemanticKernel.Plugins.Core" Version="1.44.0-alpha" />    <PackageReference Include="ModelContextProtocol" Version="0.1.0-preview.2" />    <PackageReference Include="ModelContextProtocol-SemanticKernel" Version="0.0.1-preview-03" />  </ItemGroup></Project>

Program.cs文件:

using Azure.Core;using Microsoft.Extensions.AI;using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Logging;using Microsoft.SemanticKernel;using Microsoft.SemanticKernel.Connectors.OpenAI;using ModelContextProtocol.Client;using ModelContextProtocol.Configuration;using ModelContextProtocol.Protocol.Transport;using ModelContextProtocol.Protocol.Types;using ModelContextProtocol.SemanticKernel.Extensions;using ModelContextProtocol.SemanticKernel.Options;using ModelContextProtocol.Server;using OpenAI;using System.ComponentModel;using System.Text.Json;using System.Text.Json.Serialization;
var key = File.ReadAllText("c:/gpt/key.txt");
while (true){    Console.WriteLine("===========================================================");    Console.WriteLine("1、获取工具列表  2、Client调用工具  3、SK调用工具  0、退出");    Console.WriteLine("===========================================================");    var no = Console.ReadLine();    switch (no)    {        case "1":            await MCPClientToolsListAsync();            break;        case "2":
            await MCPClientAsync();            break;        case "3":            await SKClientAsync();            break;        case "0":            return;    }}
async Task MCPClientToolsListAsync(){    var serverConfig = new McpServerConfig    {        Id = "QueryOrder",        Name = "QueryOrder",        TransportType = TransportTypes.Sse,        Location = "http://localhost:3001/sse"    };    var clientOptions = new McpClientOptions    {        ClientInfo = new()        {            Name = "QueryOrderClient",            Version = "0.0.1",        }    };    var mcpClient = await McpClientFactory.CreateAsync(serverConfig, clientOptions);    Console.WriteLine("获取Tools:");    var tools = await mcpClient.ListToolsAsync();
    foreach (var tool in tools)    {
        Console.WriteLine($"{tool.Name},{tool.Description}");    }    Console.WriteLine();}
async Task MCPClientAsync(){    var serverConfig = new McpServerConfig    {        Id = "QueryOrder",        Name = "MCPOrderTool",        TransportType = TransportTypes.Sse,        Location = "http://localhost:3001/sse"    };    var clientOptions = new McpClientOptions    {        ClientInfo = new()        {            Name = "QueryOrderClient",            Version = "0.0.1",        }    };    var mcpClient = await McpClientFactory.CreateAsync(serverConfig, clientOptions);    var functions = await mcpClient.ListToolsAsync();    IChatClient chatClient = new OpenAIClient(key).AsChatClient("gpt-4o-mini")    .AsBuilder().UseFunctionInvocation().Build();    var response = chatClient.GetStreamingResponseAsync(     "查询本周的订单",     new()     {         Tools = [.. functions],     });    await foreach (var item in response)    {        Console.Write(item.Text);    }    Console.WriteLine();}
async Task SKClientAsync(){    var builder = Kernel.CreateBuilder();    builder.Services.AddLogging(c => c.AddDebug().SetMinimumLevel(LogLevel.Trace));
    builder.Services.AddOpenAIChatCompletion(        serviceId: "openai",        modelId: "gpt-4o-mini",        apiKey: key);
    var kernel = builder.Build();    kernel.Plugins.AddFromType<TimeInformationPlugin>();    await kernel.Plugins.AddMcpFunctionsFromSseServerAsync("MCPOrderTool", "http://localhost:3001/sse");    var executionSettings = new OpenAIPromptExecutionSettings    {        Temperature = 0,        FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()    };    var prompt = "请查询本月的订单";    var result = kernel.InvokePromptStreamingAsync(prompt, new(executionSettings));    await foreach (var item in result)    {        Console.Write(item.ToString());    }    Console.WriteLine();}
public class TimeInformationPlugin{    [KernelFunction, Description("获取当前的 UTC 时间。")]    public string GetCurrentUtcTime()        => DateTime.UtcNow.ToString("R");}

下面是运行结果:1、列举服务端的工具信息,2、是用普通方式调用MCP服务端,这时注意右边红色的框,时间是2023年,取的是模型时间 3、是用SK的方式调用工具,同时SK还添加当前时间插件,看蓝色框,取的是当前时间,正确。

图片

声明:文中观点不代表本站立场。本文传送门:https://eyangzhen.com/426691.html

联系我们
联系我们
分享本页
返回顶部