diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx index 3a3c6bba9a..b86f44bc2f 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -262,6 +262,7 @@ + diff --git a/dotnet/samples/HostedAgents/AgentWithApproval/AgentWithApproval.csproj b/dotnet/samples/HostedAgents/AgentWithApproval/AgentWithApproval.csproj new file mode 100644 index 0000000000..fc119ca2a0 --- /dev/null +++ b/dotnet/samples/HostedAgents/AgentWithApproval/AgentWithApproval.csproj @@ -0,0 +1,68 @@ + + + + Exe + net10.0 + + enable + enable + + + false + + + + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/dotnet/samples/HostedAgents/AgentWithApproval/Dockerfile b/dotnet/samples/HostedAgents/AgentWithApproval/Dockerfile new file mode 100644 index 0000000000..12932ee80a --- /dev/null +++ b/dotnet/samples/HostedAgents/AgentWithApproval/Dockerfile @@ -0,0 +1,20 @@ +# Build the application +FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build +WORKDIR /src + +# Copy files from the current directory on the host to the working directory in the container +COPY . . + +RUN dotnet restore +RUN dotnet build -c Release --no-restore +RUN dotnet publish -c Release --no-build -o /app -f net10.0 + +# Run the application +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final +WORKDIR /app + +# Copy everything needed to run the app from the "build" stage. +COPY --from=build /app . + +EXPOSE 8088 +ENTRYPOINT ["dotnet", "AgentWithApproval.dll"] diff --git a/dotnet/samples/HostedAgents/AgentWithApproval/Program.cs b/dotnet/samples/HostedAgents/AgentWithApproval/Program.cs new file mode 100644 index 0000000000..6b8a4e1b4d --- /dev/null +++ b/dotnet/samples/HostedAgents/AgentWithApproval/Program.cs @@ -0,0 +1,40 @@ +// 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 demonstrates how to use MCP tools with human-in-the-loop approval by setting ApprovalMode to AlwaysRequire. +// When a tool call is requested, the caller must explicitly approve or deny it before the agent proceeds. + +using Azure.AI.AgentServer.AgentFramework.Extensions; +using Azure.AI.OpenAI; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Extensions.AI; + +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"; + +// Create an MCP tool that requires human approval before each invocation. +// Unlike the AgentWithHostedMCP sample (which uses NeverRequire), this sample gates +// every tool call behind explicit user approval — a human-in-the-loop pattern. +AITool mcpTool = new HostedMcpServerTool(serverName: "microsoft_learn", serverAddress: "https://learn.microsoft.com/api/mcp") +{ + AllowedTools = ["microsoft_docs_search"], + ApprovalMode = HostedMcpServerToolApprovalMode.AlwaysRequire +}; + +// Create an agent with the MCP tool using Azure OpenAI Responses. +// 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. +AIAgent agent = new AzureOpenAIClient( + new Uri(endpoint), + new DefaultAzureCredential()) + .GetResponsesClient(deploymentName) + .AsIChatClient() + .CreateAIAgent( + instructions: "You answer questions by searching the Microsoft Learn content only.", + name: "MicrosoftLearnAgentWithApproval", + tools: [mcpTool]); + +await agent.RunAIAgentAsync(); diff --git a/dotnet/samples/HostedAgents/AgentWithApproval/README.md b/dotnet/samples/HostedAgents/AgentWithApproval/README.md new file mode 100644 index 0000000000..309f83c7bb --- /dev/null +++ b/dotnet/samples/HostedAgents/AgentWithApproval/README.md @@ -0,0 +1,57 @@ +# What this sample demonstrates + +This sample demonstrates how to use a Hosted Model Context Protocol (MCP) server with an AI agent +that requires **human-in-the-loop approval** before executing any tool call. + +The agent connects to the Microsoft Learn MCP server to search documentation, but unlike the +[AgentWithHostedMCP](../AgentWithHostedMCP) sample (which auto-approves tool calls), this sample +requires explicit approval for every MCP tool invocation. + +Key features: +- Configuring MCP tools with required approval (`AlwaysRequire` mode) +- Human-in-the-loop pattern for tool call gating +- Using Azure OpenAI Responses with approval-gated MCP tools + +## Prerequisites + +Before running this sample, ensure you have: + +1. An Azure OpenAI endpoint configured +2. A deployment of a chat model (e.g., gpt-4o-mini) +3. Azure CLI installed and authenticated + +**Note**: This sample uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure OpenAI resource. + +## Environment Variables + +Set the following environment variables: + +```powershell +# Replace with your Azure OpenAI endpoint +$env:AZURE_OPENAI_ENDPOINT="https://your-openai-resource.openai.azure.com/" + +# Optional, defaults to gpt-4o-mini +$env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +## How It Works + +The sample connects to the Microsoft Learn MCP server with approval-gated tool calls: + +1. The agent is configured with a `HostedMcpServerTool` pointing to `https://learn.microsoft.com/api/mcp` +2. Only the `microsoft_docs_search` tool is enabled from the available MCP tools +3. Approval mode is set to `AlwaysRequire`, meaning every tool call must be explicitly approved +4. When you ask a question, Azure OpenAI Responses requests the MCP tool call and the agent surfaces an approval request to the caller +5. The caller must approve or deny the tool call before the agent proceeds +6. Once approved, the MCP tool executes and the agent returns the answer + +In this configuration, the OpenAI Responses service manages tool invocation directly — the Agent Framework does not handle MCP tool calls. The approval gate ensures that no tool call executes without explicit human consent. + +## Comparison with AgentWithHostedMCP + +| Feature | AgentWithHostedMCP | AgentWithApproval | +|---------|-------------------|-------------------| +| MCP Server | Microsoft Learn | Microsoft Learn | +| Approval Mode | `NeverRequire` | `AlwaysRequire` | +| Tool Execution | Automatic | Requires human approval | +| Use Case | Trusted, automated search | Gated access with oversight | diff --git a/dotnet/samples/HostedAgents/AgentWithApproval/agent.yaml b/dotnet/samples/HostedAgents/AgentWithApproval/agent.yaml new file mode 100644 index 0000000000..305bb20158 --- /dev/null +++ b/dotnet/samples/HostedAgents/AgentWithApproval/agent.yaml @@ -0,0 +1,32 @@ +name: AgentWithApproval +displayName: "Microsoft Learn Response Agent with MCP Approval" +description: > + An AI agent that uses Azure OpenAI Responses with a Hosted Model Context Protocol (MCP) server, + requiring explicit human approval before each tool invocation. The agent answers questions by + searching Microsoft Learn documentation using MCP tools, but every tool call must be approved + by the caller first — demonstrating a human-in-the-loop pattern. +metadata: + authors: + - Microsoft Agent Framework Team + tags: + - Azure AI AgentServer + - Microsoft Agent Framework + - Model Context Protocol + - MCP + - Human-in-the-Loop + - Tool Call Approval +template: + kind: hosted + name: AgentWithApproval + protocols: + - protocol: responses + version: v1 + environment_variables: + - name: AZURE_OPENAI_ENDPOINT + value: ${AZURE_OPENAI_ENDPOINT} + - name: AZURE_OPENAI_DEPLOYMENT_NAME + value: gpt-4o-mini +resources: + - name: "gpt-4o-mini" + kind: model + id: gpt-4o-mini diff --git a/dotnet/samples/HostedAgents/AgentWithApproval/run-requests.http b/dotnet/samples/HostedAgents/AgentWithApproval/run-requests.http new file mode 100644 index 0000000000..3563daf2ce --- /dev/null +++ b/dotnet/samples/HostedAgents/AgentWithApproval/run-requests.http @@ -0,0 +1,30 @@ +@host = http://localhost:8088 +@endpoint = {{host}}/responses + +### Health Check +GET {{host}}/readiness + +### Ask a question - This will trigger an MCP tool call that requires approval +POST {{endpoint}} +Content-Type: application/json +{ + "input": "Please summarize the Azure AI Agent documentation related to MCP Tool calling?" +} + +### Explicit input - Ask about Agent Framework (also triggers approval) +POST {{endpoint}} +Content-Type: application/json +{ + "input": [ + { + "type": "message", + "role": "user", + "content": [ + { + "type": "input_text", + "text": "What is the Microsoft Agent Framework?" + } + ] + } + ] +}