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

Author: visualfox-chCreated May 1, 2026Updated Sep 3, 2026

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:

json
{
  "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

python
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:

python
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