fix(mcpserver): emit one TextContent block when a tool returns an empty list (issue #3305)

Closed
GI
3mo ago
+71-1

Summary

  • When a tool returns an empty list or tuple, _convert_to_content previously produced zero content blocks, making "no results" indistinguishable from "call produced nothing" for clients reading unstructured content.
  • Fix: when the per-item flattening produces no blocks, emit a single TextContent whose text is the JSON-serialised empty collection ([] for both list and tuple).
  • All non-empty paths are byte-identical to the previous behaviour. structuredContent is unaffected in all cases.

Root cause

_convert_to_content (src/mcp/server/mcpserver/utilities/func_metadata.py:563) flattens a list by chaining the conversion of each item:

if isinstance(result, list | tuple):
    return list(chain.from_iterable(_convert_to_content(item) for item in result))

With zero items there is nothing to concatenate, so the result is []. The same "empty" answer then behaves differently depending on return type:

tool returnscontent blockstext seen by model
[]0 (broken)''
[{...}, {...}]2both items
""1''

Fix

if isinstance(result, list | tuple):
    items = list(chain.from_iterable(_convert_to_content(item) for item in result))
    if not items:
        return [TextContent(type="text", text=json.dumps(list(result)))]
    return items

Tests added

Four new tests in tests/server/mcpserver/test_func_metadata.py:

  • test_empty_list_produces_one_text_content_block — core fix for list
  • test_empty_tuple_produces_one_text_content_block — same guarantee for tuple
  • test_non_empty_list_content_blocks_unchanged — non-empty path is byte-identical
  • test_empty_list_structured_content_unaffected — structuredContent still correct

All 46 tests in the file pass.

Test plan

  • uv run --frozen pytest tests/server/mcpserver/test_func_metadata.py — 46/46 pass
  • Confirm a tool returning [] now delivers one content block with text "[]" to the model
  • Confirm existing tools returning non-empty lists are unaffected

Closes #3305