news 2026/10/7 8:39:13

在 mcp-for-beginners 中用 Rust 构建接入 LLM 的 MCP 客户端:从环境配置到工具调用全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
在 mcp-for-beginners 中用 Rust 构建接入 LLM 的 MCP 客户端:从环境配置到工具调用全解析
  • 教程
  • 文档
  • 人工智能

【免费下载链接】mcp-for-beginners

This 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 的客户端」(03-llm-client)一节的 Rust 解决方案为主线,讲解如何在 Rust 中把 MCP 服务器暴露的工具能力交给大语言模型(LLM),让用户用自然语言完成对 MCP 工具的调用。读完本文,你将掌握 Rust 环境下 LLM 客户端的完整搭建流程:配置 Microsoft Foundry 模型端点、通过 stdio 拉起 calculator MCP 服务器、拉取并转换工具清单、驱动 LLM 发起函数调用(function calling),最终看到Calling tool: add与计算结果输出。

为什么客户端需要接入 LLM

在前面的课程(02-client)中,客户端虽然已经能够连接服务器并显式列出 tools、resources 和 prompts,但这种「手动点名调用」的交互方式并不实用。用户期待的是用自然语言与系统对话,而不是关心底层是否使用 MCP 承载能力。解决方案就是:在客户端一侧接入 LLM,把 MCP 服务器注册的能力及其 JSON Schema 转换成 LLM 可以理解的形式,让 LLM 自主决定调用哪个工具、传入什么参数。

整体交互流程如下:

  1. 与 MCP 服务器建立连接(本示例通过 stdio 拉起 calculator 服务器进程);
  2. 列出服务器的 capabilities、工具清单并保存其 schema;
  3. 将保存的能力与 schema 转换成 LLM 的函数调用格式;
  4. 把用户提示词连同工具清单交给 LLM,由 LLM 决定是否触发工具调用,再经由 MCP 客户端回传结果。

Rust 解决方案的完整代码位于 solution/rust/src/main.rs,工程配置见 Cargo.toml。

运行前置条件

本示例是 Rust 编写的 LLM 客户端,需要满足以下前置条件:

  • 已安装 Rust toolchain(cargo与rustc可用),并确保cargo在 PATH 中;
  • 一个可通过 Azure OpenAI v1 端点访问的 Microsoft Foundry 模型部署,即已部署的活跃模型(如gpt-5.1);
  • 一个可运行的 calculator MCP 服务器。若尚未创建,请先完成 01-first-server 课程,其 Rust 实现位于 01-first-server/solution/rust,服务器通过 stdio 暴露一个add工具。

提示:课程主文档(03-llm-client/README.md)中其他语言的客户端方案各不相同——Java 客户端示例仍通过传统 HTTP+SSE 传输并针对 MCP2025-11-25SDK;新的远程客户端应使用2026-07-28兼容 SDK 与 Streamable HTTP。Rust 方案则使用 rmcp 的客户端特性与子进程传输(transport-child-process)。

第一步:配置 Microsoft Foundry 环境变量

客户端通过 Azure OpenAI v1 端点调用已部署的模型。运行前必须设置三个环境变量:

# zsh/bash export AZURE_OPENAI_ENDPOINT="https://<resource-name>.openai.azure.com" export AZURE_OPENAI_API_KEY="<api-key>" export AZURE_OPENAI_DEPLOYMENT="gpt-5.1"

PowerShell 下使用对应语法:

# 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_ENDPOINT资源端点形如https://<resource-name>.openai.azure.com,客户端会在此基础上拼接/openai/v1
AZURE_OPENAI_API_KEY访问密钥用于 Azure OpenAI 兼容端点的鉴权
AZURE_OPENAI_DEPLOYMENT部署名称传给 LLM 的model参数;注意它是部署名而非底层模型名,二者可能不同

在源码中,AZURE_OPENAI_DEPLOYMENT是可选的,缺省值为gpt-5.1:

let model = std::env::var("AZURE_OPENAI_DEPLOYMENT") .unwrap_or_else(|_| "gpt-5.1".to_string());

(见 main.rs)

选择模型前建议先查阅 Microsoft Foundry 的模型退役时间表;如果部署名与底层模型名不一致,API 调用中必须使用部署名。

第二步:构建示例

进入 Rust 解决方案目录后执行:

cargo build

依赖声明位于 Cargo.toml,核心依赖如下:

[dependencies] async-openai = { version = "0.29.0", features = ["byot"] } rmcp = { version = "1.4.0", features = ["client", "transport-child-process"] } serde_json = "1.0.141" tokio = { version = "1.46.1", features = ["rt-multi-thread"] }

各依赖的角色:

  • async-openai:社区维护的 OpenAI API Rust 客户端。官方并未提供 Rust 版 OpenAI 库,这是社区中常用的替代方案。启用byot(bring your own tools)特性以支持自定义工具定义(function calling);
  • rmcp:Rust 实现的 MCP 客户端/服务端库,启用client与transport-child-process特性,用于建立 MCP 客户端并管理子进程传输;
  • serde_json:处理工具清单与 LLM 响应中的 JSON 数据;
  • tokio:异步运行时(rt-multi-thread)。

