Skip to content

Support V1/V2 per-audience token acquisition for MCP servers#217

Merged
gwharris7 merged 20 commits intomainfrom
pmohapatra-MCP-V1-V2-python
Apr 16, 2026
Merged

Support V1/V2 per-audience token acquisition for MCP servers#217
gwharris7 merged 20 commits intomainfrom
pmohapatra-MCP-V1-V2-python

Conversation

@biswapm
Copy link
Copy Markdown
Contributor

@biswapm biswapm commented Mar 31, 2026

V2 MCP servers require individual OAuth tokens scoped to their own audience GUID rather than the shared ATG token used by V1 servers.

  • Add audience, scope, publisher, headers fields to MCPServerConfig
  • Add resolve_token_scope_for_server() to determine OAuth scope: V2 servers (GUID audience) get /.default, V1 servers fall back to the shared ATG scope
  • Add _attach_per_audience_tokens() to McpToolServerConfigurationService: acquires one token per unique audience (cached), attaches Authorization header to each server config after discovery
  • Extend list_tool_servers() to accept optional authorization context; calls _attach_per_audience_tokens() when provided
  • Preserve audience/scope/publisher fields in manifest and gateway parsers
  • Update McpToolRegistrationService to pass auth context to list_tool_servers() and use per-server headers instead of a single shared token
  • Update tests to reflect the new header flow

All V1 agents continue working unchanged (audience defaults to None, falls back to ATG scope).

V2 MCP servers require individual OAuth tokens scoped to their own
audience GUID rather than the shared ATG token used by V1 servers.

- Add audience, scope, publisher, headers fields to MCPServerConfig
- Add resolve_token_scope_for_server() to determine OAuth scope:
  V2 servers (GUID audience) get <audience>/.default, V1 servers
  fall back to the shared ATG scope
- Add _attach_per_audience_tokens() to McpToolServerConfigurationService:
  acquires one token per unique audience (cached), attaches Authorization
  header to each server config after discovery
- Extend list_tool_servers() to accept optional authorization context;
  calls _attach_per_audience_tokens() when provided
- Preserve audience/scope/publisher fields in manifest and gateway parsers
- Update McpToolRegistrationService to pass auth context to
  list_tool_servers() and use per-server headers instead of a single
  shared token
- Update tests to reflect the new header flow

All V1 agents continue working unchanged (audience defaults to None,
falls back to ATG scope).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 31, 2026

⚠️ Deprecation Warning: The deny-licenses option is deprecated for possible removal in the next major release. For more information, see issue 997.

Dependency Review

The following issues were found:
  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 40 package(s) with unknown licenses.
  • ⚠️ 2 packages with OpenSSF Scorecard issues.
See the Details below.

License Issues

uv.lock

PackageVersionLicenseIssue Type
a2a-sdk0.3.23NullUnknown License
ag-ui-protocol0.1.13NullUnknown License
agent-framework1.0.1NullUnknown License
agent-framework-a2a1.0.0b260409NullUnknown License
agent-framework-ag-ui1.0.0b260409NullUnknown License
agent-framework-anthropic1.0.0b260409NullUnknown License
agent-framework-azure-cosmos1.0.0b260409NullUnknown License
agent-framework-azurefunctions1.0.0b260409NullUnknown License
agent-framework-bedrock1.0.0b260409NullUnknown License
agent-framework-chatkit1.0.0b260409NullUnknown License
agent-framework-claude1.0.0b260409NullUnknown License
agent-framework-copilotstudio1.0.0b260311NullUnknown License
agent-framework-core1.0.1NullUnknown License
agent-framework-declarative1.0.0b260409NullUnknown License
agent-framework-devui1.0.0b260414NullUnknown License
agent-framework-durabletask1.0.0b260409NullUnknown License
agent-framework-foundry1.0.1NullUnknown License
agent-framework-foundry-local1.0.0b260409NullUnknown License
agent-framework-github-copilot1.0.0b260409NullUnknown License
agent-framework-lab1.0.0b251024NullUnknown License
agent-framework-mem01.0.0b260409NullUnknown License
agent-framework-ollama1.0.0b260409NullUnknown License
agent-framework-openai1.0.1NullUnknown License
agent-framework-orchestrations1.0.0b260409NullUnknown License
agent-framework-purview1.0.0b260409NullUnknown License
agent-framework-redis1.0.0b260409NullUnknown License
anthropic0.80.0NullUnknown License
claude-agent-sdk0.1.48NullUnknown License
clr-loader0.2.10NullUnknown License
durabletask1.4.0NullUnknown License
durabletask-azuremanaged1.4.0NullUnknown License
foundry-local-sdk0.5.1NullUnknown License
github-copilot-sdk0.2.1NullUnknown License
mem0ai1.0.11NullUnknown License
microsoft-agents-copilotstudio-client0.7.0NullUnknown License
openai-chatkit1.6.3NullUnknown License
qdrant-client1.17.1NullUnknown License
redis7.1.1NullUnknown License
redisvl0.15.0NullUnknown License
posthog7.12.0NullUnknown License
Denied Licenses: GPL-3.0-only, AGPL-3.0-only

OpenSSF Scorecard

Scorecard details
PackageVersionScoreDetails
pip/a2a-sdk 0.3.23 UnknownUnknown
pip/ag-ui-protocol 0.1.13 UnknownUnknown
pip/agent-framework 1.0.1 UnknownUnknown
pip/agent-framework-a2a 1.0.0b260409 UnknownUnknown
pip/agent-framework-ag-ui 1.0.0b260409 UnknownUnknown
pip/agent-framework-anthropic 1.0.0b260409 UnknownUnknown
pip/agent-framework-azure-ai-search 0.0.0a1 UnknownUnknown
pip/agent-framework-azure-cosmos 1.0.0b260409 UnknownUnknown
pip/agent-framework-azurefunctions 1.0.0b260409 UnknownUnknown
pip/agent-framework-bedrock 1.0.0b260409 UnknownUnknown
pip/agent-framework-chatkit 1.0.0b260409 UnknownUnknown
pip/agent-framework-claude 1.0.0b260409 UnknownUnknown
pip/agent-framework-copilotstudio 1.0.0b260311 UnknownUnknown
pip/agent-framework-core 1.0.1 UnknownUnknown
pip/agent-framework-declarative 1.0.0b260409 UnknownUnknown
pip/agent-framework-devui 1.0.0b260414 UnknownUnknown
pip/agent-framework-durabletask 1.0.0b260409 UnknownUnknown
pip/agent-framework-foundry 1.0.1 UnknownUnknown
pip/agent-framework-foundry-local 1.0.0b260409 UnknownUnknown
pip/agent-framework-github-copilot 1.0.0b260409 UnknownUnknown
pip/agent-framework-lab 1.0.0b251024 UnknownUnknown
pip/agent-framework-mem0 1.0.0b260409 UnknownUnknown
pip/agent-framework-ollama 1.0.0b260409 UnknownUnknown
pip/agent-framework-openai 1.0.1 UnknownUnknown
pip/agent-framework-orchestrations 1.0.0b260409 UnknownUnknown
pip/agent-framework-purview 1.0.0b260409 UnknownUnknown
pip/agent-framework-redis 1.0.0b260409 UnknownUnknown
pip/anthropic 0.80.0 UnknownUnknown
pip/async-timeout 5.0.1 🟢 4.5
Details
CheckScoreReason
Code-Review⚠️ -1Found no human activity in the last 30 changesets
Binary-Artifacts🟢 10no binaries found in the repo
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Packaging⚠️ -1packaging workflow not detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 9license file detected
Signed-Releases⚠️ 0Project has not signed or included provenance with any releases.
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
Security-Policy🟢 10security policy file detected
SAST🟢 9SAST tool detected but not run on all commits
pip/asyncio 4.0.0 UnknownUnknown
pip/azure-cosmos 4.15.0 🟢 6.6
Details
CheckScoreReason
Code-Review🟢 9Found 26/27 approved changesets -- score normalized to 9
Maintained🟢 1030 commit(s) and 15 issue activity found in the last 90 days -- score normalized to 10
Packaging⚠️ -1packaging workflow not detected
CII-Best-Practices🟢 5badge detected: Passing
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Security-Policy🟢 10security policy file detected
License🟢 10license file detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Signed-Releases⚠️ -1no releases found
Branch-Protection🟢 5branch protection is not maximal on development and all release branches
Binary-Artifacts🟢 8binaries present in source code
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Fuzzing🟢 10project is fuzzed
pip/azure-functions 1.24.0 UnknownUnknown
pip/azure-functions-durable 1.5.0 UnknownUnknown
pip/backoff 2.2.1 🟢 3.4
Details
CheckScoreReason
Packaging⚠️ -1packaging workflow not detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Code-Review⚠️ 2Found 7/24 approved changesets -- score normalized to 2
Binary-Artifacts🟢 10no binaries found in the repo
Maintained⚠️ 0project is archived
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST🟢 7SAST tool detected but not run on all commits
pip/boto3 1.42.89 🟢 7.7
Details
CheckScoreReason
Maintained🟢 1030 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
Security-Policy🟢 10security policy file detected
Packaging⚠️ -1packaging workflow not detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Code-Review⚠️ 1Found 4/30 approved changesets -- score normalized to 1
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Token-Permissions🟢 9detected GitHub workflow tokens with excessive permissions
Fuzzing⚠️ 0project is not fuzzed
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies🟢 10all dependencies are pinned
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
SAST🟢 10SAST tool is run on all commits
pip/botocore 1.42.89 🟢 8.3
Details
CheckScoreReason
Maintained🟢 1030 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 10
Code-Review⚠️ 1Found 4/30 approved changesets -- score normalized to 1
Packaging⚠️ -1packaging workflow not detected
Security-Policy🟢 10security policy file detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Token-Permissions🟢 9detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
License🟢 10license file detected
Fuzzing🟢 10project is fuzzed
Signed-Releases⚠️ -1no releases found
Binary-Artifacts🟢 10no binaries found in the repo
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
Pinned-Dependencies🟢 8dependency not pinned by hash detected -- score normalized to 8
SAST🟢 10SAST tool is run on all commits
pip/claude-agent-sdk 0.1.48 UnknownUnknown
pip/clr-loader 0.2.10 UnknownUnknown
pip/durabletask 1.4.0 UnknownUnknown
pip/durabletask-azuremanaged 1.4.0 UnknownUnknown
pip/foundry-local-sdk 0.5.1 UnknownUnknown
pip/furl 2.1.4 ⚠️ 2.9
Details
CheckScoreReason
Packaging⚠️ -1packaging workflow not detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 10no binaries found in the repo
Code-Review⚠️ 1Found 3/27 approved changesets -- score normalized to 1
Maintained⚠️ 11 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 1
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 9license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
pip/github-copilot-sdk 0.2.1 UnknownUnknown
pip/h2 4.3.0 UnknownUnknown
pip/hpack 4.1.0 UnknownUnknown
pip/httptools 0.7.1 UnknownUnknown
pip/hyperframe 6.1.0 UnknownUnknown
pip/jmespath 1.1.0 🟢 6.2
Details
CheckScoreReason
Code-Review🟢 4Found 7/16 approved changesets -- score normalized to 4
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Maintained🟢 44 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 4
Packaging⚠️ -1packaging workflow not detected
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies🟢 6dependency not pinned by hash detected -- score normalized to 6
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
Fuzzing🟢 10project is fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST🟢 7SAST tool detected but not run on all commits
pip/jsonpath-ng 1.8.0 🟢 5.8
Details
CheckScoreReason
Maintained🟢 109 commit(s) and 3 issue activity found in the last 90 days -- score normalized to 10
Packaging⚠️ -1packaging workflow not detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 10no binaries found in the repo
Code-Review🟢 6Found 12/19 approved changesets -- score normalized to 6
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Token-Permissions🟢 9detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST🟢 7SAST tool detected but not run on all commits
pip/mem0ai 1.0.11 UnknownUnknown
pip/microsoft-agents-copilotstudio-client 0.7.0 UnknownUnknown
pip/ml-dtypes 0.5.4 UnknownUnknown
pip/ollama 0.5.3 UnknownUnknown
pip/openai-chatkit 1.6.3 UnknownUnknown
pip/orderedmultidict 1.0.2 ⚠️ 2.1
Details
CheckScoreReason
Dangerous-Workflow⚠️ -1no workflows found
Packaging⚠️ -1packaging workflow not detected
Token-Permissions⚠️ -1No tokens found
Code-Review⚠️ 1Found 3/20 approved changesets -- score normalized to 1
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies⚠️ -1no dependencies found
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ -1no releases found
License🟢 9license file detected
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
pip/portalocker 3.2.0 UnknownUnknown
pip/posthog 7.12.0 UnknownUnknown
pip/powerfx 0.0.34 UnknownUnknown
pip/python-ulid 3.1.0 UnknownUnknown
pip/pythonnet 3.0.5 UnknownUnknown
pip/pytz 2026.1.post1 UnknownUnknown
pip/qdrant-client 1.17.1 UnknownUnknown
pip/redis 7.1.1 UnknownUnknown
pip/redisvl 0.15.0 UnknownUnknown
pip/s3transfer 0.16.0 🟢 7.7
Details
CheckScoreReason
Security-Policy🟢 10security policy file detected
Maintained🟢 57 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 5
Code-Review🟢 6Found 13/20 approved changesets -- score normalized to 6
Packaging⚠️ -1packaging workflow not detected
Binary-Artifacts🟢 10no binaries found in the repo
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Pinned-Dependencies🟢 10all dependencies are pinned
Token-Permissions🟢 9detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
SAST🟢 10SAST tool is run on all commits
pip/uvloop 0.22.1 UnknownUnknown
pip/watchfiles 1.1.1 🟢 3.4
Details
CheckScoreReason
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Binary-Artifacts🟢 10no binaries found in the repo
Code-Review🟢 3Found 8/25 approved changesets -- score normalized to 3
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Packaging⚠️ -1packaging workflow not detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0

