mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-22 21:44:18 +02:00
fix: handle circular JSON Pointer $ref in dereference_refs (#3896)
Co-authored-by: lawrence3699 <lawrence3699@users.noreply.github.com> Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
This commit is contained in:
parent
a075b14432
commit
5009d64465
2 changed files with 41 additions and 2 deletions
|
|
@ -172,8 +172,11 @@ def dereference_refs(schema: dict[str, Any]) -> dict[str, Any]:
|
|||
|
||||
return dereferenced
|
||||
|
||||
except JsonRefError:
|
||||
# Self-referencing/circular schemas can't be fully dereferenced
|
||||
except (JsonRefError, RecursionError):
|
||||
# Self-referencing/circular schemas can't be fully dereferenced.
|
||||
# RecursionError covers circular $ref using JSON Pointer paths
|
||||
# (e.g. "#/properties/nodes/items") that bypass $defs-based cycle
|
||||
# detection — common in schemas from .NET/System.Text.Json.
|
||||
# Fall back to resolving only root-level $ref (for MCP spec compliance)
|
||||
return resolve_root_ref(schema)
|
||||
|
||||
|
|
|
|||
|
|
@ -128,6 +128,42 @@ class TestDereferenceRefs:
|
|||
assert result.get("type") == "object"
|
||||
assert "$defs" in result # $defs preserved for circular refs
|
||||
|
||||
def test_falls_back_for_circular_json_pointer_refs(self):
|
||||
"""Test that circular JSON Pointer $ref (non-$defs) does not crash.
|
||||
|
||||
.NET/System.Text.Json emits $ref with JSON Pointer paths like
|
||||
#/properties/nodes/items instead of $defs-based references.
|
||||
Circular pointers must not cause a RecursionError.
|
||||
"""
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nodes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"value": {"type": "string"},
|
||||
"children": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/properties/nodes/items"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
result = dereference_refs(schema)
|
||||
|
||||
# Should return the schema without crashing; circular refs stay unresolved
|
||||
assert result["type"] == "object"
|
||||
assert "properties" in result
|
||||
# The circular $ref should be preserved (not inlined)
|
||||
children_items = result["properties"]["nodes"]["items"]["properties"][
|
||||
"children"
|
||||
]["items"]
|
||||
assert children_items["$ref"] == "#/properties/nodes/items"
|
||||
|
||||
def test_preserves_sibling_keywords(self):
|
||||
"""Test that sibling keywords (default, description) are preserved.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue