Sermantic Kernel + LLM 通过自然语言调用公开的MCP服务

用户用自然语言提问 → AI 理解并选择工具 → SK 自动调用 MCP 远程 API → 拿到数据后 AI 组织成人话回复!

本篇以百炼天气预报MCP服务为例进行说明

image

 通过截图,可看出需要两个参数!

核心步骤

1️⃣ 构建MCP客户端

这是 MCP 客户端的连接配置,类似浏览器的"地址栏 + 请求头"设置。

配置项作用必填
Endpoint MCP 服务 URL 告诉客户端连哪里 ✅ 必填
TransportMode AutoDetect 自动检测用 SSE 还是 StreamableHttp ⭕ 可选(默认就是 AutoDetect)
AdditionalHeaders Authorization: Bearer xxx 每个 HTTP 请求都带上认证头 ⚠️ 看服务端要求

1、Endpoint为MCPServe的URL地址

image

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('-', '_');
        }
    }
}
View Code

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('-', '_');
        }
    }
}
View Code

image

项目引用

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

 

posted @ 2026-08-13 15:04  天才卧龙  阅读(11)  评论(0)    收藏  举报