为 .NET 客户端接入 LLM:mcp-for-beginners 03-llm-client 实战指南

发布时间:2026/10/6 15:49:25

为 .NET 客户端接入 LLM:mcp-for-beginners 03-llm-client 实战指南 教程文档人工智能【免费下载链接】mcp-for-beginnersThis open-source curriculum introduces the fundamentals of Model Context Protocol (MCP) through real-world, cross-language examples in .NET, Java, TypeScript, JavaScript, Rust and Python. Designed for developers, it focuses on practical techniques for building modular, scalable, and secure AI workflows from session setup to service orchestration.项目地址https://gitcode.com/GitHub_Trending/mc/mcp-for-beginners点击查看免费下载本篇指南以 mcp-for-beginners 课程中「创建一个带 LLM 的客户端Creating a client with LLM」的 .NET 解决方案为主体讲解如何让客户端不再硬编码调用 MCP Server 的工具而是通过自然语言让 LLM 自主决策、动态调用 MCP 工具。读完本文你将掌握 .NET 下ModelContextProtocolSDK OpenAI SDK 的完整接入流程建立 stdio 连接、列出服务器能力、把 MCP 工具 schema 转换为 LLM 认识的工具定义并在对话循环中执行模型发起的工具调用最终拿到类似Sum 6的结果。课程背景与整体思路在之前的课程中我们已经分别创建过 MCP Server 和 MCP Client。但那种客户端是显式的调用方必须知道服务器上有哪些工具、资源、提示词并一条条手动调用。这并不符合当下 Agentic 时代用户的使用习惯——用户希望用自然语言对话而不关心能力背后是否由 MCP 承载。解决方案就是给客户端加一个 LLM。整体交互流程见 课程主文档分四步与 MCP Server 建立连接列出服务器的能力工具、资源、提示词并把 schema 保存下来接入 LLM把保存的能力与 schema 转换成 LLM 能理解的形式传给它处理用户提示词连同客户端列出的工具一起交给 LLM由 LLM 决定调用哪些工具、传什么参数。本指南聚焦其中.NETC#的完整实现源码位于 03-llm-client/solution/dotnet核心文件为 Program.cs。前置准备部署模型并配置环境变量运行示例前需要一个可用的 OpenAI 兼容模型服务。课程文档要求部署一个活动模型例如gpt-5.1并配置以下三个环境变量# zsh/bash export AZURE_OPENAI_ENDPOINThttps://resource-name.openai.azure.com export AZURE_OPENAI_API_KEYapi-key export AZURE_OPENAI_DEPLOYMENTgpt-5.1# PowerShell $env:AZURE_OPENAI_ENDPOINT https://resource-name.openai.azure.com $env:AZURE_OPENAI_API_KEY api-key $env:AZURE_OPENAI_DEPLOYMENT gpt-5.1其中AZURE_OPENAI_DEPLOYMENT是部署名用于 API 调用可能与底层模型名不同。从 Program.cs 的源码可以看到它的读取逻辑var endpoint Environment.GetEnvironmentVariable(AZURE_OPENAI_ENDPOINT); var apiKey Environment.GetEnvironmentVariable(AZURE_OPENAI_API_KEY); var deployment Environment.GetEnvironmentVariable(AZURE_OPENAI_DEPLOYMENT) ?? gpt-5.1; if (string.IsNullOrWhiteSpace(endpoint) || string.IsNullOrWhiteSpace(apiKey)) { Console.WriteLine(Please set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_API_KEY.); return; }即AZURE_OPENAI_DEPLOYMENT缺省回退到gpt-5.1而AZURE_OPENAI_ENDPOINT与AZURE_OPENAI_API_KEY缺失时会直接提示并退出。另外解决方案 README 还提示若在 GitHub Codespaces 中运行则无需本地令牌若在本地运行需要为GITHUB_TOKEN配置个人访问令牌PAT# zsh/bash export GITHUB_TOKEN{{YOUR_GITHUB_PAT}}# PowerShell $env:GITHUB_TOKEN {{YOUR_GITHUB_PAT}}安装依赖库示例是一个控制台应用先还原 NuGet 包dotnet restore翻译版文档列出的库名为 Azure AI Inference、Azure Identity、Microsoft.Extension、Model.Hosting、ModelContextProtocol而以当前仓库实际为准dotnet.csproj 声明的包引用如下ItemGroup PackageReference IncludeMicrosoft.Extensions.Hosting Version9.*-* / PackageReference IncludeModelContextProtocol Version0.*-* / PackageReference IncludeOpenAI Version2.10.0 / /ItemGroup工程目标框架为net9.0启用了ImplicitUsings与Nullable。三个包各司其职ModelContextProtocol提供McpClient、StdioClientTransport以及TextContentBlock等 MCP 客户端类型OpenAI2.10.0提供ChatClient、ChatTool、ChatCompletionOptions等用于与模型服务对话Microsoft.Extensions.Hosting宿主框架支持该示例主要使用它支撑依赖与日志等基础设施。运行示例与预期输出依赖还原完成后直接运行dotnet run文档给出的预期输出如下Setting up stdio transport Listing tools Connected to server with tools: Add Tool description: Adds two numbers Tool parameters: {title:Add,description:Adds two numbers,type:object,properties:{a:{type:integer},b:{type:integer}},required:[a,b]} Tool definition: Azure.AI.Inference.ChatCompletionsToolDefinition Properties: {a:{type:integer},b:{type:integer}} MCP Tools def: 0: Azure.AI.Inference.ChatCompletionsToolDefinition Tool call 0: Add with arguments {a:2,b:4} Sum 6说明上面输出中的Tool definition类型名称因 SDK 版本而异。翻译文档记录的是早期 Azure.AI.Inference 时代的结果当前仓库源码基于 OpenAI SDKProgram.cs 与英文版 solution README 中的实际输出为OpenAI.Chat.ChatTool。输出中大部分内容只是调试信息真正重要的是这条链路从 MCP Server 列出工具 → 转换成 LLM 能理解的工具定义 → LLM 发起工具调用 → MCP 客户端执行并返回Sum 6。源码逐段剖析从 stdio 连接到工具执行1. 建立 ChatClient 与 MCP stdio 连接Program.cs 同时创建了两个客户端var client new ChatClient( model: deployment, credential: new ApiKeyCredential(apiKey), options: new OpenAIClientOptions { Endpoint new Uri(${endpoint.TrimEnd(/)}/openai/v1/) }); var chatHistory new ListChatMessage { new SystemChatMessage(You are a helpful assistant that knows about AI) }; var clientTransport new StdioClientTransport(new() { Name Demo Server, Command ${Path.Combine(AppContext.BaseDirectory, ../../../../../../, 02-client/solution/server/bin/Debug/net9.0/server)}, Arguments [], }); Console.WriteLine(Setting up stdio transport); await using var mcpClient await McpClient.CreateAsync(clientTransport);要点解析ChatClient通过ApiKeyCredential认证并把endpoint末尾的/去掉后拼接/openai/v1/作为 API 路径StdioClientTransport通过子进程方式启动 MCP ServerCommand指向上一课 02-client 的 .NET Server 构建产物McpClient.CreateAsync(clientTransport)完成 MCP 握手并返回可用的McpClient。2. 列出 MCP 工具并转换为 LLM 工具MCP 返回的工具格式含Name、Description、JsonSchema并不能直接喂给 LLM需要先做一次翻译。GetMcpTools方法完成列出与转换ChatTool ConvertFrom(string name, string description, JsonElement jsonElement) { return ChatTool.CreateFunctionTool( functionName: name, functionDescription: description, functionParameters: BinaryData.FromString(jsonElement.GetRawText())); } async TaskListChatTool GetMcpTools() { Console.WriteLine(Listing tools); var tools await mcpClient.ListToolsAsync(); ListChatTool toolDefinitions []; foreach (var tool in tools) { Console.WriteLine($Connected to server with tools: {tool.Name}); Console.WriteLine($Tool description: {tool.Description}); Console.WriteLine($Tool parameters: {tool.JsonSchema}); var def ConvertFrom(tool.Name, tool.Description, tool.JsonSchema); Console.WriteLine($Tool definition: {def}); toolDefinitions.Add(def); } return toolDefinitions; }关键转换位于ConvertFromChatTool.CreateFunctionTool接收函数名、函数描述、函数参数 JSON三个要素正是 OpenAI 工具调用function calling所需的信息。这对应课程中将 MCP Server 响应转换为 LLM 可理解的格式这一步——MCP 的JsonSchema在这里被原样作为 function parameters 透传给 LLM。3. 发起 LLM 对话并处理工具调用接下来进入真正的对话环节Program.csvar userMessage add 2 and 4; chatHistory.Add(new UserChatMessage(userMessage)); var options new ChatCompletionOptions { Tools { tools[0] } }; ChatCompletion response await client.CompleteChatAsync(chatHistory, options); var content response.Content.FirstOrDefault()?.Text; for (int i 0; i response.ToolCalls.Count; i) { var call response.ToolCalls[i]; Console.WriteLine($Tool call {i}: {call.FunctionName} with arguments {call.FunctionArguments}); //Tool call 0: add with arguments {a:2,b:4} var dict JsonSerializer.DeserializeDictionarystring, object(call.FunctionArguments); var result await mcpClient.CallToolAsync( call.FunctionName, dict!, cancellationToken: CancellationToken.None ); var textBlock result.Content.OfTypeTextContentBlock().FirstOrDefault(); if (textBlock ! null) { Console.WriteLine(textBlock.Text); } } Console.WriteLine($Assistant response: {content});这段代码的六步流程清晰可见把用户消息add 2 and 4加入对话历史用ChatCompletionOptions.Tools把之前转换好的ChatTool这里是tools[0]即Add交给模型CompleteChatAsync向模型发起补全请求——模型会决定是否调用工具检查响应中的response.ToolCalls若有工具调用则打印调用名与参数把 LLM 返回的参数反序列化为字典通过mcpClient.CallToolAsync回传给 MCP Server 真正执行从result.Content中取出TextContentBlock并打印即得到Sum 6。4. 被调用的 MCP Server 长什么样本示例连接的 MCP Server 来自上一课其核心实现见 02-client/solution/server/Program.cs[McpServerToolType] public static class CalculatorTool { [McpServerTool, Description(Adds two numbers)] public static string Add(int a, int b) $Sum {a b}; }Add方法用[McpServerTool]特性暴露为 MCP 工具Description(Adds two numbers)正是客户端打印出的Tool description方法签名int a, int b则自动生成 JSON Schema 中的properties与required字段。这也解释了为什么输出中工具参数为{a:{type:integer},b:{type:integer}}。服务端还通过LogToStandardErrorThreshold LogLevel.Trace把所有日志导向 stderr避免污染与客户端之间的 stdio 协议通道。学习要点与延伸练习给客户端加 LLM 是更好的用户交互方式用户只需说 add 2 and 4无需知道Add(a, b)这样的客户端命令甚至意识不到背后有 MCP Server 被调用必须做格式转换MCP Server 返回的工具列表与 schema 需要转换成 LLMOpenAI function calling能理解的ChatTool定义转换的核心是把 JSON Schema 透传为 function parameters工具执行回环LLM 决定调用哪些工具 → 客户端解析参数 →CallToolAsync调回 MCP Server → 把TextContentBlock文本返回给用户。课程还给出了延伸练习Assignment参照本节代码为 Server 增加更多工具再用带 LLM 的客户端配合不同提示词测试确保所有 Server 工具都能被动态、自动地调用。如果你需要对照其他语言实现可查看同目录下的 TypeScript、Python、Java、Rust 解决方案以及 解决方案总览。下一步可以继续学习 使用 Visual Studio Code 消费 MCP Server把带 LLM 的客户端能力延伸到 IDE 场景中。赞分享教程文档人工智能【免费下载链接】mcp-for-beginnersThis open-source curriculum introduces the fundamentals of Model Context Protocol (MCP) through real-world, cross-language examples in .NET, Java, TypeScript, JavaScript, Rust and Python. Designed for developers, it focuses on practical techniques for building modular, scalable, and secure AI workflows from session setup to service orchestration.项目地址https://gitcode.com/GitHub_Trending/mc/mcp-for-beginners点击查看免费下载相关推荐mcp-for-beginners 实战使用 .NET 构建接入 LLM 的 MCP 客户端mcp for beginners 实战使用 .NET 构建接入 LLM 的 MCP 客户端 在本篇指南中你将基于 mcp for beginners 开源教程文档人工智能mcp-for-beginners 实战在 Python 中运行带 LLM 的 MCP 客户端03-llm-client 示例详解mcp for beginners 实战在 Python 中运行带 LLM 的 MCP 客户端03 llm client 示例详解 本文围绕 mcp fo教程文档人工智能为 MCP 客户端接入 LLMmcp-for-beginners 课程 .NET 示例的配置、运行与工具转换原理为 MCP 客户端接入 LLMmcp for beginners 课程 .NET 示例的配置、运行与工具转换原理 本文基于 mcp for beginners教程文档人工智能上一篇Whisper-WebUI项目如何添加自定义Whisper语音识别模型下一篇TDesign-MiniProgram中Button组件自定义图标问题解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/6 15:49:25

