Skip to content

Python: fix(python): prevent local tool approvals from serializing as MCP responses - #7473

Closed
pratik wayase (PratikWayase) wants to merge 6 commits into
microsoft:mainfrom
PratikWayase:fix/local-tool-approval-mcp-response
Closed

Python: fix(python): prevent local tool approvals from serializing as MCP responses#7473
pratik wayase (PratikWayase) wants to merge 6 commits into
microsoft:mainfrom
PratikWayase:fix/local-tool-approval-mcp-response

Conversation

@PratikWayase

Copy link
Copy Markdown
Contributor

Motivation & Context

Approved local (non-MCP) tool calls fail with a hard 400 error from the OpenAI Responses API

This blocks any agent that uses approval_mode="always_require" on a local FunctionTool when running against the Responses API with service-side storage. The bug requires two independent defects to manifest, and either fix alone prevents the 400.

Fixes #7452

Description & Review Guide

  • What are the major changes?

    1. OpenAI serializer (_chat_client.py): Added _is_hosted_tool_approval guard to both function_approval_request and function_approval_response arms in _prepare_content_for_openai. Local approvals now return {} and are filtered out by the upstream if prepared: check. Also restored the request_uses_service_side_storage guard on the response arm that was accidentally dropped in PR Python: Preserve approval decisions under OpenAI continuation #7407, preventing orphaned responses under storage.
    2. Approval control filtering (_sessions.py, _tools.py): Replaced order-dependent single-pass queue-popping logic with a two-pass set-based approach in four functions: _approval_controls_to_keep, _collect_approval_responses, _collect_unanswered_approval_requests, and _remove_unanswered_approval_batches_from_model_input. Pass 1 collects all resolved call_ids regardless of position; Pass 2 filters using the pre-built set. This eliminates the ordering bug where a terminal result appearing before an approval response (the "approval-resume" layout) caused the resolved response to leak into model input.
    3. Tests: Updated 5 existing tests that asserted the old broken behavior. Added 8 new regression tests covering order-independent resolution, local vs. hosted serialization boundaries, follow-up request semantics, and service-side storage symmetry.
  • What is the impact of these changes?

    • Local gated tools now work correctly on the Responses API path — the primary user-facing fix.
    • Approval resume flows no longer produce 400 errors regardless of message ordering.
    • Hosted/MCP tool approvals are unaffected; they continue to serialize and flow through the API as before.
    • No breaking changes to public APIs; all modifications are internal filtering/serialization logic.
  • What do you want reviewers to focus on?

    • The two-pass pattern in _approval_controls_to_keep — verify the semantic boundary between what session history filtering resolves (terminal results + matching responses) vs. what current-turn collectors resolve (also includes follow-up requests).
    • The _is_hosted_tool_approval guard placement in _chat_client.py — confirm returning {} for local approvals is safe given all upstream callers use if prepared:.
    • The updated existing tests — these previously encoded buggy contracts; verify the new assertions match the intended correct behavior.

Related Issue

Fixes #7452

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue.
  • This is not a breaking change.

Copilot AI review requested due to automatic review settings August 1, 2026 17:56
@agent-framework-automation agent-framework-automation Bot added the python Usage: [Issues, PRs], Target: Python label Aug 1, 2026
@github-actions github-actions Bot changed the title fix(python): prevent local tool approvals from serializing as MCP responses Python: fix(python): prevent local tool approvals from serializing as MCP responses Aug 1, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a Python bug in the OpenAI Responses API integration where local (non-hosted) tool approvals could be serialized as MCP approval items, leading to 400 errors due to orphaned approval responses. It also refactors approval-control filtering/collection to be order-independent so resolved approvals don’t leak into model input based on message/content ordering.

Changes:

  • Guard OpenAI serialization of approval requests/responses so only hosted/MCP approvals are emitted as mcp_approval_* items; local approvals serialize to {} and are dropped upstream.
  • Replace order-dependent approval filtering/collection logic with a two-pass, set-based approach across session/history filtering and tool-loop collectors.
  • Update existing tests and add new regression tests covering local-vs-hosted serialization boundaries and ordering scenarios.

Reviewed changes

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

Show a summary per file
File Description
python/packages/openai/agent_framework_openai/_chat_client.py Adds hosted-approval guards in _prepare_content_for_openai and adjusts message preparation behavior for approval items.
python/packages/openai/tests/openai/test_openai_chat_client.py Updates existing assertions and adds approval serialization regression tests (local vs hosted; storage vs non-storage).
python/packages/core/agent_framework/_tools.py Refactors approval response/request collection and model-input filtering to be order-independent.
python/packages/core/agent_framework/_sessions.py Refactors history-provider approval control filtering to be order-independent.
python/packages/core/tests/core/test_tools.py Adds regression tests for approval collection and model-input filtering behavior.
python/packages/core/tests/core/test_sessions.py Adds regression tests for approval-control history filtering behavior under different orderings.

Comment thread python/packages/openai/agent_framework_openai/_chat_client.py
Comment thread python/packages/openai/tests/openai/test_openai_chat_client.py Outdated
Comment thread python/packages/openai/tests/openai/test_openai_chat_client.py
Comment thread python/packages/core/tests/core/test_tools.py
@moonbox3

Copy link
Copy Markdown
Contributor

pratik wayase (@PratikWayase) please have a look at the failing unit tests

westey (westey-m) and others added 2 commits August 15, 2026 12:50
* Add approval storage and improve truth checks

* Address PR comments

* Update spec

* Revert changes to agui since it is already handled in another pr

* Add missed change
@agent-framework-automation agent-framework-automation Bot added the documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs label Aug 15, 2026
@moonbox3

Copy link
Copy Markdown
Contributor

Please re-open when CI/CD checks (unit tests, code quality, etc) are all passing locally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Bug]: A local tool's approval response is serialized to the Responses API as an MCP approval response, with no matching request → 400

4 participants