Skip to content

Python: MCP CallToolResult.structuredContent field is not parsed, causing tool results to return None #3313

@timhaintz

Description

@timhaintz

Summary

The Agent Framework's MCP tool result parser (_parse_contents_from_mcp_tool_result) only processes the content field from CallToolResult objects, completely ignoring the structured_content field. This causes MCP servers that return JSON data via structured_content (like the Power BI MCP server) to appear as if they returned None.

Related Issue

This is related to but distinct from #2284 which addressed the _meta field. The structured_content field is a separate part of the MCP specification that remains unhandled.

Bug Details

Location: agent_framework/_mcp.py in function _parse_contents_from_mcp_tool_result()

Issue: The function only iterates over mcp_type.content and ignores mcp_type.structured_content:

def _parse_contents_from_mcp_tool_result(mcp_type: mcp_types.CallToolResult) -> list[types.Contents]:
    """Parse the contents from an MCP CallToolResult."""
    contents: list[types.Contents] = []
    for item in mcp_type.content:  # <-- Only processes content field!
        contents = _parse_content_from_mcp(item)
        if contents:
            all_contents.extend(contents)
    return all_contents
    # BUG: structured_content is completely ignored!

Impact:

  • MCP servers returning data via structured_content appear to return empty/None results
  • Power BI MCP server integration fails silently
  • Any MCP server using structured_content for JSON responses won't work properly

Evidence from Official MCP Specification

From the official MCP Python SDK in src/mcp/types.py:

class ToolResultContent(MCPModel):
    """Content representing the result of a tool execution."""
    
    type: Literal["tool_result"] = "tool_result"
    tool_use_id: str
    content: list[ContentBlock] = []
    structured_content: dict[str, Any] | None = None  # <-- This field is ignored!

The structured_content field is specifically designed for returning structured JSON data from tool calls, as documented in the MCP specification.

Reproduction Steps

  1. Connect to an MCP server that returns structured_content (e.g., Power BI MCP at https://api.fabric.microsoft.com/v1/mcp/powerbi)
  2. Call a tool that returns JSON data via structured_content
  3. Observe that the tool result is empty/None

Example with Power BI MCP:

from agent_framework import ChatAgent, MCPStreamableHTTPTool
from agent_framework.azure import AzureOpenAIResponsesClient

# Create MCP tool for Power BI
powerbi_tool = MCPStreamableHTTPTool(
    name="powerbi",
    url="https://api.fabric.microsoft.com/v1/mcp/powerbi",
    # parse_tool_results=True  # default
)

agent = ChatAgent(
    chat_client=AzureOpenAIResponsesClient(...),
    tools=[powerbi_tool],
)

# When calling GetSemanticModelSchema or ExecuteQuery:
# The MCP server returns:
#   CallToolResult(content=[], structured_content={'Tables': [...], ...}, isError=False)
#
# But MAF only sees content=[] and returns None to the agent!

Debug output showing the issue:

DEBUG:agent_framework:Function powerbi-mcp_GetSemanticModelSchema succeeded.
DEBUG:agent_framework:Function result: None  # <-- Should contain schema data!

# The actual MCP response was:
meta=None content=[] structured_content={'Tables': [...]} isError=False

Current Workaround

Setting parse_tool_results=False on MCPStreamableHTTPTool bypasses the parser and returns the raw CallToolResult, which includes structured_content:

powerbi_tool = MCPStreamableHTTPTool(
    name="powerbi",
    url="https://api.fabric.microsoft.com/v1/mcp/powerbi",
    parse_tool_results=False,  # Workaround - returns raw CallToolResult
)

However, this is not ideal as it changes the return type and requires the LLM to parse the raw result.

Expected Behavior

The _parse_contents_from_mcp_tool_result() function should:

  1. Check for structured_content in addition to content
  2. Convert structured_content to an appropriate content type (e.g., TextContent with JSON string, or a new structured content type)
  3. Preserve the structured data so the LLM can access it

Proposed Solution

Update _parse_contents_from_mcp_tool_result() to handle structured_content:

def _parse_contents_from_mcp_tool_result(mcp_type: mcp_types.CallToolResult) -> list[types.Contents]:
    """Parse the contents from an MCP CallToolResult."""
    contents: list[types.Contents] = []
    
    # Process content field (existing logic)
    for item in mcp_type.content:
        parsed = _parse_content_from_mcp(item)
        if parsed:
            contents.extend(parsed)
    
    # NEW: Process structured_content field
    if mcp_type.structured_content:
        import json
        structured_text = json.dumps(mcp_type.structured_content, indent=2)
        contents.append(types.TextContent(text=structured_text))
    
    return contents

Environment

  • Agent Framework Version: 1.0.0b260116
  • MCP Python SDK Version: Latest
  • Python Version: 3.12/3.13
  • Platform: Windows 11

Affected MCP Servers

  • Power BI MCP (https://api.fabric.microsoft.com/v1/mcp/powerbi) - Returns query results and schemas via structured_content
  • Any MCP server that uses structured_content for JSON responses

Metadata

Metadata

Assignees

Labels

No fields configured for Feature.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions