Agent 智能体开发实战 · 第七课:Remote MCP —— 从本地工具到远程服务
上节回顾:第六课我们用 MCP 协议让 Agent 跨进程调用本地工具——通过 stdio 启动自己的 MCP Server 子进程。
本课聚焦:MCP 不止 stdio 本地调用,还支持HTTP 远程调用和npm 包直接拉起。本课让 Agent 同时连接高德地图(远程)、Chrome DevTools(远程调试)、FileSystem(npm 包)和自己的 my-mcp-server(本地)——四个不同来源的 MCP Server 协同工作,完成一个真实的AI 工作流。
📖 本课目录
- 一、从本地 MCP 到远程 MCP
- 二、四种 MCP Server 连接方式
- 三、核心改造:兼容多种 Tool 返回格式
- 四、完整代码拆解
- 五、AI 工作流实战:查酒店 → 开浏览器 → 保存结果
- 六、本课学习总结
一、从本地 MCP 到远程 MCP
🔙 第六课的 MCP 架构
Agent ──stdio──► my-mcp-server(本地子进程) └─ query-user Tool第六课只用了 stdio 本地通信,MCP Server 是自己写的 Node.js 脚本。
🚀 本课的 MCP 架构
┌─────────────┐ │ amap-mcp │ 高德地图(HTTP 远程) │ URL 连接 │ → 位置查询、路线规划 └─────────────┘ │ HTTP ┌─────┴───────┐ Agent ──stdio──►│ my-mcp-server│ 本地子进程(复用第六课) └─────────────┘ │ stdio ┌─────┴───────┐ │ filesystem │ npm 包(npx 拉起) │ 读写文件 │ → @modelcontextprotocol/server-filesystem └─────────────┘ │ stdio ┌─────┴───────┐ │ chrome-devtools│ npm 包(npx 拉起) │ 操控浏览器 │ → 打开 Tab、点击、截图 └─────────────┘🎯核心突破:一个 Agent 同时连接 4 个 MCP Server——2 个远程/包、1 个本地自己写的、1 个 npm 的——工具从"自己造"变成了"接入现成的生态"。
二、四种 MCP Server 连接方式
MultiServerMCPClient的配置字典中,每个 Server 用 key 标识,value 可以是两种格式:
| 连接方式 | 配置格式 | 原理 |
|---|---|---|
| 🔗HTTP 远程 | { url: "https://..." } | 直接 HTTP 请求,不需要启动子进程 |
| 🖥️stdio 本地 | { command: "...", args: [...] } | 同第六课,spawn 子进程,stdin/stdout 通信 |
基于这个配置,来看本课连接的 4 个 Server:
1. 高德地图 MCP(HTTP 远程)
'amap-mcp':{url:"https://mcp.amap.com/mcp?key=f0259c6adf20e8543e0773baxxxxx"}URL 里带 API key(需要去 developer.amap.com 申请),连接后可以调用位置查询、路线规划等工具。这是远程 MCP——不需要本地启动任何进程。
2. 自己的 MCP Server(stdio 本地)
'my-mcp-server':{command:'node',args:['C:/Users/yihao/Desktop/workspace/yh_ai/ai/ai/agent_in_action/mcp-demo/src/my-mcp-server.mjs']}复用第六课写的query-user工具,通过node+ 脚本路径拉起。
3. FileSystem MCP(npm 包,npx 拉起)
'filesystem':{command:'npx',args:['-y','@modelcontextprotocol/server-filesystem',// 允许访问的文件夹,可以配置多个,用空格隔开'C:/Users/yihao/Desktop/workspace/yh_ai/ai/ai/agent_in_action/remote-mcp']}| 要点 | 说明 |
|---|---|
command: 'npx' | 不是 node,是 npx——直接运行 npm 包 |
-y | 自动确认,跳过安装确认提示 |
@modelcontextprotocol/server-filesystem | npm 上发布的 MCP Server 包 |
| 文件夹路径参数 | 传给 Server,限制它只能操作这个文件夹 |
💡不用自己写工具了。FileSystem 是社区已有的 MCP Server,提供
read_file、write_file、list_directory等——跟第四课的all-tools.mjs功能一样,但它是独立进程、独立语言实现。
4. Chrome DevTools MCP(npm 包,npx 拉起)
// Chrome‑DevTools MCP,默认连接本地打开的Chrome// (开启远程调试:chrome --remote-debugging-port=9222)'chrome-devtools':{command:'npx',args:['-y','chrome-devtools-mcp@latest',]}连接已打开的 Chrome 浏览器(需以chrome --remote-debugging-port=9222启动),提供打开页面、点击元素、截图、修改标题等浏览器操控能力。
🔑 四种 Server 对比
| Server | 连接方式 | 来源 | 提供的能力 |
|---|---|---|---|
| 🗺️ amap-mcp | HTTP URL | 高德开放平台 | 位置查询、路线规划 |
| 🏠 my-mcp-server | stdio (node) | 自己写的(第六课) | 用户信息查询 |
| 📁 filesystem | stdio (npx) | npm 包 | 读写文件、列目录 |
| 🌐 chrome-devtools | stdio (npx) | npm 包 | 操控浏览器 Tab、截图 |
三、核心改造:兼容多种 Tool 返回格式
第六课的 Tool 返回格式是固定的——自己写的 Server,返回的content格式可控。但远程 MCP Server(高德、Chrome DevTools 等)的返回格式五花八门:
| 返回类型 | 示例 | 来源 |
|---|---|---|
| 纯字符串 | "查询结果:xxx" | 多数 stdio Server |
| content 数组 | [{ type: 'text', text: '...' }] | 标准 MCP 返回 |
| 对象 | { text: '...', result: '...' } | 某些第三方 Server |
本课的invoke部分做了格式兼容处理:
letcontentStr;try{consttoolResult=awaitselectedTool.invoke(tool_call.args);// mcp tool 返回一般是字符串// 还可能是 content 数组if(typeoftoolResult==='string'){contentStr=toolResult;}elseif(Array.isArray(toolResult)){contentStr=toolResult.map(item=>item.text||JSON.stringify(item)).join('\n');}elseif(toolResult&&typeoftoolResult==='object'){contentStr=toolResult.text||toolResult.result||JSON.stringify(toolResult);}else{contentStr=String(toolResult);}}catch(err){console.log(chalk.red(`工具调用失败${tool_call.name}:${err.message}`));contentStr=`Error:${err.message}`;}messages.push(newToolMessage({tool_call_id:tool_call.id,content:contentStr}));| 处理分支 | 逻辑 |
|---|---|
typeof === 'string' | 直接使用 |
Array.isArray | 提取每个元素的.text,拼接 |
typeof === 'object' | 优先取.text→.result→ JSON 序列化 |
| 兜底 | String()强制转字符串 |
| try-catch | 工具调用失败也不崩溃,返回错误信息 |
🎯 这是工程化的关键细节——你控制不了远程 Server 的返回格式,必须做兼容处理。
四、完整代码拆解
第1步:导入 + LLM 初始化
import'dotenv/config';import{MultiServerMCPClient}from'@langchain/mcp-adapters';import{ChatOpenAI}from'@langchain/openai';importchalkfrom'chalk';importfsfrom'node:fs';importpathfrom'node:path';import{HumanMessage,SystemMessage,ToolMessage}from'@langchain/core/messages';constmodel=newChatOpenAI({modelName:'deepseek-v4-pro',apiKey:process.env.DEEPSEEK_API_KEY,temperature:0,configuration:{baseURL:'https://api.deepseek.com/v1',},});第2步:配置 4 个 MCP Server
constmcpClient=newMultiServerMCPClient({mcpServers:{'amap-mcp':{url:"https://mcp.amap.com/mcp?key=f0259c6adf20e8543e0773baxxxxxx"},'my-mcp-server':{command:'node',args:['C:/Users/yihao/Desktop/workspace/yh_ai/ai/ai/agent_in_action/mcp-demo/src/my-mcp-server.mjs']},'filesystem':{command:'npx',args:['-y','@modelcontextprotocol/server-filesystem',// 允许访问的文件夹,可以配置多个,用空格隔开'C:/Users/yihao/Desktop/workspace/yh_ai/ai/ai/agent_in_action/remote-mcp']},// Chrome‑DevTools MCP,默认连接本地打开的Chrome// (开启远程调试:chrome --remote-debugging-port=9222)'chrome-devtools':{command:'npx',args:['-y','chrome-devtools-mcp@latest',]}}})🎯 4 个 Server 配置在一个对象里,
MultiServerMCPClient统一管理连接、获取工具、关闭连接。Agent 不用知道哪个 Tool 来自哪个 Server。
第3步:获取工具 + bindTools
consttools=awaitmcpClient.getTools();console.log(tools);// 打印所有可用工具,看看4个Server总共提供了多少constmodelWithTools=model.bindTools(tools);第4步:Agent Loop
asyncfunctionrunAgentWithTools(query,maxIterations=30){constmessages=[newHumanMessage(query)];for(leti=0;i<maxIterations;i++){console.log(chalk.bgGreen(`第${i+1}轮迭代`));constresponse=awaitmodelWithTools.invoke(messages);messages.push(response);if(!response.tool_calls||response.tool_calls.length===0){console.log(chalk.green(`AI 回答:${response.content}`));returnresponse.content;}console.log(chalk.bgBlue(`工具调用${response.tool_calls.map(t=>t.name).join(', ')}`))for(consttool_callofresponse.tool_calls){constselectedTool=tools.find(t=>t.name===tool_call.name);if(!selectedTool){console.log(chalk.red(`未找到工具:${tool_call.name}`));continue;}// 格式兼容处理(见第三节)// ...messages.push(newToolMessage({tool_call_id:tool_call.id,content:contentStr}));}}// 最后一次消息是AI的回复returnmessages[messages.length-1].content;}第5步:启动 + 保存结果
constresult=awaitrunAgentWithTools("北京南站附近的酒店,最近的 3 个酒店,拿到酒店图片,打开浏览器,展示每个酒店的图片,每个 tab 一个 url 展示,并且在把那个页面标题改为酒店名");// 保存结果到 md 文件constoutputDir=path.resolve(import.meta.dirname,'../output');fs.mkdirSync(outputDir,{recursive:true});constoutputPath=path.join(outputDir,`hotel-result-${Date.now()}.md`);fs.writeFileSync(outputPath,`# 北京南站附近酒店查询结果\n\n${result}\n`);console.log(chalk.cyan(`\n✅ 结果已保存到:${outputPath}`));📂 运行一次就生成一个带时间戳的
.md文件在remote-mcp/output/,记录完整结果。
五、AI 工作流实战:查酒店 → 开浏览器 → 保存结果
🎬 任务拆解
Agent 收到一个复杂任务:“北京南站附近的酒店,最近的 3 个酒店,拿到酒店图片,展示在浏览器,图片标题改为酒店名”。
这不是一个单一操作,而是一个多步骤工作流:
┌─────────────────────────────────────────────────┐ │ 🗺️ Step 1: amap-mcp │ │ 搜索"北京南站"坐标 → 查询附近酒店 → │ │ 返回酒店列表(名称、距离、评分) │ └─────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────┐ │ 🗺️ Step 2: amap-mcp │ │ 根据酒店名称搜索图片 URL │ │ 拿到每个酒店的图片链接 │ └─────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────┐ │ 🌐 Step 3: chrome-devtools │ │ 为每个酒店开一个 Chrome Tab │ │ 修改每个 Tab 的标题为酒店名 │ │ 设置 page title 方便用户识别 │ └─────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────┐ │ 📁 Step 4: filesystem │ │ 将结果写入 output/hotel-result-xxx.md │ │ 保存完整的查询结果 │ └─────────────────────────────────────────────────┘🎯 实际产出
运行node src/mcp-test.mjs后,Agent 自动完成了所有步骤,在output/生成了结果文件:
# 北京南站附近酒店查询结果 | 排名 | 酒店名称 | 步行距离 | 评分 | Tab | |------|----------|----------|------|-----| | 🥇 | **北京永鑫宾馆** | **228米** | ⭐4.2 | Tab 2 | | 🥈 | **轻羽精品酒店(北京南站北广场店)** | **257米** | ⭐3.2 | Tab 3 | | 🥉 | **米家青年酒店(北京南站店)** | **304米** | ⭐3.6 | Tab 4 |🤯 人工只做了一件事——描述需求。Agent 自动调度 3 个 MCP Server(高德、Chrome DevTools、FileSystem)完成了整个工作流。
🧩 MCP 生态的核心价值
有了 MCP 协议后:
以前:想用高德地图?→ 读 API 文档 → 写 fetch 调用 → 自己封 Tool 现在:高德提供了 MCP Server → url 配上去 → getTools() → 直接用 以前:想操控浏览器?→ 学 Puppeteer/Playwright → 写脚本 现在:chrome-devtools-mcp → npx 拉起 → getTools() → 直接用 以前:跨团队共享工具?→ 写成 npm 包 → 对方 import → 还是得同语言 现在:写成 MCP Server → 任何语言 → 任何项目 → getTools() → 直接用🎯MCP 把 Tool 从"自己造"变成了"接入生态"——任何人都可以开发基于这个协议的 MCP Server,然后直接复用。高德地图、Chrome DevTools、FileSystem 这些不是我们自己写的,但它们提供的 Tool 跟我们自己写的一样用。
六、本课学习总结
🧠 知识点
📋 Remote MCP · 知识点 │ ├── 🚀 从本地到远程 │ ├── 第六课:只有 stdio 本地 │ └── 本课:HTTP 远程 + npx 包 + stdio 本地 混合 │ ├── 🔗 四种 MCP Server 连接方式 │ ├── 🗺️ amap-mcp(HTTP URL) │ │ └── 高德地图 API,纯远程调用,不需本地进程 │ ├── 🏠 my-mcp-server(stdio · node) │ │ └── 复用第六课自己的 Server │ ├── 📁 filesystem(stdio · npx) │ │ └── npm 包 @modelcontextprotocol/server-filesystem │ └── 🌐 chrome-devtools(stdio · npx) │ └── npm 包 chrome-devtools-mcp@latest │ ├── 🔧 格式兼容处理 │ ├── string → 直接用 │ ├── Array → 提取 .text 拼接 │ ├── object → .text → .result → JSON │ ├── 兜底 → String() │ └── try-catch → 不崩溃 │ ├── 🔄 Agent Loop │ ├── for + ToolMessage(同第五课) │ ├── 多 Server 统一管理 │ └── tools 来自 4 个不同来源 │ ├── 💾 结果持久化 │ ├── fs.mkdirSync + recursive │ ├── fs.writeFileSync │ ├── 带时间戳的文件名 │ └── 保存在 output/ 目录 │ └── 🧩 MCP 生态价值 ├── 工具从"自己造" → "接入生态" ├── 任何人开发 → 任何人复用 ├── 高德/Chrome/FileSystem 即插即用 └── Agent 的能力 = 接入的 MCP Server 数量✅ 知识清单
| 编号 | 掌握项 | 核心要点 |
|---|---|---|
| 1 | HTTP 远程 MCP | { url: "https://..." }连接远程 Server,不需本地进程 |
| 2 | npx 拉起 MCP | { command: 'npx', args: ['-y', 'package-name'] } |
| 3 | 混合连接 | 一个MultiServerMCPClient同时连 HTTP + stdio + npx |
| 4 | 返回格式兼容 | string / array / object 三种类型都要处理 |
| 5 | 高德 MCP 接入 | URL 带 API Key,提供位置查询、路线规划 |
| 6 | Chrome DevTools MCP | 操控浏览器:打开 Tab、截图、修改标题 |
| 7 | FileSystem MCP | npm 包读写文件,限制访问目录 |
| 8 | AI 工作流 | Agent 自动编排多个 Server 完成多步骤任务 |
| 9 | 结果保存 | fs.writeFileSync+ 时间戳文件名,持久化到 output/ |
| 10 | MCP 生态 | 工具不再自己写,接入现成 MCP Server 即可 |
📊 基础 Agent → MCP 优化 → Remote MCP
| 维度 | 前五课 · 基础 Agent | 第六课 · MCP 本地 | 第七课 · Remote MCP |
|---|---|---|---|
| 工具来源 | 本地定义 / import | MCP stdio 动态获取 | HTTP + npx + stdio 混合 |
| Server 类型 | 无 | 自己写的 Node 脚本 | 自己写的 + npm 包 + 远程服务 |
| 连接方式 | 无 | stdio | stdio + HTTP URL |
| 工具数量 | 1 → 4 | N(本地 Server 提供) | N×M(M 个 Server 各提供 N 个) |
| 生态复用 | ❌ | ❌ | ✅ 高德/Chrome/FileSystem 即插即用 |
| 返回格式 | 固定字符串 | 自己控制的格式 | 需兼容 string/array/object |
🎯本课定位:第六课证明了 MCP 能让工具脱离 Agent 进程。本课进一步证明——工具不需要自己写。高德地图、Chrome DevTools、FileSystem 这些成熟的 MCP Server 直接接入,Agent 的能力不再受限于你写工具的能力,而取决于你接入了多少个 MCP Server。
📅 2026-07-13 | 🏷️ Agent · MCP · Remote MCP · HTTP · AI工作流 · 第七课
