Skip to content

Python: Clean up MCP HTTP resources after failed connections - #8126

Open
Eduard van Valkenburg (eavanvalkenburg) wants to merge 2 commits into
mainfrom
mcp-http-cleanup-pr
Open

Python: Clean up MCP HTTP resources after failed connections#8126
Eduard van Valkenburg (eavanvalkenburg) wants to merge 2 commits into
mainfrom
mcp-http-cleanup-pr

Conversation

@eavanvalkenburg

Copy link
Copy Markdown
Member

Motivation & Context

#8039 scopes provider headers per tool, but failed MCP HTTP connections can still retain authentication request hooks until an explicit close, and framework-created HTTP clients are not owned by the MCP session. This focused follow-up ties cleanup to the session exit stack so failed initialization or cancellation, normal shutdown, and reconnects release resources without replacing #8039.

Description & Review Guide

  • What are the major changes? Register authentication-hook removal and framework-owned client cleanup before transport entry, clear a closed client reference so reconnect can create a fresh client, and add real MCP transport regressions covering shared clients, failures, concurrency, and declarative cache reuse and eviction.
  • What is the impact of these changes? Failed connections release authentication hooks and owned resources without requiring a separate close; successful sessions retain hooks through shutdown; caller-owned clients and unrelated hooks remain reusable. There is no public API change.
  • What do you want reviewers to focus on? Exit-stack ordering, cancellation and reconnect behavior, and preservation of shared-client ownership during eviction.

Related Issue

N/A — no public issue is linked. Follow-up to the merged #8039, limited to remaining resource-lifecycle gaps and regression coverage. The open #7892 adds static connect-time headers and addresses a different concern.

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 (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

Register header-hook and owned HTTP client cleanup on the MCP session exit stack so failed initialization, cancellation, reconnects, and shutdown release resources without closing caller-owned clients. Preserve the request-scoping and redirect behavior from #8039 and cover shared-client declarative cache reuse and eviction.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

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.

🟢 Approval recommended

The cleanup ordering and ownership behavior are consistent and comprehensively covered by targeted regressions.

Pull request overview

Ensures MCP HTTP authentication hooks and framework-owned clients are cleaned up across failures, shutdowns, and reconnects.

Changes:

  • Registers client and hook cleanup on the session exit stack.
  • Clears closed client references to support reconnection.
  • Adds shared-client, failure, cancellation, concurrency, and cache lifecycle tests.
File summaries
File Description
python/packages/core/agent_framework/_mcp.py Implements exit-stack resource cleanup.
python/packages/core/tests/core/test_mcp_http_auth.py Adds MCP HTTP lifecycle regressions.
python/packages/declarative/tests/test_default_mcp_tool_handler.py Tests shared-client cache cleanup.
python/packages/core/AGENTS.md Documents MCP HTTP ownership semantics.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0
  • Review effort level: Balanced

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

@github-code-quality

github-code-quality Bot commented Sep 7, 2026

Copy link
Copy Markdown

Code Coverage Overview

Languages: Python

Python / code-coverage/python

The overall line coverage in commit d97c41e in the mcp-http-cleanup-pr branch is 91%. Line coverage data for the main branch is not yet available.

Show a line coverage summary of the most covered files.
File main mcp-http-cleanup-pr d97c41e +/-
packages/core/a...work/_skills.py 95%
packages/core/a...ework/_tools.py 94%
packages/core/a...rk/_sessions.py 94%
packages/core/a...ework/_types.py 93%
packages/core/a...bservability.py 93%
packages/core/a.../_compaction.py 93%
packages/core/a...amework/_mcp.py 92%
packages/openai..._chat_client.py 92%
packages/ag-ui/...i/_agent_run.py 91%
packages/foundr...g/_responses.py 87%

Updated September 07, 2026 18:46 UTC

Comment thread python/packages/core/tests/core/test_mcp_http_auth.py Fixed

@github-actions github-actions Bot 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.

MAF Automated Review — Iteration 1

Result: Findings reported
Scope: full PR (1 commit(s)): 3590960149b8
Model: gpt-5.6-sol-fast

Overview

The change correctly registers tool-specific hook removal and framework-owned client closure before transport entry, preserves caller-owned clients, and clears closed client references for reconnect. Its tests provide strong coverage for transport entry, cancellation, initialization, shared-client isolation, and cache eviction. One connection setup phase remains outside cleanup-on-failure handling: server-controlled tool or prompt discovery can fail after initialization and retain the newly registered resources.

Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
1 verified finding remained after source verification (1 medium) across 1 file. Details are attached to the affected lines below.

Affected areas: python/packages/core/agent_framework/_mcp.py

Comment thread python/packages/core/agent_framework/_mcp.py
Close the lifecycle-owned session when post-transport setup fails, reset discovery flags, and roll back partially loaded functions and metadata before retrying. Preserve caller-owned HTTP clients and the original setup exception.

Correct the Python 3.10 cancellation expectation, explicitly type async fixture values for Zuban, and assert the joined task result instead of ignoring it. Add discovery failure, cancellation, context-entry, and paginated retry coverage.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comment on lines +186 to +190
if failure == "cancellation":
task = asyncio.current_task()
assert task is not None
task.cancel()
await asyncio.sleep(0)

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.

Are we covering cancellation of the task awaiting connect(), rather than cancellation raised inside the lifecycle owner? connect() queues work and awaits a future, so cancelling the caller cancels only that future; the owner can finish setup and leave is_connected, the authentication hook, and an owned HTTP client live even though async with never entered. Could the queued action observe caller cancellation and close its resources before completing?

Comment on lines +1541 to +1543
except (Exception, asyncio.CancelledError):
try:
await self._close_on_owner()

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.

Should this cleanup preserve a caller-supplied session? _close_on_owner() unconditionally sets self.session to None, even though that session was never entered on _exit_stack; after a transient load_tools() failure, the next connect() silently opens the subclass URL or command instead of retrying the caller's still-live session. Could we track session ownership and only discard sessions created by the framework?

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.

3 participants