diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx index a9d58d4220..9a15a45e9e 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -174,6 +174,7 @@ + diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step26_FoundryToolboxMcpSkills/Agent_Step26_FoundryToolboxMcpSkills.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step26_FoundryToolboxMcpSkills/Agent_Step26_FoundryToolboxMcpSkills.csproj new file mode 100644 index 0000000000..5243b24af0 --- /dev/null +++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step26_FoundryToolboxMcpSkills/Agent_Step26_FoundryToolboxMcpSkills.csproj @@ -0,0 +1,22 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + + + + diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step26_FoundryToolboxMcpSkills/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step26_FoundryToolboxMcpSkills/Program.cs new file mode 100644 index 0000000000..6efb8ce40e --- /dev/null +++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step26_FoundryToolboxMcpSkills/Program.cs @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft. All rights reserved. + +// Foundry Toolbox MCP Skills. +// +// Uses AgentSkillsProviderBuilder to discover MCP-based skills from a Foundry +// Toolbox endpoint and inject them as AIContextProviders so the agent can +// discover and use them at runtime. + +using System.Net.Http.Headers; +using Azure.AI.Projects; +using Azure.Core; +using Azure.Identity; +using Microsoft.Agents.AI; +using ModelContextProtocol.Client; + +// --- Configuration --- +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") + ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4-mini"; +string toolboxMcpServerUrl = Environment.GetEnvironmentVariable("FOUNDRY_TOOLBOX_MCP_SERVER_URL") + ?? throw new InvalidOperationException("FOUNDRY_TOOLBOX_MCP_SERVER_URL is not set."); + +// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. +// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid +// latency issues, unintended credential probing, and potential security risks from fallback mechanisms. +TokenCredential credential = new DefaultAzureCredential(); + +using var httpClient = new HttpClient(new BearerTokenHandler(credential, "https://ai.azure.com/.default") +{ + InnerHandler = new HttpClientHandler(), +}); + +// --- Connect to the Foundry Toolbox MCP endpoint --- +await using McpClient mcpClient = await McpClient.CreateAsync( + new HttpClientTransport( + new HttpClientTransportOptions + { + Endpoint = new Uri(toolboxMcpServerUrl), + Name = "foundry_toolbox", + TransportMode = HttpTransportMode.StreamableHttp, + AdditionalHeaders = new Dictionary + { + ["Foundry-Features"] = "Toolboxes=V1Preview", + }, + }, + httpClient)); + +// --- Discover MCP-based skills --- +var skillsProvider = new AgentSkillsProviderBuilder() + .UseMcpSkills(mcpClient) + .Build(); + +// --- Create the agent --- +AIProjectClient aiProjectClient = new(new Uri(endpoint), credential); + +AIAgent agent = aiProjectClient.AsAIAgent( + options: new ChatClientAgentOptions + { + Name = "ToolboxMcpSkillsAgent", + ChatOptions = new() + { + ModelId = deploymentName, + Instructions = "You are a helpful assistant. Use available skills to answer the user.", + }, + AIContextProviders = [skillsProvider], + }); + +// --- Interactive prompt --- +Console.Write("User: "); +string? query = Console.ReadLine(); + +if (string.IsNullOrWhiteSpace(query)) +{ + Console.WriteLine("No input provided."); + return; +} + +Console.WriteLine($"Assistant: {await agent.RunAsync(query)}"); + +// --------------------------------------------------------------------------- +// DelegatingHandler: attaches a fresh Foundry bearer token to every request +// --------------------------------------------------------------------------- +internal sealed class BearerTokenHandler(TokenCredential credential, string scope) : DelegatingHandler +{ + private readonly TokenRequestContext _tokenContext = new([scope]); + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + AccessToken token = await credential.GetTokenAsync(this._tokenContext, cancellationToken).ConfigureAwait(false); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Token); + return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); + } +} diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step26_FoundryToolboxMcpSkills/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step26_FoundryToolboxMcpSkills/README.md new file mode 100644 index 0000000000..2e8efef8cc --- /dev/null +++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step26_FoundryToolboxMcpSkills/README.md @@ -0,0 +1,32 @@ +# Foundry Toolbox MCP Skills + +This sample uses +`AgentSkillsProviderBuilder` to discover MCP-based skills from a Foundry Toolbox endpoint +and inject them as `AIContextProviders` so the agent can discover and use them at runtime. + +## What this sample demonstrates + +- 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 +- Using `AgentSkillsProviderBuilder.UseMcpSkills(client)` to discover skills from the toolbox +- Injecting the discovered skills into `AIProjectClient.AsAIAgent(...)` via `AIContextProviders` + +## Prerequisites + +- A Microsoft Foundry project with a toolbox already configured +- The toolbox MCP endpoint must expose `skill://index.json` with `skill-md` entries (SEP-2640). If the resource is absent, the sample runs but the skills provider will be empty. +- Azure CLI installed and authenticated (`az login`) + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4-mini" +$env:FOUNDRY_TOOLBOX_MCP_SERVER_URL="https://your-foundry-service.services.ai.azure.com/api/projects/your-project/toolboxes/your-toolbox/mcp?api-version=v1" +``` + +## Run the sample + +```powershell +dotnet run +``` diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/README.md b/dotnet/samples/02-agents/AgentsWithFoundry/README.md index 3e203c1fc6..dda1e476f0 100644 --- a/dotnet/samples/02-agents/AgentsWithFoundry/README.md +++ b/dotnet/samples/02-agents/AgentsWithFoundry/README.md @@ -74,6 +74,7 @@ Some samples require extra tool-specific environment variables. See each sample | [Local MCP](./Agent_Step23_LocalMCP/) | Local MCP client with HTTP transport | | [Code interpreter file download](./Agent_Step24_CodeInterpreterFileDownload/) | Download container files generated by code interpreter | | [Foundry toolbox via MCP](./Agent_Step25_FoundryToolboxMcp/) | Use a Foundry Toolbox from a non-hosted agent via its MCP endpoint | +| [Foundry toolbox MCP skills](./Agent_Step26_FoundryToolboxMcpSkills/) | Use a Foundry Toolbox with MCP-based skills discovery (SEP-2640) via AIContextProviders | ## Running the samples