From a40f29a63fbb71babbd128a4cc8518fc3f36191d Mon Sep 17 00:00:00 2001 From: Naveen Chatlapalli Date: Tue, 6 Oct 2026 06:50:56 -0500 Subject: [PATCH 1/3] samples: enable Foundry toolbox tool search in Python --- .../02-agents/providers/foundry/README.md | 2 +- .../foundry_chat_client_with_toolbox.py | 42 +++++++++---------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/python/samples/02-agents/providers/foundry/README.md b/python/samples/02-agents/providers/foundry/README.md index f4253d98861..812e7532374 100644 --- a/python/samples/02-agents/providers/foundry/README.md +++ b/python/samples/02-agents/providers/foundry/README.md @@ -27,7 +27,7 @@ This folder contains Microsoft Foundry and Foundry Local samples for Agent Frame | [`foundry_chat_client_with_hosted_mcp.py`](foundry_chat_client_with_hosted_mcp.py) | Foundry Chat Client with hosted MCP | | [`foundry_chat_client_with_local_mcp.py`](foundry_chat_client_with_local_mcp.py) | Foundry Chat Client with local MCP | | [`foundry_chat_client_with_session.py`](foundry_chat_client_with_session.py) | Foundry Chat Client with session management | -| [`foundry_chat_client_with_toolbox.py`](foundry_chat_client_with_toolbox.py) | Foundry Chat Client connected to a toolbox via its MCP endpoint using `MCPStreamableHTTPTool` | +| [`foundry_chat_client_with_toolbox.py`](foundry_chat_client_with_toolbox.py) | Discover and invoke Foundry toolbox tools on demand with `tool_search` and `call_tool` over MCP | | [`foundry_chat_client_with_toolbox_skills.py`](foundry_chat_client_with_toolbox_skills.py) | Foundry Chat Client that discovers MCP-based skills from a Foundry Toolbox endpoint via `MCPSkillsSource` (uses an Azure AD bearer token and the toolbox preview header) | ## FoundryLocalClient Samples diff --git a/python/samples/02-agents/providers/foundry/foundry_chat_client_with_toolbox.py b/python/samples/02-agents/providers/foundry/foundry_chat_client_with_toolbox.py index 7d15cfb49ca..09a920c7c45 100644 --- a/python/samples/02-agents/providers/foundry/foundry_chat_client_with_toolbox.py +++ b/python/samples/02-agents/providers/foundry/foundry_chat_client_with_toolbox.py @@ -24,27 +24,27 @@ Prerequisites: - A Microsoft Foundry project with a toolbox configured - FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL environment variables set -- FOUNDRY_TOOLBOX_ENDPOINT: the toolbox's MCP endpoint URL, e.g. - ``https://.services.ai.azure.com/api/projects//toolsets//mcp?api-version=v1`` - Azure CLI authentication (``az login``) + +The helper publishes a version with tool search enabled and connects to that exact +version. The model initially sees tool_search and call_tool, then discovers the +underlying MCP tools as needed. Each run creates a version; existing versions are +not deleted. Remove ToolSearchToolboxTool() to compare the full initial tool list. """ -# Must match the ```` segment of FOUNDRY_TOOLBOX_ENDPOINT. TOOLBOX_NAME = "research_toolbox" def create_sample_toolbox(name: str) -> str: - """Create (or replace) a toolbox version in the Foundry project. + """Create a toolbox version with tool search in the Foundry project. Toolboxes are normally configured in the Foundry portal or a deployment script, not the application itself. This helper exists so the sample can - be run end-to-end without first setting a toolbox up by hand — delete any - existing toolbox under ``name``, then create a fresh version containing a - single MCP tool. Returns the created version identifier. + be run end-to-end without first setting a toolbox up by hand. Returns the + created version identifier without deleting existing versions. """ from azure.ai.projects import AIProjectClient - from azure.ai.projects.models import MCPTool, Tool - from azure.core.exceptions import ResourceNotFoundError + from azure.ai.projects.models import MCPToolboxTool, ToolboxTool, ToolSearchToolboxTool with ( AzureCliCredential() as credential, @@ -54,23 +54,18 @@ def create_sample_toolbox(name: str) -> str: if toolboxes is None: toolboxes = cast(Any, project_client.beta).toolboxes - try: - toolboxes.delete(name) - print(f"Toolbox `{name}` deleted") - except ResourceNotFoundError: - pass - - tools: list[Tool] = [ - MCPTool( + tools: list[ToolboxTool] = [ + MCPToolboxTool( server_label="api_specs", server_url="https://gitmcp.io/Azure/azure-rest-api-specs", require_approval="never", - ) + ), + ToolSearchToolboxTool(), ] created = toolboxes.create_version( name=name, - description="Toolbox version with MCP require_approval set to 'never'.", + description="Toolbox version with MCP tools discovered through tool search.", tools=tools, ) print(f"Created toolbox {created.name}@{created.version} ({len(created.tools)} tool(s))") @@ -92,13 +87,14 @@ def provide(_kwargs: dict[str, Any]) -> dict[str, str]: async def main() -> None: credential = DefaultAzureCredential() - # Comment out if the toolbox already exists in your Foundry project. - create_sample_toolbox(TOOLBOX_NAME) + # Connect to the new version so the tool-search configuration is unambiguous. + version = create_sample_toolbox(TOOLBOX_NAME) + project_endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"].rstrip("/") toolbox_tool = MCPStreamableHTTPTool( name="foundry_toolbox", description="Tools exposed by the configured Foundry toolbox", - url=os.environ["FOUNDRY_TOOLBOX_ENDPOINT"], + url=f"{project_endpoint}/toolboxes/{TOOLBOX_NAME}/versions/{version}/mcp?api-version=v1", header_provider=make_toolbox_header_provider(credential), load_prompts=False, ) @@ -112,7 +108,7 @@ async def main() -> None: instructions="You are a helpful assistant. Use the available toolbox tools to answer the user.", tools=toolbox_tool, ) as agent: - query = "What tools do you have access to?" + query = "Find the REST API documentation for Azure Container Apps session pools." print(f"User: {query}") result = await agent.run(query) print(f"Assistant: {result}") From 8025ba736394b20c8bea6f118ecdd01a9923b770 Mon Sep 17 00:00:00 2001 From: Naveen Chatlapalli Date: Tue, 20 Oct 2026 23:13:02 -0500 Subject: [PATCH 2/3] samples: explain SDK requirement for optional Toolbox search --- .../foundry/foundry_chat_client_with_toolbox.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/python/samples/02-agents/providers/foundry/foundry_chat_client_with_toolbox.py b/python/samples/02-agents/providers/foundry/foundry_chat_client_with_toolbox.py index 09a920c7c45..16bc82ad17a 100644 --- a/python/samples/02-agents/providers/foundry/foundry_chat_client_with_toolbox.py +++ b/python/samples/02-agents/providers/foundry/foundry_chat_client_with_toolbox.py @@ -25,6 +25,7 @@ - A Microsoft Foundry project with a toolbox configured - FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL environment variables set - Azure CLI authentication (``az login``) +- azure-ai-projects >= 2.4.0 for toolbox tool search The helper publishes a version with tool search enabled and connects to that exact version. The model initially sees tool_search and call_tool, then discovers the @@ -44,7 +45,15 @@ def create_sample_toolbox(name: str) -> str: created version identifier without deleting existing versions. """ from azure.ai.projects import AIProjectClient - from azure.ai.projects.models import MCPToolboxTool, ToolboxTool, ToolSearchToolboxTool + from azure.ai.projects.models import MCPToolboxTool, ToolboxTool + + try: + from azure.ai.projects.models import ToolSearchToolboxTool + except ImportError as exc: + raise ImportError( + "This sample requires azure-ai-projects>=2.4.0 for toolbox tool search. " + "Upgrade with: pip install --upgrade 'azure-ai-projects>=2.4.0'" + ) from exc with ( AzureCliCredential() as credential, From ac26635662e74db18982a0da5b2b208f10805c0e Mon Sep 17 00:00:00 2001 From: aifanatic Date: Tue, 27 Oct 2026 07:17:16 -0500 Subject: [PATCH 3/3] samples: guard every toolbox model import behind the SDK check MCPToolboxTool and ToolboxTool are missing from azure-ai-projects 2.2.0, which the foundry package still allows, so importing them outside the guard raised a raw ImportError instead of the upgrade message. Co-Authored-By: Claude Opus 5.5 --- .../foundry_chat_client_with_toolbox.py | 3 +- .../agents/test_foundry_toolbox_sample.py | 76 +++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 python/tests/samples/agents/test_foundry_toolbox_sample.py diff --git a/python/samples/02-agents/providers/foundry/foundry_chat_client_with_toolbox.py b/python/samples/02-agents/providers/foundry/foundry_chat_client_with_toolbox.py index 16bc82ad17a..fbad7b3c0ab 100644 --- a/python/samples/02-agents/providers/foundry/foundry_chat_client_with_toolbox.py +++ b/python/samples/02-agents/providers/foundry/foundry_chat_client_with_toolbox.py @@ -45,10 +45,9 @@ def create_sample_toolbox(name: str) -> str: created version identifier without deleting existing versions. """ from azure.ai.projects import AIProjectClient - from azure.ai.projects.models import MCPToolboxTool, ToolboxTool try: - from azure.ai.projects.models import ToolSearchToolboxTool + from azure.ai.projects.models import MCPToolboxTool, ToolboxTool, ToolSearchToolboxTool except ImportError as exc: raise ImportError( "This sample requires azure-ai-projects>=2.4.0 for toolbox tool search. " diff --git a/python/tests/samples/agents/test_foundry_toolbox_sample.py b/python/tests/samples/agents/test_foundry_toolbox_sample.py new file mode 100644 index 00000000000..c22cd7da4f7 --- /dev/null +++ b/python/tests/samples/agents/test_foundry_toolbox_sample.py @@ -0,0 +1,76 @@ +# Copyright (c) Microsoft. All rights reserved. + +"""Tests for the azure-ai-projects version guard in the Foundry toolbox sample.""" + +import importlib.util +import sys +from pathlib import Path +from types import ModuleType + +import pytest + +_SAMPLE_PATH = ( + Path(__file__).parents[3] + / "samples" + / "02-agents" + / "providers" + / "foundry" + / "foundry_chat_client_with_toolbox.py" +) + + +def _load_sample_module() -> ModuleType: + spec = importlib.util.spec_from_file_location("foundry_chat_client_with_toolbox", _SAMPLE_PATH) + assert spec is not None + assert spec.loader is not None + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +sample_module = _load_sample_module() + + +class _StopBeforeNetwork(Exception): + pass + + +def _install_models_surface(monkeypatch: pytest.MonkeyPatch, exported: tuple[str, ...]) -> None: + """Replace azure.ai.projects.models with a module exposing only the given toolbox names.""" + models = ModuleType("azure.ai.projects.models") + for name in exported: + setattr(models, name, type(name, (), {})) + monkeypatch.setitem(sys.modules, "azure.ai.projects.models", models) + + +@pytest.mark.parametrize( + "exported", + [ + pytest.param((), id="azure-ai-projects-2.2"), + pytest.param(("MCPToolboxTool", "ToolboxTool"), id="azure-ai-projects-2.3"), + ], +) +def test_create_sample_toolbox_reports_upgrade_for_older_sdk( + monkeypatch: pytest.MonkeyPatch, exported: tuple[str, ...] +) -> None: + _install_models_surface(monkeypatch, exported) + + def fail_if_called() -> None: + raise AssertionError("The version check must run before any credential is created.") + + monkeypatch.setattr(sample_module, "AzureCliCredential", fail_if_called) + + with pytest.raises(ImportError, match=r"azure-ai-projects>=2\.4\.0"): + sample_module.create_sample_toolbox("toolbox") + + +def test_create_sample_toolbox_accepts_tool_search_sdk(monkeypatch: pytest.MonkeyPatch) -> None: + _install_models_surface(monkeypatch, ("MCPToolboxTool", "ToolboxTool", "ToolSearchToolboxTool")) + + def stop_before_network() -> None: + raise _StopBeforeNetwork + + monkeypatch.setattr(sample_module, "AzureCliCredential", stop_before_network) + + with pytest.raises(_StopBeforeNetwork): + sample_module.create_sample_toolbox("toolbox")