Scanned Files

  • uv.lock

biswapm added 10 commits April 3, 2026 15:27
- Bump gateway discovery endpoint to /agents/v2/{id}/mcpServers
- Update resolve_token_scope_for_server() to use explicit scope when
  present (e.g. Tools.ListInvoke.All → {audience}/{scope}), falling
  back to {audience}/.default for pre-consented scopes when null
- Fix api:// V2 audience handling — only ATG AppId in api:// URI form
  is treated as V1; all other api:// audiences are correctly V2
- Pass authorization/auth_handler_name/turn_context to list_tool_servers
  in OpenAI, Semantic Kernel, and Google ADK extensions so
  _attach_per_audience_tokens runs for all frameworks (was previously
  only wired in AgentFramework extension)
- Replace shared single-token header injection with per-server header
  merge ({**base_headers, **server.headers}) in all three extensions
- Add tests for resolve_token_scope_for_server (all V1/V2/null/api://
  scenarios), _attach_per_audience_tokens (V1 dedup, V2 per-audience,
  mixed, error cases, header preservation)

Backward compatible: V1 agents (null/ATG audience) continue using the
shared ATG token unchanged. Row 3 (New blueprint + Old SDK) is by
design not supported — upgrade to this SDK is the migration path.
- Pass authorization/auth_handler_name/turn_context to list_tool_servers
  in OpenAI and Semantic Kernel extensions so _attach_per_audience_tokens
  runs for all frameworks (Google ADK already staged in prior commit)
