diff --git a/mcp_center/config/private/mcp_server/config_loader.py b/mcp_center/config/private/mcp_server/config_loader.py index 17dd2a510622f2dcff0e4138ad2687436d30fc62..5c1c6929cc54514db42e82ef8d6c3a8c4b527966 100644 --- a/mcp_center/config/private/mcp_server/config_loader.py +++ b/mcp_center/config/private/mcp_server/config_loader.py @@ -9,6 +9,11 @@ class McpServerConfigModel(BaseModel): """顶层配置模型""" port: int = Field(default=12555, description="MCP服务端口") fastapi_port : int = Field(default=12556, description="fastapi服务端口") + llm_remote: str = Field(default="", description="LLM远程主机地址") + llm_model: str = Field(default="gpt-3.5-turbo", description="LLM模型名称") + llm_api_key: str = Field(default="", description="LLM API Key") + max_tokens: int = Field(default=2048, description="LLM最大Token数") + temperature: float = Field(default=0.7, description="LLM温度参数") class McpServerConfig(BaseConfig): """顶层配置文件读取和使用Class""" diff --git a/mcp_center/servers/oe_cli_mcp_server/client/client.py b/mcp_center/servers/oe_cli_mcp_server/client/client.py index 7d3f784cc38f9da715b648a53e309ad69b1c1d86..6f6c563e07bdf586000474527ffc5cb05ca755ea 100644 --- a/mcp_center/servers/oe_cli_mcp_server/client/client.py +++ b/mcp_center/servers/oe_cli_mcp_server/client/client.py @@ -3,6 +3,7 @@ import asyncio import logging +import time from contextlib import AsyncExitStack from typing import TYPE_CHECKING, Union from pydantic import BaseModel, Field @@ -125,191 +126,258 @@ async def main() -> None: client = MCPClient(url, headers) await client.init() - # 初始化时多余的调用移除,保留下方有序测试用例 + # # 初始化时多余的调用移除,保留下方有序测试用例 + # # ================================== + # # 1. sys_info_tool 测试用例(3个,修复无效枚举值) + # # ================================== + # print("\n" + "="*60) + # print("1. sys_info_tool - 采集CPU+内存+磁盘+系统信息") + # print("="*60) + # result = await client.call_tool("sys_info_tool", {"info_types": ["cpu", "mem", "disk", "os"]}) + # print(result) + # + # print("\n" + "="*60) + # print("2. sys_info_tool - 单独采集网络信息(IP/网卡)") + # print("="*60) + # result = await client.call_tool("sys_info_tool", {"info_types": ["net"]}) + # print(result) + # + # print("\n" + "="*60) + # print("3. sys_info_tool - 采集安全信息(SELinux+防火墙)") + # print("="*60) + # result = await client.call_tool("sys_info_tool", {"info_types": ["selinux", "firewall"]}) + # print(result) + # + # # 移除无效的 "kernel" 和 "all" 类型测试(工具不支持) + # + # # ================================== + # # 2. file_tool 测试用例(4个,修复枚举值、参数名) + # # ================================== + # print("\n" + "="*60) + # print("4. file_tool - 列出 /etc 目录下的 .conf 配置文件(过滤关键词)") + # print("="*60) + # # 用 ls + 后续过滤实现(工具无find枚举,参数名改为file_path) + # result = await client.call_tool("file_tool", { + # "action": "ls", + # "file_path": "/etc", + # "detail": False, + # "encoding": "utf-8" + # }) + # print(result) + # + # print("\n" + "="*60) + # print("5. file_tool - 读取 /etc/os-release 文件内容(系统版本)") + # print("="*60) + # # action改为cat,参数名改为file_path + # result = await client.call_tool("file_tool", { + # "action": "cat", + # "file_path": "/etc/os-release", + # "encoding": "utf-8" + # }) + # print(result) + # + # print("\n" + "="*60) + # print("6. file_tool - 新建临时文件并写入内容") + # print("="*60) + # # 工具无find/mtime枚举,替换为add+edit实用场景 + # result = await client.call_tool("file_tool", { + # "action": "add", + # "file_path": "/tmp/file_tool_test.txt", + # "overwrite": True + # }) + # print("新建文件结果:", result) + # result = await client.call_tool("file_tool", { + # "action": "edit", + # "file_path": "/tmp/file_tool_test.txt", + # "content": "file_tool测试内容\n系统版本:Ubuntu 22.04", + # "encoding": "utf-8" + # }) + # print("写入内容结果:", result) + # + # print("\n" + "="*60) + # print("7. file_tool - 修改 /tmp/file_tool_test.txt 权限为755") + # print("="*60) + # # action改为chmod,参数名改为file_path + # result = await client.call_tool("file_tool", { + # "action": "chmod", + # "file_path": "/tmp/file_tool_test.txt", + # "mode": "755" + # }) + # print(result) + # + # # ================================== + # # 3. pkg_tool 测试用例(4个,修复无效枚举、参数) + # # ================================== + # print("\n" + "="*60) + # print("8. pkg_tool - 列出已安装的所有 nginx 相关包") + # print("="*60) + # result = await client.call_tool("pkg_tool", { + # "action": "list", + # "filter_key": "nginx" + # }) + # print(result) + # + # print("\n" + "="*60) + # print("9. pkg_tool - 查询 openssh-server 包详情(版本/依赖)") + # print("="*60) + # result = await client.call_tool("pkg_tool", { + # "action": "info", + # "pkg_name": "openssh-server" + # }) + # print(result) + # + # print("\n" + "="*60) + # print("10. pkg_tool - 安装 nginx 包 + 验证安装结果") + # print("="*60) + # + # # 步骤1:安装 nginx 包(双系统兼容,自动适配 apt/dnf) + # print("正在安装 nginx 包...") + # install_result = await client.call_tool("pkg_tool", { + # "action": "install", # 安装动作(双系统兼容) + # "pkg_name": "nginx", # 要安装的包名 + # "yes": True # 自动确认安装(避免交互) + # }) + # print("安装执行结果:") + # print(install_result) + # + # # 步骤2:验证安装结果(用 list 方法过滤 nginx 相关包) + # print("\n" + "-"*40) + # print("验证:查询已安装的 nginx 相关包") + # print("-"*40) + # verify_result = await client.call_tool("pkg_tool", { + # "action": "list", # 列出已安装包 + # "filter_key": "nginx" # 过滤关键词(只显示 nginx 相关) + # }) + # print("验证结果:") + # print(verify_result) + # + # print("\n" + "="*60) + # print("11. pkg_tool - 清理 yum/dnf 包缓存(all类型)") + # print("="*60) + # result = await client.call_tool("pkg_tool", { + # "action": "clean", + # "cache_type": "all", + # "yes": True + # }) + # print(result) + # + # # ================================== + # # 4. proc_tool 测试用例(4个,修复无效枚举、参数) + # # ================================== + # print("\n" + "="*60) + # print("12. proc_tool - 查找所有 systemd 相关进程") + # print("="*60) + # result = await client.call_tool("proc_tool", { + # "proc_actions": ["find"], + # "proc_name": "systemd" + # }) + # print(result) + # + # print("\n" + "="*60) + # print("13. proc_tool - 查询 PID=1 进程(systemd)资源占用") + # print("="*60) + # result = await client.call_tool("proc_tool", { + # "proc_actions": ["stat"], + # "pid": 1 + # }) + # print(result) + # + # print("\n" + "="*60) + # print("14. proc_tool - 列出所有进程(后续可筛选CPU占用前5)") + # print("="*60) + # # 工具无top枚举,用list获取所有进程(业务层可筛选) + # result = await client.call_tool("proc_tool", { + # "proc_actions": ["list"] + # }) + # print(result) + # + # print("\n" + "="*60) + # print("15. proc_tool - 重启 sshd 服务(systemd服务)") + # print("="*60) + # # 工具无tree枚举,替换为restart实用场景 + # result = await client.call_tool("proc_tool", { + # "proc_actions": ["restart"], + # "service_name": "sshd" # openEuler中sshd服务名为ssh + # }) + # print(result) + # + # # 清理临时文件 + # print("\n" + "="*60) + # print("16. file_tool - 删除临时测试文件") + # print("="*60) + # result = await client.call_tool("file_tool", { + # "action": "delete", + # "file_path": "/tmp/file_tool_test.txt" + # }) + # print(result) # ================================== - # 1. sys_info_tool 测试用例(3个,修复无效枚举值) + # 5. cmd_executor_tool 测试用例(4个,修复无效枚举、参数) # ================================== + # 场景1:执行普通ls命令(基础功能验证) print("\n" + "="*60) - print("1. sys_info_tool - 采集CPU+内存+磁盘+系统信息") + print("场景1:cmd_executor_tool - 执行本地ls命令(查看/tmp目录)") print("="*60) - result = await client.call_tool("sys_info_tool", {"info_types": ["cpu", "mem", "disk", "os"]}) - print(result) - - print("\n" + "="*60) - print("2. sys_info_tool - 单独采集网络信息(IP/网卡)") - print("="*60) - result = await client.call_tool("sys_info_tool", {"info_types": ["net"]}) - print(result) - - print("\n" + "="*60) - print("3. sys_info_tool - 采集安全信息(SELinux+防火墙)") - print("="*60) - result = await client.call_tool("sys_info_tool", {"info_types": ["selinux", "firewall"]}) - print(result) - - # 移除无效的 "kernel" 和 "all" 类型测试(工具不支持) - - # ================================== - # 2. file_tool 测试用例(4个,修复枚举值、参数名) - # ================================== - print("\n" + "="*60) - print("4. file_tool - 列出 /etc 目录下的 .conf 配置文件(过滤关键词)") - print("="*60) - # 用 ls + 后续过滤实现(工具无find枚举,参数名改为file_path) - result = await client.call_tool("file_tool", { - "action": "ls", - "file_path": "/etc", - "detail": False, - "encoding": "utf-8" - }) - print(result) - - print("\n" + "="*60) - print("5. file_tool - 读取 /etc/os-release 文件内容(系统版本)") - print("="*60) - # action改为cat,参数名改为file_path - result = await client.call_tool("file_tool", { - "action": "cat", - "file_path": "/etc/os-release", - "encoding": "utf-8" - }) - print(result) - - print("\n" + "="*60) - print("6. file_tool - 新建临时文件并写入内容") - print("="*60) - # 工具无find/mtime枚举,替换为add+edit实用场景 - result = await client.call_tool("file_tool", { - "action": "add", - "file_path": "/tmp/file_tool_test.txt", - "overwrite": True - }) - print("新建文件结果:", result) - result = await client.call_tool("file_tool", { - "action": "edit", - "file_path": "/tmp/file_tool_test.txt", - "content": "file_tool测试内容\n系统版本:Ubuntu 22.04", - "encoding": "utf-8" - }) - print("写入内容结果:", result) - - print("\n" + "="*60) - print("7. file_tool - 修改 /tmp/file_tool_test.txt 权限为755") - print("="*60) - # action改为chmod,参数名改为file_path - result = await client.call_tool("file_tool", { - "action": "chmod", - "file_path": "/tmp/file_tool_test.txt", - "mode": "755" - }) - print(result) - - # ================================== - # 3. pkg_tool 测试用例(4个,修复无效枚举、参数) - # ================================== - print("\n" + "="*60) - print("8. pkg_tool - 列出已安装的所有 nginx 相关包") - print("="*60) - result = await client.call_tool("pkg_tool", { - "action": "list", - "filter_key": "nginx" - }) - print(result) - - print("\n" + "="*60) - print("9. pkg_tool - 查询 openssh-server 包详情(版本/依赖)") - print("="*60) - result = await client.call_tool("pkg_tool", { - "action": "info", - "pkg_name": "openssh-server" - }) - print(result) - - print("\n" + "="*60) - print("10. pkg_tool - 安装 nginx 包 + 验证安装结果") - print("="*60) - - # 步骤1:安装 nginx 包(双系统兼容,自动适配 apt/dnf) - print("正在安装 nginx 包...") - install_result = await client.call_tool("pkg_tool", { - "action": "install", # 安装动作(双系统兼容) - "pkg_name": "nginx", # 要安装的包名 - "yes": True # 自动确认安装(避免交互) - }) - print("安装执行结果:") - print(install_result) - - # 步骤2:验证安装结果(用 list 方法过滤 nginx 相关包) - print("\n" + "-"*40) - print("验证:查询已安装的 nginx 相关包") - print("-"*40) - verify_result = await client.call_tool("pkg_tool", { - "action": "list", # 列出已安装包 - "filter_key": "nginx" # 过滤关键词(只显示 nginx 相关) + result = await client.call_tool("cmd_executor_tool", { + "command": "ls /tmp" }) - print("验证结果:") - print(verify_result) + print(f"执行结果:{result}") + # 场景2:重点验证超时终止能力(sleep 10秒,设置超时5秒) print("\n" + "="*60) - print("11. pkg_tool - 清理 yum/dnf 包缓存(all类型)") + print("场景2:cmd_executor_tool - 验证超时终止能力(sleep 10秒,超时5秒)") print("="*60) - result = await client.call_tool("pkg_tool", { - "action": "clean", - "cache_type": "all", - "yes": True + start_time = time.time() # 记录命令开始执行时间 + print(f"命令开始执行时间戳:{start_time:.2f}(当前时间:{time.ctime(start_time)})") + # 执行sleep 10,超时设置为5秒 + result = await client.call_tool("cmd_executor_tool", { + "command": "sleep 10", # 命令需要执行10秒 + "timeout": 5 # 超时时间仅5秒,会触发超时终止 }) - print(result) + end_time = time.time() # 记录命令执行结束时间 + print(f"命令执行结束时间戳:{end_time:.2f}(当前时间:{time.ctime(end_time)})") + print(f"实际执行时长:{end_time - start_time:.2f}秒(预期超时时间:5秒)") + print(f"超时终止结果:{result}") - # ================================== - # 4. proc_tool 测试用例(4个,修复无效枚举、参数) - # ================================== + # 场景3:验证Shell脚本的超时终止(脚本内sleep 8秒,设置超时4秒) print("\n" + "="*60) - print("12. proc_tool - 查找所有 systemd 相关进程") + print("场景3:cmd_executor_tool - 验证Shell脚本的超时终止(脚本内sleep 8秒,超时4秒)") print("="*60) - result = await client.call_tool("proc_tool", { - "proc_actions": ["find"], - "proc_name": "systemd" + # 第一步:创建一个包含sleep的测试脚本 + create_script_result = await client.call_tool("cmd_executor_tool", { + "command": "echo 'echo \"脚本开始执行,将sleep 8秒...\"; sleep 8; echo \"脚本执行完成\"' > /tmp/timeout_test.sh && chmod +x /tmp/timeout_test.sh", + "timeout": 10 }) - print(result) - - print("\n" + "="*60) - print("13. proc_tool - 查询 PID=1 进程(systemd)资源占用") - print("="*60) - result = await client.call_tool("proc_tool", { - "proc_actions": ["stat"], - "pid": 1 - }) - print(result) - - print("\n" + "="*60) - print("14. proc_tool - 列出所有进程(后续可筛选CPU占用前5)") - print("="*60) - # 工具无top枚举,用list获取所有进程(业务层可筛选) - result = await client.call_tool("proc_tool", { - "proc_actions": ["list"] + print(f"创建超时测试脚本结果:{create_script_result}") + # 第二步:执行脚本,设置超时4秒(远小于脚本内的8秒) + start_time = time.time() + print(f"脚本开始执行时间戳:{start_time:.2f}(当前时间:{time.ctime(start_time)})") + result = await client.call_tool("cmd_executor_tool", { + "command": "/tmp/timeout_test.sh", + "timeout": 4 # 超时4秒,触发脚本执行超时终止 }) - print(result) + end_time = time.time() + print(f"脚本执行结束时间戳:{end_time:.2f}(当前时间:{time.ctime(end_time)})") + print(f"实际执行时长:{end_time - start_time:.2f}秒(预期超时时间:4秒)") + print(f"脚本超时终止结果:{result}") + # 场景4:空命令测试(参数校验验证) print("\n" + "="*60) - print("15. proc_tool - 重启 sshd 服务(systemd服务)") + print("场景4:cmd_executor_tool - 空命令测试(验证参数校验)") print("="*60) - # 工具无tree枚举,替换为restart实用场景 - result = await client.call_tool("proc_tool", { - "proc_actions": ["restart"], - "service_name": "sshd" # openEuler中sshd服务名为ssh + result = await client.call_tool("cmd_executor_tool", { + "command": "" }) - print(result) + print(f"执行结果:{result}") - # 清理临时文件 + # 场景5:清理测试文件 print("\n" + "="*60) - print("16. file_tool - 删除临时测试文件") + print("场景5:cmd_executor_tool - 清理测试脚本(/tmp/timeout_test.sh)") print("="*60) - result = await client.call_tool("file_tool", { - "action": "delete", - "file_path": "/tmp/file_tool_test.txt" + result = await client.call_tool("cmd_executor_tool", { + "command": "rm -f /tmp/timeout_test.sh", + "timeout": 5 }) - print(result) - + print(f"清理结果:{result}") await client.stop() if __name__ == "__main__": diff --git a/mcp_center/servers/oe_cli_mcp_server/mcp_tools/base_tools/cmd_executor_tool/base.py b/mcp_center/servers/oe_cli_mcp_server/mcp_tools/base_tools/cmd_executor_tool/base.py new file mode 100644 index 0000000000000000000000000000000000000000..1a84c497f2addf195bddb7414ddaa547c088bc9d --- /dev/null +++ b/mcp_center/servers/oe_cli_mcp_server/mcp_tools/base_tools/cmd_executor_tool/base.py @@ -0,0 +1,3 @@ +from config.public.base_config_loader import BaseConfig + +lang = BaseConfig().get_config().public_config.language \ No newline at end of file diff --git a/mcp_center/servers/oe_cli_mcp_server/mcp_tools/base_tools/cmd_executor_tool/config.json b/mcp_center/servers/oe_cli_mcp_server/mcp_tools/base_tools/cmd_executor_tool/config.json new file mode 100644 index 0000000000000000000000000000000000000000..d779d0d0529c6c9843a733e08c1a27e4abc5140d --- /dev/null +++ b/mcp_center/servers/oe_cli_mcp_server/mcp_tools/base_tools/cmd_executor_tool/config.json @@ -0,0 +1,8 @@ +{ + "tools": { + "cmd_executor_tool": { + "zh": "【统一命令执行工具】\n功能:支持本地执行shell命令/Shell脚本,支持按指令类型自动设置超时时间,超时自动终止执行(纯Python实现,兼容常见命令与脚本),返回结构化多语言结果\n\n【核心提示】\n1. 命令参数(command)为必填项,不可传入空值;\n2. 超时参数(timeout)为可选项,若不传入则按指令类型自动匹配默认超时时间,传入时需为正整数(单位:秒),非正整数将自动使用15秒默认值;\n4. 指令类型与默认超时时间映射规则(优先级:用户指定timeout > Shell脚本指令 > 普通指令 > 全局默认):\n - Shell脚本指令(包含.sh、以bash / sh 开头):默认600秒;\n - 长耗时指令(yum/apt/docker/scp):分别为300/300/600/600秒;\n - 中等耗时指令(ping/curl):均为30秒;\n - 快速指令(ls/pwd/echo/cat/grep):分别为5/5/5/10/10秒;\n - 未匹配指令:全局默认15秒;\n5. host参数为兼容保留项,当前版本仅支持本地执行(目标固定为127.0.0.1),传入后不影响执行逻辑;\n6. 命令格式要求:支持标准shell命令与Shell脚本路径(如\"/tmp/run.sh\"、\"bash /root/install.sh\"),避免非法命令导致执行失败。\n\n【枚举类定义(必须遵守)】\n- CommandTimeoutEnum(指令超时枚举):LS=5 / PWD=5 / ECHO=5 / CAT=10 / GREP=10 / PING=30 / CURL=30 / YUM=300 / APT=300 / DOCKER=600 / SCP=600 / SHELL_SCRIPT=600 / DEFAULT=15\n- LanguageEnum(语言枚举):ZH / EN\n\n【参数详情】\n- host:远程主机标识(兼容保留项,可选,无实际作用)\n- command:需要执行的shell命令/Shell脚本(必填,标准shell格式)\n- timeout:执行超时时间(可选,正整数,单位:秒,默认按指令匹配)\n- lang:语言类型(可选,枚举值:ZH/EN,默认读取配置文件)\n\n【返回值说明】\n- success:执行结果(True=成功,False=失败)\n- message:执行信息/错误提示(多语言,如命令成功、超时、执行失败等)\n- result:命令执行结果(成功时返回命令输出内容,失败/超时返回空字符串)\n- target:执行目标(固定为127.0.0.1,本地执行)\n- timeout_used:实际使用的超时时间(单位:秒,便于排查超时问题)", + "en": "【Unified Command Execution Tool】\nFunction: Supports executing shell commands/Shell scripts locally, automatically sets timeout time by command type, terminates execution automatically when timed out (Python native implementation, compatible with common commands and scripts), returns structured multilingual results\n\n【Core Guidelines】\n1. Command parameter (command) is required, empty value is not allowed;\n2. Timeout parameter (timeout) is optional, if not passed, the default timeout time is automatically matched by command type; if passed, it must be a positive integer (unit: seconds), non-positive integers will automatically use the default value of 15 seconds;\n3. Language parameter (lang) is optional, if not passed, the default language in the configuration file is read; if passed, it must be a LanguageEnum enum value (ZH/EN);\n4. Mapping rules for command types and default timeout time (priority: user-specified timeout > Shell script command > normal command > global default):\n - Shell script commands (containing .sh, starting with bash / sh ): default 600 seconds;\n - Long-time-consuming commands (yum/apt/docker/scp): 300/300/600/600 seconds respectively;\n - Medium-time-consuming commands (ping/curl): 30 seconds each;\n - Fast commands (ls/pwd/echo/cat/grep): 5/5/5/10/10 seconds respectively;\n - Unmatched commands: global default 15 seconds;\n5. The host parameter is a compatibility reserved item, the current version only supports local execution (target fixed as 127.0.0.1), and passing it does not affect the execution logic;\n6. Command format requirement: Supports standard shell commands and Shell script paths (e.g.\"/tmp/run.sh\", \"bash /root/install.sh\"), avoid execution failure caused by illegal commands.\n\n【Enum Class Definition (Must Follow)】\n- CommandTimeoutEnum (Command Timeout Enum): LS=5 / PWD=5 / ECHO=5 / CAT=10 / GREP=10 / PING=30 / CURL=30 / YUM=300 / APT=300 / DOCKER=600 / SCP=600 / SHELL_SCRIPT=600 / DEFAULT=15\n- LanguageEnum (Language Enum): ZH / EN\n\n【Parameter Details】\n- host: Remote host identifier (compatibility reserved item, optional, no actual effect)\n- command: Shell command/Shell script to execute (required, standard shell format)\n- timeout: Execution timeout time (optional, positive integer, unit: seconds, default matched by command)\n- lang: Language type (optional, enum values: ZH/EN, default read from configuration file)\n\n【Return Value Explanation】\n- success: Execution result (True=success, False=failure)\n- message: Execution info/error prompt (multilingual, such as success, timeout, execution failure, etc.)\n- result: Command execution result (returns command output content on success, empty string on failure/timeout)\n- target: Execution target (fixed as 127.0.0.1, local execution)\n- timeout_used: The actual timeout time used (unit: seconds, convenient for troubleshooting timeout issues)" + } + } +} \ No newline at end of file diff --git a/mcp_center/servers/oe_cli_mcp_server/mcp_tools/base_tools/cmd_executor_tool/deps.toml.py b/mcp_center/servers/oe_cli_mcp_server/mcp_tools/base_tools/cmd_executor_tool/deps.toml.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/mcp_center/servers/oe_cli_mcp_server/mcp_tools/base_tools/cmd_executor_tool/tool.py b/mcp_center/servers/oe_cli_mcp_server/mcp_tools/base_tools/cmd_executor_tool/tool.py new file mode 100644 index 0000000000000000000000000000000000000000..57456f83afa1886b1835b96bc47af779be3ee9f5 --- /dev/null +++ b/mcp_center/servers/oe_cli_mcp_server/mcp_tools/base_tools/cmd_executor_tool/tool.py @@ -0,0 +1,131 @@ +import asyncio +from typing import Union, Optional +import subprocess +from config.public.base_config_loader import BaseConfig,LanguageEnum + + +# Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. All rights reserved. + + +async def cmd_executor_tool( + host: Union[str, None] = None, + command: str = "", + timeout: Optional[int] = None, +) -> dict: + """ + 本地命令执行工具,支持按指令类型自动设置超时,返回结构化字典结果(多语言) + :param host: 兼容保留参数,无实际作用 + :param command: 需要执行的shell命令/脚本(必填) + :param timeout: 手动指定超时时间(秒),可选 + :param lang: 语言类型,可选,默认读取配置中的语言 + :return: 结构化字典结果,包含success、message、result、target、timeout_used + """ + # -------------------------- 读取语言配置(优先级:传入参数 > 配置文件) -------------------------- + + lang = BaseConfig().get_config().public_config.language + + # -------------------------- 初始化返回结果字典 -------------------------- + response = { + "success": False, # 执行状态:True成功/False失败 + "message": "", # 提示信息(多语言) + "result": "", # 命令执行结果(成功时为输出内容,失败时为空) + "target": "127.0.0.1", # 执行目标,固定为本地 + "timeout_used": 0 # 实际使用的超时时间(秒) + } + + # -------------------------- 命令为空校验 -------------------------- + if not command: + response["message"] = "请提供需要执行的命令" if lang == LanguageEnum.ZH else "please give me the command to execute" + return response + + # -------------------------- 超时时间配置与处理 -------------------------- + # 定义常见指令的默认超时时间(秒),可根据需求扩展 + cmd_timeout_map = { + # 快速指令:短超时 + "ls": 5, + "pwd": 5, + "echo": 5, + "cat": 10, + "grep": 10, + # 中等耗时指令 + "ping": 30, + "curl": 30, + # 长耗时指令 + "yum": 300, + "apt": 300, + "docker": 600, + "scp": 600, + } + # Shell脚本执行的默认超时时间(秒) + SHELL_SCRIPT_DEFAULT_TIMEOUT = 600 + + def get_final_timeout(cmd: str) -> int: + """确定最终超时时间,优先级:用户指定 > Shell脚本默认 > 普通指令默认 > 全局默认15秒""" + # 优先级1:用户手动指定超时(校验合法性) + if timeout is not None: + try: + t = int(timeout) + return t if t > 0 else 15 + except (ValueError, TypeError): + return 15 + # 优先级2:执行Shell脚本的指令,使用专属超时 + cmd_lower = cmd.lower() + if ".sh" in cmd_lower or cmd_lower.startswith("bash ") or cmd_lower.startswith("sh "): + return SHELL_SCRIPT_DEFAULT_TIMEOUT + # 优先级3:匹配普通指令的默认超时 + for cmd_key, t in cmd_timeout_map.items(): + if cmd_key in cmd_lower: + return t + # 优先级4:全局默认超时 + return 15 + + final_timeout = get_final_timeout(command) + response["timeout_used"] = final_timeout # 记录实际使用的超时时间 + + # -------------------------- 本地命令执行(同步逻辑,供线程池调用) -------------------------- + def local_exec_sync(): + """同步执行本地命令,返回执行结果和错误信息""" + try: + result = subprocess.run( + command, + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + # 执行成功,返回输出内容 + return True, result.stdout.strip(), "" + except subprocess.CalledProcessError as e: + # 命令执行返回非0,返回错误信息 + return False, "", e.stderr.strip() + except Exception as e: + # 其他异常,返回异常信息 + return False, "", str(e) + + # -------------------------- 超时控制执行 -------------------------- + try: + loop = asyncio.get_running_loop() + # 用线程池执行同步的本地命令,并用wait_for控制超时 + exec_success, exec_result, exec_error = await asyncio.wait_for( + loop.run_in_executor(None, local_exec_sync), + timeout=final_timeout + ) + + if exec_success: + # 命令执行成功 + response["success"] = True + response["message"] = "命令执行成功" if lang == LanguageEnum.ZH else "Command executed successfully" + response["result"] = exec_result + else: + # 命令执行失败 + response["message"] = f"命令执行出错:{exec_error}" if lang == LanguageEnum.ZH else f"Command execution failed: {exec_error}" + + except asyncio.TimeoutError: + # 命令执行超时 + response["message"] = f"本地执行命令超时({final_timeout}秒),已终止执行" if lang == LanguageEnum.ZH else f"Local command execution timed out ({final_timeout} seconds), terminated" + except Exception as e: + # 其他执行异常(如线程池错误) + response["message"] = f"本地执行命令出错:{str(e)}" if lang == LanguageEnum.ZH else f"Local command execution failed: {str(e)}" + + return response \ No newline at end of file diff --git a/mcp_center/servers/oe_cli_mcp_server/mcp_tools/base_tools/ssh_fix_tool/base.py b/mcp_center/servers/oe_cli_mcp_server/mcp_tools/base_tools/ssh_fix_tool/base.py index e15959de2b6d03b4954153b8b3f7a758908c63cc..48f254de9b753b7f0b56c36f4a3c89ceaa5d6946 100644 --- a/mcp_center/servers/oe_cli_mcp_server/mcp_tools/base_tools/ssh_fix_tool/base.py +++ b/mcp_center/servers/oe_cli_mcp_server/mcp_tools/base_tools/ssh_fix_tool/base.py @@ -112,7 +112,7 @@ def check_sshd_status(target: Optional[str]) -> Dict: result = init_result(target_host) is_zh = get_language() - cmd = ["systemctl", "status", "sshd"] + cmd = ["/usr/bin/systemctl ", "status", "sshd"] # 本地 if target_host == "127.0.0.1":