Introduction
Microsoft Agent Framework (MAF) is an open-source development kit designed to streamline the creation and management of agents and workflows that automate tasks and processes. It builds on well-known OSS projects such as Semantic Kernel and Autogen, serving as their natural evolution while keep their strengths and ease of use.
MAF provides several building blocks for creating AI solutions:
- LLM Model clients, for chat completions and responses.
- Agent Thread, to handle context, state and memory providers.
- Middleware, to intercept messages.
- MCP Clients, to add up tools to be used by the agents.
Currently, MAF supports .NET and Python for creating agents and workflows. Additional languages may be supported in the future.
At high level MAF has this agentic architecture
Source: Microsoft Agent Framework - Overview
Creating Azure AI Foundry Project
In this article, we will create a TripAdvisor agent that helps users with travel recommendations and answer questions related to trips.
We will use Azure AI Foundry as our AI infrastructure. Follow these steps to create a new project:
- Log in to https://ai.azure.com
- Once in there, click Create a New Project button. Select Microsoft Foundry resource when prompted.
- Next set required parameters as you consider convenient.
- Once project is created in the overview section take note and keep these values:
- Azure OpenAI endpoint
- Api Key
- Now, go to section My Assets > Models + endpoints and click in Deploy Model button.
- In screen that shows up, search for gpt-4o or the model you prefer and press Confirm button
- Now select a name for your deployment and take note of it.
- In deployment type choose Global Standard
- Select the region where you have enough quota available. Take into account you should have enough capacity, in tokens per minute, to not exhaust them when interacting with your agent.
Once you already finish creating your project in Azure AI Foundry, and your deployment is completed we can move forward coding the agent.
Code
For coding our Agent we’re going to use C#. So, first of all we’re going to create a new console project with command
dotnet new console -n TripAdvisorAgent -f net10.0
once project is created, we move into its folder.
cd TripAdvisorAgent/
then we need to install these packages.
dotnet add package Azure.Identity
dotnet add package Microsoft.Agent.Framework
dotnet add package Azure.AI.OpenAI --prerelease
dotnet add package Microsoft.Agents.AI.OpenAI --prerelease
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.UserSecrets
dotnet add package Spectre.Console
Next, set up the secrets we will use, instead of hardcoding them in the code.
dotnet user-secrets init
dotnet user-secrets set AZURE_OPENAI_ENDPOINT <your_azure_openai_endpoint>
dotnet user-secrets set AZURE_OPENAI_APIKEY <your_azure_openai_apikey>
dotnet user-secrets set AZURE_OPENAI_DEPLOYMENT <your_azure_openai_deployment>
with that set now we started adding code to our project.
The TripAdvisor App
Program.cs - Initial version
using System.ClientModel;
using Azure.AI.OpenAI;
using Microsoft.Extensions.Configuration;
using OpenAI.Chat;
using Spectre.Console;
// retrieves user secrets
var configuration = new ConfigurationBuilder ()
.AddUserSecrets<Program>()
.Build ();
var systemPrompt = @"
Your are a helpful trip advisor assistant, specialized in recommend activities, places to visit,
culinary tours and activities that tourist will enjoy.
If questions/requests are no related to any of previous subjects just respond 'I'm not able to respond to your request'.
";
// creation of an agent using deployed model in Azure AI Foundry
var agent = new AzureOpenAIClient (
endpoint: new Uri (configuration["AZURE_OPENAI_ENDPOINT"]),
new ApiKeyCredential (configuration["AZURE_OPENAI_APIKEY"])
)
.GetChatClient(configuration["AZURE_OPENAI_DEPLOYMENT"])
.CreateAIAgent(instructions: systemPrompt);
Console.Clear();
while (true)
{
var input = AnsiConsole.Ask<string>("[green]User:[/]");
if (string.IsNullOrEmpty (input))
continue;
if (string.Equals (input, "exit", StringComparison.InvariantCultureIgnoreCase))
break;
// pass the user request to the agent
var response = await agent.RunAsync(input);
Console.WriteLine ($"Agent: {response.Text}");
}
Although is not mentioned explicitly the agent runs inside a AgentThread that handles the state of a specific conversation. If not defined one, a new AgentThread is created.
Now, we can run it and interact with our agent as shown here
dotnet run
however, our agent forgets everything once it responds.
To fix that we need to provide memory to our agent, so it can track the conversation. To do that MAF has two types of scenarios supported.
- In-memory storage. Memory is managed by the AgentThread where agent runs in.
- In-service storage. Memory is hold in the service where the agent is built. AgentThread that runs the agent have to retrieve memory from that store.
In our example we are going to give memory to our agent by creating a temporary In-Memory store.
var messageHistory = new InMemoryChatMessageStore ();
var agentThread = agent.GetNewThread(messageHistory);
By explicitly creating an AgentThread we can pass a memory provider to it, InMemoryChatMessageStore in this case, to save the whole conversation, so the agent can be aware of its previous responses. But also, it gives us the possibility of managing messages history to do operations on it, for example, logging it as shown here.
Program.cs - with memory
using System.ClientModel;
using Azure.AI.OpenAI;
using Microsoft.Agents.AI;
using Microsoft.Extensions.Configuration;
using OpenAI.Chat;
using Spectre.Console;
// retrieves user secrets
var configuration = new ConfigurationBuilder ()
.AddUserSecrets<Program>()
.Build ();
var systemPrompt = @"
Your are a helpful trip advisor assistant, specialized in recommend activities, places to visit,
culinary tours and activities that tourist will enjoy.
If questions/requests are no related to any of previous subjects just respond 'I'm not able to respond to your request'.
";
// creation of an agent using deployed model in Azure AI Foundry
var agent = new AzureOpenAIClient (
endpoint: new Uri (configuration["AZURE_OPENAI_ENDPOINT"]),
new ApiKeyCredential (configuration["AZURE_OPENAI_APIKEY"])
)
.GetChatClient(configuration["AZURE_OPENAI_DEPLOYMENT"])
.CreateAIAgent(instructions: systemPrompt);
// create an InMemory store to hold all conversation messages
var messageHistory = new InMemoryChatMessageStore ();
var agentThread = agent.GetNewThread(messageHistory);
Console.Clear();
while (true)
{
var input = AnsiConsole.Ask<string>("[green]User:[/]");
if (string.IsNullOrEmpty (input))
continue;
if (string.Equals (input, "exit", StringComparison.InvariantCultureIgnoreCase))
break;
// pass the user request to the agent along with the AgentThread
// that will handle conversation state
var response = await agent.RunAsync(input, agentThread);
Console.WriteLine ($"Agent: {response.Text}");
}
// print every message in the history conversation (memory) to the console
AnsiConsole.Write(new Rule("[orange1]Conversation thread[/]") { Justification = Justify.Right });
foreach (var message in messageHistory)
{
AnsiConsole.Write(new Text (message.Role.Value + ": ", new Style(Color.Orange1)));
AnsiConsole.Write(new Text (message.Text + "\n\n", new Style(Color.Orange1)));
AnsiConsole.Write(new Rule("[orange1]Message[/]") { Justification = Justify.Right });
}
Now our agent can remember its previous responses and respond accordingly.
And, as an extra benefit, now the whole conversation can be retrieved, managed and audited.
One last thing our agent could benefit from is the ability of get currency conversion rates, therefore, if the user requires some information that involves prices, we can be sure it has the most updated rates. To do so, we are going to add tools from a public MCP server that expose that kind of operations.
MAF supports integration with model context protocol servers through the official MCP SDKs. And for add it to our agent we need to create a MCP client and then fetch all tools it exposes.
So, the first thing is to add the official MCP Client package.
dotnet add package ModelContextProtocol --prerelease
And create the MCP Client pointing to the endpoint that expose the tools.
var sharedHandler = new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(2),
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(1)
};
using var httpClient = new HttpClient (sharedHandler);
var transport = new HttpClientTransport (new ()
{
Endpoint = new Uri ("https://currency-mcp.wesbos.com/mcp"),
Name = "Currency Conversion",
}, httpClient);
var mcpClient = await McpClient.CreateAsync(transport);
var mcpTools = await mcpClient.ListToolsAsync().ConfigureAwait(false);
finally, update the system prompt to instruct the agent to use tools when needed.
var systemPrompt = @"
Your are a helpful trip advisor assistant, specialized in recommend activities, places to visit,
culinary tours and activities that tourist will enjoy.
If questions/requests are no related to any of previous subjects just respond 'I'm not able to respond to your request'.
Use tools provided if your information is outdated or need refinement.
";
With those last changes our Program.cs looks like.
using System.ClientModel;
using Azure.AI.OpenAI;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using ModelContextProtocol.Client;
using OpenAI.Chat;
using Spectre.Console;
// retrieves user secrets
var configuration = new ConfigurationBuilder ()
.AddUserSecrets<Program>()
.Build ();
// creates an HttpClient to be used to request MCP Server
var sharedHandler = new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(2),
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(1)
};
using var httpClient = new HttpClient (sharedHandler);
// create MCP client transport that will use HttpClient created
// when request to the endpoint
var transport = new HttpClientTransport (new ()
{
Endpoint = new Uri ("https://currency-mcp.wesbos.com/mcp"),
Name = "Currency Conversion",
}, httpClient);
// create mcp client and retrieve tools list from the server
var mcpClient = await McpClient.CreateAsync(transport);
var mcpTools = await mcpClient.ListToolsAsync().ConfigureAwait(false);
var systemPrompt = @"
Your are a helpful trip advisor assistant, specialized in recommend activities, places to visit,
culinary tours and activities that tourist will enjoy.
If questions/requests are no related to any of previous subjects just respond 'I'm not able to respond to your request'.
Use tools provided if your information is outdated or need refinement.
";
// creation of an agent using deployed model in Azure AI Foundry
// now including tools retrieved from mcp server
var agent = new AzureOpenAIClient (
endpoint: new Uri (configuration["AZURE_OPENAI_ENDPOINT"]),
new ApiKeyCredential (configuration["AZURE_OPENAI_APIKEY"])
)
.GetChatClient(configuration["AZURE_OPENAI_DEPLOYMENT"])
.CreateAIAgent(instructions: systemPrompt, tools: [..mcpTools.Cast<AITool>()]);
// create an InMemory store to hold all conversation messages
var messageHistory = new InMemoryChatMessageStore ();
var agentThread = agent.GetNewThread(messageHistory);
Console.Clear();
while (true)
{
var input = AnsiConsole.Ask<string>("[green]User:[/]");
if (string.IsNullOrEmpty (input))
continue;
if (string.Equals (input, "exit", StringComparison.InvariantCultureIgnoreCase))
break;
// pass the user request to the agent along with the agentthread
// that will handle conversation
var response = await agent.RunAsync(input, agentThread);
Console.WriteLine ($"Agent: {response.Text}");
}
// print every message in the history conversation (memory) to the console
AnsiConsole.Write(new Rule("[orange1]Conversation thread[/]") { Justification = Justify.Right });
foreach (var message in messageHistory)
{
AnsiConsole.Write(new Text (message.Role.Value + ": ", new Style(Color.Orange1)));
AnsiConsole.Write(new Text (message.Text + "\n\n", new Style(Color.Orange1)));
AnsiConsole.Write(new Rule("[orange1]Message[/]") { Justification = Justify.Right });
}
And we can now ask for prices with more confidence about conversion rates.
Next Steps
The purpose of this article is to serve as a sneak peek about MAF capabilities and its basic usage. However, the example presented here can be extended by adding some other elements like:
- RAG. Having a repository with foundational documents where to look first when the user makes a request will give the agent grounding to respond promptly and accurately.
- Improve logging by intercepting messages with a middleware.
- Add observability with OpenTelemetry.
- Publish our Agent to make it publicly available to everyone.
References
- https://learn.microsoft.com/en-us/agent-framework
- https://github.com/modelcontextprotocol/csharp-sdk/












Top comments (0)