From 82bd9cf6b5fd47873172b1f490cdfda911e7cca9 Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Fri, 7 Nov 2025 12:46:04 +0000
Subject: [PATCH] Restructure Step1
---
dotnet/agent-framework-dotnet.slnx | 23 +-
.../Agent_Step01_Basics.csproj} | 2 +-
.../{ => Agent_Step01_Basics}/Program.cs | 0
.../{ => Agent_Step01_Basics}/README.md | 0
.../Agent_Step01_Running.csproj | 21 ++
.../Agent_Step01_Running/Program.cs | 26 ++
.../Agent_Step02_MultiturnConversation.csproj | 21 ++
.../Program.cs | 33 ++
.../Agent_Step03.1_UsingFunctionTools.csproj | 21 ++
.../Program.cs | 34 ++
...03.2_UsingFunctionTools_FromOpenAPI.csproj | 28 ++
.../OpenAPISpec.json | 354 ++++++++++++++++++
.../Program.cs | 33 ++
...p04_UsingFunctionToolsWithApprovals.csproj | 21 ++
.../Program.cs | 66 ++++
.../Agent_Step05_StructuredOutput.csproj | 21 ++
.../Agent_Step05_StructuredOutput/Program.cs | 74 ++++
...Agent_Step06_PersistedConversations.csproj | 21 ++
.../Program.cs | 41 ++
.../Agent_Step07_3rdPartyThreadStorage.csproj | 23 ++
.../Program.cs | 151 ++++++++
.../Agent_Step08_Observability.csproj | 24 ++
.../Agent_Step08_Observability/Program.cs | 44 +++
.../Agent_Step09_DependencyInjection.csproj | 22 ++
.../Program.cs | 86 +++++
.../Agent_Step10_AsMcpTool.csproj | 24 ++
.../Agent_Step10_AsMcpTool/Program.cs | 38 ++
.../Agent_Step10_AsMcpTool/README.md | 29 ++
.../Agent_Step11_UsingImages.csproj | 20 +
.../Agent_Step11_UsingImages/Program.cs | 29 ++
.../Agent_Step11_UsingImages/README.md | 52 +++
.../Agent_Step12_AsFunctionTool.csproj | 22 ++
.../Agent_Step12_AsFunctionTool/Program.cs | 38 ++
.../Agent_Step13_Memory.csproj | 21 ++
.../Agent_Step13_Memory/Program.cs | 158 ++++++++
.../Agent_Step14_Middleware.csproj | 22 ++
.../Agent_Step14_Middleware/Program.cs | 260 +++++++++++++
.../Agent_Step14_Middleware/README.md | 41 ++
.../Agent_Step15_Plugins.csproj | 24 ++
.../Agent_Step15_Plugins/Program.cs | 130 +++++++
.../Agent_Step16_ChatReduction.csproj | 21 ++
.../Agent_Step16_ChatReduction/Program.cs | 48 +++
42 files changed, 2164 insertions(+), 3 deletions(-)
rename dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/{Agent_With_AzureAIAgent.csproj => Agent_Step01_Basics/Agent_Step01_Basics.csproj} (78%)
rename dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/{ => Agent_Step01_Basics}/Program.cs (100%)
rename dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/{ => Agent_Step01_Basics}/README.md (100%)
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step01_Running/Agent_Step01_Running.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step01_Running/Program.cs
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step02_MultiturnConversation/Agent_Step02_MultiturnConversation.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step02_MultiturnConversation/Program.cs
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.1_UsingFunctionTools/Agent_Step03.1_UsingFunctionTools.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.1_UsingFunctionTools/Program.cs
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.2_UsingFunctionTools_FromOpenAPI/Agent_Step03.2_UsingFunctionTools_FromOpenAPI.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.2_UsingFunctionTools_FromOpenAPI/OpenAPISpec.json
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.2_UsingFunctionTools_FromOpenAPI/Program.cs
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step04_UsingFunctionToolsWithApprovals/Agent_Step04_UsingFunctionToolsWithApprovals.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step04_UsingFunctionToolsWithApprovals/Program.cs
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step05_StructuredOutput/Agent_Step05_StructuredOutput.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step05_StructuredOutput/Program.cs
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step06_PersistedConversations/Agent_Step06_PersistedConversations.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step06_PersistedConversations/Program.cs
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step07_3rdPartyThreadStorage/Agent_Step07_3rdPartyThreadStorage.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step07_3rdPartyThreadStorage/Program.cs
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step08_Observability/Agent_Step08_Observability.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step08_Observability/Program.cs
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step09_DependencyInjection/Agent_Step09_DependencyInjection.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step09_DependencyInjection/Program.cs
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step10_AsMcpTool/Agent_Step10_AsMcpTool.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step10_AsMcpTool/Program.cs
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step10_AsMcpTool/README.md
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step11_UsingImages/Agent_Step11_UsingImages.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step11_UsingImages/Program.cs
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step11_UsingImages/README.md
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step12_AsFunctionTool/Agent_Step12_AsFunctionTool.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step12_AsFunctionTool/Program.cs
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step13_Memory/Agent_Step13_Memory.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step13_Memory/Program.cs
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step14_Middleware/Agent_Step14_Middleware.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step14_Middleware/Program.cs
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step14_Middleware/README.md
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step15_Plugins/Agent_Step15_Plugins.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step15_Plugins/Program.cs
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step16_ChatReduction/Agent_Step16_ChatReduction.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step16_ChatReduction/Program.cs
diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index ff56549f26..cecefd4a08 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -32,7 +32,6 @@
-
@@ -44,6 +43,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -283,8 +302,8 @@
-
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_With_AzureAIAgent.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step01_Basics/Agent_Step01_Basics.csproj
similarity index 78%
rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_With_AzureAIAgent.csproj
rename to dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step01_Basics/Agent_Step01_Basics.csproj
index 61f6c90316..58f24a3220 100644
--- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_With_AzureAIAgent.csproj
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step01_Basics/Agent_Step01_Basics.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step01_Basics/Program.cs
similarity index 100%
rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs
rename to dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step01_Basics/Program.cs
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/README.md b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step01_Basics/README.md
similarity index 100%
rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/README.md
rename to dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step01_Basics/README.md
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step01_Running/Agent_Step01_Running.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step01_Running/Agent_Step01_Running.csproj
new file mode 100644
index 0000000000..8298cfe6e8
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step01_Running/Agent_Step01_Running.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step01_Running/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step01_Running/Program.cs
new file mode 100644
index 0000000000..c67756299c
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step01_Running/Program.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to create and use a simple AI agent with Azure OpenAI as the backend.
+
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Agents.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";
+
+AIAgent agent = new AzureOpenAIClient(
+ new Uri(endpoint),
+ new AzureCliCredential())
+ .GetChatClient(deploymentName)
+ .CreateAIAgent(instructions: "You are good at telling jokes.", name: "Joker");
+
+// Invoke the agent and output the text result.
+Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
+
+// Invoke the agent with streaming support.
+await foreach (var update in agent.RunStreamingAsync("Tell me a joke about a pirate."))
+{
+ Console.WriteLine(update);
+}
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step02_MultiturnConversation/Agent_Step02_MultiturnConversation.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step02_MultiturnConversation/Agent_Step02_MultiturnConversation.csproj
new file mode 100644
index 0000000000..8298cfe6e8
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step02_MultiturnConversation/Agent_Step02_MultiturnConversation.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step02_MultiturnConversation/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step02_MultiturnConversation/Program.cs
new file mode 100644
index 0000000000..626a3e98c4
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step02_MultiturnConversation/Program.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to create and use a simple AI agent with a multi-turn conversation.
+
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Agents.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";
+
+AIAgent agent = new AzureOpenAIClient(
+ new Uri(endpoint),
+ new AzureCliCredential())
+ .GetChatClient(deploymentName)
+ .CreateAIAgent(instructions: "You are good at telling jokes.", name: "Joker");
+
+// Invoke the agent with a multi-turn conversation, where the context is preserved in the thread object.
+AgentThread thread = agent.GetNewThread();
+Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", thread));
+Console.WriteLine(await agent.RunAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", thread));
+
+// Invoke the agent with a multi-turn conversation and streaming, where the context is preserved in the thread object.
+thread = agent.GetNewThread();
+await foreach (var update in agent.RunStreamingAsync("Tell me a joke about a pirate.", thread))
+{
+ Console.WriteLine(update);
+}
+await foreach (var update in agent.RunStreamingAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", thread))
+{
+ Console.WriteLine(update);
+}
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.1_UsingFunctionTools/Agent_Step03.1_UsingFunctionTools.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.1_UsingFunctionTools/Agent_Step03.1_UsingFunctionTools.csproj
new file mode 100644
index 0000000000..8298cfe6e8
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.1_UsingFunctionTools/Agent_Step03.1_UsingFunctionTools.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.1_UsingFunctionTools/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.1_UsingFunctionTools/Program.cs
new file mode 100644
index 0000000000..48a6378e1f
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.1_UsingFunctionTools/Program.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample demonstrates how to use a ChatClientAgent with function tools.
+// It shows both non-streaming and streaming agent interactions using menu-related tools.
+
+using System.ComponentModel;
+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";
+
+[Description("Get the weather for a given location.")]
+static string GetWeather([Description("The location to get the weather for.")] string location)
+ => $"The weather in {location} is cloudy with a high of 15°C.";
+
+// Create the chat client and agent, and provide the function tool to the agent.
+AIAgent agent = new AzureOpenAIClient(
+ new Uri(endpoint),
+ new AzureCliCredential())
+ .GetChatClient(deploymentName)
+ .CreateAIAgent(instructions: "You are a helpful assistant", tools: [AIFunctionFactory.Create(GetWeather)]);
+
+// Non-streaming agent interaction with function tools.
+Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?"));
+
+// Streaming agent interaction with function tools.
+await foreach (var update in agent.RunStreamingAsync("What is the weather like in Amsterdam?"))
+{
+ Console.WriteLine(update);
+}
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.2_UsingFunctionTools_FromOpenAPI/Agent_Step03.2_UsingFunctionTools_FromOpenAPI.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.2_UsingFunctionTools_FromOpenAPI/Agent_Step03.2_UsingFunctionTools_FromOpenAPI.csproj
new file mode 100644
index 0000000000..e2edbb2f8d
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.2_UsingFunctionTools_FromOpenAPI/Agent_Step03.2_UsingFunctionTools_FromOpenAPI.csproj
@@ -0,0 +1,28 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.2_UsingFunctionTools_FromOpenAPI/OpenAPISpec.json b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.2_UsingFunctionTools_FromOpenAPI/OpenAPISpec.json
new file mode 100644
index 0000000000..84715914da
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.2_UsingFunctionTools_FromOpenAPI/OpenAPISpec.json
@@ -0,0 +1,354 @@
+{
+ "openapi": "3.0.1",
+ "info": {
+ "title": "Github Versions API",
+ "version": "1.0.0"
+ },
+ "servers": [
+ {
+ "url": "https://api.github.com"
+ }
+ ],
+ "components": {
+ "schemas": {
+ "basic-error": {
+ "title": "Basic Error",
+ "description": "Basic Error",
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "string"
+ },
+ "documentation_url": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ },
+ "status": {
+ "type": "string"
+ }
+ }
+ },
+ "label": {
+ "title": "Label",
+ "description": "Color-coded labels help you categorize and filter your issues (just like labels in Gmail).",
+ "type": "object",
+ "properties": {
+ "id": {
+ "description": "Unique identifier for the label.",
+ "type": "integer",
+ "format": "int64",
+ "example": 208045946
+ },
+ "node_id": {
+ "type": "string",
+ "example": "MDU6TGFiZWwyMDgwNDU5NDY="
+ },
+ "url": {
+ "description": "URL for the label",
+ "example": "https://api.github.com/repositories/42/labels/bug",
+ "type": "string",
+ "format": "uri"
+ },
+ "name": {
+ "description": "The name of the label.",
+ "example": "bug",
+ "type": "string"
+ },
+ "description": {
+ "description": "Optional description of the label, such as its purpose.",
+ "type": "string",
+ "example": "Something isn't working",
+ "nullable": true
+ },
+ "color": {
+ "description": "6-character hex code, without the leading #, identifying the color",
+ "example": "FFFFFF",
+ "type": "string"
+ },
+ "default": {
+ "description": "Whether this label comes by default in a new repository.",
+ "type": "boolean",
+ "example": true
+ }
+ },
+ "required": [
+ "id",
+ "node_id",
+ "url",
+ "name",
+ "description",
+ "color",
+ "default"
+ ]
+ },
+ "tag": {
+ "title": "Tag",
+ "description": "Tag",
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "example": "v0.1"
+ },
+ "commit": {
+ "type": "object",
+ "properties": {
+ "sha": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string",
+ "format": "uri"
+ }
+ },
+ "required": [
+ "sha",
+ "url"
+ ]
+ },
+ "zipball_url": {
+ "type": "string",
+ "format": "uri",
+ "example": "https://github.com/octocat/Hello-World/zipball/v0.1"
+ },
+ "tarball_url": {
+ "type": "string",
+ "format": "uri",
+ "example": "https://github.com/octocat/Hello-World/tarball/v0.1"
+ },
+ "node_id": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "node_id",
+ "commit",
+ "zipball_url",
+ "tarball_url"
+ ]
+ }
+ },
+ "examples": {
+ "label-items": {
+ "value": [
+ {
+ "id": 208045946,
+ "node_id": "MDU6TGFiZWwyMDgwNDU5NDY=",
+ "url": "https://api.github.com/repos/octocat/Hello-World/labels/bug",
+ "name": "bug",
+ "description": "Something isn't working",
+ "color": "f29513",
+ "default": true
+ },
+ {
+ "id": 208045947,
+ "node_id": "MDU6TGFiZWwyMDgwNDU5NDc=",
+ "url": "https://api.github.com/repos/octocat/Hello-World/labels/enhancement",
+ "name": "enhancement",
+ "description": "New feature or request",
+ "color": "a2eeef",
+ "default": false
+ }
+ ]
+ },
+ "tag-items": {
+ "value": [
+ {
+ "name": "v0.1",
+ "commit": {
+ "sha": "c5b97d5ae6c19d5c5df71a34c7fbeeda2479ccbc",
+ "url": "https://api.github.com/repos/octocat/Hello-World/commits/c5b97d5ae6c19d5c5df71a34c7fbeeda2479ccbc"
+ },
+ "zipball_url": "https://github.com/octocat/Hello-World/zipball/v0.1",
+ "tarball_url": "https://github.com/octocat/Hello-World/tarball/v0.1",
+ "node_id": "MDQ6VXNlcjE="
+ }
+ ]
+ }
+ },
+ "parameters": {
+ "owner": {
+ "name": "owner",
+ "description": "The account owner of the repository. The name is not case sensitive.",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "repo": {
+ "name": "repo",
+ "description": "The name of the repository without the `.git` extension. The name is not case sensitive.",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "per-page": {
+ "name": "per_page",
+ "description": "The number of results per page (max 100). For more information, see \"[Using pagination in the REST API](https://docs.github.com/rest/using-the-rest-api/using-pagination-in-the-rest-api).\"",
+ "in": "query",
+ "schema": {
+ "type": "integer",
+ "default": 30
+ }
+ },
+ "page": {
+ "name": "page",
+ "description": "The page number of the results to fetch. For more information, see \"[Using pagination in the REST API](https://docs.github.com/rest/using-the-rest-api/using-pagination-in-the-rest-api).\"",
+ "in": "query",
+ "schema": {
+ "type": "integer",
+ "default": 1
+ }
+ }
+ },
+ "responses": {
+ "not_found": {
+ "description": "Resource not found",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/basic-error"
+ }
+ }
+ }
+ }
+ },
+ "headers": {
+ "link": {
+ "example": "; rel=\"next\", ; rel=\"last\"",
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "paths": {
+ "/repos/{owner}/{repo}/tags": {
+ "get": {
+ "summary": "List repository tags",
+ "description": "",
+ "tags": [
+ "repos"
+ ],
+ "operationId": "repos/list-tags",
+ "externalDocs": {
+ "description": "API method documentation",
+ "url": "https://docs.github.com/rest/repos/repos#list-repository-tags"
+ },
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/owner"
+ },
+ {
+ "$ref": "#/components/parameters/repo"
+ },
+ {
+ "$ref": "#/components/parameters/per-page"
+ },
+ {
+ "$ref": "#/components/parameters/page"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/tag"
+ }
+ },
+ "examples": {
+ "default": {
+ "$ref": "#/components/examples/tag-items"
+ }
+ }
+ }
+ },
+ "headers": {
+ "Link": {
+ "$ref": "#/components/headers/link"
+ }
+ }
+ }
+ },
+ "x-github": {
+ "githubCloudOnly": false,
+ "enabledForGitHubApps": true,
+ "category": "repos",
+ "subcategory": "repos"
+ }
+ }
+ },
+ "/repos/{owner}/{repo}/labels": {
+ "get": {
+ "summary": "List labels for a repository",
+ "description": "Lists all labels for a repository.",
+ "tags": [
+ "issues"
+ ],
+ "operationId": "issues/list-labels-for-repo",
+ "externalDocs": {
+ "description": "API method documentation",
+ "url": "https://docs.github.com/rest/issues/labels#list-labels-for-a-repository"
+ },
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/owner"
+ },
+ {
+ "$ref": "#/components/parameters/repo"
+ },
+ {
+ "$ref": "#/components/parameters/per-page"
+ },
+ {
+ "$ref": "#/components/parameters/page"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/label"
+ }
+ },
+ "examples": {
+ "default": {
+ "$ref": "#/components/examples/label-items"
+ }
+ }
+ }
+ },
+ "headers": {
+ "Link": {
+ "$ref": "#/components/headers/link"
+ }
+ }
+ },
+ "404": {
+ "$ref": "#/components/responses/not_found"
+ }
+ },
+ "x-github": {
+ "githubCloudOnly": false,
+ "enabledForGitHubApps": true,
+ "category": "issues",
+ "subcategory": "labels"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.2_UsingFunctionTools_FromOpenAPI/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.2_UsingFunctionTools_FromOpenAPI/Program.cs
new file mode 100644
index 0000000000..e61c9f845a
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step03.2_UsingFunctionTools_FromOpenAPI/Program.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample demonstrates how to use a ChatClientAgent with function tools provided via an OpenAPI spec.
+// It uses functionality from Semantic Kernel to parse the OpenAPI spec and create function tools to use with the Agent Framework Agent.
+
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.AI;
+using Microsoft.SemanticKernel;
+using Microsoft.SemanticKernel.Plugins.OpenApi;
+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";
+
+// Load the OpenAPI Spec from a file.
+KernelPlugin plugin = await OpenApiKernelPluginFactory.CreateFromOpenApiAsync("github", "OpenAPISpec.json");
+
+// Convert the Semantic Kernel plugin to Agent Framework function tools.
+// This requires a dummy Kernel instance, since KernelFunctions cannot execute without one.
+Kernel kernel = new();
+List tools = plugin.Select(x => x.WithKernel(kernel)).Cast().ToList();
+
+// Create the chat client and agent, and provide the OpenAPI function tools to the agent.
+AIAgent agent = new AzureOpenAIClient(
+ new Uri(endpoint),
+ new AzureCliCredential())
+ .GetChatClient(deploymentName)
+ .CreateAIAgent(instructions: "You are a helpful assistant", tools: tools);
+
+// Run the agent with the OpenAPI function tools.
+Console.WriteLine(await agent.RunAsync("Please list the names, colors and descriptions of all the labels available in the microsoft/agent-framework repository on github."));
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step04_UsingFunctionToolsWithApprovals/Agent_Step04_UsingFunctionToolsWithApprovals.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step04_UsingFunctionToolsWithApprovals/Agent_Step04_UsingFunctionToolsWithApprovals.csproj
new file mode 100644
index 0000000000..8298cfe6e8
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step04_UsingFunctionToolsWithApprovals/Agent_Step04_UsingFunctionToolsWithApprovals.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step04_UsingFunctionToolsWithApprovals/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step04_UsingFunctionToolsWithApprovals/Program.cs
new file mode 100644
index 0000000000..41ea8a5c92
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step04_UsingFunctionToolsWithApprovals/Program.cs
@@ -0,0 +1,66 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample demonstrates how to use a ChatClientAgent with function tools that require a human in the loop for approvals.
+// It shows both non-streaming and streaming agent interactions using menu-related tools.
+// If the agent is hosted in a service, with a remote user, combine this sample with the Persisted Conversations sample to persist the chat history
+// while the agent is waiting for user input.
+
+using System.ComponentModel;
+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";
+
+// Create a sample function tool that the agent can use.
+[Description("Get the weather for a given location.")]
+static string GetWeather([Description("The location to get the weather for.")] string location)
+ => $"The weather in {location} is cloudy with a high of 15°C.";
+
+// Create the chat client and agent.
+// Note that we are wrapping the function tool with ApprovalRequiredAIFunction to require user approval before invoking it.
+AIAgent agent = new AzureOpenAIClient(
+ new Uri(endpoint),
+ new AzureCliCredential())
+ .GetChatClient(deploymentName)
+ .CreateAIAgent(instructions: "You are a helpful assistant", tools: [new ApprovalRequiredAIFunction(AIFunctionFactory.Create(GetWeather))]);
+
+// Call the agent and check if there are any user input requests to handle.
+AgentThread thread = agent.GetNewThread();
+var response = await agent.RunAsync("What is the weather like in Amsterdam?", thread);
+var userInputRequests = response.UserInputRequests.ToList();
+
+// For streaming use:
+// var updates = await agent.RunStreamingAsync("What is the weather like in Amsterdam?", thread).ToListAsync();
+// userInputRequests = updates.SelectMany(x => x.UserInputRequests).ToList();
+
+while (userInputRequests.Count > 0)
+{
+ // Ask the user to approve each function call request.
+ // For simplicity, we are assuming here that only function approval requests are being made.
+ var userInputResponses = userInputRequests
+ .OfType()
+ .Select(functionApprovalRequest =>
+ {
+ Console.WriteLine($"The agent would like to invoke the following function, please reply Y to approve: Name {functionApprovalRequest.FunctionCall.Name}");
+ return new ChatMessage(ChatRole.User, [functionApprovalRequest.CreateResponse(Console.ReadLine()?.Equals("Y", StringComparison.OrdinalIgnoreCase) ?? false)]);
+ })
+ .ToList();
+
+ // Pass the user input responses back to the agent for further processing.
+ response = await agent.RunAsync(userInputResponses, thread);
+
+ userInputRequests = response.UserInputRequests.ToList();
+
+ // For streaming use:
+ // updates = await agent.RunStreamingAsync(userInputResponses, thread).ToListAsync();
+ // userInputRequests = updates.SelectMany(x => x.UserInputRequests).ToList();
+}
+
+Console.WriteLine($"\nAgent: {response}");
+
+// For streaming use:
+// Console.WriteLine($"\nAgent: {updates.ToAgentRunResponse()}");
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step05_StructuredOutput/Agent_Step05_StructuredOutput.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step05_StructuredOutput/Agent_Step05_StructuredOutput.csproj
new file mode 100644
index 0000000000..8298cfe6e8
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step05_StructuredOutput/Agent_Step05_StructuredOutput.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step05_StructuredOutput/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step05_StructuredOutput/Program.cs
new file mode 100644
index 0000000000..b18d8e2d84
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step05_StructuredOutput/Program.cs
@@ -0,0 +1,74 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to configure ChatClientAgent to produce structured output.
+
+using System.ComponentModel;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using OpenAI;
+using OpenAI.Chat;
+using SampleApp;
+
+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 chat client to be used by chat client agents.
+ChatClient chatClient = new AzureOpenAIClient(
+ new Uri(endpoint),
+ new AzureCliCredential())
+ .GetChatClient(deploymentName);
+
+// Create the ChatClientAgent with the specified name and instructions.
+ChatClientAgent agent = chatClient.CreateAIAgent(new ChatClientAgentOptions(name: "HelpfulAssistant", instructions: "You are a helpful assistant."));
+
+// Set PersonInfo as the type parameter of RunAsync method to specify the expected structured output from the agent and invoke the agent with some unstructured input.
+AgentRunResponse response = await agent.RunAsync("Please provide information about John Smith, who is a 35-year-old software engineer.");
+
+// Access the structured output via the Result property of the agent response.
+Console.WriteLine("Assistant Output:");
+Console.WriteLine($"Name: {response.Result.Name}");
+Console.WriteLine($"Age: {response.Result.Age}");
+Console.WriteLine($"Occupation: {response.Result.Occupation}");
+
+// Create the ChatClientAgent with the specified name, instructions, and expected structured output the agent should produce.
+ChatClientAgent agentWithPersonInfo = chatClient.CreateAIAgent(new ChatClientAgentOptions(name: "HelpfulAssistant", instructions: "You are a helpful assistant.")
+{
+ ChatOptions = new()
+ {
+ ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema()
+ }
+});
+
+// Invoke the agent with some unstructured input while streaming, to extract the structured information from.
+var updates = agentWithPersonInfo.RunStreamingAsync("Please provide information about John Smith, who is a 35-year-old software engineer.");
+
+// Assemble all the parts of the streamed output, since we can only deserialize once we have the full json,
+// then deserialize the response into the PersonInfo class.
+PersonInfo personInfo = (await updates.ToAgentRunResponseAsync()).Deserialize(JsonSerializerOptions.Web);
+
+Console.WriteLine("Assistant Output:");
+Console.WriteLine($"Name: {personInfo.Name}");
+Console.WriteLine($"Age: {personInfo.Age}");
+Console.WriteLine($"Occupation: {personInfo.Occupation}");
+
+namespace SampleApp
+{
+ ///
+ /// Represents information about a person, including their name, age, and occupation, matched to the JSON schema used in the agent.
+ ///
+ [Description("Information about a person including their name, age, and occupation")]
+ public class PersonInfo
+ {
+ [JsonPropertyName("name")]
+ public string? Name { get; set; }
+
+ [JsonPropertyName("age")]
+ public int? Age { get; set; }
+
+ [JsonPropertyName("occupation")]
+ public string? Occupation { get; set; }
+ }
+}
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step06_PersistedConversations/Agent_Step06_PersistedConversations.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step06_PersistedConversations/Agent_Step06_PersistedConversations.csproj
new file mode 100644
index 0000000000..8298cfe6e8
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step06_PersistedConversations/Agent_Step06_PersistedConversations.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step06_PersistedConversations/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step06_PersistedConversations/Program.cs
new file mode 100644
index 0000000000..1ffe3c9993
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step06_PersistedConversations/Program.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to create and use a simple AI agent with a conversation that can be persisted to disk.
+
+using System.Text.Json;
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Agents.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";
+
+// Create the agent
+AIAgent agent = new AzureOpenAIClient(
+ new Uri(endpoint),
+ new AzureCliCredential())
+ .GetChatClient(deploymentName)
+ .CreateAIAgent(instructions: "You are good at telling jokes.", name: "Joker");
+
+// Start a new thread for the agent conversation.
+AgentThread thread = agent.GetNewThread();
+
+// Run the agent with a new thread.
+Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", thread));
+
+// Serialize the thread state to a JsonElement, so it can be stored for later use.
+JsonElement serializedThread = thread.Serialize();
+
+// Save the serialized thread to a temporary file (for demonstration purposes).
+string tempFilePath = Path.GetTempFileName();
+await File.WriteAllTextAsync(tempFilePath, JsonSerializer.Serialize(serializedThread));
+
+// Load the serialized thread from the temporary file (for demonstration purposes).
+JsonElement reloadedSerializedThread = JsonSerializer.Deserialize(await File.ReadAllTextAsync(tempFilePath));
+
+// Deserialize the thread state after loading from storage.
+AgentThread resumedThread = agent.DeserializeThread(reloadedSerializedThread);
+
+// Run the agent again with the resumed thread.
+Console.WriteLine(await agent.RunAsync("Now tell the same joke in the voice of a pirate, and add some emojis to the joke.", resumedThread));
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step07_3rdPartyThreadStorage/Agent_Step07_3rdPartyThreadStorage.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step07_3rdPartyThreadStorage/Agent_Step07_3rdPartyThreadStorage.csproj
new file mode 100644
index 0000000000..1caf270c49
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step07_3rdPartyThreadStorage/Agent_Step07_3rdPartyThreadStorage.csproj
@@ -0,0 +1,23 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step07_3rdPartyThreadStorage/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step07_3rdPartyThreadStorage/Program.cs
new file mode 100644
index 0000000000..8986734972
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step07_3rdPartyThreadStorage/Program.cs
@@ -0,0 +1,151 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+#pragma warning disable CA1869 // Cache and reuse 'JsonSerializerOptions' instances
+
+// This sample shows how to create and use a simple AI agent with a conversation that can be persisted to disk.
+
+using System.Text.Json;
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.VectorData;
+using Microsoft.SemanticKernel.Connectors.InMemory;
+using OpenAI;
+using SampleApp;
+
+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 a vector store to store the chat messages in.
+// Replace this with a vector store implementation of your choice if you want to persist the chat history to disk.
+VectorStore vectorStore = new InMemoryVectorStore();
+
+// Create the agent
+AIAgent agent = new AzureOpenAIClient(
+ new Uri(endpoint),
+ new AzureCliCredential())
+ .GetChatClient(deploymentName)
+ .CreateAIAgent(new ChatClientAgentOptions
+ {
+ Instructions = "You are good at telling jokes.",
+ Name = "Joker",
+ ChatMessageStoreFactory = ctx =>
+ {
+ // Create a new chat message store for this agent that stores the messages in a vector store.
+ // Each thread must get its own copy of the VectorChatMessageStore, since the store
+ // also contains the id that the thread is stored under.
+ return new VectorChatMessageStore(vectorStore, ctx.SerializedState, ctx.JsonSerializerOptions);
+ }
+ });
+
+// Start a new thread for the agent conversation.
+AgentThread thread = agent.GetNewThread();
+
+// Run the agent with the thread that stores conversation history in the vector store.
+Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", thread));
+
+// Serialize the thread state, so it can be stored for later use.
+// Since the chat history is stored in the vector store, the serialized thread
+// only contains the guid that the messages are stored under in the vector store.
+JsonElement serializedThread = thread.Serialize();
+
+Console.WriteLine("\n--- Serialized thread ---\n");
+Console.WriteLine(JsonSerializer.Serialize(serializedThread, new JsonSerializerOptions { WriteIndented = true }));
+
+// The serialized thread can now be saved to a database, file, or any other storage mechanism
+// and loaded again later.
+
+// Deserialize the thread state after loading from storage.
+AgentThread resumedThread = agent.DeserializeThread(serializedThread);
+
+// Run the agent with the thread that stores conversation history in the vector store a second time.
+Console.WriteLine(await agent.RunAsync("Now tell the same joke in the voice of a pirate, and add some emojis to the joke.", resumedThread));
+
+// We can access the VectorChatMessageStore via the thread's GetService method if we need to read the key under which threads are stored.
+var messageStore = resumedThread.GetService()!;
+Console.WriteLine($"\nThread is stored in vector store under key: {messageStore.ThreadDbKey}");
+
+namespace SampleApp
+{
+ ///
+ /// A sample implementation of that stores chat messages in a vector store.
+ ///
+ internal sealed class VectorChatMessageStore : ChatMessageStore
+ {
+ private readonly VectorStore _vectorStore;
+
+ public VectorChatMessageStore(VectorStore vectorStore, JsonElement serializedStoreState, JsonSerializerOptions? jsonSerializerOptions = null)
+ {
+ this._vectorStore = vectorStore ?? throw new ArgumentNullException(nameof(vectorStore));
+
+ if (serializedStoreState.ValueKind is JsonValueKind.String)
+ {
+ // Here we can deserialize the thread id so that we can access the same messages as before the suspension.
+ this.ThreadDbKey = serializedStoreState.Deserialize();
+ }
+ }
+
+ public string? ThreadDbKey { get; private set; }
+
+ public override async Task AddMessagesAsync(IEnumerable messages, CancellationToken cancellationToken = default)
+ {
+ this.ThreadDbKey ??= Guid.NewGuid().ToString("N");
+
+ var collection = this._vectorStore.GetCollection("ChatHistory");
+ await collection.EnsureCollectionExistsAsync(cancellationToken);
+
+ await collection.UpsertAsync(messages.Select(x => new ChatHistoryItem()
+ {
+ Key = this.ThreadDbKey + x.MessageId,
+ Timestamp = DateTimeOffset.UtcNow,
+ ThreadId = this.ThreadDbKey,
+ SerializedMessage = JsonSerializer.Serialize(x),
+ MessageText = x.Text
+ }), cancellationToken);
+ }
+
+ public override async Task> GetMessagesAsync(CancellationToken cancellationToken = default)
+ {
+ var collection = this._vectorStore.GetCollection("ChatHistory");
+ await collection.EnsureCollectionExistsAsync(cancellationToken);
+
+ var records = await collection
+ .GetAsync(
+ x => x.ThreadId == this.ThreadDbKey, 10,
+ new() { OrderBy = x => x.Descending(y => y.Timestamp) },
+ cancellationToken)
+ .ToListAsync(cancellationToken);
+
+ var messages = records.ConvertAll(x => JsonSerializer.Deserialize(x.SerializedMessage!)!)
+;
+ messages.Reverse();
+ return messages;
+ }
+
+ public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null) =>
+ // We have to serialize the thread id, so that on deserialization we can retrieve the messages using the same thread id.
+ JsonSerializer.SerializeToElement(this.ThreadDbKey);
+
+ ///
+ /// The data structure used to store chat history items in the vector store.
+ ///
+ private sealed class ChatHistoryItem
+ {
+ [VectorStoreKey]
+ public string? Key { get; set; }
+
+ [VectorStoreData]
+ public string? ThreadId { get; set; }
+
+ [VectorStoreData]
+ public DateTimeOffset? Timestamp { get; set; }
+
+ [VectorStoreData]
+ public string? SerializedMessage { get; set; }
+
+ [VectorStoreData]
+ public string? MessageText { get; set; }
+ }
+ }
+}
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step08_Observability/Agent_Step08_Observability.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step08_Observability/Agent_Step08_Observability.csproj
new file mode 100644
index 0000000000..980e282641
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step08_Observability/Agent_Step08_Observability.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step08_Observability/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step08_Observability/Program.cs
new file mode 100644
index 0000000000..c48242f5ca
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step08_Observability/Program.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to create and use a simple AI agent with Azure OpenAI as the backend that logs telemetry using OpenTelemetry.
+
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Azure.Monitor.OpenTelemetry.Exporter;
+using Microsoft.Agents.AI;
+using OpenAI;
+using OpenTelemetry;
+using OpenTelemetry.Trace;
+
+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";
+var applicationInsightsConnectionString = Environment.GetEnvironmentVariable("APPLICATIONINSIGHTS_CONNECTION_STRING");
+
+// Create TracerProvider with console exporter
+// This will output the telemetry data to the console.
+string sourceName = Guid.NewGuid().ToString("N");
+var tracerProviderBuilder = Sdk.CreateTracerProviderBuilder()
+ .AddSource(sourceName)
+ .AddConsoleExporter();
+if (!string.IsNullOrWhiteSpace(applicationInsightsConnectionString))
+{
+ tracerProviderBuilder.AddAzureMonitorTraceExporter(options => options.ConnectionString = applicationInsightsConnectionString);
+}
+using var tracerProvider = tracerProviderBuilder.Build();
+
+// Create the agent, and enable OpenTelemetry instrumentation.
+AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
+ .GetChatClient(deploymentName)
+ .CreateAIAgent(instructions: "You are good at telling jokes.", name: "Joker")
+ .AsBuilder()
+ .UseOpenTelemetry(sourceName: sourceName)
+ .Build();
+
+// Invoke the agent and output the text result.
+Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
+
+// Invoke the agent with streaming support.
+await foreach (var update in agent.RunStreamingAsync("Tell me a joke about a pirate."))
+{
+ Console.WriteLine(update);
+}
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step09_DependencyInjection/Agent_Step09_DependencyInjection.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step09_DependencyInjection/Agent_Step09_DependencyInjection.csproj
new file mode 100644
index 0000000000..b0890e1817
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step09_DependencyInjection/Agent_Step09_DependencyInjection.csproj
@@ -0,0 +1,22 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step09_DependencyInjection/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step09_DependencyInjection/Program.cs
new file mode 100644
index 0000000000..894c034eb0
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step09_DependencyInjection/Program.cs
@@ -0,0 +1,86 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+#pragma warning disable CA1812
+
+// This sample shows how to use dependency injection to register an AIAgent and use it from a hosted service with a user input chat loop.
+
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+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 a host builder that we will register services with and then run.
+HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
+
+// Add agent options to the service collection.
+builder.Services.AddSingleton(
+ new ChatClientAgentOptions(instructions: "You are good at telling jokes.", name: "Joker"));
+
+// Add a chat client to the service collection.
+builder.Services.AddKeyedChatClient("AzureOpenAI", (sp) => new AzureOpenAIClient(
+ new Uri(endpoint),
+ new AzureCliCredential())
+ .GetChatClient(deploymentName)
+ .AsIChatClient());
+
+// Add the AI agent to the service collection.
+builder.Services.AddSingleton((sp) => new ChatClientAgent(
+ chatClient: sp.GetRequiredKeyedService("AzureOpenAI"),
+ options: sp.GetRequiredService()));
+
+// Add a sample service that will use the agent to respond to user input.
+builder.Services.AddHostedService();
+
+// Build and run the host.
+using IHost host = builder.Build();
+await host.RunAsync().ConfigureAwait(false);
+
+///
+/// A sample service that uses an AI agent to respond to user input.
+///
+internal sealed class SampleService(AIAgent agent, IHostApplicationLifetime appLifetime) : IHostedService
+{
+ private AgentThread? _thread;
+
+ public async Task StartAsync(CancellationToken cancellationToken)
+ {
+ // Create a thread that will be used for the entirety of the service lifetime so that the user can ask follow up questions.
+ this._thread = agent.GetNewThread();
+ _ = this.RunAsync(appLifetime.ApplicationStopping);
+ }
+
+ public async Task RunAsync(CancellationToken cancellationToken)
+ {
+ // Delay a little to allow the service to finish starting.
+ await Task.Delay(100, cancellationToken);
+
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ Console.WriteLine("\nAgent: Ask me to tell you a joke about a specific topic. To exit just press Ctrl+C or enter without any input.\n");
+ Console.Write("> ");
+ var input = Console.ReadLine();
+
+ // If the user enters no input, signal the application to shut down.
+ if (string.IsNullOrWhiteSpace(input))
+ {
+ appLifetime.StopApplication();
+ break;
+ }
+
+ // Stream the output to the console as it is generated.
+ await foreach (var update in agent.RunStreamingAsync(input, this._thread, cancellationToken: cancellationToken))
+ {
+ Console.Write(update);
+ }
+
+ Console.WriteLine();
+ }
+ }
+
+ public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
+}
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step10_AsMcpTool/Agent_Step10_AsMcpTool.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step10_AsMcpTool/Agent_Step10_AsMcpTool.csproj
new file mode 100644
index 0000000000..1fb367c044
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step10_AsMcpTool/Agent_Step10_AsMcpTool.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+ 3afc9b74-af74-4d8e-ae96-fa1c511d11ac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step10_AsMcpTool/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step10_AsMcpTool/Program.cs
new file mode 100644
index 0000000000..16bc3cd51e
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step10_AsMcpTool/Program.cs
@@ -0,0 +1,38 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to expose an AI agent as an MCP tool.
+
+using Azure.AI.Agents.Persistent;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using ModelContextProtocol.Server;
+
+var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set.");
+var deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
+var persistentAgentsClient = new PersistentAgentsClient(endpoint, new AzureCliCredential());
+
+// Create a server side persistent agent
+var agentMetadata = await persistentAgentsClient.Administration.CreateAgentAsync(
+ model: deploymentName,
+ instructions: "You are good at telling jokes, and you always start each joke with 'Aye aye, captain!'.",
+ name: "Joker",
+ description: "An agent that tells jokes.");
+
+// Retrieve the server side persistent agent as an AIAgent.
+AIAgent agent = await persistentAgentsClient.GetAIAgentAsync(agentMetadata.Value.Id);
+
+// Convert the agent to an AIFunction and then to an MCP tool.
+// The agent name and description will be used as the mcp tool name and description.
+McpServerTool tool = McpServerTool.Create(agent.AsAIFunction());
+
+// Register the MCP server with StdIO transport and expose the tool via the server.
+HostApplicationBuilder builder = Host.CreateEmptyApplicationBuilder(settings: null);
+builder.Services
+ .AddMcpServer()
+ .WithStdioServerTransport()
+ .WithTools([tool]);
+
+await builder.Build().RunAsync();
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step10_AsMcpTool/README.md b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step10_AsMcpTool/README.md
new file mode 100644
index 0000000000..c56c9a7a68
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step10_AsMcpTool/README.md
@@ -0,0 +1,29 @@
+This sample demonstrates how to expose an existing AI agent as an MCP tool.
+
+## Run the sample
+
+To run the sample, please use one of the following MCP clients: https://modelcontextprotocol.io/clients
+
+Alternatively, use the QuickstartClient sample from this repository: https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples/QuickstartClient
+
+## Run the sample using MCP Inspector
+
+To use the [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector), follow these steps:
+
+1. Open a terminal in the Agent_Step10_AsMcpTool project directory.
+1. Run the `npx @modelcontextprotocol/inspector dotnet run` command to start the MCP Inspector. Make sure you have [node.js](https://nodejs.org/en/download/) and npm installed.
+ ```bash
+ npx @modelcontextprotocol/inspector dotnet run
+ ```
+1. When the inspector is running, it will display a URL in the terminal, like this:
+ ```
+ MCP Inspector is up and running at http://127.0.0.1:6274
+ ```
+1. Open a web browser and navigate to the URL displayed in the terminal. If not opened automatically, this will open the MCP Inspector interface.
+1. In the MCP Inspector interface, add the following environment variables to allow your MCP server to access Azure AI Foundry Project to create and run the agent:
+ - AZURE_FOUNDRY_PROJECT_ENDPOINT = https://your-resource.openai.azure.com/ # Replace with your Azure AI Foundry Project endpoint
+ - AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME = gpt-4o-mini # Replace with your model deployment name
+1. Find and click the `Connect` button in the MCP Inspector interface to connect to the MCP server.
+1. As soon as the connection is established, open the `Tools` tab in the MCP Inspector interface and select the `Joker` tool from the list.
+1. Specify your prompt as a value for the `query` argument, for example: `Tell me a joke about a pirate` and click the `Run Tool` button to run the tool.
+1. The agent will process the request and return a response in accordance with the provided instructions that instruct it to always start each joke with 'Aye aye, captain!'.
\ No newline at end of file
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step11_UsingImages/Agent_Step11_UsingImages.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step11_UsingImages/Agent_Step11_UsingImages.csproj
new file mode 100644
index 0000000000..7e9e70c763
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step11_UsingImages/Agent_Step11_UsingImages.csproj
@@ -0,0 +1,20 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step11_UsingImages/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step11_UsingImages/Program.cs
new file mode 100644
index 0000000000..8e8c5701ad
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step11_UsingImages/Program.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to use Image Multi-Modality with an AI agent.
+
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Extensions.AI;
+using OpenAI;
+
+var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
+var deploymentName = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o";
+
+var agent = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
+ .GetChatClient(deploymentName)
+ .CreateAIAgent(
+ name: "VisionAgent",
+ instructions: "You are a helpful agent that can analyze images");
+
+ChatMessage message = new(ChatRole.User, [
+ new TextContent("What do you see in this image?"),
+ new UriContent("https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg", "image/jpeg")
+]);
+
+var thread = agent.GetNewThread();
+
+await foreach (var update in agent.RunStreamingAsync(message, thread))
+{
+ Console.WriteLine(update);
+}
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step11_UsingImages/README.md b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step11_UsingImages/README.md
new file mode 100644
index 0000000000..49d1bff4a2
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step11_UsingImages/README.md
@@ -0,0 +1,52 @@
+# Using Images with AI Agents
+
+This sample demonstrates how to use image multi-modality with an AI agent. It shows how to create a vision-enabled agent that can analyze and describe images using Azure OpenAI.
+
+## What this sample demonstrates
+
+- Creating a persistent AI agent with vision capabilities
+- Sending both text and image content to an agent in a single message
+- Using `UriContent` to Uri referenced images
+- Processing multimodal input (text + image) with an AI agent
+
+## Key features
+
+- **Vision Agent**: Creates an agent specifically instructed to analyze images
+- **Multimodal Input**: Combines text questions with image uri in a single message
+- **Azure OpenAI Integration**: Uses AzureOpenAI LLM agents
+
+## Prerequisites
+
+Before running this sample, ensure you have:
+
+1. An Azure OpenAI project set up
+2. A compatible model deployment (e.g., gpt-4o)
+3. Azure CLI installed and authenticated
+
+## Environment Variables
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/" # Replace with your Azure OpenAI endpoint
+$env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o" # Replace with your model deployment name (optional, defaults to gpt-4o)
+```
+
+## Run the sample
+
+Navigate to the sample directory and run:
+
+```powershell
+cd Agent_Step11_UsingImages
+dotnet run
+```
+
+## Expected behavior
+
+The sample will:
+
+1. Create a vision-enabled agent named "VisionAgent"
+2. Send a message containing both text ("What do you see in this image?") and a Uri image of a green walk
+3. The agent will analyze the image and provide a description
+4. Clean up resources by deleting the thread and agent
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step12_AsFunctionTool/Agent_Step12_AsFunctionTool.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step12_AsFunctionTool/Agent_Step12_AsFunctionTool.csproj
new file mode 100644
index 0000000000..21c8d9e49e
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step12_AsFunctionTool/Agent_Step12_AsFunctionTool.csproj
@@ -0,0 +1,22 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+ 3afc9b74-af74-4d8e-ae96-fa1c511d11ac
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step12_AsFunctionTool/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step12_AsFunctionTool/Program.cs
new file mode 100644
index 0000000000..cce53ef3c0
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step12_AsFunctionTool/Program.cs
@@ -0,0 +1,38 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to create and use a Azure OpenAI AI agent as a function tool.
+
+using System.ComponentModel;
+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";
+
+[Description("Get the weather for a given location.")]
+static string GetWeather([Description("The location to get the weather for.")] string location)
+ => $"The weather in {location} is cloudy with a high of 15°C.";
+
+// Create the chat client and agent, and provide the function tool to the agent.
+AIAgent weatherAgent = new AzureOpenAIClient(
+ new Uri(endpoint),
+ new AzureCliCredential())
+ .GetChatClient(deploymentName)
+ .CreateAIAgent(
+ instructions: "You answer questions about the weather.",
+ name: "WeatherAgent",
+ description: "An agent that answers questions about the weather.",
+ tools: [AIFunctionFactory.Create(GetWeather)]);
+
+// Create the main agent, and provide the weather agent as a function tool.
+AIAgent agent = new AzureOpenAIClient(
+ new Uri(endpoint),
+ new AzureCliCredential())
+ .GetChatClient(deploymentName)
+ .CreateAIAgent(instructions: "You are a helpful assistant who responds in French.", tools: [weatherAgent.AsAIFunction()]);
+
+// Invoke the agent and output the text result.
+Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?"));
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step13_Memory/Agent_Step13_Memory.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step13_Memory/Agent_Step13_Memory.csproj
new file mode 100644
index 0000000000..8298cfe6e8
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step13_Memory/Agent_Step13_Memory.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step13_Memory/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step13_Memory/Program.cs
new file mode 100644
index 0000000000..ad59deb97f
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step13_Memory/Program.cs
@@ -0,0 +1,158 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to add a basic custom memory component to an agent.
+// The memory component subscribes to all messages added to the conversation and
+// extracts the user's name and age if provided.
+// The component adds a prompt to ask for this information if it is not already known
+// and provides it to the model before each invocation if known.
+
+using System.Text;
+using System.Text.Json;
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.AI;
+using OpenAI;
+using OpenAI.Chat;
+using SampleApp;
+
+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";
+
+ChatClient chatClient = new AzureOpenAIClient(
+ new Uri(endpoint),
+ new AzureCliCredential())
+ .GetChatClient(deploymentName);
+
+// Create the agent and provide a factory to add our custom memory component to
+// all threads created by the agent. Here each new memory component will have its own
+// user info object, so each thread will have its own memory.
+// In real world applications/services, where the user info would be persisted in a database,
+// and preferably shared between multiple threads used by the same user, ensure that the
+// factory reads the user id from the current context and scopes the memory component
+// and its storage to that user id.
+AIAgent agent = chatClient.CreateAIAgent(new ChatClientAgentOptions()
+{
+ Instructions = "You are a friendly assistant. Always address the user by their name.",
+ AIContextProviderFactory = ctx => new UserInfoMemory(chatClient.AsIChatClient(), ctx.SerializedState, ctx.JsonSerializerOptions)
+});
+
+// Create a new thread for the conversation.
+AgentThread thread = agent.GetNewThread();
+
+Console.WriteLine(">> Use thread with blank memory\n");
+
+// Invoke the agent and output the text result.
+Console.WriteLine(await agent.RunAsync("Hello, what is the square root of 9?", thread));
+Console.WriteLine(await agent.RunAsync("My name is Ruaidhrí", thread));
+Console.WriteLine(await agent.RunAsync("I am 20 years old", thread));
+
+// We can serialize the thread. The serialized state will include the state of the memory component.
+var threadElement = thread.Serialize();
+
+Console.WriteLine("\n>> Use deserialized thread with previously created memories\n");
+
+// Later we can deserialize the thread and continue the conversation with the previous memory component state.
+var deserializedThread = agent.DeserializeThread(threadElement);
+Console.WriteLine(await agent.RunAsync("What is my name and age?", deserializedThread));
+
+Console.WriteLine("\n>> Read memories from memory component\n");
+
+// It's possible to access the memory component via the thread's GetService method.
+var userInfo = deserializedThread.GetService()?.UserInfo;
+
+// Output the user info that was captured by the memory component.
+Console.WriteLine($"MEMORY - User Name: {userInfo?.UserName}");
+Console.WriteLine($"MEMORY - User Age: {userInfo?.UserAge}");
+
+Console.WriteLine("\n>> Use new thread with previously created memories\n");
+
+// It is also possible to set the memories in a memory component on an individual thread.
+// This is useful if we want to start a new thread, but have it share the same memories as a previous thread.
+var newThread = agent.GetNewThread();
+if (userInfo is not null && newThread.GetService() is UserInfoMemory newThreadMemory)
+{
+ newThreadMemory.UserInfo = userInfo;
+}
+
+// Invoke the agent and output the text result.
+// This time the agent should remember the user's name and use it in the response.
+Console.WriteLine(await agent.RunAsync("What is my name and age?", newThread));
+
+namespace SampleApp
+{
+ ///
+ /// Sample memory component that can remember a user's name and age.
+ ///
+ internal sealed class UserInfoMemory : AIContextProvider
+ {
+ private readonly IChatClient _chatClient;
+
+ public UserInfoMemory(IChatClient chatClient, UserInfo? userInfo = null)
+ {
+ this._chatClient = chatClient;
+ this.UserInfo = userInfo ?? new UserInfo();
+ }
+
+ public UserInfoMemory(IChatClient chatClient, JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null)
+ {
+ this._chatClient = chatClient;
+
+ this.UserInfo = serializedState.ValueKind == JsonValueKind.Object ?
+ serializedState.Deserialize(jsonSerializerOptions)! :
+ new UserInfo();
+ }
+
+ public UserInfo UserInfo { get; set; }
+
+ public override async ValueTask InvokedAsync(InvokedContext context, CancellationToken cancellationToken = default)
+ {
+ // Try and extract the user name and age from the message if we don't have it already and it's a user message.
+ if ((this.UserInfo.UserName is null || this.UserInfo.UserAge is null) && context.RequestMessages.Any(x => x.Role == ChatRole.User))
+ {
+ var result = await this._chatClient.GetResponseAsync(
+ context.RequestMessages,
+ new ChatOptions()
+ {
+ Instructions = "Extract the user's name and age from the message if present. If not present return nulls."
+ },
+ cancellationToken: cancellationToken);
+
+ this.UserInfo.UserName ??= result.Result.UserName;
+ this.UserInfo.UserAge ??= result.Result.UserAge;
+ }
+ }
+
+ public override ValueTask InvokingAsync(InvokingContext context, CancellationToken cancellationToken = default)
+ {
+ StringBuilder instructions = new();
+
+ // If we don't already know the user's name and age, add instructions to ask for them, otherwise just provide what we have to the context.
+ instructions
+ .AppendLine(
+ this.UserInfo.UserName is null ?
+ "Ask the user for their name and politely decline to answer any questions until they provide it." :
+ $"The user's name is {this.UserInfo.UserName}.")
+ .AppendLine(
+ this.UserInfo.UserAge is null ?
+ "Ask the user for their age and politely decline to answer any questions until they provide it." :
+ $"The user's age is {this.UserInfo.UserAge}.");
+
+ return new ValueTask(new AIContext
+ {
+ Instructions = instructions.ToString()
+ });
+ }
+
+ public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null)
+ {
+ return JsonSerializer.SerializeToElement(this.UserInfo, jsonSerializerOptions);
+ }
+ }
+
+ internal sealed class UserInfo
+ {
+ public string? UserName { get; set; }
+ public int? UserAge { get; set; }
+ }
+}
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step14_Middleware/Agent_Step14_Middleware.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step14_Middleware/Agent_Step14_Middleware.csproj
new file mode 100644
index 0000000000..09beb78195
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step14_Middleware/Agent_Step14_Middleware.csproj
@@ -0,0 +1,22 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step14_Middleware/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step14_Middleware/Program.cs
new file mode 100644
index 0000000000..28a50cc7d7
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_Step14_Middleware/Program.cs
@@ -0,0 +1,260 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows multiple middleware layers working together with Azure OpenAI:
+// chat client (global/per-request), agent run (PII filtering and guardrails),
+// function invocation (logging and result overrides), and human-in-the-loop
+// approval workflows for sensitive function calls.
+
+using System.ComponentModel;
+using System.Text.RegularExpressions;
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.AI;
+
+// Get Azure AI Foundry configuration from environment variables
+var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
+var deploymentName = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o";
+
+// Get a client to create/retrieve server side agents with
+var azureOpenAIClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
+ .GetChatClient(deploymentName);
+
+[Description("Get the weather for a given location.")]
+static string GetWeather([Description("The location to get the weather for.")] string location)
+ => $"The weather in {location} is cloudy with a high of 15°C.";
+
+[Description("The current datetime offset.")]
+static string GetDateTime()
+ => DateTimeOffset.Now.ToString();
+
+// Adding middleware to the chat client level and building an agent on top of it
+var originalAgent = azureOpenAIClient.AsIChatClient()
+ .AsBuilder()
+ .Use(getResponseFunc: ChatClientMiddleware, getStreamingResponseFunc: null)
+ .BuildAIAgent(
+ instructions: "You are an AI assistant that helps people find information.",
+ tools: [AIFunctionFactory.Create(GetDateTime, name: nameof(GetDateTime))]);
+
+// Adding middleware to the agent level
+var middlewareEnabledAgent = originalAgent
+ .AsBuilder()
+ .Use(FunctionCallMiddleware)
+ .Use(FunctionCallOverrideWeather)
+ .Use(PIIMiddleware, null)
+ .Use(GuardrailMiddleware, null)
+ .Build();
+
+var thread = middlewareEnabledAgent.GetNewThread();
+
+Console.WriteLine("\n\n=== Example 1: Wording Guardrail ===");
+var guardRailedResponse = await middlewareEnabledAgent.RunAsync("Tell me something harmful.");
+Console.WriteLine($"Guard railed response: {guardRailedResponse}");
+
+Console.WriteLine("\n\n=== Example 2: PII detection ===");
+var piiResponse = await middlewareEnabledAgent.RunAsync("My name is John Doe, call me at 123-456-7890 or email me at john@something.com");
+Console.WriteLine($"Pii filtered response: {piiResponse}");
+
+Console.WriteLine("\n\n=== Example 3: Agent function middleware ===");
+
+// Agent function middleware support is limited to agents that wraps a upstream ChatClientAgent or derived from it.
+
+// Add Per-request tools
+var options = new ChatClientAgentRunOptions(new()
+{
+ Tools = [AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))]
+});
+
+var functionCallResponse = await middlewareEnabledAgent.RunAsync("What's the current time and the weather in Seattle?", thread, options);
+Console.WriteLine($"Function calling response: {functionCallResponse}");
+
+// Special per-request middleware agent.
+Console.WriteLine("\n\n=== Example 4: Per-request middleware with human in the loop function approval ===");
+
+var optionsWithApproval = new ChatClientAgentRunOptions(new()
+{
+ // Adding a function with approval required
+ Tools = [new ApprovalRequiredAIFunction(AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather)))],
+})
+{
+ ChatClientFactory = (chatClient) => chatClient
+ .AsBuilder()
+ .Use(PerRequestChatClientMiddleware, null) // Using the non-streaming for handling streaming as well
+ .Build()
+};
+
+// var response = middlewareAgent // Using per-request middleware pipeline in addition to existing agent-level middleware
+var response = await originalAgent // Using per-request middleware pipeline without existing agent-level middleware
+ .AsBuilder()
+ .Use(PerRequestFunctionCallingMiddleware)
+ .Use(ConsolePromptingApprovalMiddleware, null)
+ .Build()
+ .RunAsync("What's the current time and the weather in Seattle?", thread, optionsWithApproval);
+
+Console.WriteLine($"Per-request middleware response: {response}");
+
+// Function invocation middleware that logs before and after function calls.
+async ValueTask