用C#构建模型上下文协议(MCP)服务器
在人工智能和机器学习快速发展的世界中,模型与应用程序之间的有效沟通至关重要。模型上下文协议(Model Context Protocol,简称MCP)是一种标准化的协议,旨在通过提供一种结构化的方式,在AI模型与其客户端之间交换上下文和数据,从而促进这种通信。无论您是构建由AI驱动的应用程序,还是将多个模型集成到一个协调的系统中,MCP都能确保互操作性和可扩展性。对于使用Visual Studio Code等工具的开发者而言,现在可以将MCP服务端集成到您的开发流程中,并能够在本地机器上轻松地构建和测试MCP服务端。
随着MCP C# SDK的发布,开发者现在可以轻松地构建利用该协议的服务端和客户端。该SDK简化了实现过程,使您能够专注于应用程序的独特功能,而无需处理协议本身的复杂性。此外,SDK还包含对调用MCP服务端的支持,使开发者能够创建出能够与MCP服务端无缝交互的健壮客户端应用程序。
在本篇博客文章中,我们将探讨如何使用C# SDK创建您自己的MCP服务端和客户端应用程序。
备注
MCP C# SDK 目前处于预览阶段,其API可能会发生变化。随着SDK的不断演进,我们将持续更新本篇博客。
MCP服务端构建入门
MCP C# SDK 以 NuGet 包的形式分发,您可以将其集成到一个简单的控制台应用程序中。让我们先创建一个新的控制台应用,来开始构建我们的第一个 MCP 服务端:
dotnet new console -n MyFirstMCP
现在,让我们添加一些 MCP C# SDK 的基础 NuGet 包,以及用于托管服务端的包(通过 Microsoft.Extensions.Hosting)。
dotnet add package ModelContextProtocol --prerelease
dotnet add package Microsoft.Extensions.Hosting
ModelContextProtocol 包提供了新的 API,可用于创建连接到 MCP 服务端的客户端、构建 MCP 服务端,以及通过 Microsoft.Extensions.AI 集成大语言模型(LLM)的 AI 辅助库。
服务端启动
让我们用一些基本的脚手架代码来更新我们的 Program.cs,以创建 MCP 服务端,配置标准的服务端传输方式,并让服务端在当前运行的程序集中搜索可用的工具(或 API)。
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Server;
using System.ComponentModel;
var builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole(consoleLogOptions =>
{
// Configure all logs to go to stderr
consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
});
builder.Services
.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();
await builder.Build().RunAsync();
有了这段代码,我们现在就可以开始构建希望向 MCP 服务端公开的第一个工具了。
定义首个工具
让我们创建一个最基本的工具,用于复述接收到的指令。首先,我们定义一个类,该类将包含作为工具暴露出来的函数。
[McpServerToolType]
public static class EchoTool
{
[McpServerTool, Description("Echoes the message back to the client.")]
public static string Echo(string message) => $"Hello from C#: {message}";
[McpServerTool, Description("Echoes in reverse the message sent by the client.")]
public static string ReverseEcho(string message) => new string(message.Reverse().ToArray());
}
在我们的启动代码中,WithToolsFromAssembly
方法将扫描程序集,查找带有McpServerToolType
特性的类,并注册所有具有McpServerTool
特性的方法。请注意,McpServerTool
特性包含描述信息,这些信息将提供给任何连接到服务端的客户端。该描述有助于客户端判断应调用哪个工具。
在VS Code中配置并运行
通过这段极简的代码,我们的 MCP 服务端已经可以进行测试了!如果你还没有在 VS Code 中体验过 MCP 的支持功能,可以查看此处以获得详细引导。要在本地运行我们的项目,只需在 .vscode文件夹或用户设置中的mcp.json文件里添加一个新的服务端配置即可:
{
"inputs": [],
"servers": {
"MyFirstMCP": {
"type": "stdio",
"command": "dotnet",
"args": [
"run",
"--project",
"D:\\source\\MyFirstMCP\\MyFirstMCP\\MyFirstMCP.csproj"
]
}
}
}
当我们进入 GitHub Copilot 并开启 Agent 模式时,将会看到我们的新工具已成功配置:
打开 GitHub Copilot 的 Agent 模式后,我们现在可以要求它为我们反转消息。系统将提示我们确认是否允许调用该工具:
选择“继续”后,将运行该工具,并将消息传递给我们的 MCP 服务端执行:
与自有数据及API集成
当MCP服务端集成到现有API或服务中,以查询可供客户端使用的实际数据时,其强大之处便得以体现。目前已有越来越多的服务端可供客户端使用,其中一些我每天都在用,例如Git、GitHub、Playwright和文件系统。因此,让我们扩展我们的MCP服务端,以连接某个API,接收查询参数,并返回相应的数据。如果你一直关注我的话,就会知道我非常喜欢用猴子相关的例子做演示,而我觉得如果能随时使用一个“猴子MCP服务端”,将会非常有用。于是,我做的第一件事就是集成一个简单的服务,该服务可以查询我的猴子数据库,返回猴子列表或特定种类猴子的详细信息:
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace MyFirstMCP;
public class MonkeyService
{
private readonly HttpClient httpClient;
public MonkeyService(IHttpClientFactory httpClientFactory)
{
httpClient = httpClientFactory.CreateClient();
}
List<Monkey> monkeyList = new();
public async Task<List<Monkey>> GetMonkeys()
{
if (monkeyList?.Count > 0)
return monkeyList;
var response = await httpClient.GetAsync("https://www.montemagno.com/monkeys.json");
if (response.IsSuccessStatusCode)
{
monkeyList = await response.Content.ReadFromJsonAsync(MonkeyContext.Default.ListMonkey) ?? [];
}
monkeyList ??= [];
return monkeyList;
}
public async Task<Monkey?> GetMonkey(string name)
{
var monkeys = await GetMonkeys();
return monkeys.FirstOrDefault(m => m.Name?.Equals(name, StringComparison.OrdinalIgnoreCase) == true);
}
}
public partial class Monkey
{
public string? Name { get; set; }
public string? Location { get; set; }
public string? Details { get; set; }
public string? Image { get; set; }
public int Population { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
}
[JsonSerializable(typeof(List<Monkey>))]
internal sealed partial class MonkeyContext : JsonSerializerContext {
}
然后我可以将其注册到内置的 .NET 依赖注入服务中,以便后续使用:
builder.Services.AddHttpClient();
builder.Services.AddSingleton<MonkeyService>();
该服务可以调用您现有的API、查询数据库、处理数据,或执行任何其他操作。MCP服务端通常会配置为在启动时接收访问令牌或其他参数,以便您的代码能够获得调用服务所需的必要信息。在本示例中,我只是从我的示例猴子数据中读取信息。若要将这些功能作为工具提供出来,我只需定义新的McpServerToolType
特性,并设置几个调用此服务的McpServerTool
方法:
using System;
using System.ComponentModel;
using System.Text.Json;
using ModelContextProtocol.Server;
namespace MyFirstMCP;
[McpServerToolType]
public static class MonkeyTools
{
[McpServerTool, Description("Get a list of monkeys.")]
public static async Task<string> GetMonkeys(MonkeyService monkeyService)
{
var monkeys = await monkeyService.GetMonkeys();
return JsonSerializer.Serialize(monkeys);
}
[McpServerTool, Description("Get a monkey by name.")]
public static async Task<string> GetMonkey(MonkeyService monkeyService, [Description("The name of the monkey to get details for")] string name)
{
var monkey = await monkeyService.GetMonkey(name);
return JsonSerializer.Serialize(monkey);
}
}
在上面的代码中,我只是简单地将数据以JSON格式返回,但您也可以采用不同格式进行组织,以便大型语言模型(LLM)处理。
现在我们可以重启VS Code中的MCP服务端并开始测试!其强大之处在于将 GitHub Copilot 及其所调用的模型能力结合起来,对数据进行转换并应用于软件开发。例如,我可以要求它列出所有猴子,并以表格形式展示出来。
或者,我们可以查询某一种特定猴子的信息,并要求生成该数据类型的 Mermaid 图表:
更妙的是,如果正在构建.NET MAUI应用程序,我可以让Copilot根据正在探索的数据生成所需的XAML和代码:
发布MCP服务端
.NET 可轻松为任何应用程序创建容器镜像,只需在项目文件中添加必要的配置即可:
<PropertyGroup>
<EnableSdkContainerSupport>true</EnableSdkContainerSupport>
<ContainerRepository>jamesmontemagno/monkeymcp</ContainerRepository>
<ContainerFamily>alpine</ContainerFamily>
<RuntimeIdentifiers>linux-x64;linux-arm64</RuntimeIdentifiers>
</PropertyGroup>
在这里,我们将使用 Alpine 容器镜像系列来构建一个轻量小巧的镜像,并指定多个运行时标识符(runtime identifiers),以便无论用户使用的是 x64 还是基于 arm64 的设备,都能获得经过优化的镜像。.NET SDK 提供了通过运行dotnet publish /t:PublishContainer
命令来创建镜像的功能。由于我们指定了多个运行时标识符,因此本地将生成两个不同的镜像。如果我们要将这些镜像上传,可以通过命令行界面(CLI)直接完成,只需传入目标容器register(注册表)地址进行推送即可:
dotnet publish /t:PublishContainer -p ContainerRegistry=docker.io
现在,一个合并后的镜像将被推送到docker.io注册表,随后可在VS Code或其他支持MCP服务端的客户端中进行配置供后续使用。
{
"inputs": [],
"servers": {
"monkeymcp": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"jamesmontemagno/monkeymcp"
],
"env": {}
}
}
}
这样做的好处是,系统会根据用户所使用的机器类型自动拉取正确的镜像。欲了解更多信息,请参阅 .NET 应用容器化的相关文档。
服务端推送事件(SSE)SSE已过时弃用,请使用streaming http
SSE 传输通过 HTTP POST 请求实现客户端到服务端的通信,同时支持服务端到客户端的流式传输。借助 MCP C# SDK,在 MCP 服务端中实现 SSE 非常简单。该 SDK 支持配置服务端传输方式,以便高效地向连接的客户端传输流式数据。你可以在 GitHub 和 MCP C# SDK 示例中查看 SSE 版猴子 MCP 服务端(MonkeyMCP)的实现。你还可以更进一步,利用新增的 Azure Functions 支持来探索远程 MCP 服务端的用法。
拓展MCP应用场景
现在起,你可以持续为你的公司、社区和现有服务构建新的功能,开发出可在 GitHub Copilot 或其他客户端中使用的 MCP 服务端。MCP C# SDK 提供了丰富的示例,涵盖 MCP 服务端和客户端的创建,以及高级教程,充分展示了 MCP 的强大能力,以及使用 C# 构建这些功能的便捷性。
