ASP.NET Core WebApi 集成 MCP 协议完全指南

本文详细介绍如何在 ASP.NET Core WebApi 项目中集成 Model Context Protocol (MCP) 支持,让你的 API 能够被 AI 客户端(如 Claude、ChatGPT)自动调用。

📖 目录

什么是 MCP?

MCP(Model Context Protocol) 是一个开放协议,旨在标准化 AI 应用与外部工具、数据源之间的通信方式。

MCP 的优势

  • 🔌 标准化接口:统一的协议规范
  • 🤖 AI 友好:自动工具发现和调用
  • 🔒 安全可靠:内置认证和授权机制
  • 🚀 易于集成:官方 SDK 支持多种语言

核心特性

本项目实现了以下功能:

特性 说明
✅ 官方 SDK 使用 ModelContextProtocol.AspNetCore
✅ 特性标记 通过 [McpServerTool] 快速定义工具
✅ 自动绑定 自动参数绑定和 JSON Schema 生成
✅ 双传输模式 支持 HTTP 和 Stdio
✅ 认证授权 基于 Token 的安全机制
✅ 完美共存 与现有 WebApi 无缝集成

快速开始

步骤 1:安装 NuGet 包

dotnet add package ModelContextProtocol.AspNetCore --version 0.4.0-preview.3

步骤 2:配置 MCP 服务

Program.cs 中添加配置:

using ModelContextProtocol.Server;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

// 添加 MCP 服务器
builder.Services
    .AddMcpServer(options =>
    {
        options.ServerInfo = new ModelContextProtocol.Protocol.Implementation
        {
            Name = "Weather API",
            Version = "1.0.0"
        };
    })
    .WithHttpTransport()           // HTTP 模式
    .WithStdioServerTransport()    // Stdio 模式
    .WithToolsFromAssembly();

var app = builder.Build();

// 添加认证中间件
app.UseMiddleware<McpAuthenticationMiddleware>();

app.UseAuthorization();
app.MapControllers();

// 映射 MCP 端点
app.MapMcp("/mcp");

app.Run();

步骤 3:定义 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)
    {
        // 实现逻辑
    }
}

步骤 4:配置认证

appsettings.json

{
  "McpAuth": {
    "Enabled": true,
    "ValidTokens": ["your-secret-token-here"]
  }
}

appsettings.Development.json(开发环境):

{
  "McpAuth": {
    "Enabled": false
  }
}

步骤 5:运行测试

dotnet run

访问地址:

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

传输模式详解

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 环境变量/配置
网络 需要网络端口 无需网络
性能 网络开销 进程间通信,更快
调试 可用浏览器/Postman 需要专门工具

认证和授权

实现认证中间件

创建 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;

// 创建 HTTP 客户端传输
var transport = new HttpClientTransport(new HttpClientTransportOptions
{
    BaseUrl = new Uri("http://localhost:5000/mcp"),
    Headers = new Dictionary<string, string>
    {
        ["Authorization"] = "Bearer your-secret-token-here"
    }
});

// 创建 MCP 客户端
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();
foreach (var tool in tools)
{
    Console.WriteLine($"工具: {tool.Name} - {tool.Description}");
}

// 调用工具
var result = await client.CallToolAsync(
    "GetWeatherForecast",
    new Dictionary<string, object?>()
);

Console.WriteLine($"结果: {result.Content[0].Text}");

JavaScript/Vue 客户端示例

<template>
  <div class="weather-app">
    <h1>天气应用</h1>
    <button @click="getWeather" class="btn">获取天气预报</button>
    <pre v-if="weather" class="result">{{ weather }}</pre>
  </div>
</template>

<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>

