Skip to content

Commit 9528da5

Browse files
committed
fix(mcpserver): serialize empty list and tuple returns into TextContent
1 parent 0d92192 commit 9528da5

3 files changed

Lines changed: 44 additions & 0 deletions

File tree

src/mcp/server/mcpserver/utilities/func_metadata.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,9 @@ def _convert_to_content(result: Any) -> list[ContentBlock]:
590590
return [result.to_audio_content()]
591591

592592
if isinstance(result, list | tuple):
593+
if not result:
594+
return [TextContent(type="text", text="[]" if isinstance(result, list) else "()")]
595+
593596
return list(
594597
chain.from_iterable(
595598
_convert_to_content(item)

tests/interaction/mcpserver/test_tools.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,3 +441,24 @@ async def collect(message: IncomingMessage) -> None:
441441
assert received == snapshot(
442442
[LoggingMessageNotification(params=LoggingMessageNotificationParams(level="info", data="tool set changed"))]
443443
)
444+
445+
446+
async def test_tool_returning_empty_list_and_tuple_produces_text_content(connect: Connect, unstamped: Unstamp) -> None:
447+
"""A tool returning an empty list or tuple produces a TextContent block rather than zero content blocks."""
448+
mcp = MCPServer("empty-tools")
449+
450+
@mcp.tool()
451+
def get_empty_list() -> list[str]:
452+
return []
453+
454+
@mcp.tool()
455+
def get_empty_tuple() -> tuple[str, ...]:
456+
return ()
457+
458+
async with connect(mcp) as client:
459+
list_result = await client.call_tool("get_empty_list", {})
460+
tuple_result = await client.call_tool("get_empty_tuple", {})
461+
462+
assert list_result.content == [TextContent(text="[]")]
463+
assert tuple_result.content == [TextContent(text="()")]
464+

tests/server/mcpserver/test_func_metadata.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,3 +1376,23 @@ def fn() -> StepA | StepB: ... # pragma: no branch
13761376

13771377
meta = func_metadata(fn)
13781378
assert meta.output_schema is None
1379+
1380+
1381+
def test_convert_to_content_empty_list_and_tuple():
1382+
from mcp.server.mcpserver.utilities.func_metadata import _convert_to_content
1383+
1384+
empty_list_res = _convert_to_content([])
1385+
assert len(empty_list_res) == 1
1386+
assert isinstance(empty_list_res[0], TextContent)
1387+
assert empty_list_res[0].text == "[]"
1388+
1389+
empty_tuple_res = _convert_to_content(())
1390+
assert len(empty_tuple_res) == 1
1391+
assert isinstance(empty_tuple_res[0], TextContent)
1392+
assert empty_tuple_res[0].text == "()"
1393+
1394+
non_empty_res = _convert_to_content(["hello", "world"])
1395+
assert len(non_empty_res) == 2
1396+
assert non_empty_res[0].text == "hello"
1397+
assert non_empty_res[1].text == "world"
1398+

0 commit comments

Comments
 (0)