Python: Persist hosted MCP call/results as canonical mcp_call output - #5950
Conversation
|
The tests shall fail because we are using a unreleased version in our code |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Persists hosted MCP call/result pairs as a single canonical mcp_call output item (with output populated) on the foundry hosting write path, and reconstructs them back into paired MCP call/result content on replay. Fixes follow-up turn 400 errors caused by orphaned tool-output items in replayed history (#5546).
Changes:
- Streaming and non-streaming conversion paths now coalesce
mcp_server_tool_call+mcp_server_tool_resultinto a single completedmcp_callitem keyed by the originalcall_id. - Replay (
_item_to_message/_output_item_to_message) expandsmcp_callitems carryingoutputback into both an MCP call and MCP result content. - Bumps
azure-ai-agentserver-responsesfloor to>=1.0.0b6to pick up the required SDK APIs.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py | Implements coalescing, MCP builder id propagation, output stringification, and reconstruction logic. |
| python/packages/foundry_hosting/tests/test_responses.py | Adds regression tests for single-item persistence, streaming coalescing, replay reconstruction, and multi-turn round-trip. |
| python/packages/foundry_hosting/pyproject.toml | Raises azure-ai-agentserver-responses minimum to 1.0.0b6. |
| and content.call_id is not None | ||
| and content.call_id == self._mcp_builder.item_id | ||
| ): | ||
| accumulated = "".join(self._accumulated) | ||
| yield self._mcp_builder.emit_arguments_done(accumulated) | ||
| yield self._mcp_builder.emit_completed() | ||
| yield self._mcp_builder.emit_done(output=_stringify_mcp_output(content.output)) | ||
| self._mcp_builder = None | ||
| self._active_type = None | ||
| self._active_id = None | ||
| self._accumulated.clear() | ||
| self.needs_async = False | ||
| return | ||
|
|
| item = OutputItemMcpToolCall({ | ||
| "type": "mcp_call", | ||
| "id": "mcp-1", | ||
| "server_label": "my_server", | ||
| "name": "search", | ||
| "arguments": '{"q": "test"}', | ||
| "output": "found 10 cats", | ||
| }) |
There was a problem hiding this comment.
Copilot apply changes based on this feedback
| if pending_mcp_call is not None: | ||
| if content.type == "mcp_server_tool_result" and content.call_id == pending_mcp_call.call_id: | ||
| for event in _emit_completed_mcp_call( |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
Per our offline discussion, please re-open the PR when the underlying Python SDK changes are in and the new package is released. Thanks. |
Motivation and Context
This PR introduces a standalone fix for hosted MCP relay persistence and replay reliability in Agent Framework foundry hosting.
Fix for #5546
Problem addressed:
Scenario supported:
Dependency and rollout note:
azure-ai-agentserver-responses1.0.0b6.Description
Overall approach:
mcp_callrepresentation and keep replay reconstruction aligned with that shape.Changes made:
mcp_server_tool_resultnow completes the activemcp_callbuilder instead of emittingcustom_tool_call_outputfallback.mcp_server_tool_call+mcp_server_tool_resultare coalesced into one completedmcp_calloutput item.mcp_callitems withoutputare reconstructed back into MCP call/result content.foundry_hostingdependency floor fromazure-ai-agentserver-responses>=1.0.0b5,<2to>=1.0.0b6,<2.Testing performed:
foundry_hostingsuite: 135 passed, 13 skippedCompatibility verification:
Files changed:
python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.pypython/packages/foundry_hosting/tests/test_responses.pypython/packages/foundry_hosting/pyproject.tomlContribution Checklist