SkyWalking 6.x 安装、调试与 Java Agent 探针接入实战指南

文档教程技术博客 【免费下载链接】Linux-Tutorial 《Java 程序员眼中的 Linux》 项目地址: https://gitcode.com/gh_mirrors/li/Linux-Tutorial 点击查看 免费下载 SkyWalking 是 Apache 基金会下的开源 APM(Application Performance Monitoring&#…

2026/10/6 15:44:24

ltemlb负载均衡实战:多链路调度、等开销配置与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 16:44:28

C#实现U盘禁用守护进程:插拔检测、强制禁用与自我保活

干这行的人多少都遇到过这种需求:公司内网机器要封U盘、机房设备只允许指定U盘读写、或者就是单纯不想让人随便往服务器上插东西。市面上现成的管控软件不是价格离谱,就是策略太死板,弄来弄去还不如自己用C#写一个。今天这篇就聊一个完整的C#…

2026/10/6 16:44:28

Python实现π的10000位精确计算:任意精度与算法选型实战解析

在技术社区搜pi,跳出来多半是树莓派、PI控制器、pi agent这类内容,真要搜“计算pi小数点后10000位”,反而会掉进一堆年代久远的代码片段里,有的用C语言全篇宏定义,有的只贴出几千位就说“已算到一万位”。我自己动手完…

