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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions