MCP 进阶实战:用 LangChain 将 MCP 工具集成到你的 AI Agent 程序
上一篇我们学习了如何编写 MCP Server 并在 Cursor 中使用。本文更进一步:如何把 MCP 工具直接嵌入你自己的 Node.js AI 应用,实现完全自主可控的 Agent 工作流。
一、为什么要把 MCP 集成到自己的程序里?
在上一篇文章中,我们通过 Cursor / Trae 的配置文件加载 MCP Server,让 AI 编程助手拥有了查询用户信息的能力。这种方式非常适合交互式编程场景。
但如果你正在开发一个:
- 后台自动化处理任务
- 企业微信 / 钉钉机器人
- 数据报表生成服务
- 内部运维 Agent
你会发现,你需要的不是让 Cursor 去调工具,而是让你自己写的 Node.js / Python 程序去调度大模型 + 调用 MCP 工具。
这时候,@langchain/mcp-adapters 就派上用场了。
二、核心组件:MCP 三者关系再梳理
结合我们之前的 readme.md 内容,先来一张关系图:
| 角色 | 说明 | 示例 |
|---|---|---|
| MCP Host | 发起对话、承载 Agent 逻辑的宿主程序 | Cursor、Trae、你写的 LangChain 应用 |
| MCP Client | 与 MCP Server 通信,管理工具列表 | MultiServerMCPClient |
| MCP Server | 提供具体工具 / 资源 / 提示词的执行容器 | my-mcp-server.mjs |
arduino
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ MCP Host │────▶│ MCP Client │────▶│ MCP Server │
│ (你的 LangChain │ │ (适配器) │ │ (工具容器) │
│ 应用程序) │◀────│ │◀────│ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
工作流程回顾:
- Host 启动时,Client 与 Server 建立连接(Stdio / HTTP)
- Client 向 Server 发起
initialize请求,获取所有工具列表及详细 schema - Host 收到用户任务,大模型判断需要调用某个工具
- Client 将调用请求转发给 Server
- Server 执行工具函数,返回结构化结果
- Host 将结果以
ToolMessage形式喂回大模型,继续对话
三、环境准备与依赖安装
我们要在已有 MCP Server 的基础上,新建一个 LangChain 应用来调用它。
bash
# 进入你的项目目录(例如 ai/agent/mini-cursor/mcp/)
cd mcp-tool
# 安装所需依赖
npm install @langchain/mcp-adapters @langchain/openai @langchain/core dotenv chalk zod
依赖说明:
@langchain/mcp-adapters:官方 MCP 与 LangChain 之间的桥接器@langchain/openai:调用 OpenAI 兼容接口的大模型chalk:美化终端输出(可选)dotenv:加载.env环境变量
在项目根目录创建 .env 文件,填入你的大模型配置:
env
MODEL_NAME=gpt-4o-mini
OPENAI_API_KEY=sk-xxxxxx
OPENAI_BASE_URL=https://api.openai.com/v1
四、编写 MCP Agent 主程序
创建一个新文件 agent.mjs,内容如下(已包含详细注释):
javascript
import 'dotenv/config';
import { MultiServerMCPClient } from '@langchain/mcp-adapters';
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage, ToolMessage } from '@langchain/core/messages';
import chalk from 'chalk';
// ---------- 1. 初始化大模型 ----------
const model = new ChatOpenAI({
modelName: process.env.MODEL_NAME,
apiKey: process.env.OPENAI_API_KEY,
configuration: {
baseURL: process.env.OPENAI_BASE_URL,
}
});
// ---------- 2. 创建 MCP 客户端,连接我们之前写的 Server ----------
const mcpClient = new MultiServerMCPClient({
mcpServers: {
'my-mcp-server': {
command: 'node',
args: ['C:/Users/29031/Desktop/workspace/lesson_zp/ai/agent/mini-cursor/mcp/mcp-tool/my-mcp-server.mjs'],
},
},
});
// ---------- 3. 获取 MCP Server 中的所有工具 ----------
const tools = await mcpClient.getTools();
console.log(chalk.green('✅ 已加载 MCP 工具列表:'), tools.map(t => t.name));
// 将工具绑定到大模型上(让模型知道它可以调用哪些工具)
const modelWithTools = model.bindTools(tools);
// ---------- 4. Agent 循环函数 ----------
async function runAgentWithTools(query, maxIterations = 30) {
const messages = [new HumanMessage(query)];
for (let i = 0; i < maxIterations; i++) {
console.log(chalk.bgGreen(`⏳ 第 ${i + 1} 轮思考...`));
// 调用大模型,它会返回一个 AI 消息,可能包含 tool_calls
const response = await modelWithTools.invoke(messages);
messages.push(response);
// 如果模型没有要求调用工具,说明对话结束,返回最终答案
if (!response.tool_calls || response.tool_calls.length === 0) {
console.log(chalk.bgWhite.black('✅ 最终回复:'));
return response.content;
}
// 模型要求调用工具,我们逐一执行
console.log(chalk.bgBlue(`🔧 检测到 ${response.tool_calls.length} 个工具调用:`));
for (const toolCall of response.tool_calls) {
console.log(chalk.blue(` → 调用工具:${toolCall.name},参数:${JSON.stringify(toolCall.args)}`));
// 从 MCP 工具列表中找到对应的工具实例
const foundTool = tools.find(t => t.name === toolCall.name);
if (foundTool) {
// 执行工具,获得结果
const toolResult = await foundTool.invoke(toolCall.args);
// 将工具执行结果包装成 ToolMessage,加回对话历史
messages.push(new ToolMessage({
content: toolResult,
tool_call_id: toolCall.id
}));
console.log(chalk.gray(` ← 工具返回:${toolResult.substring(0, 100)}...`));
} else {
console.log(chalk.red(` ✗ 未找到工具 ${toolCall.name}`));
}
}
}
return '已达到最大迭代次数,对话终止。';
}
// ---------- 5. 执行一个实际任务 ----------
const result = await runAgentWithTools("查一下用户 002 的信息");
console.log(chalk.yellow('\n🎉 最终结果:\n'), result);
// ---------- 6. 关闭 MCP 客户端连接 ----------
await mcpClient.close();
代码关键点解析
| 代码段 | 作用 |
|---|---|
MultiServerMCPClient |
管理多个 MCP Server 的连接,内部会自动处理 Stdio 子进程的启动与通信。 |
mcpClient.getTools() |
获取所有 Server 提供的工具列表,这些工具的 schema 已经自动转换为 LangChain 能识别的格式。 |
model.bindTools(tools) |
将工具列表告知大模型,后续对话中模型会在需要时返回 tool_calls。 |
ToolMessage |
LangChain 中专门用于携带工具执行结果的消息类型,必须与对应的 tool_call_id 绑定。 |
| 循环调用 | Agent 的核心逻辑:模型输出工具调用 → 执行工具 → 将结果喂回模型 → 继续,直到模型不再要求调用工具。 |
五、运行测试
在终端执行:
bash
node agent.mjs
你将看到类似如下的输出:
sql
✅ 已加载 MCP 工具列表: [ 'query-user' ]
⏳ 第 1 轮思考...
🔧 检测到 1 个工具调用:
→ 调用工具:query-user,参数:{"userId":"002"}
← 工具返回:用户信息:
- ID: 002
- 姓名: 李四
- 邮箱: lisi@example.com
- 角色: user...
⏳ 第 2 轮思考...
✅ 最终回复:
用户 002 的信息如下:
姓名:李四,邮箱:lisi@example.com,角色:user。
🎉 最终结果:
用户 002 的信息如下:
姓名:李四,邮箱:lisi@example.com,角色:user。
至此,你已经成功将 MCP 工具无缝集成到自己的 LangChain 程序中!
如图

六、进阶:为 MCP Server 添加"资源"(Resource)
MCP 协议不仅支持 Tool(工具) ,还支持 Resource(资源) 和 Prompt(提示模板)。
工具:执行动作(查询、写入、计算)
资源:暴露静态数据或文件内容(文档、配置、数据库表结构)
提示模板:预设的对话模板
在我们的 my-mcp-server.mjs 中,我们已经添加了一个资源:
javascript
server.registerResource('使用指南', 'docs://guide', {
description: 'MCP Server 使用指南',
mimeType: 'text/plain',
},
async () => {
return {
contents: [{
uri: 'docs://guide',
mimeType: 'text/plain',
text: `MCP Server 使用指南...`,
}]
};
});
资源的使用方式:
在 LangChain 中,你可以通过 mcpClient.getResources() 获取所有资源,然后通过 mcpClient.readResource(uri) 读取内容。这使得大模型可以在对话中主动查阅你提供的"知识库"。
七、为什么说 MCP 是"可拔插"的工具生态?
从上面的实践可以看出,MultiServerMCPClient 通过一个统一的配置对象,就能启动并管理多个 MCP Server。
- 你想添加一个 Python 写的图像处理 MCP Server ?只需在
mcpServers里加一项配置。 - 你想调用公司内部 Java 写的工单查询 MCP 服务?配置 HTTP 地址即可。
- 你想临时禁用某个工具?注释掉对应配置即可。
这种 "配置即集成" 的模式,让 Agent 的能力扩展变得前所未有的简单。MCP 真正实现了大模型应用层的 Lego 化。
如果这篇文章对你有帮助,欢迎点赞、收藏。有任何疑问或想了解的内容,欢迎在评论区留言!