第三步:运行示例并观察工具调用

cargo run

程序启动后依次完成以下动作:拉起 calculator MCP 服务器进程、获取工具列表、调用部署模型、由模型决定调用add工具。预期输出中应能看到工具调用日志(例如⚡ Calling tool: add)以及该次调用的计算结果。

源码级解析:main.rs 如何串起「MCP + LLM」链路

接下来按执行顺序拆解 main.rs 的实现,说明每一步与文档命令的对应关系。

1. 初始化用户消息与 OpenAI 客户端

let mut messages = vec![json!({"role": "user", "content": "What is the sum of 3 and 2?"})]; let endpoint = std::env::var("AZURE_OPENAI_ENDPOINT")?; let api_key = std::env::var("AZURE_OPENAI_API_KEY")?; let openai_client = Client::with_config( OpenAIConfig::new() .with_api_base(format!("{}/openai/v1", endpoint.trim_end_matches('/'))) .with_api_key(api_key), );

(见 main.rs)

要点:

  • messages保存完整的对话历史,起始是一条用户消息,后续会不断追加助手消息与工具结果消息;
  • endpoint末尾的多余斜杠会被trim_end_matches('/')去除,再拼上/openai/v1,得到 Azure OpenAI 兼容端点;
  • OpenAIConfig同时注入 API Key,用于后续completions().create_byot(...)请求鉴权。

2. 以子进程方式启动 MCP 服务器

let server_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .parent() // solution .and_then(|p| p.parent()) // 03-llm-client .and_then(|p| p.parent()) // 03-GettingStarted .map(|p| p.join("01-first-server/solution/rust")) .expect("Failed to resolve server directory path"); let mcp_client = () .serve( TokioChildProcess::new(Command::new("cargo").configure(|cmd| { cmd.arg("run").current_dir(server_dir); })) .map_err(RmcpError::transport_creation::<TokioChildProcess>)?, ) .await?;

(见 main.rs)

这里通过CARGO_MANIFEST_DIR向上回溯,定位到课程根目录下的01-first-server/solution/rust,然后用cargo run以子进程方式启动服务器,建立 stdio 传输。也就是说,cargo run一条命令会同时拉起服务器进程与客户端逻辑——服务器端add工具的实现见 01-first-server/solution/rust/src/main.rs,其核心定义如下:

#[tool(description = "Adds a and b")] async fn add( &self, Parameters(CalculatorRequest { a, b }): Parameters<CalculatorRequest>, ) -> String { (a + b).to_string() }

参数结构CalculatorRequest { a: f64, b: f64 }通过schemars::JsonSchema自动生成 JSON Schema,这正是后续format_tools所需inputSchema的来源。

3. 获取 MCP 工具清单

let tools = mcp_client.list_tools(Default::default()).await?;

(见 main.rs)

返回的ListToolsResult中包含服务器注册的add工具及其 JSON Schema。

4. 把 MCP 工具转换为 LLM 可理解的格式

MCP 的工具定义不能直接交给 LLM,需要转换成 OpenAI 兼容的 function calling 结构。format_tools完成这一转换:

async fn format_tools(tools: &ListToolsResult) -> Result<Vec<Value>, Box<dyn Error>> { let tools_json = serde_json::to_value(tools)?; let Some(tools_array) = tools_json.get("tools").and_then(|t| t.as_array()) else { return Ok(vec![]); }; let formatted_tools = tools_array .iter() .filter_map(|tool| { let name = tool.get("name")?.as_str()?; let description = tool.get("description")?.as_str()?; let schema = tool.get("inputSchema")?; Some(json!({ "type": "function", "function": { "name": name, "description": description, "parameters": { "type": "object", "properties": schema.get("properties").unwrap_or(&json!({})), "required": schema.get("required").unwrap_or(&json!([])) } } })) }) .collect(); Ok(formatted_tools) }

(见 main.rs)

转换规则要点:

  • 每个工具被映射为{"type": "function", "function": {...}}结构;
  • 从inputSchema中提取properties(参数属性)与required(必填参数)两个关键字段;
  • 缺失properties或required时分别回退为空对象与空数组,保证格式完整性。

5. 调用 LLM 并让其决定是否调用工具

async fn call_llm( client: &Client<OpenAIConfig>, messages: &[Value], tools: &ListToolsResult, ) -> Result<Value, Box<dyn Error>> { let model = std::env::var("AZURE_OPENAI_DEPLOYMENT") .unwrap_or_else(|_| "gpt-5.1".to_string()); let response = client .completions() .create_byot(json!({ "messages": messages, "model": model, "tools": format_tools(tools).await?, })) .await?; Ok(response) }

(见 main.rs)

请求体同时携带对话历史、部署名(model)与格式化后的tools数组,LLM 根据用户问题决定是否返回tool_calls。

6. 处理 LLM 响应并回传工具结果

process_llm_response是整个回路的核心:解析choices[0].message,若含content则打印模型文本;若含tool_calls则逐条执行:

