Fix nondeterministic required order in transformed tool schemas

Co-authored-by: Bill Easton <6384545+strawgate@users.noreply.github.com>
This commit is contained in:
Marvin Context Protocol 2026-07-20 11:42:14 +00:00
commit 9f5a9f34d8
2 changed files with 29 additions and 2 deletions

View file

@ -707,7 +707,9 @@ class TransformedTool(Tool):
schema = {
"type": "object",
"properties": new_props,
"required": list(new_required),
# Order by property insertion order so the schema is deterministic
# across processes (a bare set() order depends on PYTHONHASHSEED).
"required": [name for name in new_props if name in new_required],
"additionalProperties": False,
}
@ -899,7 +901,9 @@ class TransformedTool(Tool):
result = {
"type": "object",
"properties": merged_props,
"required": list(final_required),
# Order by property insertion order so the schema is deterministic
# across processes (a bare set() order depends on PYTHONHASHSEED).
"required": [name for name in merged_props if name in final_required],
"additionalProperties": False,
}

View file

@ -553,3 +553,26 @@ class TestInputSchema:
"additionalProperties": False,
}
)
class TestRequiredOrdering:
def test_required_follows_property_order(self):
"""Transformed `required` should match property order deterministically.
Regression test for a bug where `required` was built from a set, so its
order depended on PYTHONHASHSEED and differed across processes.
"""
def base(alpha: int, beta: str, gamma: float, delta: bool, epsilon: int) -> str:
return "x"
transformed = Tool.from_tool(
Tool.from_function(base),
transform_args={"alpha": ArgTransform(name="a")},
)
properties = list(transformed.parameters["properties"])
required = transformed.parameters["required"]
assert required == [name for name in properties if name in set(required)]
assert required == ["a", "beta", "gamma", "delta", "epsilon"]