(一) Dotnet使用MCP的Csharp SDK

微软官方定义如下:

模型上下文协议(MCP)是一种开放协议,旨在标准化 AI 应用与外部工具和数据源之间的集成。 通过使用 MCP,开发人员可以增强 AI 模型的功能,使他们能够生成更准确、更相关和上下文感知的响应。

整体设计如下(来自微软官方):

Server

项目引入Microsoft.Extensions.HostingModelContextProtocol

前者用于构建主机环境,后者用于提供MCP SDK 相关函数。

xml 复制代码
<ItemGroup>
  <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-rc.1.25451.107" />
  <PackageReference Include="ModelContextProtocol" Version="0.4.0-preview.2" />
</ItemGroup>

stdio

通过命令行实现服务的创建,适用于同主机范围内,工具的直接调用。

引入服务MCP 服务并指定为stdio方式。

cs 复制代码
internal class Program
{
    static async Task Main(string[] args)
    {
        // 创建主机构建器实例
        var builder = Host.CreateApplicationBuilder(args);
        // 引入控制台日志输出
        builder.Logging.AddConsole(consoleLogOptions =>
        {
            // 配置所有日志输出到stderr
            consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
        });

        // 添加MCPServer服务
        builder.Services
        .AddMcpServer() // 注册Mcp服务
        .WithStdioServerTransport() // 添加stdio传输配置用于接收控制台输入输出流
        .WithToolsFromAssembly(); // 配置Mcp工具通过当前程序集获取
        // 运行宿主应用
        await builder.Build().RunAsync();
    }
}

httpsse

需要注意的是对于httpsse 实际都是使用的AspNetCore,对应需要引入NugetModelContextProtocol.AspNetCore

xml 复制代码
<ItemGroup>
  <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-rc.1.25451.107" />
  <PackageReference Include="ModelContextProtocol" Version="0.4.0-preview.2" />
  <PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.4.0-preview.2" />
</ItemGroup>

对应Host的构建器转换为WebApplication.CreateBuilder(args),而不是Host.CreateApplicationBuilder(args)

cs 复制代码
static async Task Main(string[] args)
{
    // 创建主机构建器实例
    var builder = WebApplication.CreateBuilder(args);
    // 引入控制台日志输出
    builder.Logging.AddConsole(consoleLogOptions =>
    {
        // 配置所有日志输出到stderr
        consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
    });

    // 添加MCPServer服务
    builder.Services
    .AddMcpServer() // 注册Mcp服务
    //.WithStdioServerTransport() // 添加stdio传输配置用于接收控制台输入输出流
    .WithHttpTransport() // 添加http传输配置用于接收http请求输入输出
    .WithToolsFromAssembly(); // 配置Mcp工具通过当前程序集获取
    // 运行宿主应用
    var app = builder.Build();
    app.MapMcp(); // streamable http(http://[ip]:[port]/) 与 sse (http://[ip]:[port]/sse)
    await app.RunAsync();
}

运行服务,本地访问地址为http://localhost:5000

bash 复制代码
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]

连接类型转换为SSE 或者 Streamable http,需要注意的是,选择SSE时,以当前服务为例,连接时,访问地址默认应该为http://localhost:5000/sse;选择Streamable HTTP时,访问地址为http://localhost:5000/。也可以按照自身需求进行调整。
SSE

Streamable HTTP

工具列表:

MCP服务调试

npx

可以通过npx 执行@modelcontextprotocol/inspector 以及程序运行指令用于调试Server 端。
node 版本为15.x 及其以上。否则会出现AbortController is not defined

查看版本。

bash 复制代码
node --version

切换目录到项目根目录,运行dotnet run --project <project_name>.csproj;实际生产环境直接执行应用程序集,与控制台执行命令行无差异。

bash 复制代码
>npx @modelcontextprotocol/inspector dotnet run --project <project_name>.csproj
Need to install the following packages:
@modelcontextprotocol/inspector@0.17.0
Ok to proceed? (y) y

