Two functions that claim to return new schemas actually mutate their input.
1. _prune_param (utilities/json_schema.py)
Docstring says "Return a new schema with param removed" but uses .pop() and .remove() on the input dict:
import copy
from fastmcp.utilities.json_schema import _prune_param
schema = {'properties': {'a': {'type': 'string'}, 'b': {'type': 'int'}}, 'required': ['a', 'b']}
backup = copy.deepcopy(schema)
_prune_param(schema, 'a')
assert schema == backup # FAILS — 'a' was removed from the original
2. _convert_nullable_field (utilities/openapi/json_schema_converter.py)
Uses schema.copy() (shallow) then .append() on the shared anyOf list:
from fastmcp.utilities.openapi.json_schema_converter import _convert_nullable_field
schema = {"anyOf": [{"type": "string"}], "nullable": True}
original_len = len(schema["anyOf"])
_convert_nullable_field(schema)
assert len(schema["anyOf"]) == original_len # FAILS — null type appended to original list
Both can corrupt shared schema objects when the same schema dict is referenced from multiple places (common with $ref resolution).
Two functions that claim to return new schemas actually mutate their input.
1. _prune_param (utilities/json_schema.py)
Docstring says "Return a new schema with param removed" but uses
.pop()and.remove()on the input dict:2. _convert_nullable_field (utilities/openapi/json_schema_converter.py)
Uses
schema.copy()(shallow) then.append()on the sharedanyOflist:Both can corrupt shared schema objects when the same schema dict is referenced from multiple places (common with $ref resolution).