@@ -265,10 +265,9 @@ def test_from_any_openapi_schema_empty(self):
265265 schema = ToolSchema .from_any_openapi_schema (None )
266266 assert schema .type == "string"
267267
268- # Empty dict creates a ToolSchema with None type (pydash_get returns None for missing keys)
268+ # Empty dict is a valid JSON Schema meaning "any" — should not collapse to "string"
269269 schema = ToolSchema .from_any_openapi_schema ({})
270- # Actually returns "string" due to the check at the beginning of the method
271- assert schema .type == "string"
270+ assert schema .type is None
272271
273272 def test_from_any_openapi_schema_non_dict (self ):
274273 """测试从非 dict 输入创建"""
@@ -335,6 +334,72 @@ def test_from_any_openapi_schema_allof(self):
335334 assert schema .all_of [0 ].type == "object"
336335 assert schema .all_of [1 ].type == "object"
337336
337+ def test_from_any_openapi_schema_additional_properties_schema (self ):
338+ """测试 additionalProperties 为 schema 对象时的解析"""
339+ openapi_schema = {
340+ "type" : "object" ,
341+ "properties" : {
342+ "filters" : {
343+ "type" : "object" ,
344+ "additionalProperties" : {
345+ "anyOf" : [
346+ {"type" : "string" },
347+ {"type" : "integer" },
348+ ]
349+ },
350+ }
351+ },
352+ }
353+
354+ schema = ToolSchema .from_any_openapi_schema (openapi_schema )
355+
356+ assert schema .properties is not None
357+ assert "filters" in schema .properties
358+ filters_schema = schema .properties ["filters" ]
359+ assert filters_schema .additional_properties is not None
360+ assert filters_schema .additional_properties .any_of is not None
361+ assert len (filters_schema .additional_properties .any_of ) == 2
362+ assert filters_schema .additional_properties .any_of [0 ].type == "string"
363+ assert filters_schema .additional_properties .any_of [1 ].type == "integer"
364+
365+ json_schema = schema .to_json_schema ()
366+ assert (
367+ json_schema ["properties" ]["filters" ]["additionalProperties" ][
368+ "anyOf"
369+ ][0 ]["type" ]
370+ == "string"
371+ )
372+ assert (
373+ json_schema ["properties" ]["filters" ]["additionalProperties" ][
374+ "anyOf"
375+ ][1 ]["type" ]
376+ == "integer"
377+ )
378+
379+ def test_from_any_openapi_schema_empty_additional_properties_schema (self ):
380+ """测试 additionalProperties 为空 schema 时保留原语义"""
381+ openapi_schema = {
382+ "type" : "object" ,
383+ "properties" : {
384+ "metadata" : {
385+ "type" : "object" ,
386+ "additionalProperties" : {},
387+ }
388+ },
389+ }
390+
391+ schema = ToolSchema .from_any_openapi_schema (openapi_schema )
392+
393+ assert schema .properties is not None
394+ metadata_schema = schema .properties ["metadata" ]
395+ assert metadata_schema .additional_properties is not None
396+ assert metadata_schema .additional_properties .type is None
397+
398+ json_schema = schema .to_json_schema ()
399+ assert (
400+ json_schema ["properties" ]["metadata" ]["additionalProperties" ] == {}
401+ )
402+
338403 def test_to_json_schema_simple (self ):
339404 """测试转换为 JSON Schema - 简单情况"""
340405 schema = ToolSchema (
@@ -659,3 +724,38 @@ def model_dump(self):
659724 assert "param1" in info .parameters .properties
660725 assert "param2" in info .parameters .properties
661726 assert info .parameters .required == ["param1" ]
727+
728+ def test_from_mcp_tool_with_schema_additional_properties (self ):
729+ """测试 MCP tool schema 中 additionalProperties 为对象时的解析"""
730+ mcp_tool = {
731+ "name" : "get_news_by_date" ,
732+ "description" : "A tool with MCP schema-style date_range" ,
733+ "inputSchema" : {
734+ "type" : "object" ,
735+ "properties" : {
736+ "date_range" : {
737+ "anyOf" : [
738+ {"type" : "string" },
739+ {
740+ "type" : "object" ,
741+ "additionalProperties" : {"type" : "string" },
742+ },
743+ {"type" : "null" },
744+ ]
745+ }
746+ },
747+ },
748+ }
749+
750+ info = ToolInfo .from_mcp_tool (mcp_tool )
751+
752+ assert info .name == "get_news_by_date"
753+ assert info .parameters is not None
754+ assert info .parameters .properties is not None
755+ date_range_schema = info .parameters .properties ["date_range" ]
756+ assert date_range_schema .any_of is not None
757+ assert date_range_schema .any_of [1 ].type == "object"
758+ assert date_range_schema .any_of [1 ].additional_properties is not None
759+ assert (
760+ date_range_schema .any_of [1 ].additional_properties .type == "string"
761+ )
0 commit comments