ASP.NET Core WebApi 集成(cheng) MCP 協議完全指(zhi)南
前言
Model Context Protocol (MCP) 是一個(ge)標準化協(xie)議,讓 AI 客(ke)戶(hu)端(如 Claude、ChatGPT 等(deng))能夠(gou)通過統一的接(jie)口調用你(ni)的 API。本(ben)文將詳細介紹如何在 ASP.NET Core WebApi 項目中集成(cheng) MCP 支(zhi)持,實現 AI 與你(ni)的服務無縫對接(jie)。
什么是 MCP?
MCP(Model Context Protocol)是一個開放協議,旨在標準(zhun)化(hua) AI 應(ying)用與外部工具、數據源之(zhi)間的通(tong)信(xin)方式。通(tong)過 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 中添(tian)加(jia) 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
應用啟動后,可(ke)以(yi)訪問:
- Swagger UI:
//localhost:5000/swagger - WebApi:
//localhost:5000/weatherforecast - MCP 端點:
//localhost:5000/mcp
傳輸模式詳解
HTTP 模式
適用于 Web 應用、Claude Desktop、遠程訪(fang)問等(deng)場景。
測試示例:
# 列出所有工具
curl -X POST //localhost:5000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# 調用工具
curl -X POST //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": "//localhost:5000/mcp",
"headers": {
"Authorization": "Bearer your-secret-token-here"
}
}
}
}
Stdio 模式
適用于 Kiro IDE、本地命令行工具等(deng)場景(jing),無需網絡端口。
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("//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 = '//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 更準確(que)地使(shi)用你的工具(ju)是成功的關(guan)鍵(jian)。以下是經(jing)過實踐(jian)驗證的最(zui)佳實踐(jian)。
核心原則
AI 通(tong)過以下信息(xi)決定是否使用你的工具(ju):
- 工具名稱 - 清晰、描述性
- 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(最重要!)
這(zhe)是(shi)最關鍵的(de)部分!AI 主要通(tong)過 Description 判斷是(shi)否使用工具。
// ? 不好 - 太簡短
[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. 優化檢查清單
在(zai)發布工(gong)具前,檢查以下項目:
高級特性
依賴注入支持
工具方法可以(yi)注入(ru)服務:
[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("//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 規范
- 更易維護:無需維護自定義代碼
總結
通過本文,我們(men)學習(xi)了如何(he)在 ASP.NET Core WebApi 中(zhong)集成 MCP 協議支持。使用(yong)官方 SDK,只需幾行代碼就能(neng)讓你的(de) API 被 AI 客戶端調用(yong)。MCP 協議的(de)標準化特性(xing),讓 AI 應用(yong)與后端服務的(de)集成變得前所未有(you)的(de)簡(jian)單。
參考資源
源碼地址
完整(zheng)示(shi)例(li)代(dai)碼(ma)請訪問:[GitHub 倉庫(ku)地址]
如果本文對你有幫助(zhu),歡迎點贊、收藏、關(guan)注!有任何問題歡迎在評(ping)論區(qu)討論。
