.net MCP Sampling (MCP Server 回调客户端 LLM)

MCP 协议中提供了一种机制 ,通过在 MCP client 中设置,可以为 MCP 服务端提供调用 LLM 的桥梁。 在 .net 中的处理流程就是 MCP Client 中配置回调机制,然后在 MCP Server 工具类 中注入 McpServer 对象,然后调用 var chatClient = thisServer.AsSamplingChatClient(); 得到 IChatClient 。 于是就可以与普通的调用 LLM 流程一样的处理了

配置 MCP Client 中处理回调

创建 mcpClient 并配置回调函数

# 生成 mcpClient 时配置 mcpClientOptions 指定 llm 回调函数, 这里调用下面的 UpdateOptions 函数进行配置
var mcpClientOptions = new McpClientOptions();
updateOptions?.Invoke(mcpClientOptions);

var mcpClient = await McpClient.CreateAsync(new StdioClientTransport(stdioClientTransportOptions), mcpClientOptions);

mcpClient 中配置 llm 回调函数


    // 1. 更新 MCP 采样回调 (配置 mcp client 的 llm 回调函数处理)
    private void UpdateOptions(McpClientOptions options, AsyncServiceScope serviceScope)
    {
        options.Handlers = new McpClientHandlers
        {
            // 指定 SamplingHandler 即为回调函数
            // requestParams 为 MCP Server 传入的查询参数
            SamplingHandler = async (requestParams, progress, cancellationToken) => // 接收服务端发送的调用请求 
            {
                if (requestParams == null)
                {
                    throw new InvalidOperationException("没有输入有效的查询参数");
                }

                // 配置 LLM 的调用参数
                var chatOptions = new ChatOptions
                {
                    MaxOutputTokens = requestParams.MaxTokens,
                    Temperature = requestParams.Temperature,
                };

                // LLM 对话数据
                var messages = (
                        from samplingMessage in requestParams.Messages
                        let content = (samplingMessage.Content as TextContentBlock)?.Text ?? string.Empty
                        where !string.IsNullOrWhiteSpace(content)
                        select new ChatMessage(this.GetChatRole(samplingMessage.Role), content)
                    )
                    .ToList();

                // 调用 llm 
                var chateClient = serviceScope.ServiceProvider.GetRequiredService<IChatClient>();

                var response = await chateClient.GetResponseAsync(messages, chatOptions, cancellationToken);

                // 返回结果 
                return new CreateMessageResult
                {
                    Content = new TextContentBlock { Type = "text", Text = response.Text, },
                    Model = response.ModelId ?? string.Empty,
                    Role = response.Messages.LastOrDefault()?.Role == ChatRole.Assistant ? Role.Assistant : Role.User,
                };
            },
        };
    }

MCP Client 调用 MCP Server

    // 创建 mcpClient 作为 tools 传入 chateClient 供 llm 调用
    // 询问  llm 穿衣指数,AI 会调用 MCP Server 中的 GetDressingAdvice 函数
    // 而 MCP Server 中的 GetDressingAdvice 函数中会回调用访问 llm 
    [Fact(DisplayName = "测试 MCP 采样回调")]
    public async Task TestSamplingAsync()
    {
        await using var serviceProvider = this.CreateServiceProviderCore(it => it.UseBaiLianAiChatClinet());

        await using var serviceScope = serviceProvider.CreateAsyncScope();

        // 创建可用的工具列表, 并在其中调用 UpdateOptions 配置 SamplingHandler 作为 LLM 回调处理函数
        var mcpClient = await this.CreateMcpClient(options => this.UpdateOptions(options, serviceScope));
        var tools = await mcpClient.ListToolsAsync();

        // 进行聊天,传入天气工具, 记得设置一下 UseFunctionInvocation
        using var chateClient = serviceProvider.GetRequiredService<IChatClient>().ConfigChatClient(it => it.UseFunctionInvocation());
        var chatResponse = await chateClient.GetResponseAsync(new ChatMessage(ChatRole.User, "根据天气推荐在南京如何穿衣"), new ChatOptions { Tools = [.. tools,], });

        // 输出 AI 查询的结果
        this.LogInfo(chatResponse.Text);
    }

MCP Server 中注入 McpServer 并调用


    // 1. 注入 McpServer
    // 2. 调用 thisServer.AsSamplingChatClient() 获得 IChatClient
    // 3. 通过 `IChatClient` 调用 `LLM`
    [McpServerTool]
    [Description("返回指定城市的当前的穿衣建议")]
    public async Task<string> GetDressingAdvice(
        McpServer thisServer,
        [Description("待查询穿衣建议的城市的 CityId")] string cityId)
    {
        // 查询出天气信息
        var weather = await this.GetCityWeatherAsync(cityId);

        // 获取 ichatclient
        var chatClient = thisServer.AsSamplingChatClient();

        // 调用 llm 分析穿衣指数
        const string message = @"""
  你是一个专业的天气与生活助手。请根据用户提供的【城市名称】、【天气状况】(如晴、多云、小雨、大雨、雪等)和【当前气温(℃)】,生成一条简洁、实用、人性化的穿衣建议。

        要求:
        1. 先简要说明今日体感(如“微凉”“炎热”“湿冷”等);
        2. 给出具体的穿衣推荐(如“建议穿长袖衬衫+薄外套”);
        3. 如遇雨、雪或高温晴天,请附加相应提示(如“记得带伞”“注意防晒”);
        4. 语言亲切自然,避免专业术语,适合普通大众阅读;
        5. 输出控制在 2–3 句话内,不超过 80 字。

        输入格式示例:
        - 城市:上海
            - 天气:小雨
            - 气温:19℃

        请按上述要求生成穿衣建议。
""";

        var messages = new List<ChatMessage>
        {
            new(ChatRole.System, message),
            new(ChatRole.User, weather),
        };

        using var ts = new CancellationTokenSource(TimeSpan.FromSeconds(15));

        var response = await chatClient.GetResponseAsync(messages, cancellationToken: ts.Token);

        // 返回结果
        return response.Text;
    }

上一篇