fix(mcpserver): emit one TextContent block when a tool returns an empty list (issue #3305)
Closed
GI
3mo ago+71+71+01234567890123456789-1-1-0123456789
Summary
- When a tool returns an empty
listortuple,_convert_to_contentpreviously 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
TextContentwhose text is the JSON-serialised empty collection ([]for bothlistandtuple). - All non-empty paths are byte-identical to the previous behaviour.
structuredContentis 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 returns | content blocks | text seen by model |
|---|---|---|
[] | 0 (broken) | '' |
[{...}, {...}] | 2 | both 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 forlisttest_empty_tuple_produces_one_text_content_block— same guarantee fortupletest_non_empty_list_content_blocks_unchanged— non-empty path is byte-identicaltest_empty_list_structured_content_unaffected—structuredContentstill 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