diff --git a/src/fastmcp/utilities/openapi/schemas.py b/src/fastmcp/utilities/openapi/schemas.py index bb057f2eb..fa93b6c8d 100644 --- a/src/fastmcp/utilities/openapi/schemas.py +++ b/src/fastmcp/utilities/openapi/schemas.py @@ -123,6 +123,19 @@ def _replace_ref_with_defs( schema["additionalProperties"] = _replace_ref_with_defs( additionalProperties ) + # Handle propertyNames + if property_names := schema.get("propertyNames"): + if isinstance(property_names, dict): + schema["propertyNames"] = _replace_ref_with_defs(property_names) + # Handle patternProperties + if pattern_properties := schema.get("patternProperties"): + if isinstance(pattern_properties, dict): + schema["patternProperties"] = { + pattern: _replace_ref_with_defs(subschema) + if isinstance(subschema, dict) + else subschema + for pattern, subschema in pattern_properties.items() + } if info.get("description", description) and not schema.get("description"): schema["description"] = description return schema diff --git a/tests/utilities/openapi/test_propertynames_ref_rewrite.py b/tests/utilities/openapi/test_propertynames_ref_rewrite.py new file mode 100644 index 000000000..809363410 --- /dev/null +++ b/tests/utilities/openapi/test_propertynames_ref_rewrite.py @@ -0,0 +1,34 @@ +from fastmcp.utilities.openapi.schemas import _replace_ref_with_defs + + +def test_replace_ref_with_defs_rewrites_propertyNames_ref(): + """ + Regression test for issue #3303. + + When using dict[StrEnum, Model], Pydantic generates: + + { + "type": "object", + "propertyNames": {"$ref": "#/components/schemas/Category"}, + "additionalProperties": {"$ref": "#/components/schemas/ItemInfo"} + } + + _replace_ref_with_defs should rewrite BOTH refs to #/$defs/. + """ + + schema = { + "type": "object", + "propertyNames": {"$ref": "#/components/schemas/Category"}, + "additionalProperties": {"$ref": "#/components/schemas/ItemInfo"}, + } + + result = _replace_ref_with_defs(schema) + + # additionalProperties ref is rewritten + assert result["additionalProperties"]["$ref"] == "#/$defs/ItemInfo" + + # propertyNames ref must also be rewritten (this was the bug) + assert result["propertyNames"]["$ref"] == "#/$defs/Category" + + # Ensure no dangling OpenAPI refs remain + assert "#/components/schemas/" not in str(result)