上节我们学了 MCP:自己实现了一个 MCP Server,然后在 Cursor 或者 LangChain 里连上这个 Server,就可以用里面的 tools 了。
它本质上还是 Tool,只不过包了一层进程,可以通过 stdio 和 http 来访问。
有了这一层协议之后,有个巨大的好处:任何人都可以开发基于这个协议的 MCP Server,然后我们可以直接复用! 比如上节我们写的那个 MCP Server 就可以被别人用。
这节我们用一下别人写好的 MCP Server,感受下 MCP 有多爽!用这三个:
- 高德 MCP:做位置查询、路线规划等
- Chrome DevTools MCP:控制浏览器,打开关闭页面、点击元素、截图等
- FileSystem MCP:读写文件、创建目录等
高德 MCP:先申请 API Key
首先是高德 MCP,我们需要先获取一个 API Key:
- 地址:
https://developer.amap.com/ - 创建应用,然后创建一个 API Key,类型选 Web 服务 就行
然后先在 Cursor 里测试下这个 MCP 服务是否可用。配好之后,就可以查到这个 MCP Server 里的一堆 tool 了。
记得我们说过 MCP 有两种接入方式么?这就是 http 的接入方式:
"amap-maps-streamableHTTP": {
"url": "https://mcp.amap.com/mcp?key=你的 api key"
}当然,高德也支持 stdio 的本地进程接入方式,这样写:
"amap-maps": {
"command": "npx",
"args": [
"-y",
"@amap/amap-maps-mcp-server"
],
"env": {
"AMAP_MAPS_API_KEY": "你的 api key"
}
}就是用 npx 跑一个 npm 包,会创建一个支持 stdio 连接的进程,然后连上其中的 MCP Server 就好了。这个 MCP Server 里肯定封装了和高德服务端的通信,本质上是一样的。
顺带说一句:这个技能完全可以写进简历——"我开发了一个 MCP Server 的 npm 包,包含 xxx tool,支持 stdio 访问,可以在 Cursor 或 LangChain 里用 npx 执行来连上这个 MCP Server"。面试官一看就知道,这个人是真懂 MCP 的,而且还有实践经验。
在 LangChain 里用高德 MCP
说回正题,在 tool-test 项目里创建 src/mcp-test.mjs:
import 'dotenv/config';
import { MultiServerMCPClient } from '@langchain/mcp-adapters';
import { ChatOpenAI } from '@langchain/openai';
import chalk from 'chalk';
import { HumanMessage, SystemMessage, ToolMessage } from '@langchain/core/messages';
const model = new ChatOpenAI({
modelName: "qwen-plus",
apiKey: process.env.OPENAI_API_KEY,
configuration: {
baseURL: process.env.OPENAI_BASE_URL,
},
});
const mcpClient = new MultiServerMCPClient({
mcpServers: {
'my-mcp-server': {
command: "node",
args: [
"/Users/guang/code/tool-test/src/my-mcp-server.mjs"
]
},
"amap-maps-streamableHTTP": {
"url": "https://mcp.amap.com/mcp?key=" + process.env.AMAP_MAPS_API_KEY
},
}
});
const tools = await mcpClient.getTools();
const modelWithTools = model.bindTools(tools);
async function runAgentWithTools(query, maxIterations = 30) {
const messages = [
new HumanMessage(query)
];
for (let i = 0; i < maxIterations; i++) {
console.log(chalk.bgGreen(`⏳ 正在等待 AI 思考...`));
const response = await modelWithTools.invoke(messages);
messages.push(response);
// 检查是否有工具调用
if (!response.tool_calls || response.tool_calls.length === 0) {
console.log(`\n✨ AI 最终回复:\n${response.content}\n`);
return response.content;
}
console.log(chalk.bgBlue(`🔍 检测到 ${response.tool_calls.length} 个工具调用`));
console.log(chalk.bgBlue(`🔍 工具调用: ${response.tool_calls.map(t => t.name).join(', ')}`));
// 执行工具调用
for (const toolCall of response.tool_calls) {
const foundTool = tools.find(t => t.name === toolCall.name);
if (foundTool) {
const toolResult = await foundTool.invoke(toolCall.args);
messages.push(new ToolMessage({
content: toolResult,
tool_call_id: toolCall.id,
}));
}
}
}
return messages[messages.length - 1].content;
}
await runAgentWithTools("北京南站附近的酒店,以及去的路线");
await mcpClient.close();MCP Client 的代码和上节一样,用 @langchain/mcp-adapters:拿到其中的 tools 绑定给 model,然后调用 model,如果有 tool_calls 就调用下,把工具调用结果封装为 ToolMessage 传给大模型继续处理。这里的高德 API Key 同样放到了 .env 里。
先注释掉高德 MCP Server 跑一下:可以看到,大模型没法处理地理位置信息,让你用地图。
启用高德 MCP Server 后再跑:大模型就可以调用高德 MCP 里的 tool,给出酒店位置和路线了!
这就是 MCP 的好处——直接复用别人写好的 Tool。
FileSystem MCP:文件读写也不用自己写了
然后文件读写、创建目录这种,也不用自己写 Tool,可以用现成的 MCP——MCP 官方维护的一个 MCP Server:
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
...(process.env.ALLOWED_PATHS.split(',') || '')
]
}后面是可访问的目录,我们配在 .env 里,逗号分隔。
可以先在 Cursor 里配置下这个 MCP 服务,看下有哪些 tool:有文件读写、目录创建、文件移动等 tool。配上这个 MCP,大模型就有文件读写能力了。
不过这里有个坑注意下:一般我们写 Tool 都是直接返回字符串,但是 FileSystem MCP 封装的这些 Tool 返回的是对象,有 text 属性,所以要处理下:
// 确保 content 是字符串类型
let contentStr;
if (typeof toolResult === 'string') {
contentStr = toolResult;
} else if (toolResult && toolResult.text) {
// 如果返回对象有 text 字段,优先使用
contentStr = toolResult.text;
}
messages.push(new ToolMessage({
content: contentStr,
tool_call_id: toolCall.id,
}));改下提示词跑跑试试:
await runAgentWithTools("北京南站附近的 5 个酒店,以及去的路线,路线规划生成文档保存到 /Users/guang/Documents/...");可以看到,大模型首先调用高德 MCP 拿到了附近的酒店位置,然后规划了路线,最后调用 FileSystem MCP 写入了文件。
直接复用别人的 MCP,完全不用自己写。你自己写的 Tool 想给别人用,也可以封装成 MCP,最好发个 npm 包,这样还可以写到简历上,让面试官用。
Chrome DevTools MCP:浏览器自动化
最后用一下 Chrome DevTools 的 MCP,它可以用来做浏览器自动化,比如打开页面、点击元素、截图等。
在 Cursor 里配置:
"chrome-devtools": {
"command": "npx",
"args": [
"-y",
"chrome-devtools-mcp@latest"
]
}可以看到很多工具。在 LangChain 里调用下,改下提示词:
await runAgentWithTools("北京南站附近的酒店,最近的 3 个酒店,拿到酒店图片,打开浏览器,展示每个酒店的图片");运行后可以看到:搜到了北京南站最近的 3 个酒店,并且浏览器打开了酒店图片。
只要配好 MCP,大模型就可以直接调用里面的 tools 了。
常见问题
模型没有调用工具,而是"描述它会做什么"就结束了? 因为 messages 里只有一条 HumanMessage,没有 SystemMessage 引导模型主动调用工具。有些模型(特别是 qwen 系列)需要明确的系统提示才会主动执行工具调用。加一条 SystemMessage 即可,比如:"你是一个智能助手,拥有多种工具能力。当用户提出需求时,你必须直接调用工具来完成任务,而不是描述你会怎么做。"
高德接口报 QPS_HAS_EXCEEDED_THE_LIMIT? 高德个人版 API 有 QPS 限制(3 次/秒)。可以在系统提示词里设计分批调用规则(每批最多 3 个,等上一批返回后再发起下一批),或者在工具调用时添加延迟。
浏览器无法直接控制? 把其他浏览器关掉再运行。这与 userDataDir 有关,一个用户目录只能跑一个浏览器实例。
打开浏览器后想让它不自动关闭? 注释掉 await mcpClient.close(),不关闭 MCP 进程浏览器就不会自动关掉;或者改配置加 --browserUrl 参数指向本地已存在的 Chrome。
去哪找现成的 MCP? 去专门做相关业务的平台,比如天气预报找"墨迹天气",股票数据找"同花顺",一般都会提供官方 MCP Server。
FileSystem MCP 报 Invalid input: expected "object"? 这是 @langchain/mcp-adapters 加载 MCP 工具时校验 inputSchema.type 必须是字符串 "object",而 server-filesystem 返回的 schema 里 type 缺失或为数组,两者兼容性问题。等修复,或换其他实现规范的 filesystem MCP 服务。
MCP 太多会不会费 token? 会,执行时会把所有 MCP 的 tools 都添加到内存中。可以显式指定使用哪个 MCP(在 prompt 里点名),比全量加载更省 token。
Windows 下路径注意:ALLOWED_PATHS 后面用从根目录开始的绝对路径(如 C:\...),给大模型的提示词里也是。
总结
这节我们使用了高德、FileSystem、Chrome DevTools 的 MCP,用它们组合来实现了一些功能。这些 MCP Server 有的是 stdio 本地进程调用,有的是 http 远程进程调用。
MCP 的一大好处就是:别人开发好的,可以直接用。
你全程不需要知道怎么用高德的 API 查询位置、路线,不需要知道怎么用 CDP 协议控制浏览器。你只需要把这些 MCP 给到 AI,让它自己去调用——大模型会自己读取 tool 描述来传入参数调用。
是不是特别爽!
