用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 模式时,将会看到我们的新工具已成功配置:

image

打开 GitHub Copilot 的 Agent 模式后,我们现在可以要求它为我们反转消息。系统将提示我们确认是否允许调用该工具:

image

选择“继续”后,将运行该工具,并将消息传递给我们的 MCP 服务端执行:

image

与自有数据及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 及其所调用的模型能力结合起来,对数据进行转换并应用于软件开发。例如,我可以要求它列出所有猴子,并以表格形式展示出来。image

或者,我们可以查询某一种特定猴子的信息,并要求生成该数据类型的 Mermaid 图表:

image

更妙的是,如果正在构建.NET MAUI应用程序,我可以让Copilot根据正在探索的数据生成所需的XAML和代码:

image

发布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# 构建这些功能的便捷性。

posted @ 2025-09-11 18:45  菜鸟吊思  阅读(29)  评论(0)    收藏  举报