(一) Dotnet使用MCP的Csharp SDK
微软官方定义如下:
模型上下文协议(MCP)是一种开放协议,旨在标准化 AI 应用与外部工具和数据源之间的集成。 通过使用 MCP,开发人员可以增强 AI 模型的功能,使他们能够生成更准确、更相关和上下文感知的响应。
整体设计如下(来自微软官方):
Server
项目引入Microsoft.Extensions.Hosting
和 ModelContextProtocol
。
前者用于构建主机环境
,后者用于提供MCP SDK
相关函数。
<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
方式。
internal class Program
{static async Task Main(string[] args){// 创建主机构建器实例var builder = Host.CreateApplicationBuilder(args);// 引入控制台日志输出builder.Logging.AddConsole(consoleLogOptions =>{// 配置所有日志输出到stderrconsoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;});// 添加MCPServer服务builder.Services.AddMcpServer() // 注册Mcp服务.WithStdioServerTransport() // 添加stdio传输配置用于接收控制台输入输出流.WithToolsFromAssembly(); // 配置Mcp工具通过当前程序集获取// 运行宿主应用await builder.Build().RunAsync();}
}
http
与sse
需要注意的是对于http
与sse
实际都是使用的AspNetCore
,对应需要引入Nuget
包ModelContextProtocol.AspNetCore
。
<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)
。
static async Task Main(string[] args)
{// 创建主机构建器实例var builder = WebApplication.CreateBuilder(args);// 引入控制台日志输出builder.Logging.AddConsole(consoleLogOptions =>{// 配置所有日志输出到stderrconsoleLogOptions.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
。
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
。
查看版本。
node --version
切换目录到项目根目录,运行dotnet run --project <project_name>.csproj
;实际生产环境直接执行应用程序集,与控制台执行命令行无差异。
>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
或者。
>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
,进入页面后再进行连接配置。
运行效果如下:
左侧点击连接,连接成功,右侧内容将显示出来,包括Tools
、Ping
等。
点击工具,获取工具列表,选择工具项,可对工具进行交互测试。
MCP
服务资源
作为MCP
服务器,暴露给模型上下文的资源,可以是文本,二进制文件,数据库记录,API 响应,实时系统数据,截图和图像以及更多;每个资源都由唯一的 URI(统一资源标识符)标识。
[协议]://[主机]/[路径]
[protocol]://[host]/[path]
使用[McpServerResourceType]
作为资源类型,[McpServerResource]
配置资源。
创建一个资源生成器ResourceGenerator
,用于模拟资源,直接拷贝的官方案例代码。
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
。
/// <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()
进行自动注册。
// 添加MCPServer服务
builder.Services
.AddMcpServer() // 注册Mcp服务
//.WithStdioServerTransport() // 添加stdio传输配置用于接收控制台输入输出流
.WithHttpTransport() // 添加http传输配置用于接收http请求输入输出
//---------------------------通过程序集加载资源------------------------
.WithResourcesFromAssembly()
//---------------------------通过程序集加载资源------------------------
手动配置
也可以通过WithResources
进行手动注册资源类型。
// 添加MCPServer服务
builder.Services
.AddMcpServer() // 注册Mcp服务
//.WithStdioServerTransport() // 添加stdio传输配置用于接收控制台输入输出流
.WithHttpTransport() // 添加http传输配置用于接收http请求输入输出
//---------------------------通过程序集加载资源------------------------
//.WithResourcesFromAssembly()
//---------------------------通过程序集加载资源------------------------
//---------------------------通过手动配置工具------------------------
.WithTools<CommonTools>()
//---------------------------通过手动配置工具------------------------
//---------------------------手动配置提示词------------------------
.WithPrompts<MyPrompts>()
//---------------------------手动配置提示词------------------------
//---------------------------手动配置访问资源------------------------
.WithResources<MyResources>()
//---------------------------手动配置访问资源------------------------
运行效果如下:
定向资源。
模板资源。
服务提示词
使用[McpServerPromptType]
实现对服务端特定提示词类型的自定义声明,使用McpServerPrompt
用于定义对应的服务提示词。
/// <summary>
/// 服务端提示词
/// </summary>
[McpServerPromptType]
internal class MyPrompts
{[McpServerPrompt, Description("创建一个提示词获取天气模板提供给大模型.")]public static ChatMessage Weather([Description("需要获取天气的地点")] string address) =>new(ChatRole.User, $"获取:{address}当前天气");
}
程序集注册(自动)
在Program
的主函数中使用.WithPromptsFromAssembly()
实现程序集注册Prompt
。
// 添加MCPServer服务
builder.Services
.AddMcpServer() // 注册Mcp服务
.WithHttpTransport() // 添加http传输配置用于接收http请求输入输出
//---------------------------通过程序集加载提示词------------------------
//.WithToolsFromAssembly() // 配置Mcp工具通过当前程序集获取
//---------------------------通过程序集加载提示词------------------------
.WithPromptsFromAssembly()
;
手动配置
也可以使用WithPrompts
实现提示词类型手动配置。
// 添加MCPServer服务
builder.Services
.AddMcpServer() // 注册Mcp服务
//.WithStdioServerTransport() // 添加stdio传输配置用于接收控制台输入输出流
.WithHttpTransport() // 添加http传输配置用于接收http请求输入输出
//---------------------------通过程序集加载工具------------------------
//.WithToolsFromAssembly() // 配置Mcp工具通过当前程序集获取
//---------------------------通过程序集加载工具------------------------
//---------------------------通过程序集加载提示词------------------------
//.WithPromptsFromAssembly()
//---------------------------通过程序集加载提示词------------------------
//---------------------------通过手动配置工具------------------------
.WithTools<CommonTools>()
//---------------------------通过手动配置工具------------------------
//---------------------------手动配置提示词------------------------
.WithPrompts<MyPrompts>()
//---------------------------手动配置提示词------------------------
调测效果如下:
工具编写与注册
创建工具类,需要注意的是类顶部需要添加特性[McpServerToolType]
,工具函数添加[McpServerTool]
,工具函数描述尽可能描述清晰,.WithToolsFromAssembly()
简化了工具的注册和解析过程。
/// <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";
}
程序集注册(自动)
// 添加MCPServer服务builder.Services.AddMcpServer() // 注册Mcp服务.WithStdioServerTransport().WithToolsFromAssembly(); // 配置Mcp工具通过当前程序集获取// 运行宿主应用await builder.Build().RunAsync();
项目运行如下:
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]
实现类。
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
中注册服务。
internal class Program
{static async Task Main(string[] args){// 创建主机构建器实例var builder = WebApplication.CreateBuilder(args);// 注册HttpClient服务(用于依赖注入)builder.Services.AddHttpClient();// 添加MCPServer服务builder.Services.AddMcpServer(); // 注册Mcp服务// 省略不变内容}
}
请求实体类RequestBody
属性如下:
/// <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; }
}
工具函数中进行依赖注入。
[McpServerTool, Description("获取客户端信息,通过调用该工具返回当前服务与客户端信息")]
public static string GetClientInfo(McpServer mcpServer,HttpClient httpClient,[Description("这是请求下载对应请求参数")]RequestBody requestBody,[Description("这是一个用于动态下载的请求连接")]string url) {return $"{mcpServer.SessionId}:{url}";
}
使用测试工具连接后,效果如下: