@@ -66,19 +66,26 @@ def __init__(
6666 description : str ,
6767 parameters : dict [str , Any ],
6868 annotations : "Optional[ToolAnnotations]" = None ,
69+ strict : Optional [bool ] = None ,
6970 ):
7071 self .name = name
7172 self .func = func
7273 self .annotations = annotations
7374 self ._is_async = _utils .is_async_callable (func )
74- self .schema : "ChatCompletionToolParam" = {
75- "type" : "function" ,
76- "function" : {
77- "name" : name ,
78- "description" : description ,
79- "parameters" : parameters ,
80- },
75+ func_schema : dict [str , Any ] = {
76+ "name" : name ,
77+ "description" : description ,
78+ "parameters" : parameters ,
8179 }
80+ if strict is not None :
81+ func_schema ["strict" ] = strict
82+ self .schema : "ChatCompletionToolParam" = cast (
83+ "ChatCompletionToolParam" ,
84+ {
85+ "type" : "function" ,
86+ "function" : func_schema ,
87+ },
88+ )
8289
8390 @classmethod
8491 def from_func (
@@ -230,6 +237,9 @@ async def _call(**args: Any) -> AsyncGenerator[ContentToolResult, None]:
230237 description = mcp_tool .description or "" ,
231238 parameters = params ,
232239 annotations = annotations ,
240+ # MCP tools use standard JSON Schema conventions for optional params
241+ # (not in required array), which requires strict=False for OpenAI
242+ strict = False ,
233243 )
234244
235245
@@ -406,8 +416,6 @@ def sanitize_schema(
406416 - `title`: Pydantic includes titles at model/field level, but they're not needed
407417 - `format`: JSON Schema format hints (e.g., "uri", "date-time") that some
408418 providers like OpenAI reject
409- - `required`: OpenAI requires all properties to be in required array, with
410- optional params using anyOf with null type
411419 """
412420 if "title" in params :
413421 del params ["title" ]
@@ -416,28 +424,10 @@ def sanitize_schema(
416424 del params ["format" ]
417425
418426 if "properties" in params and isinstance (params ["properties" ], dict ):
419- required_keys = set (params .get ("required" , []))
420-
421- for key , prop in params ["properties" ].items ():
427+ for prop in params ["properties" ].values ():
422428 if isinstance (prop , dict ):
423429 sanitize_schema (prop )
424430
425- # For optional properties (not in required), make them nullable
426- # OpenAI requires all props in required array, using anyOf with
427- # null type to indicate optionality
428- if key not in required_keys :
429- prop_type = prop .get ("type" )
430- if prop_type and prop_type != "null" :
431- # Convert to anyOf with null
432- prop ["anyOf" ] = [{"type" : prop_type }, {"type" : "null" }]
433- del prop ["type" ]
434- elif "anyOf" not in prop and "oneOf" not in prop :
435- # No type specified, just allow null
436- prop ["anyOf" ] = [prop .copy (), {"type" : "null" }]
437-
438- # OpenAI requires all properties to be in required array
439- params ["required" ] = list (params ["properties" ].keys ())
440-
441431 return params
442432
443433
0 commit comments