1. 资料地址hello-agents/docs/chapter10/第十章 智能体通信协议.md at main · datawhalechina/hello-agents · GitHub2. 环境安装按教材安装即可 pip installhello-agents[protocol]0.2.2装完后确认 python -cfrom hello_agents.tools import MCPTool, A2ATool, ANPTool; print(ok)错误是系统找不到 npx。先看你的脚本在连什么 MCP 服务器。 npx:Nonenode:None确认环境里没有 Node.js所以找不到 npx。脚本通过 npx 启动 modelcontextprotocol/server-filesystem。 原因脚本用 npx 拉起 MCP 文件系统服务器但当前环境没有 Node.js所以报 No such fileordirectory:npx。 处理安装 Node.js会带上 npx# 方式一conda推荐和当前 agent 环境一致conda install -n agent -c conda-forge nodejs -y# 方式二系统包# apt-get update apt-get install -y nodejs npm装完验证 node -v npx -v3. 调试测试1: 01_TestConnectimportasynciofromhello_agents.protocolsimportMCPClientasyncdefconnect_to_server():# 方式1连接到社区提供的文件系统服务器# npx会自动下载并运行modelcontextprotocol/server-filesystem包client MCPClient([npx,-y,modelcontextprotocol/server-filesystem,.# 指定根目录])# 使用async with确保连接正确关闭asyncwithclient:# 在这里使用clienttools awaitclient.list_tools()print(f可用工具:{[t[name]fortintools]})# 方式2连接到自定义的Python MCP服务器client MCPClient([python,my_mcp_server.py])asyncwithclient:# 使用client...pass# 运行异步函数asyncio.run(connect_to_server())asyncdefdiscover_tools(): client MCPClient([npx,-y,modelcontextprotocol/server-filesystem,.])asyncwithclient:# 获取所有可用工具tools awaitclient.list_tools()print(f服务器提供了{len(tools)}个工具)fortoolintools:print(f\n工具名称:{tool[name]})print(f描述:{tool.get(description,无描述)})# 打印参数信息ifinputSchemaintool: schema tool[inputSchema]ifpropertiesinschema:print(参数:)forparam_name, param_infoinschema[properties].items(): param_type param_info.get(type,any) param_desc param_info.get(description,)print(f -{param_name}({param_type}):{param_desc}) asyncio.run(discover_tools())报错RuntimeError: Event loopisclosed Exception ignoredin: function BaseSubprocessTransport.__del__ at0x7f69583048b0 Traceback (most recent call last): File/data//envs/agent/lib/python3.10/asyncio/base_subprocess.py, line126,in__del__ self.close() File/data//envs/agent/lib/python3.10/asyncio/base_subprocess.py, line104,inclose proto.pipe.close() File/data//envs/agent/lib/python3.10/asyncio/unix_events.py, line547,inclose self._close(None) File/data//envs/agent/lib/python3.10/asyncio/unix_events.py, line571,in_close self._loop.call_soon(self._call_connection_lost, exc) File/data//envs/agent/lib/python3.10/asyncio/base_events.py, line753,incall_soon self._check_closed() File/data//envs/agent/lib/python3.10/asyncio/base_events.py, line515,in_check_closedraiseRuntimeError(Event loop is closed) RuntimeError: Event loopisclosed测试2: 01_TestConnect修改为importasyncioimportsysfromhello_agents.protocolsimportMCPClient SERVER_CMD [npx,-y,modelcontextprotocol/server-filesystem,.]def_ignore_closed_loop_on_subprocess_del(unraisable):过滤 asyncio 子进程管道在 loop 关闭后析构时的无害报错。if( unraisable.exc_typeisRuntimeErrorandEvent loop is closedinstr(unraisable.exc_value)andgetattr(unraisable.object,__qualname__,) BaseSubprocessTransport.__del__):returnsys.__unraisablehook__(unraisable) sys.unraisablehook _ignore_closed_loop_on_subprocess_delasyncdefconnect_to_server(client: MCPClient): tools awaitclient.list_tools()print(f可用工具:{[t[name]fortintools]})asyncdefdiscover_tools(client: MCPClient): tools awaitclient.list_tools()print(f服务器提供了{len(tools)}个工具)fortoolintools:print(f\n工具名称:{tool[name]})print(f描述:{tool.get(description,无描述)}) schema tool.get(input_schema)ortool.get(inputSchema)or{}ifpropertiesinschema:print(参数:)forparam_name, param_infoinschema[properties].items(): param_type param_info.get(type,any) param_desc param_info.get(description,)print(f -{param_name}({param_type}):{param_desc})asyncdefmain():asyncwithMCPClient(SERVER_CMD)asclient:awaitconnect_to_server(client)awaitdiscover_tools(client)if__name__ __main__: asyncio.run(main())测试3:02_Connect2MCP# 02_Connect2MCP.pyimportasyncioimportsysfromhello_agents.protocolsimportMCPClient SERVER_CMD [npx,-y,modelcontextprotocol/server-filesystem,.]def_ignore_closed_loop_on_subprocess_del(unraisable):过滤 asyncio 子进程管道在 loop 关闭后析构时的无害报错。if( unraisable.exc_typeisRuntimeErrorandEvent loop is closedinstr(unraisable.exc_value)andgetattr(unraisable.object,__qualname__,) BaseSubprocessTransport.__del__):returnsys.__unraisablehook__(unraisable) sys.unraisablehook _ignore_closed_loop_on_subprocess_delasyncdefconnect_to_server(client: MCPClient): tools awaitclient.list_tools()print(f可用工具:{[t[name]fortintools]})asyncdefdiscover_tools(client: MCPClient): tools awaitclient.list_tools()print(f服务器提供了{len(tools)}个工具)fortoolintools:print(f\n工具名称:{tool[name]})print(f描述:{tool.get(description,无描述)}) schema tool.get(input_schema)ortool.get(inputSchema)or{}ifpropertiesinschema:print(参数:)forparam_name, param_infoinschema[properties].items(): param_type param_info.get(type,any) param_desc param_info.get(description,)print(f -{param_name}({param_type}):{param_desc})asyncdefuse_tools(client: MCPClient): result awaitclient.call_tool(read_file, {path:my_README.md})print(f文件内容\n{result}) result awaitclient.call_tool(list_directory, {path:.})print(f当前目录文件{result}) result awaitclient.call_tool(write_file, {path:output.txt,content:Hello from MCP!})print(f写入结果{result})asyncdefsafe_tool_call(client: MCPClient):try: result awaitclient.call_tool(read_file, {path:nonexistent.txt})print(result)exceptExceptionase:print(f工具调用失败:{e})# 可以选择重试、使用默认值或向用户报告错误asyncdefmain():asyncwithMCPClient(SERVER_CMD)asclient:awaitconnect_to_server(client)awaitdiscover_tools(client)awaituse_tools(client)awaitsafe_tool_call(client)if__name__ __main__: asyncio.run(main())输出It will be compatible before version2.0.0.fromauthlib.joseimportJsonWebKey, JsonWebToken 使用 Stdio 传输 (命令): npx -y modelcontextprotocol/server-filesystem . 连接到 MCP 服务器... Secure MCP Filesystem Server running on stdio ✅ 连接成功 Client doesnotsupport MCP Roots, using allowed directoriessetfromserver args: [/data/py_workspace/agent_learning/hello_agents/chapter10] 可用工具: [read_file,read_text_file,read_media_file,read_multiple_files,write_file,edit_file,create_directory,list_directory,list_directory_with_sizes,directory_tree,move_file,search_files,get_file_info,list_allowed_directories] 服务器提供了14个工具 工具名称: read_file 描述: Read the complete contents of a fileastext. DEPRECATED: Use read_text_file instead. 参数: - path (string): - tail (number): If provided, returns only the last N lines of the file - head (number): If provided, returns only the first N lines of the file测试4: 03_GitHubMCP和 02_Connect2MCP.py 是同一类问题。先看你的 03_GitHubMCP.py。 和 02_Connect2MCP.py 是同一类问题不是 GitHub MCP 调用失败。 从终端输出看你的脚本已经正常跑完了工具列表和仓库搜索结果都打印出来了。报错出现在程序退出之后。 原因 MCPTool 通过 npx 启动了一个子进程 主程序结束时asyncio 的事件循环已经关闭 子进程管道对象在垃圾回收时还想往已关闭的 loop 注册清理回调 于是出现 RuntimeError: Event loopisclosed 这是 Python3.10 asyncio 子进程清理时的无害警告可以忽略。 如果想消掉它在脚本开头加上这段和 02_Connect2MCP.py 一样importsysdef_ignore_closed_loop_on_subprocess_del(unraisable):if( unraisable.exc_typeisRuntimeErrorandEvent loop is closedinstr(unraisable.exc_value)andgetattr(unraisable.object,__qualname__,) BaseSubprocessTransport.__del__):returnsys.__unraisablehook__(unraisable) sys.unraisablehook _ignore_closed_loop_on_subprocess_del 结论 GitHub MCP 功能本身没问题这只是退出时的清理噪音。# 03_GitHubMCP.py from hello_agents.tools import MCPTool import sys def _ignore_closed_loop_on_subprocess_del(unraisable): if ( unraisable.exc_type is RuntimeError and Event loop is closed in str(unraisable.exc_value) and getattr(unraisable.object, __qualname__, ) BaseSubprocessTransport.__del__ ): return sys.__unraisablehook__(unraisable) sys.unraisablehook _ignore_closed_loop_on_subprocess_del github_tool MCPTool( server_command[npx, -y, modelcontextprotocol/server-github] ) # 1. 列出可用工具 print( 可用工具) result github_tool.run({action: list_tools}) print(result) # 2. 搜索仓库 print(\n 搜索仓库) result github_tool.run({ action: call_tool, tool_name: search_repositories, arguments: { query: AI agents language:python, page: 1, perPage: 3 } }) print(result)测试504_MCPTransport# 04_MCPTransport.pyimportsysfromhello_agents.toolsimportMCPTooldef_ignore_mcp_cleanup_warnings(unraisable):过滤 MCP 子进程在程序退出时的无害清理警告。ifunraisable.exc_typeisRuntimeErrorandEvent loop is closedinstr(unraisable.exc_value):returnif( unraisable.exc_typeisAttributeErrorand_stop_eventinstr(unraisable.exc_value)andgetattr(unraisable.object,__qualname__,) StdioTransport.__del__):returnsys.__unraisablehook__(unraisable) sys.unraisablehook _ignore_mcp_cleanup_warnings# 1. Memory Transport - 内存传输用于测试# mcp_tool MCPTool()# 2. Stdio Transport - 标准输入输出传输本地开发# mcp_tool MCPTool(server_command[python, my_mcp_server.py])# 3. Stdio Transport with Args - 带参数的命令传输# mcp_tool MCPTool(server_command[python, my_mcp_server.py, --debug])# 4. Stdio Transport - 社区服务器npx方式# mcp_tool MCPTool(server_command[npx, -y, modelcontextprotocol/server-filesystem, .])# 5. HTTP/SSE/StreamableHTTP Transport# 注意MCPTool 主要用于 Stdio 和 Memory 传输# 对于 HTTP/SSE 等远程传输建议直接使用 MCPClient# 使用内置演示服务器Memory 传输mcp_tool MCPTool()# 列出可用工具result mcp_tool.run({action:list_tools})print(result)# 调用工具result mcp_tool.run({action:call_tool,tool_name:add,arguments: {a:1,b:2} })print(result)# 方式1使用自定义Python服务器mcp_tool MCPTool(server_command[python,my_mcp_server.py])# 方式2使用社区服务器文件系统mcp_tool MCPTool(server_command[npx,-y,modelcontextprotocol/server-filesystem,.])# 列出工具result mcp_tool.run({action:list_tools})print(result)# 调用工具result mcp_tool.run({action:call_tool,tool_name:read_file,arguments: {path:my_README.md} })print(result)测试605_UseMCPToolInAgent# 05_UseMCPToolInAgent.py from dotenv import load_dotenv load_dotenv() # 从当前目录的 .env 加载 LLM_API_KEY、LLM_BASE_URL 等配置 from hello_agents import SimpleAgent, HelloAgentsLLM from hello_agents.tools import MCPTool import sys def _ignore_mcp_cleanup_warnings(unraisable): 过滤 MCP 子进程在程序退出时的无害清理警告。 if unraisable.exc_type is RuntimeError and Event loop is closed in str(unraisable.exc_value): return if ( unraisable.exc_type is AttributeError and _stop_event in str(unraisable.exc_value) and getattr(unraisable.object, __qualname__, ) StdioTransport.__del__ ): return sys.__unraisablehook__(unraisable) sys.unraisablehook _ignore_mcp_cleanup_warnings print( * 70) print(方式1使用内置演示服务器) print( * 70) agent SimpleAgent(name助手, llmHelloAgentsLLM()) # 无需任何配置自动使用内置演示服务器 # 内置服务器提供add, subtract, multiply, divide, greet, get_system_info mcp_tool MCPTool() # 默认namemcp agent.add_tool(mcp_tool) # 智能体可以使用内置工具 response agent.run(计算 123 456) print(response) # 智能体会自动调用add工具 print(\n * 70) print(方式2连接外部MCP服务器使用多个服务器) print( * 70) # 重要为每个MCP服务器指定不同的name避免工具名称冲突 # 示例1连接到社区提供的文件系统服务器 fs_tool MCPTool( namefilesystem, # 指定唯一名称 description访问本地文件系统, server_command[npx, -y, modelcontextprotocol/server-filesystem, .] ) agent.add_tool(fs_tool)