Python: Remove bespoke Foundry toolbox helpers; standardize on MCP for toolbox consumption#5671
Python: Remove bespoke Foundry toolbox helpers; standardize on MCP for toolbox consumption#5671moonbox3 wants to merge 9 commits intomicrosoft:mainfrom
Conversation
…ption - Remove RawFoundryChatClient.get_toolbox() and its fetch_toolbox import - Remove fetch_toolbox, select_toolbox_tools, get_toolbox_tool_name, get_toolbox_tool_type, FoundryHostedToolType, ToolboxToolSelectionInput from agent_framework_foundry._tools - Remove ExperimentalFeature.TOOLBOXES from _feature_stage.py (no consumers) - Drop toolbox re-exports from agent_framework_foundry/__init__.py and agent_framework.foundry namespace - Update _sanitize_foundry_response_tool docstring to remove toolbox framing; sanitization logic itself is unchanged - Update _agent.py docstring: 'toolbox-fetched MCP' → 'hosted MCP' - Delete tests/test_toolbox.py (all tests covered removed helpers) - Update test_foundry_chat_client.py: rename/redoc tests that mentioned toolbox but test sanitization that remains - Delete foundry_chat_client_with_toolbox.py (bespoke toolbox API sample) - Delete foundry_toolbox_context_provider.py (relied on select_toolbox_tools) - Rename foundry_chat_client_with_toolbox_mcp.py → foundry_chat_client_with_toolbox.py (canonical MCP pattern) - Rewrite 04_foundry_toolbox/main.py to use MCPStreamableHTTPTool - Update provider/README, context_providers/README, 04_foundry_toolbox/README Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…osoft#5670) Replace removed get_toolbox/select_toolbox_tools APIs with MCPStreamableHTTPTool, using allowed_tools=["code_interpreter"] to select only the code interpreter from the toolbox endpoint. Update .env.example and README to use FOUNDRY_TOOLBOX_ENDPOINT instead of TOOLBOX_NAME. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…crosoft#5670) Remove the 'fetch, optionally filter, and pass tools directly' pattern from the FoundryChatClient toolbox documentation, as select_toolbox_tools and get_toolbox were removed. Only the MCP endpoint pattern is documented. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…uction report Remove REPRODUCTION_REPORT.md (workflow artifact that should not be committed), and update two remaining docstring references that still said 'toolbox reads' /'toolbox definition' after the toolbox helpers were removed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…r toolbox consumption Fixes microsoft#5670
Python Test Coverage Report •
Python Unit Test Overview
|
||||||||||||||||||||||||||||||||||||||||||||||||||
moonbox3
left a comment
There was a problem hiding this comment.
Automated Code Review
Reviewers: 4 | Confidence: 93%
✓ Correctness
All four changed files contain only cosmetic or housekeeping edits: a blank-line removal in a test file, three string-literal reformats in sample files, and removal of four symbols (FoundryHostedToolType, get_toolbox_tool_name, get_toolbox_tool_type, select_toolbox_tools) from the foundry stub (init.pyi). The removed symbols were already absent from init.py, so the stub now correctly reflects the package's public surface. No logic changes, no new code paths, and no correctness issues were identified.
✓ Security Reliability
This PR makes four unrelated changes: (1) removes a blank line in a test file, (2) removes four symbols from the foundry type stub that no longer exist in the underlying package, (3) reformats a long function call in a sample, and (4) joins a split string literal in another sample. The symbol removals from the .pyi stub are safe — a grep over the entire python/ tree finds zero references to FoundryHostedToolType, get_toolbox_tool_name, get_toolbox_tool_type, or select_toolbox_tools. No security or reliability issues were identified.
✓ Test Coverage
The PR makes four kinds of changes: (1) a blank-line removal in a test file, (2) removal of four stale symbols (
FoundryHostedToolType,get_toolbox_tool_name,get_toolbox_tool_type,select_toolbox_tools) from thefoundry/__init__.pyitype stub, and (3)/(4) line-length reformatting in two sample files. No new runtime behaviour is introduced, so there are no missing tests for new code paths. The removed symbols were never present infoundry/__init__.py's_IMPORTSdict (the actual lazy-loader), meaning they were never importable at runtime; the.pyientries were simply stale. The existingtest_foundry_namespace.pycovers the positive surface (symbols that should be present) but has no negative assertion guarding against re-introduction of the removed names. This is a pre-existing gap rather than something introduced by this PR, so no change is blocked. A low-priority suggestion is to add a negative assertion so that accidental re-addition of these symbols is caught automatically.
✓ Design Approach
I did not find any design-approach issues in this diff. The sample and test edits are formatting-only, and the
agent_framework.foundry.__init__.pyichange appears to remove stale type-stub exports that are already absent from the runtime lazy namespace (python/packages/core/agent_framework/foundry/__init__.py) and from the underlyingagent_framework_foundrypackage surface, so it aligns the stub with the actual public contract rather than narrowing a supported API.
Suggestions
- Consider adding negative assertions in
python/packages/core/tests/core/test_foundry_namespace.pyto guard against accidental re-introduction of the removed symbols — e.g.assert 'FoundryHostedToolType' not in dir(foundry). This mirrors the pattern used intest_azure_namespace_no_longer_exposes_foundry_symbolsand would catch stale stub entries being re-synced with the wrong implementation.
Automated review by automated agents
There was a problem hiding this comment.
Automated Code Review
Reviewers: 4 | Confidence: 90%
✓ Correctness
The PR cleanly removes the bespoke toolbox helpers and consistently updates all production code, exports, type stubs, and most samples. One
.env.examplefile was missed —04_foundry_toolbox/.env.examplestill references the oldTOOLBOX_NAMEvariable while the correspondingmain.pywas updated to readFOUNDRY_TOOLBOX_ENDPOINT. The observability README also still references the now-removedclient.get_toolbox(...)API, though that's informational rather than functional.
✓ Security Reliability
This PR cleanly removes the bespoke Foundry toolbox helpers (get_toolbox, select_toolbox_tools, etc.) and standardizes on MCPStreamableHTTPTool for toolbox consumption. The security-critical sanitization layer (_sanitize_foundry_response_tool) that strips extraneous fields and validates hosted tool payloads before sending to the Responses API is preserved. The sample rewrites use standard Azure credential patterns (DefaultAzureCredential, get_bearer_token_provider) and properly scope tokens to 'https://ai.azure.com/.default'. No injection risks, secrets, resource leaks, or unhandled failure modes were identified.
✓ Test Coverage
This PR cleanly removes bespoke toolbox helpers and their corresponding tests. The deleted test_toolbox.py (435 lines) tested exclusively the removed functions (get_toolbox, select_toolbox_tools, get_toolbox_tool_name, get_toolbox_tool_type, fetch_toolbox). The remaining sanitization logic (_sanitize_foundry_response_tool, _validate_hosted_tool_payload) retains comprehensive test coverage in test_foundry_chat_client.py covering all branches: MCP tool name stripping, code_interpreter container injection/preservation, file_search validation, mcp validation, and non-function hosted tool dict stripping. The MCPStreamableHTTPTool pattern used in rewritten samples is already tested in core/tests/core/test_mcp.py including the allowed_tools parameter. No test coverage gaps found.
✗ Design Approach
The MCP migration is directionally sound, but it drops one concrete capability the old toolbox surface explicitly supported: immutable version pining. The previous
get_toolbox(..., version=...)path and its tests show callers could bind to a specific toolbox version, while the new endpoint-only guidance standardizes on a name-based/toolsets/<name>/mcpURL. That means a toolbox default-version flip can silently change the tools an agent gets, so I would request changes unless a version-aware MCP consumption path remains available or is documented.
Flagged Issues
- Immutable toolbox version pining is lost by the MCP-only replacement. The removed
get_toolbox()API documentedversionas an 'Optional immutable version identifier to pin to' (_chat_client.py:520-533), andtests/test_toolbox.py:93-109exercised that path. The new guidance standardizes on/toolsets/<name>/mcp(name-based), meaning a toolbox default-version change can silently alter the tools an agent receives. A version-aware MCP consumption path should be preserved or documented.
Automated review by moonbox3's agents
There was a problem hiding this comment.
Pull request overview
This PR removes the bespoke “Foundry toolbox helper” surface from the Python Foundry integration and standardizes toolbox consumption on MCP (MCPStreamableHTTPTool), updating samples/docs and cleaning up the related experimental flag and exports.
Changes:
- Removed toolbox-fetching/selection helpers (
get_toolbox,fetch_toolbox,select_toolbox_tools, etc.) and theExperimentalFeature.TOOLBOXESflag, plus associated exports/re-exports. - Updated Foundry toolbox samples and hosted-agent samples to connect via the toolbox MCP endpoint using
MCPStreamableHTTPTool. - Removed now-obsolete toolbox-focused tests and updated remaining sanitizer tests/docstrings to be toolbox-agnostic (“hosted tool” framing).
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| python/samples/04-hosting/foundry-hosted-agents/responses/06_files/README.md | Updates run instructions to use FOUNDRY_TOOLBOX_ENDPOINT (MCP endpoint). |
| python/samples/04-hosting/foundry-hosted-agents/responses/06_files/main.py | Switches from toolbox helpers to MCPStreamableHTTPTool (with bearer token header provider). |
| python/samples/04-hosting/foundry-hosted-agents/responses/06_files/.env.example | Replaces TOOLBOX_NAME with FOUNDRY_TOOLBOX_ENDPOINT. |
| python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/README.md | Documents MCP endpoint pattern for toolboxes. |
| python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/main.py | Uses MCPStreamableHTTPTool against toolbox MCP endpoint. |
| python/samples/04-hosting/container/hyperlight_codeact/call_server.py | Minor string formatting cleanup for default endpoint value. |
| python/samples/02-agents/security/email_security_example.py | Formatting-only change (wraps a long agent.run(...) call). |
| python/samples/02-agents/providers/foundry/README.md | Removes separate “toolbox vs toolbox_mcp” listing; keeps MCP pattern as canonical. |
| python/samples/02-agents/providers/foundry/foundry_chat_client_with_toolbox.py | Reworked to demonstrate toolbox consumption via MCP endpoint (and deletes prior multi-toolbox helper flows). |
| python/samples/02-agents/providers/foundry/foundry_chat_client_with_toolbox_mcp.py | Deleted (superseded by the canonical toolbox sample). |
| python/samples/02-agents/context_providers/README.md | Removes the toolbox-context-provider sample entry and prerequisites. |
| python/samples/02-agents/context_providers/foundry_toolbox_context_provider.py | Deleted (relied on removed helper APIs). |
| python/packages/foundry/tests/test_toolbox.py | Deleted (tests for removed toolbox helper surface). |
| python/packages/foundry/tests/foundry/test_foundry_chat_client.py | Renames/retargets tests/docs from “toolbox” to “hosted tool” framing for sanitization. |
| python/packages/foundry/README.md | Drops toolbox-helper pattern docs; documents MCP endpoint approach. |
| python/packages/foundry/agent_framework_foundry/_tools.py | Removes toolbox helper implementations; keeps/updates hosted tool sanitization and error messages. |
| python/packages/foundry/agent_framework_foundry/_chat_client.py | Removes get_toolbox and related imports; keeps hosted tool sanitization in tool preparation. |
| python/packages/foundry/agent_framework_foundry/_agent.py | Updates docstring wording to “hosted MCP tools” (sanitization rationale). |
| python/packages/foundry/agent_framework_foundry/init.py | Removes toolbox helper exports. |
| python/packages/core/agent_framework/foundry/init.pyi | Removes re-exported toolbox helper symbols from the public stub. |
| python/packages/core/agent_framework/foundry/init.py | Removes lazy-export mappings for toolbox helper symbols. |
| python/packages/core/agent_framework/_feature_stage.py | Removes ExperimentalFeature.TOOLBOXES. |
| python/packages/bedrock/tests/test_bedrock_client.py | Removes an extra blank line (formatting-only). |
…ack; add namespace regression tests - Add _resolve_toolbox_endpoint() helper in 04_foundry_toolbox/main.py and 06_files/main.py that prefers FOUNDRY_TOOLBOX_ENDPOINT but falls back to deriving the MCP URL from FOUNDRY_PROJECT_ENDPOINT + TOOLBOX_NAME — fixing the startup KeyError when agents are deployed via azd provision (which injects TOOLBOX_NAME, not FOUNDRY_TOOLBOX_ENDPOINT). - Update 04_foundry_toolbox/.env.example to use FOUNDRY_TOOLBOX_ENDPOINT (consistent with 06_files). - Add TOOLBOX_NAME env var to 06_files/agent.yaml so deployed agents have it available for the fallback derivation. - Update both READMEs to document the two ways to supply the toolbox endpoint. - Add test_foundry_namespace_no_longer_exposes_toolbox_helpers() with negative assertions for FoundryHostedToolType, get_toolbox_tool_name, get_toolbox_tool_type, and select_toolbox_tools — guarding against accidental re-introduction of removed symbols. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
moonbox3
left a comment
There was a problem hiding this comment.
Automated Code Review
Reviewers: 4 | Confidence: 86%
✓ Correctness
The PR removes toolbox helpers from the
foundrynamespace, adds a test to enforce that, and introduces a_resolve_toolbox_endpoint()helper in two samplemain.pyfiles that supports both a directFOUNDRY_TOOLBOX_ENDPOINTenv var and a constructed URL fromFOUNDRY_PROJECT_ENDPOINT+TOOLBOX_NAME. All changes are internally consistent and correct: the namespace__init__.pyalready lacks the four tested symbols, the URL-building logic correctly strips trailing slashes, and the README/agent.yaml docs match the new behavior.
✓ Security Reliability
The changes introduce a
_resolve_toolbox_endpoint()helper (duplicated in two samplemain.pyfiles) that resolves the MCP toolbox URL from env vars. The logic is straightforward and correct for its purpose. One reliability edge case exists: ifFOUNDRY_TOOLBOX_ENDPOINTis set to an empty string", the walrus-operator truthiness check silently skips it and falls through to theTOOLBOX_NAME-based fallback, which may then raise a confusingKeyErrorrather than surfacing the real misconfiguration. This is a developer-facing sample so the blast radius is small, and none of the changes introduce injection risks, secret leakage, resource leaks, or unhandled failure modes at real trust boundaries.
✓ Test Coverage
The PR adds a
_resolve_toolbox_endpoint()helper function (duplicated in two samplemain.pyfiles) with two distinct code paths: (1) returnFOUNDRY_TOOLBOX_ENDPOINTdirectly if set, and (2) fall back to constructing the URL fromFOUNDRY_PROJECT_ENDPOINT+TOOLBOX_NAME. Neither code path has any unit tests. The new namespace test for removed toolbox helpers is well-structured and meaningful. However, the untested_resolve_toolbox_endpointlogic is the substantive new behaviour introduced by this PR, and given that it runs at startup and raisesKeyErroron missing env vars, both the happy paths and the error path should be covered.
✓ Design Approach
I did not find a design-approach issue that warrants changes. The new toolbox endpoint resolution is consistent with the surrounding sample contracts: both hosted-agent samples declare
TOLBOX_NAMEin their manifest/runtime YAML, both manifests provision a toolbox namedagent-tools, and the code still preserves the explicitFOUNDRY_TOOLBOX_ENDPOINToverride for local/manual setups. The added namespace regression test also fits the intended API-surface cleanup.
Suggestions
- In
_resolve_toolbox_endpoint()(both04_foundry_toolbox/main.pyand06_files/main.py, around line 26), the checkif endpoint := os.environ.get("FOUNDRY_TOOLBOX_ENDPOINT"):silently ignores an explicitly set empty-string value and falls through to the fallback. Useif (endpoint := os.environ.get("FOUNDRY_TOOLBOX_ENDPOINT")) is not None:to distinguish between 'not set' (fall back) and 'set to empty string' (fail fast with a clear error). - The
_resolve_toolbox_endpoint()function (added to both04_foundry_toolbox/main.pyand06_files/main.py) has no unit tests. Consider adding tests for: (a)FOUNDRY_TOOLBOX_ENDPOINTset → returned as-is; (b) fallback URL construction with trailing-slash stripping; (c) neither variable group set →KeyError.
Automated review by automated agents
…ests Addresses review feedback for microsoft#5670: - In _resolve_toolbox_endpoint() (04_foundry_toolbox/main.py and 06_files/main.py) change the walrus-operator check from a truthy test to an explicit 'is not None' guard. An explicitly set empty string now raises ValueError immediately with a clear message instead of silently falling through to the fallback URL construction. - Add tests/samples/hosting/test_toolbox_endpoint.py covering both sample modules: (a) FOUNDRY_TOOLBOX_ENDPOINT set → returned as-is (b) FOUNDRY_TOOLBOX_ENDPOINT set to empty string → ValueError (c) fallback constructs URL from FOUNDRY_PROJECT_ENDPOINT + TOOLBOX_NAME, stripping trailing slashes (d) neither variable group set → KeyError Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
moonbox3
left a comment
There was a problem hiding this comment.
Automated Code Review
Reviewers: 4 | Confidence: 96%
✓ Correctness
The changes correctly fix a subtle bug where FOUNDRY_TOOLBOX_ENDPOINT set to an empty string would silently fall through to the fallback path instead of raising an error. The
is not Noneguard properly distinguishes 'not set' from 'set but empty', and the new ValueError is clear. The accompanying tests cover all meaningful branches (explicit value, empty string, fallback construction, trailing slash stripping, and missing variables). No correctness issues found.
✓ Security Reliability
The two sample files correctly tighten the empty-string edge case that the walrus-operator truthiness check previously silently swallowed, and the new test file provides thorough branch coverage. No security or reliability issues were found: the ValueError is raised before the empty string can be used as a URL, the sys.modules stub uses setdefault (non-destructive), and all error paths (KeyError, ValueError) are tested. Nothing in the diff introduces injection risks, resource leaks, or unhandled failure modes.
✓ Test Coverage
The new test file provides thorough coverage for the changed _resolve_toolbox_endpoint() behavior in both sample modules. All five test cases (explicit endpoint returned as-is, empty string raises ValueError, fallback URL construction, trailing-slash stripping, and missing vars raising KeyError) directly exercise the changed/new code paths. The URL format asserted in tests exactly matches the f-string on line 32 of both sample files. The stubing strategy for unavailable packages is appropriate and the parameterization across both modules ensures identical coverage. No gaps or issues found.
✗ Design Approach
The change improves one narrow failure mode, but it does not address the configuration problem these samples actually expose to users. Both samples still treat any non-empty
FOUNDRY_TOOLBOX_ENDPOINTas authoritative, while their own.env.examplefiles ship that variable with a non-empty placeholder ("...") and the READMEs documentTOLBOX_NAMEas the alternative configuration path. As a result, a copied sample.envcan still bypass the fallback entirely and produce an invalid endpoint, so the approach is a symptom-level fix rather than aligning the runtime behavior with the sample contract.
Flagged Issues
- The guard in
python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/main.py:26-29andpython/samples/04-hosting/foundry-hosted-agents/responses/06_files/main.py:26-29only treats an empty string as invalid, but the shipped templates still defineFOUNDRY_TOOLBOX_ENDPOINT="..."(responses/04_foundry_toolbox/.env.example:1-3,responses/06_files/.env.example:1-3) while the READMEs documentTOLBOX_NAMEas a supported alternative (responses/04_foundry_toolbox/README.md:31-50,responses/06_files/README.md:34-53). A user who copies the template without editing it will bypass the fallback and use a bogus endpoint.
Suggestions
- Align the configuration model across code,
.env.example, and README: either removeFOUNDRY_TOOLBOX_ENDPOINTfrom the sample templates whenTOLBOX_NAMEfallback is supported, or validate the explicit endpoint as a real URL (rejecting sentinel-like placeholders such as"...") before treating it as authoritative.
Automated review by automated agents
- Remove test_foundry_namespace_no_longer_exposes_toolbox_helpers (no longer warranted) - Remove docstring from _agent.py _prepare_tools_for_openai (extraneous) - Trim _chat_client.py _prepare_tools_for_openai docstring to one-liner (toolbox references no longer relevant) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…_prepare_tools_for_openai Address review comment on PR microsoft#5671: reviewer noted the description isn't warranted now that toolbox helpers have been removed. Matches the pattern in RawFoundryAgentChatClient which has no docstring. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Motivation and Context
FoundryChatClientexposed a hand-rolled surface (get_toolbox,fetch_toolbox,select_toolbox_tools,get_toolbox_tool_name,get_toolbox_tool_type,FoundryHostedToolType) that required calers to manually fetch, filter, and normalize aToolboxVersionObjectbefore passing tools to an agent. Since every Foundry toolbox already exposes an MCP endpoint, this bespoke layer is redundant and adds unnecessary complexity.Fixes #5670
Description
The fix removes the toolbox-specific helpers from
_tools.pyand_chat_client.py, drops their exports from bothagent_framework_foundryandagent_framework.foundry, and removes the now-obsoleteExperimentalFeature.TOOLBOXESflag. Samples that usedget_toolbox/select_toolbox_toolsare rewritten to pointMCPStreamableHTTPToolat the toolbox's MCP endpoint instead, with the existing MCP sample (foundry_chat_client_with_toolbox_mcp.py) renamed to become the canonical pattern. The_sanitize_foundry_response_toolhelper and its docstrings are updated to remove toolbox-specific framing, since sanitization remains relevant for any hosted MCP tool definition delivered through the Responses API.Contribution Checklist