From a1a867de376a0d896add776f1c774ff8bcfc4643 Mon Sep 17 00:00:00 2001 From: Guillaume Raille Date: Wed, 17 Sep 2025 10:05:39 +0200 Subject: [PATCH 1/2] add a test reproducing issue #71 --- tests/test_crewai_adapter.py | 66 ++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/test_crewai_adapter.py b/tests/test_crewai_adapter.py index 06281c6..aee8150 100644 --- a/tests/test_crewai_adapter.py +++ b/tests/test_crewai_adapter.py @@ -430,3 +430,69 @@ def test_multiple_content_response_handling(multiple_content_server_script): # Should return string representation of list of text contents expected = str(["First response", "Second response", "Third response"]) assert result == expected + + +@pytest.fixture +def empty_union_server_script(): + """Server that returns a tool with a schema containing only null types in anyOf.""" + return dedent( + """ + import mcp.types as types + from mcp.server.lowlevel import Server + from mcp.server.stdio import stdio_server + import anyio + + app = Server("empty-union-server") + + @app.list_tools() + async def list_tools() -> list[types.Tool]: + return [ + types.Tool( + name="problematic_tool", + description="Tool with empty union after null filtering", + inputSchema={ + "type": "object", + "properties": { + "nullable_only": { + "anyOf": [ + {"type": "null"} + ] + }, + "type_array_null_only": { + "type": ["null"] + } + }, + }, + ) + ] + + @app.call_tool() + async def call_tool(name: str, arguments: dict | None) -> list[types.TextContent]: + return [types.TextContent(type="text", text="Success")] + + async def arun(): + async with stdio_server() as streams: + await app.run( + streams[0], streams[1], app.create_initialization_options() + ) + + anyio.run(arun) + """ + ) + + +def test_empty_union_type_error(empty_union_server_script): + """Test that empty unions (anyOf with only null types) raise TypeError. + + This test reproduces issue #71 where schemas with anyOf containing only + null types cause 'Cannot take a Union of no types' error. + """ + with MCPAdapt( + StdioServerParameters( + command="uv", + args=["run", "python", "-c", empty_union_server_script], + ), + CrewAIAdapter(), + ) as tools: + # Should raise TypeError before reaching here + pass From c1e11e846c1caf5150f2e5ada946b51fd8cf9159 Mon Sep 17 00:00:00 2001 From: Guillaume Raille Date: Wed, 17 Sep 2025 10:10:06 +0200 Subject: [PATCH 2/2] fixes Cannot take a Union of no types. Fixes #71 --- src/mcpadapt/utils/modeling.py | 15 +++++++++++---- tests/test_crewai_adapter.py | 7 +++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/mcpadapt/utils/modeling.py b/src/mcpadapt/utils/modeling.py index 49fbf76..6ac704c 100644 --- a/src/mcpadapt/utils/modeling.py +++ b/src/mcpadapt/utils/modeling.py @@ -132,7 +132,12 @@ def get_field_type(field_name: str, field_schema: Dict[str, Any], required: set) types.append(created_models[ref_name]) - field_type = types[0] if len(types) == 1 else Union[tuple(types)] # type: ignore + if len(types) == 0: + field_type = Any + elif len(types) == 1: + field_type = types[0] + else: + field_type = Union[tuple(types)] # type: ignore default = field_schema.get("default") is_required = field_name in required and default is None @@ -158,10 +163,12 @@ def get_field_type(field_name: str, field_schema: Dict[str, Any], required: set) mapped_type = json_type_mapping.get(t, Any) types.append(mapped_type) - if len(types) > 1: - field_type = Union[tuple(types)] # type: ignore + if len(types) == 0: + field_type = Any + elif len(types) == 1: + field_type = types[0] else: - field_type = types[0] if types else Any + field_type = Union[tuple(types)] # type: ignore else: # Original code for simple types field_type = json_type_mapping.get(json_type, Any) # type: ignore diff --git a/tests/test_crewai_adapter.py b/tests/test_crewai_adapter.py index aee8150..679e6d7 100644 --- a/tests/test_crewai_adapter.py +++ b/tests/test_crewai_adapter.py @@ -482,9 +482,9 @@ async def arun(): def test_empty_union_type_error(empty_union_server_script): - """Test that empty unions (anyOf with only null types) raise TypeError. + """Test that empty unions (anyOf with only null types) do not raise TypeError. - This test reproduces issue #71 where schemas with anyOf containing only + This test reproduced issue #71 where schemas with anyOf containing only null types cause 'Cannot take a Union of no types' error. """ with MCPAdapt( @@ -494,5 +494,4 @@ def test_empty_union_type_error(empty_union_server_script): ), CrewAIAdapter(), ) as tools: - # Should raise TypeError before reaching here - pass + assert len(tools) == 1