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 包

bash 复制代码
dotnet add package ModelContextProtocol.AspNetCore --version 0.4.0-preview.3

第二步:配置 MCP 服务

Program.cs 中添加 MCP 配置:

csharp 复制代码
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

csharp 复制代码
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 中配置:

json 复制代码
{
  "McpAuth": {
    "Enabled": true,
    "ValidTokens": ["your-secret-token-here"]
  }
}

开发环境可以禁用认证(appsettings.Development.json):

json 复制代码
{
  "McpAuth": {
    "Enabled": false
  }
}

第五步:运行和测试

bash 复制代码
dotnet run

应用启动后,可以访问:

  • Swagger UI : http://localhost:5000/swagger
  • WebApi : http://localhost:5000/weatherforecast
  • MCP 端点 : http://localhost:5000/mcp

传输模式详解

HTTP 模式

适用于 Web 应用、Claude Desktop、远程访问等场景。

测试示例

bash 复制代码
# 列出所有工具
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):

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

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

csharp 复制代码
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# 客户端

csharp 复制代码
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 客户端

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. 使用清晰的命名

csharp 复制代码
// ❌ 不好 - 名称模糊
[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 判断是否使用工具。

csharp 复制代码
// ❌ 不好 - 太简短
[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. 详细的参数描述

csharp 复制代码
[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. 返回格式化、易读的结果

csharp 复制代码
// ❌ 不好 - 返回原始对象
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. 完整示例:查询工具

csharp 复制代码
[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. 完整示例:比较工具

csharp 复制代码
[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 模板

基础模板:

csharp 复制代码
[Description(@"[简短功能说明]

Use this tool when users ask about:
- [使用场景1]
- [使用场景2]
- [使用场景3]

Examples of user queries:
- '[示例问题1]'
- '[示例问题2]'
- '[示例问题3]'")]

完整模板:

csharp 复制代码
[Description(@"[详细功能说明,包括返回的数据类型和格式]

Use this tool when users want to:
- [使用场景1]
- [使用场景2]
- [使用场景3]

Supports:
- [支持的功能1]
- [支持的功能2]

Examples of user queries:
- '[英文示例1]'
- '[英文示例2]'
- '[中文示例1]'
- '[中文示例2]'

Note: [特殊说明或限制]")]

8. 优化检查清单

在发布工具前,检查以下项目:

  • 工具名称清晰、描述性强
  • Description 包含详细功能说明
  • Description 包含使用场景("Use this tool when...")
  • Description 包含示例查询
  • 所有参数都有详细描述
  • 参数描述包含示例值
  • 返回值格式化、易读
  • 包含错误处理
  • 支持多语言(如果需要)
  • 在 MCP Inspector 中测试通过
  • 在 AI 客户端中测试通过

高级特性

依赖注入支持

工具方法可以注入服务:

csharp 复制代码
[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

csharp 复制代码
[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 支持:

csharp 复制代码
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowMcpClients", policy =>
    {
        policy.WithOrigins("http://localhost:3000")
              .AllowAnyHeader()
              .AllowAnyMethod();
    });
});

app.UseCors("AllowMcpClients");

项目结构

bash 复制代码
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 应用与后端服务的集成变得前所未有的简单。

参考资源

相关推荐
后端小肥肠1 天前
效率狂飙!n8n 无人值守工作流,每天自动把领域最新热点做成小红书卡片存本地
人工智能·agent·mcp
程序员辉哥5 天前
在Cursor中通过SSH MCP运维自己的服务器
ssh·cursor·mcp
后端小肥肠5 天前
【n8n入门系列】输入抖音分享链接,3步自动提无水印视频 + 文案,小白也能上手!
agent·deepseek·mcp
beyond阿亮5 天前
nacos支持MCP Server注册与发现
java·python·ai·nacos·mcp
todoitbo5 天前
我用 TRAE 做了一个不一样的 MySQL MCP
数据库·mysql·adb·ai工具·mcp·trae·mysql-mcp
大模型真好玩5 天前
低代码Agent开发框架使用指南(五)—Coze消息卡片详解
人工智能·coze·mcp
AAA小肥杨6 天前
Mac 从零开始配置 VS Code + Claude/Codex AI 协同开发环境教程
人工智能·macos·ai·mcp
许泽宇的技术分享6 天前
Windows MCP.Net:解锁AI助手的Windows桌面自动化潜能
人工智能·windows·.net·mcp
大模型真好玩7 天前
LangGraph实战项目:从零手搓DeepResearch(三)——LangGraph多智能体搭建与部署
人工智能·langchain·mcp