前言
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 } }
第五步:运行和测试
dotnet run
应用启动后,可以访问:
- 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%Claudeclaude_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 客户端
<script setup> import { ref } from 'vue'; const weather = ref(''); const MCP_URL = 'http://localhost:5000/mcp'; const TOKEN = 'your-secret-token-here'; const callMcp = async (method, params = {}) => { const response = await fetch(MCP_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${TOKEN}`, }, body: JSON.stringify({ jsonrpc: '2.0', id: Date.now(), method, params, }), }); return response.json(); }; const getWeather = async () => { const data = await callMcp('tools/call', { name: 'GetWeatherForecast', arguments: {}, }); weather.value = data.result.content[0].text; }; </script>
MCP Tools 最佳实践
让 AI 更准确地使用你的工具是成功的关键。以下是经过实践验证的最佳实践。
核心原则
AI 通过以下信息决定是否使用你的工具:
- 工具名称 - 清晰、描述性
- Description - 详细的功能说明
- 参数描述 - 明确的参数用途
- 使用场景 - 何时应该使用这个工具
1. 使用清晰的命名
// ❌ 不好 - 名称模糊 [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. 返回格式化、易读的结果
// ❌ 不好 - 返回原始对象 public static WeatherForecast GetWeather(string city) { return new WeatherForecast { ... }; } // ✅ 好 - 返回格式化的文本 public static string GetWeather(string city) { var weather = GetWeatherData(city); return $@"🌍 Current Weather in {city} 📅 Date: {weather.Date:yyyy-MM-dd} 🌡️ Temperature: {weather.TemperatureC}°C ({weather.TemperatureF}°F) ☁️ Conditions: {weather.Summary} ⏰ Updated: {DateTime.Now:HH:mm:ss}"; }
5. 完整示例:查询工具
[McpServerTool] [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 - Weather conditions for travel planning Examples of user queries: - 'What's the weather forecast for the next 5 days?' - 'Will it rain this week?' - 'What's the temperature trend?' - 'Should I bring a jacket tomorrow?' - '未来几天天气怎么样?' - '这周会下雨吗?'")] public static string GetWeatherForecast( [Description("Number of days to forecast (1-7 days). Default is 5 days if not specified.")] int days = 5) { var forecasts = GenerateForecasts(days); var result = new StringBuilder(); result.AppendLine($"🌤️ Weather Forecast for Next {days} Days"); result.AppendLine(); foreach (var forecast in forecasts) { result.AppendLine($"📅 {forecast.Date:yyyy-MM-dd (ddd)}"); result.AppendLine($" 🌡️ Temperature: {forecast.TemperatureC}°C ({forecast.TemperatureF}°F)"); result.AppendLine($" ☁️ Conditions: {forecast.Summary}"); result.AppendLine(); } return result.ToString(); }
6. 完整示例:比较工具
[McpServerTool] [Description(@"Compare weather conditions between two cities to help with travel decisions or general comparison. Use this tool when users want to: - Compare weather between cities - Decide which city has better weather - Plan travel between cities - Compare temperatures - Choose destination based on weather Examples of user queries: - 'Compare weather between Beijing and Shanghai' - 'Which city is warmer, Tokyo or Seoul?' - 'Weather difference between New York and London' - '北京和上海哪个城市天气更好?' - '东京和首尔哪里更暖和?'")] public static string CompareWeatherBetweenCities( [Description("First city name (English or Chinese)")] string city1, [Description("Second city name (English or Chinese)")] string city2) { var weather1 = GetWeatherData(city1); var weather2 = GetWeatherData(city2); return $@"🌍 Weather Comparison 📍 {city1}: 🌡️ Temperature: {weather1.TemperatureC}°C ☁️ Conditions: {weather1.Summary} 📍 {city2}: 🌡️ Temperature: {weather2.TemperatureC}°C ☁️ Conditions: {weather2.Summary} 📊 Difference: {Math.Abs(weather1.TemperatureC - weather2.TemperatureC)}°C {(weather1.TemperatureC > weather2.TemperatureC ? $"🔥 {city1} is warmer" : $"🔥 {city2} is warmer")}"; }
7. Description 模板
基础模板:
[Description(@"[简短功能说明] Use this tool when users ask about: - [使用场景1] - [使用场景2] - [使用场景3] Examples of user queries: - '[示例问题1]' - '[示例问题2]' - '[示例问题3]'")]
完整模板:
[Description(@"[详细功能说明,包括返回的数据类型和格式] Use this tool when users want to: - [使用场景1] - [使用场景2] - [使用场景3] Supports: - [支持的功能1] - [支持的功能2] Examples of user queries: - '[英文示例1]' - '[英文示例2]' - '[中文示例1]' - '[中文示例2]' Note: [特殊说明或限制]")]
8. 优化检查清单
在发布工具前,检查以下项目:
高级特性
依赖注入支持
工具方法可以注入服务:
[McpServerTool] [Description("Get weather with logging")] public static string GetWeatherWithLogging( ILogger<WeatherTools> logger, IWeatherService weatherService, string city) { logger.LogInformation("Getting weather for {City}", city); return weatherService.GetWeather(city); }
添加 Prompts
[McpServerPromptType] public static class WeatherPrompts { [McpServerPrompt] [Description("Creates a prompt to help plan outdoor activities based on weather")] public static ChatMessage PlanOutdoorActivity( [Description("The city name")] string city, [Description("The activity type")] string activity) { return new ChatMessage( ChatRole.User, $@"I want to plan a {activity} activity in {city}. Please check the weather forecast and suggest the best day and time. Consider temperature, conditions, and provide detailed recommendations." ); } }
复杂参数类型
SDK 自动支持:
- 基本类型:
string,int,bool,double等 - 复杂对象:自动序列化/反序列化
- 可选参数:使用默认值
- 数组和集合
故障排除
工具未被发现
检查项:
- 类是否有
[McpServerToolType]特性 - 方法是否有
[McpServerTool]特性 - 类是否是静态的
- 是否重启了应用
认证失败
检查项:
- Token 是否正确
appsettings.json中Enabled设置- Authorization header 格式
- 环境配置(Development vs Production)
CORS 问题
在 Program.cs 中添加 CORS 支持:
builder.Services.AddCors(options => { options.AddPolicy("AllowMcpClients", policy => { policy.WithOrigins("http://localhost:3000") .AllowAnyHeader() .AllowAnyMethod(); }); }); app.UseCors("AllowMcpClients");
项目结构
NetCoreApiMcpDemo/ ├── Controllers/ │ └── WeatherForecastController.cs # 标准 WebApi 控制器 ├── Tools/ │ └── WeatherTools.cs # MCP 工具定义 ├── Middleware/ │ └── McpAuthenticationMiddleware.cs # 认证中间件 ├── Program.cs # 应用配置 ├── appsettings.json # 配置文件 └── appsettings.Development.json # 开发配置
为什么选择官方 SDK?
- 代码更少:无需自定义特性和提供者
- 更可靠:官方维护和更新
- 更强大:自动 Schema、DI 支持
- 更标准:完全符合 MCP 规范
- 更易维护:无需维护自定义代码
总结
通过本文,我们学习了如何在 ASP.NET Core WebApi 中集成 MCP 协议支持。使用官方 SDK,只需几行代码就能让你的 API 被 AI 客户端调用。MCP 协议的标准化特性,让 AI 应用与后端服务的集成变得前所未有的简单。
参考资源
源码地址
完整示例代码请访问:[GitHub 仓库地址]
如果本文对你有帮助,欢迎点赞、收藏、关注!有任何问题欢迎在评论区讨论。