Skip to content

Commit ca9f8de

Browse files
committed
fix(server): don't raise when tool_use is not followed by tool_result
Closes #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. The ID-matching check in validate_tool_use_result_messages ran whenever the previous message contained a tool_use, regardless of whether the last message actually had any tool_result blocks. With no tool_result blocks present, tool_result_ids was empty and never matched the non-empty tool_use_ids, raising a false 'ids ... do not match' ValueError. Gate the check on has_tool_results so it only runs when the last message actually contains tool_result blocks. Adds a regression test that fails without the fix.
1 parent a527142 commit ca9f8de

2 files changed

Lines changed: 25 additions & 1 deletion

File tree

src/mcp/server/validation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def validate_tool_use_result_messages(messages: list[SamplingMessage]) -> None:
8080
if not has_previous_tool_use:
8181
raise ValueError("tool_result blocks do not match any tool_use in the previous message")
8282

83-
if has_previous_tool_use and previous_content:
83+
if has_tool_results and has_previous_tool_use and previous_content:
8484
tool_use_ids = {c.id for c in previous_content if c.type == "tool_use"}
8585
tool_result_ids = {c.tool_use_id for c in last_content if c.type == "tool_result"}
8686
if tool_use_ids != tool_result_ids:

tests/server/test_validation.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,3 +159,27 @@ def test_validate_tool_use_result_messages_no_error_when_tool_result_matches_too
159159
),
160160
]
161161
validate_tool_use_result_messages(messages) # Should not raise
162+
163+
164+
def test_validate_tool_use_result_messages_no_error_when_tool_use_not_followed_by_tool_result() -> None:
165+
"""No error when a tool_use is followed by a plain (non-tool_result) response.
166+
167+
Regression test for #2960: per SEP-1577, tool_result blocks MUST be preceded
168+
by a tool_use block, but there is NO requirement that a tool_use MUST be
169+
followed by a tool_result. A plain text response after a tool_use is valid and
170+
must not raise a false "ids do not match" ValueError.
171+
"""
172+
messages = [
173+
SamplingMessage(
174+
role="assistant",
175+
content=[
176+
TextContent(type="text", text="Hold on..."),
177+
ToolUseContent(type="tool_use", id="abc", name="search", input={"q": "test"}),
178+
],
179+
),
180+
SamplingMessage(
181+
role="user",
182+
content=TextContent(type="text", text="Thanks, no results needed"),
183+
),
184+
]
185+
validate_tool_use_result_messages(messages) # Should not raise

0 commit comments

Comments
 (0)