Skip to content

[FEATURE] MCP Tools do not surface streamed tool output (AsyncGenerator) through Agent.stream_async() #1273

@yevhenp

Description

@yevhenp

Problem Statement

According to #543 and the follow-up PRs (#788 and #773), Strands now supports true Python tool streaming where tool yield events (from an AsyncGenerator) appear inside the agent’s event stream via Agent.stream_async().

This works correctly for Strands-native Python tools, as documented here:
https://strandsagents.com/latest/documentation/docs/user-guide/concepts/tools/python-tools/#tool-streaming

However, when using MCP tools (via MCPClient), streaming events from the server are not surfaced to the agent. Instead:

  • The MCP Python SDK successfully receives all tool chunks (verified with logging).
  • The Strands MCP integration consumes those streaming chunks internally.
  • Only the final MCP tool result is exposed to the LLM / agent.
  • Intermediate streamed chunks are not emitted as AgentEvents and do not show up in Agent.stream_async().

Expected Behavior

When an MCP tool returns an AsyncGenerator and streams multiple chunks:

  • Each chunk should appear as a tool streaming event in Agent.stream_async() (similar to the behavior of native Python tools after feat: Implement async generator tools #788).
  • The LLM should be able to observe partial tool output in real time.

Actual Behavior

  • MCP tool streams are fully consumed internally.
  • No intermediate chunks are surfaced.
  • The agent only receives the final tool result.
  • The LLM sometimes comments that "the tool is returning an async generator which means it's designed to stream the story content as it's being created. The actual story content would typically be streamed in real-time, but I'm not able to display the streaming output directly in this interface,” reflecting the missing event propagation.

Reproduction

Server-side (FastMCP)

from fastmcp import FastMCP
from typing import AsyncGenerator
mcp = FastMCP("Sample App")

@mcp.tool()
async def story(...) -> AsyncGenerator[dict]:
    yield {"msg": "chunk 1"}
    yield {"msg": "chunk 2"}
    yield {"msg": "chunk 3"}
    return {"msg": "final"}

# Run mcp server
mcp.run_async(transport="http", host="localhost", port=8005)

Client-side (Strands)

from strands import Agent
from strands.tools.mcp.mcp_client import MCPClient
from mcp.client.streamable_http import streamablehttp_client

mcp_client = MCPClient(lambda: streamablehttp_client("http://localhost:8005/mcp"))
with mcp_client as mcp:
    agent = Agent(model=..., tools=[client.list_tools_sync()])
    async for event in agent.stream_async("Generate a story"):
        print(event)

Environment

  • Strands SDK: 1.18.0
  • FastMCP: 2.13.1
  • Transport: streamable-http (SSE event stream)

Request

Could MCP tool streams be forwarded into Agent.stream_async() the same way Python tool streams are handled after PR #788?

This would allow MCP tools to benefit from the new streaming tool infrastructure and unlock real-time tool output for remote/HTTP-based tools.

Proposed Solution

  1. Surface MCP tool stream events as AgentEvents
  • When MCPClient / MCPAgentTool receives a streaming chunk from the MCP Python SDK (via call_tool_async):
  • Wrap each chunk in the same event type used by native Python tool streaming (ToolStreamEvent introduced in feat: Implement async generator tools #788), or an equivalent adapter event.
  • Forward it to the agent’s callback handlers and Agent.stream_async() generator.

This would mirror the behavior implemented for Python tools and enable tool streaming parity.

  1. Preserve the existing final result behavior - with the final AgentEvent and structured output (if applicable)

  2. Do not require MCP protocol changes - streamable-http transport already supports streaming these events - they just have to be forwarded to the Strands client-side agents.

Use Case

This would allow MCP tools to benefit from the new streaming tool infrastructure and unlock real-time tool output for remote/HTTP-based tools.

If a long-running MCP tool streams in-progress events, Strands on the client-side would be able to emit that progress to the user, rather than waiting for the final tool result.

Alternatives Solutions

  • Using Strands-native tools - doesn't work if MCP architecture required
  • Simply returning a final result object from the MCP tool, rather than streaming - but that simply avoids the problem

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions