{"record":{"id":"00f741956b9ae943","repo":"BerriAI/litellm","slug":"request-arguments-are-required","errorCode":null,"errorMessage":"Request arguments are required","messagePattern":"Request arguments are required","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"litellm/proxy/_experimental/mcp_server/server.py","lineNumber":3103,"sourceCode":"        name: str,\n        arguments: dict[str, object] | None = None,\n        user_api_key_auth: UserAPIKeyAuth | None = None,\n        mcp_auth_header: str | None = None,\n        mcp_servers: list[str] | None = None,\n        mcp_server_auth_headers: dict[str, dict[str, str]] | None = None,\n        oauth2_headers: dict[str, str] | None = None,\n        raw_headers: dict[str, str] | None = None,\n        **kwargs: Any,\n    ) -> CallToolResult:\n        \"\"\"\n        Call a specific tool with the provided arguments (handles prefixed tool names).\n        \"\"\"\n        start_time: Final = datetime.now()\n        litellm_logging_obj: Final[LiteLLMLoggingObj | None] = kwargs.get(\"litellm_logging_obj\", None)\n\n        try:\n            if arguments is None:\n                raise HTTPException(status_code=400, detail=\"Request arguments are required\")\n\n            ## CHECK IF USER IS ALLOWED TO CALL THIS TOOL\n            allowed_mcp_server_ids: Final = await global_mcp_server_manager.get_allowed_mcp_servers(\n                user_api_key_auth=user_api_key_auth,\n            )\n\n            allowed_mcp_servers: list[MCPServer] = []\n            for allowed_mcp_server_id in allowed_mcp_server_ids:\n                allowed_server = global_mcp_server_manager.get_mcp_server_by_id(allowed_mcp_server_id)\n                if allowed_server is not None:\n                    # Same request-time oauth2_flow backstop the listing path applies,\n                    # so a null-flow M2M-shape row is treated as M2M on tool calls too.\n                    allowed_server = MCPServerManager.resolve_oauth2_flow_for_request(allowed_server)\n                    allowed_mcp_servers.append(allowed_server)\n\n            allowed_mcp_servers = await _get_allowed_mcp_servers_from_mcp_server_names(\n                mcp_servers=mcp_servers,\n                allowed_mcp_servers=allowed_mcp_servers,","sourceCodeStart":3085,"sourceCodeEnd":3121,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/_experimental/mcp_server/server.py#L3085-L3121","documentation":"The session-based tool-call entry (call_tool) requires arguments to be a mapping; arguments=None is rejected immediately with HTTP 400 \"Request arguments are required\". The MCP protocol makes tools/call arguments optional, so minimal clients that omit the field hit this. An empty object is accepted — only None is refused. The REST endpoint (POST /mcp-rest/tools/call) defaults arguments to {} and never raises this.","triggerScenarios":"A tools/call JSON-RPC request without an \"arguments\" member; SDK calls like execute_mcp_tool(..., arguments=None); no-argument tools invoked with the field skipped entirely.","commonSituations":"Custom MCP clients following the protocol's optionality; agent frameworks passing None for parameterless tools; hand-rolled JSON-RPC senders.","solutions":["Always include \"arguments\": {} (or the real payload) in every tools/call request.","Update wrapper code that passes arguments=None to pass an empty dict instead.","Prefer POST /mcp-rest/tools/call for no-arg tools — it defaults arguments to {} for you."],"exampleFix":"# before\nresult = await session.call_tool(\"get_time\", arguments=None)\n\n# after\nresult = await session.call_tool(\"get_time\", arguments={})","handlingStrategy":"validation","validationCode":"def build_tool_call(name: str, arguments: dict | None) -> dict:\n    return {\"name\": name, \"arguments\": arguments if arguments is not None else {}}\n\npayload = build_tool_call(\"get_time\", None)  # never omit arguments","typeGuard":"def is_arguments_mapping(obj: object) -> bool:\n    \"\"\"MCP arguments may be optional per spec, but LiteLLM requires a mapping.\"\"\"\n    return isinstance(obj, dict)","tryCatchPattern":"try:\n    result = await session.call_tool(name, arguments=arguments)\nexcept Exception as e:\n    if \"Request arguments are required\" in str(e):\n        return await session.call_tool(name, arguments={})  # retry once with empty mapping\n    raise","preventionTips":["Default arguments to {} in every tool-call wrapper — never let None reach the wire.","In JSON-RPC clients, always emit the \"arguments\" member even for no-arg tools.","Prefer POST /mcp-rest/tools/call for no-arg tools; it defaults arguments to {} itself."],"tags":["mcp","request-validation","http-400","missing-parameter"],"backgroundTag":"missing-required-parameter","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T08:17:14.275Z"}