diff --git a/src/serena/mcp.py b/src/serena/mcp.py index ef8b04c..6bbfc5f 100644 --- a/src/serena/mcp.py +++ b/src/serena/mcp.py @@ -61,7 +61,10 @@ def make_tool( # Mount the tool description as a combination of the docstring description and # the return value description, if it exists. - func_doc = f"{docstring.description.strip().strip('.')}." + if docstring.description: + func_doc = f"{docstring.description.strip().strip('.')}." + else: + func_doc = "No description available." if (docstring.returns) and (docstring_returns := docstring.returns.description): func_doc = f"{func_doc} Returns {docstring_returns.strip().strip('.')}." diff --git a/test/serena/test_mcp.py b/test/serena/test_mcp.py index 6e6a8ae..0e90e76 100644 --- a/test/serena/test_mcp.py +++ b/test/serena/test_mcp.py @@ -198,3 +198,65 @@ def test_make_tool_missing_apply() -> None: with pytest.raises(AttributeError): make_tool(tool) + + +def test_make_tool_missing_description() -> None: + """Test make_tool with a function that has no description in the docstring.""" + + class NoDescriptionTool(BaseMockTool): + def apply(self, param: str) -> str: + """ + :param param: The parameter + :return: A result + """ + return f"Result: {param}" + + def apply_ex(self, *args, **kwargs) -> str: + return self.apply(**kwargs) + + tool = NoDescriptionTool() + mcp_tool = make_tool(tool) + + assert mcp_tool.name == "no_description" + assert mcp_tool.description == "No description available. Returns A result." + assert mcp_tool.parameters["properties"]["param"]["description"] == "The parameter." + + +def test_make_tool_all_tools() -> None: + """Test that make_tool works for all tools in the codebase.""" + from serena.agent import iter_tool_classes, Tool + + # Create a mock agent for tool initialization + class MockAgent: + def __init__(self): + self.project_config = None + self.serena_config = None + + # Get all tool classes + tool_classes = list(iter_tool_classes()) + + # Make sure we found some tools + assert len(tool_classes) > 0 + + # Test each tool class + for tool_class in tool_classes: + try: + # Skip abstract base classes that can't be instantiated + if tool_class.__name__ == "Tool" or getattr(tool_class, "__abstractmethods__", set()): + continue + + # Create an instance of the tool + tool_instance = tool_class(MockAgent()) + + # Try to create an MCP tool from it + mcp_tool = make_tool(tool_instance) + + # Basic validation + assert isinstance(mcp_tool, MCPTool) + assert mcp_tool.name == tool_class.get_name() + + # The description should be a string (either from docstring or default) + assert isinstance(mcp_tool.description, str) + + except Exception as e: + pytest.fail(f"Failed to create MCP tool for {tool_class.__name__}: {e}")