MCP协议进阶:构建多工具Agent实现智能查询与浏览器交互

MCP协议的扩展应用:从单一工具到多服务器集成

在上篇文章中,我们介绍了MCP(Model Context Protocol)协议的基本概念及其在LLM(大型语言模型)工具集成中的作用。MCP作为一种开源协议,由Anthropic贡献,帮助开发者规范工具的提供和调用,实现跨进程、跨语言的Agent系统。今天,我们进一步探讨MCP的进阶用法:多服务器集成。通过结合多个MCP服务器,我们可以让Agent处理更复杂的任务,如地图查询、文件系统操作和浏览器交互。这不仅放大LLM的"干活"能力,还能无缝融入实际应用场景。

回想MCP的核心:它统一了工具的通信协议,支持stdio和http等方式。扩展到多服务器时,我们可以使用MultiServerMCPClient来管理多个MCP实例。例如,集成第三方API如高德地图(amap-maps)、本地文件系统(filesystem)和Chrome开发工具(chrome-devtools)。这些服务器提供不同的工具集,让Agent能调用地图服务获取位置信息、操作文件保存结果,甚至控制浏览器展示视觉内容。这使得LLM从简单响应转向智能自动化。

为什么选择多服务器集成?单一MCP服务器可能局限于特定功能,而多服务器允许模块化扩展。举例来说,在查询"广州站附近的酒店"时,Agent可以先用地图工具获取酒店列表和路线,然后用文件工具保存文档,最后用浏览器工具展示酒店图片。这种链式调用体现了MCP的灵活性,避免了手动对接的复杂性。

多MCP服务器的配置与工具绑定

在开发多工具Agent时,首先需要配置MCP客户端。使用@langchain/mcp-adapters库的MultiServerMCPClient,可以指定多个服务器。每个服务器有不同的启动方式:远程URL、本地命令行等。这确保了工具的多样性。

例如,配置高德地图服务器使用http:

JavaScript

bash 复制代码
"mcpServers": {
  "amap-maps-streamableHTTP": {
    "url": `https://mcp.amap.com/mcp?key=${process.env.AMAP_MAPS_API_KEY}`
  }
}

这里,环境变量存储API密钥,确保安全。文件系统服务器用npx启动本地工具,指定工作目录:

JavaScript

perl 复制代码
"filesystem": {
  "command": "npx",
  "args": [
    "-y",
    "@modelcontextprotocol/server-filesystem",
    "D:/lesson_zp/ai/agent/mcp_in_action"
  ]
}

Chrome开发工具服务器类似,用于浏览器自动化:

JavaScript

perl 复制代码
"chorme-devtools": {
  "command": "npx",
  "args": [
    "-y",
    "chrome-devtools-mcp@latest"
  ]
}

启动客户端后,获取所有工具列表并绑定到LLM模型:

JavaScript

ini 复制代码
const tools = await mcpClient.getTools();
const modelWithTools = model.bindTools(tools);

这让LLM能智能调用工具。模型使用ChatOpenAI,配置modelName、API密钥和baseURL,确保兼容自定义端点。

Agent运行流程:工具调用循环与结果处理

Agent的核心是处理用户查询的循环机制。初始化消息数组以HumanMessage开始,然后迭代调用模型,检查tool_calls。如果无调用,则返回最终内容;否则,执行工具并添加ToolMessage。

关键代码:

JavaScript

ini 复制代码
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);
        let contentStr;
        if (typeof toolResult === 'string') {
          contentStr = toolResult;
        } else if (toolResult && toolResult.text) {
          contentStr = toolResult.text;
        }
        messages.push(new ToolMessage({
          content: contentStr,
          tool_call_id: toolCall.id
        }));
      }
    }
  }
  return messages[messages.length - 1].content;
}

这个循环限制在30次迭代,防止无限循环。工具结果处理考虑string或对象形式,确保兼容不同服务器输出。使用chalk库美化日志,提升开发体验。

实际示例:查询酒店并浏览器展示

现在,看一个完整示例:查询"广州站附近的3个酒店,拿到酒店的图片,展示浏览器,展示每个酒店的图片,每个tab一个url展示,并且那个页面标题改为酒店名"。注意,查询中有重复"附近的附近的",可能是笔误,可指正为"广州站附近的3个酒店"以优化清晰度。

运行:

JavaScript

javascript 复制代码
await runAgentWithTools(`
  广州站附近的附近的3个酒店,拿到酒店的图片,展示浏览器,展示每个酒店的图片,
  每个tab一个url展示,并且那个页面标题改为酒店名
`);

Agent流程:

  1. LLM解析查询,调用amap-maps工具获取酒店列表(位置、名称、图片URL)。
  2. 对于每个酒店,调用chrome-devtools工具打开新tab,加载图片URL,并修改标题为酒店名。
  3. 如果需要保存路线,可调用filesystem工具生成MD文件(注释中示例)。

高德地图工具可能提供POI搜索、路线规划。浏览器工具允许自动化tab管理、标题修改。文件工具支持读写指定目录。

这个示例展示了MCP的多服务器协同:地图提供数据,浏览器处理展示,文件可选持久化。相比单一工具,Agent更智能,能链式执行复杂任务。

MCP在实际项目中的价值

多MCP集成让Agent适用于各种场景,如智能助手、自动化脚本。未来,随着更多官方服务器(如filesystem),开发者可轻松扩展。MCP的开源性鼓励社区贡献,规范工具生态。

通过以上代码,你可以快速构建类似系统。欢迎讨论你的应用!

完整代码示例:多MCP Agent实现

以下是完整代码,用于本地运行:

JavaScript

javascript 复制代码
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 mcpClient = new MultiServerMCPClient({
  mcpServers: {
    "amap-maps-streamableHTTP": {
      "url": `https://mcp.amap.com/mcp?key=${process.env.AMAP_MAPS_API_KEY}`
    },
    // mcp 官方提供
    "filesystem": {
      "command" : "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "D:/lesson_zp/ai/agent/mcp_in_action"
      ]
    },
    "chorme-devtools": {
      "command": "npx",
      "args": [
        "-y",
        "chrome-devtools-mcp@latest"
      ]
    }
  }
})

const model = new ChatOpenAI({
  modelName: process.env.MODEL_NAME,
  openAIApiKey: process.env.OPENAI_API_KEY,
  configuration: {
    baseURL: process.env.OPENAI_API_BASE_URL,
  }
})

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);
        let contentStr;
        if (typeof toolResult === 'string') {
          contentStr = toolResult;
        } else if (toolResult && toolResult.text) {
          contentStr = toolResult.text;
        }
        messages.push(new ToolMessage({
          content: contentStr,
          tool_call_id: toolCall.id
        }));
      }
    }
  }
  return messages[messages.length - 1].content;
}

// await runAgentWithTools('广州站附近的酒店,以及去的路线');
// await runAgentWithTools(`广州站附近的2个酒店,以及去的路线,路线规划生成文档保存到
// D:\lesson_zp\ai\agent\mcp_in_action 的一个md 文件`);

await runAgentWithTools(`
  广州站附近的附近的3个酒店,拿到酒店的图片,展示浏览器,展示每个酒店的图片,
  每个tab一个url展示,并且那个页面标题改为酒店名
`)

await mcpClient.close();

这个代码演示了MCP的多面性。实际中,根据需求调整服务器和查询。

结语:MCP驱动的未来Agent

MCP协议通过多服务器集成,赋予LLM强大执行力。从地图到浏览器,Agent能处理现实任务。希望这篇文章启发你的开发实践!

相关推荐
手机不死我是天子2 小时前
拆解大模型二:Transformer 最核心的设计,其实你高中就学过
人工智能·llm
Halo咯咯2 小时前
Claude Code 的工程哲学:缓存与工具设计的真实教训 | 经验分享
人工智能
风象南3 小时前
最适合新手先装的 20 个 OpenClaw Skills 来了!
人工智能
哈里谢顿12 小时前
ai-agent工程师指南
agent
小兵张健14 小时前
35岁程序员的春天来了
人工智能
大怪v14 小时前
AI抢饭?前端佬:我要验牌!
前端·人工智能·程序员
冬奇Lab14 小时前
OpenClaw 深度解析(六):节点、Canvas 与子 Agent
人工智能·开源
刀法如飞15 小时前
AI提示词框架深度对比分析
人工智能·ai编程
IT_陈寒17 小时前
Python开发者必知的5大性能陷阱:90%的人都踩过的坑!
前端·人工智能·后端