From 64fc3f381f20183d28d137d98c5160ed8bc12a99 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:54:20 +0000 Subject: [PATCH] .NET: Response & foundry agent hosted MCP sample (#1568) * Adding sample demonstrating hosted MCP with Responses * Add mcp readme.md to slnx * Update FoundryAgent sample to use MCP types from abstraction and to show how to do approval * Fix param name after package update. * Fix environment variable name for consistency * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Chris <66376200+crickman@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- dotnet/agent-framework-dotnet.slnx | 2 + .../FoundryAgent_Hosted_MCP/Program.cs | 116 +++++++++++++----- .../ModelContextProtocol/README.md | 1 + .../ResponseAgent_Hosted_MCP/Program.cs | 95 ++++++++++++++ .../ResponseAgent_Hosted_MCP/README.md | 17 +++ .../ResponseAgent_Hosted_MCP.csproj | 20 +++ 6 files changed, 220 insertions(+), 31 deletions(-) create mode 100644 dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs create mode 100644 dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/README.md create mode 100644 dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/ResponseAgent_Hosted_MCP.csproj diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx index 896a31c9fc..1bfb5814f9 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -68,9 +68,11 @@ + + diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs b/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs index 32017af194..f824f09991 100644 --- a/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs @@ -1,52 +1,106 @@ // Copyright (c) Microsoft. All rights reserved. -// This sample shows how to create and use a simple AI agent with Azure Foundry Agents as the backend. +// This sample shows how to create and use a simple AI agent with Azure Foundry Agents as the backend, that uses a Hosted MCP Tool. +// In this case the Azure Foundry Agents service will invoke any MCP tools as required. MCP tools are not invoked by the Agent Framework. +// The sample first shows how to use MCP tools with auto approval, and then how to set up a tool that requires approval before it can be invoked and how to approve such a tool. using Azure.AI.Agents.Persistent; using Azure.Identity; using Microsoft.Agents.AI; +using Microsoft.Extensions.AI; var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -var model = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_MODEL_ID") ?? "gpt-4.1-mini"; +var model = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4.1-mini"; // Get a client to create/retrieve server side agents with. var persistentAgentsClient = new PersistentAgentsClient(endpoint, new AzureCliCredential()); +// **** MCP Tool with Auto Approval **** +// ************************************* + // Create an MCP tool definition that the agent can use. -var mcpTool = new MCPToolDefinition( - serverLabel: "microsoft_learn", - serverUrl: "https://learn.microsoft.com/api/mcp"); -mcpTool.AllowedTools.Add("microsoft_docs_search"); - -// Create a server side persistent agent with the Azure.AI.Agents.Persistent SDK. -var agentMetadata = await persistentAgentsClient.Administration.CreateAgentAsync( - model: model, - name: "MicrosoftLearnAgent", - instructions: "You answer questions by searching the Microsoft Learn content only.", - tools: [mcpTool]); - -// Retrieve an already created server side persistent agent as an AIAgent. -AIAgent agent = await persistentAgentsClient.GetAIAgentAsync(agentMetadata.Value.Id); - -// Create run options to configure the agent invocation. -var runOptions = new ChatClientAgentRunOptions() +// In this case we allow the tool to always be called without approval. +var mcpTool = new HostedMcpServerTool( + serverName: "microsoft_learn", + serverAddress: "https://learn.microsoft.com/api/mcp") { - ChatOptions = new() - { - RawRepresentationFactory = (_) => new ThreadAndRunOptions() - { - ToolResources = new MCPToolResource(serverLabel: "microsoft_learn") - { - RequireApproval = new MCPApproval("never"), - }.ToToolResources() - } - } + AllowedTools = ["microsoft_docs_search"], + ApprovalMode = HostedMcpServerToolApprovalMode.NeverRequire }; +// Create a server side persistent agent with the mcp tool, and expose it as an AIAgent. +AIAgent agent = await persistentAgentsClient.CreateAIAgentAsync( + model: model, + options: new() + { + Name = "MicrosoftLearnAgent", + Instructions = "You answer questions by searching the Microsoft Learn content only.", + ChatOptions = new() + { + Tools = [mcpTool] + }, + }); + // You can then invoke the agent like any other AIAgent. AgentThread thread = agent.GetNewThread(); -var response = await agent.RunAsync("Please summarize the Azure AI Agent documentation related to MCP Tool calling?", thread, runOptions); -Console.WriteLine(response); +Console.WriteLine(await agent.RunAsync("Please summarize the Azure AI Agent documentation related to MCP Tool calling?", thread)); // Cleanup for sample purposes. await persistentAgentsClient.Administration.DeleteAgentAsync(agent.Id); + +// **** MCP Tool with Approval Required **** +// ***************************************** + +// Create an MCP tool definition that the agent can use. +// In this case we require approval before the tool can be called. +var mcpToolWithApproval = new HostedMcpServerTool( + serverName: "microsoft_learn", + serverAddress: "https://learn.microsoft.com/api/mcp") +{ + AllowedTools = ["microsoft_docs_search"], + ApprovalMode = HostedMcpServerToolApprovalMode.AlwaysRequire +}; + +// Create an agent based on Azure OpenAI Responses as the backend. +AIAgent agentWithRequiredApproval = await persistentAgentsClient.CreateAIAgentAsync( + model: model, + options: new() + { + Name = "MicrosoftLearnAgentWithApproval", + Instructions = "You answer questions by searching the Microsoft Learn content only.", + ChatOptions = new() + { + Tools = [mcpToolWithApproval] + }, + }); + +// You can then invoke the agent like any other AIAgent. +var threadWithRequiredApproval = agentWithRequiredApproval.GetNewThread(); +var response = await agentWithRequiredApproval.RunAsync("Please summarize the Azure AI Agent documentation related to MCP Tool calling?", threadWithRequiredApproval); +var userInputRequests = response.UserInputRequests.ToList(); + +while (userInputRequests.Count > 0) +{ + // Ask the user to approve each MCP call request. + // For simplicity, we are assuming here that only MCP approval requests are being made. + var userInputResponses = userInputRequests + .OfType() + .Select(approvalRequest => + { + Console.WriteLine($""" + The agent would like to invoke the following MCP Tool, please reply Y to approve. + ServerName: {approvalRequest.ToolCall.ServerName} + Name: {approvalRequest.ToolCall.ToolName} + Arguments: {string.Join(", ", approvalRequest.ToolCall.Arguments?.Select(x => $"{x.Key}: {x.Value}") ?? [])} + """); + return new ChatMessage(ChatRole.User, [approvalRequest.CreateResponse(Console.ReadLine()?.Equals("Y", StringComparison.OrdinalIgnoreCase) ?? false)]); + }) + .ToList(); + + // Pass the user input responses back to the agent for further processing. + response = await agentWithRequiredApproval.RunAsync(userInputResponses, threadWithRequiredApproval); + + userInputRequests = response.UserInputRequests.ToList(); +} + +Console.WriteLine($"\nAgent: {response}"); diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/README.md b/dotnet/samples/GettingStarted/ModelContextProtocol/README.md index be84bff51f..874afa28b8 100644 --- a/dotnet/samples/GettingStarted/ModelContextProtocol/README.md +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/README.md @@ -21,6 +21,7 @@ Before you begin, ensure you have the following prerequisites: |---|---| |[Agent with MCP server tools](./Agent_MCP_Server/)|This sample demonstrates how to use MCP server tools with a simple agent| |[Agent with MCP server tools and authorization](./Agent_MCP_Server_Auth/)|This sample demonstrates how to use MCP Server tools from a protected MCP server with a simple agent| +|[Responses Agent with Hosted MCP tool](./ResponseAgent_Hosted_MCP/)|This sample demonstrates how to use the Hosted MCP tool with the Responses Service, where the service invokes any MCP tools directly| ## Running the samples from the console diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs b/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs new file mode 100644 index 0000000000..19793e64df --- /dev/null +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to create and use a simple AI agent with OpenAI Responses as the backend, that uses a Hosted MCP Tool. +// In this case the OpenAI responses service will invoke any MCP tools as required. MCP tools are not invoked by the Agent Framework. +// The sample first shows how to use MCP tools with auto approval, and then how to set up a tool that requires approval before it can be invoked and how to approve such a tool. + +using Azure.AI.OpenAI; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Extensions.AI; +using OpenAI; + +var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); +var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +// **** MCP Tool with Auto Approval **** +// ************************************* + +// Create an MCP tool definition that the agent can use. +// In this case we allow the tool to always be called without approval. +var mcpTool = new HostedMcpServerTool( + serverName: "microsoft_learn", + serverAddress: "https://learn.microsoft.com/api/mcp") +{ + AllowedTools = ["microsoft_docs_search"], + ApprovalMode = HostedMcpServerToolApprovalMode.NeverRequire +}; + +// Create an agent based on Azure OpenAI Responses as the backend. +AIAgent agent = new AzureOpenAIClient( + new Uri(endpoint), + new AzureCliCredential()) + .GetOpenAIResponseClient(deploymentName) + .CreateAIAgent( + instructions: "You answer questions by searching the Microsoft Learn content only.", + name: "MicrosoftLearnAgent", + tools: [mcpTool]); + +// You can then invoke the agent like any other AIAgent. +AgentThread thread = agent.GetNewThread(); +Console.WriteLine(await agent.RunAsync("Please summarize the Azure AI Agent documentation related to MCP Tool calling?", thread)); + +// **** MCP Tool with Approval Required **** +// ***************************************** + +// Create an MCP tool definition that the agent can use. +// In this case we require approval before the tool can be called. +var mcpToolWithApproval = new HostedMcpServerTool( + serverName: "microsoft_learn", + serverAddress: "https://learn.microsoft.com/api/mcp") +{ + AllowedTools = ["microsoft_docs_search"], + ApprovalMode = HostedMcpServerToolApprovalMode.AlwaysRequire +}; + +// Create an agent based on Azure OpenAI Responses as the backend. +AIAgent agentWithRequiredApproval = new AzureOpenAIClient( + new Uri(endpoint), + new AzureCliCredential()) + .GetOpenAIResponseClient(deploymentName) + .CreateAIAgent( + instructions: "You answer questions by searching the Microsoft Learn content only.", + name: "MicrosoftLearnAgentWithApproval", + tools: [mcpToolWithApproval]); + +// You can then invoke the agent like any other AIAgent. +var threadWithRequiredApproval = agentWithRequiredApproval.GetNewThread(); +var response = await agentWithRequiredApproval.RunAsync("Please summarize the Azure AI Agent documentation related to MCP Tool calling?", threadWithRequiredApproval); +var userInputRequests = response.UserInputRequests.ToList(); + +while (userInputRequests.Count > 0) +{ + // Ask the user to approve each MCP call request. + // For simplicity, we are assuming here that only MCP approval requests are being made. + var userInputResponses = userInputRequests + .OfType() + .Select(approvalRequest => + { + Console.WriteLine($""" + The agent would like to invoke the following MCP Tool, please reply Y to approve. + ServerName: {approvalRequest.ToolCall.ServerName} + Name: {approvalRequest.ToolCall.ToolName} + Arguments: {string.Join(", ", approvalRequest.ToolCall.Arguments?.Select(x => $"{x.Key}: {x.Value}") ?? [])} + """); + return new ChatMessage(ChatRole.User, [approvalRequest.CreateResponse(Console.ReadLine()?.Equals("Y", StringComparison.OrdinalIgnoreCase) ?? false)]); + }) + .ToList(); + + // Pass the user input responses back to the agent for further processing. + response = await agentWithRequiredApproval.RunAsync(userInputResponses, threadWithRequiredApproval); + + userInputRequests = response.UserInputRequests.ToList(); +} + +Console.WriteLine($"\nAgent: {response}"); diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/README.md b/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/README.md new file mode 100644 index 0000000000..f84bd8f1b4 --- /dev/null +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/README.md @@ -0,0 +1,17 @@ +# Prerequisites + +Before you begin, ensure you have the following prerequisites: + +- .NET 8.0 SDK or later +- Azure OpenAI service endpoint and deployment configured +- Azure CLI installed and authenticated (for Azure credential authentication) +- User has the `Cognitive Services OpenAI Contributor` role for the Azure OpenAI resource. + +**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure OpenAI resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively). + +Set the following environment variables: + +```powershell +$env:AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/" # Replace with your Azure OpenAI resource endpoint +$env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4.1-mini" # Optional, defaults to gpt-4.1-mini +``` diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/ResponseAgent_Hosted_MCP.csproj b/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/ResponseAgent_Hosted_MCP.csproj new file mode 100644 index 0000000000..0eacdab258 --- /dev/null +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/ResponseAgent_Hosted_MCP.csproj @@ -0,0 +1,20 @@ + + + + Exe + net9.0 + + enable + enable + + + + + + + + + + + +