-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Python: Add Foundry-Features header to toolbox requests #7696
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,6 +42,22 @@ | |
| DEFAULT_TOOLBOX_SCOPE = "https://ai.azure.com/.default" | ||
| # Default timeout (seconds) for toolbox MCP requests. | ||
| _DEFAULT_TIMEOUT = 120.0 | ||
| # Environment variable used to inject platform-provided toolbox feature flags. | ||
| _TOOLSET_FEATURES_ENV_VAR = "FOUNDRY_AGENT_TOOLSET_FEATURES" | ||
| # Mandatory preview feature flag for Foundry toolbox requests. | ||
| _MANDATORY_TOOLBOX_FEATURE = "Toolboxes=V1Preview" | ||
|
|
||
|
|
||
| def _build_toolbox_features_header(additional_features: str | None) -> str: | ||
| """Merge platform-provided features with the mandatory toolbox feature.""" | ||
| if additional_features is None or not additional_features.strip(): | ||
| return _MANDATORY_TOOLBOX_FEATURE | ||
| if any( | ||
| feature.strip().casefold() == _MANDATORY_TOOLBOX_FEATURE.casefold() | ||
| for feature in additional_features.split(",") | ||
| ): | ||
| return additional_features | ||
| return f"{_MANDATORY_TOOLBOX_FEATURE},{additional_features}" | ||
|
|
||
|
|
||
| def _resolve_toolbox_endpoint() -> str: | ||
|
|
@@ -81,7 +97,7 @@ def _toolbox_name_from_endpoint(endpoint: str) -> str: | |
|
|
||
|
|
||
| class _ToolboxAuth(httpx.Auth): | ||
| """Injects a fresh bearer token and the platform call-id on every request. | ||
| """Injects a fresh bearer token, feature flags, and the platform call-id on every request. | ||
|
|
||
| Both the synchronous (``sync_auth_flow``) and asynchronous (``async_auth_flow``) | ||
| httpx auth hooks are implemented, so the same auth works regardless of which | ||
|
|
@@ -99,11 +115,14 @@ class _ToolboxAuth(httpx.Auth): | |
| def __init__(self, credential: AzureCredentialTypes, scope: str) -> None: | ||
| self._credential = credential | ||
| self._scope = scope | ||
| # Feature flags are startup configuration, matching the .NET toolbox service. | ||
| self._features_header = _build_toolbox_features_header(os.environ.get(_TOOLSET_FEATURES_ENV_VAR)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we capture this value once in |
||
|
|
||
| def _apply_headers(self, request: httpx.Request, token: AccessToken) -> None: | ||
| request.headers["Authorization"] = f"Bearer {token.token}" | ||
| for key, value in get_request_context().platform_headers().items(): | ||
| request.headers[key] = value | ||
| request.headers["Foundry-Features"] = self._features_header | ||
|
Comment on lines
121
to
+125
|
||
|
|
||
| def sync_auth_flow(self, request: httpx.Request) -> Generator[httpx.Request, httpx.Response, None]: | ||
| # azure-core credentials cache the token internally and only refresh near | ||
|
|
@@ -138,7 +157,8 @@ class FoundryToolbox(MCPStreamableHTTPTool): | |
| ``MCPStreamableHTTPTool`` by hand it: | ||
|
|
||
| - resolves the toolbox endpoint and tool name from the environment when not given, | ||
| - authenticates every request with a bearer token from ``credential``, and | ||
| - authenticates every request with a bearer token from ``credential``, | ||
| - sends the mandatory toolbox preview feature plus platform-provided feature flags, and | ||
| - forwards the platform per-request call-id (``x-agent-foundry-call-id``) so the | ||
| Foundry MCP proxy can resolve the caller context server-side. | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the preview flag is obsolete. Where did you see it's still required?