[BUG] query() with string prompt + mcp_servers blocks generator until result — no streaming events
Summary
When query() is called with a string prompt and mcp_servers containing SDK MCP servers, the async generator does not yield any events until the entire conversation completes (final result message). All intermediate events (SystemMessage, StreamEvent, AssistantMessage) are buffered in the memory channel but never reach the consumer.
This makes include_partial_messages=True ineffective for string prompts with MCP servers — the option works correctly only with AsyncIterable prompts.
Root cause
In claude_agent_sdk/_internal/client.py, process_query() lines 131–150:
if isinstance(prompt, str):
user_message = {
"type": "user", "session_id": "",
"message": {"role": "user", "content": prompt},
"parent_tool_use_id": None,
}
await chosen_transport.write(json.dumps(user_message) + "\n")
await query.wait_for_result_and_end_input() # ← BLOCKS generator
elif isinstance(prompt, AsyncIterable):
query.spawn_task(query.stream_input(prompt)) # ← runs in background
# Only AFTER the above does yielding begin:
async for data in query.receive_messages():
message = parse_message(data)
if message is not None:
yield message
For string prompts: wait_for_result_and_end_input() awaits _first_result_event (query.py line 697), which is set only when a "result" type message arrives (query.py line 233). The generator is blocked — receive_messages() never starts until the entire conversation is done.
For AsyncIterable prompts: stream_input() is spawned as a background task. receive_messages() starts immediately, yielding events as they arrive.
Impact
- Simple queries (no tool calls): work fine — result arrives in seconds, buffered events flush quickly
- Tool-calling queries (MCP tools): total time = tool execution + second API call. If this exceeds the consumer's timeout (e.g. 120s), the consumer never receives any events at all — not even
SystemMessage include_partial_messages=Truehas no effect for string prompts — all events are buffered regardless
Reproduction
from claude_agent_sdk import query, ClaudeAgentOptions, create_sdk_mcp_server, SdkMcpTool
import asyncio, time
# Create MCP server with a slow tool
async def slow_handler(seconds: int = 30):
await asyncio.sleep(seconds)
return f"Slept {seconds}s"
server = create_sdk_mcp_server(name="test", tools=[
SdkMcpTool(name="slow_tool", description="Sleeps",
input_schema={"type": "object", "properties": {"seconds": {"type": "integer"}}},
handler=slow_handler)
])
options = ClaudeAgentOptions(
mcp_servers={"test": server},
allowed_tools=["mcp__test__slow_tool"],
include_partial_messages=True, # has no effect for string prompts
)
async def main():
t0 = time.monotonic()
async for msg in query(prompt="call slow_tool with 30 seconds", options=options):
# First event arrives only AFTER slow_tool completes + Claude responds
print(f"+{time.monotonic()-t0:.1f}s: {type(msg).__name__}")
asyncio.run(main())
Expected: SystemMessage arrives within ~5s, StreamEvents stream during API response
Actual: First event arrives only after ~40-60s (tool execution + second API call complete)
Workaround
Wrap the string prompt in an AsyncIterable to use the non-blocking code path:
async def as_prompt_stream(text: str):
yield {
"type": "user",
"session_id": "",
"message": {"role": "user", "content": text},
"parent_tool_use_id": None,
}
# Use AsyncIterable instead of string:
async for msg in query(prompt=as_prompt_stream("call slow_tool"), options=options):
print(type(msg).__name__) # events stream immediately
Suggested fix
In process_query(), the string prompt path should match the AsyncIterable path — spawn wait_for_result_and_end_input as a background task instead of awaiting it inline:
if isinstance(prompt, str):
await chosen_transport.write(json.dumps(user_message) + "\n")
query.spawn_task(query._wait_and_end_input()) # background, not blocking
Related
- #35077 — describes the same
wait_for_result_and_end_input/firstResultReceivedResolvemechanism causing stdin closure issues with background subagents
Environment
claude-agent-sdk: 0.1.52- Python: 3.12
- Platform: macOS (Darwin 25.3.0)
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