diff --git a/src/mcp/server/validation.py b/src/mcp/server/validation.py index 08f5754f1..7c9eb87f7 100644 --- a/src/mcp/server/validation.py +++ b/src/mcp/server/validation.py @@ -80,7 +80,7 @@ def validate_tool_use_result_messages(messages: list[SamplingMessage]) -> None: if not has_previous_tool_use: raise ValueError("tool_result blocks do not match any tool_use in the previous message") - if has_previous_tool_use and previous_content: + if has_tool_results and has_previous_tool_use and previous_content: tool_use_ids = {c.id for c in previous_content if c.type == "tool_use"} tool_result_ids = {c.tool_use_id for c in last_content if c.type == "tool_result"} if tool_use_ids != tool_result_ids: diff --git a/tests/server/test_validation.py b/tests/server/test_validation.py index 19f4eb108..646fc4240 100644 --- a/tests/server/test_validation.py +++ b/tests/server/test_validation.py @@ -159,3 +159,27 @@ def test_validate_tool_use_result_messages_no_error_when_tool_result_matches_too ), ] validate_tool_use_result_messages(messages) # Should not raise + + +def test_validate_tool_use_result_messages_no_error_when_tool_use_not_followed_by_tool_result() -> None: + """No error when a tool_use is followed by a plain (non-tool_result) response. + + Regression test for #2960: per SEP-1577, tool_result blocks MUST be preceded + by a tool_use block, but there is NO requirement that a tool_use MUST be + followed by a tool_result. A plain text response after a tool_use is valid and + must not raise a false "ids do not match" ValueError. + """ + messages = [ + SamplingMessage( + role="assistant", + content=[ + TextContent(type="text", text="Hold on..."), + ToolUseContent(type="tool_use", id="abc", name="search", input={"q": "test"}), + ], + ), + SamplingMessage( + role="user", + content=TextContent(type="text", text="Thanks, no results needed"), + ), + ] + validate_tool_use_result_messages(messages) # Should not raise