Microsoft Agent Framework: First Agent, MCP, and Multi-Agent Workflows

Microsoft Agent Framework with Ollama and MCP

Microsoft Agent Framework: First Agent, MCP, and Multi-Agent Workflows

Part 4 in the “Local AI with Ollama and .NET” series: Part 1 – Ollama and .NET | Part 2 – Local RAG | Part 3 – AI Agents | Part 3.5 – MCP Server | Version française

In the previous posts, we covered a lot of ground. Ollama for running LLMs locally. An MCP weather server in C# to expose tools to any AI client. And in between, hand-rolled ReAct agents: parsing TOOL: name, a tool registry, a homemade loop.

It worked. But the client side was throwaway code. Every integration needed its own plumbing.

Microsoft Agent Framework solves this. It’s the official successor to Semantic Kernel and AutoGen, built by the same teams at Microsoft. It takes any IChatClient (Ollama, OpenAI, Azure, etc.), handles the tool-calling loop automatically, and offers ready-made multi-agent workflows.

In this post: three progressive examples. A hello world agent. The same agent connected to our MCP weather server. And a sequential workflow with two specialized agents.

The full code is available here: mongeon/code-examples · agent-framework-ollama-mcp.

What is Microsoft Agent Framework?

Agent Framework is the convergence of two Microsoft projects: Semantic Kernel (AI integration in .NET) and AutoGen (multi-agent research). The same teams merged the best ideas from both into a unified framework.

Two core concepts:

  • Agent: an LLM + instructions + optional tools. It handles the conversation loop, tool calling, and message history automatically.
  • Workflow: multiple agents collaborating through an orchestration pattern (sequential, concurrent, handoff, group chat).

The framework is in Release Candidate (RC4 at time of writing). The API is stable. Minor adjustments possible before GA, but nothing that will break your code.

Beginner note: Agent Framework does not replace Ollama. Ollama is the inference engine (it runs the LLM). Agent Framework is the layer above: it orchestrates conversations, tools, and workflows.

Installation

dotnet add package Microsoft.Agents.AI --prerelease
dotnet add package OllamaSharp

For multi-agent workflows (example 3), also add:

dotnet add package Microsoft.Agents.AI.Workflows --prerelease

For the MCP connection (examples 2 and 3):

dotnet add package ModelContextProtocol

Note: the --prerelease flag is needed as long as the framework is in RC. Pin the versions in your .csproj to avoid surprises on dotnet restore.

First Agent: Hello World with Ollama

using Microsoft.Agents.AI;
using OllamaSharp;

// OllamaApiClient implements IChatClient. Agent Framework accepts any IChatClient.
var chatClient = new OllamaApiClient(
    new Uri("http://localhost:11434"), "qwen3");

// Create the agent: a name, instructions, a client. That's it.
AIAgent agent = new ChatClientAgent(
    chatClient,
    name: "Assistant",
    instructions: "You are a concise assistant. Answer in two sentences or fewer.");

// RunAsync sends the prompt, handles the conversation loop, returns the response.
var response = await agent.RunAsync("What is the Model Context Protocol?");

Console.WriteLine(response);

That’s 10 useful lines. OllamaApiClient implements IChatClient (the Microsoft.Extensions.AI abstraction), so Agent Framework accepts it directly. No wrapper, no extra configuration.

ChatClientAgent is the generic agent type: it takes any IChatClient and adds history management, system instructions, and the tool-calling loop.

RunAsync does all the work: sends the message, gets the response, returns the final text. If the agent had tools, it would call them automatically before answering.

Connecting the MCP Weather Server

The MCP server from the previous post exposes a GetCurrentWeather tool. We don’t modify anything on the server side, we simply plug it in as a tool source for our agent.

using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Transport;
using OllamaSharp;

// 1. Start the MCP weather server as a child process via stdio.
var mcpClient = await McpClientFactory.CreateAsync(
    new McpClientOptions { ClientInfo = new() { Name = "WeatherAgent", Version = "1.0.0" } },
    new StdioClientTransport(new StdioClientTransportOptions
    {
        Command = "dotnet",
        Arguments = ["run", "--project", "../path/to/mcp-weather-server"]
    }));

// 2. Discover the tools exposed by the MCP server.
var mcpTools = await mcpClient.ListToolsAsync();

// 3. Build the Ollama chat client with function invocation support.
IChatClient chatClient = new ChatClientBuilder(
        new OllamaApiClient(new Uri("http://localhost:11434"), "llama3.2"))
    .UseFunctionInvocation()
    .Build();

// 4. Create the agent with the MCP tools.
AIAgent agent = new ChatClientAgent(
    chatClient,
    name: "WeatherAgent",
    instructions: "You help users check the weather. Use the available tools to get real data.",
    tools: [.. mcpTools]);

// 5. The agent calls GetCurrentWeather via MCP automatically.
var response = await agent.RunAsync("What is the weather in Montreal?");
Console.WriteLine(response);

await mcpClient.DisposeAsync();

Three key things here:

  1. McpClientFactory.CreateAsync starts the MCP server as a child process. The stdio transport works exactly like when Claude Desktop launches the server. Same protocol, same binary.

  2. UseFunctionInvocation() is the bridge between Ollama and MCP. Ollama doesn’t support MCP natively (it has its own tool-calling format). Agent Framework bridges the gap: it translates MCP tools into functions the LLM can call, and executes the calls automatically.

  3. tools: [.. mcpTools] passes the discovered tools to the agent. The LLM sees the tool descriptions and decides when to call them. No manual ReAct loop.

Multi-Agent Workflow

One agent is fine. Two specialized agents is better. Here’s a sequential workflow:

  • Agent 1 (WeatherFetcher): has access to MCP tools, fetches raw weather data
  • Agent 2 (FriendlyFormatter): no tools, rewrites the response in a friendly, conversational way
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.AI;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Transport;
using OllamaSharp;

// --- MCP setup (same as example 2) ---
var mcpClient = await McpClientFactory.CreateAsync(
    new McpClientOptions { ClientInfo = new() { Name = "WorkflowDemo", Version = "1.0.0" } },
    new StdioClientTransport(new StdioClientTransportOptions
    {
        Command = "dotnet",
        Arguments = ["run", "--project", "../path/to/mcp-weather-server"]
    }));
var mcpTools = await mcpClient.ListToolsAsync();

var ollamaEndpoint = new Uri("http://localhost:11434");
const string model = "qwen3";

// --- Agent 1: Weather Fetcher ---
IChatClient fetcherClient = new ChatClientBuilder(
        new OllamaApiClient(ollamaEndpoint, model))
    .UseFunctionInvocation()
    .Build();

AIAgent weatherFetcher = new ChatClientAgent(
    fetcherClient,
    name: "WeatherFetcher",
    instructions: """
        You are a weather data agent. When asked about weather,
        use your tools to get the current conditions.
        Return the raw weather data as-is, without commentary.
        """,
    tools: [.. mcpTools]);

// --- Agent 2: Friendly Formatter ---
IChatClient formatterClient = new OllamaApiClient(ollamaEndpoint, model);

AIAgent friendlyFormatter = new ChatClientAgent(
    formatterClient,
    name: "FriendlyFormatter",
    instructions: """
        You receive raw weather data. Rewrite it as a friendly,
        conversational message. Use casual language.
        Add a short clothing suggestion based on the conditions.
        """);

// --- Sequential workflow: Fetcher → Formatter ---
var workflow = AgentWorkflowBuilder.BuildSequential(weatherFetcher, friendlyFormatter);

var messages = new List<ChatMessage>
{
    new(ChatRole.User, "What is the weather in Montreal?")
};

var run = InProcessExecution.StreamAsync(workflow, messages);

await foreach (var ev in run.WatchStreamAsync())
{
    if (ev is AgentResponseItem { Message: { } msg })
    {
        Console.WriteLine($"[{msg.AuthorName}] {msg.Text}");
    }
}

await mcpClient.DisposeAsync();

Why split into two agents?

Clear responsibility. The fetcher only fetches data. The formatter only rewrites it. If you don’t like the phrasing, change the formatter’s instructions without touching the fetcher. If you add a new MCP tool, only the fetcher is affected.

Easy to test. You can test the formatter with static weather data, without starting Ollama or the MCP server.

Extensible. Adding a third agent (translator, fact-checker, etc.) is one more line in BuildSequential().

Things I Wish I’d Known

Microsoft.Agents.AI vs the old packages. On NuGet, you’ll find Microsoft.SemanticKernel, AutoGen, and Microsoft.Agents.AI. Use the latter. Semantic Kernel and AutoGen are the predecessors. They still work, but active development is on Agent Framework.

Ollama doesn’t speak MCP. There’s an open issue from November 2024. Ollama has its own tool-calling format (OpenAI-compatible). Agent Framework bridges the gap: it translates MCP tools into OpenAI-style functions the LLM understands, and executes the calls via the MCP protocol on the server side.

RC = not GA yet. The API is stable, but minor adjustments are possible. Pin your package versions. At time of writing, it’s 1.0.0-rc4 for Microsoft.Agents.AI and Microsoft.Agents.AI.Workflows.

Model choice matters. Not all Ollama models handle tool-calling well. qwen3 is the current community recommendation for tool calling — it’s stable, rarely hallucinates calls, and works well even for multi-tool workflows. llama4:scout is another solid option. Smaller models like llama3.2 can work for simple cases but tend to struggle with complex tool interactions.

Conclusion

The series is complete. In four posts, we built a working .NET agent stack end to end:

  1. Ollama for local inference, private, free, offline
  2. RAG with Qdrant and LiteLLM for giving models context
  3. Hand-rolled ReAct agents to understand the fundamentals
  4. MCP server to standardize tool exposure
  5. Microsoft Agent Framework to orchestrate everything properly

The full code for all three examples is available here: mongeon/code-examples · agent-framework-ollama-mcp

Resources


This post was written with the help of AI.


See also