- Replace shared single-token header injection with per-server header
  merge ({**base_headers, **server.headers}) in OpenAI and SK extensions
- Use explicit scope for V2 token resolution: {audience}/{scope} when
  scope is present, {audience}/.default as pre-consented fallback
- Fix api:// V2 audience handling — only ATG AppId in api:// URI form
  is treated as V1; all other api:// audiences are correctly V2
- Add comprehensive tests for resolve_token_scope_for_server covering
  all V1/V2/null/api:// scenarios including test env audience handling
- Add CHANGELOG entry for microsoft-agents-a365-tooling package

Backward compatible: V1 agents (null/ATG audience) continue using the
shared ATG token unchanged. V2 blueprint + old SDK is by design not
supported — upgrade to this SDK is the migration path.
- Normalize "default" audience → None and "null" scope → None in both
  _parse_manifest_server_config() and _parse_gateway_server_config() so
  Dataverse-style servers are correctly treated as V1 (shared ATG token)
  instead of triggering a bogus V2 token exchange
- Add "default" guard to resolve_token_scope_for_server() as defense-in-depth
- Fall back to mcpServerName when mcpServerUniqueName is absent from the
  manifest or gateway response
- Add _attach_dev_tokens() — reads BEARER_TOKEN_<SERVER_UNIQUE_NAME> and
  BEARER_TOKEN env vars written by `a365 develop get-token` and attaches
  per-server Authorization headers during local dev manifest loading;
  no-op in production where OBO via _attach_per_audience_tokens() is used
…context from add_tool_servers_to_agent into _get_mcp_tool_definitions_and_resources, which now passes them as keyword args to list_tool_servers() so _attach_per_audience_tokens() runs for V2 servers. Header injection reads server.headers (per-audience token) with fallback to the shared auth_token.

test_mcp_tool_registration_service.py — 7 tests covering: auth context forwarding, per-audience token used over shared token, fallback when headers empty, no double Bearer prefix, User-Agent header, and per-server isolation across multiple servers.
…dless of what auth context the extension passes

Prod mode (gateway): OBO runs only when auth context is present
Replace separate _attach_dev_tokens() / _attach_per_audience_tokens(auth, handler, ctx)
with a unified _attach_per_audience_tokens(servers, acquire: TokenAcquirer) that accepts
a pluggable async callable — aligning with Node.js PR #226.

- Add TokenAcquirer type alias: Callable[[MCPServerConfig, str], Awaitable[Optional[str]]]
- Add _create_dev_token_acquirer(): reads BEARER_TOKEN_<MCP_SERVER_NAME_UPPER> (now using
  mcp_server_name, not mcp_server_unique_name, to match Node.js) with BEARER_TOKEN fallback
- Add _create_obo_token_acquirer(): performs OBO exchange per unique audience scope
- Remove _attach_dev_tokens() entirely
- list_tool_servers(): dev branch uses dev acquirer; prod branch uses OBO acquirer when
  auth context present; both paths go through the unified _attach_per_audience_tokens()
- Update TestAttachPerAudienceTokens tests to use _create_obo_token_acquirer() helper
Propagates the inbound activity ID (turn_context.activity.id) as the
x-ms-correlation-id header on all tooling gateway requests, falling back
to a freshly generated UUID4 when no TurnContext is available.
@biswapm biswapm marked this pull request as ready for review April 15, 2026 12:18
@biswapm biswapm requested a review from a team as a code owner April 15, 2026 12:18
Copilot AI review requested due to automatic review settings April 15, 2026 12:18
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 support for per-audience OAuth token acquisition so V2 MCP servers receive audience-scoped tokens (while preserving V1 shared ATG behavior), then threads the resulting per-server headers through tool registration flows and tests.

Changes:

  • Introduces audience/scope/publisher/headers on MCPServerConfig and preserves them in manifest/gateway parsing.
  • Adds resolve_token_scope_for_server() and _attach_per_audience_tokens() to acquire/deduplicate tokens per resolved scope and attach Authorization per server.
  • Updates extensions and tests to use per-server headers (and adds correlation-id support on gateway requests).

Reviewed changes

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

