diff --git a/fastmcp_slim/fastmcp/tools/tool_transform.py b/fastmcp_slim/fastmcp/tools/tool_transform.py index 910b89262..c11f58eda 100644 --- a/fastmcp_slim/fastmcp/tools/tool_transform.py +++ b/fastmcp_slim/fastmcp/tools/tool_transform.py @@ -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, } diff --git a/tests/tools/tool_transform/test_schemas.py b/tests/tools/tool_transform/test_schemas.py index 703dd7955..8b0d8069a 100644 --- a/tests/tools/tool_transform/test_schemas.py +++ b/tests/tools/tool_transform/test_schemas.py @@ -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"]