Skip to content

Commit a933792

Browse files
Add MCP builder support for output/error and caller-supplied item IDs (#46985)
* Add MCP builder support for output/error and caller-supplied item IDs * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent b0873ba commit a933792

4 files changed

Lines changed: 77 additions & 15 deletions

File tree

sdk/agentserver/azure-ai-agentserver-responses/azure/ai/agentserver/responses/streaming/_builders/_tools.py

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from __future__ import annotations
66

77
from collections.abc import AsyncIterable
8-
from typing import TYPE_CHECKING, AsyncIterator, Iterator, cast
8+
from typing import TYPE_CHECKING, Any, AsyncIterator, Iterator, cast
99

1010
from ...models import _generated as generated_models
1111
from ._base import BaseOutputItemBuilder, _require_non_empty
@@ -540,26 +540,39 @@ def emit_failed(self) -> generated_models.ResponseMCPCallFailedEvent:
540540
self._emit_item_state_event(generated_models.ResponseStreamEventType.RESPONSE_MCP_CALL_FAILED.value),
541541
)
542542

543-
def emit_done(self) -> generated_models.ResponseOutputItemDoneEvent:
543+
def emit_done(
544+
self,
545+
*,
546+
output: str | None = None,
547+
error: dict[str, Any] | None = None,
548+
) -> generated_models.ResponseOutputItemDoneEvent:
544549
"""Emit an ``output_item.done`` event for this MCP call.
545550
546551
The ``status`` field reflects the most recent terminal state event
547552
(``emit_completed`` or ``emit_failed``). Defaults to ``"completed"``
548553
if neither was called.
549554
555+
:keyword output: Optional MCP tool output payload.
556+
:keyword type output: str | None
557+
:keyword error: Optional MCP tool error payload.
558+
:keyword type error: dict[str, Any] | None
559+
550560
:returns: The emitted event dict.
551561
:rtype: ResponseOutputItemDoneEvent
552562
"""
553-
return self._emit_done(
554-
{
555-
"type": "mcp_call",
556-
"id": self._item_id,
557-
"server_label": self._server_label,
558-
"name": self._name,
559-
"arguments": self._final_arguments or "",
560-
"status": self._terminal_status or "completed",
561-
}
562-
)
563+
item: dict[str, Any] = {
564+
"type": "mcp_call",
565+
"id": self._item_id,
566+
"server_label": self._server_label,
567+
"name": self._name,
568+
"arguments": self._final_arguments or "",
569+
"status": self._terminal_status or "completed",
570+
}
571+
if output is not None:
572+
item["output"] = output
573+
if error is not None:
574+
item["error"] = error
575+
return self._emit_done(item)
563576

564577
# ---- Sub-item convenience generators (S-053) ----
565578

sdk/agentserver/azure-ai-agentserver-responses/azure/ai/agentserver/responses/streaming/_event_stream.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -443,23 +443,38 @@ def add_output_item_image_gen_call(self) -> OutputItemImageGenCallBuilder:
443443
item_id = IdGenerator.new_image_gen_call_item_id(self._response_id)
444444
return OutputItemImageGenCallBuilder(self, output_index=output_index, item_id=item_id)
445445

446-
def add_output_item_mcp_call(self, server_label: str, name: str) -> OutputItemMcpCallBuilder:
446+
def add_output_item_mcp_call(
447+
self,
448+
server_label: str,
449+
name: str,
450+
*,
451+
item_id: str | None = None,
452+
) -> OutputItemMcpCallBuilder:
447453
"""Add an MCP tool call output item and return its scoped builder.
448454
449455
:param server_label: Label identifying the MCP server.
450456
:type server_label: str
451457
:param name: Name of the MCP tool being called.
452458
:type name: str
459+
:keyword item_id: Optional caller-supplied output item identifier.
460+
:keyword type item_id: str | None
453461
:returns: A builder for emitting MCP call argument deltas and lifecycle events.
454462
:rtype: OutputItemMcpCallBuilder
455463
"""
456464
output_index = self._output_index
457465
self._output_index += 1
458-
item_id = IdGenerator.new_mcp_call_item_id(self._response_id)
466+
if item_id is None:
467+
resolved_item_id = IdGenerator.new_mcp_call_item_id(self._response_id)
468+
else:
469+
if not isinstance(item_id, str):
470+
raise TypeError("item_id must be a string")
471+
resolved_item_id = item_id.strip()
472+
if not resolved_item_id:
473+
raise ValueError("item_id must be a non-empty string")
459474
return OutputItemMcpCallBuilder(
460475
self,
461476
output_index=output_index,
462-
item_id=item_id,
477+
item_id=resolved_item_id,
463478
server_label=server_label,
464479
name=name,
465480
)

sdk/agentserver/azure-ai-agentserver-responses/tests/unit/test_builders.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,31 @@ def test_stream_item_id_generation__uses_expected_shape_and_response_partition_k
278278
assert len(body) == 50
279279

280280

281+
def test_add_output_item_mcp_call__uses_caller_supplied_item_id() -> None:
282+
stream = ResponseEventStream(response_id=IdGenerator.new_response_id())
283+
stream.emit_created()
284+
285+
mcp_call = stream.add_output_item_mcp_call("srv", "tool", item_id="mcp_06b686e11f")
286+
287+
assert mcp_call.item_id == "mcp_06b686e11f"
288+
289+
290+
def test_output_item_mcp_call_emit_done__includes_output_and_error_when_provided() -> None:
291+
stream = ResponseEventStream(response_id=IdGenerator.new_response_id())
292+
stream.emit_created()
293+
294+
mcp_call = stream.add_output_item_mcp_call("srv", "tool", item_id="mcp_custom")
295+
mcp_call.emit_added()
296+
mcp_call.emit_arguments_done('{"arg": 1}')
297+
mcp_call.emit_failed()
298+
done = mcp_call.emit_done(output='{"value": 42}', error={"code": "tool_error"})
299+
300+
assert done["type"] == "response.output_item.done"
301+
assert done["item"]["id"] == "mcp_custom"
302+
assert done["item"]["output"] == '{"value": 42}'
303+
assert done["item"]["error"] == {"code": "tool_error"}
304+
305+
281306
def test_response_event_stream__exposes_mutable_response_snapshot_for_lifecycle_events() -> None:
282307
stream = ResponseEventStream(response_id="resp_builder_snapshot", model="gpt-4o-mini")
283308
stream.response.temperature = 1

sdk/agentserver/azure-ai-agentserver-responses/tests/unit/test_emit_return_types.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,15 @@ def test_emit_done(self) -> None:
787787
event = mcp.emit_done()
788788
assert isinstance(event, ResponseOutputItemDoneEvent)
789789

790+
def test_emit_done_with_output_and_error(self) -> None:
791+
s = _stream()
792+
s.emit_created()
793+
mcp = s.add_output_item_mcp_call("server", "tool", item_id="mcp_test")
794+
mcp.emit_added()
795+
mcp.emit_failed()
796+
event = mcp.emit_done(output="ok", error={"reason": "failed"})
797+
assert isinstance(event, ResponseOutputItemDoneEvent)
798+
790799

791800
# =====================================================================
792801
# OutputItemMcpListToolsBuilder

0 commit comments

Comments
 (0)