if let Some(tool_calls) = message.get("tool_calls").and_then(|tc| tc.as_array()) { messages.push(message.clone()); // Add assistant message for tool_call in tool_calls { let (tool_id, name, args) = extract_tool_call_info(tool_call)?; println!("⚡ Calling tool: {}", name); let result = mcp_client .call_tool(CallToolRequestParam { name: name.into(), arguments: serde_json::from_str::<Value>(&args)?.as_object().cloned(), }) .await?; messages.push(json!({ "role": "tool", "tool_call_id": tool_id, "content": serde_json::to_string_pretty(&result)? })); } let response = call_llm(openai_client, messages, mcp_tools).await?; Box::pin(process_llm_response( &response, mcp_client, openai_client, mcp_tools, messages, )) .await?; }

(见 main.rs)

关键细节:

  • 工具调用日志⚡ Calling tool: add即文档中「Calling tool: add」期望输出的来源;
  • 每次工具调用后,以role: "tool"消息(携带tool_call_id与序列化结果)追加进对话历史;
  • 随后递归调用call_llm把工具结果交还给模型,让模型基于真实计算结果生成最终答复,实现完整的「思考—调用—再思考」循环。

extract_tool_call_info负责从工具调用对象中拆出id、function.name与function.arguments(JSON 字符串),供call_tool使用(见 main.rs)。

与课程整体脉络的对应关系

本 Rust 方案完整覆盖了课程(03-llm-client/README.md)中「创建带 LLM 的客户端」的全部四个步骤:连接服务器(子进程 stdio)、列出 capabilities(list_tools)、转换 schema(format_tools)、处理用户提示词(call_llm+process_llm_response)。与 TypeScript、Python、.NET、Java 等兄弟方案相比(各语言实现分别位于 solution 目录),Rust 方案的特点是:

  • 以rmcp直接内嵌启动服务器进程,无需预先单独启动;
  • 借助async-openai的byot特性,以原始 JSON 形式构建工具定义与请求体,不依赖框架层面的自动绑定;
  • 循环逻辑显式可见,方便理解 function calling 协议本身。

验证与排错要点

  • 运行cargo run后若未出现工具调用日志,首先确认AZURE_OPENAI_ENDPOINT、AZURE_OPENAI_API_KEY已正确导出,且AZURE_OPENAI_DEPLOYMENT对应的部署处于可用状态;
  • 若服务器路径解析失败,程序会输出Failed to resolve server directory path,请确认在 solution/rust 目录下运行(CARGO_MANIFEST_DIR的路径推导依赖该相对布局);
  • 修改 Cargo.toml 后需重新cargo build;
  • 与 calculator 服务器相关的工具定义、JSON Schema 生成细节可对照 01-first-server/solution/rust/src/main.rs 阅读,add工具的入参{a, b}即CalculatorRequest的字段。

至此,一个「自然语言提问 → LLM 决策 → MCP 工具执行 → 结果回传模型」的完整闭环已经在 Rust 中跑通,你可以在此基础上替换为任意 MCP 服务器与工具集,扩展自己的 LLM Agent 应用。

  • 教程
  • 文档
  • 人工智能

【免费下载链接】mcp-for-beginners

This 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
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 8:35:15

千问无门槛券申领通道,限新人,【新人5047】,日常消费都能抵

​ 先把千问这个APP弄在手机里&#xff0c;然后在对话框里输入10月专属口令内容(新人5047)后&#xff0c;会看到"待领取"按钮&#xff0c;按照页面指引完成账号绑定&#xff0c;成功后8面额的券就会自动发放到你的卡包中&#xff0c;整个流程也就完成了。快去试一试吧…

作者头像 李华
网站建设 2026/10/7 8:35:03

unet_DeepLabV3+模型训练道路车辆行人 目标分割检测数据集 训练识别道路目标分割检测数据集json 道路车辆行人公交车自行车路标等

使用unet/DeepLabV3对汽车道路分割检测数据集 道路分割 9000张 json coco 道路语义分割数据集 道路分割数据集 道路语义分割数据集 语义分割检测数据集 26类9000张 文章目录使用unet/DeepLabV3对汽车道路分割检测数据集 道路分割 9000张 json coco 道路语义分割数据集 道路分割…

作者头像 李华
网站建设 2026/10/7 8:34:01

免注册调用大漠插件:NetCore5.0 WinForm将DLL转为COM对象实践

简介&#xff1a;这是一份面向C# WinForm开发者的免注册调用大漠插件&#xff08;dm.dll&#xff09;资源包&#xff0c;基于.NET Core 5.0框架&#xff0c;适用于Windows 10环境。大漠插件提供找字、找图、截图、打字等图像识别与自动化能力&#xff0c;本资源可为自动化测试、…

作者头像 李华
网站建设 2026/10/7 8:33:50

投研AI判断一致性评估:LLM框架效应与基准率忽视的工程实践

1. 从一个反直觉的信贷场景说起1.1 为什么87%和13%这两个数字值得单独拎出来聊先把这个标题拆开看。87%未违约、13%违约&#xff0c;这是一个典型的信贷资产组合的标签分布。做过评分卡或者投研模型的人对这个比例不会陌生——绝大多数样本是“好人”&#xff0c;少数是“坏人”…

作者头像 李华