Skip to content

Support structured responses from MCP Tool should make text content optional #1378

@mrecouly

Description

@mrecouly

Initial Checks

Description

While testing an MCP Tool which returns only structured content, I got this error:

Failed to call tool 'my_tool_returning_structured_content'.

Traceback (most recent call last):
  File "/my_project/.venv/Lib/site-packages/semantic_kernel/connectors/mcp.py", line 422, in call_tool
    return _mcp_call_tool_result_to_kernel_contents(await self.session.call_tool(tool_name, arguments=kwargs))
                                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/my_project/.venv/Lib/site-packages/mcp/client/session.py", line 293, in call_tool
    result = await self.send_request(
             ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/my_project/.venv/Lib/site-packages/mcp/shared/session.py", line 288, in send_request
    return result_type.model_validate(response_or_error.result)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/my_project/.venv/Lib/site-packages/pydantic/main.py", line 703, in model_validate
    return cls.__pydantic_validator__.validate_python(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pydantic_core._pydantic_core.ValidationError: 1 validation error for CallToolResult
content
  Field required [type=missing, input_value={'isError': False, 'struc...', 'my_json_string_1']}}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.11/v/missing

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/my_project/.venv/Lib/site-packages/semantic_kernel/kernel.py", line 454, in _inner_auto_function_invoke_handler
    result = await context.function.invoke(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/my_project/.venv/Lib/site-packages/semantic_kernel/functions/kernel_function.py", line 259, in invoke
    raise e
  File "/my_project/.venv/Lib/site-packages/semantic_kernel/functions/kernel_function.py", line 251, in invoke
    await stack(function_context)
  File "/my_project/.venv/Lib/site-packages/semantic_kernel/functions/kernel_function_from_method.py", line 107, in _invoke_internal
    result = await result
             ^^^^^^^^^^^^
  File "/my_project/.venv/Lib/site-packages/semantic_kernel/connectors/mcp.py", line 426, in call_tool
    raise FunctionExecutionException(f"Failed to call tool '{tool_name}'.") from ex

Further testing unveiled that the error was due to the presence of only the structuredContent element in the result element of the JSON response:

{
  "jsonrpc" : "2.0",
  "id" : 3,
  "result" : {
    "isError" : false,
    "structuredContent" : {
      "my_json_array" : [ "my_json_string_1", "my_json_string_2", "my_json_string_3"]
    }
  }
}

Whereas the presence of the element content for text content is expected systematically as per definition of MCP type CallToolResult in the SDK.
Thus a valid response according to the code would be:

{
  "jsonrpc" : "2.0",
  "id" : 3,
  "result" : {
    "isError" : false,
    "content" : [
      {
        "type": "text",
        "text": "{\"my_json_array\" : [\"my_json_string_1\", \"my_json_string_2\", \"my_json_string_3\"]}"
      }
    ],
    "structuredContent" : {
      "my_json_array" : [ "my_json_string_1", "my_json_string_2", "my_json_string_3"]
    }
  }
}

However the MCP specification 2025-06-18 about structured content says: For backwards compatibility, a tool that returns structured content SHOULD also return the serialized JSON in a TextContent block..

Hence the content element should be made optional when the Tool definition comes with outputSchema, since a valid response according to the specification could contain only the structuredContent element ...

Example Code

Python & MCP Python SDK

Tested using Python v3.12 and MCP Python SDK v1.12.3

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingneeds reproneeds additional information to be able to reproduce bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions