Skip to content

Commit 223f5dc

Browse files
committed
fix: skip output schema validation when tool returns is_error=True
When a tool with an inferred output_schema returns CallToolResult(is_error=True), the SDK's convert_result() calls model_validate(None) unconditionally, raising a pydantic error that replaces the intended error message. Skip output schema validation when the result signals an error, matching the TypeScript SDK fix (modelcontextprotocol/typescript-sdk#655). Fixes #2429
1 parent 3d7b311 commit 223f5dc

File tree

2 files changed

+27
-2
lines changed

2 files changed

+27
-2
lines changed

src/mcp/server/mcpserver/utilities/func_metadata.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def convert_result(self, result: Any) -> Any:
103103
the structured output.
104104
"""
105105
if isinstance(result, CallToolResult):
106-
if self.output_schema is not None:
106+
if self.output_schema is not None and not result.is_error:
107107
assert self.output_model is not None, "Output model must be set if output schema is defined"
108108
self.output_model.model_validate(result.structured_content)
109109
return result

tests/server/mcpserver/test_func_metadata.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from mcp.server.mcpserver.exceptions import InvalidSignature
1616
from mcp.server.mcpserver.utilities.func_metadata import func_metadata
17-
from mcp.types import CallToolResult
17+
from mcp.types import CallToolResult, TextContent
1818

1919

2020
class SomeInputModelA(BaseModel):
@@ -875,6 +875,31 @@ def func_returning_annotated_tool_call_result() -> Annotated[CallToolResult, Per
875875
meta.convert_result(func_returning_annotated_tool_call_result())
876876

877877

878+
def test_tool_call_result_annotated_is_error_skips_validation():
879+
"""Test that is_error=True skips output schema validation.
880+
881+
Regression test for https://github.com/modelcontextprotocol/python-sdk/issues/2429
882+
"""
883+
884+
class PersonClass(BaseModel):
885+
name: str
886+
887+
def func_returning_error() -> Annotated[CallToolResult, PersonClass]:
888+
return CallToolResult(
889+
content=[TextContent(type="text", text="Resource not found")],
890+
is_error=True,
891+
)
892+
893+
meta = func_metadata(func_returning_error)
894+
assert meta.output_schema is not None
895+
896+
result = meta.convert_result(func_returning_error())
897+
assert isinstance(result, CallToolResult)
898+
assert result.is_error is True
899+
assert isinstance(result.content[0], TextContent)
900+
assert result.content[0].text == "Resource not found"
901+
902+
878903
def test_tool_call_result_in_optional_is_rejected():
879904
"""Test that Optional[CallToolResult] raises InvalidSignature"""
880905

0 commit comments

Comments
 (0)