百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

使用MCP C# SDK开发MCP Server + Client

bigegpt 2025-05-02 09:11 9 浏览

大家好,我是Edison。

近日被MCP刷屏了,刚好看到张队发了一篇文章提到MCP的官方C# SDK发布了预览版,于是手痒痒尝了一下鲜,写了一个DEMO分享给大家。

MCP是什么鬼?

MCP,全称是“模型上下文协议”(Model Context Protocol),是Anthropic开源的一个标准协议。打个比方,它就像是AI世界的“USB-C”接口。你知道USB-C吧?一根线就能连接手机、电脑、充电器,超级方便。MCP的作用也差不多,它让AI模型(比如Anthropic的Claude)可以轻松地跟外部的数据源和工具连接起来,比如数据库、文件系统、API等等。
以前,如果想让AI访问你的数据库或者调用某个工具,得专门写一堆代码,特别麻烦。现在有了MCP,就像是插上USB-C线那么简单,AI模型通过这个标准协议就能直接获取数据或执行操作,不用每次都重新开发连接方式。这样,开发AI应用就变得更快、更省事了。
MCP是如何工作的?
MCP是一个典型的C/S架构模式,即客户端 和 服务端,它们之间采用一种标准的消息格式(JSON-RPC)进行通信,大模型可以通过这些消息进行:
(1)获取数据:例如通过SQL从DB中查询订单数据;
(2)执行操作:例如通过API调用发个消息通知;
(3)理解指令:例如通过一些提示词模板,LLM可以知道如何使用数据和工具;
简单来说,MCP就是AI的“万能接口”。有了它,AI模型就能像插上USB-C线一样,轻松连接到各种外部数据源和工具,变得更聪明、更实用。不管是开发者还是普通用户,都能通过MCP让AI干更多事,而且过程简单又安全。未来随着MCP的普及,我们可能会看到更多酷炫的AI应用冒出来!
创建一个MCP Server
这里我们使用MCP C# SDK来实现,使用标准的IO传输方式。
(1)创建一个.NET 8.0控制台应用,假设命名为:EDT.McpServer.ConsoleHost
(2)安装MCP SDK
ModelContextProtocol: 0.1.0-preview.2
(3)创建一个Tools目录,然后添加一个TimeTool.cs
这个TimeTool就是我们定义的基于MCP的Tool,可以看到基于SDK提供的Attribute,可以方便地将其指定为MCP Server Tools。
using ModelContextProtocol.Server;using System.ComponentModel;
namespace EDT.McpServer.Tools.ConsoleHost;
[McpServerToolType]public static class TimeTool{ [McpServerTool, Description("Get the current time for a city")] public static string GetCurrentTime(string city) => $"It is {DateTime.Now.Hour}:{DateTime.Now.Minute} in {city}.";}
(3)修改Program.cs设置为启动MCP Server
同样,也是很方便地就完成了MCP Server的创建,重点关注WithToolsFromAssembly这个扩展方法,它会扫描程序集中添加了McpServerTool标签的类进行注册。
using Microsoft.Extensions.Hosting;using ModelContextProtocol;using EDT.McpServer.Tools.ConsoleHost;
try{ Console.WriteLine("Starting MCP Server...");
var builder = Host.CreateEmptyApplicationBuilder(settings: ); builder.Services .AddMcpServer() .WithStdioServerTransport() .WithToolsFromAssembly();
await builder.Build().RunAsync(); return 0;}catch (Exception ex){ Console.WriteLine($"Host terminated unexpectedly : {ex.Message}"); return 1;}
这时我们已经完成了MCP Server的创建,可以把它启动起来了。
但是,要完成测试,我们还得实现一个Client来调用Server。
创建一个MCP Client
(1)创建一个.NET 8.0控制台应用,假设命名为:EDT.McpServer.Client
(2)安装MCP SDK
ModelContextProtocol: 0.1.0-preview.2
(3)修改Program.cs,实现以下步骤:
创建MCP Client:
await using var mcpClient = await McpClientFactory.CreateAsync(new(){ Id = "time", Name = "Time MCP Server", TransportType = TransportTypes.StdIo, TransportOptions = new() { ["command"] = @"..\..\..\..\EDT.McpServer\bin\Debug\net8.0\EDT.McpServer.exe" }});
需要注意的是:这里我们MCP Server使用的是标准IO传输方式,因此指定TransportType为StdIo,同时指定command为MCP Server应用程序所在的exe的目录位置。当然,这里的这种方式有点不是很规范,但你只需要了解它是需要访问MCP Server的程序地址就行了。
列出可用的Tools:
var tools = await mcpClient.ListToolsAsync();foreach (var tool in tools){ Console.WriteLine($"{tool.Name} ({tool.Description})");}
直接执行Tool:(一般情况下不会这样用,而是在LLM中来调用)
var result = await mcpClient.CallToolAsync( "GetCurrentTime", new Dictionary<string, object?>() { ["city"] = "Chengdu" }, CancellationToken.None);Console.WriteLine(result.Content.First(c => c.Type == "text").Text);
通过LLM来调用Tool:
这里基于Microsoft.Extensions.AI核心库来实现的,你也可以用Semantic Kernel库来做这个事,都行!
var apiKeyCredential = new ApiKeyCredential(config["LLM:ApiKey"]);var aiClientOptions = new OpenAIClientOptions();aiClientOptions.Endpoint = new Uri(config["LLM:EndPoint"]);var aiClient = new OpenAIClient(apiKeyCredential, aiClientOptions) .AsChatClient(config["LLM:ModelId"]);var chatClient = new ChatClientBuilder(aiClient) .UseFunctionInvocation() .Build();IList<ChatMessage> chatHistory =[ new(ChatRole.System, """ You are a helpful assistant delivering time in one sentence in a short format, like 'It is 10:08 in Paris, France.' """),];// Core Part: Get AI Tools from MCP Servervar mcpTools = await mcpClient.ListToolsAsync();var chatOptions = new ChatOptions(){ Tools = [..mcpTools]};// Prompt the user for a question.Console.ForegroundColor = ConsoleColor.Green;Console.WriteLine($"Assistant> How can I assist you today?");while (true){ // Read the user question. Console.ForegroundColor = ConsoleColor.White; Console.Write("User> "); var question = Console.ReadLine(); // Exit the application if the user didn't type anything. if (!string.IsOrWhiteSpace(question) && question.ToUpper() == "EXIT") break;
chatHistory.Add(new ChatMessage(ChatRole.User, question)); Console.ForegroundColor = ConsoleColor.Green; var response = await chatClient.GetResponseAsync(chatHistory, chatOptions); var content = response.ToString(); Console.WriteLine($"Assistant> {content}"); chatHistory.Add(new ChatMessage(ChatRole.Assistant, content));
Console.WriteLine();}
最后的效果如下图所示:
创建一个ASP.NET MCP SSE Server
除了使用标准的IO协议,我们还可以实现一个基于ASP.NET Core的MCP SSE Server,顾名思义它就是使用SSE传输方式。
(1)创建一个.NET 8.0 ASP.NET WebAPI应用,假设命名为:EDT.McpServer.WebHost
(2)安装MCP SDK
ModelContextProtocol: 0.1.0-preview.2
(3)创建一个Tools目录,然后添加一个TimeTool.cs
这里和上面的一样,不再赘述。
(4)创建一个McpEndpointRouteBuilderExtensions,参考下面的示例代码,它来自MCP C# SDK的Sample Code
public static class McpEndpointRouteBuilderExtensions{ public static IEndpointConventionBuilder MapMcpSse(this IEndpointRouteBuilder endpoints) { SseResponseStreamTransport? transport = ; 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) => { response.Headers.ContentType = "text/event-stream"; response.Headers.CacheControl = "no-cache";
await using var localTransport = transport = new SseResponseStreamTransport(response.Body); await using var server = McpServerFactory.Create(transport, mcpServerOptions.Value, loggerFactory, endpoints.ServiceProvider);
try { var transportTask = transport.RunAsync(cancellationToken: requestAborted); await server.StartAsync(cancellationToken: requestAborted); await transportTask; } catch (OperationCanceledException) when (requestAborted.IsCancellationRequested) { // RequestAborted always triggers when the client disconnects before a complete response body is written, // but this is how SSE connections are typically closed. } });
routeGroup.MapPost("/message", async context => { if (transport is ) { 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 ) { 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; }}
(5)修改Program.cs完成MCP Server配置:
using EDT.McpServer.WebHost.Extensions;using EDT.McpServer.WebHost.Tools;using ModelContextProtocol;
try{ Console.WriteLine("Starting MCP Server...");
var builder = WebApplication.CreateBuilder(args); builder.Services.AddMcpServer().WithToolsFromAssembly(); builder.Services.AddWeatherToolService(); var app = builder.Build();
app.UseHttpsRedirection(); app.MapGet("/", () => "Hello MCP Server!"); app.MapMcpSse();
app.Run(); return 0;}catch (Exception ex){ Console.WriteLine($"Host terminated unexpectedly : {ex.Message}"); return 1;}
这时,你就可以把这个ASP.NET WebAPI应用启动起来,假设我们这里是https://localhost:8843,你就可以通过下面的一点点修改,让MCP Client连接上这个SSE Server:
await using var mcpClient = await McpClientFactory.CreateAsync(new(){ Id = "time", Name = "Time MCP Server", TransportType = TransportTypes.Sse, Location = "https://localhost:8443/sse"});
可以看到,仅仅修改TransportType为SSE,然后指定Server的BaseUrl即可。
OK,让我们再来运行一下Client看看能否再次成功调用Tool:
看来这次使用SSE传输方式也能调用成功了!Perfect!
小结
本文介绍了MCP的基本概念和工作模式,然后演示了如何通过MCP C# SDK创建MCP Server和Client,以及基于ASP.NET WebAPI创建SSE Server,相信会对你有所帮助。
如果你也是.NET程序员希望参与AI应用的开发,那就快快了解和使用基于Microsoft.Extensioins.AI + MCP C# SDK 的生态组件库吧。
示例源码
GitHub: https://github.com/edisontalk/EdisonTalk.AI.Agents
>> 点击本文底部“阅读原文”即可直达
参考内容
MCP C# SDK Samples: https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples
推荐内容
Microsoft Learn: https://learn.microsoft.com/zh-cn/dotnet/ai/ai-extensions?wt.mc_id=MVP_397012
eShopSupport: https://github.com/dotnet/eShopSupport?wt.mc_id=MVP_397012
devblogs: https://devblogs.microsoft.com/dotnet/e-shop-infused-with-ai-comprehensive-intelligent-dotnet-app-sample?wt.mc_id=MVP_397012
年终总结:Edison的2024年终总结
数字化转型:我在传统企业做数字化转型
C#刷算法题:C#刷剑指Offer算法题系列文章目录
C#刷设计模式:C#刷23种设计模式系列文章目录
.NET面试:.NET开发面试知识体系

相关推荐

当Frida来“敲”门(frida是什么)

0x1渗透测试瓶颈目前,碰到越来越多的大客户都会将核心资产业务集中在统一的APP上,或者对自己比较重要的APP,如自己的主业务,办公APP进行加壳,流量加密,投入了很多精力在移动端的防护上。而现在挖...

服务端性能测试实战3-性能测试脚本开发

前言在前面的两篇文章中,我们分别介绍了性能测试的理论知识以及性能测试计划制定,本篇文章将重点介绍性能测试脚本开发。脚本开发将分为两个阶段:阶段一:了解各个接口的入参、出参,使用Python代码模拟前端...

Springboot整合Apache Ftpserver拓展功能及业务讲解(三)

今日分享每天分享技术实战干货,技术在于积累和收藏,希望可以帮助到您,同时也希望获得您的支持和关注。架构开源地址:https://gitee.com/msxyspringboot整合Ftpserver参...

Linux和Windows下:Python Crypto模块安装方式区别

一、Linux环境下:fromCrypto.SignatureimportPKCS1_v1_5如果导包报错:ImportError:Nomodulenamed'Crypt...

Python 3 加密简介(python des加密解密)

Python3的标准库中是没多少用来解决加密的,不过却有用于处理哈希的库。在这里我们会对其进行一个简单的介绍,但重点会放在两个第三方的软件包:PyCrypto和cryptography上,我...

怎样从零开始编译一个魔兽世界开源服务端Windows

第二章:编译和安装我是艾西,上期我们讲述到编译一个魔兽世界开源服务端环境准备,那么今天跟大家聊聊怎么编译和安装我们直接进入正题(上一章没有看到的小伙伴可以点我主页查看)编译服务端:在D盘新建一个文件夹...

附1-Conda部署安装及基本使用(conda安装教程)

Windows环境安装安装介质下载下载地址:https://www.anaconda.com/products/individual安装Anaconda安装时,选择自定义安装,选择自定义安装路径:配置...

如何配置全世界最小的 MySQL 服务器

配置全世界最小的MySQL服务器——如何在一块IntelEdison为控制板上安装一个MySQL服务器。介绍在我最近的一篇博文中,物联网,消息以及MySQL,我展示了如果Partic...

如何使用Github Action来自动化编译PolarDB-PG数据库

随着PolarDB在国产数据库领域荣膺桂冠并持续获得广泛认可,越来越多的学生和技术爱好者开始关注并涉足这款由阿里巴巴集团倾力打造且性能卓越的关系型云原生数据库。有很多同学想要上手尝试,却卡在了编译数据...

面向NDK开发者的Android 7.0变更(ndk android.mk)

订阅Google官方微信公众号:谷歌开发者。与谷歌一起创造未来!受Android平台其他改进的影响,为了方便加载本机代码,AndroidM和N中的动态链接器对编写整洁且跨平台兼容的本机...

信创改造--人大金仓(Kingbase)数据库安装、备份恢复的问题纪要

问题一:在安装KingbaseES时,安装用户对于安装路径需有“读”、“写”、“执行”的权限。在Linux系统中,需要以非root用户执行安装程序,且该用户要有标准的home目录,您可...

OpenSSH 安全漏洞,修补操作一手掌握

1.漏洞概述近日,国家信息安全漏洞库(CNNVD)收到关于OpenSSH安全漏洞(CNNVD-202407-017、CVE-2024-6387)情况的报送。攻击者可以利用该漏洞在无需认证的情况下,通...

Linux:lsof命令详解(linux lsof命令详解)

介绍欢迎来到这篇博客。在这篇博客中,我们将学习Unix/Linux系统上的lsof命令行工具。命令行工具是您使用CLI(命令行界面)而不是GUI(图形用户界面)运行的程序或工具。lsoflsof代表&...

幻隐说固态第一期:固态硬盘接口类别

前排声明所有信息来源于网络收集,如有错误请评论区指出更正。废话不多说,目前固态硬盘接口按速度由慢到快分有这几类:SATA、mSATA、SATAExpress、PCI-E、m.2、u.2。下面我们来...

新品轰炸 影驰SSD多款产品登Computex

分享泡泡网SSD固态硬盘频道6月6日台北电脑展作为全球第二、亚洲最大的3C/IT产业链专业展,吸引了众多IT厂商和全球各地媒体的热烈关注,全球存储新势力—影驰,也积极参与其中,为广大玩家朋友带来了...