動態

詳情 返回 返回

ASP.NET Core WebApi 集成 MCP 協議完全指南 - 動態 詳情

前言

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%\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);
    }
}

安全最佳實踐

  1. 使用強 Token:至少 32 字符的隨機字符串
  2. 定期輪換:定期更換 Token
  3. 使用 HTTPS:生產環境必須使用 HTTPS
  4. 環境隔離:開發和生產使用不同的 Token
  5. 日誌安全:不要在日誌中記錄完整 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 通過以下信息決定是否使用你的工具:

  1. 工具名稱 - 清晰、描述性
  2. Description - 詳細的功能説明
  3. 參數描述 - 明確的參數用途
  4. 使用場景 - 何時應該使用這個工具

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 應該包含:

  1. 功能説明 - 工具做什麼
  2. 使用場景 - 何時使用("Use this tool when...")
  3. 示例查詢 - 用户可能的提問方式
  4. 支持的功能 - 特殊能力或限制

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.jsonEnabled 設置
  • 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?

  1. 代碼更少:無需自定義特性和提供者
  2. 更可靠:官方維護和更新
  3. 更強大:自動 Schema、DI 支持
  4. 更標準:完全符合 MCP 規範
  5. 更易維護:無需維護自定義代碼

總結

通過本文,我們學習瞭如何在 ASP.NET Core WebApi 中集成 MCP 協議支持。使用官方 SDK,只需幾行代碼就能讓你的 API 被 AI 客户端調用。MCP 協議的標準化特性,讓 AI 應用與後端服務的集成變得前所未有的簡單。

參考資源

  • MCP 官方文檔
  • MCP C# SDK
  • MCP 規範
  • NuGet 包

源碼地址

完整示例代碼請訪問:[GitHub 倉庫地址]


如果本文對你有幫助,歡迎點贊、收藏、關注!有任何問題歡迎在評論區討論。

Add a new 評論

Some HTML is okay.