diff --git a/src/mcp/server/mcpserver/utilities/func_metadata.py b/src/mcp/server/mcpserver/utilities/func_metadata.py index 0ffac07c4e..3dfc79ce2c 100644 --- a/src/mcp/server/mcpserver/utilities/func_metadata.py +++ b/src/mcp/server/mcpserver/utilities/func_metadata.py @@ -109,7 +109,7 @@ def model_dump_one_level(self) -> dict[str, Any]: kwargs[output_name] = value return kwargs - model_config = ConfigDict(arbitrary_types_allowed=True) + model_config = ConfigDict(arbitrary_types_allowed=True, hide_input_in_errors=True) class FuncMetadata(BaseModel): diff --git a/tests/server/mcpserver/test_func_metadata.py b/tests/server/mcpserver/test_func_metadata.py index dba0637ded..3725eaebdb 100644 --- a/tests/server/mcpserver/test_func_metadata.py +++ b/tests/server/mcpserver/test_func_metadata.py @@ -1520,3 +1520,22 @@ def fn() -> StepA | StepB: ... # pragma: no branch meta = func_metadata(fn) assert meta.output_schema is None + + +def test_validation_error_does_not_echo_input_value(): + """Tool validation errors must not leak the rejected input value (PII/PHI risk). + + Regression test for: https://github.com/modelcontextprotocol/python-sdk/issues/3572 + """ + + def fn(name: str, age: int) -> str: ... # pragma: no branch + + meta = func_metadata(fn) + with pytest.raises(Exception) as exc_info: + meta.arg_model.model_validate({"name": "Alice", "age": "not-a-number"}) + + error_text = str(exc_info.value) + assert "not-a-number" not in error_text, "Rejected input value must not appear in validation error message" + assert "int_parsing" in error_text or "int" in error_text.lower(), ( + "Error should still describe the rule (type mismatch)" + )