Skip to content

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

Closed
gingeekrishna wants to merge 1 commit into
modelcontextprotocol:mainfrom
gingeekrishna:fix/3305-empty-list-zero-content-blocks
Closed

fix(mcpserver): emit one TextContent block when a tool returns an empty list (issue #3305)#3319
gingeekrishna wants to merge 1 commit into
modelcontextprotocol:mainfrom
gingeekrishna:fix/3305-empty-list-zero-content-blocks

Conversation

@gingeekrishna

Copy link
Copy Markdown

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 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 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_unaffectedstructuredContent 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

Copilot AI lite review requested due to automatic review settings August 16, 2026 16:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 2 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/mcp/server/mcpserver/utilities/func_metadata.py Outdated
…ty list

When a tool function returns an empty list or tuple, `func_metadata` was
producing zero content blocks (an empty `CallToolResult.content`). The MCP
spec requires at least one content item, so LLM clients that assume the list
is non-empty would raise an index error or silently drop the result.

Root cause: the branch that handled falsy sequences fell through to the
normal `convert_result` path, which converts each element of the sequence
into a `TextContent` block.  An empty sequence produced nothing.

Fix: detect `not result` before the element-wise conversion and return a
single `TextContent` containing `[]` (the JSON serialisation of an empty
list).  Tuple return types are covered by the same branch.

Signed-off-by: Radhakrishnan Pachyappan <gingeekrishna@gmail.com>
@gingeekrishna
gingeekrishna force-pushed the fix/3305-empty-list-zero-content-blocks branch from 39aef8c to 25a9532 Compare August 17, 2026 04:53
@github-actions github-actions Bot added the missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md) label Aug 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for the contribution. This repository only keeps pull requests open when they're linked to an issue that a maintainer has assigned to the author — CONTRIBUTING.md explains why and how we work. This PR has been closed for now because you aren't currently assigned to #3305.

If a maintainer would like this change as a PR from you, they'll assign you to #3305 and this PR will reopen automatically — there's nothing more you need to do. (If you opened the issue, this PR already shows up on its timeline.)

There's no need to open a new PR — this one will be reopened. While it's closed, please push any updates as new commits rather than force-pushing, since GitHub can't reopen a PR whose branch has been rewritten.

Maintainers: reopening this PR, removing the missing-issue-link label, or adding bypass-issue-check bypasses the check.

@github-actions github-actions Bot closed this Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A tool returning an empty list produces a CallToolResult with zero content blocks

2 participants