Skip to content

McpTool drops structuredContent and non-text content from CallToolResult#1443

Description

@akbarkanso

馃敶 Required Information

Describe the Bug:

AbstractMcpTool.wrapCallResult(...) silently discards parts of a successful MCP CallToolResult.

When the result contains at least one TextContent, native McpTool.runAsync(...) returns only the legacy text_output map. It does not expose:

  • CallToolResult.isError on successful results
  • CallToolResult.structuredContent
  • non-text content entries such as images or embedded resources
  • the original ordered content list

This loss is irreversible for callers. An outer BaseTool decorator sees only the already-reduced map and cannot recover the discarded fields without bypassing native McpTool and invoking McpSyncClient itself.

Steps to Reproduce:

  1. Use com.google.adk:google-adk:1.7.1.
  2. Construct a native McpToolset over a streamable-HTTP MCP server.
  3. Have the server advertise a zero-argument tool.
  4. Return this successful tools/call result:
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {"type": "text", "text": "first"},
      {"type": "text", "text": "second"},
      {
        "type": "image",
        "data": "aW1hZ2U=",
        "mimeType": "image/png"
      }
    ],
    "structuredContent": {"count": 2},
    "isError": false
  }
}
  1. Call the discovered native tool through BaseTool.runAsync(...).

Expected Behavior:

Native MCP tools should preserve the complete semantic CallToolResult, including structured content and non-text content, without breaking callers that consume the existing text_output field.

A compatible resolution could be either:

  1. An additive envelope that retains text_output unchanged while also exposing content, structuredContent, isError, and metadata.
  2. A supported result-mapper/adapter hook on McpToolset or McpTool that receives the raw CallToolResult before reduction.

Observed Behavior:

The returned map contains only:

{text_output=[{text=first}, {text=second}]}

structuredContent, image data and MIME type, the original content parts, and explicit non-error state are absent.

The loss is silent. No exception or warning indicates that valid MCP response data was discarded.

Environment Details:

  • ADK Library Version (see maven dependency): 1.7.1
  • MCP Java SDK resolved by ADK: 1.1.2
  • MCP transport: streamable HTTP
  • Java: 21

馃煛 Optional Information

Regression:

No

Additional Context:

PR #1078 changed wrapCallResult to serialize the complete CallToolResult, but PR #1090 reverted that change to preserve backward compatibility with text_output.

The requested fix is therefore not to remove text_output. It is to preserve compatibility without silently losing the remaining MCP result fields.

This blocks replacing a custom MCP bridge with native McpToolset: the custom implementation currently preserves text, structuredContent, non-text content parts, and error state, while native McpTool does not.

Minimal Reproduction Code:

N/A. The issue can be reproduced using the MCP response above and a native McpToolset over a streamable-HTTP MCP server.

How often has this issue occurred?:

  • Always (100%)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions