前言
Model Context Protocol (MCP) 是一个标准化协议,让 AI 客户端(如 Claude、ChatGPT 等)能够通过统一的接口调用你的 API。本文将详细介绍如何在 ASP.NET Core WebApi 项目中集成 MCP 支持,实现 AI 与你的服务无缝对接。
什么是 MCP?
MCP(Model Context Protocol)是一个开放协议,旨在标准化 AI 应用与外部工具、数据源之间的通信方式。通过 MCP,你的 API 可以:
- 被 AI 助手自动发现和调用
- 提供标准化的工具描述和参数定义
- 支持多种传输模式(HTTP、Stdio)
- 实现安全的认证和授权
核心特性
本项目实现了以下功能:
- ✅ 使用官方 ModelContextProtocol.AspNetCore SDK
- ✅ 通过 [McpServerTool] 特性快速定义工具
- ✅ 自动参数绑定和 JSON Schema 生成
- ✅ 支持 HTTP 和 Stdio 双传输模式
- ✅ 基于 Token 的认证和授权
- ✅ 与现有 WebApi 完美共存
快速开始
第一步:安装 NuGet 包
- dotnet add package ModelContextProtocol.AspNetCore --version 0.4.0-preview.3
复制代码 第二步:配置 MCP 服务
在 Program.cs 中添加 MCP 配置:- using ModelContextProtocol.Server;
- var builder = WebApplication.CreateBuilder(args);
- builder.Services.AddControllers();
- // 添加 MCP 服务器(支持 HTTP 和 Stdio 双模式)
- builder.Services
- .AddMcpServer(options =>
- {
- options.ServerInfo = new ModelContextProtocol.Protocol.Implementation
- {
- Name = "Weather API",
- Version = "1.0.0"
- };
- })
- .WithHttpTransport() // HTTP 模式:用于 Web 客户端
- .WithStdioServerTransport() // Stdio 模式:用于 Kiro IDE 等本地工具
- .WithToolsFromAssembly();
- var app = builder.Build();
- // 添加认证中间件(可选)
- app.UseMiddleware<McpAuthenticationMiddleware>();
- app.UseAuthorization();
- app.MapControllers();
- // 映射 MCP 端点
- app.MapMcp("/mcp");
- app.Run();
复制代码 第三步:定义 MCP 工具
创建 Tools/WeatherTools.cs:- using System.ComponentModel;
- using ModelContextProtocol.Server;
- [McpServerToolType]
- public static class WeatherTools
- {
- [McpServerTool]
- [Description("Get weather forecast for the next 5 days")]
- public static IEnumerable<WeatherForecast> GetWeatherForecast()
- {
- var rng = new Random();
- return Enumerable.Range(1, 5).Select(index => new WeatherForecast
- {
- Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
- TemperatureC = rng.Next(-20, 55),
- Summary = Summaries[rng.Next(Summaries.Length)]
- }).ToArray();
- }
- [McpServerTool]
- [Description("Get current weather for a specific city")]
- public static WeatherForecast GetWeatherByCity(
- [Description("The name of the city")] string city)
- {
- var rng = new Random();
- return new WeatherForecast
- {
- Date = DateOnly.FromDateTime(DateTime.Now),
- TemperatureC = rng.Next(-20, 55),
- Summary = $"Weather in {city}: {Summaries[rng.Next(Summaries.Length)]}"
- };
- }
- private static readonly string[] Summaries = new[]
- {
- "Freezing", "Bracing", "Chilly", "Cool", "Mild",
- "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
- };
- }
复制代码 第四步:配置认证(可选)
在 appsettings.json 中配置:- {
- "McpAuth": {
- "Enabled": true,
- "ValidTokens": ["your-secret-token-here"]
- }
- }
复制代码 开发环境可以禁用认证(appsettings.Development.json):- {
- "McpAuth": {
- "Enabled": false
- }
- }
复制代码 第五步:运行和测试
应用启动后,可以访问:
- Swagger UI: http://localhost:5000/swagger
- WebApi: http://localhost:5000/weatherforecast
- MCP 端点: http://localhost:5000/mcp
传输模式详解
HTTP 模式
适用于 Web 应用、Claude Desktop、远程访问等场景。
测试示例:- # 列出所有工具
- curl -X POST http://localhost:5000/mcp \
- -H "Content-Type: application/json" \
- -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
- # 调用工具
- curl -X POST http://localhost:5000/mcp \
- -H "Authorization: Bearer your-secret-token-here" \
- -H "Content-Type: application/json" \
- -d '{
- "jsonrpc":"2.0",
- "id":2,
- "method":"tools/call",
- "params":{
- "name":"GetWeatherForecast",
- "arguments":{}
- }
- }'
复制代码 Claude Desktop 配置:
编辑配置文件(Windows: %APPDATA%\Claude\claude_desktop_config.json):- {
- "mcpServers": {
- "weather-api": {
- "url": "http://localhost:5000/mcp",
- "headers": {
- "Authorization": "Bearer your-secret-token-here"
- }
- }
- }
- }
复制代码 Stdio 模式
适用于 Kiro IDE、本地命令行工具等场景,无需网络端口。
Kiro IDE 配置:
编辑 .kiro/settings/mcp.json:- {
- "mcpServers": {
- "weather-api": {
- "command": "dotnet",
- "args": ["run", "--project", "path/to/NetCoreApiMcpDemo.csproj"],
- "env": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- }
- }
- }
- }
复制代码 模式对比
特性HTTP 模式Stdio 模式传输方式HTTP POST标准输入/输出适用场景Web 应用、远程访问本地工具、IDE 集成认证HTTP Header环境变量/配置网络需要网络端口无需网络性能网络开销进程间通信,更快认证和授权
实现认证中间件
创建 Middleware/McpAuthenticationMiddleware.cs:- public class McpAuthenticationMiddleware
- {
- private readonly RequestDelegate _next;
- private readonly IConfiguration _configuration;
- private readonly ILogger<McpAuthenticationMiddleware> _logger;
- public McpAuthenticationMiddleware(
- RequestDelegate next,
- IConfiguration configuration,
- ILogger<McpAuthenticationMiddleware> logger)
- {
- _next = next;
- _configuration = configuration;
- _logger = logger;
- }
- public async Task InvokeAsync(HttpContext context)
- {
- // 只对 MCP 端点进行认证
- if (!context.Request.Path.StartsWithSegments("/mcp"))
- {
- await _next(context);
- return;
- }
- // 检查是否启用认证
- var authEnabled = _configuration.GetValue<bool>("McpAuth:Enabled");
- if (!authEnabled)
- {
- await _next(context);
- return;
- }
- // 验证 Token
- var authHeader = context.Request.Headers["Authorization"].FirstOrDefault();
- if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer "))
- {
- context.Response.StatusCode = 401;
- await context.Response.WriteAsJsonAsync(new { error = "Unauthorized" });
- return;
- }
- var token = authHeader.Substring("Bearer ".Length).Trim();
- var validTokens = _configuration.GetSection("McpAuth:ValidTokens").Get<string[]>();
- if (validTokens == null || !validTokens.Contains(token))
- {
- context.Response.StatusCode = 401;
- await context.Response.WriteAsJsonAsync(new { error = "Invalid token" });
- return;
- }
- await _next(context);
- }
- }
复制代码 安全最佳实践
- 使用强 Token:至少 32 字符的随机字符串
- 定期轮换:定期更换 Token
- 使用 HTTPS:生产环境必须使用 HTTPS
- 环境隔离:开发和生产使用不同的 Token
- 日志安全:不要在日志中记录完整 Token
客户端集成示例
C# 客户端
- using ModelContextProtocol;
- using ModelContextProtocol.Client;
- var transport = new HttpClientTransport(new HttpClientTransportOptions
- {
- BaseUrl = new Uri("http://localhost:5000/mcp"),
- Headers = new Dictionary<string, string>
- {
- ["Authorization"] = "Bearer your-secret-token-here"
- }
- });
- var client = await McpClient.CreateAsync(transport);
- await client.InitializeAsync(new InitializeParams
- {
- ProtocolVersion = "2025-06-18",
- ClientInfo = new Implementation
- {
- Name = "MyApp",
- Version = "1.0.0"
- }
- });
- // 列出工具
- var tools = await client.ListToolsAsync();
- // 调用工具
- var result = await client.CallToolAsync(
- "GetWeatherForecast",
- new Dictionary<string, object?>()
- );
复制代码 JavaScript/Vue 客户端
- [/code][size=5]MCP Tools 最佳实践[/size]
- 让 AI 更准确地使用你的工具是成功的关键。以下是经过实践验证的最佳实践。
- [size=4]核心原则[/size]
- AI 通过以下信息决定是否使用你的工具:
- [list=1]
- [*][b]工具名称[/b] - 清晰、描述性
- [*][b]Description[/b] - 详细的功能说明
- [*][b]参数描述[/b] - 明确的参数用途
- [*][b]使用场景[/b] - 何时应该使用这个工具
- [/list][size=4]1. 使用清晰的命名[/size]
- [code]// ❌ 不好 - 名称模糊
- [McpServerTool]
- public static string Get() { }
- // ✅ 好 - 动词开头,描述清晰
- [McpServerTool]
- public static string GetWeatherForecast() { }
- // ✅ 更好 - 包含具体信息
- [McpServerTool]
- public static string GetWeatherForecastForNextDays() { }
复制代码 命名建议:
- 使用动词开头:Get, Search, Calculate, Compare, Analyze
- 包含操作对象:Weather, Temperature, Forecast
- 避免缩写和简称
- 使用 PascalCase
2. 编写详细的 Description(最重要!)
这是最关键的部分!AI 主要通过 Description 判断是否使用工具。- // ❌ 不好 - 太简短
- [Description("Get weather")]
- // ⚠️ 一般 - 有基本信息但不够
- [Description("Get weather forecast for the next 5 days")]
- // ✅ 好 - 包含详细信息和使用场景
- [Description(@"Get detailed weather forecast for the next several days including temperature, weather conditions, and trends.
- Use this tool when users ask about:
- - Future weather (tomorrow, next week, upcoming days)
- - Weather predictions or forecasts
- - Planning activities based on weather
- - Temperature trends
- Examples of user queries:
- - 'What's the weather forecast for the next 5 days?'
- - 'Will it rain this week?'
- - 'What's the temperature trend?'")]
复制代码 Description 应该包含:
- 功能说明 - 工具做什么
- 使用场景 - 何时使用("Use this tool when...")
- 示例查询 - 用户可能的提问方式
- 支持的功能 - 特殊能力或限制
3. 详细的参数描述
- [McpServerTool]
- public static string GetWeatherByCity(
- // ❌ 不好
- [Description("city")] string city,
- // ✅ 好
- [Description("The name of the city in English or Chinese (e.g., 'Beijing', '北京', 'Shanghai', 'New York')")]
- string city,
- // ✅ 更好 - 包含默认值说明
- [Description("Number of days to forecast (1-7 days). Default is 5 days if not specified.")]
- int days = 5
- )
复制代码 参数描述应该包含:
- 参数的用途
- 支持的格式或值范围
- 示例值
- 默认值(如果有)
4. 返回格式化、易读的结果
[code]// ❌ 不好 - 返回原始对象public static WeatherForecast GetWeather(string city){ return new WeatherForecast { ... };}// ✅ 好 - 返回格式化的文本public static string GetWeather(string city){ var weather = GetWeatherData(city); return $@"
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |