Give recursive tool return types an object-rooted output schema by maxisbey · Pull Request #3376 · modelcontextprotocol/python-sdk · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion src/mcp/server/mcpserver/utilities/func_metadata.py
28 changes: 28 additions & 0 deletions tests/server/mcpserver/test_func_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,34 @@ def func_nested() -> PersonWithAddress: # pragma: no cover
}


def test_structured_output_self_referential_model_gets_an_object_root():
"""pydantic publishes a recursive model as a bare root `$ref`; the definition is inlined onto the
root and `$defs` is kept for the nested reference."""

class Node(BaseModel):
name: str
children: list["Node"] = []

def tree() -> Node:
return Node(name="root", children=[Node(name="leaf")])

node_definition: dict[str, Any] = {
"properties": {
"name": {"title": "Name", "type": "string"},
"children": {"default": [], "items": {"$ref": "#/$defs/Node"}, "title": "Children", "type": "array"},
},
"required": ["name"],
"title": "Node",
"type": "object",
}
meta = func_metadata(tree)
assert meta.output_schema == {**node_definition, "$defs": {"Node": node_definition}}

result = meta.convert_result(tree())
assert isinstance(result, CallToolResult)
assert result.structured_content == {"name": "root", "children": [{"name": "leaf", "children": []}]}


def test_structured_output_unserializable_type_error():
"""Test error when structured_output=True is used with unserializable types"""

Expand Down
23 changes: 23 additions & 0 deletions tests/server/mcpserver/test_server.py
Loading