11import json
22import logging
3+ from collections .abc import Sequence
34from dataclasses import dataclass
4- from typing import Any , TypedDict
5+ from typing import Any , TypedDict , cast
56
67import pytest
78from pydantic import BaseModel
1213from mcp .server .mcpserver .tools import Tool , ToolManager
1314from mcp .server .mcpserver .utilities .func_metadata import ArgModelBase , FuncMetadata
1415from mcp .server .session import ServerSessionT
15- from mcp .types import TextContent , ToolAnnotations
16+ from mcp .types import ContentBlock , TextContent , ToolAnnotations
17+
18+
19+ def _text_contents (unstructured_content : Sequence [ContentBlock ]) -> list [TextContent ]:
20+ assert all (isinstance (item , TextContent ) for item in unstructured_content )
21+ return [cast (TextContent , item ) for item in unstructured_content ]
1622
1723
1824class TestAddTools :
@@ -456,7 +462,12 @@ def get_user(user_id: int) -> UserOutput:
456462 manager .add_tool (get_user )
457463 result = await manager .call_tool ("get_user" , {"user_id" : 1 }, Context (), convert_result = True )
458464 # don't test unstructured output here, just the structured conversion
459- assert len (result ) == 2 and result [1 ] == {"name" : "John" , "age" : 30 }
465+ assert isinstance (result , tuple )
466+ assert len (result ) == 2
467+ unstructured_content , structured_content = cast (tuple [Sequence [ContentBlock ], dict [str , Any ]], result )
468+ text_items = _text_contents (unstructured_content )
469+ assert structured_content == {"name" : "John" , "age" : 30 }
470+ assert json .loads (text_items [0 ].text ) == structured_content
460471
461472 @pytest .mark .anyio
462473 async def test_tool_with_primitive_output (self ):
@@ -471,7 +482,12 @@ def double_number(n: int) -> int:
471482 result = await manager .call_tool ("double_number" , {"n" : 5 }, Context ())
472483 assert result == 10
473484 result = await manager .call_tool ("double_number" , {"n" : 5 }, Context (), convert_result = True )
474- assert isinstance (result [0 ][0 ], TextContent ) and result [1 ] == {"result" : 10 }
485+ assert isinstance (result , tuple )
486+ assert len (result ) == 2
487+ unstructured_content , structured_content = cast (tuple [Sequence [ContentBlock ], dict [str , Any ]], result )
488+ text_items = _text_contents (unstructured_content )
489+ assert text_items [0 ].text == "10"
490+ assert structured_content == {"result" : 10 }
475491
476492 @pytest .mark .anyio
477493 async def test_tool_with_typeddict_output (self ):
@@ -511,7 +527,12 @@ def get_person() -> Person:
511527 manager .add_tool (get_person )
512528 result = await manager .call_tool ("get_person" , {}, Context (), convert_result = True )
513529 # don't test unstructured output here, just the structured conversion
514- assert len (result ) == 2 and result [1 ] == expected_output
530+ assert isinstance (result , tuple )
531+ assert len (result ) == 2
532+ unstructured_content , structured_content = cast (tuple [Sequence [ContentBlock ], dict [str , Any ]], result )
533+ text_items = _text_contents (unstructured_content )
534+ assert structured_content == expected_output
535+ assert json .loads (text_items [0 ].text ) == structured_content
515536
516537 @pytest .mark .anyio
517538 async def test_tool_with_list_output (self ):
@@ -529,7 +550,12 @@ def get_numbers() -> list[int]:
529550 result = await manager .call_tool ("get_numbers" , {}, Context ())
530551 assert result == expected_list
531552 result = await manager .call_tool ("get_numbers" , {}, Context (), convert_result = True )
532- assert isinstance (result [0 ][0 ], TextContent ) and result [1 ] == expected_output
553+ assert isinstance (result , tuple )
554+ assert len (result ) == 2
555+ unstructured_content , structured_content = cast (tuple [Sequence [ContentBlock ], dict [str , Any ]], result )
556+ text_items = _text_contents (unstructured_content )
557+ assert [item .text for item in text_items ] == ["1" , "2" , "3" , "4" , "5" ]
558+ assert structured_content == expected_output
533559
534560 @pytest .mark .anyio
535561 async def test_tool_without_structured_output (self ):
0 commit comments