Python: fix(core): authenticate MCP connect with run-supplied header_provider kwargs - #3
Closed
jpalvarezl wants to merge 9 commits into
Closed
Python: fix(core): authenticate MCP connect with run-supplied header_provider kwargs#3jpalvarezl wants to merge 9 commits into
jpalvarezl wants to merge 9 commits into
Conversation
Constructor-supplied MCP tools now seed the run's function_invocation_kwargs before connecting, matching the run-supplied path. Adds coverage for the connect-time credential matrix and fixes the API-key sample to close over a construction-time credential.
jpalvarezl
had a problem deploying
to
github-app-auth
September 9, 2026 15:19 — with
GitHub Actions
Failure
jpalvarezl
had a problem deploying
to
github-app-auth
September 9, 2026 15:19 — with
GitHub Actions
Failure
jpalvarezl
had a problem deploying
to
github-app-auth
September 9, 2026 15:19 — with
GitHub Actions
Failure
… provider's key Ambient header resolution previously swallowed every KeyError from header_provider, so a run whose kwargs did not carry the key the provider reads would silently send the initialize handshake unauthenticated and surface as a 401. Only tolerate the KeyError when no connection kwargs were seeded at all, which is the case a caller cannot avoid; a key missing from seeded kwargs is a misconfiguration and now propagates.
jpalvarezl
had a problem deploying
to
github-app-auth
September 10, 2026 09:29 — with
GitHub Actions
Failure
jpalvarezl
had a problem deploying
to
github-app-auth
September 10, 2026 09:30 — with
GitHub Actions
Failure
jpalvarezl
had a problem deploying
to
github-app-auth
September 10, 2026 09:38 — with
GitHub Actions
Failure
…wargs on failed connect Addresses review on microsoft#8225. An empty mapping could not express the difference between a run that seeded no kwargs and a connection no run ever seeded, so a lazy run with empty kwargs still swallowed the provider's KeyError and sent an unauthenticated handshake. _connection_kwargs is now None until a run seeds it. The seeded kwargs were also only released through close(); a rejected handshake unwinds via _close_and_check_cancelled instead, leaving the failed run's credential for a later unseeded reconnect, so the release now happens there. Documents the connection-lifetime semantics on the public header_provider parameter.
… claim Addresses review on microsoft#8225. is_connected only turns true after initialize returns, so two concurrent runs could both pass the guard and the second would swap the credential out from under the first run's in-flight handshake, authenticating a shared tool as the wrong caller. The seed is now a first-writer claim, released when the connection closes or its setup fails.
jpalvarezl
had a problem deploying
to
github-app-auth
September 10, 2026 12:04 — with
GitHub Actions
Failure
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
jpalvarezl
had a problem deploying
to
github-app-auth
September 10, 2026 12:16 — with
GitHub Actions
Failure
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
header_providerwas only consulted with the run's kwargs fortools/call. Connection-lifetime requests (theinitializehandshake, tool discovery, pings) resolved against an empty mapping, so a provider deriving credentials from the run could authenticate tool calls but never the handshake. Servers that authenticateinitializereturned 401 before any tool ran.Both agent connect paths now seed the run's
function_invocation_kwargsinto connection-lifetime header resolution. The kwargs are cached rather than the resolved headers, so providers are still re-invoked per request and token-refresh setups keep working. Connection headers resolve once per connection (the run that establishes it wins) and are released on close.No public API change.
Fixes microsoft#7841.
Test matrix
test_agent_run_supplies_mcp_connect_headerstest_agent_context_manager_authenticates_connect_with_closure_providertest_constructor_supplied_mcp_tool_uses_run_credentials_on_lazy_connecttest_standalone_static_header_provider_authenticates_without_a_runtest_connection_kwargs_are_fixed_for_the_connection_and_cleared_on_closetest_seeded_connection_kwargs_authenticate_the_handshake(parametrized)Each runs against a mock server that returns 401 for unauthenticated requests, so they fail for the right reason.
Notes
samples/02-agents/mcp/mcp_api_key_auth.pyused a kwargs-indexing provider underasync with Agent(...), where connect precedes any run, so the credential could never be read. It now closes over the construction-time key, with guidance on when per-run kwargs apply instead.headers=constructor argument (as proposed in Python: Add origin-scoped headers for MCP connect authentication microsoft/agent-framework#7892). A provider closing over a construction-time credential already authenticates the eager handshake, and a static value would be frozen for the connection lifetime, regressing token-refresh setups like the Foundry toolbox sample.Agent.__aenter__. AG-UIcollect_server_tools()gates onis_connectedand runs before the agent's run, so deferring would silently drop MCP tools from approval flows.Follow-up: KeyError handling on ambient requests
Ambient header resolution previously swallowed every
KeyErrorfromheader_provider. With kwargs now seeded, that tolerance hid a real misconfiguration: a provider readingkwargs["credential"]against seeded kwargs that carry a different key would send the handshake unauthenticated and surface as an opaque 401.The
KeyErroris now tolerated only when nothing was seeded — the one case no caller can avoid, since a connection-lifetime request genuinely has no per-call values. When kwargs were seeded, a missing key propagates. Covered bytest_seeded_kwargs_missing_the_providers_key_fails_the_handshake.