"""MCP Skill 加载器 - Phase 9.2 从 MCP (Model Context Protocol) 服务器发现和加载 Skills。 """ import os from typing import Any from app.agents.skills.metadata import SkillMetadata class MCPSkillLoader: """MCP Skill 加载器 从 MCP 服务器发现可用的 Skills。 """ def __init__(self, mcp_servers: list[dict[str, Any]] | None = None): """ Args: mcp_servers: MCP 服务器列表,每项包含 name, command, env 等 """ self.mcp_servers = mcp_servers or [] self._discovered_skills: dict[str, SkillMetadata] = {} def discover_skills(self) -> list[SkillMetadata]: """从所有配置的 MCP 服务器发现 Skills Returns: 发现的 Skill 列表 """ skills = [] for server in self.mcp_servers: server_skills = self._discover_from_server(server) skills.extend(server_skills) return skills def _discover_from_server(self, server: dict[str, Any]) -> list[SkillMetadata]: """从单个 MCP 服务器发现 Skills Args: server: 服务器配置 Returns: Skill 列表 """ skills = [] server_name = server.get("name", "unknown") # 模拟从 MCP 服务器获取工具列表 # 实际实现时,这里会调用 MCP 服务器的 list_tools 接口 try: tools = self._call_mcp_list_tools(server) for tool in tools: skill = self._tool_to_skill(tool, server_name) if skill: skills.append(skill) self._discovered_skills[skill.name] = skill except Exception: pass return skills def _call_mcp_list_tools(self, server: dict[str, Any]) -> list[dict[str, Any]]: """调用 MCP 服务器的 list_tools 接口 Args: server: 服务器配置 Returns: 工具列表 """ # TODO: 实现实际的 MCP 协议调用 # 目前返回空列表,实际使用时需要实现 MCP 客户端 return [] def _tool_to_skill(self, tool: dict[str, Any], server: str) -> SkillMetadata | None: """将 MCP 工具转换为 Skill Args: tool: MCP 工具定义 server: 服务器名 Returns: Skill 元数据或 None """ tool_name = tool.get("name") if not tool_name: return None return SkillMetadata( id=f"mcp_{server}_{tool_name}", name=f"{server}:{tool_name}", description=tool.get("description", f"MCP tool: {tool_name}"), version="1.0.0", content=self._generate_skill_content(tool), triggers=[f"@{server}", f"/{tool_name}"], tools=[tool_name], tags=["mcp", server], enabled=True, ) def _generate_skill_content(self, tool: dict[str, Any]) -> str: """生成 Skill 内容 Args: tool: MCP 工具定义 Returns: Skill 内容字符串 """ name = tool.get("name", "unknown") description = tool.get("description", "No description") input_schema = tool.get("inputSchema", {}) content = f"""# MCP Tool: {name} **Description**: {description} **Server**: {tool.get("server", "unknown")} **Input Schema**: ```json {input_schema} ``` **Usage**: Use the `/{name}` command or `@{tool.get("server", "server")}` to invoke this tool. **Examples**: ``` /{name} arg1=value1 arg2=value2 @{tool.get("server", "server")} {name} --arg1 value1 ``` """ return content def get_skill(self, name: str) -> SkillMetadata | None: """获取已发现的 Skill Args: name: Skill 名称 Returns: Skill 元数据或 None """ return self._discovered_skills.get(name) def list_skills(self) -> list[SkillMetadata]: """列出所有已发现的 Skills Returns: Skill 列表 """ return list(self._discovered_skills.values()) # 全局加载器 _loader: MCPSkillLoader | None = None def get_mcp_skill_loader() -> MCPSkillLoader: """获取全局 MCP Skill 加载器""" global _loader if _loader is None: _loader = MCPSkillLoader() return _loader