Skip to content

Python: [Bug]: MCP Tool Calls + Reasoning Not Emitted as AG-UI Events (Foundry) #4213

Closed

Description

@djw-bsn

Description

On Foundry (Responses API) streams, mcp_server_tool_call, mcp_server_tool_result, and text_reasoning content items are received by the server but produce no AG-UI SSE events because they fall through unhandled in _emit_content.

Impact: frontends consuming AG-UI events cannot display MCP tool execution state (tool call start/result) and cannot reflect reasoning/progress during text_reasoning.

Scope

Foundry agents only (Azure AI Foundry / OpenAI Responses API MCP tool items).

Standard function_call / function_result mapping is unaffected.

Where It Happens

agent_framework_ag_ui/_run.py::run_agent_stream dispatches each streamed content item through _emit_content(...) and yields whatever events it returns. If _emit_content returns [] for a given content type, nothing is emitted to the SSE stream.

# Emit events for each content item
for content in update.contents:
    content_type = getattr(content, "type", None)
    logger.debug(f"Processing content type={content_type}, message_id={flow.message_id}")
    for event in _emit_content(
        content,
        flow,
        predictive_handler,
        skip_text,
        config.require_confirmation,
    ):
        yield event

Evidence / Symptoms

Server debug shows content arriving, e.g.:

Processing content type=mcp_server_tool_call

Processing content type=mcp_server_tool_result

AG-UI SSE stream shows no TOOL_CALL_* events for those items.

Root Cause

_emit_content only handles text, function_call, function_result, and function_approval_request. The Foundry MCP and reasoning content types are not handled and therefore return [].

Expected Fix

Add handling in _emit_content for:

mcp_server_tool_call → emit tool-call start (same as function_call mapping)

mcp_server_tool_result → emit tool-call result (same as function_result mapping)

text_reasoning → emit some progress/timing-visible event (e.g. a state/progress delta) so reasoning is not silently invisible

This makes MCP tool calls and text_reasoning visible to AG-UI consumers.

Code Sample

Error Messages / Stack Traces

Package Versions

agent-framework-ag-ui==1.0.0b260219 agent-framework-azure-ai==1.0.0rc1 agent-framework-core==1.0.0rc1

Python Version

Python 3.12

Additional Context

No response

Activity

  1. added
    bugUsage: [Issues], Target: all issues (Legacy, prefer issue type: bug)
    on Feb 24, 2026
  2. LEDazzio01 commented on Feb 24, 2026

    @LEDazzio01
    Contributor

    I'd like to take this one! I just had a fix merged in this same area (PR #4200 — duplicate tool_call arguments in _emit_tool_call), so I'm familiar with the AG-UI event emission flow in _run.py.

    I'll start by mapping out the unhandled content types (mcp_server_tool_call, mcp_server_tool_result, text_reasoning) and propose how they should map to existing AG-UI event types.

  3. moonbox3 commented on Feb 24, 2026

    @moonbox3
    Contributor

    This points to something potentially in the underlying client that isn't creating the proper Function Call type for those tools.

  4. LEDazzio01 commented on Feb 24, 2026

    @LEDazzio01
    Contributor

    Thanks for the feedback Evan Mattson (@moonbox3)! I dug into _responses_client.py to understand the flow and wanted to share what I found:

    The client already creates dedicated MCP content types — both the non-streaming path (_parse_response_from_openai) and the streaming path (_parse_chunk_from_openai → response.output_item.added) explicitly use Content.from_mcp_server_tool_call() for mcp_call items rather than Content.from_function_call().

    This appears intentional because MCP tools carry fields that don't map 1:1 to regular function calls:

    • tool_name + server_name (vs name)
    • output (vs result)
    • The server_label / server_name for disambiguation

    The same is true for text_reasoning — the client creates Content.from_text_reasoning() for response.reasoning_text.delta/done events.

    So if I'm understanding your comment correctly, there are two possible approaches:

    1. My PR (Python: Emit AG-UI events for MCP tool calls, results, and text reasoning #4233): Handle the MCP types in _emit_content → map them to AG-UI ToolCall* events (preserving the MCP-specific metadata like server_name/tool_name display names)
    2. Client-level approach: Have _responses_client.py convert mcp_call items to function_call Content types instead of mcp_server_tool_call — but this would lose the MCP-specific metadata and change the content type semantics across all downstream consumers

    Would you prefer approach 2? Or were you suggesting something else entirely? Happy to adjust the PR either way! 🙂

  5. added and removed
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Feb 25, 2026
  6. moonbox3 commented on Feb 25, 2026

    @moonbox3
    Contributor

    L. Elaine Dazzio (@LEDazzio01) it does look like we need to support option 1, as you have it.

  7. added 4 commits that reference this issue on Mar 18, 2026
    a86d0f5
    d05fbe2
    be61396
    45b8e9e
  8. added a commit that references this issue on Mar 20, 2026
    4afc088
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugUsage: [Issues], Target: all issues (Legacy, prefer issue type: bug)pythonUsage: [Issues, PRs], Target: Python

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions