Skip to content

[Bug]: McpToolSpec.create_model_from_json_schema drops nested inline object properties #22141

Description

@schelv

Bug Description

McpToolSpec.create_model_from_json_schema does not handle inline nested objects (objects defined directly under a properties entry with their own properties/required, rather than via a $ref/$defs). Instead of recursively building a nested Pydantic model, the nested object is collapsed into a bare Dict, so its properties and required are lost.

Where

  • Package: llama-index-tools-mcp
  • File: llama-index-integrations/tools/llama-index-tools-mcp/llama_index/tools/mcp/tool_spec_mixins.py

Version

llama-index-tools-mcp==0.4.8
llama-index-core==0.14.23

Steps to Reproduce

from llama_index.tools.mcp import McpToolSpec, BasicMCPClient

schema = {
    "properties": {
        "nested": {
            "properties": {"value": {"title": "Value", "type": "string"}},
            "required": ["value"],
            "type": "object",
        },
    },
    "required": ["nested"],
    "type": "object",
}

tool_spec = McpToolSpec(BasicMCPClient("python", args=["server.py"]), allowed_tools=[])
model = tool_spec.create_model_from_json_schema(schema)
out = model.model_json_schema()
del out["title"]
print(out)

Expected Behavior

Build a model from the JSON schema using McpToolSpec.create_model_from_json_schema , and then calling model_json_schema() should reproduce the input schema with the nested object structure:

{
    "properties": {
        "nested": {
            "properties": {"value": {"title": "Value", "type": "string"}},
            "required": ["value"],
            "type": "object",
        }
    },
    "required": ["nested"],
    "type": "object",
}

Actual Behavior

The nested field is generated as a bare dict, losing its inner properties:

{
    "properties": {
        "nested": {"additionalProperties": True, "title": "Nested", "type": "object"}
    },
    "required": ["nested"],
    "type": "object",
}

Test Case

I've created a test case that can be added to llama-index-integrations/tools/llama-index-tools-mcp/tests/test_tools_mcp.py.
Only the nested-object test case fails; referenced-nested-object passes and is included as a contrast.

base_test_cases = [
    pytest.param(
        {
            "properties": {
                "nested": {
                    "properties": {
                        "value": {"title": "Value", "type": "string"},
                    },
                    "required": ["value"],
                    "type": "object",
                },
            },
            "required": ["nested"],
            "type": "object",
        },
        id="nested-object",
    ),
    pytest.param(
        {
            "$defs": {
                "Inner": {
                    "properties": {
                        "value": {"title": "Value", "type": "string"},
                    },
                    "required": ["value"],
                    "title": "Inner",
                    "type": "object",
                },
            },
            "properties": {
                "nested": {"$ref": "#/$defs/Inner"},
            },
            "required": ["nested"],
            "type": "object",
        },
        id="referenced-nested-object",
    ),
]


@pytest.mark.parametrize(
    "json_schema",
    [
        *base_test_cases,
    ],
)
def test_create_model_from_json_schema(client: BasicMCPClient, json_schema: dict):
    """Converting a JSON schema into a Pydantic model and back to a JSON schema should yield the original schema."""
    tool_spec = McpToolSpec(client, allowed_tools=[])
    pydantic_model = tool_spec.create_model_from_json_schema(json_schema)
    json_schema_from_pydantic_model = pydantic_model.model_json_schema()

    del json_schema_from_pydantic_model["title"]

    assert json_schema == json_schema_from_pydantic_model

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtriageIssue needs to be triaged/prioritized

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions