diff --git a/api/services/tools/mcp_tools_manage_service.py b/api/services/tools/mcp_tools_manage_service.py index ba664a0154..c5b0adfdf8 100644 --- a/api/services/tools/mcp_tools_manage_service.py +++ b/api/services/tools/mcp_tools_manage_service.py @@ -254,11 +254,23 @@ class MCPToolManageService: def list_providers(self, *, tenant_id: str, for_list: bool = False) -> list[ToolProviderApiEntity]: """List all MCP providers for a tenant.""" + from models.account import Account + stmt = select(MCPToolProvider).where(MCPToolProvider.tenant_id == tenant_id).order_by(MCPToolProvider.name) mcp_providers = self._session.scalars(stmt).all() + if not mcp_providers: + return [] + + # Batch query all users to avoid N+1 problem + user_ids = {provider.user_id for provider in mcp_providers} + users = self._session.query(Account).where(Account.id.in_(user_ids)).all() + user_name_map = {user.id: user.name for user in users} + return [ - ToolTransformService.mcp_provider_to_user_provider(provider, for_list=for_list) + ToolTransformService.mcp_provider_to_user_provider( + provider, for_list=for_list, user_name=user_name_map.get(provider.user_id) + ) for provider in mcp_providers ] diff --git a/api/services/tools/tools_transform_service.py b/api/services/tools/tools_transform_service.py index 22f63a7aa4..931d23e3e0 100644 --- a/api/services/tools/tools_transform_service.py +++ b/api/services/tools/tools_transform_service.py @@ -233,17 +233,23 @@ class ToolTransformService: ) @staticmethod - def mcp_provider_to_user_provider(db_provider: MCPToolProvider, for_list: bool = False) -> ToolProviderApiEntity: + def mcp_provider_to_user_provider( + db_provider: MCPToolProvider, for_list: bool = False, user_name: str | None = None + ) -> ToolProviderApiEntity: + # Use provided user_name to avoid N+1 query, fallback to load_user() if not provided + if user_name is None: + user = db_provider.load_user() + user_name = user.name if user else None + # Convert to entity and use its API response method provider_entity = db_provider.to_entity() - user = db_provider.load_user() - response = provider_entity.to_api_response(user_name=user.name if user else None) + response = provider_entity.to_api_response(user_name=user_name) # Add additional fields specific to the transform response["id"] = db_provider.server_identifier if not for_list else db_provider.id response["tools"] = ToolTransformService.mcp_tool_to_user_tool( - db_provider, [MCPTool(**tool) for tool in json.loads(db_provider.tools)] + db_provider, [MCPTool(**tool) for tool in json.loads(db_provider.tools)], user_name=user_name ) response["server_identifier"] = db_provider.server_identifier @@ -257,11 +263,17 @@ class ToolTransformService: return ToolProviderApiEntity(**response) @staticmethod - def mcp_tool_to_user_tool(mcp_provider: MCPToolProvider, tools: list[MCPTool]) -> list[ToolApiEntity]: - user = mcp_provider.load_user() + def mcp_tool_to_user_tool( + mcp_provider: MCPToolProvider, tools: list[MCPTool], user_name: str | None = None + ) -> list[ToolApiEntity]: + # Use provided user_name to avoid N+1 query, fallback to load_user() if not provided + if user_name is None: + user = mcp_provider.load_user() + user_name = user.name if user else "Anonymous" + return [ ToolApiEntity( - author=user.name if user else "Anonymous", + author=user_name or "Anonymous", name=tool.name, label=I18nObject(en_US=tool.name, zh_Hans=tool.name), description=I18nObject(en_US=tool.description or "", zh_Hans=tool.description or ""),