""" MCP Server 基类 提供通用的 MCP 服务器基础设施 设计说明: - McpServer: 通用 MCP 服务器基类 - 子类在 __init__ 中通过 self.tools = [...] 赋值工具实例 - 提供工具列表展示、调用处理、服务器启动等通用逻辑 使用示例: class MyServer(McpServer): def __init__(self): super().__init__("my-mcp", "1.0.0") # 直接赋值工具实例列表 self.tools = [MyTool1(), MyTool2()] # 启动服务器 server = MyServer() await server.run_stdio() """ import asyncio import logging from typing import Any, Dict, List, Optional from mcp.server import Server from mcp.server.models import InitializationOptions from mcp.types import ( Tool, TextContent, ImageContent, EmbeddedResource, ServerCapabilities, ) logger = logging.getLogger(__name__) class BaseMcpServer: """ 通用 MCP 服务器基类 子类在 __init__ 中通过 self.tools 属性赋值工具实例列表。 封装了工具列表展示、调用处理、结果格式化、服务器启动等通用逻辑。 """ def __init__(self, server_name: str, version: str = "1.0.0"): """ 初始化 MCP 服务器 Args: server_name: 服务器名称 version: 服务器版本 """ self.server_name = server_name self.version = version self.server = Server(server_name) # 工具列表(子类在 __init__ 中赋值) self.tools: List[Any] = [] # 注册处理器 self.server.list_tools()(self._handle_list_tools) self.server.call_tool()(self._handle_call_tool) async def _handle_list_tools(self) -> list[Tool]: """列出所有可用的工具""" try: tools = [ Tool( name=tool.name, description=tool.description, inputSchema=tool.parameters_schema, ) for tool in self.tools ] logger.debug(f"返回工具列表: {[t.name for t in tools]}") return tools except Exception as e: logger.error(f"列出工具列表时发生异常: {str(e)}") return [] async def _handle_call_tool( self, name: str, arguments: Optional[dict[str, Any]] = None ) -> list[TextContent | ImageContent | EmbeddedResource]: """处理工具调用""" if not arguments: arguments = {} logger.info(f"调用工具: {name}, 参数: {arguments}") # 查找工具 tool = next((t for t in self.tools if t.name == name), None) if not tool: error_msg = f"未知工具:{name}" logger.error(error_msg) return [TextContent(type="text", text=f"❌ {error_msg}")] try: result = tool.execute(**arguments) response_text = self._format_tool_result(result) logger.info(f"工具 {name} 执行成功: {response_text[:100]}...") return [TextContent(type="text", text=response_text)] except ValueError as e: error_msg = f"参数错误:{str(e)}" logger.error(f"工具 {name} 参数错误: {str(e)}") return [TextContent(type="text", text=f"❌ {error_msg}")] except RuntimeError as e: error_msg = f"执行失败:{str(e)}" logger.error(f"工具 {name} 执行失败: {str(e)}") return [TextContent(type="text", text=f"❌ {error_msg}")] except Exception as e: error_msg = f"未知错误:{str(e)}" logger.error(f"工具 {name} 未知错误: {str(e)}", exc_info=True) return [TextContent(type="text", text=f"❌ {error_msg}")] @staticmethod def _format_tool_result(result: Any) -> str: """ 格式化工具执行结果 支持多种返回格式: - 包含 message/data 属性的对象 - 字典(包含 message/data 键) - 其他类型(直接转为字符串) """ response_text = "" # 提取消息 message = getattr(result, 'message', None) if message: response_text += f"✅ {message}\n\n" elif isinstance(result, dict) and 'message' in result: response_text += f"✅ {result['message']}\n\n" # 提取数据 data = getattr(result, 'data', None) if data is None and isinstance(result, dict): data = result.get('data') if data: response_text += "详情:\n" if isinstance(data, dict): for key, value in data.items(): response_text += f" - {key}: {value}\n" else: response_text += f" {data}\n" # 如果没有消息也没有数据,直接返回结果的字符串形式 if not response_text: response_text = str(result) return response_text async def run_stdio(self): """ 启动服务器,使用标准输入输出通信 """ from mcp.server.stdio import stdio_server logger.info(f"启动 MCP 服务器: {self.server_name} v{self.version}") logger.info(f"可用工具: {[t.name for t in self.tools]}") try: async with stdio_server() as (read_stream, write_stream): logger.info("stdio_server 连接已建立") await self.server.run( read_stream, write_stream, InitializationOptions( server_name=self.server_name, server_version=self.version, capabilities=ServerCapabilities( tools={} # 启用工具能力 ), ), ) except Exception as e: logger.error(f"MCP 服务器运行异常: {str(e)}", exc_info=True) raise finally: logger.info("MCP 服务器已关闭")