diff --git a/src/mcp/server/mcpserver/utilities/func_metadata.py b/src/mcp/server/mcpserver/utilities/func_metadata.py index 4a7610637..1d303ea86 100644 --- a/src/mcp/server/mcpserver/utilities/func_metadata.py +++ b/src/mcp/server/mcpserver/utilities/func_metadata.py @@ -103,7 +103,7 @@ def convert_result(self, result: Any) -> Any: the structured output. """ if isinstance(result, CallToolResult): - if self.output_schema is not None: + if self.output_schema is not None and not result.is_error: assert self.output_model is not None, "Output model must be set if output schema is defined" self.output_model.model_validate(result.structured_content) return result diff --git a/tests/server/mcpserver/test_func_metadata.py b/tests/server/mcpserver/test_func_metadata.py index c57d1ee9f..63098f257 100644 --- a/tests/server/mcpserver/test_func_metadata.py +++ b/tests/server/mcpserver/test_func_metadata.py @@ -875,6 +875,24 @@ def func_returning_annotated_tool_call_result() -> Annotated[CallToolResult, Per meta.convert_result(func_returning_annotated_tool_call_result()) +def test_tool_call_result_with_is_error_skips_output_schema_validation(): + """Test that convert_result skips outputSchema validation when is_error=True.""" + + class PersonClass(BaseModel): + name: str + + def func_with_error() -> Annotated[CallToolResult, PersonClass]: + return CallToolResult(content=[], is_error=True) + + meta = func_metadata(func_with_error) + assert meta.output_schema is not None + + result = meta.convert_result(func_with_error()) + + assert isinstance(result, CallToolResult) + assert result.is_error is True + + def test_tool_call_result_in_optional_is_rejected(): """Test that Optional[CallToolResult] raises InvalidSignature"""