2026/10/6 16:44:28

Flutter鸿蒙化迁移实战:用dia依赖注入实现无感多端适配

最近 Flutter 社区里最热闹的事,就是鸿蒙原生适配这条赛道。我手头正好有个老项目,核心依赖注入层用的是 dia 这个轻量库,趁热把它迁到了 OpenHarmony 的 Flutter 分支上。整个过程比想象中顺,但也踩了不少教科书里不会写的坑。这…

2026/10/6 16:44:28

用百度AI手势识别打造程序员专属视力自测工具

每天盯着屏幕八小时起步,下班还要接着刷手机,干眼、视疲劳、飞蚊症几乎成了程序员标配。大家都爱拿"钛合金狗眼"自嘲,可体检报告上一行"建议进一步检查"还是让人心里发虚。我前阵子实在不想再靠猜来判断自己的眼睛状态&a…

2026/10/6 16:44:28

OpenClaw接入飞书实战:从WSL2环境到Agent消息互通

前阵子折腾了一整天,终于把OpenClaw接进了飞书。最开始以为就是走个webhook转发的事,结果先是WSL2环境验证失败,后来又卡在事件订阅的回调超时上,等真能收消息了,机器人又半天不吭声。那感觉就是每一步都在给下一层的坑…

2026/10/6 16:39:28

Spring容器动态代理Bean注入实战:从JDK到CGLIB全解析

最近在项目里要做中间件改造,碰上一个逃不开的需求:在Spring容器启动过程中,把一批接口动态代理成Bean,然后注册进容器里。这种东西单看标题就一行字,真做起来全是细节,从代理方式选型到注册时机的控制&…

2026/10/5 6:32:56

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/6 4:01:51

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/5 17:38:27

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 0:03:23

MR25H40CDF+STM32F031C6工业级高可靠数据存储方案

1. 项目概述:为什么在工业现场非得用 MR25H40CDF 配 STM32F031C6 做数据存储?在工厂产线的 PLC 控制柜里、在风电变流器的散热片背面、在矿井监测终端的金属外壳下,你经常能看到一块指甲盖大小的黑色芯片——它既不是 Flash,也不是…

2026/10/6 0:03:23

MRAM+STM32工业断电数据保全实战指南

1. 项目概述:为什么在工业现场非得用 MR25H40CDF 配 STM32F031C6 做数据存储?在工厂产线的PLC柜里、在野外无人值守的环境监测终端里、在高速运转的包装机控制板上,你经常能看到一块指甲盖大小的黑色芯片,旁边贴着“MR25H40CDF”丝…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