Agent 智能体开发实战 · 第七课:Remote MCP —— 从本地工具到远程服务

📅 2026/7/14 1:09:50
Agent 智能体开发实战 · 第七课:Remote MCP —— 从本地工具到远程服务
上节回顾第六课我们用 MCP 协议让 Agent 跨进程调用本地工具——通过 stdio 启动自己的 MCP Server 子进程。本课聚焦MCP 不止 stdio 本地调用还支持HTTP 远程调用和npm 包直接拉起。本课让 Agent 同时连接高德地图远程、Chrome DevTools远程调试、FileSystemnpm 包和自己的 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 个 Server1. 高德地图 MCPHTTP 远程amap-mcp:{url:https://mcp.amap.com/mcp?keyf0259c6adf20e8543e0773baxxxxx}URL 里带 API key需要去 developer.amap.com 申请连接后可以调用位置查询、路线规划等工具。这是远程 MCP——不需要本地启动任何进程。2. 自己的 MCP Serverstdio 本地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 MCPnpm 包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-filesystemnpm 上发布的 MCP Server 包文件夹路径参数传给 Server限制它只能操作这个文件夹不用自己写工具了。FileSystem 是社区已有的 MCP Server提供read_file、write_file、list_directory等——跟第四课的all-tools.mjs功能一样但它是独立进程、独立语言实现。4. Chrome DevTools MCPnpm 包npx 拉起// Chrome‑DevTools MCP默认连接本地打开的Chrome// 开启远程调试chrome --remote-debugging-port9222chrome-devtools:{command:npx,args:[-y,chrome-devtools-mcplatest,]}连接已打开的 Chrome 浏览器需以chrome --remote-debugging-port9222启动提供打开页面、点击元素、截图、修改标题等浏览器操控能力。 四种 Server 对比Server连接方式来源提供的能力️ amap-mcpHTTP URL高德开放平台位置查询、路线规划 my-mcp-serverstdio (node)自己写的第六课用户信息查询 filesystemstdio (npx)npm 包读写文件、列目录 chrome-devtoolsstdio (npx)npm 包操控浏览器 Tab、截图三、核心改造兼容多种 Tool 返回格式第六课的 Tool 返回格式是固定的——自己写的 Server返回的content格式可控。但远程 MCP Server高德、Chrome DevTools 等的返回格式五花八门返回类型示例来源纯字符串查询结果xxx多数 stdio Servercontent 数组[{ type: text, text: ... }]标准 MCP 返回对象{ text: ..., result: ... }某些第三方 Server本课的invoke部分做了格式兼容处理letcontentStr;try{consttoolResultawaitselectedTool.invoke(tool_call.args);// mcp tool 返回一般是字符串// 还可能是 content 数组if(typeoftoolResultstring){contentStrtoolResult;}elseif(Array.isArray(toolResult)){contentStrtoolResult.map(itemitem.text||JSON.stringify(item)).join(\n);}elseif(toolResulttypeoftoolResultobject){contentStrtoolResult.text||toolResult.result||JSON.stringify(toolResult);}else{contentStrString(toolResult);}}catch(err){console.log(chalk.red(工具调用失败${tool_call.name}:${err.message}));contentStrError:${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 初始化importdotenv/config;import{MultiServerMCPClient}fromlangchain/mcp-adapters;import{ChatOpenAI}fromlangchain/openai;importchalkfromchalk;importfsfromnode:fs;importpathfromnode:path;import{HumanMessage,SystemMessage,ToolMessage}fromlangchain/core/messages;constmodelnewChatOpenAI({modelName:deepseek-v4-pro,apiKey:process.env.DEEPSEEK_API_KEY,temperature:0,configuration:{baseURL:https://api.deepseek.com/v1,},});第2步配置 4 个 MCP ServerconstmcpClientnewMultiServerMCPClient({mcpServers:{amap-mcp:{url:https://mcp.amap.com/mcp?keyf0259c6adf20e8543e0773baxxxxxx},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-port9222chrome-devtools:{command:npx,args:[-y,chrome-devtools-mcplatest,]}}}) 4 个 Server 配置在一个对象里MultiServerMCPClient统一管理连接、获取工具、关闭连接。Agent 不用知道哪个 Tool 来自哪个 Server。第3步获取工具 bindToolsconsttoolsawaitmcpClient.getTools();console.log(tools);// 打印所有可用工具看看4个Server总共提供了多少constmodelWithToolsmodel.bindTools(tools);第4步Agent LoopasyncfunctionrunAgentWithTools(query,maxIterations30){constmessages[newHumanMessage(query)];for(leti0;imaxIterations;i){console.log(chalk.bgGreen(第${i1}轮迭代));constresponseawaitmodelWithTools.invoke(messages);messages.push(response);if(!response.tool_calls||response.tool_calls.length0){console.log(chalk.green(AI 回答${response.content}));returnresponse.content;}console.log(chalk.bgBlue(工具调用${response.tool_calls.map(tt.name).join(, )}))for(consttool_callofresponse.tool_calls){constselectedTooltools.find(tt.nametool_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步启动 保存结果constresultawaitrunAgentWithTools(北京南站附近的酒店最近的 3 个酒店拿到酒店图片打开浏览器展示每个酒店的图片每个 tab 一个 url 展示并且在把那个页面标题改为酒店名);// 保存结果到 md 文件constoutputDirpath.resolve(import.meta.dirname,../output);fs.mkdirSync(outputDir,{recursive:true});constoutputPathpath.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-mcpHTTP URL │ │ └── 高德地图 API纯远程调用不需本地进程 │ ├── my-mcp-serverstdio · node │ │ └── 复用第六课自己的 Server │ ├── filesystemstdio · npx │ │ └── npm 包 modelcontextprotocol/server-filesystem │ └── chrome-devtoolsstdio · npx │ └── npm 包 chrome-devtools-mcplatest │ ├── 格式兼容处理 │ ├── 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 数量✅ 知识清单编号掌握项核心要点1HTTP 远程 MCP{ url: https://... }连接远程 Server不需本地进程2npx 拉起 MCP{ command: npx, args: [-y, package-name] }3混合连接一个MultiServerMCPClient同时连 HTTP stdio npx4返回格式兼容string / array / object 三种类型都要处理5高德 MCP 接入URL 带 API Key提供位置查询、路线规划6Chrome DevTools MCP操控浏览器打开 Tab、截图、修改标题7FileSystem MCPnpm 包读写文件限制访问目录8AI 工作流Agent 自动编排多个 Server 完成多步骤任务9结果保存fs.writeFileSync 时间戳文件名持久化到 output/10MCP 生态工具不再自己写接入现成 MCP Server 即可 基础 Agent → MCP 优化 → Remote MCP维度前五课 · 基础 Agent第六课 · MCP 本地第七课 · Remote MCP工具来源本地定义 / importMCP stdio 动态获取HTTP npx stdio 混合Server 类型无自己写的 Node 脚本自己写的 npm 包 远程服务连接方式无stdiostdio HTTP URL工具数量1 → 4N本地 Server 提供N×MM 个 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工作流 · 第七课