或者。

bash 复制代码
>npx -y @modelcontextprotocol/inspector dotnet run --project <project_name>.csproj
Need to install the following packages:
@modelcontextprotocol/inspector@0.17.0

也可以直接运行npx -y @modelcontextprotocol/inspector,进入页面后再进行连接配置。

运行效果如下:

左侧点击连接,连接成功,右侧内容将显示出来,包括ToolsPing 等。

点击工具,获取工具列表,选择工具项,可对工具进行交互测试。

MCP服务资源

作为MCP 服务器,暴露给模型上下文的资源,可以是文本,二进制文件,数据库记录,API 响应,实时系统数据,截图和图像以及更多;每个资源都由唯一的 URI(统一资源标识符)标识。

bash 复制代码
[协议]://[主机]/[路径] 
[protocol]://[host]/[path]

使用[McpServerResourceType] 作为资源类型,[McpServerResource] 配置资源。

创建一个资源生成器ResourceGenerator,用于模拟资源,直接拷贝的官方案例代码。

cs 复制代码
static class ResourceGenerator
{
    private static readonly List<Resource> _resources = Enumerable.Range(1, 100).Select(i =>
    {
        var uri = $"test://template/resource/{i}";
        if (i % 2 != 0)
        {
            return new Resource
            {
                Uri = uri,
                Name = $"Resource {i}",
                MimeType = "text/plain",
                Description = $"Resource {i}: This is a plaintext resource"
            };
        }
        else
        {
            var buffer = System.Text.Encoding.UTF8.GetBytes($"Resource {i}: This is a base64 blob");
            return new Resource
            {
                Uri = uri,
                Name = $"Resource {i}",
                MimeType = "application/octet-stream",
                Description = Convert.ToBase64String(buffer)
            };
        }
    }).ToList();

    public static IReadOnlyList<Resource> Resources => _resources;
}

创建服务资源MyResources

cs 复制代码
/// <summary>
/// 自定义资源
/// </summary>
[McpServerResourceType]
internal class MyResources
{
    [McpServerResource(UriTemplate = "test://direct/text/resource", Name = "定向文本资源", MimeType = "text/plain")]
    //[Description("一个定向文本资源")]
    public static string DirectTextResource() => "这是一个定向文本资源";

    [McpServerResource(UriTemplate = "test://template/resource/{id}", Name = "通过id获取资源的模板资源")]
    //[Description("通过id获取资源的资源模板")]
    public static ResourceContents TemplateResource(RequestContext<ReadResourceRequestParams> requestContext, int id)
    {
        int index = id - 1;
        if ((uint)index >= ResourceGenerator.Resources.Count)
        {
            throw new NotSupportedException($"Unknown resource: {requestContext.Params?.Uri}");
        }

        var resource = ResourceGenerator.Resources[index];
        return resource.MimeType == "text/plain" ?
            new TextResourceContents
            {
                Text = resource.Description!,
                MimeType = resource.MimeType,
                Uri = resource.Uri,
            } :
            new BlobResourceContents
            {
                Blob = resource.Description!,
                MimeType = resource.MimeType,
                Uri = resource.Uri,
            };
    }
}

程序集注册(自动)

使用WithResourcesFromAssembly() 进行自动注册。

bash 复制代码
// 添加MCPServer服务
builder.Services
.AddMcpServer() // 注册Mcp服务
//.WithStdioServerTransport() // 添加stdio传输配置用于接收控制台输入输出流
.WithHttpTransport() // 添加http传输配置用于接收http请求输入输出
//---------------------------通过程序集加载资源------------------------
.WithResourcesFromAssembly()
//---------------------------通过程序集加载资源------------------------

手动配置

也可以通过WithResources进行手动注册资源类型。

cs 复制代码
// 添加MCPServer服务
builder.Services
.AddMcpServer() // 注册Mcp服务
//.WithStdioServerTransport() // 添加stdio传输配置用于接收控制台输入输出流
.WithHttpTransport() // 添加http传输配置用于接收http请求输入输出
//---------------------------通过程序集加载资源------------------------
//.WithResourcesFromAssembly()
//---------------------------通过程序集加载资源------------------------
//---------------------------通过手动配置工具------------------------
.WithTools<CommonTools>()
//---------------------------通过手动配置工具------------------------
//---------------------------手动配置提示词------------------------
.WithPrompts<MyPrompts>()
//---------------------------手动配置提示词------------------------
//---------------------------手动配置访问资源------------------------
.WithResources<MyResources>()
//---------------------------手动配置访问资源------------------------

运行效果如下:

定向资源。

模板资源。

服务提示词

使用[McpServerPromptType] 实现对服务端特定提示词类型的自定义声明,使用McpServerPrompt 用于定义对应的服务提示词。

cs 复制代码
/// <summary>
/// 服务端提示词
/// </summary>
[McpServerPromptType]
internal class MyPrompts
{
    [McpServerPrompt, Description("创建一个提示词获取天气模板提供给大模型.")]
    public static ChatMessage Weather([Description("需要获取天气的地点")] string address) =>
    new(ChatRole.User, $"获取:{address}当前天气");
}

程序集注册(自动)

Program的主函数中使用.WithPromptsFromAssembly() 实现程序集注册Prompt

cs 复制代码
// 添加MCPServer服务
builder.Services
.AddMcpServer() // 注册Mcp服务
.WithHttpTransport() // 添加http传输配置用于接收http请求输入输出
//---------------------------通过程序集加载提示词------------------------
//.WithToolsFromAssembly() // 配置Mcp工具通过当前程序集获取
//---------------------------通过程序集加载提示词------------------------
.WithPromptsFromAssembly()
;

手动配置

也可以使用WithPrompts实现提示词类型手动配置。

cs 复制代码
// 添加MCPServer服务
builder.Services
.AddMcpServer() // 注册Mcp服务
//.WithStdioServerTransport() // 添加stdio传输配置用于接收控制台输入输出流
.WithHttpTransport() // 添加http传输配置用于接收http请求输入输出
//---------------------------通过程序集加载工具------------------------
//.WithToolsFromAssembly() // 配置Mcp工具通过当前程序集获取
//---------------------------通过程序集加载工具------------------------
//---------------------------通过程序集加载提示词------------------------
//.WithPromptsFromAssembly()
//---------------------------通过程序集加载提示词------------------------
//---------------------------通过手动配置工具------------------------
.WithTools<CommonTools>()
//---------------------------通过手动配置工具------------------------
//---------------------------手动配置提示词------------------------
.WithPrompts<MyPrompts>()
//---------------------------手动配置提示词------------------------

调测效果如下:

工具编写与注册

创建工具类,需要注意的是类顶部需要添加特性[McpServerToolType],工具函数添加[McpServerTool],工具函数描述尽可能描述清晰,.WithToolsFromAssembly() 简化了工具的注册和解析过程。

cs 复制代码
/// <summary>
/// 通用命令工具
/// </summary>
[McpServerToolType]
internal class CommonTools
{
    /// <summary>
    /// Echo 输出工具
    /// </summary>
    /// <param name="message">客户端发送信息</param>
    /// <returns></returns>
    [McpServerTool, Description("Echo 输出工具,将客户端发送进行返回输出")]
    public static string Echo(string message) => $"你好 {message}";
    /// <summary>
    /// 获取当前天气
    /// </summary>
    /// <returns></returns>
    [McpServerTool, Description("获取当前天气,通过调用为客户端返回当前天气")]
    internal static string GetCurrentWeather() => Random.Shared.NextDouble() > 0.5 ? "It's sunny" : "It's raining";
}

程序集注册(自动)

cs 复制代码
        // 添加MCPServer服务
        builder.Services
        .AddMcpServer() // 注册Mcp服务
        .WithStdioServerTransport()
        .WithToolsFromAssembly(); // 配置Mcp工具通过当前程序集获取
        // 运行宿主应用
        await builder.Build().RunAsync();

项目运行如下:

bash 复制代码
info: ModelContextProtocol.Server.StdioServerTransport[857250842]
      Server (stream) (EchoMcpServer) transport reading messages.
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]

手动配置

手动配置通过WithTools<>() 配置[McpServerToolType] 实现类。

cs 复制代码
static async Task Main(string[] args)
{
	// 省略不变部分
    // 添加MCPServer服务
    builder.Services
    .AddMcpServer() // 注册Mcp服务
    //.WithStdioServerTransport() // 添加stdio传输配置用于接收控制台输入输出流
    .WithHttpTransport() // 添加http传输配置用于接收http请求输入输出
    //---------------------------通过程序集加载工具------------------------
    //.WithToolsFromAssembly() // 配置Mcp工具通过当前程序集获取
    //---------------------------通过程序集加载工具------------------------
    //---------------------------通过手动配置工具------------------------
    .WithTools<CommonTools>()
    //---------------------------通过手动配置工具------------------------
    ;
    // 运行宿主应用
    var app = builder.Build();
    app.MapMcp(); // streamable http(http://[ip]:[port]/) 与 sse (http://[ip]:[port]/sse)
    await app.RunAsync();
}

依赖注入与请求参数

工具函数为静态函数,官方库也支持通过函数方式实现服务的依赖注入,前提条件为服务已经注册;请求参数通过json/form方式进行接收,对应描述使用[Description],希望后续能支持注释。
Program 中注册服务。

cs 复制代码
internal class Program
{
    static async Task Main(string[] args)
    {
        // 创建主机构建器实例
        var builder = WebApplication.CreateBuilder(args);

        // 注册HttpClient服务(用于依赖注入)
        builder.Services.AddHttpClient();

        // 添加MCPServer服务
        builder.Services
        .AddMcpServer(); // 注册Mcp服务
        // 省略不变内容
    }
}

请求实体类RequestBody属性如下:

cs 复制代码
/// <summary>
/// 请求体
/// </summary>
[Description("请求体")]
internal class RequestBody
{
    /// <summary>
    /// 用户Id
    /// </summary>
    [Required]
    [Description("用户Id,不允许值为0")]
    public int UserId { get; set; }
    /// <summary>
    /// 用户名
    /// </summary>
    [MaxLength(10)]
    [Description("用户名称")]
    public string UserName { get; set; }
}

工具函数中进行依赖注入。

cs 复制代码
[McpServerTool, Description("获取客户端信息,通过调用该工具返回当前服务与客户端信息")]
public static string GetClientInfo(McpServer mcpServer,
    HttpClient httpClient,
    [Description("这是请求下载对应请求参数")]
    RequestBody requestBody,
    [Description("这是一个用于动态下载的请求连接")]
    string url) {
    return $"{mcpServer.SessionId}:{url}";
}

使用测试工具连接后,效果如下:

相关推荐
迎風吹頭髮4 小时前
Linux服务器编程实践26-TCP连接超时重连机制:超时时间计算与重连策略
服务器·网络·php
wanhengidc4 小时前
什么是站群服务器
运维·服务器·网络·游戏·智能手机
deng-c-f4 小时前
Linux C/C++ 学习日记(24):UDP协议的介绍:广播、多播的实现
linux·网络·学习·udp
卓码软件测评5 小时前
第三方软件质量检测:RTSP协议和HLS协议哪个更好用来做视频站?
网络·网络协议·http·音视频·web
琦琦琦baby5 小时前
RIP路由协议总结
网络·rip
琦琦琦baby5 小时前
VRRP技术重点总结
运维·网络·智能路由器·vrrp
星瞰物联7 小时前
RDSS 与 RNSS 定位技术深度解析(二)——系统架构、性能指标
网络·系统架构
小钻风33667 小时前
HTTPS是如何确保安全的
网络·数据库
duration~7 小时前
UDP 首部
网络·网络协议·udp