Spaces:
Running
Running
Jeremiah Lowin
Fix breaking change: restore output_schema=False compatibility (#1482)
3685b24 unverified | """Test deprecated output_schema=False behavior (deprecated in 2.11.4).""" | |
| import warnings | |
| import pytest | |
| from fastmcp import FastMCP | |
| from fastmcp.tools import Tool | |
| class TestDeprecatedOutputSchemaFalse: | |
| """Test that output_schema=False is deprecated but still works.""" | |
| async def test_tool_decorator_output_schema_false_deprecated(self): | |
| """Test that @mcp.tool(output_schema=False) shows deprecation warning.""" | |
| mcp = FastMCP() | |
| with pytest.warns( | |
| DeprecationWarning, match="output_schema=False is deprecated" | |
| ): | |
| # type: ignore[arg-type] | |
| def simple_tool() -> int: | |
| """A simple tool.""" | |
| return 42 | |
| # Verify the tool was created with None as output_schema | |
| tool = mcp._tool_manager._tools["simple_tool"] | |
| assert tool.output_schema is None | |
| async def test_tool_from_function_output_schema_false_deprecated(self): | |
| """Test that Tool.from_function(output_schema=False) shows deprecation warning.""" | |
| def my_function() -> str: | |
| """A simple function.""" | |
| return "hello" | |
| with pytest.warns( | |
| DeprecationWarning, match="output_schema=False is deprecated" | |
| ): | |
| tool = Tool.from_function(my_function, output_schema=False) # type: ignore[arg-type] | |
| # Verify the tool was created with None as output_schema | |
| assert tool.output_schema is None | |
| async def test_tool_from_tool_output_schema_false_deprecated(self): | |
| """Test that Tool.from_tool(output_schema=False) shows deprecation warning.""" | |
| # Create a parent tool | |
| def parent_function() -> dict[str, str]: | |
| """A parent function.""" | |
| return {"status": "ok"} | |
| parent_tool = Tool.from_function(parent_function) | |
| with pytest.warns( | |
| DeprecationWarning, match="output_schema=False is deprecated" | |
| ): | |
| transformed_tool = Tool.from_tool(parent_tool, output_schema=False) # type: ignore[arg-type] | |
| # Verify the tool was created with None as output_schema | |
| assert transformed_tool.output_schema is None | |
| async def test_output_schema_false_functionality_preserved(self): | |
| """Test that output_schema=False still works functionally like output_schema=None.""" | |
| mcp = FastMCP() | |
| # Create two tools - one with False, one with None | |
| with warnings.catch_warnings(): | |
| warnings.simplefilter("ignore", DeprecationWarning) | |
| # type: ignore[arg-type] | |
| def tool_with_false() -> dict[str, str]: | |
| """Tool with output_schema=False.""" | |
| return {"result": "false"} | |
| def tool_with_none() -> dict[str, str]: | |
| """Tool with output_schema=None.""" | |
| return {"result": "none"} | |
| # Both should have None as output_schema | |
| assert mcp._tool_manager._tools["tool_with_false"].output_schema is None | |
| assert mcp._tool_manager._tools["tool_with_none"].output_schema is None | |
| # Both should work the same way | |
| result_false = await mcp._tool_manager._tools["tool_with_false"].run({}) | |
| result_none = await mcp._tool_manager._tools["tool_with_none"].run({}) | |
| # Both should return structured content for dict-like objects | |
| assert result_false.structured_content == {"result": "false"} | |
| assert result_none.structured_content == {"result": "none"} | |
| async def test_output_schema_false_with_scalar_return(self): | |
| """Test that output_schema=False works with scalar returns (no structured content).""" | |
| mcp = FastMCP() | |
| with warnings.catch_warnings(): | |
| warnings.simplefilter("ignore", DeprecationWarning) | |
| # type: ignore[arg-type] | |
| def scalar_tool() -> int: | |
| """Tool returning a scalar.""" | |
| return 42 | |
| tool = mcp._tool_manager._tools["scalar_tool"] | |
| assert tool.output_schema is None | |
| result = await tool.run({}) | |
| # Scalar values don't produce structured content | |
| assert result.structured_content is None | |
| assert len(result.content) == 1 | |
| assert result.content[0].text == "42" # type: ignore[attr-defined] | |
| async def test_transform_with_output_schema_false(self): | |
| """Test that transformation with output_schema=False still works.""" | |
| # Create a parent tool | |
| def parent_function(x: int) -> dict[str, int]: | |
| """A parent function.""" | |
| return {"value": x * 2} | |
| parent_tool = Tool.from_function(parent_function) | |
| with warnings.catch_warnings(): | |
| warnings.simplefilter("ignore", DeprecationWarning) | |
| # Transform with output_schema=False | |
| transformed = Tool.from_tool( | |
| parent_tool, | |
| name="doubled", | |
| output_schema=False, # type: ignore[arg-type] | |
| ) | |
| assert transformed.output_schema is None | |
| # Tool should still work | |
| result = await transformed.run({"x": 5}) | |
| assert result.structured_content == {"value": 10} | |