Skip to content

[Bug] Request body handling ignores non-object schemas and suffers from parameter name collisions #319

Description

@saitejabandaru-in

There are two significant issues with how requestBody parameters and schemas are converted and executed, leading to unreachable endpoints or 422 Unprocessable Entity errors.

1. Non-object request bodies are silently ignored

If a FastAPI endpoint expects a top-level array, primitive, or a custom __root__ object, it is completely omitted from the generated MCP tool schema.

In convert.py (convert_openapi_to_mcp_tools):

if "properties" in schema:
    for prop_name, prop_schema in schema["properties"].items():
        # ... added to body_params

If schema is {"type": "array", "items": {...}}, it lacks a properties field. The MCP tool will be generated with no input arguments for the body, making it impossible for an LLM to provide the required payload for endpoints like bulk updates.

2. Parameter name collisions cause data loss during execution

Because path_params, query_params, and body_params are flattened into a single properties dictionary for the MCP tool's inputSchema, name collisions (e.g., a path parameter named id and a JSON body field named id) overwrite each other.

Even worse, during tool execution in server.py (_execute_api_tool):

for param in parameters:
    if param.get("in") == "path" and param.get("name") in arguments:
        param_name = param.get("name", None)
        path = path.replace(f"{{{param_name}}}", str(arguments.pop(param_name)))

If an argument matches a path parameter, it is pop()ped from arguments. When the remaining arguments are passed as the request body (body = arguments if arguments else None), the id field is now missing from the body payload, which will likely trigger a 422 Unprocessable Entity error from FastAPI.

Suggested Fixes

  1. Schema Conversion: Instead of flattening request body properties into the root of arguments, nest them under a specific key (e.g., requestBody or payload), or explicitly handle non-object schemas by mapping the entire body to a single argument.
  2. Argument Extraction: Instead of arguments.pop(param_name), use arguments.get(param_name) to retain the value if it also needs to be passed in the JSON body, or cleanly separate routing parameters from body parameters in the MCP schema.

Activity

  1. K4bain commented on Sep 2, 2026

    @K4bain

    Investigated and opened a fix for both parts: #344.

    • Non-object bodies (top-level arrays / __root__ primitives) now map to a single payload argument carrying the body schema — previously the tool was generated with no body input and the endpoint was unreachable.
    • Name collisions no longer lose body data: the operation map records which arguments are body fields (body_param_names), and the executor rebuilds the body before consuming routing params, so a body item_id reaches the JSON payload even when item_id is also substituted into the path. The route param keeps the inputSchema slot and a warning is logged at conversion time. (Fully separating the namespaces via nesting would change the tool schema shape for every existing consumer — left out as a breaking change, can be added behind an opt-in flag if wanted.)
    • Legacy operation_map entries without the metadata keep the exact old behavior.

    Reproduced the 422 end-to-end on main first (path /orders/42 + body {"quantity": 7} with item_id missing), then regression-tested the corrected behavior plus the raw-body passthrough and the legacy fallback.

  2. K4bain commented on Sep 3, 2026

    @K4bain

    This is fixed by #344 (non-object request bodies + parameter name collision handling). #307's union-type rejection is separately fixed by #345. Both PRs include tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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