[GH-ISSUE #50] Bug: MCP tool results return "(empty result)" due to Pydantic model handling in extract_text_content() #31

Closed
opened 2026-02-27 07:17:34 +03:00 by kerem · 1 comment
Owner

Originally created by @bhaskoro-muthohar on GitHub (Jan 21, 2026).
Original GitHub issue: https://github.com/jwadow/kiro-gateway/issues/50

Description

MCP tool results are being converted to "(empty result)" instead of preserving the actual content. This affects all MCP servers that return tool results in the standard format [{"type": "text", "text": "..."}].

Root Cause

When Claude Code sends MCP tool results, the content comes as:

{"type": "tool_result", "tool_use_id": "...", "content": [{"type": "text", "text": "actual data here"}]}

Pydantic parses this into:

ToolResultContentBlock(content=[TextContentBlock(type='text', text='actual data here')])

However, extract_text_content() in kiro/converters_core.py only handles dict and str types:

for item in content:
    if isinstance(item, dict):  # False for Pydantic models!
        ...
    elif isinstance(item, str):
        ...

Since TextContentBlock is a Pydantic model (not a dict), the content is not extracted, resulting in an empty string that falls back to "(empty result)".

Steps to Reproduce

  1. Configure any MCP server (e.g., slack-mcp-server, context7)
  2. Use Claude Code with kiro-gateway as the proxy
  3. Call any MCP tool
  4. Observe that the model receives "(empty result)" instead of actual data

Test Case

from kiro.converters_core import extract_text_content
from kiro.models_anthropic import TextContentBlock

# Dict format works
dict_content = [{'type': 'text', 'text': 'Hello'}]
assert extract_text_content(dict_content) == "Hello"  # PASS

# Pydantic format fails
pydantic_content = [TextContentBlock(type='text', text='Hello')]
assert extract_text_content(pydantic_content) == "Hello"  # FAIL - returns ""

Proposed Fix

Add handling for Pydantic models in extract_text_content() (around line 143):

elif hasattr(item, "text"):
    # Handle Pydantic models like TextContentBlock
    text_parts.append(getattr(item, "text", ""))

Impact

  • All MCP tool results are affected
  • MCP resources work fine (they return plain strings, not wrapped in content blocks)
  • This explains why users see data in Claude Code terminal output but the model claims it received empty results

Environment

  • kiro-gateway version: latest
  • Python: 3.x with Pydantic v2
  • Affected MCP servers: slack-mcp-server, context7, likely all others
Originally created by @bhaskoro-muthohar on GitHub (Jan 21, 2026). Original GitHub issue: https://github.com/jwadow/kiro-gateway/issues/50 ## Description MCP tool results are being converted to `"(empty result)"` instead of preserving the actual content. This affects all MCP servers that return tool results in the standard format `[{"type": "text", "text": "..."}]`. ## Root Cause When Claude Code sends MCP tool results, the content comes as: ```json {"type": "tool_result", "tool_use_id": "...", "content": [{"type": "text", "text": "actual data here"}]} ``` Pydantic parses this into: ```python ToolResultContentBlock(content=[TextContentBlock(type='text', text='actual data here')]) ``` However, `extract_text_content()` in `kiro/converters_core.py` only handles `dict` and `str` types: ```python for item in content: if isinstance(item, dict): # False for Pydantic models! ... elif isinstance(item, str): ... ``` Since `TextContentBlock` is a Pydantic model (not a dict), the content is not extracted, resulting in an empty string that falls back to `"(empty result)"`. ## Steps to Reproduce 1. Configure any MCP server (e.g., slack-mcp-server, context7) 2. Use Claude Code with kiro-gateway as the proxy 3. Call any MCP tool 4. Observe that the model receives `"(empty result)"` instead of actual data ## Test Case ```python from kiro.converters_core import extract_text_content from kiro.models_anthropic import TextContentBlock # Dict format works dict_content = [{'type': 'text', 'text': 'Hello'}] assert extract_text_content(dict_content) == "Hello" # PASS # Pydantic format fails pydantic_content = [TextContentBlock(type='text', text='Hello')] assert extract_text_content(pydantic_content) == "Hello" # FAIL - returns "" ``` ## Proposed Fix Add handling for Pydantic models in `extract_text_content()` (around line 143): ```python elif hasattr(item, "text"): # Handle Pydantic models like TextContentBlock text_parts.append(getattr(item, "text", "")) ``` ## Impact - All MCP tool results are affected - MCP resources work fine (they return plain strings, not wrapped in content blocks) - This explains why users see data in Claude Code terminal output but the model claims it received empty results ## Environment - kiro-gateway version: latest - Python: 3.x with Pydantic v2 - Affected MCP servers: slack-mcp-server, context7, likely all others
kerem 2026-02-27 07:17:34 +03:00
  • closed this issue
  • added the
    bug
    fixed
    labels
Author
Owner

@jwadow commented on GitHub (Jan 23, 2026):

@bhaskoro-muthohar Your analysis was perfect. Fixed by adding Pydantic model handling to extract_text_content():

elif hasattr(item, "text"):
    text_parts.append(getattr(item, "text", ""))

Added you to CONTRIBUTORS.md

<!-- gh-comment-id:3788749441 --> @jwadow commented on GitHub (Jan 23, 2026): @bhaskoro-muthohar Your analysis was perfect. Fixed by adding Pydantic model handling to `extract_text_content()`: ``` elif hasattr(item, "text"): text_parts.append(getattr(item, "text", "")) ``` Added you to CONTRIBUTORS.md
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/kiro-gateway-jwadow#31
No description provided.