From a4cfb73084ef1d50d7e51c2f32dbc5f3b4c83b01 Mon Sep 17 00:00:00 2001 From: Naveen Chatlapalli Date: Sat, 3 Oct 2026 00:15:38 -0500 Subject: [PATCH 1/2] samples: demonstrate Foundry toolbox tool search --- .../Agent_Step25_FoundryToolboxMcp/Program.cs | 27 ++++-------- .../Agent_Step25_FoundryToolboxMcp/README.md | 23 ++++++++-- .../02-agents/providers/foundry/README.md | 2 +- .../foundry_chat_client_with_toolbox.py | 42 +++++++++---------- 4 files changed, 47 insertions(+), 47 deletions(-) diff --git a/dotnet/samples/02-agents/AgentProviders/foundry/Agent_Step25_FoundryToolboxMcp/Program.cs b/dotnet/samples/02-agents/AgentProviders/foundry/Agent_Step25_FoundryToolboxMcp/Program.cs index 023fc31b988..3dc6267106f 100644 --- a/dotnet/samples/02-agents/AgentProviders/foundry/Agent_Step25_FoundryToolboxMcp/Program.cs +++ b/dotnet/samples/02-agents/AgentProviders/foundry/Agent_Step25_FoundryToolboxMcp/Program.cs @@ -5,7 +5,6 @@ // Point an `McpClient` at a Foundry Toolbox's MCP endpoint. The agent // discovers the toolbox's tools at runtime and invokes them locally. -using System.ClientModel; using System.Net.Http.Headers; using Azure.AI.Projects; using Azure.AI.Projects.Agents; @@ -21,7 +20,7 @@ // Name of the toolbox to create and connect to. const string ToolboxName = "research_toolbox"; -const string Query = "What tools do you have access to?"; +const string Query = "Find the REST API documentation for Azure Container Apps session pools."; string endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set."); @@ -29,7 +28,7 @@ TokenCredential credential = new DefaultAzureCredential(); -// Comment out if the toolbox already exists in your Foundry project. +// Connect to the new version so the tool-search configuration is unambiguous. var toolboxEndpoint = await CreateSampleToolboxAsync(ToolboxName, endpoint, credential); // Inject a fresh Azure AI bearer token on every MCP request. @@ -72,7 +71,7 @@ Console.WriteLine($"Assistant: {await agent.RunAsync(Query)}"); // --------------------------------------------------------------------------- -// Helper: create (or replace) a sample toolbox so the sample runs end-to-end +// Helper: create a sample toolbox version so the sample runs end-to-end // --------------------------------------------------------------------------- static async Task CreateSampleToolboxAsync(string name, string endpoint, TokenCredential credential) { @@ -83,18 +82,8 @@ static async Task CreateSampleToolboxAsync(string name, string endpoint, var adminClient = new AgentAdministrationClient(new Uri(endpoint), credential); var toolboxClient = adminClient.GetAgentToolboxes(); - // Delete existing toolbox if present (ignore 404). - try - { - await toolboxClient.DeleteAsync(name); - Console.WriteLine($"Deleted existing toolbox '{name}'"); - } - catch (ClientResultException ex) when (ex.Status == 404) - { - // Toolbox does not exist — nothing to delete. - } - - // Create a fresh version with a single MCP tool. + // Enable discovery through tool_search and invocation through call_tool. + // The underlying MCP tools are not all placed in the model's initial context. MCPToolboxTool mcpTool = new("api-specs") { ServerUri = new Uri("https://gitmcp.io/Azure/azure-rest-api-specs"), @@ -103,11 +92,11 @@ static async Task CreateSampleToolboxAsync(string name, string endpoint, ToolboxVersion created = (await toolboxClient.CreateVersionAsync( name: name, - tools: [mcpTool], - description: "Sample toolbox with an MCP tool — created by Agent_Step25 sample.")).Value; + tools: [mcpTool, new ToolSearchToolboxTool()], + description: "Sample toolbox with tool search — created by Agent_Step25 sample.")).Value; Console.WriteLine($"Created toolbox '{created.Name}' v{created.Version} ({created.Tools.Count} tool(s))"); - return $"{endpoint}/toolboxes/{created.Name}/mcp?api-version=v{created.Version}"; + return $"{endpoint.TrimEnd('/')}/toolboxes/{created.Name}/versions/{created.Version}/mcp?api-version=v1"; } // --------------------------------------------------------------------------- diff --git a/dotnet/samples/02-agents/AgentProviders/foundry/Agent_Step25_FoundryToolboxMcp/README.md b/dotnet/samples/02-agents/AgentProviders/foundry/Agent_Step25_FoundryToolboxMcp/README.md index 31206477780..7290b0c166f 100644 --- a/dotnet/samples/02-agents/AgentProviders/foundry/Agent_Step25_FoundryToolboxMcp/README.md +++ b/dotnet/samples/02-agents/AgentProviders/foundry/Agent_Step25_FoundryToolboxMcp/README.md @@ -7,7 +7,7 @@ This sample shows how to use a Foundry Toolbox by pointing an `McpClient` at the - Connecting to a Foundry toolbox's MCP endpoint via Streamable HTTP transport - Injecting a fresh Azure AI bearer token (`https://ai.azure.com/.default`) on every MCP request - Passing the discovered MCP tools to `AIProjectClient.AsAIAgent(...)` -- Optional helper to create (or replace) a sample toolbox in the project so the sample is runnable end-to-end +- Creating a toolbox version with tool search enabled and connecting to that version ## Prerequisites @@ -21,9 +21,24 @@ $env:FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.co $env:FOUNDRY_MODEL="gpt-5.4-mini" ``` -The sample creates a toolbox named `research_toolbox` in your Foundry project on -startup, then connects to its MCP endpoint at -`{FOUNDRY_PROJECT_ENDPOINT}/toolboxes/research_toolbox/mcp?api-version=v{version}`. +The sample creates a version of `research_toolbox` on startup, then connects to +`{FOUNDRY_PROJECT_ENDPOINT}/toolboxes/research_toolbox/versions/{version}/mcp?api-version=v1`. +Existing versions are not deleted. Review and remove sample versions in your +Foundry project when you no longer need them. + +## Tool search + +`ToolSearchToolboxTool` enables the toolbox's `tool_search` and `call_tool` +meta-tools. The agent searches for relevant capabilities before invoking a +discovered tool, rather than receiving all underlying tool definitions initially. +The sample prints the initial MCP tool names; expect those two meta-tools. +Remove `new ToolSearchToolboxTool()` from the creation helper to compare ordinary +toolbox discovery. + +This demonstrates [Foundry toolbox tool search](https://learn.microsoft.com/azure/foundry/agents/how-to/tools/tool-search), +not the separate request-scoped deferred-tool feature of the OpenAI Responses API. +The existing MCP integration handles discovery and invocation without adding an +Agent Framework tool-search API. ## Run the sample 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 6b986c10fd7b51ddf600d6c7ac77615d67a98ae4 Mon Sep 17 00:00:00 2001 From: Naveen Chatlapalli Date: Tue, 6 Oct 2026 06:49:04 -0500 Subject: [PATCH 2/2] refactor: move Python changes to a separate PR --- .../02-agents/providers/foundry/README.md | 2 +- .../foundry_chat_client_with_toolbox.py | 42 ++++++++++--------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/python/samples/02-agents/providers/foundry/README.md b/python/samples/02-agents/providers/foundry/README.md index 812e7532374..f4253d98861 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) | Discover and invoke Foundry toolbox tools on demand with `tool_search` and `call_tool` over MCP | +| [`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_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 09a920c7c45..7d15cfb49ca 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 a toolbox version with tool search in the Foundry project. + """Create (or replace) a toolbox version 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. Returns the - created version identifier without deleting existing versions. + 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. """ from azure.ai.projects import AIProjectClient - from azure.ai.projects.models import MCPToolboxTool, ToolboxTool, ToolSearchToolboxTool + from azure.ai.projects.models import MCPTool, Tool + from azure.core.exceptions import ResourceNotFoundError with ( AzureCliCredential() as credential, @@ -54,18 +54,23 @@ def create_sample_toolbox(name: str) -> str: if toolboxes is None: toolboxes = cast(Any, project_client.beta).toolboxes - tools: list[ToolboxTool] = [ - MCPToolboxTool( + try: + toolboxes.delete(name) + print(f"Toolbox `{name}` deleted") + except ResourceNotFoundError: + pass + + tools: list[Tool] = [ + MCPTool( 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 tools discovered through tool search.", + description="Toolbox version with MCP require_approval set to 'never'.", tools=tools, ) print(f"Created toolbox {created.name}@{created.version} ({len(created.tools)} tool(s))") @@ -87,14 +92,13 @@ def provide(_kwargs: dict[str, Any]) -> dict[str, str]: async def main() -> None: credential = DefaultAzureCredential() - # 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("/") + # Comment out if the toolbox already exists in your Foundry project. + create_sample_toolbox(TOOLBOX_NAME) toolbox_tool = MCPStreamableHTTPTool( name="foundry_toolbox", description="Tools exposed by the configured Foundry toolbox", - url=f"{project_endpoint}/toolboxes/{TOOLBOX_NAME}/versions/{version}/mcp?api-version=v1", + url=os.environ["FOUNDRY_TOOLBOX_ENDPOINT"], header_provider=make_toolbox_header_provider(credential), load_prompts=False, ) @@ -108,7 +112,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 = "Find the REST API documentation for Azure Container Apps session pools." + query = "What tools do you have access to?" print(f"User: {query}") result = await agent.run(query) print(f"Assistant: {result}")