<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>AI Agents | The .NET Blog</title><link>https://thedotnetblog.com/tags/ai-agents/</link><description>Articles, tutorials and insights from the .NET community.</description><generator>Hugo</generator><language>en</language><managingEditor>@thedotnetblog (The .NET Blog)</managingEditor><webMaster>@thedotnetblog</webMaster><lastBuildDate>Tue, 05 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://thedotnetblog.com/tags/ai-agents/index.xml" rel="self" type="application/rss+xml"/><item><title>Microsoft Agent Framework Part 3: From Tools to Workflows — The Building Blocks Click Into Place</title><link>https://thedotnetblog.com/news/emiliano-montesdeoca/maf-building-blocks-part-3-agents-tools-workflows/</link><pubDate>Tue, 05 May 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/news/emiliano-montesdeoca/maf-building-blocks-part-3-agents-tools-workflows/</guid><description>Part 3 of the .NET AI building blocks series covers the Microsoft Agent Framework — from single agents with tools to multi-agent workflows with memory. Here's what actually matters.</description><content:encoded>&lt;p&gt;If you&amp;rsquo;ve been following the Building Blocks for AI in .NET series, you know Part 1 gave us &lt;code&gt;IChatClient&lt;/code&gt; (the universal model interface) and Part 2 gave us &lt;code&gt;Microsoft.Extensions.VectorData&lt;/code&gt; (semantic search and RAG). Both are foundational, both are useful on their own. But this is where everything starts to connect.&lt;/p&gt;
&lt;p&gt;Part 3 is about the &lt;a href="https://github.com/microsoft/agent-framework"&gt;Microsoft Agent Framework&lt;/a&gt; — and honestly, it&amp;rsquo;s the piece I&amp;rsquo;ve been waiting to see land in .NET. 1.0 shipped in April. The API is stable. It&amp;rsquo;s time to actually build agents.&lt;/p&gt;
&lt;h2 id="what-an-agent-actually-is-vs-a-chatbot"&gt;What an Agent Actually Is (vs. a Chatbot)&lt;/h2&gt;
&lt;p&gt;Before diving into code, let&amp;rsquo;s get this distinction out of the way. A chatbot receives input, calls a model, returns output. Simple loop.&lt;/p&gt;
&lt;p&gt;An agent has &lt;em&gt;autonomy&lt;/em&gt;. It can reason about a task, decide which tools to use, call those tools, evaluate results, and decide what to do next — all without you writing explicit step-by-step logic for every scenario. You give it tools and instructions, and it figures out the orchestration.&lt;/p&gt;
&lt;p&gt;Think of it this way: &lt;code&gt;IChatClient&lt;/code&gt; is like having a conversation. An agent is like handing someone a task list.&lt;/p&gt;
&lt;h2 id="your-first-agent-in-10-lines"&gt;Your First Agent in 10 Lines&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet add package Microsoft.Agents.AI
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;AIAgent&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;AzureOpenAIClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;DefaultAzureCredential&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetChatClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deploymentName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsAIAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;You are good at telling jokes.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Joker&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Tell me a joke about a pirate.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;.AsAIAgent()&lt;/code&gt; extension method is the bridge. Same pattern as &lt;code&gt;.AsIChatClient()&lt;/code&gt; from MEAI — it wraps a provider&amp;rsquo;s SDK in a stable abstraction. It works with Azure OpenAI, OpenAI, GitHub Models, Microsoft Foundry, or local models via Foundry Local or Ollama.&lt;/p&gt;
&lt;p&gt;Streaming works too:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunStreamingAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Tell me a joke about a pirate.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="giving-the-agent-tools"&gt;Giving the Agent Tools&lt;/h2&gt;
&lt;p&gt;This is where agents stop being fancy chatbots. Tools are functions the model can decide to call based on what the user asks. No routing logic needed on your part — the model figures it out.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;[Description(&amp;#34;Get the weather for a given location.&amp;#34;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;GetWeather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [Description(&amp;#34;The location to get the weather for.&amp;#34;)]&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$&amp;#34;The weather in {location} is cloudy with a high of 15°C.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;AIAgent&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chatClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsAIAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;You are a helpful assistant&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AIFunctionFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetWeather&lt;/span&gt;&lt;span class="p"&gt;)]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Two things to notice here. First, &lt;code&gt;AIFunctionFactory&lt;/code&gt; is from MEAI — same tool factory you&amp;rsquo;d use with a plain &lt;code&gt;IChatClient&lt;/code&gt;. If you&amp;rsquo;ve already defined tools for your chat scenarios, they work here too.&lt;/p&gt;
&lt;p&gt;Second, those &lt;code&gt;Description&lt;/code&gt; attributes matter a lot. They&amp;rsquo;re how the model understands what a tool does and when to use it. Treat them as documentation for your AI, not for humans.&lt;/p&gt;
&lt;h2 id="sessions-conversations-that-actually-remember"&gt;Sessions: Conversations That Actually Remember&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;AgentSession&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateSessionAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Tell me a joke about a pirate.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;Now add some emojis and tell it in the voice of a pirate&amp;#39;s parrot.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Without a session, each &lt;code&gt;RunAsync&lt;/code&gt; call is stateless. With a session, the agent knows which joke you&amp;rsquo;re referring to. The &lt;code&gt;AgentSession&lt;/code&gt; preserves conversation history between turns.&lt;/p&gt;
&lt;p&gt;For production stateless services, sessions serialize cleanly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;JsonElement&lt;/span&gt; &lt;span class="n"&gt;sessionState&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SerializeSessionAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ... store it somewhere ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;restoredSession&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeserializeSessionAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;What were we just talking about?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;restoredSession&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is critical if your agent runs in a serverless or horizontally-scaled environment.&lt;/p&gt;
&lt;h2 id="aicontextprovider-memory-that-persists-across-sessions"&gt;AIContextProvider: Memory That Persists Across Sessions&lt;/h2&gt;
&lt;p&gt;Sessions preserve conversation history &lt;em&gt;within&lt;/em&gt; a session. But what about knowing things about a user across sessions? &lt;code&gt;AIContextProvider&lt;/code&gt; handles that.&lt;/p&gt;
&lt;p&gt;It has two hooks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ProvideAIContextAsync&lt;/code&gt;&lt;/strong&gt; — runs &lt;em&gt;before&lt;/em&gt; each interaction, injects context into the agent (e.g., &amp;ldquo;The user&amp;rsquo;s name is Emiliano&amp;rdquo;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;StoreAIContextAsync&lt;/code&gt;&lt;/strong&gt; — runs &lt;em&gt;after&lt;/em&gt; each interaction, lets you learn from what was said and persist it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The pattern is elegant: you can stack multiple providers — one for user preferences, one for recent interactions, one that queries your VectorData store for relevant documents. That last one is exactly the RAG pattern from Part 2, now running automatically as part of every agent call.&lt;/p&gt;
&lt;h2 id="multi-agent-workflows"&gt;Multi-Agent Workflows&lt;/h2&gt;
&lt;p&gt;This is where the framework earns its name. The Agent Framework includes a graph-based workflow system where executors (agents, functions, whatever) connect via edges.&lt;/p&gt;
&lt;p&gt;Some patterns that are natively supported:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sequential&lt;/strong&gt;: Agent A&amp;rsquo;s output feeds Agent B&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Concurrent (fan-out/fan-in)&lt;/strong&gt;: Dispatch to multiple agents in parallel, collect results&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conditional routing&lt;/strong&gt;: Route work to different agents based on output&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Writer-critic loops&lt;/strong&gt;: One agent writes, another evaluates, loop until approved&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sub-workflows&lt;/strong&gt;: Compose workflows hierarchically&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A writer-critic example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;WorkflowBuilder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;writerAgent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;builder&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddEdge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;writerAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;criticAgent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddEdge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;criticAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;writerAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsApproved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithOutputFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;criticAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsApproved&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;workflow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Clean, readable, and the condition-based routing means you don&amp;rsquo;t write loop logic yourself. The framework drives the iteration.&lt;/p&gt;
&lt;h2 id="human-in-the-loop"&gt;Human-in-the-Loop&lt;/h2&gt;
&lt;p&gt;Not everything should run fully autonomously. For sensitive operations — database writes, financial transactions, sending communications — you want a human to approve before the agent executes.&lt;/p&gt;
&lt;p&gt;The framework has built-in support for this via &lt;code&gt;FunctionApprovalRequestContent&lt;/code&gt; and &lt;code&gt;FunctionApprovalResponseContent&lt;/code&gt;. The agent proposes the tool call, your application code presents it to the user, and the response determines whether execution proceeds.&lt;/p&gt;
&lt;p&gt;This is the right way to think about agents in enterprise settings: not fully autonomous, but &lt;em&gt;autonomy-with-guardrails&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="the-full-picture"&gt;The Full Picture&lt;/h2&gt;
&lt;p&gt;If you step back for a second:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MEAI&lt;/strong&gt; gives you a universal interface to any model&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VectorData&lt;/strong&gt; gives your agents access to your organization&amp;rsquo;s knowledge through semantic search&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agent Framework&lt;/strong&gt; orchestrates everything — it uses &lt;code&gt;IChatClient&lt;/code&gt; under the hood, composes with context providers, and coordinates through workflows&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each piece was designed to compose with the others. You can use any of them independently, but together they form a coherent stack for building AI applications in .NET.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href="https://devblogs.microsoft.com/dotnet/microsoft-agent-framework-building-blocks-for-ai-part-3/"&gt;original post by Jeremy Likness&lt;/a&gt; and the &lt;a href="https://github.com/microsoft/agent-framework/tree/main/dotnet"&gt;Agent Framework GitHub repo&lt;/a&gt; for the full samples.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;The Microsoft Agent Framework Part 3 post closes the loop on the building blocks series (with MCP coming next). For .NET developers who want to build AI agents — not just chatbots, actual agents that use tools, remember things, and coordinate — this is your path forward.&lt;/p&gt;
&lt;p&gt;The 1.0 stable release means you can build on this in production. The composition with MEAI and VectorData means you&amp;rsquo;re not learning a parallel set of abstractions. It all fits together.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve been waiting to jump into agent development in .NET, the timing is right now.&lt;/p&gt;</content:encoded></item><item><title>SQL MCP Server on Azure App Service — No Containers Required</title><link>https://thedotnetblog.com/news/emiliano-montesdeoca/sql-mcp-server-azure-app-service-no-containers/</link><pubDate>Tue, 05 May 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/news/emiliano-montesdeoca/sql-mcp-server-azure-app-service-no-containers/</guid><description>The SQL MCP Server can now run on Azure App Service without Docker or Kubernetes. Here's what that means for .NET developers building AI agents that talk to SQL databases.</description><content:encoded>&lt;p&gt;Let me be honest with you: every time I see &amp;ldquo;requires a container&amp;rdquo; in a tutorial, a little part of me sighs. Containers are great — until your team doesn&amp;rsquo;t have a container strategy, and suddenly a feature that looked simple is blocked behind orchestration overhead you didn&amp;rsquo;t plan for.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s why this one caught my eye. The SQL MCP Server can now run on Azure App Service — no Docker, no Kubernetes, just the same Data API builder (DAB) configuration that exposes your SQL database through MCP, REST, and GraphQL.&lt;/p&gt;
&lt;h2 id="whats-sql-mcp-server-again"&gt;What&amp;rsquo;s SQL MCP Server, Again?&lt;/h2&gt;
&lt;p&gt;Quick context if you haven&amp;rsquo;t run into it yet. SQL MCP Server sits between your AI agent and your SQL database. Instead of giving your agent direct database access (which is a terrible idea), it exposes your tables and views as an abstraction layer — entities with defined permissions.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s built on top of &lt;a href="https://learn.microsoft.com/en-us/azure/data-api-builder/"&gt;Data API builder&lt;/a&gt;, which means one configuration file drives MCP &lt;em&gt;and&lt;/em&gt; REST &lt;em&gt;and&lt;/em&gt; GraphQL simultaneously. Your agent talks to the MCP endpoint. Your traditional app talks to REST or GraphQL. Same config, same runtime, different surfaces.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s genuinely useful. You&amp;rsquo;re not maintaining two separate API layers.&lt;/p&gt;
&lt;h2 id="the-container-problem-and-the-solution"&gt;The Container Problem (and the Solution)&lt;/h2&gt;
&lt;p&gt;The original deployment model for SQL MCP Server was containers. That works well in many shops — but not all. Plenty of .NET teams standardize on Azure App Service or VMs. Requiring a container runtime just to expose a SQL endpoint adds friction nobody asked for.&lt;/p&gt;
&lt;p&gt;The new walkthrough shows you how to skip the container entirely. The whole thing runs with a &lt;code&gt;dab start&lt;/code&gt; command, hosted on App Service as a standard .NET 8 web process.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the local setup flow in a nutshell:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Install Data API builder&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet tool install microsoft.dataapibuilder --prerelease -g
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Initialize the configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dab init --database-type mssql --host-mode Development --connection-string &lt;span class="s2"&gt;&amp;#34;@env(&amp;#39;SQL_CONNECTION_STRING&amp;#39;)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Add an entity&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dab add products --source dbo.products --permissions &lt;span class="s2"&gt;&amp;#34;authenticated:*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Configure App Service auth provider&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dab configure --runtime.host.authentication.provider AppService
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Start the server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dab start
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point you have MCP at &lt;code&gt;/mcp&lt;/code&gt;, REST and GraphQL from the same process, and nothing running in a container.&lt;/p&gt;
&lt;h2 id="authentication-that-doesnt-involve-shared-api-keys"&gt;Authentication That Doesn&amp;rsquo;t Involve Shared API Keys&lt;/h2&gt;
&lt;p&gt;This is the part I appreciate most. When you deploy to App Service, you configure Microsoft Entra ID as the authentication provider. No shared secrets embedded in config files, no API keys to rotate.&lt;/p&gt;
&lt;p&gt;The connection string stays in an App Service environment variable (not in &lt;code&gt;dab-config.json&lt;/code&gt;), and the MCP endpoint is protected by platform authentication. If you&amp;rsquo;re already aligned to Entra ID across your Azure workloads — which you probably are if you&amp;rsquo;re using Azure AI Foundry agents — this fits naturally.&lt;/p&gt;
&lt;p&gt;For local development, you switch to &lt;code&gt;Simulator&lt;/code&gt; mode and STDIO transport. Flip back to &lt;code&gt;AppService&lt;/code&gt; mode when deploying. Clean and explicit.&lt;/p&gt;
&lt;h2 id="deploying-to-app-service"&gt;Deploying to App Service&lt;/h2&gt;
&lt;p&gt;The actual deployment is straightforward Azure CLI work:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Create the App Service plan&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;az appservice plan create &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --name &amp;lt;plan-name&amp;gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --resource-group &amp;lt;resource-group&amp;gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --sku B1 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --is-linux
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Create the web app (.NET 8 runtime)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;az webapp create &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --name &amp;lt;app-name&amp;gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --resource-group &amp;lt;resource-group&amp;gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --plan &amp;lt;plan-name&amp;gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --runtime &lt;span class="s2"&gt;&amp;#34;DOTNETCORE:8.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Set the startup command&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;az webapp config &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --name &amp;lt;app-name&amp;gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --resource-group &amp;lt;resource-group&amp;gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --startup-file &lt;span class="s2"&gt;&amp;#34;dab start&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then deploy your DAB project using whatever code deployment path your team already uses — VS Code, GitHub Actions, Zip Deploy. The key detail: it&amp;rsquo;s a &lt;strong&gt;code&lt;/strong&gt; deployment, not a container deployment. No image to build, push, or manage.&lt;/p&gt;
&lt;h2 id="why-this-matters-for-net-developers"&gt;Why This Matters for .NET Developers&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re building AI agents in .NET — whether with the Microsoft Agent Framework, Semantic Kernel, or Azure AI Foundry hosted agents — eventually your agent needs to talk to a database. SQL MCP Server gives you a structured way to do that without exposing raw connection strings or writing a custom API layer.&lt;/p&gt;
&lt;p&gt;Running it on App Service closes the gap for teams that aren&amp;rsquo;t running containers. It&amp;rsquo;s the same DAB config, the same Entra auth, the same MCP protocol — just on infrastructure you already know.&lt;/p&gt;
&lt;p&gt;Check out the full walkthrough in the &lt;a href="https://devblogs.microsoft.com/azure-sql/sql-mcp-server-app-service/"&gt;original blog post&lt;/a&gt; and the &lt;a href="https://github.com/Azure-Samples/SQL-MCP-NoContainer"&gt;sample repo on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;The SQL MCP Server on App Service is a solid pragmatic option for .NET teams that want to give their agents structured access to SQL data without a container strategy. The combination of DAB&amp;rsquo;s entity model, App Service&amp;rsquo;s built-in Entra auth, and the &lt;code&gt;dab start&lt;/code&gt; startup command makes for a deployment that&amp;rsquo;s simple to explain and easy to operate.&lt;/p&gt;
&lt;p&gt;Give it a try. Your agents will appreciate the clean API surface. Your ops team will appreciate not having to deal with container registries.&lt;/p&gt;</content:encoded></item><item><title>Governing MCP Tool Calls in .NET with the Agent Governance Toolkit</title><link>https://thedotnetblog.com/news/emiliano-montesdeoca/governing-mcp-tool-calls-dotnet/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/news/emiliano-montesdeoca/governing-mcp-tool-calls-dotnet/</guid><description>How to introduce governance, policy checks, and safer tool execution for MCP-based .NET agents.</description><content:encoded>&lt;p&gt;&lt;a href="https://devblogs.microsoft.com/dotnet/governing-mcp-tool-calls-in-dotnet-with-the-agent-governance-toolkit/"&gt;Governing MCP Tool Calls in .NET with the Agent Governance Toolkit&lt;/a&gt; is worth a close look if you are building or operating .NET systems at scale.&lt;/p&gt;
&lt;p&gt;From my perspective, the important part is not the headline feature but how quickly a team can convert it into a safer, repeatable engineering workflow.&lt;/p&gt;
&lt;h2 id="why-it-matters-for-net-teams"&gt;Why it matters for .NET teams&lt;/h2&gt;
&lt;p&gt;Most teams are balancing delivery speed, platform consistency, and governance. This update is useful because it gives you a more concrete path to improve one of those constraints without rewriting everything.&lt;/p&gt;
&lt;h2 id="practical-next-steps"&gt;Practical next steps&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Validate the feature in a small .NET pilot with production-like data.&lt;/li&gt;
&lt;li&gt;Add clear rollback and observability checkpoints before broader rollout.&lt;/li&gt;
&lt;li&gt;Capture the implementation pattern in your internal templates so other teams can reuse it.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="source"&gt;Source&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Original article: &lt;a href="https://devblogs.microsoft.com/dotnet/governing-mcp-tool-calls-in-dotnet-with-the-agent-governance-toolkit/"&gt;https://devblogs.microsoft.com/dotnet/governing-mcp-tool-calls-in-dotnet-with-the-agent-governance-toolkit/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Visual Studio April Update: Cloud Agent Integration for .NET Workflows</title><link>https://thedotnetblog.com/news/emiliano-montesdeoca/visual-studio-april-update-cloud-agent-integration/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/news/emiliano-montesdeoca/visual-studio-april-update-cloud-agent-integration/</guid><description>A focused walkthrough of the April Visual Studio update and what Cloud Agent integration changes for .NET workflows.</description><content:encoded>&lt;p&gt;&lt;a href="https://devblogs.microsoft.com/visualstudio/visual-studio-april-update-cloud-agent-integration/"&gt;Visual Studio April Update: Cloud Agent Integration for .NET Workflows&lt;/a&gt; is worth a close look if you are building or operating .NET systems at scale.&lt;/p&gt;
&lt;p&gt;From my perspective, the important part is not the headline feature but how quickly a team can convert it into a safer, repeatable engineering workflow.&lt;/p&gt;
&lt;h2 id="why-it-matters-for-net-teams"&gt;Why it matters for .NET teams&lt;/h2&gt;
&lt;p&gt;Most teams are balancing delivery speed, platform consistency, and governance. This update is useful because it gives you a more concrete path to improve one of those constraints without rewriting everything.&lt;/p&gt;
&lt;h2 id="practical-next-steps"&gt;Practical next steps&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Validate the feature in a small .NET pilot with production-like data.&lt;/li&gt;
&lt;li&gt;Add clear rollback and observability checkpoints before broader rollout.&lt;/li&gt;
&lt;li&gt;Capture the implementation pattern in your internal templates so other teams can reuse it.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="source"&gt;Source&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Original article: &lt;a href="https://devblogs.microsoft.com/visualstudio/visual-studio-april-update-cloud-agent-integration/"&gt;https://devblogs.microsoft.com/visualstudio/visual-studio-april-update-cloud-agent-integration/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>