Show a summary per file
File Description
tests/tooling/test_mcp_server_configuration.py Adds test coverage for audience parsing, scope resolution, and per-audience token attachment behavior
tests/tooling/extensions/azureaifoundry/services/test_mcp_tool_registration_service.py New unit tests validating that per-server Authorization is used and auth context is forwarded
tests/tooling/extensions/agentframework/services/test_mcp_tool_registration_service.py Updates tests to reflect per-server headers attached during discovery
libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py Adds scope resolution helper and bumps gateway endpoint to /agents/v2/...
libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/constants.py Adds correlation-id header constant
libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/services/mcp_tool_server_configuration_service.py Implements token acquisition/attachment + correlation-id header generation and parser field propagation
libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/models/mcp_server_config.py Extends config model with headers/audience/scope/publisher
libraries/microsoft-agents-a365-tooling/CHANGELOG.md Documents new V1/V2 token behavior and endpoint change
libraries/microsoft-agents-a365-tooling-extensions-semantickernel/.../mcp_tool_registration_service.py Merges base headers with per-server headers from discovery
libraries/microsoft-agents-a365-tooling-extensions-openai/.../mcp_tool_registration_service.py Propagates server_config.headers and uses per-server headers when creating clients
libraries/microsoft-agents-a365-tooling-extensions-googleadk/.../mcp_tool_registration_service.py Switches from a single shared auth header to per-server headers
libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/.../mcp_tool_registration_service.py Uses per-server Authorization if present and falls back defensively to discovery token
libraries/microsoft-agents-a365-tooling-extensions-agentframework/.../mcp_tool_registration_service.py Passes auth context and merges base headers with per-server headers

Comment thread libraries/microsoft-agents-a365-tooling/CHANGELOG.md Outdated
biswapm added 2 commits April 16, 2026 16:16
- McpToolServerConfigurationService: add _create_dev_token_acquirer() and
  _create_obo_token_acquirer() TokenAcquirer factories; _attach_per_audience_tokens()
  now accepts a TokenAcquirer to decouple token strategy from header attachment
- Strip existing Bearer prefix (case-insensitive) from BEARER_TOKEN* env vars in
  dev acquirer to prevent doubled Authorization headers
- Replace manual MCPServerConfig reconstruction with dataclasses.replace() in
  _attach_per_audience_tokens() so future fields are carried forward automatically
- Merge _parse_manifest_server_config() and _parse_gateway_server_config() into
  single _parse_server_config() (shared JSON schema between both sources)
- utility.py: add is_development_environment() with 4-level env var resolution
  (PYTHON_ENVIRONMENT > ENVIRONMENT > ASPNETCORE_ENVIRONMENT > DOTNET_ENVIRONMENT);
  normalize audience to lowercase in resolve_token_scope_for_server() to ensure
  consistent V1/V2 classification regardless of GUID casing
- OpenAI, Semantic Kernel, Google ADK, Agent Framework extensions: pass auth context
  to list_tool_servers(), skip token exchange in dev mode, add auth fallback header
  when no per-server token is present; fix typing.Any usages
- CHANGELOG: correct stale method names (_attach_dev_tokens -> _create_dev_token_acquirer,
  _parse_*_server_config -> _parse_server_config) and env var naming
  (BEARER_TOKEN_<SERVER_UNIQUE_NAME> -> BEARER_TOKEN_<MCP_SERVER_NAME_UPPER>)
- Tests: add coverage for Bearer prefix stripping (4 casing variants), raw token
  passthrough, per-server env var stripping; fix test fixtures for merged parse
  method and json() mock pattern
- Replace agent-framework-azure-ai (pre-release only) with agent-framework >= 1.0.0
  in root pyproject.toml constraint-dependencies and agentframework extension package;
  agent-framework-core is now pinned to >= 1.0.0 as a separate explicit constraint
- agent-framework 1.0.1 (GA) bundles Azure OpenAI support directly in OpenAIChatClient
  via credential/azure_endpoint params; AzureOpenAIChatClient no longer exists
- Update mcp_tool_registration_service.py (agentframework extension):
  - Import HistoryProvider (renamed from BaseHistoryProvider in GA)
  - Replace Union[OpenAIChatClient, AzureOpenAIChatClient] with OpenAIChatClient
  - Remove agent_framework.azure import; remove unused Union import
- Update test fixtures and docstrings to reflect renamed types
- Revert google-adk constraint to >= 1.0.0 (from >= 1.28.1):
  google-adk >= 1.28.1 pins opentelemetry-api < 1.39.0 while
  agent-framework-core >= 1.0.0 requires opentelemetry-api >= 1.39.0;
  no compatible intersection exists. CVE-2026-4810 (GHSA-rg7c-g689-fr3x)
  affects ADK Web server mode only; this SDK uses google-adk purely as a library
  and does not expose ADK Web, so the vulnerability is not applicable.
