Skip to content

Python: drop hosted MCP calls when reasoning is stripped#6210

Merged
moonbox3 merged 1 commit into
microsoft:mainfrom
he-yufeng:fix/openai-drop-mcp-call-with-reasoning
Jun 4, 2026
Merged

Python: drop hosted MCP calls when reasoning is stripped#6210
moonbox3 merged 1 commit into
microsoft:mainfrom
he-yufeng:fix/openai-drop-mcp-call-with-reasoning

Conversation

@he-yufeng
Copy link
Copy Markdown
Contributor

Motivation and Context

Fixes #6074.

When OpenAI Responses history is replayed without service-side storage, the client strips text_reasoning because replaying standalone reasoning items is rejected by the API. Hosted MCP calls from the same model output were still kept, which can leave the request with a bare mcp_call after its paired reasoning item has been removed.

Description

This change treats that stateless replay path as an all-or-nothing pair for hosted MCP items. If a message contains reasoning that will be stripped, hosted MCP call/result contents in the same message are skipped too. Any later unmatched MCP result marker is then dropped by the existing coalescing logic.

The existing behavior for hosted MCP calls without reasoning is unchanged, and storage-backed continuation still strips server-issued MCP items as before.

A regression test covers a reasoning plus hosted MCP call/result history and verifies that the prepared input does not contain a bare reasoning, mcp_call, or function_call_output item.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

Copilot AI review requested due to automatic review settings May 30, 2026 15:23
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds handling for a replay scenario where reasoning content is stripped (when not using service-side storage) by also dropping the paired hosted-MCP tool call/result to avoid emitting invalid item sequences to the OpenAI API.

Changes:

  • Add a unit test that asserts hosted-MCP call/result are dropped when paired reasoning is stripped (no service-side storage).
  • Update _prepare_message_for_openai to detect “reasoning will be dropped” and skip serializing mcp_call accordingly.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
python/packages/openai/tests/openai/test_openai_chat_client.py Adds regression test for dropping hosted-MCP call/result when reasoning is stripped.
python/packages/openai/agent_framework_openai/_chat_client.py Drops mcp_call when reasoning is stripped without storage to prevent invalid payloads.

Comment thread python/packages/openai/agent_framework_openai/_chat_client.py Outdated
Comment thread python/packages/openai/agent_framework_openai/_chat_client.py Outdated
@moonbox3
Copy link
Copy Markdown
Contributor

moonbox3 commented Jun 2, 2026

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/openai/agent_framework_openai
   _chat_client.py109214886%276, 289, 631–635, 643–646, 652–656, 706–713, 715–717, 724–726, 772, 780, 803, 921, 1020, 1079, 1081, 1083, 1085, 1151, 1165, 1245, 1255, 1260, 1303, 1414–1415, 1430, 1657, 1662, 1666–1668, 1672–1673, 1756, 1766, 1793, 1799, 1809, 1815, 1820, 1826, 1831–1832, 1851, 1854–1857, 1871, 1873, 1881–1882, 1894, 1936, 2026, 2048–2049, 2064–2065, 2083–2084, 2127, 2293, 2331–2332, 2350, 2430–2438, 2468, 2578, 2613, 2628, 2648–2658, 2671, 2682–2686, 2700, 2714–2725, 2734, 2766–2769, 2779–2780, 2791–2793, 2807–2809, 2819–2820, 2826, 2841
TOTAL37988442688% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
7579 34 💤 0 ❌ 0 🔥 1m 53s ⏱️

Copy link
Copy Markdown
Contributor

@moonbox3 moonbox3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check the failing tests as well.

Comment thread python/packages/openai/agent_framework_openai/_chat_client.py Outdated
Comment thread python/packages/openai/agent_framework_openai/_chat_client.py Outdated
@he-yufeng he-yufeng force-pushed the fix/openai-drop-mcp-call-with-reasoning branch from b20225e to 67f2890 Compare June 2, 2026 18:51
@he-yufeng
Copy link
Copy Markdown
Contributor Author

Rebased this branch onto latest upstream/main and re-ran the checks for the OpenAI changes here.

Local validation:

  • uv run --dev python -m ruff check packages/openai/agent_framework_openai/_chat_client.py packages/openai/tests/openai/test_openai_chat_client.py
  • uv run --dev python -m py_compile packages/openai/agent_framework_openai/_chat_client.py packages/openai/tests/openai/test_openai_chat_client.py
  • uv run --dev python -m mypy --config-file pyproject.toml packages/openai/agent_framework_openai/_chat_client.py
  • uv run --dev python -m pytest packages\openai\tests\openai\test_openai_chat_client.py -q -m "not integration" --basetemp .tmp\pytest-6210-unit

I also checked the failed GitHub Actions job. The failing cases are the Docker shell-tool tests, and the failure is from pulling alpine:3:

Unable to find image 'alpine:3' locally
docker: Error response from daemon: Get "https://registry-1.docker.io/v2/": context deadline exceeded

Those tests do not touch the OpenAI message serialization path changed in this PR. I did not change Docker-related code.

One local note: running the full test_openai_chat_client.py file includes live integration tests; those are blocked in my local environment by missing model config / an invalid local OpenAI credential, so I used the non-integration subset above for this PR's regression coverage.

@he-yufeng he-yufeng force-pushed the fix/openai-drop-mcp-call-with-reasoning branch from 67f2890 to 207e60c Compare June 4, 2026 04:08
@he-yufeng
Copy link
Copy Markdown
Contributor Author

Follow-up pushed in 207e60c89.

Changes:

  • lifted the hosted-MCP drop decision from per-message to the full replay input, so custom history providers that split text_reasoning, mcp_server_tool_call, and mcp_server_tool_result across adjacent messages do not replay a bare mcp_call;
  • added a regression test for that cross-message case;
  • kept ordinary function_call replay unchanged under store=False.

I checked the function_call question, and it is not equivalent to hosted MCP in this path: the existing store=false tool-loop tests need the function_call and matching function_call_output to be replayed together on the second request. Dropping them breaks test_tool_loop_store_false_omits_reasoning_items_from_second_request and the full-conversation reasoning/function-call test. Hosted MCP is different here because the call/result is serialized as one mcp_call item, and replaying the call side without its stripped reasoning is the invalid shape this PR is fixing.

Validation:

uv run --dev python -m pytest packages\openai\tests\openai\test_openai_chat_client.py -q -m "not integration" --basetemp .tmp\pytest-6210-20260604b
uv run --dev python -m ruff check packages\openai\agent_framework_openai\_chat_client.py packages\openai\tests\openai\test_openai_chat_client.py
uv run --dev python -m py_compile packages\openai\agent_framework_openai\_chat_client.py packages\openai\tests\openai\test_openai_chat_client.py
uv run --dev python -m mypy --config-file pyproject.toml packages\openai\agent_framework_openai\_chat_client.py
git diff --check upstream/main..HEAD

@moonbox3 moonbox3 requested review from TaoChenOSU and giles17 June 4, 2026 04:35
@moonbox3 moonbox3 enabled auto-merge June 4, 2026 04:39
@moonbox3 moonbox3 added this pull request to the merge queue Jun 4, 2026
Merged via the queue into microsoft:main with commit bc0e65d Jun 4, 2026
36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Bug]: ] Orphaned mcp_call items cause HTTP 400 in multi-turn reasoning model flows

4 participants