当前位置: 首页 > news >正文

Semantic Kernel也能充当MCP Client

背景

笔者之前,分别写过两篇关于Semantic Kernel(下简称SK)相关的博客,最近模型上下文协议(下称MCP)大火,实际上了解过SK的小伙伴,一看到 MCP的一些具体呈现,会发现,Client 调用 Server的方式,和SK调用插件的过程很像,实际操作了一下,发现确实是可以的。

也就是说,如果我们之前的项目里用到SK做过Agent相关的模块,如今也可以丝滑的让其充当MCP Client的角色,去使用更多MCP生态的东西,而不需要做更多的改动。

虽然SK是为AI Agent的发展而诞生的,但好框架就是好框架,没想到它和MCP也这么契合。

本篇,建立在《再尝Semantic Kernel,Planning特性很香》的基础上,再次扩展一下MCP相关的介绍。

注意:本篇不会深入介绍MCP相关的概念,架构等等前置内容,主要还是通过案例说明SK和MCP
Server之间的联系,建议不熟悉MCP相关内容的小伙伴,先登录MCP官网进行了解。

创建一个MCP Server

在MCP的官方介绍文档里已经有C#的官方SDK了,这里我们参照它官方的例子,先来一个MCP Server。

Tips:官方的案例已经非常简洁和完善了,建议没搞过MCP的小伙伴上手试一下,虽然案例很简单,一看就能明白,但那真正跑通的获得感,还是得自己动手试一下才能体会到。

构建服务

这一步我觉得大家还是直接看官方文档更清楚,我这里不在赘述

传送门👉:https://modelcontextprotocol.io/quickstart/server#building-your-server-5

需要注意的是,我这里的Server是使用SSE的传输方式。

目前SSE的方式官方已经声明会逐步被Streamable Http的形式替代,但目前还是Built-in状态,本地调试的话还可以使用stdio的方式,这也是Claude Desktop,Cline之类客户端工具支持的方式,这点大家按需设定即可,这部分内容可以参考这里👉:https://mcp-framework.com/docs/Transports/transports-overview。

编写Tool

定义一个class,然后标记上MCPServer的特定属性,这部分官网也有介绍,我就直接上代码了

[McpServerToolType]
public static class WeatherTools
{[McpServerTool(Name = "GetWeather"), Description("获取当前城市的天气")]public static async Task<string> GetWeather(HttpClient client,[Description("中国的城市编码adcode")] string adcode){if (string.IsNullOrEmpty(adcode)){return "adcode不能为空";}string gdKey = ConfigHelper.GetAppSetting("GaoDeKey");var jsonElement = await client.GetFromJsonAsync<JsonElement>($"/v3/weather/weatherInfo?key={gdKey}&city={adcode}&extensions=base");var lives = jsonElement.GetProperty("lives").EnumerateArray();if (!lives.Any()){return "当前城市天气获取失败";}return string.Join("\n--\n",lives.Select(live =>{var city =  $"{live.GetProperty("province").GetString()}--{live.GetProperty("city").GetString()}";var weather = live.GetProperty("weather").GetString();var temperature = live.GetProperty("temperature").GetString();var windPower = live.GetProperty("windpower").GetString();var humidity = live.GetProperty("humidity").GetString();return $"城市:{city}\n天气:{weather}\n温度:{temperature}°C\n风力:{windPower}级\n湿度:{humidity}%";}));}
}

我这里,没有使用官方案例里的天气接口,而是改成了高德的天气接口,因为一会儿还要演示一下SK调用MCP Server的能力,除了调用本地的Server,高德还有一个云端的MCP Server,非常好用,稍后一并介绍一下,正好就连天气接口也改成高德的。

编写完成后,启动我们的Server服务。

dotnet run

验证

Tool编写完成后,可以先使用一些软件或者工具类的MCP Client验证一下,开发阶段,这些工具还是非常有必要的,它的角色定位就像我们用到的数据库管理工具,比如SSMS,Pg Admin,DBeaver等。

我这里使用的是官方的MCP Inspector,本地只要有node和npx环境即可。

另外,因为要经常测试一些Server,建议把Python和Python包管理工具uv也安装一下。

然后,我们启动Inspector

npx @modelcontextprotocol/inspector node build/index.js

在这里插入图片描述

启动之后,控制台会监听一个端口,然后在浏览器打开,然后配置好我们的Server地址,如下图

获取到所有的Tool之后,测试验证一下我们刚完成的天气接口是否生效。

至此,验证工作完成,说明我们的MCP Server是可以正常工作的,接下来就是接入实际的业务系统,来调用这个Server提供的能力了。

在业务系统创建MCP Client

注入SK服务

这部分略过,在前面的系列文章里已经写过了,或者大家也可以直接查看微软的官方文档,这里不再赘述。

编写插件

这里呢,因为我在之前的项目里,已经开始使用SK框架了,并且完成了部分的Agent功能,都是以Plugin的方式注入到系统里的,这里的演示也就暂时以这种方式来接入,后续再根据实际情况调整。

插件的代码如下

[KernelFunction("call_weather_api")]
[Description("通过地理编码,获取天气信息")]
[return: Description("如果运行正常,返回编号所属地址的天气详情")]
public async Task<string> CallWeatherApi(string adcode)
{Logger.Debug("--------天气插件正确执行---------------");var defaultOptions = new McpClientOptions{ClientInfo = new() { Name = "SK", Version = "1.0.0" }};var defaultConfig = new SseClientTransportOptions{Endpoint = new Uri($"http://localhost:5001/sse"),Name = "Magic.Services.MCPServer",};await using var client = await McpClientFactory.CreateAsync(new SseClientTransport(defaultConfig),defaultOptions);var result = await client.CallToolAsync("GetWeather", new Dictionary<string, object?>{{ "adcode",adcode}});return JsonHelper.JsonSerialize(result);
}

调用

我这里是在Web系统里进行的演示,所以以接口形式来调用插件,代码如下

public async Task<IActionResult> CallLocalServer(string adcode)
{_kernel.Plugins.AddFromType<LocalServer>("LocalServer", _serviceProvider);// 获取聊天完成服务var chatCompletionService = _kernel.GetRequiredService<IChatCompletionService>();// 启用自动函数调用OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new(){ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions,//FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()};PromptExecutionSettings settings = new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() };ChatHistory chatHistory = [];chatHistory.AddSystemMessage($"你是一个天气预报员,本函数的入参会给你一个中国地理位置编码,你要调用合适的MCP Server来完成天气预报。");chatHistory.AddUserMessage($"查询地理编码为【{adcode}】的地点天气");var chatResult = await chatCompletionService.GetChatMessageContentAsync(chatHistory,openAIPromptExecutionSettings,_kernel);Console.Write($"\nAssistant : {chatResult}\n");return Json(chatResult);
}

验证

为了方便验证,可以先把接口的访问等级降低,直接通过URL地址访问,效果如下

控制台打印的信息如下

至此,我们已经在本地创建了一个MCPServer,并通过MCP Inspector进行了验证,同时又在原有使用SK的系统里,通过SK成功调用了这个Server提供的tool,接入复杂度可以接受,效果也非常不错。

接下来,再试试SK能不能成功调用高德地图的MCP Server

调用高德MCP Server

前置工作

注意,如果前面的天气接口你是使用的高德的服务,那么相信你已经注册了高德的key,如果没有,这里需要去注册一下。

编写服务

由于调用的是第三方的MCP,我们这里可以直接在接口或者服务类里编写调用代码

public async Task<IActionResult> CallGaodeServer(string msg)
{// 第一步:创建 mcp 客户端var defaultOptions = new McpClientOptions{ClientInfo = new() { Name = "地图规划", Version = "1.0.0" }};var defaultConfig = new SseClientTransportOptions{Endpoint = new Uri(ConfigurationHelper.GetSectionValue("GaodeMCP")),Name = "Magic.Services.MCPServer",};await using var client = await McpClientFactory.CreateAsync(new SseClientTransport(defaultConfig),defaultOptions);var tools = await client.ListToolsAsync();foreach (var tool in tools){Logger.Debug($"秀一下高德的能力之--- {tool.Name}");}#pragma warning disable SKEXP0001_kernel.Plugins.AddFromFunctions("gaodemap", tools.Select(aiFunction => aiFunction.AsKernelFunction()));#pragma warning restore SKEXP0001// 获取聊天完成服务var chatCompletionService = _kernel.GetRequiredService<IChatCompletionService>();// 启用自动函数调用OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new(){ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions,};PromptExecutionSettings settings = new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() };ChatHistory chatHistory = [];chatHistory.AddUserMessage(msg);var chatResult = await chatCompletionService.GetChatMessageContentAsync(chatHistory,openAIPromptExecutionSettings,_kernel);Console.Write($"\nAssistant : {chatResult}\n");return Json(chatResult);
}

验证

验证工作,还是和前面一样,暂时在浏览器里直接访问即可。

控制台打印的信息更友好一点

结束语

好了,至此,我们成功在原有的系统上,使用SK框架充当MCP Client的角色,完成了本地MCP Server的调用和第三方MCP Server的调用目标。

最后,再推荐几个介绍MCP的参考站点

  • 中文官网:https://mcp-docs.cn/
  • MCP.so:https://mcp.so/zh
  • 痴者工良的博客:https://www.cnblogs.com/whuanle/p/18837493
  • Semantic Kernel的学习文档:https://learn.microsoft.com/zh-cn/semantic-kernel/
  • 本人 之前写的两篇介绍SK的博客,本文的代码部分缺失很多初始化的代码,均在这两篇当中https://juejin.cn/post/7460393309552164902,https://juejin.cn/post/7463301527991762955

相关文章:

  • 像拆盲盒一样读懂 XML:从新手到掌握它在大数据的用武之地
  • ​​激光雷达(LiDAR)数据​​ 的标准存储格式.las文件介绍
  • AI数字人:繁荣背后的伦理困境与法律迷局(8/10)
  • 【因果推断】(二)CV中的应用
  • 大肠杆菌诱导蛋白时OD600=0.6-0.8添加IPTG的思考-实验操作系列-009
  • Mysql日志undo redo binlog与更新一条数据的执行过程详解
  • 基于C#+Unity实现遇见李白小游戏
  • 【金仓数据库征文】金仓数据库KingbaseES:千行百业国产化征程中的璀璨之星
  • 使用yolo用python检测
  • 模式设计简介
  • java多线程(7.0)
  • GIT下载步骤
  • springboot如何管理多数据源?
  • 【6】GD32 高级通信外设 CAN、USBD
  • AUTOSAR图解==>AUTOSAR_SWS_SAEJ1939TransportLayer
  • 金仓数据库 KingbaseES 产品深度优化提案:迈向卓越的全面升级
  • 从字节码开始到ASM的gadgetinspector源码解析
  • cdh平台管理与运维最佳实践
  • ubuntu22.04 命令行修改静态ip
  • 连续帧点云目标检测结果展示,python实现
  • 铜钴巨头洛阳钼业一季度净利润同比大增九成,最新宣布首度进军黄金矿产
  • 税务部门曝光3起通过拆分经营骗享小规模纳税人税费优惠偷税案件
  • 美称中美贸易谈判仍在进行中,外交部:美方不要混淆视听
  • 美银证券前董事总经理胡霁光履新,任摩根士丹利中国区副主席
  • 瑞士外长答澎湃:瑞中都愿升级自贸协定,关税战没有任何好处
  • 安徽铁塔再通报“会议室不雅行为”事件:涉事员工停职检查