Copilot AI review requested due to automatic review settings April 16, 2026 11:07
@biswapm biswapm requested a review from a team as a code owner April 16, 2026 11:07
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

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

Comment thread tests/tooling/test_mcp_server_configuration.py Outdated
biswapm and others added 2 commits April 16, 2026 16:45
Collapse multi-line logger.info() call to single line to satisfy
ruff format --check (line fits within 100-char limit).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- mcp_tool_server_configuration_service.py: replace 'mirrors Node.js behaviour'
  and 'aligns with Node.js' with self-contained descriptions
- utility.py: rephrase is_development_environment() docstring to describe env var
  precedence without mentioning .NET (ASPNETCORE_ENVIRONMENT and DOTNET_ENVIRONMENT
  variable names are kept, only the platform labels are removed)
- test_mcp_server_configuration.py: remove reference to non-existent
  MCP_PLATFORM_APP_ID env var in test section header and docstring; replace with
  accurate guidance pointing to MCP_PLATFORM_AUTHENTICATION_SCOPE /
  MCP_PLATFORM_ENDPOINT as the actual configurable inputs
Copilot AI review requested due to automatic review settings April 16, 2026 11:20
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

Copilot reviewed 17 out of 18 changed files in this pull request and generated 8 comments.

Comment thread tests/tooling/test_mcp_server_configuration.py
biswapm added 2 commits April 16, 2026 17:49
In some CI environments (Linux Python 3.12) agent_framework loads but
its top-level namespace is missing RawAgent/MCPStreamableHTTPTool due to
a partial circular-import during pytest collection.  The new conftest.py
is loaded by pytest before test files in this directory tree are imported,
and adds MagicMock stubs for any absent names so the module-level
`from agent_framework import ...` in the production service succeeds.
The agentframework, semantickernel, and openai extensions unconditionally
prepended BEARER_PREFIX to auth_token in the fallback Authorization
header, producing "Bearer Bearer <token>" when callers already included
the prefix.  Align with the azureaifoundry extension by applying the
same case-insensitive startswith guard before prepending the prefix.
Copilot AI review requested due to automatic review settings April 16, 2026 12: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

Copilot reviewed 18 out of 19 changed files in this pull request and generated no new comments.

biswapm added 2 commits April 16, 2026 18:09
agent_framework.openai causes a circular-import chain under Python 3.12
on Linux: openai.__getattr__ lazily imports agent_framework_openai, which
eventually does `from . import __version__` on the still-initialising
agent_framework package.  Move OpenAIChatClient under TYPE_CHECKING so the
import is never executed at runtime.

Also add the missing is_development_environment() guard to the Azure AI
Foundry extension's add_tool_servers_to_agent, matching the pattern used
by the other three extensions: dev mode skips token exchange and uses ""
as the agentic_app_id for manifest-based discovery.
…th tests

_create_dev_token_acquirer now emits a WARNING when BEARER_TOKEN is the
only token available but the server requires a different audience scope
(V2 server). The warning names the per-server env var the caller should
set to avoid a 401.

Also adds two unit tests that verify the existing legacy production-path
guard: a hard error is raised when list_tool_servers is called without
auth context and V2 servers are discovered, and the call succeeds when
only V1 servers are present.
Copilot AI review requested due to automatic review settings April 16, 2026 13:35
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

Copilot reviewed 18 out of 19 changed files in this pull request and generated 3 comments.

Comment thread libraries/microsoft-agents-a365-tooling/CHANGELOG.md
Comment thread tests/tooling/test_mcp_server_configuration.py
googleadk unconditionally prepended 'Bearer' in the shared-token
fallback path, producing 'Bearer Bearer <token>' when the caller
already included the prefix. Apply the same guard used in the other
three extensions.
@gwharris7 gwharris7 merged commit dc936b1 into main Apr 16, 2026
9 checks passed
@gwharris7 gwharris7 deleted the pmohapatra-MCP-V1-V2-python branch April 16, 2026 17:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants