Sermantic Kernel + LLM 通过自然语言调用公开的MCP服务
用户用自然语言提问 → AI 理解并选择工具 → SK 自动调用 MCP 远程 API → 拿到数据后 AI 组织成人话回复!
本篇以百炼天气预报MCP服务为例进行说明

通过截图,可看出需要两个参数!
核心步骤
1️⃣ 构建MCP客户端
这是 MCP 客户端的连接配置,类似浏览器的"地址栏 + 请求头"设置。
| 配置项 | 值 | 作用 | 必填 |
|---|---|---|---|
Endpoint |
MCP 服务 URL | 告诉客户端连哪里 | ✅ 必填 |
TransportMode |
AutoDetect |
自动检测用 SSE 还是 StreamableHttp | ⭕ 可选(默认就是 AutoDetect) |
AdditionalHeaders |
Authorization: Bearer xxx |
每个 HTTP 请求都带上认证头 | ⚠️ 看服务端要求 |
1、Endpoint为MCPServe的URL地址

2、TransportMode(传输模式)
MCP over HTTP 有三种传输模式:
| 模式 | 说明 | 适用场景 |
|---|---|---|
AutoDetect |
先试 StreamableHttp,失败自动降级到 SSE | ✅ 推荐,省心 |
StreamableHttp |
新标准,支持双向流式 | 服务端明确支持时用 |
Sse |
传统 Server-Sent Events | 老版本 MCP 服务 |
建议:永远用 AutoDetect,让 SDK 自己协商。
3、设置 AdditionalHeaders(认证头)
百炼 MCP 用 Bearer Token 认证,每个 HTTP 请求都会带上这个头:
POST /api/v1/mcps/market-cmgjmcp00074931/mcp HTTP/1.1 Host: dashscope.aliyuncs.com Authorization: Bearer sk-adc1111111111111111111111111101d Content-Type: application/json
⚠️ 不同MCP认证方式可能不同:
| 服务商 | 认证方式 | 代码 |
|---|---|---|
| 百炼 | Bearer Token | ["Authorization"] = $"Bearer {apiKey}" |
| 某些服务 | API Key Header | ["X-API-Key"] = apiKey |
| OAuth | OAuth 2.0 | 用 OAuth 属性,不用 AdditionalHeaders |
/// <summary> /// 创建 MCP Client /// </summary> static async Task<McpClient> CreateMcpClientAsync() { var transportOptions = new HttpClientTransportOptions { Endpoint = new Uri("https://dashscope.aliyuncs.com/api/v1/mcps/market-cmgjmcp00074931/mcp"), TransportMode = HttpTransportMode.AutoDetect, AdditionalHeaders = new Dictionary<string, string> { ["Authorization"] = $"Bearer {ConstParm.apiKey}" } }; var transport = new HttpClientTransport(transportOptions); return await McpClient.CreateAsync(transport); }
✅ 一句话总结
CreateMcpClientAsync= 配置连接地址 + 认证信息 → 创建 HTTP 传输 → 握手连接 → 返回可用客户端
2️⃣注册MCP工具TOOLS
构造MCP客户端后,就可以拿到Mcp的工具了,我们需要将MCP工具映射为 KernelFunction,KernelFunction是可以被AI调用的函数!
static void RegisterMcpTools(Kernel kernel, McpClient mcpClient, IList<McpClientTool> tools) { var functions = new List<KernelFunction>(); foreach (var tool in tools) { var kernelFunction = KernelFunctionFactory.CreateFromMethod( (string area, string area_code) => CallMcpToolAsync(mcpClient, tool.Name, area, area_code), functionName: ToValidFunctionName(tool.Name), description: tool.Description ); functions.Add(kernelFunction); } kernel.Plugins.AddFromFunctions("WeatherPlugin", functions); }
📋 方法签名分析
static void RegisterMcpTools( Kernel kernel, // ← SK 核心对象,工具注册到哪里 McpClient mcpClient, // ← MCP 客户端,用来调用远程工具 IList<McpClientTool> tools // ← 从服务器获取的工具列表 )
| 参数 | 类型 | 来源 | 作用 |
|---|---|---|---|
kernel |
Kernel |
CreateKernel() 创建 |
工具注册的目标容器 |
mcpClient |
McpClient |
CreateMcpClientAsync() 创建 |
调用 MCP 服务的通道 |
tools |
IList<McpClientTool> |
mcpClient.ListToolsAsync() |
要注册的工具定义列表 |
返回值:void(直接修改 kernel.Plugins,不需要返回)
🔄 完整流程图
┌─────────────────────────────────────────────────────────────────┐ │ 输入:kernel, mcpClient, tools │ │ tools = [ weather.search_local, operator.verify_three_elements ]│ └─────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ Step 1: 创建空列表 │ │ functions = new List<KernelFunction>() │ └─────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ Step 2: 遍历 tools │ │ foreach (var tool in tools) │ └─────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ Step 3: 为每个 tool 创建 KernelFunction │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ Lambda: (area, area_code) => CallMcpToolAsync(...) │ │ │ │ functionName: weather_search_local (转换后) │ │ │ │ description: "地名查询当前天气" │ │ │ └─────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ Step 4: 添加到列表 │ │ functions.Add(kernelFunction) │ └─────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ 重复 Step 3-4 直到所有 tool 处理完 │ └─────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ Step 5: 批量注册到 Kernel │ │ kernel.Plugins.AddFromFunctions("WeatherPlugin", functions) │ └─────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ 输出:kernel.Plugins 现在包含 WeatherPlugin 插件 │ │ AI 可以通过 SK 自动调用这些工具 │ └─────────────────────────────────────────────────────────────────┘
✅ 一句话总结
RegisterMcpTools= 遍历 MCP 工具列表 → 为每个工具创建 KernelFunction(包装 Lambda) → 批量注册到 Kernel 插件系统
3、天气预报MCP调用相关代码
using ConsoleApp1; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Connectors.OpenAI; using ModelContextProtocol; using ModelContextProtocol.Client; namespace ConsoleApp9 { internal class Program { static async Task Main(string[] args) { Console.WriteLine("=== MCP 天气查询 Demo ===\n"); // 创建 MCP Client Console.WriteLine("正在连接百炼 MCP 服务..."); var mcpClient = await CreateMcpClientAsync(); // 获取工具列表 var tools = await mcpClient.ListToolsAsync(); Console.WriteLine($"发现 {tools.Count} 个工具:"); foreach (var tool in tools) { Console.WriteLine($" - {tool.Name}: {tool.Description}"); } // 创建 Kernel var kernel = CreateKernel(); // 注册 MCP 工具到 Kernel RegisterMcpTools(kernel, mcpClient, tools); // 让 AI 使用工具 var chatService = kernel.GetRequiredService<IChatCompletionService>(); var history = new ChatHistory("你是一个天气助手,请使用 WeatherPlugin 工具查询天气并回答用户问题。必须调用工具获取真实数据,不要编造。"); Console.WriteLine("\n=== 开始对话 ==="); Console.WriteLine("用户:丽江今天天气怎么样?\n"); history.AddUserMessage("丽江今天天气怎么样?"); var settings = new OpenAIPromptExecutionSettings { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions }; var result = await chatService.GetChatMessageContentAsync( history, settings, kernel ); Console.WriteLine($"\n=== AI 回答 ==="); Console.WriteLine(result.Content); } /// <summary> /// 创建 MCP Client /// </summary> static async Task<McpClient> CreateMcpClientAsync() { var transportOptions = new HttpClientTransportOptions { Endpoint = new Uri("https://dashscope.aliyuncs.com/api/v1/mcps/market-cmgjmcp00074931/mcp"), TransportMode = HttpTransportMode.AutoDetect, AdditionalHeaders = new Dictionary<string, string> { ["Authorization"] = $"Bearer {ConstParm.apiKey}" } }; var transport = new HttpClientTransport(transportOptions); return await McpClient.CreateAsync(transport); } /// <summary> /// 创建 Kernel(包含模型配置) /// </summary> static Kernel CreateKernel() { var builder = Kernel.CreateBuilder(); builder.AddOpenAIChatCompletion( modelId: "qwen-plus", apiKey: ConstParm.apiKey, endpoint: new Uri("https://dashscope.aliyuncs.com/compatible-mode/v1") ); return builder.Build(); } /// <summary> /// 注册 MCP 工具到 Kernel /// </summary> static void RegisterMcpTools(Kernel kernel, McpClient mcpClient, IList<McpClientTool> tools) { var functions = new List<KernelFunction>(); foreach (var tool in tools) { var kernelFunction = KernelFunctionFactory.CreateFromMethod( (string area, string area_code) => CallMcpToolAsync(mcpClient, tool.Name, area, area_code), functionName: ToValidFunctionName(tool.Name), description: tool.Description ); functions.Add(kernelFunction); } kernel.Plugins.AddFromFunctions("WeatherPlugin", functions); } /// <summary> /// 调用 MCP 工具的封装方法 /// </summary> static async Task<string> CallMcpToolAsync( McpClient mcpClient, string toolName, string area, string area_code) { Console.WriteLine($"\n[工具调用] {toolName}"); Console.WriteLine($" area = {area ?? "null"}"); Console.WriteLine($" area_code = {area_code ?? "null"}"); var arguments = new Dictionary<string, object?>(); if (!string.IsNullOrWhiteSpace(area)) arguments["area"] = area; if (!string.IsNullOrWhiteSpace(area_code)) arguments["area_code"] = area_code; var result = await mcpClient.CallToolAsync(toolName, arguments); var responseText = result.Content?.FirstOrDefault()?.ToString() ?? "无结果"; Console.WriteLine($"[工具返回] {responseText}"); return responseText; } /// <summary> /// MCP 工具名转合法函数名 /// </summary> static string ToValidFunctionName(string name) { return name.Replace('.', '_').Replace('-', '_'); } } }
4、身份证号核验MCP调用相关代码-MCP客户端连接带重试
using ConsoleApp1; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Connectors.OpenAI; using ModelContextProtocol; using ModelContextProtocol.Client; using System.Net; namespace ConsoleApp10 { internal class Program { static async Task Main(string[] args) { Console.WriteLine("=== 身份证信息核验 Demo ===\n"); // 创建 MCP Client Console.WriteLine("正在连接百炼 MCP 服务..."); var mcpClient = await CreateMcpClientAsync(); // 获取工具列表 var tools = await mcpClient.ListToolsAsync(); Console.WriteLine($"发现 {tools.Count} 个工具:"); foreach (var tool in tools) { Console.WriteLine($" - {tool.Name}: {tool.Description}"); } // 创建 Kernel var kernel = CreateKernel(); // 注册 MCP 工具到 Kernel RegisterMcpTools(kernel, mcpClient, tools); // 让 AI 使用工具 var chatService = kernel.GetRequiredService<IChatCompletionService>(); var history = new ChatHistory("你是一个天气助手,请使用 PhoneCheckPlugin 工具核验手机三要素。必须调用工具获取真实数据,不要编造。"); Console.WriteLine("\n=== 开始对话 ==="); Console.WriteLine("用户:张三身份证号为:411481199002163338?\n"); history.AddUserMessage("姓名张三,身份证号为:411481199002163338,请核验是否一致"); var settings = new OpenAIPromptExecutionSettings { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions }; var result = await chatService.GetChatMessageContentAsync( history, settings, kernel ); Console.WriteLine($"\n=== AI 回答 ==="); Console.WriteLine(result.Content); } /// <summary> /// <summary> /// 创建 MCP Client(带重试策略) /// </summary> static async Task<McpClient> CreateMcpClientAsync() { var transportOptions = new HttpClientTransportOptions { Endpoint = new Uri("https://dashscope.aliyuncs.com/api/v1/mcps/market-cmgjmcp00075093/mcp"), TransportMode = HttpTransportMode.AutoDetect, AdditionalHeaders = new Dictionary<string, string> { ["Authorization"] = $"Bearer {ConstParm.apiKey}" } }; var transport = new HttpClientTransport(transportOptions); // 重试逻辑:最多 5 次,每次间隔 5 秒 for (int i = 0; i < 5; i++) { try { Console.WriteLine($"正在连接 MCP 服务... (尝试 {i + 1}/5)"); var client = await McpClient.CreateAsync(transport); // 验证连接:试一下 ListTools Console.WriteLine("正在获取工具列表..."); var tools = await client.ListToolsAsync(); Console.WriteLine($"MCP 服务连接成功!发现 {tools.Count} 个工具"); return client; } catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { Console.WriteLine($"服务未就绪:{ex.Message}"); if (i < 4) // 不是最后一次 { Console.WriteLine($"等待 5 秒后重试...\n"); await Task.Delay(5000); // 等 5 秒 } } } throw new Exception("MCP 服务连接失败,请稍后再试(服务可能正在开通或初始化中)"); } /// <summary> /// 创建 Kernel(包含模型配置) /// </summary> static Kernel CreateKernel() { var builder = Kernel.CreateBuilder(); builder.AddOpenAIChatCompletion( modelId: "qwen-plus", apiKey: ConstParm.apiKey, endpoint: new Uri("https://dashscope.aliyuncs.com/compatible-mode/v1") ); return builder.Build(); } /// <summary> /// 注册 MCP 工具到 Kernel /// </summary> static void RegisterMcpTools(Kernel kernel, McpClient mcpClient, IList<McpClientTool> tools) { var functions = new List<KernelFunction>(); foreach (var tool in tools) { var kernelFunction = KernelFunctionFactory.CreateFromMethod( ( string name, string idcard) => CallMcpToolAsync(mcpClient, tool.Name, name, idcard), functionName: ToValidFunctionName(tool.Name), description: tool.Description ); functions.Add(kernelFunction); } kernel.Plugins.AddFromFunctions("PhoneCheckPlugin", functions); } /// <summary> /// 调用 MCP 工具的封装方法 /// </summary> static async Task<string> CallMcpToolAsync(McpClient mcpClient, string toolName, string name, string idcard) { Console.WriteLine($"\n[工具调用] {toolName}"); Console.WriteLine($" name = {name ?? "null"}"); Console.WriteLine($" idcard = {idcard ?? "null"}"); var arguments = new Dictionary<string, object?>(); if (!string.IsNullOrWhiteSpace(name)) arguments["name"] = name; if (!string.IsNullOrWhiteSpace(idcard)) arguments["idcard"] = idcard; var result = await mcpClient.CallToolAsync(toolName, arguments); var responseText = result.Content?.FirstOrDefault()?.ToString() ?? "无结果"; Console.WriteLine($"[工具返回] {responseText}"); return responseText; } /// <summary> /// MCP 工具名转合法函数名 /// </summary> static string ToValidFunctionName(string name) { return name.Replace('.', '_').Replace('-', '_'); } } }

项目引用
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <!-- Semantic Kernel 核心 --> <PackageReference Include="Microsoft.SemanticKernel" Version="1.78.0" /> <PackageReference Include="ModelContextProtocol" Version="2.1.0" /> <!-- 阿里云 DashScope SDK --> <PackageReference Include="Sdcb.DashScope" Version="2.0.0" /> <!-- HTTP 客户端 --> <PackageReference Include="System.Net.Http.Json" Version="8.0.0" /> <!-- JSON 处理 --> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> </ItemGroup> </Project>

浙公网安备 33010602011771号