<style scoped>
.weather-app {
  padding: 20px;
}
.btn {
  padding: 10px 20px;
  background: #42b983;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
.result {
  margin-top: 20px;
  padding: 15px;
  background: #f5f5f5;
  border-radius: 4px;
}
</style>

Python/LangChain 集成

from langchain.tools import Tool
from langchain.agents import initialize_agent, AgentType
from langchain.llms import OpenAI
import requests

def call_mcp_tool(tool_name: str, arguments: dict = None) -> str:
    """调用 MCP 工具"""
    response = requests.post(
        'http://localhost:5000/mcp',
        headers={
            'Content-Type': 'application/json',
            'Authorization': 'Bearer your-secret-token-here'
        },
        json={
            'jsonrpc': '2.0',
            'id': 1,
            'method': 'tools/call',
            'params': {
                'name': tool_name,
                'arguments': arguments or {}
            }
        }
    )
    return response.json()['result']['content'][0]['text']

# 创建 LangChain 工具
weather_tool = Tool(
    name="GetWeatherForecast",
    func=lambda x: call_mcp_tool("GetWeatherForecast"),
    description="Get weather forecast for the next 5 days"
)

# 创建 Agent
llm = OpenAI(temperature=0)
agent = initialize_agent(
    [weather_tool],
    llm,
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True
)

# 使用 Agent
result = agent.run("What's the weather forecast?")
print(result)

高级特性

依赖注入支持

工具方法可以注入服务:

[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 MyPrompts
{
    [McpServerPrompt]
    [Description("Creates a prompt to summarize content")]
    public static ChatMessage Summarize(
        [Description("The content to summarize")] string content)
    {
        return new ChatMessage(
            ChatRole.User,
            $"Please summarize: {content}");
    }
}

支持的参数类型

SDK 自动支持:

  • ✅ 基本类型:string, int, bool, double
  • ✅ 复杂对象:自动序列化/反序列化
  • ✅ 可选参数:使用默认值
  • ✅ 数组和集合:List<T>, T[]

故障排除

❌ 工具未被发现

检查项

  • 类是否有 [McpServerToolType] 特性
  • 方法是否有 [McpServerTool] 特性
  • 类是否是静态的
  • 是否重启了应用

❌ 认证失败

检查项

  • Token 是否正确
  • appsettings.jsonEnabled 设置
  • Authorization header 格式:Bearer {token}
  • 环境配置(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 协议支持。主要内容包括:

  1. ✅ MCP 协议的基本概念和优势
  2. ✅ 使用官方 SDK 快速集成
  3. ✅ HTTP 和 Stdio 双传输模式配置
  4. ✅ 基于 Token 的认证和授权
  5. ✅ 多种客户端集成示例
  6. ✅ 高级特性和故障排除

使用官方 SDK,只需几行代码就能让你的 API 被 AI 客户端调用。MCP 协议的标准化特性,让 AI 应用与后端服务的集成变得前所未有的简单。

参考资源

源码地址

完整示例代码请访问:[GitHub 仓库地址]


💡 提示:如果本文对你有帮助,欢迎点赞 👍、收藏 ⭐、关注 🔔!

💬 交流:有任何问题欢迎在评论区讨论,我会及时回复。

🔗 分享:欢迎转发分享给更多需要的朋友。


标签ASP.NET Core MCP AI WebApi Claude ChatGPT C# .NET

传输模式详解

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

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. 优化检查清单

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

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

客户端集成

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 客户端

<template>
  <div>
    <h1>天气应用</h1>
    <button @click="getWeather">获取天气预报</button>
    <pre v-if="weather">{{ weather }}</pre>
  </div>
</template>

<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>

高级特性

依赖注入支持

[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."
        );
    }
}

故障排除

工具未被发现

检查项

  • 类是否有 [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");

AI 没有使用我的工具?

检查

  1. Description 是否足够详细?
  2. 是否包含使用场景说明?
  3. 是否包含示例查询?
  4. 工具名称是否清晰?

项目结构

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 协议支持,以及如何编写高质量的 MCP Tools。关键要点:

  1. 使用官方 SDK - 简化开发流程
  2. 详细的 Description - 让 AI 准确理解工具用途
  3. 清晰的命名和参数 - 提高工具可用性
  4. 格式化的返回值 - 让 AI 更好地理解结果
  5. 充分的测试 - 确保工具在真实场景中可用

记住:AI 是通过文本理解你的工具,所以文档质量直接影响工具的可用性!

参考资源

源码地址

完整示例代码请访问:[GitHub 仓库地址]


💡 提示:如果本文对你有帮助,欢迎点赞 👍、收藏 ⭐、关注 🔔!有任何问题欢迎在评论区讨论 💬。

标签ASP.NET Core MCP AI WebApi Claude ChatGPT Model Context Protocol

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