From 0b8870c90c965879153302afb8a9d5931763237d Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Mon, 20 Oct 2025 09:56:53 +0100 Subject: [PATCH 1/6] Adding sample demonstrating hosted MCP with Responses --- dotnet/agent-framework-dotnet.slnx | 1 + .../ModelContextProtocol/README.md | 1 + .../ResponseAgent_Hosted_MCP/Program.cs | 95 +++++++++++++++++++ .../ResponseAgent_Hosted_MCP/README.md | 17 ++++ .../ResponseAgent_Hosted_MCP.csproj | 20 ++++ 5 files changed, 134 insertions(+) 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 9d3b86535c..90ca88c257 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -67,6 +67,7 @@ + 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..9a7165fdaa --- /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", + url: "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", + url: "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..74e41dbbc1 --- /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 Foundry 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 + + + + + + + + + + + + From b6902a79125c1f4bb6ed0c9236730964bc24f4bd Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Mon, 20 Oct 2025 09:57:23 +0100 Subject: [PATCH 2/6] Add mcp readme.md to slnx --- dotnet/agent-framework-dotnet.slnx | 1 + 1 file changed, 1 insertion(+) diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx index 90ca88c257..b576417472 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -64,6 +64,7 @@ + From 34ff19ac2219fdd2fb78c97176598c5088a477ff Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Mon, 20 Oct 2025 18:12:18 +0100 Subject: [PATCH 3/6] Update FoundryAgent sample to use MCP types from abstraction and to show how to do approval --- .../FoundryAgent_Hosted_MCP/Program.cs | 110 +++++++++++++----- 1 file changed, 82 insertions(+), 28 deletions(-) diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs b/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs index 32017af194..0a2f5acdfa 100644 --- a/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs @@ -1,10 +1,13 @@ // 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"; @@ -12,41 +15,92 @@ // 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"); +// In this case we allow the tool to always be called without approval. +var mcpTool = new HostedMcpServerTool( + serverName: "microsoft_learn", + url: "https://learn.microsoft.com/api/mcp") +{ + AllowedTools = ["microsoft_docs_search"], + ApprovalMode = HostedMcpServerToolApprovalMode.NeverRequire +}; -// Create a server side persistent agent with the Azure.AI.Agents.Persistent SDK. -var agentMetadata = await persistentAgentsClient.Administration.CreateAgentAsync( +// Create a server side persistent agent with the mcp tool, and expose it as an AIAgent. +AIAgent agent = await persistentAgentsClient.CreateAIAgentAsync( 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() -{ - ChatOptions = new() + options: new() { - RawRepresentationFactory = (_) => new ThreadAndRunOptions() + Name = "MicrosoftLearnAgent", + Instructions = "You answer questions by searching the Microsoft Learn content only.", + ChatOptions = new() { - ToolResources = new MCPToolResource(serverLabel: "microsoft_learn") - { - RequireApproval = new MCPApproval("never"), - }.ToToolResources() - } - } -}; + 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", + url: "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}"); From c8f0c324e6aeffae3af156b997e950cc83b1fddf Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Tue, 28 Oct 2025 12:35:24 +0000 Subject: [PATCH 4/6] Fix param name after package update. --- .../ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs | 4 ++-- .../ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs b/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs index 0a2f5acdfa..f4a6038387 100644 --- a/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs @@ -22,7 +22,7 @@ // In this case we allow the tool to always be called without approval. var mcpTool = new HostedMcpServerTool( serverName: "microsoft_learn", - url: "https://learn.microsoft.com/api/mcp") + serverAddress: "https://learn.microsoft.com/api/mcp") { AllowedTools = ["microsoft_docs_search"], ApprovalMode = HostedMcpServerToolApprovalMode.NeverRequire @@ -55,7 +55,7 @@ // In this case we require approval before the tool can be called. var mcpToolWithApproval = new HostedMcpServerTool( serverName: "microsoft_learn", - url: "https://learn.microsoft.com/api/mcp") + serverAddress: "https://learn.microsoft.com/api/mcp") { AllowedTools = ["microsoft_docs_search"], ApprovalMode = HostedMcpServerToolApprovalMode.AlwaysRequire diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs b/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs index 9a7165fdaa..19793e64df 100644 --- a/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs @@ -20,7 +20,7 @@ // In this case we allow the tool to always be called without approval. var mcpTool = new HostedMcpServerTool( serverName: "microsoft_learn", - url: "https://learn.microsoft.com/api/mcp") + serverAddress: "https://learn.microsoft.com/api/mcp") { AllowedTools = ["microsoft_docs_search"], ApprovalMode = HostedMcpServerToolApprovalMode.NeverRequire @@ -47,7 +47,7 @@ // In this case we require approval before the tool can be called. var mcpToolWithApproval = new HostedMcpServerTool( serverName: "microsoft_learn", - url: "https://learn.microsoft.com/api/mcp") + serverAddress: "https://learn.microsoft.com/api/mcp") { AllowedTools = ["microsoft_docs_search"], ApprovalMode = HostedMcpServerToolApprovalMode.AlwaysRequire From b79470aee9b098dd6cf40b5b102b810599ef3c66 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Mon, 3 Nov 2025 11:19:03 +0000 Subject: [PATCH 5/6] Fix environment variable name for consistency --- .../ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs b/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs index f4a6038387..f824f09991 100644 --- a/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs @@ -10,7 +10,7 @@ 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()); From e0bf1dbd90cc1cd53d0b34119b16106e2e8df7e7 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Mon, 3 Nov 2025 11:56:27 +0000 Subject: [PATCH 6/6] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../ModelContextProtocol/ResponseAgent_Hosted_MCP/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/README.md b/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/README.md index 74e41dbbc1..f84bd8f1b4 100644 --- a/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/README.md +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/README.md @@ -7,7 +7,7 @@ Before you begin, ensure you have the following prerequisites: - 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 Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively). +**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: