Skip to content

clean_schema_for_display() strips anyOf and loses items for Optional[List[X]] parameters #304

@visualfox-ch

Description

@visualfox-ch

Summary

clean_schema_for_display() in fastapi_mcp/openapi/utils.py (lines 76-86) removes anyOf from schemas but does not hoist the items field from array variants to the top level. This produces {type: "array"} without items, which is invalid JSON Schema.

MCP clients (VS Code Copilot, Claude Code) reject the tool with: Error: tool parameters array type must have items.

Root Cause

For Optional[List[X]] parameters, Pydantic v2 generates:

{
  "anyOf": [
    {"type": "array", "items": {"type": "object", ...}},
    {"type": "null"}
  ]
}

clean_schema_for_display() strips anyOf (line 76). Then convert.py line 248 calls get_single_param_type_from_schema() which returns "array" from the anyOf, and sets type: "array" on the property — but items was inside the anyOf variant and is now gone.

Result: {type: "array", title: "Images"} — no items.

Reproduction

from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional, List

app = FastAPI()

class MyRequest(BaseModel):
    tags: Optional[List[str]] = None

@app.post("/test")
def test(req: MyRequest):
    return {"ok": True}

# Mount fastapi_mcp, then inspect the MCP tool schema for /test
# The 'tags' parameter will have {type: "array"} without items

Affected Versions

  • 0.3.7 — confirmed
  • 0.4.0 — confirmed (same code path)

Suggested Fix

In clean_schema_for_display(), before stripping anyOf, hoist items and type from array variants:

if "anyOf" in schema:
    for variant in schema["anyOf"]:
        if isinstance(variant, dict) and variant.get("type") == "array":
            if "items" in variant:
                schema.setdefault("items", variant["items"])
            schema.setdefault("type", "array")
            break

Environment

  • fastapi-mcp: 0.3.7, 0.4.0
  • FastAPI: 0.115+
  • Python: 3.11
  • MCP clients: VS Code Copilot Chat (v0.45.1), Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    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