You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Python: [Bug]: Hosted toolbox MCP call_id from a previous turn is replayed as function_call_output without its function_call (400 from Responses API) #5546
When a Foundry-hosted agent uses a toolbox MCP tool that completed successfully on a previous turn within the same conversation_id, the next turn fails with a 400 from the Azure OpenAI Responses API:
openai.BadRequestError: Error code: 400 - {
'error': {
'message': "No tool call found for function call output with call_id mcp_06b686e11f118cf40169f0e5badb3081979842929d5cf04920.",
'type': 'invalid_request_error',
'param': 'input',
'code': None
}
}
The error is raised inside agent_framework_openai/_chat_client.py:555 when calling client.responses.create(stream=True, **run_options). The input array sent to the Responses API contains a function_call_output whose call_id has no matching function_call in the same array, which violates the Responses API contract. The 400 happens before the model is invoked.
The same conversation_id is reused across turns, while caresp_* ids change. A retry of the failing prompt produces the exact same 400 with the same orphan call_id, which confirms the orphan is now persisted in conversation state and is replayed every time.
What did you expect to happen?
A subsequent user message in the same conversation should be processed normally. Either:
the framework should not replay a function_call_output from a prior turn without its matching function_call in the same input payload, or
the hosted MCP tool calls should be persisted as a balanced pair (call + output) so the Responses API accepts them on replay.
Steps to reproduce the issue
Build a Foundry hosted agent with agent-framework-foundry-hosting==1.0.0a260428 and agent-framework-core==1.2.1 / agent-framework-foundry==1.2.1.
Wire a Foundry Toolbox MCP connector backed by an OAuth-protected MCP server (in our case Logic Apps with Entra ID auth code + PKCE).
From the Foundry playground, send three prompts in the same conversation:
Prompt 1: a question that does not require the tool. Succeeds.
Prompt 2: a question that triggers the MCP tool. Returns oauth_consent_request. After the user authorizes and resends, the tool runs and returns the result. Succeeds.
Prompt 3: any follow-up question. Fails with the 400 above before the model streams anything.
Code Sample
Minimal shape of the agent wiring (full project is a Foundry hosted agent, but the failure is reproducible with this skeleton):
from agent_framework_foundry import FoundryChatClient
from agent_framework_foundry_hosting import ResponsesHostServer, InMemoryResponseProvider
from azure.identity.aio import ManagedIdentityCredential
async def main() -> None:
async with (
ManagedIdentityCredential() as credential,
FoundryChatClient(
project_endpoint=PROJECT_ENDPOINT,
model=MODEL_DEPLOYMENT_NAME,
credential=credential,
).as_agent(
name="agent-baseline-python",
instructions=SYSTEM_PROMPT,
tools=[mcp_toolbox_tool], # Foundry Toolbox MCP connector (OAuth-protected)
default_options={
"store": False,
"tool_choice": "required",
"temperature": 0.2,
"max_tokens": 2000,
},
) as agent,
):
server = ResponsesHostServer(agent, store=InMemoryResponseProvider())
await server.run_async()
Note: `default_options={"store": False, ...}` is set on the agent, but the hosting layer still creates the response with `store=True` (visible in the host log below), so the conversation state is persisted server-side and replayed on every subsequent turn.
Error Messages / Stack Traces
Relevant slice of the host log (timestamps and ids preserved):
2026-04-28 16:51:33 Creating response caresp_...E2VI1... conversation_id=conv_24fe0da107637088004mYJr8gW9lTSNP9jBYudLITxlXvLk9Kp store=True
2026-04-28 16:51:40 POST /responses 200 (turn 1, no tool, ok)
2026-04-28 16:52:07 Creating response caresp_...QC68... conversation_id=conv_24fe0...4mYJr... store=True
2026-04-28 16:52:19 POST /responses 200 (turn 2, after consent + retry, tool ran, ok)
2026-04-28 16:55:33 Creating response caresp_...Yj9D... conversation_id=conv_24fe0...4mYJr... store=True
2026-04-28 16:55:34 ERROR Handler raised after response.created
Full stack of the failing turn:
Traceback (most recent call last):
File "/app/.venv/lib/python3.12/site-packages/agent_framework_openai/_chat_client.py", line 555, in _stream
async for chunk in await client.responses.create(stream=True, **run_options):
File "/app/.venv/lib/python3.12/site-packages/openai/resources/responses/responses.py", line 2565, in create
return await self._post(
File "/app/.venv/lib/python3.12/site-packages/openai/_base_client.py", line 1884, in post
return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)
File "/app/.venv/lib/python3.12/site-packages/openai/_base_client.py", line 1669, in request
raise self._make_status_error_from_response(err.response) from None
openai.BadRequestError: Error code: 400 - {'error': {'message': 'No tool call found for function call output with call_id mcp_06b686e11f118cf40169f0e5badb3081979842929d5cf04920.', 'type': 'invalid_request_error', 'param': 'input', 'code': None}}
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/app/.venv/lib/python3.12/site-packages/azure/ai/agentserver/responses/hosting/_orchestrator.py", line 1243, in _process_handler_events
async for raw in _iter_with_winddown(handler_iterator, ctx.cancellation_signal):
File "/app/.venv/lib/python3.12/site-packages/azure/ai/agentserver/responses/hosting/_orchestrator.py", line 156, in _iter_with_winddown
item = await aiter.__anext__()
File "/app/.venv/lib/python3.12/site-packages/azure/ai/agentserver/responses/hosting/_routing.py", line 347, in _await_and_normalize
async for event in self._normalize_handler_result(inner):
File "/app/.venv/lib/python3.12/site-packages/agent_framework_foundry_hosting/_responses.py", line 230, in _handle_inner_agent
async for update in self._agent.run(stream=True, **run_kwargs):
File "/app/.venv/lib/python3.12/site-packages/agent_framework/_types.py", line 3015, in __anext__
update: UpdateT = await self._iterator.__anext__()
File "/app/.venv/lib/python3.12/site-packages/agent_framework/_tools.py", line 2486, in _stream
async for update in inner_stream:
File "/app/.venv/lib/python3.12/site-packages/agent_framework_openai/_chat_client.py", line 563, in _stream
self._handle_request_error(ex)
File "/app/.venv/lib/python3.12/site-packages/agent_framework_openai/_chat_client.py", line 494, in _handle_request_error
raise ChatClientException(
agent_framework.exceptions.ChatClientException: ("<class '__main__._AzureOpenAIFoundryChatClient'> service failed to complete the prompt: Error code: 400 - {'error': {'message': 'No tool call found for function call output with call_id mcp_06b686e11f118cf40169f0e5badb3081979842929d5cf04920.', 'type': 'invalid_request_error', 'param': 'input', 'code': None}}", BadRequestError(...))
The retry 20 seconds later fails with the exact same `call_id`:
2026-04-28 16:55:55 same trace, same call_id mcp_06b686e11f118cf40169f0e5badb3081979842929d5cf04920, same 400
The MCP tool is a Foundry Toolbox connector pointing to a Logic Apps MCP server protected by Entra ID (auth code + PKCE). The agent forwards the caller bearer token via a custom ASGI middleware; service identity is only used during MCP startup. None of that path is involved in the failing turn, the 400 is raised before the model call goes out.
We have store=False in default_options per project guidance, but the hosting layer logs store=True regardless, so the conversation state appears to be persisted server-side and replayed on every subsequent turn.
Storage on the host side is the default InMemoryResponseProvider from agent-framework-foundry-hosting.
Happy to share a fuller HAR / OTel export privately if helpful.
Description
What happened?
When a Foundry-hosted agent uses a toolbox MCP tool that completed successfully on a previous turn within the same
conversation_id, the next turn fails with a 400 from the Azure OpenAI Responses API:The error is raised inside
agent_framework_openai/_chat_client.py:555when callingclient.responses.create(stream=True, **run_options). Theinputarray sent to the Responses API contains afunction_call_outputwhosecall_idhas no matchingfunction_callin the same array, which violates the Responses API contract. The 400 happens before the model is invoked.The same
conversation_idis reused across turns, whilecaresp_*ids change. A retry of the failing prompt produces the exact same 400 with the same orphancall_id, which confirms the orphan is now persisted in conversation state and is replayed every time.What did you expect to happen?
A subsequent user message in the same conversation should be processed normally. Either:
function_call_outputfrom a prior turn without its matchingfunction_callin the sameinputpayload, orSteps to reproduce the issue
agent-framework-foundry-hosting==1.0.0a260428andagent-framework-core==1.2.1/agent-framework-foundry==1.2.1.oauth_consent_request. After the user authorizes and resends, the tool runs and returns the result. Succeeds.Code Sample
Minimal shape of the agent wiring (full project is a Foundry hosted agent, but the failure is reproducible with this skeleton): from agent_framework_foundry import FoundryChatClient from agent_framework_foundry_hosting import ResponsesHostServer, InMemoryResponseProvider from azure.identity.aio import ManagedIdentityCredential async def main() -> None: async with ( ManagedIdentityCredential() as credential, FoundryChatClient( project_endpoint=PROJECT_ENDPOINT, model=MODEL_DEPLOYMENT_NAME, credential=credential, ).as_agent( name="agent-baseline-python", instructions=SYSTEM_PROMPT, tools=[mcp_toolbox_tool], # Foundry Toolbox MCP connector (OAuth-protected) default_options={ "store": False, "tool_choice": "required", "temperature": 0.2, "max_tokens": 2000, }, ) as agent, ): server = ResponsesHostServer(agent, store=InMemoryResponseProvider()) await server.run_async() Note: `default_options={"store": False, ...}` is set on the agent, but the hosting layer still creates the response with `store=True` (visible in the host log below), so the conversation state is persisted server-side and replayed on every subsequent turn.Error Messages / Stack Traces
Package Versions
agent-framework-core: 1.2.1, agent-framework-foundry: 1.2.1, agent-framework-openai: 1.2.1, agent-framework-foundry-hosting: 1.0.0a260428, mcp: >=1.24,<2, azure-ai-agentserver-core: 2.0.0b3, azure-ai-agentserver-responses: 1.0.0b5,
Python Version
Python 3.12
Additional Context
oauth_consent_requestto the user via aFoundryChatClientsubclass that overrides_parse_chunk_from_openai, since the playground does not render that event natively (related to Python: [Bug]: ResponsesHostServer drops oauth_consent_request and function_approval_request from toolbox MCP connectors #5535). The tool eventually executes successfully on turn 2.store=Falseindefault_optionsper project guidance, but the hosting layer logsstore=Trueregardless, so the conversation state appears to be persisted server-side and replayed on every subsequent turn.InMemoryResponseProviderfromagent-framework-foundry-hosting.