使(shi)用MCP C# SDK開發MCP Server + Client
大(da)家好,我是Edison。
近日(ri)被MCP刷屏了,剛好看到張(zhang)隊發了一(yi)篇文章(zhang)提到MCP的官方C# SDK發布了預覽版,于是手癢(yang)癢(yang)嘗了一(yi)下鮮(xian),寫了一(yi)個DEMO分享給大家(jia)。
MCP是什么鬼?
MCP,全稱是“模型上下文協(xie)議(yi)(yi)”(Model Context Protocol),是(shi)Anthropic開(kai)源的(de)一個標準協(xie)議(yi)(yi)。打個比方(fang),它(ta)就像(xiang)是(shi)AI世界的(de)“USB-C”接口。你知道USB-C吧?一根線(xian)就能連接手機、電腦、充電器(qi),超級方(fang)便(bian)。MCP的(de)作用也差不多,它(ta)讓AI模型(比如(ru)Anthropic的(de)Claude)可以輕松(song)地跟外部的(de)數據源和工具(ju)連接起來,比如(ru)數據庫、文件系統(tong)、API等(deng)等(deng)。以前,如果想讓AI訪問你的數據庫或(huo)者調(diao)用(yong)(yong)某(mou)個工具,得專門寫一堆代碼,特別麻煩。現(xian)在(zai)有了MCP,就像是(shi)插上USB-C線那么簡單,AI模型通過這個標(biao)準協議就能直(zhi)接獲(huo)取(qu)數據或(huo)執行(xing)操作,不用(yong)(yong)每(mei)次都重(zhong)新開(kai)發(fa)連接方式。這樣,開(kai)發(fa)AI應用(yong)(yong)就變得更(geng)快、更(geng)省事了。

MCP是如何工作的?
MCP是一個(ge)典型的C/S架構模(mo)式,即客戶端(duan) 和 服務端(duan),它們之間(jian)采用一種標準的消(xiao)息(xi)格式(JSON-RPC)進行通信,大模(mo)型可以通過(guo)這些消(xiao)息(xi)進行:
(1)獲取數據(ju):例(li)如通過SQL從DB中查詢訂單(dan)數據(ju);
(2)執(zhi)行操作:例如通過API調用發個消息通知;
(3)理解指令:例如(ru)通過一(yi)些提示詞模板,LLM可以知(zhi)道如(ru)何(he)使(shi)用數據和(he)工(gong)具;

簡(jian)單來說(shuo),MCP就是AI的“萬能(neng)(neng)(neng)接(jie)口”。有了它,AI模型(xing)就能(neng)(neng)(neng)像插上USB-C線一(yi)樣,輕松連接(jie)到各種外部(bu)數據源(yuan)和工具,變(bian)得更(geng)聰明(ming)、更(geng)實(shi)用。不(bu)管是開發者還是普通用戶,都能(neng)(neng)(neng)通過(guo)MCP讓AI干(gan)更(geng)多(duo)事,而且過(guo)程(cheng)簡(jian)單又(you)安全(quan)。未(wei)來隨著MCP的普及(ji),我們可能(neng)(neng)(neng)會看到更(geng)多(duo)能(neng)(neng)(neng)干(gan)實(shi)事兒的AI應用冒出來!
創建一個MCP Server
這里我們(men)使用MCP C# SDK來(lai)實現,使用標準的IO傳輸方式。
(1)創建一個.NET 8.0控制臺應(ying)用,假(jia)設命名(ming)為:EDT.McpServer.ConsoleHost
(2)安裝MCP SDK
ModelContextProtocol 0.1.0-preview.4
(3)創建一個Tools目錄(lu),然后添(tian)加一個TimeTool.cs
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}."; }
這個(ge)TimeTool就是我們定義(yi)的基于(yu)(yu)MCP的Tool,可(ke)以(yi)看(kan)到基于(yu)(yu)SDK提(ti)供(gong)的Attribute,可(ke)以(yi)方便地將(jiang)其指定為(wei)MCP Server Tools。
(3)修改(gai)Program.cs設置為啟動(dong)MCP Server
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.DependencyInjection; using EDT.McpServer.Tools.ConsoleHost; try { Console.WriteLine("Starting MCP Server..."); var builder = Host.CreateEmptyApplicationBuilder(settings: null); builder.Services .AddMcpServer() .WithStdioServerTransport() .WithToolsFromAssembly(); await builder.Build().RunAsync(); return 0; } catch (Exception ex) { Console.WriteLine($"Host terminated unexpectedly : {ex.Message}"); return 1; }
同樣,也是很方便地就完成了(le)MCP Server的創(chuang)建,重點關注(zhu)WithToolsFromAssembly這(zhe)個擴展方法,它會掃描程序集中添加(jia)了(le)McpServerTool標簽(qian)的類進行注(zhu)冊。
這時我們已經(jing)完成了(le)MCP Server的創建,可以把它啟(qi)動(dong)起來(lai)了(le)。
但(dan)是,要完(wan)成今天的目標(biao),連(lian)接起來測試,我(wo)們還得(de)實現一個Client來調用Server。
創建一個MCP Client
(1)創建一(yi)個(ge).NET 8.0控制(zhi)臺應用,假設命名為:EDT.McpServer.Client
(2)安裝MCP SDK
ModelContextProtocol 0.1.0-preview.4
(3)修(xiu)改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" } });
需要注意的(de)是(shi)(shi):這里(li)我們(men)MCP Server使用的(de)是(shi)(shi)標準IO傳輸方(fang)式,因此指定(ding)TransportType為StdIo,同時(shi)指定(ding)command為MCP Server應用程序所在的(de)exe的(de)目錄位置。當然(ran),這里(li)的(de)這種方(fang)式有(you)點不是(shi)(shi)很(hen)規范,但你(ni)只需要了解它是(shi)(shi)需要訪(fang)問MCP Server的(de)程序地址就行了。
列出可用的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:這里基(ji)于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 Server var 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.IsNullOrWhiteSpace(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(); }
最后的(de)效果如下圖所示:

創建(jian)一(yi)個(ge)基于ASP.NET的MCP Server
除了使(shi)用標準的IO協議(yi),我們還(huan)可以實現一個基于(yu)ASP.NET Core的MCP SSE Server,顧名思義它就是(shi)使(shi)用SSE傳(chuan)輸方(fang)式。
(1)創(chuang)建(jian)一個.NET 8.0 ASP.NET WebAPI應用,假設命(ming)名(ming)為(wei):EDT.McpServer.WebHost
(2)安裝MCP SDK
ModelContextProtocol 0.1.0-preview.4 ModelContextProtocol.AspNetCore 0.1.0-preview.4
(3)創(chuang)建(jian)一(yi)個Tools目錄,然后添加一(yi)個TimeTool.cs
這里和上面的一樣,不再贅(zhui)述。
(4)修改Program.cs完成MCP Server配置:
using EDT.McpServer.WebHost.Tools; using ModelContextProtocol.AspNetCore; try { Console.WriteLine("Starting MCP Server..."); var builder = WebApplication.CreateBuilder(args); builder.Services.AddMcpServer().WithToolsFromAssembly(); var app = builder.Build(); app.UseHttpsRedirection(); app.MapGet("/", () => "Hello MCP Server!"); app.MapMcp(); app.Run(); return 0; } catch (Exception ex) { Console.WriteLine($"Host terminated unexpectedly : {ex.Message}"); return 1; }
可以看(kan)到,就(jiu)是(shi)(shi)這么簡單,通(tong)過(guo)MapMcp實現了/sse端點(dian)(dian)的(de)(de)(de)映射。后續MCP Client要連接的(de)(de)(de)就(jiu)是(shi)(shi)這個(ge)/sse的(de)(de)(de)端點(dian)(dian)。
(5)這時,你就可以把這個ASP.NET WebAPI應用啟動起來,假設我們這里是 //localhost:8443,你就可以(yi)通過(guo)下面(mian)的一(yi)點點修改,讓之前的這(zhe)個(ge)MCP Client連接上這(zhe)個(ge)MCP Server:
await using var mcpClient = await McpClientFactory.CreateAsync(new() { Id = "time", Name = "Time MCP Server", TransportType = TransportTypes.Sse, Location = "//localhost:8443/sse" });
可以看到,僅(jin)(jin)僅(jin)(jin)修改TransportType為(wei)SSE,然后指(zhi)定Server的BaseUrl即可。
OK,讓我們再來(lai)運行一下(xia)Client看看能(neng)否再次(ci)成功(gong)調用Tool:

看來這次使用(yong)(yong)SSE傳輸方式也(ye)能調用(yong)(yong)成(cheng)功了!Perfect!
小結
本(ben)(ben)文介紹了MCP的基本(ben)(ben)概念和工作模式,然后演示了如何通(tong)過MCP C# SDK創建MCP Server和Client,以(yi)及基于(yu)ASP.NET WebAPI創建SSE Server,相(xiang)信(xin)會對你(ni)有所幫助。
如果(guo)你也是.NET程序員希望參(can)與AI應(ying)用的開(kai)發,那就快快了解(jie)和使用基于Microsoft.Extensioins.AI + MCP C# SDK 的生態組(zu)件庫吧。
示例(li)源(yuan)碼
GitHub:
參考內容
MCP C# SDK Samples 《》
推薦(jian)內容

作者:
出處:
本文版(ban)權歸作(zuo)者和博客園共(gong)有,歡迎轉載,但未經作(zuo)者同意必須保留(liu)此段聲(sheng)明,且(qie)在文章頁面明顯位置給出(chu)原(yuan)文鏈(lian)接。

近日被MCP刷屏了,剛好看到張隊發了一篇文章提到MCP的官方C# SDK發布了預覽版,于是手癢癢嘗了一下鮮,寫了一個DEMO分享給大家。MCP,全稱是“模型上下文協議”(Model Context Protocol),是Anthropic開源的一個標準協議,AI模型通過這個標準協議就能直接獲取數據或執行操作,不用每次都重新開發連接方式。