diff --git a/dotnet/samples/GettingStarted/Steps/Step10_ChatClientAgent_UsingFunctionToolsWithApprovals.cs b/dotnet/samples/GettingStarted/Steps/Step10_ChatClientAgent_UsingFunctionToolsWithApprovals.cs
new file mode 100644
index 0000000000..7ae7a8d631
--- /dev/null
+++ b/dotnet/samples/GettingStarted/Steps/Step10_ChatClientAgent_UsingFunctionToolsWithApprovals.cs
@@ -0,0 +1,205 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.ComponentModel;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.AI.Agents;
+
+namespace Steps;
+
+///
+/// Demonstrates how to indicate that certain function calls require user approval before they can be executed and how to then approve or reject those function calls.
+///
+public sealed class Step10_ChatClientAgent_UsingFunctionToolsWithApprovals(ITestOutputHelper output) : AgentSample(output)
+{
+ [Theory]
+ [InlineData(ChatClientProviders.AzureOpenAI)]
+ [InlineData(ChatClientProviders.AzureAIAgentsPersistent)]
+ [InlineData(ChatClientProviders.OpenAIAssistant)]
+ [InlineData(ChatClientProviders.OpenAIChatCompletion)]
+ [InlineData(ChatClientProviders.OpenAIResponses)]
+ public async Task ApprovalsWithTools(ChatClientProviders provider)
+ {
+ // Creating a MenuTools instance to be used by the agent.
+ var menuTools = new MenuTools();
+
+ // Define the options for the chat client agent.
+ // We mark GetMenu and GetSpecial as requiring approval before they can be invoked, while GetItemPrice can be invoked without user approval.
+ // IMPORTANT: A limitation of the approvals flow when using ChatClientAgent is that if more than one function needs to be executed in one run,
+ // and any one of them requires approval, approval will be sought for all function calls produced during that run.
+ var agentOptions = new ChatClientAgentOptions(
+ name: "Host",
+ instructions: "Answer questions about the menu",
+ tools: [
+ new ApprovalRequiredAIFunction(AIFunctionFactory.Create(menuTools.GetMenu)),
+ new ApprovalRequiredAIFunction(AIFunctionFactory.Create(menuTools.GetSpecials)),
+ AIFunctionFactory.Create(menuTools.GetItemPrice)
+ ]);
+
+ // Create the server-side agent Id when applicable (depending on the provider).
+ agentOptions.Id = await base.AgentCreateAsync(provider, agentOptions);
+
+ // Get the chat client to use for the agent.
+ using var chatClient = base.GetChatClient(provider, agentOptions);
+
+ // Define the agent
+ var agent = new ChatClientAgent(chatClient, agentOptions);
+
+ // Create the chat history thread to capture the agent interaction.
+ var thread = agent.GetNewThread();
+
+ // Respond to user input, invoking functions where appropriate.
+ await RunAgentAsync("What is the special soup and its price?");
+ await RunAgentAsync("What is the special drink?");
+
+ async Task RunAgentAsync(string input)
+ {
+ this.WriteUserMessage(input);
+ var response = await agent.RunAsync(input, thread);
+
+ // Loop until all user input requests are handled.
+ var userInputRequests = response.UserInputRequests.ToList();
+ while (userInputRequests.Count > 0)
+ {
+ // Approve GetSpecials function calls, reject all others.
+ List nextIterationMessages = userInputRequests?.Select((request) => request switch
+ {
+ FunctionApprovalRequestContent functionApprovalRequest when functionApprovalRequest.FunctionCall.Name == "GetSpecials" =>
+ new ChatMessage(ChatRole.User, [functionApprovalRequest.CreateResponse(approved: true)]),
+
+ FunctionApprovalRequestContent functionApprovalRequest =>
+ new ChatMessage(ChatRole.User, [functionApprovalRequest.CreateResponse(approved: false)]),
+
+ _ => throw new NotSupportedException($"Unsupported user input request type: {request.GetType().Name}")
+ })?.ToList() ?? [];
+
+ // Write out what the decision was for each function approval request.
+ nextIterationMessages.ForEach(x => Console.WriteLine($"Approval for the {(x.Contents[0] as FunctionApprovalResponseContent)?.FunctionCall.Name} function call is set to {(x.Contents[0] as FunctionApprovalResponseContent)?.Approved}."));
+
+ // Pass the user input responses back to the agent for further processing.
+ response = await agent.RunAsync(nextIterationMessages, thread);
+
+ userInputRequests = response.UserInputRequests.ToList();
+ }
+
+ this.WriteResponseOutput(response);
+ }
+
+ // Clean up the server-side agent after use when applicable (depending on the provider).
+ await base.AgentCleanUpAsync(provider, agent, thread);
+ }
+
+ [Theory]
+ [InlineData(ChatClientProviders.AzureOpenAI)]
+ [InlineData(ChatClientProviders.AzureAIAgentsPersistent)]
+ [InlineData(ChatClientProviders.OpenAIAssistant)]
+ [InlineData(ChatClientProviders.OpenAIChatCompletion)]
+ [InlineData(ChatClientProviders.OpenAIResponses)]
+ public async Task ApprovalsWithToolsStreaming(ChatClientProviders provider)
+ {
+ // Creating a MenuTools instance to be used by the agent.
+ var menuTools = new MenuTools();
+
+ // Creating a MenuTools instance to be used by the agent.
+ // We mark GetMenu and GetSpecial as requiring approval before they can be invoked, while GetItemPrice can be invoked without user approval.
+ // IMPORTANT: A limitation of the approvals flow when using ChatClientAgent is that if more than one function needs to be executed in one run,
+ // and any one of them requires approval, approval will be sought for all function calls produced during that run.
+ var agentOptions = new ChatClientAgentOptions(
+ name: "Host",
+ instructions: "Answer questions about the menu",
+ tools: [
+ new ApprovalRequiredAIFunction(AIFunctionFactory.Create(menuTools.GetMenu)),
+ new ApprovalRequiredAIFunction(AIFunctionFactory.Create(menuTools.GetSpecials)),
+ AIFunctionFactory.Create(menuTools.GetItemPrice),
+ ]);
+
+ // Create the server-side agent Id when applicable (depending on the provider).
+ agentOptions.Id = await base.AgentCreateAsync(provider, agentOptions);
+
+ // Get the chat client to use for the agent.
+ using var chatClient = base.GetChatClient(provider, agentOptions);
+
+ // Define the agent
+ var agent = new ChatClientAgent(chatClient, agentOptions);
+
+ // Create the chat history thread to capture the agent interaction.
+ var thread = agent.GetNewThread();
+
+ // Respond to user input, invoking functions where appropriate.
+ await RunAgentAsync("What is the special soup and its price?");
+ await RunAgentAsync("What is the special drink?");
+
+ async Task RunAgentAsync(string input)
+ {
+ this.WriteUserMessage(input);
+ var updates = await agent.RunStreamingAsync(input, thread).ToListAsync();
+
+ // Loop until all user input requests are handled.
+ var userInputRequests = updates.SelectMany(x => x.UserInputRequests).ToList();
+ while (userInputRequests.Count > 0)
+ {
+ // Approve GetSpecials function calls, reject all others.
+ List nextIterationMessages = userInputRequests?.Select((request) => request switch
+ {
+ FunctionApprovalRequestContent functionApprovalRequest when functionApprovalRequest.FunctionCall.Name == "GetSpecials" =>
+ new ChatMessage(ChatRole.User, [functionApprovalRequest.CreateResponse(approved: true)]),
+
+ FunctionApprovalRequestContent functionApprovalRequest =>
+ new ChatMessage(ChatRole.User, [functionApprovalRequest.CreateResponse(approved: false)]),
+
+ _ => throw new NotSupportedException($"Unsupported request type: {request.GetType().Name}")
+ })?.ToList() ?? [];
+
+ // Write out what the decision was for each function approval request.
+ nextIterationMessages.ForEach(x => Console.WriteLine($"Approval for the {(x.Contents[0] as FunctionApprovalResponseContent)?.FunctionCall.Name} function call is set to {(x.Contents[0] as FunctionApprovalResponseContent)?.Approved}."));
+
+ // Pass the user input responses back to the agent for further processing.
+ updates = await agent.RunStreamingAsync(nextIterationMessages, thread).ToListAsync();
+
+ userInputRequests = updates.SelectMany(x => x.UserInputRequests).ToList();
+ }
+
+ this.WriteResponseOutput(updates.ToAgentRunResponse());
+ }
+
+ // Clean up the server-side agent after use when applicable (depending on the provider).
+ await base.AgentCleanUpAsync(provider, agent, thread);
+ }
+
+ private sealed class MenuTools
+ {
+ [Description("Get the full menu items.")]
+ public MenuItem[] GetMenu()
+ {
+ return s_menuItems;
+ }
+
+ [Description("Get the specials from the menu.")]
+ public IEnumerable