ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于Cloudflare Worker与MCP协议实现AI Agent去中心化服务发现

基于Cloudflare Worker与MCP协议实现AI Agent去中心化服务发现 大家好我是专注于分享前沿技术实战经验的博主。最近在探索AI Agent协同工作时一个核心痛点浮出水面如何让运行在不同环境、不同网络下的Agent能够安全、高效地发现彼此并建立通信传统的中心化注册服务或复杂的网络穿透方案往往带来额外的运维负担和单点故障风险。本文将围绕一个创新的开源方案展开——利用Cloudflare Worker作为轻量级、全球化的“信标”结合Model Context ProtocolMCP实现Agent间的去中心化发现。无论你是正在构建多Agent系统的开发者还是对Serverless和AI应用集成感兴趣的工程师这篇文章都将为你提供一套从原理到部署的完整实操指南。1. 背景与核心概念为什么需要Agent发现在构建复杂的AI应用时单个Agent的能力是有限的。我们常常需要多个具备不同专长如数据分析、代码执行、网络搜索的Agent协同工作形成一个“智能体团队”。这就引出了一个基础且关键的问题Agent之间如何找到对方1.1 传统方案的挑战中心化注册表所有Agent启动时向一个中心服务器注册自己的地址和元数据。问题在于中心服务器成了单点故障和性能瓶颈且需要维护。静态配置在配置文件中硬编码其他Agent的地址。这在动态伸缩、故障转移或Agent临时上线/下线的场景下极不灵活。复杂的服务网格引入如Consul、Etcd等服务发现组件虽然功能强大但也带来了显著的学习成本和运维复杂度对于轻量级AI应用来说可能“杀鸡用牛刀”。1.2 新方案的核心组件本方案巧妙地结合了两个现代技术提供了一个优雅的解决方案Model Context Protocol (MCP)是什么MCP是由Anthropic提出的一种开放协议旨在标准化AI应用如Claude与外部工具、数据源之间的通信方式。它定义了一套基于JSON-RPC的接口用于资源发现、内容读取和工具调用。在本方案中的作用我们将MCP进行“创造性复用”。不仅用于AI与工具的交互更将其作为Agent之间通信的“标准语言”。每个Agent都实现一个MCP服务器对外提供自身的“能力”作为工具和“状态”作为资源。Cloudflare Worker是什么Cloudflare提供的无服务器函数计算平台在全球数百个边缘节点运行具备超低延迟、高可用的特性。在本方案中的作用作为轻量级的发现信标和消息中转站。它不存储Agent的长期状态而是作为一个无状态的“公告板”和“邮局”帮助Agent相互发现并传递初始连接信息。核心思路每个Agent启动后向一个公共的Cloudflare Worker“报到”。Worker记录下Agent的临时联系信息。当其他Agent需要寻找伙伴时也询问同一个Worker从而获得当前在线的Agent列表。后续的Agent间直接通信通过MCP协议则无需再经过Worker。2. 环境准备与版本说明在开始实战之前请确保你的开发环境满足以下要求。本文示例将主要使用Node.js环境进行演示但原理适用于任何能实现HTTP服务器和客户端的语言。操作系统Windows 10/11, macOS, 或 Linux (Ubuntu 20.04)。方案与系统无关。Node.js版本 18.0.0 或更高。这是运行示例Agent和工具链的基础。包管理器npm 或 yarn。本文使用npm。Cloudflare 账户你需要一个免费的Cloudflare账户来创建和部署Worker。命令行工具wranglerCloudflare的官方Worker命令行工具。通过npm install -g wrangler安装。curl或httpie用于测试API。代码编辑器VS Code 或其他你熟悉的IDE。版本兼容性说明MCP协议本身处于快速发展中本文基于其核心的、稳定的JSON-RPC规范进行设计。Cloudflare Worker的API保持向后兼容。请确保你的wrangler工具是最新版本。3. 核心原理与架构拆解让我们深入看看这个“Agent通过Worker发现彼此”的系统是如何工作的。3.1 架构全景图整个系统包含三个核心角色Agent实现特定功能的AI智能体。它同时扮演两个角色MCP服务器对外提供标准的/mcpHTTP端点供其他Agent或客户端查询其能力和资源。Worker客户端定期或在启动时向Discovery Worker注册/更新自己的信息并从Worker拉取其他Agent的信息。Discovery Worker (Cloudflare Worker)一个无状态的Serverless函数提供两个核心APIPOST /register接收Agent的注册请求将其元数据如ID、名称、MCP服务器地址存储在一个短暂的KV存储或内存中可设置TTL。GET /discover返回当前所有已注册且未过期的Agent列表。MCP协议作为Agent间通信的“普通话”。一旦Agent A通过Worker发现了Agent B的地址它就可以直接向Agent B的MCP服务器发起标准的JSON-RPC调用例如tools/list来查询B提供了哪些工具然后调用tools/call来使用这些工具。---------------- 1. 注册 ---------------------- | Agent A |-------------------| | | (MCP Server | | Discovery Worker | | 192.168.1.10) |-------------------| (Cloudflare Edge) | ---------------- 2. 发现列表 ---------------------- | | | 3. 获取到B的地址 | 4. 提供B的地址 | (e.g., 192.168.1.20:8000) | V V ---------------- 5. 直接MCP调用 ---------------- | Agent B |---------------------------| | | (MCP Server | | | | 192.168.1.20) |---------------------------| | ---------------- 6. MCP响应 ----------------3.2 关键设计决策去中心化通信Discovery Worker只负责最初的“介绍”。一旦Agent们互相认识它们就会建立直接的P2P连接避免了Worker成为后续所有通信的瓶颈。心跳与TTLAgent定期向Worker发送心跳通过再次调用/register以保持其注册信息有效。Worker为每个注册项设置一个生存时间TTL如30秒。如果一个Agent崩溃了它的注册信息会在TTL后自动过期并从发现列表中移除实现了故障自愈。无状态WorkerWorker本身不持久化数据。我们使用Cloudflare的KV命名空间或Durable Objects来存储短暂的注册信息。KV更适合本例因为它天然支持TTL。安全性考虑在生产环境中需要对/register端点进行认证例如使用共享密钥或JWT以防止恶意Agent注册。Agent间的MCP通信也可以考虑使用TLS。4. 完整实战构建一个微型多Agent系统现在让我们动手搭建一个包含两个Agent和一个Discovery Worker的迷你系统。Agent A是一个“计算器”Agent B是一个“天气查询员”。我们将让它们互相发现并调用对方的功能。4.1 步骤一创建并部署Discovery Worker首先我们创建负责Agent发现的Cloudflare Worker。初始化Worker项目mkdir agent-discovery-worker cd agent-discovery-worker npm create cloudflarelatest . -- --typehello-world # 按照提示登录你的Cloudflare账户绑定KV命名空间 Worker需要存储Agent信息。在Cloudflare Dashboard中创建一个KV命名空间命名为AGENT_REGISTRY。记下其ID。 在wrangler.toml文件中添加绑定# wrangler.toml name agent-discovery-worker main src/index.js compatibility_date 2024-08-01 [[kv_namespaces]] binding AGENT_REGISTRY # 在Worker代码中使用的变量名 id your-kv-namespace-id-here # 替换为你的KV命名空间ID编写Worker核心代码// src/index.js // 定义Agent信息的结构 const AGENT_TTL 30; // 注册信息存活时间秒 export default { async fetch(request, env) { const url new URL(request.url); const path url.pathname; // 处理注册请求 if (path /register request.method POST) { try { const agentInfo await request.json(); // 基础验证 if (!agentInfo.id || !agentInfo.name || !agentInfo.mcpEndpoint) { return new Response(JSON.stringify({ error: Missing required fields: id, name, mcpEndpoint }), { status: 400 }); } const key agent:${agentInfo.id}; // 将Agent信息存入KV并设置TTL await env.AGENT_REGISTRY.put(key, JSON.stringify(agentInfo), { expirationTtl: AGENT_TTL }); return new Response(JSON.stringify({ success: true, message: Registered successfully, ttl: AGENT_TTL }), { status: 200, headers: { Content-Type: application/json }, }); } catch (error) { return new Response(JSON.stringify({ error: Invalid JSON or processing error }), { status: 500 }); } } // 处理发现请求 if (path /discover request.method GET) { try { // 列出KV中所有以 agent: 为前缀的键 const keys await env.AGENT_REGISTRY.list({ prefix: agent: }); const agents []; // 并行获取所有Agent的详细信息 for (const key of keys.keys) { const value await env.AGENT_REGISTRY.get(key.name); if (value) { agents.push(JSON.parse(value)); } } return new Response(JSON.stringify({ success: true, agents }), { status: 200, headers: { Content-Type: application/json }, }); } catch (error) { return new Response(JSON.stringify({ error: Discovery failed }), { status: 500 }); } } // 健康检查或未匹配的路由 return new Response(JSON.stringify({ message: Agent Discovery Worker. Use POST /register or GET /discover }), { headers: { Content-Type: application/json }, }); }, };部署Workernpx wrangler deploy部署成功后你会获得一个类似https://agent-discovery-worker.your-subdomain.workers.dev的URL。记下它我们称之为WORKER_URL。4.2 步骤二创建“计算器”Agent A我们创建一个简单的Node.js HTTP服务器它既是一个MCP服务器也会向Discovery Worker注册自己。初始化项目并安装依赖mkdir calculator-agent cd calculator-agent npm init -y npm install express cors编写Agent A的代码// server.js const express require(express); const cors require(cors); const app express(); const PORT process.env.PORT || 8001; const AGENT_ID calc_agent_001; const AGENT_NAME Advanced Calculator; // 替换为你的实际Worker URL const DISCOVERY_WORKER_URL https://agent-discovery-worker.your-subdomain.workers.dev; app.use(cors()); app.use(express.json()); // 1. 实现MCP服务器端点 app.post(/mcp, (req, res) { const { method, params, id } req.body; if (method tools/list) { // 声明自己提供的工具 res.json({ jsonrpc: 2.0, id, result: { tools: [ { name: add_numbers, description: Add two numbers together., inputSchema: { type: object, properties: { a: { type: number, description: First number }, b: { type: number, description: Second number }, }, required: [a, b], }, }, { name: multiply_numbers, description: Multiply two numbers., inputSchema: { type: object, properties: { a: { type: number }, b: { type: number }, }, required: [a, b], }, }, ], }, }); } else if (method tools/call) { // 处理工具调用 const { name, arguments: args } params; let result; if (name add_numbers) { result args.a args.b; } else if (name multiply_numbers) { result args.a * args.b; } else { return res.status(400).json({ jsonrpc: 2.0, id, error: { code: -32601, message: Method not found } }); } res.json({ jsonrpc: 2.0, id, result: { content: [{ type: text, text: String(result) }] } }); } else { // 其他MCP方法如resources/list可以在此扩展 res.status(400).json({ jsonrpc: 2.0, id, error: { code: -32601, message: Method not found } }); } }); // 2. 向Discovery Worker注册自己的函数 async function registerWithDiscovery() { const agentInfo { id: AGENT_ID, name: AGENT_NAME, mcpEndpoint: http://localhost:${PORT}/mcp, // 注意生产环境需用公网地址 capabilities: [arithmetic], timestamp: new Date().toISOString(), }; try { const response await fetch(${DISCOVERY_WORKER_URL}/register, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(agentInfo), }); const data await response.json(); console.log([${AGENT_NAME}] Registration response:, data); } catch (error) { console.error([${AGENT_NAME}] Failed to register:, error.message); } } // 3. 定期发送心跳以维持注册 setInterval(registerWithDiscovery, 20 * 1000); // 每20秒注册一次小于TTL(30秒) // 4. 启动服务器并立即注册 app.listen(PORT, async () { console.log([${AGENT_NAME}] MCP server listening on http://localhost:${PORT}); await registerWithDiscovery(); });运行Agent Anode server.js控制台应输出服务器启动和注册成功的日志。4.3 步骤三创建“天气查询员”Agent BAgent B的代码结构与A类似但提供不同的工具。创建新项目mkdir weather-agent cd weather-agent npm init -y npm install express cors node-fetch编写Agent B的代码// server.js const express require(express); const cors require(cors); const fetch (...args) import(node-fetch).then(({default: fetch}) fetch(...args)); // 动态导入 const app express(); const PORT process.env.PORT || 8002; const AGENT_ID weather_agent_001; const AGENT_NAME City Weather Fetcher; const DISCOVERY_WORKER_URL https://agent-discovery-worker.your-subdomain.workers.dev; // 同上替换 // 模拟天气数据真实场景可调用第三方API const mockWeatherData { Beijing: { temp: 22, condition: Sunny }, Shanghai: { temp: 25, condition: Cloudy }, New York: { temp: 18, condition: Rainy }, }; app.use(cors()); app.use(express.json()); app.post(/mcp, (req, res) { const { method, params, id } req.body; if (method tools/list) { res.json({ jsonrpc: 2.0, id, result: { tools: [ { name: get_weather, description: Get current weather for a city., inputSchema: { type: object, properties: { city: { type: string, description: City name }, }, required: [city], }, }, ], }, }); } else if (method tools/call) { const { name, arguments: args } params; if (name get_weather) { const city args.city; const weather mockWeatherData[city] || { temp: null, condition: Unknown city }; const resultText weather.temp ! null ? Current weather in ${city}: ${weather.temp}°C, ${weather.condition}. : Sorry, weather data for ${city} is not available.; res.json({ jsonrpc: 2.0, id, result: { content: [{ type: text, text: resultText }] } }); } else { res.status(400).json({ jsonrpc: 2.0, id, error: { code: -32601, message: Method not found } }); } } else { res.status(400).json({ jsonrpc: 2.0, id, error: { code: -32601, message: Method not found } }); } }); // 注册与心跳逻辑与Agent A完全相同 async function registerWithDiscovery() { const agentInfo { id: AGENT_ID, name: AGENT_NAME, mcpEndpoint: http://localhost:${PORT}/mcp, capabilities: [weather], timestamp: new Date().toISOString(), }; try { const response await fetch(${DISCOVERY_WORKER_URL}/register, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(agentInfo), }); const data await response.json(); console.log([${AGENT_NAME}] Registration response:, data); } catch (error) { console.error([${AGENT_NAME}] Failed to register:, error.message); } } setInterval(registerWithDiscovery, 20 * 1000); app.listen(PORT, async () { console.log([${AGENT_NAME}] MCP server listening on http://localhost:${PORT}); await registerWithDiscovery(); });运行Agent Bnode server.js4.4 步骤四测试发现与交互现在我们有两个Agent在运行并已向Worker注册。让我们验证发现机制并模拟一个Agent调用另一个Agent工具的过程。测试Discovery Worker 在终端中使用curl查询所有已注册的Agent。curl -X GET https://agent-discovery-worker.your-subdomain.workers.dev/discover你应该会收到一个JSON响应包含两个Agent的信息类似于{ success: true, agents: [ { id: calc_agent_001, name: Advanced Calculator, mcpEndpoint: http://localhost:8001/mcp, capabilities: [arithmetic], timestamp: ... }, { id: weather_agent_001, name: City Weather Fetcher, mcpEndpoint: http://localhost:8002/mcp, capabilities: [weather], timestamp: ... } ] }这表明发现服务工作正常。模拟Agent间调用 我们写一个简单的测试脚本模拟“计算器Agent”在发现了“天气Agent”后去查询天气。// test_discovery_and_call.js const fetch (...args) import(node-fetch).then(({default: fetch}) fetch(...args)); const DISCOVERY_WORKER_URL https://agent-discovery-worker.your-subdomain.workers.dev; async function discoverAndCall() { console.log(1. Discovering available agents...); const discoverRes await fetch(${DISCOVERY_WORKER_URL}/discover); const discoverData await discoverRes.json(); if (!discoverData.success || discoverData.agents.length 0) { console.log(No agents found.); return; } console.log(Found ${discoverData.agents.length} agent(s):); discoverData.agents.forEach(agent console.log( - ${agent.name} (${agent.id}))); // 2. 找到天气Agent const weatherAgent discoverData.agents.find(a a.capabilities.includes(weather)); if (!weatherAgent) { console.log(No weather agent found.); return; } console.log(\n2. Found weather agent at: ${weatherAgent.mcpEndpoint}); // 3. 直接调用天气Agent的MCP接口查询工具列表 console.log(3. Fetching tools from weather agent...); const listToolsReq { jsonrpc: 2.0, id: 1, method: tools/list, params: {}, }; const toolsRes await fetch(weatherAgent.mcpEndpoint, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(listToolsReq), }); const toolsData await toolsRes.json(); console.log(Tools available:, JSON.stringify(toolsData.result.tools, null, 2)); // 4. 调用天气查询工具 const callToolReq { jsonrpc: 2.0, id: 2, method: tools/call, params: { name: get_weather, arguments: { city: Beijing }, }, }; console.log(\n4. Calling weather tool for Beijing...); const callRes await fetch(weatherAgent.mcpEndpoint, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(callToolReq), }); const callData await callRes.json(); console.log(Weather result:, callData.result.content[0].text); } discoverAndCall().catch(console.error);运行测试脚本node test_discovery_and_call.js输出应展示完整的发现和调用流程1. Discovering available agents... Found 2 agent(s): - Advanced Calculator (calc_agent_001) - City Weather Fetcher (weather_agent_001) 2. Found weather agent at: http://localhost:8002/mcp 3. Fetching tools from weather agent... Tools available: [...] 4. Calling weather tool for Beijing... Weather result: Current weather in Beijing: 22°C, Sunny.恭喜你已经成功搭建了一个基于Cloudflare Worker和MCP协议的去中心化Agent发现与协作系统。Agent A和B通过Worker找到了彼此并成功进行了直接的服务调用。5. 常见问题与排查思路在实际部署和运行中你可能会遇到以下问题问题现象可能原因排查步骤与解决方案Agent注册到Worker失败Failed to register1. Worker URL错误。2. Worker代码部署失败或存在语法错误。3. KV命名空间未正确绑定或权限不足。4. 网络问题。1. 检查DISCOVERY_WORKER_URL变量是否正确。2. 运行wrangler tail查看Worker实时日志排查错误。3. 在Cloudflare Dashboard确认KV命名空间已绑定且wrangler.toml中的ID正确。4. 使用curl直接测试Worker的根路径看是否返回预期信息。发现列表为空No agents found1. Agent的注册逻辑未执行或报错。2. Agent注册的TTL已过期且心跳未维持。3. Worker的/discover接口逻辑有误。1. 检查Agent控制台确认registerWithDiscovery函数被调用且无报错。2. 确保Agent的心跳间隔如20秒小于Worker设置的TTL如30秒。3. 直接调用GET /discover并检查Worker日志看KV列表操作是否成功。Agent间直接MCP调用失败网络错误1.mcpEndpoint地址不可达如使用了localhost。2. 目标Agent的MCP服务器未运行或端口被占用。3. 防火墙或安全组规则阻止了连接。1.这是开发中最常见的问题。确保mcpEndpoint是其他Agent能访问的地址。在本地开发时可使用内网IP如http://192.168.1.x:port/mcp或借助内网穿透工具如ngrok。生产环境必须使用公网域名或IP。2. 检查目标Agent的进程是否存活端口是否监听。3. 检查本地和服务器防火墙设置。MCP调用返回Method not found1. JSON-RPCmethod字段拼写错误。2. Agent的MCP服务器未实现对应的tools/list或tools/call方法。3. 请求体格式不符合JSON-RPC 2.0规范。1. 仔细对照MCP协议规范检查method字符串如tools/list。2. 确保目标Agent的/mcp端点正确解析了method并实现了对应分支逻辑。3. 使用工具如Postman检查发送的请求体格式确保包含jsonrpc,id,method,params等必需字段。Worker返回520或5xx错误Cloudflare Worker运行时错误。1. 使用wrangler tail --format pretty查看详细的错误堆栈。2. 常见原因未处理的异常、异步操作未await、KV操作超限免费计划有速率限制。3. 简化代码逐步调试。6. 最佳实践与工程建议将本方案用于实际生产环境时需要考虑以下方面以提升系统的可靠性、安全性和可维护性。6.1 安全加固认证与授权在Worker的/register端点添加认证。例如要求Agent在注册时提供一个预共享密钥PSKWorker验证该密钥后才接受注册。// Worker端 const EXPECTED_AUTH_HEADER Bearer your-shared-secret-token; const authHeader request.headers.get(Authorization); if (authHeader ! EXPECTED_AUTH_HEADER) { return new Response(JSON.stringify({ error: Unauthorized }), { status: 401 }); }考虑使用JWT使Token可以包含更多元数据如Agent角色、权限并设置有效期。通信安全Agent间的MCP通信应使用HTTPSTLS加密防止中间人攻击。确保mcpEndpoint是https://开头。在内部网络中可以考虑使用mTLS双向TLS进行更严格的认证。6.2 可扩展性与可靠性使用Durable Objects替代KV对于更复杂的状态管理如需要强一致性、实时通知Agent下线可以考虑使用Cloudflare Durable Objects。它可以维护WebSocket连接在Agent下线时立即通知其他Agent。多区域与高可用Cloudflare Worker天然在全球边缘运行已具备高可用性。对于Agent本身可以考虑部署多个实例并在注册时带上健康检查端点Worker可以定期检查Agent健康状态只返回健康的Agent。元数据与版本控制在Agent注册信息中增加version字段。这样请求方可以发现并选择兼容版本的Agent进行调用避免API不匹配的问题。6.3 生产环境部署要点服务发现mcpEndpoint必须是一个稳定的、可被其他所有Agent访问的网络地址。可以是公网IP端口需配置安全组和防火墙。域名通过DNS解析。内部服务网格中的服务名如Kubernetes Service。优雅下线Agent在关闭前应主动向Worker发送一个注销请求例如DELETE /register/{agent_id}让Worker立即将其从发现列表中移除而不是等待TTL过期。监控与日志为Worker和每个Agent添加详细的日志记录注册、发现、调用关键事件和错误。利用Cloudflare的Analytics监控Worker的调用次数和错误率。为Agent设置健康检查接口便于运维。6.4 协议扩展与生态集成丰富MCP内容除了toolsMCP协议还定义了resources资源和prompts提示词。你可以让Agent对外提供数据资源如数据库连接或可复用的提示模板进一步增强协作能力。与现有MCP客户端集成本方案中的Agent本身就是一个MCP服务器。这意味着它可以被任何支持MCP协议的客户端如Claude Desktop、支持MCP的IDE插件直接连接和使用。这极大地扩展了Agent的用途。定义通用接口为你的多Agent系统定义一些通用的工具接口例如ask_question,process_data让不同类型的Agent都能以统一的方式被调用促进异构Agent间的协作。这个基于Cloudflare Worker和MCP的Agent发现方案为我们构建灵活、松散耦合、可扩展的多智能体系统提供了一个强大的基础框架。它巧妙地将无服务器的全球网络与标准化的AI协议相结合降低了分布式AI应用的入门门槛。你可以在此基础上继续探索更复杂的路由策略、负载均衡、语义发现根据能力描述而非ID发现等高级特性打造出真正智能的Agent网络。
返回列表