Skip to content

Python: [Bug]: MCP sampling callback cannot return structured tool-use responses #7160

Description

@SensorLock

Description

MCPTool.sampling_callback forwards MCP sampling tools and toolChoice to the chat client, but cannot convert the resulting Agent Framework function_call content back into an MCP structured sampling response.

When a model follows a supplied output-schema tool, it returns Content(type="function_call"). The callback passes this through _prepare_message_for_mcp(), which handles text, image, data, and URI content but not function calls. It consequently returns: Failed to get right content types from the response.

Expected behavior

Convert Agent Framework function calls into MCP tool-use results:
Content(type="function_call")
-> ToolUseContent
-> CreateMessageResultWithTools(stopReason="toolUse")

Actual behavior

Structured sampling fails even though the tool schema is correctly forwarded to the chat client.

Impact

FastMCP Context.sample(result_type=some_dataclass) and other MCP sampling clients using tools cannot receive structured results through Agent Framework. Plain text sampling continues to work.

Suggested fix

Update sampling_callback to return CreateMessageResultWithTools when the chat response contains function calls, including updating its return annotation. The conversion should inspect all response messages and preserve each function call’s ID, name, and arguments.

Code Sample

import asyncio
from typing import Any, cast
from unittest.mock import MagicMock

from agent_framework import ChatResponse, Content, MCPStdioTool, Message
from mcp import types
from mcp.client.session import ClientSession
from mcp.shared.context import RequestContext


class StructuredClient:
    async def get_response(self, messages, **kwargs):
        # Simulate a model following the sampling request's output-schema tool.
        return ChatResponse(
            model="test-model",
            messages=[
                Message(
                    role="assistant",
                    contents=[
                        Content.from_function_call(
                            call_id="call-1",
                            name="Answer",
                            arguments={"answer": "hello"},
                        )
                    ],
                )
            ],
        )


async def main():
    tool = MCPStdioTool(
        name="repro",
        command="unused",  # No subprocess is started.
        client=StructuredClient(),
        sampling_approval_callback=lambda params: True,
    )

    result = await tool.sampling_callback(
        cast(RequestContext[ClientSession, Any], MagicMock()),
        types.CreateMessageRequestParams(
            messages=[
                types.SamplingMessage(
                    role="user",
                    content=types.TextContent(
                        type="text",
                        text="Answer the question",
                    ),
                )
            ],
            maxTokens=100,
            tools=[
                types.Tool(
                    name="Answer",
                    description="Return a structured answer",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "answer": {"type": "string"},
                        },
                        "required": ["answer"],
                    },
                )
            ],
        ),
    )

    print(result)


asyncio.run(main())

Error Messages / Stack Traces

Failed to get right content types from the response.

Package Versions

agent-framework-core==1.11.0 agent-framework-openai==1.10.1

Python Version

3.12

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    pythonUsage: [Issues, PRs], Target: PythonreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflowtriageUsage: [Issues], Target: All issues that still need to be triaged

    Type

    Fields

    No fields configured for Bug.

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions