AI答题
28.5MB · 2026-04-24
在上一篇文章中,我们通过 Cursor / Trae 的配置文件加载 MCP Server,让 AI 编程助手拥有了查询用户信息的能力。这种方式非常适合交互式编程场景。
但如果你正在开发一个:
你会发现,你需要的不是让 Cursor 去调工具,而是让你自己写的 Node.js / Python 程序去调度大模型 + 调用 MCP 工具。
这时候,@langchain/mcp-adapters 就派上用场了。
结合我们之前的 readme.md 内容,先来一张关系图:
| 角色 | 说明 | 示例 |
|---|---|---|
| MCP Host | 发起对话、承载 Agent 逻辑的宿主程序 | Cursor、Trae、你写的 LangChain 应用 |
| MCP Client | 与 MCP Server 通信,管理工具列表 | MultiServerMCPClient |
| MCP Server | 提供具体工具 / 资源 / 提示词的执行容器 | my-mcp-server.mjs |
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ MCP Host │────▶│ MCP Client │────▶│ MCP Server │
│ (你的 LangChain │ │ (适配器) │ │ (工具容器) │
│ 应用程序) │◀────│ │◀────│ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
工作流程回顾:
initialize 请求,获取所有工具列表及详细 schemaToolMessage 形式喂回大模型,继续对话我们要在已有 MCP Server 的基础上,新建一个 LangChain 应用来调用它。
# 进入你的项目目录(例如 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 文件,填入你的大模型配置:
MODEL_NAME=gpt-4o-mini
OPENAI_API_KEY=sk-xxxxxx
OPENAI_BASE_URL=
创建一个新文件 agent.mjs,内容如下(已包含详细注释):
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 的核心逻辑:模型输出工具调用 → 执行工具 → 将结果喂回模型 → 继续,直到模型不再要求调用工具。 |
在终端执行:
node agent.mjs
你将看到类似如下的输出:
已加载 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 协议不仅支持 Tool(工具),还支持 Resource(资源) 和 Prompt(提示模板)。
在我们的 my-mcp-server.mjs 中,我们已经添加了一个资源:
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) 读取内容。这使得大模型可以在对话中主动查阅你提供的“知识库”。
从上面的实践可以看出,MultiServerMCPClient 通过一个统一的配置对象,就能启动并管理多个 MCP Server。
mcpServers 里加一项配置。这种 “配置即集成” 的模式,让 Agent 的能力扩展变得前所未有的简单。MCP 真正实现了大模型应用层的 Lego 化。
如果这篇文章对你有帮助,欢迎点赞、收藏。有任何疑问或想了解的内容,欢迎在评论区留言!