<?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>The .NET Blog</title><link>https://thedotnetblog.com/</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>Mon, 14 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://thedotnetblog.com/index.xml" rel="self" type="application/rss+xml"/><item><title>NDC Oslo 2026</title><link>https://thedotnetblog.com/events/ndc-oslo-2026/</link><pubDate>Mon, 14 Sep 2026 00:00:00 +0000</pubDate><guid>https://thedotnetblog.com/events/ndc-oslo-2026/</guid><description>One of the biggest developer conferences in Europe — 5 days of workshops, sessions, and networking at Oslo Spektrum with 150+ speakers and 160 sessions.</description><content:encoded>&lt;p&gt;&lt;strong&gt;NDC Oslo 2026&lt;/strong&gt; runs from &lt;strong&gt;September 14–18, 2026&lt;/strong&gt; at &lt;strong&gt;Oslo Spektrum&lt;/strong&gt; in Oslo, Norway.&lt;/p&gt;
&lt;p&gt;NDC Oslo is one of the largest and most respected developer conferences in Europe, covering everything from .NET and cloud to security, architecture, AI, and beyond. The 2026 edition is currently in the booking phase, with a massive lineup shaping up.&lt;/p&gt;
&lt;h2 id="by-the-numbers"&gt;By the numbers&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;160 sessions&lt;/strong&gt; (currently booking)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;150 speakers&lt;/strong&gt; (currently booking)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;15 workshops&lt;/strong&gt; (currently booking)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;5 days&lt;/strong&gt; — workshops + conference&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="confirmed-speakers"&gt;Confirmed speakers&lt;/h2&gt;
&lt;p&gt;The speaker list is being built out, with confirmed names including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Nick Chapsas&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maddy Montaquila&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Troy Hunt&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kevlin Henney&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Venkat Subramaniam&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jeff Fritz&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Philippe De Ryck&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nhlanhla Lucky Nkosi&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aleksander Stensby&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="tickets"&gt;Tickets&lt;/h2&gt;
&lt;p&gt;Early Bird tickets are available — the Early Bird offer ends &lt;strong&gt;May 22, 2026&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The CFP (Call for Papers) is also currently open.&lt;/p&gt;
&lt;h2 id="other-ndc-events-in-2026"&gt;Other NDC events in 2026&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;NDC Sydney — April 22–24, 2026&lt;/li&gt;
&lt;li&gt;NDC Toronto — May 5–8, 2026&lt;/li&gt;
&lt;li&gt;NDC Copenhagen — June 1–4, 2026&lt;/li&gt;
&lt;li&gt;NDC AI — June 8–10, 2026&lt;/li&gt;
&lt;li&gt;NDC TechTown — September 21–24, 2026&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="links"&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ndcoslo.com/"&gt;Event website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ndcoslo.com/tickets"&gt;Tickets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ndcoslo.com/call-for-papers"&gt;Call for Papers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ndcoslo.com/speakers"&gt;Speakers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>.NET Day Switzerland 2026</title><link>https://thedotnetblog.com/events/dotnet-day-switzerland-2026/</link><pubDate>Tue, 25 Aug 2026 00:00:00 +0000</pubDate><guid>https://thedotnetblog.com/events/dotnet-day-switzerland-2026/</guid><description>A non-profit community conference for .NET developers, architects, and experts — covering .NET, C#, ASP.NET Core, Azure, and more — in Zürich.</description><content:encoded>&lt;p&gt;&lt;strong&gt;.NET Day Switzerland 2026&lt;/strong&gt; takes place on &lt;strong&gt;August 25, 2026&lt;/strong&gt; at &lt;strong&gt;Arena Cinemas Sihlcity&lt;/strong&gt; (Kalanderplatz 8, 8045 Zürich).&lt;/p&gt;
&lt;p&gt;This is an independent, non-profit community conference for developers, architects, and experts to discuss .NET, C#, ASP.NET Core, Azure, and the broader Microsoft development ecosystem. All speakers and staff volunteer their time, and any surplus from ticket sales goes to charity or the Swiss .NET community.&lt;/p&gt;
&lt;h2 id="what-you-get"&gt;What you get&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Top quality sessions from international subject experts&lt;/li&gt;
&lt;li&gt;Networking with other participants&lt;/li&gt;
&lt;li&gt;Food, snacks, and beverages during breaks, lunch, and apéro&lt;/li&gt;
&lt;li&gt;Career opportunities through sponsor interactions&lt;/li&gt;
&lt;li&gt;Direct discussions with speakers during breaks&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="tickets"&gt;Tickets&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Availability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Very Early Bird (Apr 1–30)&lt;/td&gt;
&lt;td&gt;299 CHF&lt;/td&gt;
&lt;td&gt;Max 100 tickets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Early Bird&lt;/td&gt;
&lt;td&gt;399 CHF&lt;/td&gt;
&lt;td&gt;Max 100 tickets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regular&lt;/td&gt;
&lt;td&gt;449 CHF&lt;/td&gt;
&lt;td&gt;Until sold out&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Speakers and schedule are not yet announced — the Call for Speakers is open on &lt;a href="https://sessionize.com/net-day-switzerland-2026/"&gt;Sessionize&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="organizers"&gt;Organizers&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/FabianGosebrink"&gt;Fabian Gosebrink&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/manumeyer1"&gt;Manuel Meyer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/gassmannt"&gt;Thomas Gassmann&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="links"&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dotnetday.ch/"&gt;Event website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://eepurl.com/dDoFEn"&gt;Newsletter signup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>SDD Conference 2026</title><link>https://thedotnetblog.com/events/sdd-conference-2026/</link><pubDate>Mon, 11 May 2026 00:00:00 +0000</pubDate><guid>https://thedotnetblog.com/events/sdd-conference-2026/</guid><description>A 5-day software development conference at the Barbican Centre in London with 78 sessions and 14 workshops covering architecture, .NET, AI, Azure, DevOps, and more.</description><content:encoded>&lt;p&gt;&lt;strong&gt;SDD 2026&lt;/strong&gt; runs from &lt;strong&gt;May 11–15, 2026&lt;/strong&gt; at the &lt;strong&gt;Barbican Centre in London&lt;/strong&gt;. The core 3-day conference is Tuesday through Thursday, with optional full-day workshops on Monday and Friday.&lt;/p&gt;
&lt;p&gt;With &lt;strong&gt;78 sessions&lt;/strong&gt; and &lt;strong&gt;14 workshops&lt;/strong&gt;, this is one of the most packed developer conferences in Europe.&lt;/p&gt;
&lt;h2 id="topics"&gt;Topics&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Architectural Thinking&lt;/li&gt;
&lt;li&gt;Functional Code in C# 13&lt;/li&gt;
&lt;li&gt;Serverless Design&lt;/li&gt;
&lt;li&gt;Semantic AI&lt;/li&gt;
&lt;li&gt;Azure Kubernetes Services&lt;/li&gt;
&lt;li&gt;Lean DevOps Strategies&lt;/li&gt;
&lt;li&gt;The Model Context Protocol (MCP)&lt;/li&gt;
&lt;li&gt;Agentic AI in .NET&lt;/li&gt;
&lt;li&gt;Refactoring the Monolith&lt;/li&gt;
&lt;li&gt;Coding Faster with LLMs&lt;/li&gt;
&lt;li&gt;Cryptography in a Post-Quantum World&lt;/li&gt;
&lt;li&gt;Local First Development&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="speakers"&gt;Speakers&lt;/h2&gt;
&lt;p&gt;World-class lineup including &lt;strong&gt;Kevlin Henney&lt;/strong&gt;, &lt;strong&gt;Neal Ford&lt;/strong&gt;, &lt;strong&gt;Sander Hoogendoorn&lt;/strong&gt;, &lt;strong&gt;Andrew Clymer&lt;/strong&gt;, &lt;strong&gt;Jacqui Read&lt;/strong&gt;, &lt;strong&gt;Christian Weyer&lt;/strong&gt;, &lt;strong&gt;Jeff Prosise&lt;/strong&gt;, &lt;strong&gt;Jules May&lt;/strong&gt;, &lt;strong&gt;Oliver Sturm&lt;/strong&gt;, and &lt;strong&gt;Raju Gandhi&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="tickets-and-info"&gt;Tickets and info&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://sddconf.com/"&gt;Event website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sddvault.s3.amazonaws.com/assets/SDD_2026_schedule.pdf"&gt;Full agenda PDF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sddconf.com/register"&gt;Registration options&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;98% of SDD 2025 attendees rated the overall experience as good, very good, or excellent.&lt;/p&gt;</content:encoded></item><item><title>Azure DevOps MCP Server April Update: WIQL Queries, PAT Auth, and Experimental MCP Apps</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-devops-mcp-server-april-2026-wiql-pat-apps/</link><pubDate>Mon, 27 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-devops-mcp-server-april-2026-wiql-pat-apps/</guid><description>The Azure DevOps MCP Server gets WIQL-powered work item queries, Personal Access Token authentication, MCP annotations, and an experimental MCP Apps feature that packages common workflows into reusable tools.</description><content:encoded>&lt;p&gt;The Azure DevOps MCP Server keeps getting better. Dan Hellem&amp;rsquo;s April update covers both the local and remote servers, and there are some genuinely useful additions here — especially if you&amp;rsquo;ve been using Copilot to navigate boards and repos.&lt;/p&gt;
&lt;h2 id="wiql-query-support"&gt;WIQL Query Support&lt;/h2&gt;
&lt;p&gt;The headline feature: a new &lt;code&gt;wit_query_by_wiql&lt;/code&gt; tool that lets you run Work Item Query Language queries directly from your MCP client.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve used Azure Boards for any length of time, you know WIQL. It&amp;rsquo;s the SQL-like query syntax for work items: &lt;code&gt;SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.AssignedTo] = @Me AND [System.State] = 'Active'&lt;/code&gt;. Having that available as an MCP tool means your Copilot sessions can now pull precise work item sets without you manually filtering or clicking through board views.&lt;/p&gt;
&lt;p&gt;One caveat: on the remote MCP Server, this tool currently requires the &lt;strong&gt;Insiders&lt;/strong&gt; feature flag while they validate query performance at scale. It&amp;rsquo;ll come to everyone once the telemetry looks good.&lt;/p&gt;
&lt;h2 id="personal-access-tokens-on-the-local-server"&gt;Personal Access Tokens on the Local Server&lt;/h2&gt;
&lt;p&gt;The local MCP Server now supports PAT authentication. This sounds like a minor quality-of-life fix, but it&amp;rsquo;s actually important for integration scenarios — specifically when you&amp;rsquo;re running the MCP server in a context where interactive authentication isn&amp;rsquo;t available, or when you&amp;rsquo;re connecting from external clients and automation.&lt;/p&gt;
&lt;p&gt;Setup is documented in the &lt;a href="https://github.com/microsoft/azure-devops-mcp/blob/main/docs/GETTINGSTARTED.md#-personal-access-token-pat"&gt;Getting Started guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="mcp-annotations-on-the-remote-server"&gt;MCP Annotations on the Remote Server&lt;/h2&gt;
&lt;p&gt;Annotations are metadata tags on MCP tools that tell LLMs how to use them safely. The Azure DevOps MCP Server is now implementing annotations for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Read-only tools&lt;/strong&gt; — the LLM knows these are safe to call without user confirmation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Destructive tools&lt;/strong&gt; — the LLM knows to be cautious and confirm before proceeding&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Open-world tools&lt;/strong&gt; — the LLM understands these may return unpredictable results&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is foundational for agent reliability. Without annotations, the LLM has to guess from the tool name whether it&amp;rsquo;s safe to call. With annotations, the behavior is explicit and the agent can make better decisions.&lt;/p&gt;
&lt;h2 id="wiki-tool-consolidation"&gt;Wiki Tool Consolidation&lt;/h2&gt;
&lt;p&gt;The remote server is starting to consolidate related tools into fewer, more capable ones. The wiki tools are the first to get this treatment:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;New Tool&lt;/th&gt;
&lt;th&gt;Replaces&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wiki&lt;/code&gt; (read-only)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wiki_get_page&lt;/code&gt;, &lt;code&gt;wiki_get_page_content&lt;/code&gt;, &lt;code&gt;wiki_list_pages&lt;/code&gt;, &lt;code&gt;wiki_list_wikis&lt;/code&gt;, &lt;code&gt;wiki_get_wiki&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wiki_upsert_page&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wiki_create_or_update_page&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Fewer tools = better LLM performance. This is a consistent pattern across MCP server design — smaller, focused tool sets work better because the LLM doesn&amp;rsquo;t have to reason about which of five similarly-named tools to pick.&lt;/p&gt;
&lt;h2 id="experimental-mcp-apps"&gt;Experimental: MCP Apps&lt;/h2&gt;
&lt;p&gt;This is the most interesting addition, and it&amp;rsquo;s clearly experimental. MCP Apps are packaged workflows that run inside the MCP server environment:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&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="nt"&gt;&amp;#34;servers&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&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="nt"&gt;&amp;#34;ado&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&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="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;stdio&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="nt"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;mcp-server-azuredevops&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="nt"&gt;&amp;#34;args&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;contoso&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;-d&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;core&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;work&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;work-items&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;mcp-apps&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="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;p&gt;The first example is &lt;code&gt;mcp_app_my_work_item&lt;/code&gt; — a self-contained work item experience that lets you view, filter, and edit work items assigned to you, without manually chaining multiple tool calls.&lt;/p&gt;
&lt;p&gt;The idea is compelling: instead of your agent calling &lt;code&gt;wit_get_work_item&lt;/code&gt; → &lt;code&gt;wit_list_work_items&lt;/code&gt; → &lt;code&gt;wit_update_work_item&lt;/code&gt; across multiple turns, a single MCP App provides the entire workflow as one structured, reusable unit. Reduced setup time, consistent behavior, fewer moving parts.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s on the &lt;code&gt;mcp-apps-poc&lt;/code&gt; branch right now, which tells you where it stands in terms of production readiness. But the direction is right — more workflow composition at the MCP layer, less ad-hoc tool chaining in your agent prompts.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;The Azure DevOps MCP Server is maturing quickly. WIQL support and PAT auth are immediate wins for anyone using Copilot with Azure Boards. The annotation work makes the remote server safer for agentic use cases. And MCP Apps, while experimental, hints at where this is going: from raw tools to composable workflows.&lt;/p&gt;
&lt;p&gt;Worth keeping an eye on the &lt;a href="https://learn.microsoft.com/en-us/azure/devops/mcp-server/remote-mcp-server"&gt;documentation&lt;/a&gt; as the remote server continues to evolve.&lt;/p&gt;
&lt;p&gt;Original post by Dan Hellem: &lt;a href="https://devblogs.microsoft.com/devops/azure-devops-mcp-server-april-update/"&gt;Azure DevOps MCP Server April Update&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>SQL Server 2025 as Your Agent-Ready Database: Security, Backup, and MCP in One Engine</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/sql-server-2025-agent-ready-security-mcp/</link><pubDate>Sun, 26 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/sql-server-2025-agent-ready-security-mcp/</guid><description>The final part of the Polyglot Tax series tackles the hard production problems: unified Row-Level Security across relational, JSON, graph, and vector data — plus cryptographic audit trails and MCP integration that make SQL Server 2025 genuinely agent-ready.</description><content:encoded>&lt;p&gt;I&amp;rsquo;ve been following the Polyglot Tax series by Aditya Badramraju with a lot of interest. Parts 1-3 built a compelling case for SQL Server 2025 as a genuinely multi-model database — JSON, graph, vectors, and relational data all in one engine with a unified query planner. Part 4 closes the series with the parts that actually determine whether you&amp;rsquo;d trust this architecture in production.&lt;/p&gt;
&lt;p&gt;Spoiler: the production story is solid.&lt;/p&gt;
&lt;h2 id="one-security-model-to-rule-all-data-models"&gt;One Security Model to Rule All Data Models&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the thing with polyglot stacks: when an auditor asks &amp;ldquo;prove that Tenant A cannot see Tenant B&amp;rsquo;s data,&amp;rdquo; you have to answer that question for each database independently. Five databases, five security models, five proofs.&lt;/p&gt;
&lt;p&gt;With SQL Server 2025, you define one Row-Level Security policy and it covers every data model:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fn_TenantFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;TenantID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&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;RETURNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SCHEMABINDING&lt;/span&gt;&lt;span class="w"&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;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RETURN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fn_result&lt;/span&gt;&lt;span class="w"&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;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;TenantID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SESSION_CONTEXT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;TenantID&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&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;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SECURITY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;POLICY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TenantIsolation&lt;/span&gt;&lt;span class="w"&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;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FILTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PREDICATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fn_TenantFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TenantID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&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;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- Relational
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FILTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PREDICATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fn_TenantFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TenantID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&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;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- JSON data
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FILTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PREDICATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fn_TenantFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TenantID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&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;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Relationships&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- Graph edges
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FILTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PREDICATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fn_TenantFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TenantID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&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;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Embeddings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- Vector data
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;STATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;From that point, every query — relational joins, JSON path queries, graph traversals, vector similarity searches — is automatically filtered by tenant. The engine injects the predicate into the execution plan before any data leaves storage. Your calling code doesn&amp;rsquo;t need &lt;code&gt;WHERE TenantID = @id&lt;/code&gt; everywhere. You test the policy once.&lt;/p&gt;
&lt;p&gt;The layers compose further: Dynamic Data Masking for columns that shouldn&amp;rsquo;t show full values to certain roles, Always Encrypted for end-to-end encryption (even DBAs can&amp;rsquo;t read it), and stored procedures as the permission boundary so agents only call what you explicitly exposed.&lt;/p&gt;
&lt;p&gt;This is the part of the architecture that matters most for compliance-heavy SaaS. One policy, one proof.&lt;/p&gt;
&lt;h2 id="unified-backup--atomic-recovery"&gt;Unified Backup = Atomic Recovery&lt;/h2&gt;
&lt;p&gt;One statement, all data models, consistent point in time:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;BACKUP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DATABASE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MultiModelApp&lt;/span&gt;&lt;span class="w"&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;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://storage.blob.core.windows.net/backups/MultiModelApp.bak&amp;#39;&lt;/span&gt;&lt;span class="w"&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;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;COMPRESSION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ENCRYPTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ALGORITHM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AES_256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SERVER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CERTIFICATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BackupCert&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&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;RESTORE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DATABASE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MultiModelApp&lt;/span&gt;&lt;span class="w"&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;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://storage.blob.core.windows.net/backups/MultiModelApp.bak&amp;#39;&lt;/span&gt;&lt;span class="w"&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;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;STOPAT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2026-02-01 10:30:00&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In a polyglot stack, point-in-time recovery across five databases means coordinating five restore operations and hoping the timestamps line up within a second or two. For financial data, that two-second inconsistency is unacceptable. With one database, one transaction log, one restore — recovery is atomic by definition.&lt;/p&gt;
&lt;h2 id="ledger-tables-for-tamper-evident-audit-trails"&gt;Ledger Tables for Tamper-Evident Audit Trails&lt;/h2&gt;
&lt;p&gt;For regulated industries, you need more than &amp;ldquo;we have logs.&amp;rdquo; You need cryptographic proof that those logs weren&amp;rsquo;t modified:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FinancialTransactions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TransactionID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIMARY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AccountID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MONEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TransactionType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NVARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TransactionDate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DATETIME2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DEFAULT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SYSUTCDATETIME&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&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="w"&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;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SYSTEM_VERSIONING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LEDGER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Every insert, update, and delete gets cryptographically hashed into a blockchain-style structure. You can prove to an auditor — mathematically — that a row hasn&amp;rsquo;t been tampered with since it was written. In a polyglot stack, this capability doesn&amp;rsquo;t exist uniformly across all your databases.&lt;/p&gt;
&lt;h2 id="mcp-integration-agents-without-hand-coded-middleware"&gt;MCP Integration: Agents Without Hand-Coded Middleware&lt;/h2&gt;
&lt;p&gt;The series built toward this: SQL Server 2025 supports the SQL MCP Server directly, which means your agents can call the database through natural language tool calls without you writing middleware for every operation.&lt;/p&gt;
&lt;p&gt;Combine that with stored procedures as the permission boundary and Row-Level Security enforced at the engine, and you have a model where:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Agent calls a tool (e.g., &amp;ldquo;get customer context for account 12345&amp;rdquo;)&lt;/li&gt;
&lt;li&gt;MCP translates to the stored procedure you defined&lt;/li&gt;
&lt;li&gt;SQL engine enforces tenant isolation and column masking automatically&lt;/li&gt;
&lt;li&gt;Agent gets exactly the data it&amp;rsquo;s allowed to see&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;No middleware layer. No ad-hoc query injection risk. The engine handles authorization, not the agent.&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 .NET services with SQL Server as your primary store, the message from this series is: you don&amp;rsquo;t need to add Redis for caching, a graph DB for relationships, or a vector store for embeddings. SQL Server 2025 handles all of that — with better operational consistency than a polyglot stack and unified security that&amp;rsquo;s actually auditable.&lt;/p&gt;
&lt;p&gt;The MCP integration means your Semantic Kernel agents or Microsoft Agent Framework workflows can interact with your data tier through the same SQL MCP Server, with the same security guarantees you&amp;rsquo;d enforce for human queries.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;The Polyglot Tax series is worth reading end-to-end. Parts 1-3 prove the query planner story. Part 4 proves the production story. For .NET developers building agent-first or AI-augmented applications on Azure SQL, this architecture deserves serious consideration.&lt;/p&gt;
&lt;p&gt;Original post by Aditya Badramraju: &lt;a href="https://devblogs.microsoft.com/azure-sql/the-polyglot-tax-part-4/"&gt;The Polyglot Tax – Part 4&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>.NET 10 Ships with Ubuntu 26.04 LTS — Here's What's New</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/dotnet-ubuntu-2604-resolute-raccoon-net10/</link><pubDate>Sat, 25 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/dotnet-ubuntu-2604-resolute-raccoon-net10/</guid><description>Ubuntu 26.04 LTS (Resolute Raccoon) launched today with .NET 10 as a first-class supported toolchain. Native AOT, chiseled containers, Linux 7.0 — here's what you need to know.</description><content:encoded>&lt;p&gt;It&amp;rsquo;s Ubuntu LTS day. &lt;a href="https://canonical.com/blog/canonical-releases-ubuntu-26-04-lts-resolute-raccoon"&gt;Ubuntu 26.04 (Resolute Raccoon)&lt;/a&gt; launched today, and as with every Ubuntu LTS, it ships with the latest .NET LTS — in this case, &lt;a href="https://devblogs.microsoft.com/dotnet/whats-new-for-dotnet-in-ubuntu-2604/"&gt;.NET 10&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you deploy .NET apps on Linux, this is the release cycle you care about. LTS on LTS — five years of support for the OS, matching .NET 10&amp;rsquo;s own long-term support window.&lt;/p&gt;
&lt;h2 id="install-net-10-in-two-commands"&gt;Install .NET 10 in two commands&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;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install dotnet-sdk-10.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. .NET is one of the &lt;a href="https://ubuntu.com/toolchains"&gt;officially supported toolchains on Ubuntu&lt;/a&gt; — not a third-party add-on. Microsoft and Canonical work together to make sure it works on day one.&lt;/p&gt;
&lt;h2 id="try-it-immediately"&gt;Try it immediately&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the thing I love about this: you can pull an &lt;code&gt;ubuntu:resolute&lt;/code&gt; container image and be running C# in under a minute.&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;docker run --rm -it ubuntu:resolute
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apt install -y dotnet-sdk-10.0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet run - &lt;span class="s"&gt;&amp;lt;&amp;lt; &amp;#39;EOF&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;using System.Runtime.InteropServices;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;Console.WriteLine($&amp;#34;Hello {RuntimeInformation.OSDescription} from .NET {RuntimeInformation.FrameworkDescription}&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That &lt;code&gt;dotnet run -&lt;/code&gt; with a heredoc is a file-based app pattern — no project file, no directory, just C# piped to stdin. Honest, if you haven&amp;rsquo;t tried file-based apps yet, it&amp;rsquo;s worth a look.&lt;/p&gt;
&lt;h2 id="containers-update--noble-to--resolute"&gt;Containers: update &lt;code&gt;-noble&lt;/code&gt; to &lt;code&gt;-resolute&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The new container images use the &lt;code&gt;resolute&lt;/code&gt; tag. Migration is a one-liner:&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;sed -i &lt;span class="s2"&gt;&amp;#34;s/noble/resolute/g&amp;#34;&lt;/span&gt; Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All existing image flavors — including &lt;a href="https://devblogs.microsoft.com/dotnet/announcing-dotnet-chiseled-containers/"&gt;Chiseled&lt;/a&gt; — are available. The Chiseled images are still my go-to for production: minimal attack surface, no shell, no package manager, just the runtime. Update the tag and rebuild.&lt;/p&gt;
&lt;h2 id="native-aot-3ms-startup-14mb-binary"&gt;Native AOT: 3ms startup, 1.4MB binary&lt;/h2&gt;
&lt;p&gt;Ubuntu 26.04 ships a dedicated AOT package:&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;apt install -y dotnet-sdk-aot-10.0 clang
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s what you get when you publish a simple app:&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;dotnet publish app.cs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# artifacts/app/app — 1.4MB native binary&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Startup time:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;real 0m0.003s
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;3 milliseconds. For a full ASP.NET Core web service, the self-contained binary is around 13MB. That&amp;rsquo;s a completely self-contained deployable with no runtime dependency whatsoever.&lt;/p&gt;
&lt;p&gt;For cloud-native workloads where cold-start time matters — Functions, containers, serverless — this is a legitimate game changer.&lt;/p&gt;
&lt;h2 id="what-changed-in-ubuntu-2604-that-affects-net"&gt;What changed in Ubuntu 26.04 that affects .NET&lt;/h2&gt;
&lt;p&gt;Three things worth knowing:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Linux 7.0&lt;/strong&gt; — The .NET team will start Linux 7.0 testing once they get 26.04 VMs in the lab. No breaking changes expected, but they&amp;rsquo;ll verify.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Post-quantum cryptography&lt;/strong&gt; — Ubuntu 26.04 introduces PQC support, and .NET 10 &lt;a href="https://devblogs.microsoft.com/dotnet/post-quantum-cryptography-in-dotnet/"&gt;added post-quantum cryptography APIs&lt;/a&gt; as well. Good alignment.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;cgroup v1 removed&lt;/strong&gt; — Ubuntu 26.04 drops cgroup v1. .NET added cgroup v2 support years ago, so this is a non-event. But if you&amp;rsquo;re on an older runtime, double-check.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="need-net-8-or-9"&gt;Need .NET 8 or 9?&lt;/h2&gt;
&lt;p&gt;Those are available via the &lt;a href="https://launchpad.net/~dotnet/&amp;#43;archive/ubuntu/backports"&gt;dotnet-backports PPA&lt;/a&gt;:&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;apt install -y software-properties-common
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;add-apt-repository ppa:dotnet/backports
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apt install -y dotnet-sdk-8.0 &lt;span class="c1"&gt;# or dotnet-sdk-9.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Support is &amp;ldquo;best-effort&amp;rdquo; — not the same guarantee as the LTS package in the main archive — but the packages are there and they work.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Every two years, the Ubuntu LTS + .NET LTS alignment gives you a solid, long-support foundation for production workloads. Ubuntu 26.04 with .NET 10 is that foundation for the next cycle.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re containerizing .NET apps, update your Dockerfiles. If you&amp;rsquo;re deploying on bare metal or VMs, &lt;code&gt;apt install dotnet-sdk-10.0&lt;/code&gt; and you&amp;rsquo;re done.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="https://devblogs.microsoft.com/dotnet/whats-new-for-dotnet-in-ubuntu-2604/"&gt;full post from Richard Lander&lt;/a&gt; for the complete installation walkthrough and container details.&lt;/p&gt;</content:encoded></item><item><title>Azure MCP Server Is Now a .mcpb — Install It Without Any Runtime</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-mcp-server-mcpb-no-runtime-install/</link><pubDate>Sat, 25 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-mcp-server-mcpb-no-runtime-install/</guid><description>The Azure MCP Server is now available as an MCP Bundle (.mcpb) — download it, drag it into Claude Desktop, and you're done. No Node.js, Python, or .NET runtime required.</description><content:encoded>&lt;p&gt;You know what was annoying about setting up MCP servers? You needed a runtime. Node.js for the npm version, Python for pip/uvx, .NET SDK for the dotnet flavor, Docker if you wanted containers. Just to get a tool connected to your AI client.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://devblogs.microsoft.com/azure-sdk/azure-mcp-server-mcpb-support/"&gt;Azure MCP Server just changed that&lt;/a&gt;. It&amp;rsquo;s now available as an &lt;code&gt;.mcpb&lt;/code&gt; — an MCP Bundle — and the setup is drag-and-drop.&lt;/p&gt;
&lt;h2 id="whats-an-mcp-bundle"&gt;What&amp;rsquo;s an MCP Bundle?&lt;/h2&gt;
&lt;p&gt;Think of it like a VS Code extension (&lt;code&gt;.vsix&lt;/code&gt;) or a browser extension (&lt;code&gt;.crx&lt;/code&gt;), but for MCP servers. A &lt;code&gt;.mcpb&lt;/code&gt; file is a self-contained ZIP archive that includes the server binary and all its dependencies. Everything needed to run on your platform, packaged together.&lt;/p&gt;
&lt;p&gt;The end result: you download one file, open it in a supported client, and the server runs. No runtime to install, no &lt;code&gt;package.json&lt;/code&gt; to manage, no version conflicts.&lt;/p&gt;
&lt;h2 id="how-to-install-it"&gt;How to install it&lt;/h2&gt;
&lt;p&gt;Three steps:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Download the bundle for your platform&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Go to the &lt;a href="https://github.com/microsoft/mcp/releases?q=Azure.Mcp.Server"&gt;GitHub Releases page&lt;/a&gt; and grab the &lt;code&gt;.mcpb&lt;/code&gt; file for your OS and architecture. Make sure you pick the right one — &lt;code&gt;osx-arm64&lt;/code&gt; for Apple Silicon, &lt;code&gt;osx-x64&lt;/code&gt; for Intel Mac, etc.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Install in Claude Desktop&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The easiest way: drag and drop the &lt;code&gt;.mcpb&lt;/code&gt; file into the Claude Desktop window while you&amp;rsquo;re on the Extensions settings page (&lt;code&gt;☰ → File → Settings → Extensions&lt;/code&gt;). Review the server details, click Install, confirm. Done.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: You can also set Claude Desktop as the default app for &lt;code&gt;.mcpb&lt;/code&gt; files and double-click to install.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;3. Authenticate to Azure&lt;/strong&gt;&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;az login
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. The Azure MCP Server uses your existing Azure credentials.&lt;/p&gt;
&lt;h2 id="what-you-can-do-with-it"&gt;What you can do with it&lt;/h2&gt;
&lt;p&gt;Once installed, you have access to 100+ Azure service tools directly from your AI client:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Query and manage Cosmos DB, Storage, Key Vault, App Service, Foundry&lt;/li&gt;
&lt;li&gt;Generate &lt;code&gt;az&lt;/code&gt; CLI commands for any task&lt;/li&gt;
&lt;li&gt;Create Bicep and Terraform templates&lt;/li&gt;
&lt;li&gt;Get architecture recommendations and diagnostics&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Try prompts like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;List all resource groups in my subscription&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Generate a Bicep template for a web app with a SQL database&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;What Cosmos DB databases do I have?&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Show me the secrets in my Key Vault named my-vault&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="which-install-method-should-you-use"&gt;Which install method should you use?&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.mcpb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Claude Desktop users who want zero-config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VS Code Extension&lt;/td&gt;
&lt;td&gt;Developers working in VS Code + GitHub Copilot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;npm/npx&lt;/td&gt;
&lt;td&gt;Developers who already have Node.js&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pip/uvx&lt;/td&gt;
&lt;td&gt;Python developers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;td&gt;CI/CD pipelines and containers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;All methods give you the same tools. The &lt;code&gt;.mcpb&lt;/code&gt; is just the most frictionless path for Claude Desktop users.&lt;/p&gt;
&lt;h2 id="why-this-matters"&gt;Why this matters&lt;/h2&gt;
&lt;p&gt;MCP servers are genuinely useful — they let AI clients interact with external systems in a structured way. But the setup friction has been a real barrier, especially for users who aren&amp;rsquo;t developers or who just don&amp;rsquo;t want to manage runtimes for every tool they install.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;.mcpb&lt;/code&gt; format feels like the right direction. It&amp;rsquo;s the same principle as VS Code extensions or browser extensions: one file, platform-native binary, install and go.&lt;/p&gt;
&lt;p&gt;If the MCP ecosystem keeps moving this direction, connecting AI clients to services will get a lot simpler.&lt;/p&gt;
&lt;h2 id="get-started"&gt;Get started&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Download&lt;/strong&gt;: &lt;a href="https://github.com/microsoft/mcp/releases?q=Azure.Mcp.Server-"&gt;GitHub Releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Repo&lt;/strong&gt;: &lt;a href="https://aka.ms/azmcp"&gt;aka.ms/azmcp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docs&lt;/strong&gt;: &lt;a href="https://aka.ms/azmcp/docs"&gt;aka.ms/azmcp/docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Check the &lt;a href="https://devblogs.microsoft.com/azure-sdk/azure-mcp-server-mcpb-support/"&gt;full post&lt;/a&gt; for troubleshooting tips and a comparison of all install methods.&lt;/p&gt;</content:encoded></item><item><title>Azure SDK April 2026: AI Foundry 2.0 and What .NET Developers Should Know</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-sdk-april-2026-ai-foundry-2-stable/</link><pubDate>Sat, 25 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-sdk-april-2026-ai-foundry-2-stable/</guid><description>The April 2026 Azure SDK release ships Azure.AI.Projects 2.0.0 stable with significant breaking changes, critical Cosmos DB security fixes, and a wave of new Provisioning libraries for .NET.</description><content:encoded>&lt;p&gt;Monthly SDK releases are often easy to skip. This one has a few things worth paying attention to — especially if you&amp;rsquo;re building with AI Foundry, Cosmos DB in Java, or doing infrastructure provisioning from .NET code.&lt;/p&gt;
&lt;h2 id="azureaiprojects-200--breaking-changes-that-make-sense"&gt;Azure.AI.Projects 2.0.0 — Breaking Changes That Make Sense&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Azure.AI.Projects&lt;/code&gt; NuGet package reaches stable 2.0.0 with some significant architectural changes. If you&amp;rsquo;re already using the preview, here&amp;rsquo;s what changed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Namespace splits&lt;/strong&gt;: Evaluations moved to &lt;code&gt;Azure.AI.Projects.Evaluation&lt;/code&gt;, memory operations moved to &lt;code&gt;Azure.AI.Projects.Memory&lt;/code&gt;. Your using statements will need updating.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Renamed types&lt;/strong&gt;: &lt;code&gt;Insights&lt;/code&gt; → &lt;code&gt;ProjectInsights&lt;/code&gt;, &lt;code&gt;Schedules&lt;/code&gt; → &lt;code&gt;ProjectSchedules&lt;/code&gt;, &lt;code&gt;Evaluators&lt;/code&gt; → &lt;code&gt;ProjectEvaluators&lt;/code&gt;, &lt;code&gt;Trigger&lt;/code&gt; → &lt;code&gt;ScheduleTrigger&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Naming conventions&lt;/strong&gt;: Boolean properties now follow the &lt;code&gt;Is*&lt;/code&gt; convention consistently&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are the kinds of breaking changes that hurt once and then feel right forever. If you&amp;rsquo;ve been building on the preview, update your imports and let the compiler point you to the rest.&lt;/p&gt;
&lt;p&gt;The good news: it&amp;rsquo;s stable. You can actually rely on this API now.&lt;/p&gt;
&lt;h2 id="cosmos-db-java-critical-security-fix-rce"&gt;Cosmos DB Java: Critical Security Fix (RCE)&lt;/h2&gt;
&lt;p&gt;This one is serious. The Java Cosmos DB library (&lt;code&gt;azure-cosmos&lt;/code&gt;) version 4.79.0 includes a critical security fix for a &lt;strong&gt;Remote Code Execution vulnerability (CWE-502)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The issue was Java deserialization in &lt;code&gt;CosmosClientMetadataCachesSnapshot&lt;/code&gt;, &lt;code&gt;AsyncCache&lt;/code&gt;, and &lt;code&gt;DocumentCollection&lt;/code&gt;. The fix replaces Java deserialization with JSON-based serialization, eliminating the entire class of deserialization attacks.&lt;/p&gt;
&lt;p&gt;If you have any Java services using Azure Cosmos DB, update to 4.79.0 immediately. This isn&amp;rsquo;t optional.&lt;/p&gt;
&lt;h2 id="new-provisioning-libraries-for-net"&gt;New Provisioning Libraries for .NET&lt;/h2&gt;
&lt;p&gt;A wave of stable Provisioning libraries hit 1.0.0 this month — these are the libraries that let you define Azure infrastructure in C# code rather than ARM templates or Bicep:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/Azure.Provisioning.Network/1.0.0"&gt;Azure.Provisioning.Network 1.0.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/Azure.Provisioning.PrivateDns/1.0.0"&gt;Azure.Provisioning.PrivateDns 1.0.0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Several more are in beta.1, covering API Management, Batch, Compute, Monitor, MySQL, and Security Center. If you&amp;rsquo;re doing infrastructure-as-code from .NET — particularly with Aspire deployments — these libraries are your entry point.&lt;/p&gt;
&lt;h2 id="azure-ai-agents-java-200-ga"&gt;Azure AI Agents Java: 2.0.0 GA&lt;/h2&gt;
&lt;p&gt;The Java Azure AI Agents library also reaches general availability this month. The key breaking changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Several enum types converted to &lt;code&gt;ExpandableStringEnum&lt;/code&gt;-based classes (more flexible for new values)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*Param&lt;/code&gt; model classes renamed to &lt;code&gt;*Parameter&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MCPToolConnectorId&lt;/code&gt; → &lt;code&gt;McpToolConnectorId&lt;/code&gt; (consistent casing)&lt;/li&gt;
&lt;li&gt;New convenience overload for &lt;code&gt;beginUpdateMemories&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;The headline for .NET developers this month is &lt;code&gt;Azure.AI.Projects 2.0.0&lt;/code&gt; hitting stable — if you&amp;rsquo;re building with AI Foundry, now&amp;rsquo;s the time to pin to stable and update your imports. For Java shops using Cosmos DB, the security update is urgent.&lt;/p&gt;
&lt;p&gt;Full release notes at &lt;a href="https://aka.ms/azsdk/releases"&gt;aka.ms/azsdk/releases&lt;/a&gt;. Original post: &lt;a href="https://devblogs.microsoft.com/azure-sdk/azure-sdk-release-april-2026/"&gt;Azure SDK Release (April 2026)&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>CodeAct in Agent Framework: How to Cut Your Agent's Latency in Half</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/codeact-agent-framework-hyperlight-50-percent-faster/</link><pubDate>Sat, 25 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/codeact-agent-framework-hyperlight-50-percent-faster/</guid><description>CodeAct collapses multi-step tool chains into a single sandboxed code block — cutting latency by 52% and token usage by 64%. Here's what it means for your agents and when to reach for it.</description><content:encoded>&lt;p&gt;There&amp;rsquo;s a moment in every agent project where you look at the trace and think: &amp;ldquo;why is this taking so long?&amp;rdquo; The model is fine. The tools work. But there are seven round trips to get a result you could compute in one shot.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s exactly the problem CodeAct solves — and the &lt;a href="https://devblogs.microsoft.com/agent-framework/codeact-with-hyperlight/"&gt;Agent Framework team just shipped alpha support for it&lt;/a&gt; via a new &lt;code&gt;agent-framework-hyperlight&lt;/code&gt; package.&lt;/p&gt;
&lt;h2 id="what-is-codeact"&gt;What is CodeAct?&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://arxiv.org/abs/2402.01030"&gt;CodeAct pattern&lt;/a&gt; is elegantly simple: instead of giving the model a list of tools and letting it call them one by one, you give it a single &lt;code&gt;execute_code&lt;/code&gt; tool and let it express the &lt;em&gt;entire plan&lt;/em&gt; as a short Python program. The agent writes the code once, the sandbox runs it, and you get back a single consolidated result.&lt;/p&gt;
&lt;p&gt;A five-step plan that used to be five model turns becomes one &lt;code&gt;execute_code&lt;/code&gt; turn containing a Python script that calls your tools via &lt;code&gt;call_tool(...)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The benchmark in the repo makes this concrete. Eight users, dozens of orders, five tools (list users, get orders, discount rate, tax rate, compute line total). Same model, same tools, same prompt — just different wiring:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Wiring&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Tokens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Traditional&lt;/td&gt;
&lt;td&gt;27.81s&lt;/td&gt;
&lt;td&gt;6,890&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CodeAct&lt;/td&gt;
&lt;td&gt;13.23s&lt;/td&gt;
&lt;td&gt;2,489&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Improvement&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;52.4%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;63.9%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That&amp;rsquo;s not a micro-benchmark. That&amp;rsquo;s a realistic workload with real orchestration overhead.&lt;/p&gt;
&lt;h2 id="the-safety-piece-hyperlight-micro-vms"&gt;The safety piece: Hyperlight micro-VMs&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the thing that made me actually excited about this: safety has historically been CodeAct&amp;rsquo;s Achilles heel. If you&amp;rsquo;re running model-generated code, where exactly is it running? Against your process? In a shared container?&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;agent-framework-hyperlight&lt;/code&gt; package solves this with &lt;a href="https://github.com/hyperlight-dev/hyperlight"&gt;Hyperlight&lt;/a&gt; micro-VMs. Every single &lt;code&gt;execute_code&lt;/code&gt; call gets its own freshly created micro-VM — with its own memory, no host filesystem access beyond what you explicitly mount, and no network access beyond the domains you allow. Startup is measured in milliseconds. The isolation is basically free.&lt;/p&gt;
&lt;p&gt;Your tools still run on the host (they&amp;rsquo;re your code, with your access). The model-generated &lt;em&gt;glue&lt;/em&gt; — the Python that decides which tools to call and in what order — runs sandboxed. That&amp;rsquo;s the right split.&lt;/p&gt;
&lt;h2 id="wiring-it-up"&gt;Wiring it up&lt;/h2&gt;
&lt;p&gt;The minimal setup is straightforward:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;agent_framework&lt;/span&gt; &lt;span class="kn"&gt;import&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;tool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;agent_framework_hyperlight&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HyperlightCodeActProvider&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="nd"&gt;@tool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_weather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;str&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="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Return the current weather for a city.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;city&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;temperature_c&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;21.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;conditions&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;partly cloudy&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;codeact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HyperlightCodeActProvider&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;get_weather&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;approval_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;never_require&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Agent&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;client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client&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="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;CodeActAgent&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;instructions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&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;context_providers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;codeact&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&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="s2"&gt;&amp;#34;Get the weather for Seattle and Amsterdam and compare them.&amp;#34;&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;p&gt;The provider registers &lt;code&gt;execute_code&lt;/code&gt; on every run and injects the CodeAct instructions into the system prompt automatically. You don&amp;rsquo;t need to write a custom prompt fragment.&lt;/p&gt;
&lt;h2 id="mixing-codeact-with-approval-gated-tools"&gt;Mixing CodeAct with approval-gated tools&lt;/h2&gt;
&lt;p&gt;This is where it gets interesting. Not every tool should run inside the sandbox without approval. You might want to gate &lt;code&gt;send_email&lt;/code&gt; or &lt;code&gt;charge_credit_card&lt;/code&gt; individually. The framework handles this cleanly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;approval_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;always_require&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&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="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Send an email. Requires approval on every call.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&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;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Agent&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;client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client&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="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;MixedToolsAgent&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;instructions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&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;context_providers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;codeact&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;# invoked directly, approval-gated&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;p&gt;Tools on the provider → the model reaches them via &lt;code&gt;call_tool(...)&lt;/code&gt; inside the sandbox, cheap and chainable.&lt;br&gt;
Tools on the agent directly → the model calls them as first-class tool calls, approval applies individually.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s a clean split: chainable data-lookup tools go through CodeAct, side-effect tools stay on the agent.&lt;/p&gt;
&lt;h2 id="when-to-use-codeact-and-when-not-to"&gt;When to use CodeAct (and when not to)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Reach for CodeAct when:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The task chains many small tool calls (lookups, joins, computations, formatting)&lt;/li&gt;
&lt;li&gt;You care about latency and token cost&lt;/li&gt;
&lt;li&gt;You want strong per-call isolation on model-generated code by default&lt;/li&gt;
&lt;li&gt;Tools are cheap and safe to invoke in sequence&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Stick with traditional tool-calling when:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The agent only makes one or two tool calls per turn&lt;/li&gt;
&lt;li&gt;Each tool has side effects you want approved individually&lt;/li&gt;
&lt;li&gt;Tool descriptions are sparse or ambiguous — CodeAct relies on good docstrings&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last point matters. Because the model writes Python that calls your tools by name, docstrings and parameter annotations become part of the contract the model reasons about. Weak descriptions hurt CodeAct more than traditional tool-calling.&lt;/p&gt;
&lt;h2 id="try-it-now"&gt;Try it now&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;pip install agent-framework-hyperlight --pre
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# or&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv add --prerelease&lt;span class="o"&gt;=&lt;/span&gt;allow agent-framework-hyperlight
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Samples are under &lt;a href="https://github.com/microsoft/agent-framework/tree/main/python/packages/hyperlight/samples"&gt;&lt;code&gt;python/packages/hyperlight/samples/&lt;/code&gt;&lt;/a&gt;. The &lt;a href="https://github.com/microsoft/agent-framework/blob/main/python/packages/hyperlight/samples/codeact_benchmark.py"&gt;benchmark sample&lt;/a&gt; is the best place to start — run it against your own tools to see if the wins apply to your workload.&lt;/p&gt;
&lt;p&gt;Worth noting: Linux and Windows are supported today. macOS support is on the way. A .NET counterpart is also coming, so if you&amp;rsquo;re on C#, keep an eye on the repo.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;CodeAct isn&amp;rsquo;t magic — it&amp;rsquo;s a sensible pattern that was just too risky to use without proper sandboxing. Hyperlight changes that equation. Per-call micro-VM isolation, millisecond startup, 50%+ latency improvement on the right workloads. That&amp;rsquo;s a combination worth experimenting with.&lt;/p&gt;
&lt;p&gt;Check the &lt;a href="https://devblogs.microsoft.com/agent-framework/codeact-with-hyperlight/"&gt;full post on the Agent Framework blog&lt;/a&gt; for deeper coverage on filesystem mounts, network policy, and the standalone &lt;code&gt;HyperlightExecuteCodeTool&lt;/code&gt; wiring.&lt;/p&gt;</content:encoded></item><item><title>GPT-5.5 Is Here and It's Coming to Azure Foundry — What .NET Developers Need to Know</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/gpt-55-foundry-ga-what-dotnet-developers-need-to-know/</link><pubDate>Sat, 25 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/gpt-55-foundry-ga-what-dotnet-developers-need-to-know/</guid><description>GPT-5.5 is generally available in Microsoft Foundry. Here's the progression from GPT-5 to 5.5, what's actually improved, and how to start using it in your agents today.</description><content:encoded>&lt;p&gt;Microsoft just announced that &lt;a href="https://azure.microsoft.com/en-us/blog/openais-gpt-5-5-in-microsoft-foundry-frontier-intelligence-on-an-enterprise-ready-platform/"&gt;GPT-5.5 is generally available in Microsoft Foundry&lt;/a&gt;. If you&amp;rsquo;ve been building agents on Azure, this is the update you&amp;rsquo;ve been waiting for.&lt;/p&gt;
&lt;p&gt;Let me break down what actually changed and why it matters for developers building on this stack.&lt;/p&gt;
&lt;h2 id="the-gpt-5-progression"&gt;The GPT-5 progression&lt;/h2&gt;
&lt;p&gt;It helps to understand the arc. This isn&amp;rsquo;t just a version bump:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GPT-5&lt;/strong&gt;: unified reasoning and speed into a single system&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GPT-5.4&lt;/strong&gt;: stronger multi-step reasoning, early agentic capabilities for enterprise use&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GPT-5.5&lt;/strong&gt;: deeper long-context reasoning, more reliable agentic execution, improved computer-use accuracy, better token efficiency&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each step has been deliberately aimed at production agentic workloads. GPT-5.5 continues that arc with a specific focus on sustained, high-stakes professional workflows — not just one-shot queries.&lt;/p&gt;
&lt;h2 id="whats-actually-different"&gt;What&amp;rsquo;s actually different&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Improved agentic coding&lt;/strong&gt;: GPT-5.5 holds context across large codebases, can diagnose architectural-level failures, and anticipates downstream testing requirements. That last point is interesting — the model reasons about &lt;em&gt;what else&lt;/em&gt; a fix affects before making a move. Less back-and-forth to get to a working result.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Token efficiency&lt;/strong&gt;: Higher-quality outputs with fewer tokens and fewer retries. This translates directly to lower cost and latency for production deployments. If you&amp;rsquo;re running agents at scale, this compounds fast.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Long-context analysis&lt;/strong&gt;: Handles extensive documents, codebases, and multi-session histories without losing the thread. For agentic workflows that maintain large working state, this matters.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also a &lt;strong&gt;GPT-5.5 Pro&lt;/strong&gt; variant for the most demanding enterprise workloads — deeper reasoning, higher cost.&lt;/p&gt;
&lt;h2 id="pricing"&gt;Pricing&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Input ($/M tokens)&lt;/th&gt;
&lt;th&gt;Cached Input&lt;/th&gt;
&lt;th&gt;Output ($/M tokens)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.5&lt;/td&gt;
&lt;td&gt;$5.00&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;td&gt;$30.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.5 Pro&lt;/td&gt;
&lt;td&gt;$30.00&lt;/td&gt;
&lt;td&gt;$3.00&lt;/td&gt;
&lt;td&gt;$180.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;GPT-5.5 is priced at the same input rate as GPT-5 but the token efficiency improvements mean you&amp;rsquo;re actually paying less per useful output. Worth running a benchmark on your specific workload before committing.&lt;/p&gt;
&lt;h2 id="why-foundry-matters-here"&gt;Why Foundry matters here&lt;/h2&gt;
&lt;p&gt;Access to a frontier model is just the starting point. What matters for .NET developers is how you operationalize it.&lt;/p&gt;
&lt;p&gt;Foundry Agent Service lets you define agents in YAML or wire them up with Microsoft Agent Framework, GitHub Copilot SDK, LangGraph, or OpenAI Agents SDK — and run them as isolated hosted agents with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A persistent filesystem&lt;/li&gt;
&lt;li&gt;A distinct Microsoft Entra identity&lt;/li&gt;
&lt;li&gt;Scale-to-zero pricing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One command to deploy. No infrastructure to manage. Your agents get GPT-5.5 as the model underneath.&lt;/p&gt;
&lt;h2 id="getting-started"&gt;Getting started&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re already using Azure AI Foundry, GPT-5.5 shows up as a new model option. Point your client at it and you&amp;rsquo;re done:&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="c1"&gt;// C# — just update the model name&lt;/span&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;aiProjectClient&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 class="s"&gt;&amp;#34;gpt-5.5&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&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 class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;MyAgent&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;If you haven&amp;rsquo;t tried Foundry yet, &lt;a href="https://ai.azure.com/"&gt;ai.azure.com&lt;/a&gt; is where to start. The model catalog has a direct link to try GPT-5.5.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;GPT-5.5 is a real step forward for production agentic workloads. The combination of better long-context handling, improved agentic execution, and token efficiency makes it worth evaluating for anything you&amp;rsquo;re running at scale.&lt;/p&gt;
&lt;p&gt;The frontier is moving fast. Keep building.&lt;/p&gt;
&lt;p&gt;See the &lt;a href="https://azure.microsoft.com/en-us/blog/openais-gpt-5-5-in-microsoft-foundry-frontier-intelligence-on-an-enterprise-ready-platform/"&gt;full announcement&lt;/a&gt; for the complete feature breakdown and enterprise details.&lt;/p&gt;</content:encoded></item><item><title>VS Code 1.118: Copilot CLI Gets Session Names, Model Badges, and TypeScript 7.0 Nightly Opt-In</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/vscode-1-118-copilot-cli-session-names-model-badge/</link><pubDate>Sat, 25 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/vscode-1-118-copilot-cli-session-names-model-badge/</guid><description>Visual Studio Code 1.118 is a focused release centered on Copilot CLI improvements — session naming, model badges, auto model selection, and a new option to opt into TypeScript 7.0 nightlies.</description><content:encoded>&lt;p&gt;&lt;a href="https://code.visualstudio.com/updates/v1_118"&gt;Visual Studio Code 1.118&lt;/a&gt; is a smaller focused release — mostly Copilot CLI refinements — but there are a few things worth noting if you use the Agents app or TypeScript heavily.&lt;/p&gt;
&lt;h2 id="copilot-cli-sessions-get-real-names"&gt;Copilot CLI: sessions get real names&lt;/h2&gt;
&lt;p&gt;The Copilot CLI SDK session-title APIs are now used as the source of truth for session names. Previously you&amp;rsquo;d get auto-generated labels; now sessions surface the actual name from the SDK. Small quality-of-life improvement but it makes navigating multiple agent sessions a lot less confusing.&lt;/p&gt;
&lt;h2 id="switch-sessions-faster-with-keyboard-shortcuts"&gt;Switch sessions faster with keyboard shortcuts&lt;/h2&gt;
&lt;p&gt;The Agents app now has &lt;code&gt;Ctrl+1&lt;/code&gt;, &lt;code&gt;Ctrl+2&lt;/code&gt;, etc. bound to quickly switch between sessions. If you&amp;rsquo;re running multiple Copilot CLI sessions in parallel (which, if you&amp;rsquo;re doing agent-heavy work, you probably are), this removes a lot of mouse clicking.&lt;/p&gt;
&lt;h2 id="model-badges-in-chat"&gt;Model badges in chat&lt;/h2&gt;
&lt;p&gt;Copilot CLI responses in the chat panel now show a model badge — you can see at a glance which model handled each request. Useful when you&amp;rsquo;re experimenting with different models in the same session.&lt;/p&gt;
&lt;h2 id="auto-model-selection-lands-in-copilot-cli"&gt;Auto model selection lands in Copilot CLI&lt;/h2&gt;
&lt;p&gt;The auto model selection feature — previously available in other parts of Copilot — now works in the Copilot CLI agent too. Let the system pick the best model for the task rather than manually switching.&lt;/p&gt;
&lt;h2 id="typescript-70-nightly-opt-in"&gt;TypeScript 7.0 nightly opt-in&lt;/h2&gt;
&lt;p&gt;This one&amp;rsquo;s interesting if you&amp;rsquo;re on the TypeScript cutting edge: you can now opt in to testing TypeScript 7.0 nightlies directly from VS Code settings. TypeScript 7.0 is a major release with significant changes (the &lt;a href="https://devblogs.microsoft.com/typescript/announcing-typescript-7-0-beta/"&gt;beta dropped a few days ago&lt;/a&gt;), and the opt-in path makes it easy to test without changing your global TypeScript install.&lt;/p&gt;
&lt;h2 id="under-the-hood-node-pty-cleanup"&gt;Under the hood: &lt;code&gt;node-pty&lt;/code&gt; cleanup&lt;/h2&gt;
&lt;p&gt;The Copilot CLI SDK now resolves &lt;code&gt;node-pty&lt;/code&gt; from VS Code via &lt;code&gt;hostRequire&lt;/code&gt; instead of copying binaries into the SDK&amp;rsquo;s prebuilds folder at build time. This is an internal change but it simplifies distribution and means fewer things that can go wrong at runtime.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Not a splashy release, but the Copilot CLI improvements add up — especially session management for anyone doing agent-heavy development. The TypeScript 7.0 nightly opt-in is a nice touch for those who like living on the edge.&lt;/p&gt;
&lt;p&gt;See the &lt;a href="https://code.visualstudio.com/updates/v1_118"&gt;full release notes&lt;/a&gt; for the complete change list.&lt;/p&gt;</content:encoded></item><item><title>Where Does Your Agent Remember Things? A Practical Guide to Chat History Storage</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/chat-history-storage-patterns-agent-framework/</link><pubDate>Sat, 25 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/chat-history-storage-patterns-agent-framework/</guid><description>Service-managed or client-managed? Linear or forking? The architectural decision that shapes what your AI agent can actually do — with code examples in C# and Python.</description><content:encoded>&lt;p&gt;When you build an AI agent, you spend most of your energy on the model, the tools, and the prompts. The question of &lt;em&gt;where the conversation history lives&lt;/em&gt; feels like an implementation detail — but it&amp;rsquo;s actually one of the most important architectural decisions you&amp;rsquo;ll make.&lt;/p&gt;
&lt;p&gt;It determines whether users can branch conversations, undo responses, resume sessions after a restart, and whether your data ever leaves your infrastructure. The &lt;a href="https://devblogs.microsoft.com/agent-framework/chat-history-storage-patterns-in-microsoft-agent-framework/"&gt;Agent Framework team published a deep dive on this&lt;/a&gt; and it&amp;rsquo;s worth understanding the full landscape.&lt;/p&gt;
&lt;h2 id="two-fundamental-patterns"&gt;Two fundamental patterns&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Service-managed&lt;/strong&gt;: the AI service stores the conversation state. Your app holds a reference (a thread ID, a response ID) and the service automatically includes relevant history on each request. Simpler to set up. Less control.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Client-managed&lt;/strong&gt;: your app maintains the full history and sends relevant messages with every request. The service is stateless. You control everything — what gets sent, how it&amp;rsquo;s compressed, where it lives.&lt;/p&gt;
&lt;p&gt;Neither is universally better. The right choice depends on what you&amp;rsquo;re building.&lt;/p&gt;
&lt;h2 id="service-managed-linear-vs-forking"&gt;Service-managed: linear vs forking&lt;/h2&gt;
&lt;p&gt;Not all service-managed storage is the same. There are two distinct models:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Linear (single-threaded)&lt;/strong&gt;: messages form an ordered sequence. You can append, but you can&amp;rsquo;t branch. This is the traditional chat model — used by Foundry Prompt Agents and the now-deprecated OpenAI Assistants API. Great for chatbots and support agents. Terrible if you want &amp;ldquo;try again&amp;rdquo; or parallel exploration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Forking-capable&lt;/strong&gt;: each response has a unique ID, and new requests can reference &lt;em&gt;any&lt;/em&gt; previous response as the continuation point. This is what the Responses API (Microsoft Foundry, Azure OpenAI, OpenAI) supports. Users can branch conversations, build &amp;ldquo;undo&amp;rdquo; flows, explore multiple answer paths.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re building any kind of agentic workflow where multiple paths might be explored, forking is a capability you want.&lt;/p&gt;
&lt;h2 id="client-managed-you-own-the-complexity"&gt;Client-managed: you own the complexity&lt;/h2&gt;
&lt;p&gt;When the service doesn&amp;rsquo;t store history, your app does everything:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Context window management&lt;/strong&gt; — you can&amp;rsquo;t send unlimited history. You need truncation, sliding windows, summarization, or tool-call collapse strategies.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Persistence&lt;/strong&gt; — in-memory works for demos. Production needs a database, Redis, or blob storage.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Privacy&lt;/strong&gt; — conversation data never leaves your infrastructure unless you explicitly send it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The upside on privacy is real. For sensitive applications where you can&amp;rsquo;t have conversation history sitting on a third-party server, client-managed is the only option.&lt;/p&gt;
&lt;p&gt;Agent Framework ships built-in compaction strategies for all the common patterns, so you don&amp;rsquo;t have to build them from scratch. But you do need to choose and configure the right one.&lt;/p&gt;
&lt;h2 id="how-agent-framework-abstracts-this"&gt;How Agent Framework abstracts this&lt;/h2&gt;
&lt;p&gt;The beauty of the framework is that your agent invocation code stays the same regardless of which storage model you&amp;rsquo;re using. The &lt;code&gt;AgentSession&lt;/code&gt; handles the underlying differences.&lt;/p&gt;
&lt;p&gt;In C#:&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="c1"&gt;// Works with Chat Completions (client-managed)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// AND with Responses API (service-managed)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// The session handles the details.&lt;/span&gt;
&lt;/span&gt;&lt;/span&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 class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;first&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;My name is Alice.&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 class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;second&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 is my name?&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In Python:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_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="n"&gt;first&lt;/span&gt; &lt;span class="o"&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;My name is Alice.&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="o"&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="n"&gt;second&lt;/span&gt; &lt;span class="o"&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;What is my name?&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="o"&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When you switch from OpenAI Chat Completions to the Responses API, you change the client configuration — not the agent invocation code.&lt;/p&gt;
&lt;h2 id="the-responses-api-is-uniquely-flexible"&gt;The Responses API is uniquely flexible&lt;/h2&gt;
&lt;p&gt;Most providers have a fixed storage model. The Responses API is the exception — it&amp;rsquo;s configurable via the &lt;code&gt;store&lt;/code&gt; parameter:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;store=true&lt;/code&gt; (default)&lt;/strong&gt;: service stores each response, supports forking via response IDs. Service handles compaction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;store=false&lt;/code&gt;&lt;/strong&gt;: service is stateless, Agent Framework manages history client-side. You control compaction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conversations API&lt;/strong&gt;: linear thread model on top of Responses. Pass a conversation ID instead of a response ID.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;rsquo;s the client-managed mode in practice (C#):&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;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;OpenAIClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;lt;your_api_key&amp;gt;&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 class="n"&gt;GetResponseClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;gpt-5.4-mini&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 class="n"&gt;AsIChatClientWithStoredOutputDisabled&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 class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ChatClientAgentOptions&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;ChatOptions&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="p"&gt;{&lt;/span&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;ChatHistoryProvider&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;InMemoryChatHistoryProvider&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;p&gt;And in Python:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Agent&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;client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;OpenAIChatClient&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="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;StatelessAgent&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;instructions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&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;default_options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;store&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;False&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;context_providers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;InMemoryHistoryProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;memory&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;load_messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&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;p&gt;Swap &lt;code&gt;InMemoryHistoryProvider&lt;/code&gt; for your &lt;code&gt;DatabaseHistoryProvider&lt;/code&gt; when you&amp;rsquo;re ready for production persistence.&lt;/p&gt;
&lt;h2 id="provider-quick-reference"&gt;Provider quick reference&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Storage&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Compaction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OpenAI / Azure OpenAI Chat Completions&lt;/td&gt;
&lt;td&gt;Client&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;You&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Foundry Agent Service&lt;/td&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;td&gt;Linear&lt;/td&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Responses API (default)&lt;/td&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;td&gt;Forking&lt;/td&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Responses API (&lt;code&gt;store=false&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Client&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;You&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anthropic Claude, Ollama&lt;/td&gt;
&lt;td&gt;Client&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;You&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="how-to-choose"&gt;How to choose&lt;/h2&gt;
&lt;p&gt;Start with these questions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Do you need conversation branching or &amp;ldquo;undo&amp;rdquo;?&lt;/strong&gt; → Forking service-managed (Responses API)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do you need full data sovereignty?&lt;/strong&gt; → Client-managed, with a database-backed provider&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is this a simple chatbot or support flow?&lt;/strong&gt; → Service-managed linear is fine&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do you need to migrate between providers later?&lt;/strong&gt; → Client-managed gives you portability&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The most important thing: don&amp;rsquo;t default to whatever is easiest to start with and forget to revisit it. Changing storage patterns after launch is painful.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Chat history storage shapes what your agents can actually do — not just in demos but in production, under real user behavior. Agent Framework&amp;rsquo;s abstractions let you evolve your choice without rewriting your application logic, which is genuinely useful when you&amp;rsquo;re still figuring out the right model.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="https://devblogs.microsoft.com/agent-framework/chat-history-storage-patterns-in-microsoft-agent-framework/"&gt;full post&lt;/a&gt; for the complete decision tree, the Conversations API walkthrough, and the compaction strategy details.&lt;/p&gt;</content:encoded></item><item><title>Aspire 13.2 Gets Bun, Better Containers, and Less Debugging Friction</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/aspire-132-bun-container-enhancements/</link><pubDate>Fri, 24 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/aspire-132-bun-container-enhancements/</guid><description>Aspire 13.2 adds first-class Bun support for Vite apps, fixes Yarn reliability, and ships container improvements that make local dev behavior honest. Here's what actually changed and why it matters.</description><content:encoded>&lt;p&gt;If you&amp;rsquo;ve been building .NET backends with JavaScript frontends in Aspire, 13.2 is the kind of update that quietly makes your day better. No splashy new paradigms. Just solid improvements to things that were mildly annoying.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s walk through what actually landed.&lt;/p&gt;
&lt;h2 id="bun-is-now-a-first-class-citizen"&gt;Bun is Now a First-Class Citizen&lt;/h2&gt;
&lt;p&gt;The headline feature: Bun support for Vite apps in Aspire. One fluent call, done.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// TypeScript AppHost
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;createBuilder&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&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="nx"&gt;addViteApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;frontend&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;./frontend&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 class="nx"&gt;withBun&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;run&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;If your team already uses Bun — which you might, given its dramatically faster install times and startup — Aspire no longer makes you fight against the grain. Previously, Aspire assumed npm and you had to work around it. Now &lt;code&gt;.withBun()&lt;/code&gt; is a first-class option alongside &lt;code&gt;.withYarn()&lt;/code&gt; and the default npm behavior.&lt;/p&gt;
&lt;p&gt;Why does this matter? Because JavaScript tooling speed directly affects your inner dev loop. If your frontend takes 30 seconds to install dependencies every time you spin up a fresh environment, that adds up. Bun cuts that dramatically.&lt;/p&gt;
&lt;p&gt;The C# AppHost equivalents are documented at &lt;a href="https://aspire.dev/integrations/frameworks/javascript/#use-bun"&gt;aspire.dev&lt;/a&gt; if you prefer authoring in C# — all the same patterns apply.&lt;/p&gt;
&lt;h2 id="yarn-got-more-reliable"&gt;Yarn Got More Reliable&lt;/h2&gt;
&lt;p&gt;Bun gets the spotlight, but Yarn users get something arguably more impactful: fewer mysterious failures. Aspire 13.2 improves reliability for &lt;code&gt;withYarn()&lt;/code&gt; with &lt;code&gt;addViteApp()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These kinds of fixes don&amp;rsquo;t sound exciting until you&amp;rsquo;ve spent 20 minutes debugging why your Yarn-backed frontend resource wouldn&amp;rsquo;t start. Consider it fixed.&lt;/p&gt;
&lt;h2 id="container-publishing-you-can-actually-reason-about"&gt;Container Publishing You Can Actually Reason About&lt;/h2&gt;
&lt;p&gt;Two container improvements worth knowing:&lt;/p&gt;
&lt;h3 id="explicit-pull-policy"&gt;Explicit Pull Policy&lt;/h3&gt;
&lt;p&gt;Docker Compose publishing now supports &lt;code&gt;PullPolicy&lt;/code&gt;, including a &lt;code&gt;Never&lt;/code&gt; option:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ImagePullPolicy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;./.modules/aspire.js&amp;#39;&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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;createBuilder&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;await&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addDockerComposeEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;compose&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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;worker&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;myorg/worker:latest&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 class="nx"&gt;withImagePullPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ImagePullPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Never&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;run&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 the &amp;ldquo;please use the image I already built and leave the registry out of it&amp;rdquo; workflow. Super useful when iterating locally on images you&amp;rsquo;re building and publishing manually, or when your CI produces an image and you want Compose to use exactly that one without sneaking in a remote pull.&lt;/p&gt;
&lt;h3 id="postgresql-18-volumes-work-again"&gt;PostgreSQL 18+ Volumes Work Again&lt;/h3&gt;
&lt;p&gt;PostgreSQL 18 changed its internal data directory layout. This broke volume mapping in Aspire silently — your data volume would be set up but persistence wouldn&amp;rsquo;t actually work correctly. 13.2 fixes this.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;postgres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addPostgres&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;postgres&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 class="nx"&gt;withDataVolume&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;isReadOnly&lt;/span&gt;: &lt;span class="kt"&gt;false&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;If you&amp;rsquo;re running PostgreSQL 18 or later with a data volume, upgrade to Aspire 13.2 and don&amp;rsquo;t think about it again.&lt;/p&gt;
&lt;h2 id="debugging-quality-of-life-improvements"&gt;Debugging Quality-of-Life Improvements&lt;/h2&gt;
&lt;p&gt;A few things that make stepping through an AppHost session less frustrating:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DebuggerDisplayAttribute on core types&lt;/strong&gt; — &lt;code&gt;DistributedApplication&lt;/code&gt;, resources, endpoint expressions now show useful values in the debugger instead of requiring you to drill into object trees&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better WaitFor failure messages&lt;/strong&gt; — when resources fail to start, the error context is actually helpful now&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;BeforeResourceStartedEvent&lt;/code&gt; fires at the right time&lt;/strong&gt; — only when a resource is actually starting, not on unrelated state transitions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;launchSettings.json&lt;/code&gt; is more resilient&lt;/strong&gt; — less chance of a malformed setting corrupting your dev startup&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of these are individually earth-shattering, but collectively they remove friction from the debugging experience. If you&amp;rsquo;ve ever had to drill three levels deep into an Aspire resource object to figure out what endpoint it was using, the debugger display improvement alone is worth the update.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Aspire 13.2 is a focused quality release. Bun support is the headline, but the container and debugging improvements are what will make your day-to-day work smoother. Worth updating — especially if you&amp;rsquo;re on PostgreSQL 18 with data volumes.&lt;/p&gt;
&lt;p&gt;Full details in the &lt;a href="https://devblogs.microsoft.com/aspire/aspire-bun-support-and-container-enhancements/"&gt;original post by David Pine&lt;/a&gt; and the &lt;a href="https://aspire.dev/whats-new/aspire-13-2/"&gt;Aspire 13.2 what&amp;rsquo;s new docs&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>68 Minutes a Day Re-Explaining Code to Copilot? There's a Fix for That</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/auto-memory-stop-re-explaining-code-to-copilot/</link><pubDate>Thu, 23 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/auto-memory-stop-re-explaining-code-to-copilot/</guid><description>Context rot is real — your AI agent drifts after 30 turns, and you're paying the compaction tax every hour. auto-memory gives GitHub Copilot CLI surgical recall without burning thousands of tokens.</description><content:encoded>&lt;p&gt;You know that moment when your Copilot session hits &lt;code&gt;/compact&lt;/code&gt; and the agent completely forgets what you were doing? You spend the next five minutes re-explaining the file structure, the failing test, the three approaches you already tried. Then it happens again. And again.&lt;/p&gt;
&lt;p&gt;Desi Villanueva timed it: &lt;strong&gt;68 minutes per day&lt;/strong&gt; — just on re-orientation. Not writing code. Not reviewing PRs. Just catching the AI up on things it already knew.&lt;/p&gt;
&lt;p&gt;Turns out there&amp;rsquo;s a concrete reason this happens, and a concrete fix.&lt;/p&gt;
&lt;h2 id="the-context-window-lie"&gt;The Context Window Lie&lt;/h2&gt;
&lt;p&gt;Your agent ships with a big number on the box. 200K tokens. Sounds massive. In practice it&amp;rsquo;s a ceiling, not a guarantee.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the actual math:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;200K total context&lt;/li&gt;
&lt;li&gt;Minus ~65K for MCP tools loaded at startup (~33%)&lt;/li&gt;
&lt;li&gt;Minus ~10K for instruction files like &lt;code&gt;AGENTS.md&lt;/code&gt; or &lt;code&gt;copilot-instructions.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That leaves you with roughly &lt;strong&gt;125K before you type a word&lt;/strong&gt;. And it gets worse — LLMs don&amp;rsquo;t degrade gracefully as context fills up. They hit a wall at around 60% capacity. The model starts losing things mentioned 30 turns ago, contradicts earlier responses, hallucinates file names it stated confidently 10 minutes prior. The industry calls this the &amp;ldquo;lost in the middle&amp;rdquo; problem.&lt;/p&gt;
&lt;p&gt;Effective limit: &lt;strong&gt;45K tokens&lt;/strong&gt; before quality degrades. That&amp;rsquo;s maybe 20-30 turns of active conversation before the agent starts drifting. Which is why you&amp;rsquo;re hitting &lt;code&gt;/compact&lt;/code&gt; every 45 minutes — not because you&amp;rsquo;ve filled 200K tokens, but because the model is already rotting at 120K.&lt;/p&gt;
&lt;h2 id="the-compaction-tax"&gt;The Compaction Tax&lt;/h2&gt;
&lt;p&gt;Every &lt;code&gt;/compact&lt;/code&gt; costs you flow state. You&amp;rsquo;re deep in a debugging session. Shared context built up over 30 minutes. The agent knows the file structure, the failing test, the hypothesis. Then the warning hits.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ignore it → agent gets progressively dumber, hallucinates old state&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;/compact&lt;/code&gt; → agent has a 2-paragraph summary of a 30-minute investigation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Either way you lose. Either way you&amp;rsquo;re narrating your own project back to it like a new hire on day one.&lt;/p&gt;
&lt;p&gt;The cruel part? &lt;strong&gt;The memory already exists.&lt;/strong&gt; Copilot CLI writes every session to a local SQLite database at &lt;code&gt;~/.copilot/session-store.db&lt;/code&gt; — every file touched, every turn, every checkpoint. It&amp;rsquo;s all sitting on disk. The agent just can&amp;rsquo;t read it.&lt;/p&gt;
&lt;h2 id="auto-memory-a-recall-layer-not-a-memory-system"&gt;auto-memory: A Recall Layer, Not a Memory System&lt;/h2&gt;
&lt;p&gt;That&amp;rsquo;s the key insight behind &lt;a href="https://github.com/dezgit2025/auto-memory"&gt;auto-memory&lt;/a&gt;: don&amp;rsquo;t build a new memory system — build a read-only query layer over the one that already exists.&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;pip install auto-memory
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;~1,900 lines of Python. Zero dependencies. Installs in 30 seconds.&lt;/p&gt;
&lt;p&gt;Instead of flooding the context with grep results, you give the agent surgical access to what actually matters:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Tokens&lt;/th&gt;
&lt;th&gt;What you get&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -r &amp;quot;auth&amp;quot; src/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~5,000–10,000&lt;/td&gt;
&lt;td&gt;500 results, most irrelevant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;find . -name &amp;quot;*.py&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~2,000&lt;/td&gt;
&lt;td&gt;Every Python file, no context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent re-orientation&lt;/td&gt;
&lt;td&gt;~2,000&lt;/td&gt;
&lt;td&gt;You explaining what it should know&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;auto-memory files --json --limit 10&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~50&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;The 10 files you touched yesterday&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That&amp;rsquo;s a 200x improvement. The agent skips the archaeological dig and goes straight to what matters.&lt;/p&gt;
&lt;p&gt;The recommended flow: when you&amp;rsquo;re approaching 50-70% context usage, run &lt;code&gt;/clear&lt;/code&gt; and then prompt with &amp;ldquo;review last sessions we discussed topic X&amp;rdquo;. Instead of burning 12K tokens on blind searches, auto-memory pulls the relevant context in 50.&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 using GitHub Copilot CLI for .NET work — scaffolding services, debugging EF Core queries, iterating on Blazor components — the context rot problem hits just as hard. Complex solutions with multiple projects, shared libraries, and deep call chains are exactly the kind of codebase where the agent loses track fastest.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/dezgit2025/auto-memory/blob/main/deploy/install.md"&gt;install guide&lt;/a&gt; walks through pointing Copilot CLI at it. It&amp;rsquo;s a one-time setup.&lt;/p&gt;
&lt;p&gt;Honestly? 68 minutes a day back in your pocket is not a minor quality-of-life tweak. That&amp;rsquo;s almost 6 hours a week.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Context rot is a real architectural constraint, not a bug that will get patched. auto-memory works around it by giving your agent a cheap, precise recall mechanism instead of expensive, noisy re-exploration. If you&amp;rsquo;re doing serious AI-assisted development with GitHub Copilot CLI, it&amp;rsquo;s worth the 30-second install.&lt;/p&gt;
&lt;p&gt;Check it out: &lt;a href="https://github.com/dezgit2025/auto-memory"&gt;auto-memory on GitHub&lt;/a&gt;. Original post by Desi Villanueva: &lt;a href="https://devblogs.microsoft.com/all-things-azure/i-wasted-68-minutes-a-day-re-explaining-my-code-then-i-built-auto-memory/"&gt;I Wasted 68 Minutes a Day Re-Explaining My Code&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>azd Hooks in Python, TypeScript, and .NET: Stop Fighting Shell Scripts</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/azd-hooks-python-javascript-typescript-dotnet/</link><pubDate>Thu, 23 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/azd-hooks-python-javascript-typescript-dotnet/</guid><description>The Azure Developer CLI now lets you write hooks in Python, JavaScript, TypeScript, or .NET. No more context-switching to Bash just to run a migration script.</description><content:encoded>&lt;p&gt;If you&amp;rsquo;ve ever had a fully .NET project and still found yourself writing Bash scripts just to run azd hooks, you know the pain. Why switch to shell syntax just for a pre-provision step when everything else in your project is C#?&lt;/p&gt;
&lt;p&gt;That frustration is now officially solved. The Azure Developer CLI &lt;a href="https://devblogs.microsoft.com/azure-sdk/azd-multi-language-hooks/"&gt;just shipped multi-language hook support&lt;/a&gt;, and it&amp;rsquo;s exactly as good as it sounds.&lt;/p&gt;
&lt;h2 id="hooks-quickly-if-youre-not-familiar"&gt;Hooks, quickly, if you&amp;rsquo;re not familiar&lt;/h2&gt;
&lt;p&gt;Hooks are scripts that run at key points in the &lt;code&gt;azd&lt;/code&gt; lifecycle — before provisioning, after deployment, and more. They&amp;rsquo;re defined in &lt;code&gt;azure.yaml&lt;/code&gt; and let you inject custom logic without forking the CLI itself.&lt;/p&gt;
&lt;p&gt;Previously you were limited to Bash and PowerShell. Now you can use &lt;strong&gt;Python, JavaScript, TypeScript, or .NET&lt;/strong&gt; — and &lt;code&gt;azd&lt;/code&gt; handles the rest automatically.&lt;/p&gt;
&lt;h2 id="how-the-detection-works"&gt;How the detection works&lt;/h2&gt;
&lt;p&gt;You just point the hook at a file and &lt;code&gt;azd&lt;/code&gt; infers the language from the extension:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;preprovision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./hooks/setup.py&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;postdeploy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./hooks/seed.ts&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;postprovision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./hooks/migrate.cs&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. No extra config. If the extension is ambiguous, you can add an explicit &lt;code&gt;kind: python&lt;/code&gt; (or whatever) to override.&lt;/p&gt;
&lt;h2 id="language-specific-details-worth-knowing"&gt;Language-specific details worth knowing&lt;/h2&gt;
&lt;h3 id="python"&gt;Python&lt;/h3&gt;
&lt;p&gt;Drop a &lt;code&gt;requirements.txt&lt;/code&gt; or &lt;code&gt;pyproject.toml&lt;/code&gt; next to your script (or anywhere up the directory tree) and &lt;code&gt;azd&lt;/code&gt; creates a virtual environment, installs deps, and runs your script:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;hooks/
├── setup.py
└── requirements.txt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;No virtualenv management on your end. &lt;code&gt;azd&lt;/code&gt; walks up from the script location to find the nearest project file.&lt;/p&gt;
&lt;h3 id="javascript-and-typescript"&gt;JavaScript and TypeScript&lt;/h3&gt;
&lt;p&gt;Same pattern — put a &lt;code&gt;package.json&lt;/code&gt; near your script and &lt;code&gt;azd&lt;/code&gt; runs &lt;code&gt;npm install&lt;/code&gt; first. For TypeScript, it uses &lt;code&gt;npx tsx&lt;/code&gt; so there&amp;rsquo;s no compile step and no &lt;code&gt;tsconfig.json&lt;/code&gt; required:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;hooks/
├── seed.ts
└── package.json
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Want to use pnpm or yarn? There&amp;rsquo;s a &lt;code&gt;config.packageManager&lt;/code&gt; option for that.&lt;/p&gt;
&lt;h3 id="net"&gt;.NET&lt;/h3&gt;
&lt;p&gt;Two modes here, which is nice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project mode&lt;/strong&gt;: If there&amp;rsquo;s a &lt;code&gt;.csproj&lt;/code&gt; next to the script, &lt;code&gt;azd&lt;/code&gt; runs &lt;code&gt;dotnet restore&lt;/code&gt; and &lt;code&gt;dotnet build&lt;/code&gt; automatically.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Single-file mode&lt;/strong&gt;: On .NET 10+, you can drop a standalone &lt;code&gt;.cs&lt;/code&gt; file and it runs directly via &lt;code&gt;dotnet run script.cs&lt;/code&gt;. No project file needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;postprovision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./hooks/migrate.cs&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you&amp;rsquo;re already on .NET 10, single-file mode is honestly the cleanest option for simple migration or seeding scripts. No project scaffolding, no &lt;code&gt;.csproj&lt;/code&gt; to maintain.&lt;/p&gt;
&lt;h2 id="executor-specific-config"&gt;Executor-specific config&lt;/h2&gt;
&lt;p&gt;Each language supports an optional &lt;code&gt;config&lt;/code&gt; block when you need to tweak the defaults:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;preprovision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./hooks/setup.ts&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;packageManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;pnpm&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;postdeploy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./hooks/seed.py&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;virtualEnvName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;.venv&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;postprovision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./hooks/migrate.cs&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Release&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;framework&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;net10.0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can also mix formats in the same &lt;code&gt;hooks:&lt;/code&gt; block — different languages for different lifecycle events, platform-specific overrides for Windows vs. Linux, whatever you need.&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;The boring answer is &amp;ldquo;consistency.&amp;rdquo; But honestly it goes deeper than that. Hooks are often the last place in an azd-based project that forces you into a different language context. Now your entire deployment pipeline — from app code to infrastructure scripts to lifecycle hooks — can live in one language.&lt;/p&gt;
&lt;p&gt;More practically: you can now reuse your existing .NET utilities in hooks. Have a shared class library for database schema management? Just reference it in your hook project. Have a Python data-seeding script you already wrote? Drop it straight into &lt;code&gt;azure.yaml&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;This is one of those changes that sounds small but quietly removes a lot of friction from daily azd workflows. Multi-language hook support is available now — check the &lt;a href="https://devblogs.microsoft.com/azure-sdk/azd-multi-language-hooks/"&gt;official post&lt;/a&gt; for the full docs, and head to &lt;a href="https://github.com/Azure/azure-dev"&gt;the azd GitHub repo&lt;/a&gt; to try it out on your next project.&lt;/p&gt;</content:encoded></item><item><title>Foundry Toolboxes: One Endpoint for All Your Agent Tools</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/foundry-toolboxes-curate-manage-tools-ai-agents/</link><pubDate>Thu, 23 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/foundry-toolboxes-curate-manage-tools-ai-agents/</guid><description>Microsoft Foundry just launched Toolboxes in public preview — a way to curate, manage, and expose AI agent tools through a single MCP-compatible endpoint without re-wiring everything per agent.</description><content:encoded>&lt;p&gt;Here&amp;rsquo;s a problem that sounds boring until you&amp;rsquo;ve actually hit it: your organization is building multiple AI agents, each one needs tools, and every team is wiring those tools up from scratch. Same Web Search integration, same Azure AI Search config, same GitHub MCP server connection — just in a different repo, by a different team, with different credentials and no shared governance.&lt;/p&gt;
&lt;p&gt;Microsoft Foundry just shipped &lt;a href="https://devblogs.microsoft.com/foundry/introducing-toolboxes-in-foundry/"&gt;Toolboxes&lt;/a&gt; in public preview, and it&amp;rsquo;s a direct answer to that problem.&lt;/p&gt;
&lt;h2 id="whats-a-toolbox"&gt;What&amp;rsquo;s a Toolbox?&lt;/h2&gt;
&lt;p&gt;A Toolbox is a named, reusable bundle of tools that you define once in Foundry and expose through a single MCP-compatible endpoint. Any agent runtime that speaks MCP can consume it — you&amp;rsquo;re not locked to Foundry Agents.&lt;/p&gt;
&lt;p&gt;The pitch is simple: &lt;strong&gt;build once, consume anywhere&lt;/strong&gt;. Define the tools, configure auth centrally (OAuth passthrough, Entra managed identity), publish the endpoint. Every agent that needs those tools connects to the endpoint and gets them all.&lt;/p&gt;
&lt;p&gt;No per-tool wiring. No per-agent credential management.&lt;/p&gt;
&lt;h2 id="the-four-pillars-two-of-which-ship-today"&gt;The four pillars (two of which ship today)&lt;/h2&gt;
&lt;p&gt;The Toolbox feature is organized around four ideas:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pillar&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Discover&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Coming soon&lt;/td&gt;
&lt;td&gt;Find existing approved tools without hunting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Available now&lt;/td&gt;
&lt;td&gt;Curate tools into a named, reusable bundle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consume&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Available now&lt;/td&gt;
&lt;td&gt;Single MCP endpoint exposes all tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Govern&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Coming soon&lt;/td&gt;
&lt;td&gt;Centralized auth + observability across all tool calls&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Today the focus is on Build and Consume. That&amp;rsquo;s enough to remove the most immediate friction.&lt;/p&gt;
&lt;h2 id="getting-started-in-practice"&gt;Getting started in practice&lt;/h2&gt;
&lt;p&gt;The SDK is Python-first for now. You start by creating an &lt;code&gt;AIProjectClient&lt;/code&gt; and then build a toolbox:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;azure.identity&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DefaultAzureCredential&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;azure.ai.projects&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AIProjectClient&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&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;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AIProjectClient&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;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;FOUNDRY_PROJECT_ENDPOINT&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;credential&lt;/span&gt;&lt;span class="o"&gt;=&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then you create a toolbox version with the tools you want to bundle:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;toolbox_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toolboxes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_toolbox_version&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;toolbox_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;customer-feedback-triaging-toolbox&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;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Search public and internal docs, then respond to GitHub issues.&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="o"&gt;=&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="s2"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;web_search&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Search approved public documentation&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 class="s2"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;azure_ai_search&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;index_name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;internal-docs&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 class="s2"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;mcp_server&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;server_url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://your-github-mcp-server.com&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="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once published, Foundry gives you a unified endpoint:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;https://zava.services.ai.azure.com/api/projects/&amp;lt;project&amp;gt;/toolbox/&amp;lt;toolbox-name&amp;gt;/mcp?api-version=v1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Point any MCP-compatible agent runtime at that URL and it discovers all the tools in the bundle dynamically. One connection. All tools.&lt;/p&gt;
&lt;h2 id="not-locked-to-foundry-agents"&gt;Not locked to Foundry Agents&lt;/h2&gt;
&lt;p&gt;This is worth spelling out because it&amp;rsquo;s a common concern when Microsoft ships something under the Foundry brand.&lt;/p&gt;
&lt;p&gt;Toolboxes are &lt;strong&gt;created and governed&lt;/strong&gt; in Foundry, but the consumption surface is the open MCP protocol. That means you can use them from:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Custom agents&lt;/strong&gt; built with Microsoft Agent Framework, LangGraph, or your own code&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt; and other MCP-enabled IDEs&lt;/li&gt;
&lt;li&gt;Any other runtime that speaks MCP&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You&amp;rsquo;re not locked in. The toolbox is Foundry-homed (that&amp;rsquo;s where you manage it) but not Foundry-bound (you can consume it from anywhere).&lt;/p&gt;
&lt;h2 id="why-it-matters-now"&gt;Why it matters now&lt;/h2&gt;
&lt;p&gt;The multi-agent wave is hitting production. Teams are building 5, 10, 20 agents — and the tool-wiring problem compounds fast. Every new agent is a new surface for duplicated config, stale credentials, and inconsistent behavior.&lt;/p&gt;
&lt;p&gt;Toolboxes don&amp;rsquo;t solve governance and discovery yet (those are &amp;ldquo;coming soon&amp;rdquo;), but the Build + Consume foundation is enough to start centralizing. Once the Govern pillar ships, you&amp;rsquo;ll have a proper observable, centrally-controlled tool layer for your entire agent fleet.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;This is early — public preview, Python SDK first, with Discover and Govern still coming. But the model is sound, and the MCP-native design means it works with the tools you&amp;rsquo;re already building on. Take a look at the &lt;a href="https://devblogs.microsoft.com/foundry/introducing-toolboxes-in-foundry/"&gt;official announcement&lt;/a&gt; to get started.&lt;/p&gt;</content:encoded></item><item><title>Windows App Dev CLI v0.3: F5 from the Terminal and UI Automation for Agents</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/windows-app-dev-cli-v03-run-ui-automation/</link><pubDate>Thu, 23 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/windows-app-dev-cli-v03-run-ui-automation/</guid><description>Windows App Development CLI v0.3 ships winapp run for terminal-based debug launches, winapp ui for UI automation, and a new NuGet package that makes dotnet run work with packaged apps.</description><content:encoded>&lt;p&gt;Visual Studio&amp;rsquo;s F5 experience is great. But having to open VS just to launch and debug a packaged Windows app is a bit much when you&amp;rsquo;re deep in a CI pipeline, running an automated workflow, or — increasingly — when an AI agent is doing the testing.&lt;/p&gt;
&lt;p&gt;Windows App Development CLI v0.3 just &lt;a href="https://devblogs.microsoft.com/ifdef-windows/windows-app-development-cli-v0-3-new-run-and-ui-commands-plus-dotnet-run-support-for-packaged-apps/"&gt;shipped&lt;/a&gt;, and it addresses this directly with two headline features: &lt;code&gt;winapp run&lt;/code&gt; and &lt;code&gt;winapp ui&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="winapp-run-f5-from-anywhere"&gt;winapp run: F5 from anywhere&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;winapp run&lt;/code&gt; takes an unpackaged app folder and a manifest, and does everything VS does for a debug launch: registers a loose package, launches the app, and preserves your &lt;code&gt;LocalState&lt;/code&gt; across re-deploys.&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;# Build your app, then run it as a packaged app&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;winapp run ./bin/Debug
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Works for WinUI, WPF, WinForms, Console, Avalonia, and more. The modes are designed for both developers and automated workflows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;--detach&lt;/code&gt;&lt;/strong&gt;: Launch and return control to the terminal immediately. Good for CI/automation where you need the app running but don&amp;rsquo;t want to block.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;--unregister-on-exit&lt;/code&gt;&lt;/strong&gt;: Cleans up the registered package when the app closes. Clean test runs, no leftover state.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;--debug-output&lt;/code&gt;&lt;/strong&gt;: Captures &lt;code&gt;OutputDebugString&lt;/code&gt; messages and exceptions in real time. When a crash happens, a minidump is captured and analyzed in-process — managed (.NET) crashes via ClrMD, native (C++/WinRT) crashes via DbgEng. Add &lt;code&gt;--symbols&lt;/code&gt; to pull PDBs from the Microsoft Symbol Server.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is the kind of setup that makes headless test runs and agent-driven validation actually work. An agent can now launch your app, interact with it, verify behavior, and clean up — all without Visual Studio.&lt;/p&gt;
&lt;h2 id="new-nuget-package-dotnet-run-for-packaged-apps"&gt;New NuGet package: dotnet run for packaged apps&lt;/h2&gt;
&lt;p&gt;For .NET developers specifically, there&amp;rsquo;s a new NuGet package: &lt;code&gt;Microsoft.Windows.SDK.BuildTools.WinApp&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Add it to your project (or let &lt;code&gt;winapp init&lt;/code&gt; do it), and &lt;code&gt;dotnet run&lt;/code&gt; handles the entire inner loop: build, prepare a loose-layout package, register with Windows, and launch — all in one step.&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;# Let winapp init set it up&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;winapp init
&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;# Or install directly&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet add package Microsoft.Windows.SDK.BuildTools.WinApp
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Works with WinUI, WPF, WinForms, Console, Avalonia. No manual registration, no extra commands. Just &lt;code&gt;dotnet run&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This is a big quality-of-life win. If you&amp;rsquo;ve been maintaining a Makefile or PowerShell script just to wire together the build-and-register-and-launch cycle, that&amp;rsquo;s now a solved problem.&lt;/p&gt;
&lt;h2 id="winapp-ui-ui-automation-from-the-command-line"&gt;winapp ui: UI Automation from the command line&lt;/h2&gt;
&lt;p&gt;This is the one that opens up agentic scenarios. &lt;code&gt;winapp ui&lt;/code&gt; gives you full UI Automation access to any running Windows app — WPF, WinForms, Win32, Electron, WinUI3 — all from the terminal.&lt;/p&gt;
&lt;p&gt;What you can do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;List all top-level windows&lt;/li&gt;
&lt;li&gt;Walk the full UI Automation tree of any window&lt;/li&gt;
&lt;li&gt;Find elements by name, type, or automation ID&lt;/li&gt;
&lt;li&gt;Click, invoke, and set values&lt;/li&gt;
&lt;li&gt;Take screenshots (per-window or multi-window composites)&lt;/li&gt;
&lt;li&gt;Wait for elements to appear — useful for test synchronization&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Combine &lt;code&gt;winapp ui&lt;/code&gt; with &lt;code&gt;winapp run&lt;/code&gt; and you have a complete build → launch → verify workflow from the terminal. An agent can run your app, inspect the UI state, interact with it programmatically, and validate the result. No Visual Studio, no test framework bootstrapping, no manual steps.&lt;/p&gt;
&lt;p&gt;For those building CI pipelines that do actual UI validation on Windows desktop apps, this is genuinely useful.&lt;/p&gt;
&lt;h2 id="other-bits-worth-noting"&gt;Other bits worth noting&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;winapp unregister&lt;/code&gt;&lt;/strong&gt;: The cleanup counterpart to &lt;code&gt;winapp run&lt;/code&gt;. Removes a sideloaded dev package when you&amp;rsquo;re done.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;winapp manifest add-alias&lt;/code&gt;&lt;/strong&gt;: Adds a &lt;code&gt;uap5:AppExecutionAlias&lt;/code&gt; so a packaged app can be launched by name from the terminal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tab completion&lt;/strong&gt;: One command to set up completions for PowerShell, then every &lt;code&gt;winapp&lt;/code&gt; command and option is tab-completable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Package.appxmanifest&lt;/code&gt; by default&lt;/strong&gt;: &lt;code&gt;winapp init&lt;/code&gt; now creates &lt;code&gt;Package.appxmanifest&lt;/code&gt; (VS convention) instead of &lt;code&gt;appxmanifest.xml&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="get-it"&gt;Get it&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;winget install Microsoft.WinAppCli
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# or&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install -g @microsoft/winappcli
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The CLI is in public preview. Check the &lt;a href="https://github.com/microsoft/WinAppCli"&gt;GitHub repo&lt;/a&gt; for full docs, guides for .NET, C++, Electron, Rust, Flutter, and more — and to file issues. The &lt;a href="https://devblogs.microsoft.com/ifdef-windows/windows-app-development-cli-v0-3-new-run-and-ui-commands-plus-dotnet-run-support-for-packaged-apps/"&gt;original announcement&lt;/a&gt; has all the details.&lt;/p&gt;</content:encoded></item><item><title>Patch This Now: .NET 10.0.7 OOB Security Update for ASP.NET Core Data Protection</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/dotnet-10-0-7-oob-security-patch-data-protection/</link><pubDate>Wed, 22 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/dotnet-10-0-7-oob-security-patch-data-protection/</guid><description>.NET 10.0.7 is an out-of-band release fixing a security vulnerability in Microsoft.AspNetCore.DataProtection — the managed authenticated encryptor was computing HMAC over the wrong bytes, leading to potential elevation of privilege. Update immediately.</description><content:encoded>&lt;p&gt;This one is not optional. If your application uses &lt;code&gt;Microsoft.AspNetCore.DataProtection&lt;/code&gt;, you need to update to 10.0.7.&lt;/p&gt;
&lt;h2 id="what-happened"&gt;What Happened&lt;/h2&gt;
&lt;p&gt;After the Patch Tuesday &lt;code&gt;.NET 10.0.6&lt;/code&gt; release, some users started reporting that decryption was failing in their applications. The issue was filed as &lt;a href="https://github.com/dotnet/aspnetcore/issues/66335"&gt;aspnetcore#66335&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While investigating that regression, the team discovered it also exposed a security vulnerability: &lt;strong&gt;CVE-2026-40372&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In versions &lt;code&gt;10.0.0&lt;/code&gt; through &lt;code&gt;10.0.6&lt;/code&gt; of &lt;code&gt;Microsoft.AspNetCore.DataProtection&lt;/code&gt;, the managed authenticated encryptor had a bug where it computed its HMAC validation tag over the &lt;strong&gt;wrong bytes&lt;/strong&gt; of the payload and then discarded the computed hash. This could result in elevation of privilege.&lt;/p&gt;
&lt;p&gt;In plain terms: the integrity check wasn&amp;rsquo;t doing what it was supposed to do. Data Protection uses authenticated encryption to prevent tampering — the HMAC is the &amp;ldquo;has this been modified?&amp;rdquo; check. If the HMAC is computed over the wrong data, you lose that guarantee.&lt;/p&gt;
&lt;h2 id="who-is-affected"&gt;Who Is Affected&lt;/h2&gt;
&lt;p&gt;Any .NET 10 application using &lt;code&gt;Microsoft.AspNetCore.DataProtection&lt;/code&gt; — versions 10.0.0 through 10.0.6. The good news is this package is specific to .NET 10. If you&amp;rsquo;re still on .NET 8 or 9, you&amp;rsquo;re not affected by this specific CVE.&lt;/p&gt;
&lt;p&gt;Common use cases for Data Protection: cookie encryption, antiforgery tokens, temp data in MVC, and any other use of &lt;code&gt;IDataProtector&lt;/code&gt; in your application.&lt;/p&gt;
&lt;h2 id="how-to-fix-it"&gt;How to Fix It&lt;/h2&gt;
&lt;p&gt;Update the &lt;code&gt;Microsoft.AspNetCore.DataProtection&lt;/code&gt; NuGet package to &lt;strong&gt;10.0.7&lt;/strong&gt;:&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;dotnet add package Microsoft.AspNetCore.DataProtection --version 10.0.7
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or update your SDK/runtime: &lt;a href="https://dotnet.microsoft.com/download/dotnet/10.0"&gt;download .NET 10.0.7&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Verify you&amp;rsquo;re on the right version:&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;dotnet --info
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then &lt;strong&gt;rebuild and redeploy&lt;/strong&gt; your application. The fix doesn&amp;rsquo;t take effect until you&amp;rsquo;re running the updated package.&lt;/p&gt;
&lt;h2 id="the-bigger-picture"&gt;The Bigger Picture&lt;/h2&gt;
&lt;p&gt;Out-of-band security releases are uncommon — they happen when a vulnerability is serious enough that it can&amp;rsquo;t wait for the next scheduled Patch Tuesday. This one is a direct consequence of a regression in 10.0.6 creating a security gap. The fact that it was discovered through bug reports is a good sign that the process worked. The fix is fast and the scope is narrow.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re running .NET 10 in production with any web application framework, this is a same-day update situation.&lt;/p&gt;
&lt;p&gt;Original announcement by Rahul Bhandari: &lt;a href="https://devblogs.microsoft.com/dotnet/dotnet-10-0-7-oob-security-update/"&gt;.NET 10.0.7 Out-of-Band Security Update&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>azd + GitHub Copilot: AI-Powered Project Setup and Smarter Error Fixes</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/azd-copilot-integration-ai-setup-troubleshooting/</link><pubDate>Tue, 21 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/azd-copilot-integration-ai-setup-troubleshooting/</guid><description>The Azure Developer CLI now integrates with GitHub Copilot to scaffold your project and fix deployment errors — all without leaving the terminal.</description><content:encoded>&lt;p&gt;You know that moment when you want to deploy an existing app to Azure and suddenly find yourself staring at a blank &lt;code&gt;azure.yaml&lt;/code&gt;, trying to remember whether your Express API should use Container Apps or App Service? Yeah, that moment. It just got a whole lot shorter.&lt;/p&gt;
&lt;p&gt;The Azure Developer CLI (&lt;code&gt;azd&lt;/code&gt;) now integrates with GitHub Copilot in two meaningful ways: AI-assisted project scaffolding during &lt;code&gt;azd init&lt;/code&gt;, and intelligent error troubleshooting when deployments go sideways. Both features stay entirely in your terminal, which is exactly where I want them.&lt;/p&gt;
&lt;h2 id="setting-up-with-copilot-during-azd-init"&gt;Setting Up with Copilot During azd init&lt;/h2&gt;
&lt;p&gt;When you run &lt;code&gt;azd init&lt;/code&gt;, there&amp;rsquo;s now a &amp;ldquo;Set up with GitHub Copilot (Preview)&amp;rdquo; option. Select it and Copilot analyzes your codebase to generate the &lt;code&gt;azure.yaml&lt;/code&gt;, infrastructure templates, and Bicep modules — based on your actual code, not a template guess.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;azd init
# Select: &amp;#34;Set up with GitHub Copilot (Preview)&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For this to work you&amp;rsquo;ll need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;azd 1.23.11 or later&lt;/strong&gt; — check with &lt;code&gt;azd version&lt;/code&gt; or update with &lt;code&gt;azd update&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;An active GitHub Copilot subscription&lt;/strong&gt; (Individual, Business, or Enterprise)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub CLI (&lt;code&gt;gh&lt;/code&gt;)&lt;/strong&gt; — &lt;code&gt;azd&lt;/code&gt; will prompt for login if needed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What I find genuinely useful here is that it works both ways. Building from scratch? Copilot helps you set up the right Azure services from the start. Have an existing app you&amp;rsquo;ve been putting off deploying? Point Copilot at it and it generates the configuration without you having to restructure anything.&lt;/p&gt;
&lt;h3 id="what-it-actually-does"&gt;What it actually does&lt;/h3&gt;
&lt;p&gt;Say you have a Node.js Express API with a PostgreSQL dependency. Instead of manually figuring out whether to target Container Apps or App Service, and then writing Bicep from scratch, Copilot detects your stack and generates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An &lt;code&gt;azure.yaml&lt;/code&gt; with the right &lt;code&gt;language&lt;/code&gt;, &lt;code&gt;host&lt;/code&gt;, and &lt;code&gt;build&lt;/code&gt; settings&lt;/li&gt;
&lt;li&gt;A Bicep module for Azure Container Apps&lt;/li&gt;
&lt;li&gt;A Bicep module for Azure Database for PostgreSQL&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And it runs preflight checks before touching anything — verifies your git working directory is clean, asks for MCP server tool consent upfront. Nothing happens without you knowing exactly what&amp;rsquo;s about to change.&lt;/p&gt;
&lt;h2 id="copilot-powered-error-troubleshooting"&gt;Copilot-Powered Error Troubleshooting&lt;/h2&gt;
&lt;p&gt;Deployment errors are a fact of life. Missing parameters, permission issues, SKU availability problems — and the error message rarely tells you the one thing you actually need to know: &lt;em&gt;how to fix it&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Without Copilot, your loop looks like: copy the error → search docs → read through three unrelated Stack Overflow answers → run some &lt;code&gt;az&lt;/code&gt; CLI commands → try again and hope. With Copilot integrated into &lt;code&gt;azd&lt;/code&gt;, that loop collapses. When any &lt;code&gt;azd&lt;/code&gt; command fails, it immediately offers four options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Explain&lt;/strong&gt; — plain-language description of what went wrong&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Guidance&lt;/strong&gt; — step-by-step fix instructions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diagnose and Guide&lt;/strong&gt; — full analysis + Copilot applies the fix (with your approval) + optional retry&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skip&lt;/strong&gt; — handle it yourself&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key thing: Copilot already has context about your project, the command that failed, and the error output. Its suggestions are specific to &lt;em&gt;your situation&lt;/em&gt;, not generic docs.&lt;/p&gt;
&lt;h3 id="real-examples-where-this-shines"&gt;Real examples where this shines&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Resource provider not registered:&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ERROR: deployment failed: MissingSubscriptionRegistration:
The subscription is not registered to use namespace &amp;#39;Microsoft.App&amp;#39;.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This trips up anyone deploying to a fresh subscription. Copilot can register the provider and rerun the deployment automatically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SKU not available:&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ERROR: deployment failed: SkuNotAvailable:
The requested VM size &amp;#39;Standard_D2s_v3&amp;#39; is not available in location &amp;#39;westus&amp;#39;.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Copilot explains which VM size or region is blocked and suggests alternatives that are actually available in your subscription.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Storage account name collision:&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ERROR: deployment failed: StorageAccountAlreadyTaken:
The storage account named &amp;#39;myappstorage&amp;#39; is already taken.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Global uniqueness bites everyone at least once. Copilot suggests adding your environment name or a random suffix to your Bicep parameters.&lt;/p&gt;
&lt;h3 id="set-a-default-behavior"&gt;Set a default behavior&lt;/h3&gt;
&lt;p&gt;If you always want the same option, skip the interactive prompt:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;azd config set copilot.errorHandling.category troubleshoot
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Options: &lt;code&gt;explain&lt;/code&gt;, &lt;code&gt;guidance&lt;/code&gt;, &lt;code&gt;troubleshoot&lt;/code&gt;, &lt;code&gt;fix&lt;/code&gt;, &lt;code&gt;skip&lt;/code&gt;. You can also enable auto-fix and retry:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;azd config set copilot.errorHandling.fix allow
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Reset to interactive at any time:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;azd config unset copilot.errorHandling.category
&lt;/code&gt;&lt;/pre&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 on Azure — whether it&amp;rsquo;s a .NET Aspire app, a containerized API, or anything in between — &lt;code&gt;azd&lt;/code&gt; has been the tool to know for a while now. This Copilot integration removes the last bit of friction that used to make it feel like you needed a cheat sheet just to get started.&lt;/p&gt;
&lt;p&gt;The scaffolding piece is great for brownfield projects. You&amp;rsquo;ve got an ASP.NET Core app running locally, it works perfectly, but getting it &lt;em&gt;onto&lt;/em&gt; Azure has always required a bit of infrastructure knowledge. Now Copilot bridges that gap. And the error troubleshooting feature is something I genuinely wish I&amp;rsquo;d had the last time I spent 45 minutes debugging a &lt;code&gt;SkuNotAvailable&lt;/code&gt; error across three regions.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;This is exactly the kind of Copilot integration that adds real value — not AI for AI&amp;rsquo;s sake, but AI that understands your context and saves you actual time. Try it out by running &lt;code&gt;azd update&lt;/code&gt; to get the latest version, then use &lt;code&gt;azd init&lt;/code&gt; on your next project. The team is working on deeper features including Copilot-assisted infrastructure customization, so now&amp;rsquo;s a good time to &lt;a href="https://aka.ms/azd-user-research-signup"&gt;sign up for user research&lt;/a&gt; if you want to influence what comes next.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="https://devblogs.microsoft.com/azure-sdk/azd-copilot-integration/"&gt;original announcement here&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>Writing Node.js Native Addons in C# with .NET Native AOT</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/nodejs-addons-csharp-native-aot/</link><pubDate>Tue, 21 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/nodejs-addons-csharp-native-aot/</guid><description>The C# Dev Kit team replaced C++ Node.js addons with .NET Native AOT — and the result is cleaner, safer, and only needs the .NET SDK.</description><content:encoded>&lt;p&gt;Here&amp;rsquo;s a scenario I love: a team that works on .NET tooling had native Node.js addons written in C++ and compiled via &lt;code&gt;node-gyp&lt;/code&gt;. It worked. But it required Python to be installed on every developer&amp;rsquo;s machine — an old version of Python, mind you — just to build a package that nobody on the team would ever touch directly.&lt;/p&gt;
&lt;p&gt;So they asked a very reasonable question: we already have the .NET SDK installed, why are we writing C++ at all?&lt;/p&gt;
&lt;p&gt;The answer was Native AOT, and the result is genuinely elegant. Drew Noakes from the C# Dev Kit team wrote up how they did it, and I think it&amp;rsquo;s worth understanding even if you&amp;rsquo;re not building VS Code extensions.&lt;/p&gt;
&lt;h2 id="the-basic-idea"&gt;The basic idea&lt;/h2&gt;
&lt;p&gt;A Node.js native addon is a shared library (&lt;code&gt;.dll&lt;/code&gt; on Windows, &lt;code&gt;.so&lt;/code&gt; on Linux, &lt;code&gt;.dylib&lt;/code&gt; on macOS) that Node.js can load at runtime. The interface is called &lt;a href="https://nodejs.org/api/n-api.html"&gt;N-API&lt;/a&gt; — a stable, ABI-compatible C API. N-API doesn&amp;rsquo;t care what language produced the library, only that it exports the right symbols.&lt;/p&gt;
&lt;p&gt;.NET Native AOT can produce exactly that. It compiles your C# code ahead-of-time into a native shared library with arbitrary entry points. That&amp;rsquo;s the whole trick.&lt;/p&gt;
&lt;h2 id="the-project-setup"&gt;The project setup&lt;/h2&gt;
&lt;p&gt;The project file is minimal:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;Project&lt;/span&gt; &lt;span class="na"&gt;Sdk=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Microsoft.NET.Sdk&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net10.0&lt;span class="nt"&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;PublishAot&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/PublishAot&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;AllowUnsafeBlocks&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/AllowUnsafeBlocks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;PublishAot&lt;/code&gt; is what tells the SDK to produce a shared library on &lt;code&gt;dotnet publish&lt;/code&gt;. &lt;code&gt;AllowUnsafeBlocks&lt;/code&gt; is needed for the N-API interop with function pointers and fixed buffers.&lt;/p&gt;
&lt;h2 id="exporting-the-entry-point"&gt;Exporting the entry point&lt;/h2&gt;
&lt;p&gt;Node.js expects your library to export &lt;code&gt;napi_register_module_v1&lt;/code&gt;. In C#, &lt;code&gt;[UnmanagedCallersOnly]&lt;/code&gt; does exactly that:&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="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;unsafe&lt;/span&gt; &lt;span class="kd"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RegistryAddon&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="na"&gt; [UnmanagedCallersOnly(
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; EntryPoint = &amp;#34;napi_register_module_v1&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; CallConvs = [typeof(CallConvCdecl)]&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="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;nint&lt;/span&gt; &lt;span class="n"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nint&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nint&lt;/span&gt; &lt;span class="n"&gt;exports&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;Initialize&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;RegisterFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;readStringValue&amp;#34;&lt;/span&gt;&lt;span class="n"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ReadStringValue&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;return&lt;/span&gt; &lt;span class="n"&gt;exports&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="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A few things worth calling out here. &lt;code&gt;nint&lt;/code&gt; is a native-sized integer — the managed equivalent of &lt;code&gt;intptr_t&lt;/code&gt;. The &lt;code&gt;u8&lt;/code&gt; suffix produces a &lt;code&gt;ReadOnlySpan&amp;lt;byte&amp;gt;&lt;/code&gt; with a UTF-8 string literal, passed directly to N-API without any encoding allocation. And &lt;code&gt;[UnmanagedCallersOnly]&lt;/code&gt; exports the method with the exact entry point name Node.js is looking for.&lt;/p&gt;
&lt;h2 id="resolving-n-api-against-the-host-process"&gt;Resolving N-API against the host process&lt;/h2&gt;
&lt;p&gt;N-API functions are exported by &lt;code&gt;node.exe&lt;/code&gt; itself, not a separate library. So instead of linking against something, you resolve them against the running process at startup:&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="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Initialize&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;NativeLibrary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetDllImportResolver&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;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Reflection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Assembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetExecutingAssembly&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;ResolveDllImport&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="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;nint&lt;/span&gt; &lt;span class="n"&gt;ResolveDllImport&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;string&lt;/span&gt; &lt;span class="n"&gt;libraryName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Assembly&lt;/span&gt; &lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DllImportSearchPath&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;searchPath&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libraryName&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;node&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&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;return&lt;/span&gt; &lt;span class="n"&gt;NativeLibrary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetMainProgramHandle&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="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With that in place, your P/Invoke declarations work 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="na"&gt;[LibraryImport(&amp;#34;node&amp;#34;, EntryPoint = &amp;#34;napi_create_string_utf8&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;internal&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;partial&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="n"&gt;CreateStringUtf8&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;nint&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ReadOnlySpan&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nuint&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="n"&gt;nint&lt;/span&gt; &lt;span class="n"&gt;result&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 source-generated &lt;code&gt;[LibraryImport]&lt;/code&gt; handles the marshalling. &lt;code&gt;ReadOnlySpan&amp;lt;byte&amp;gt;&lt;/code&gt; maps to &lt;code&gt;const char*&lt;/code&gt;, function pointers pass through directly, and it&amp;rsquo;s all trimming-compatible.&lt;/p&gt;
&lt;h2 id="what-an-actual-exported-function-looks-like"&gt;What an actual exported function looks like&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the registry reader they built — the whole function, from reading arguments to returning a result:&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;[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)]&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="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;nint&lt;/span&gt; &lt;span class="n"&gt;ReadStringValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nint&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nint&lt;/span&gt; &lt;span class="n"&gt;info&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="k"&gt;try&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;keyPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetStringArg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&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;valueName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetStringArg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyPath&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;valueName&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;null&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;ThrowError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Expected two string arguments: keyPath, valueName&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="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenSubKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valueName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="k"&gt;value&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;CreateString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;value&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;GetUndefined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&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="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&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;ThrowError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;$&amp;#34;Registry read failed: {ex.Message}&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="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&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="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Important note on the &lt;code&gt;try/catch&lt;/code&gt;: an unhandled exception in an &lt;code&gt;[UnmanagedCallersOnly]&lt;/code&gt; method crashes the host process. You always catch and forward to JavaScript via &lt;code&gt;ThrowError&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="calling-it-from-typescript"&gt;Calling it from TypeScript&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;dotnet publish&lt;/code&gt; produces your platform-specific native library. You rename it to &lt;code&gt;.node&lt;/code&gt; (Node.js convention for native addons) and use it with a standard &lt;code&gt;require()&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;RegistryAddon&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="nx"&gt;readStringValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keyPath&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;valueName&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./native/win32-x64/RegistryAddon.node&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;RegistryAddon&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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sdkPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readStringValue&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="s1"&gt;&amp;#39;SOFTWARE\\dotnet\\Setup\\InstalledVersions\\x64\\sdk&amp;#39;&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="s1"&gt;&amp;#39;InstallLocation&amp;#39;&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;p&gt;That&amp;rsquo;s it. TypeScript into C#, no Python, no C++.&lt;/p&gt;
&lt;h2 id="what-they-gained"&gt;What they gained&lt;/h2&gt;
&lt;p&gt;The immediate win was contributor experience: no specific Python version needed, &lt;code&gt;yarn install&lt;/code&gt; works with just Node.js and the .NET SDK. CI pipelines got simpler too.&lt;/p&gt;
&lt;p&gt;Performance was comparable to the C++ implementation. Native AOT produces optimized native code, and for string marshalling and registry access, the difference is negligible. The slightly larger memory footprint of the .NET runtime doesn&amp;rsquo;t matter in a long-running VS Code extension process.&lt;/p&gt;
&lt;p&gt;But the bit that caught my attention was this: they noted that with Native AOT producing shared libraries that load directly into the Node.js process, they could potentially host &lt;em&gt;more&lt;/em&gt; .NET logic in-process, avoiding the serialization and process-management overhead of their current pipe-based approach. That&amp;rsquo;s a longer-term exploration, but the foundation is now in place.&lt;/p&gt;
&lt;h2 id="why-i-think-this-is-interesting-beyond-vs-code-extensions"&gt;Why I think this is interesting beyond VS Code extensions&lt;/h2&gt;
&lt;p&gt;We often talk about Native AOT in the context of serverless cold starts or embedded scenarios. This is a different angle: using Native AOT to bridge .NET into ecosystems that traditionally required C or C++. Any environment that can load a native shared library — Electron apps, Python extensions via ctypes, Rust via FFI, Node.js via N-API — is now a potential host for C# code.&lt;/p&gt;
&lt;p&gt;For .NET developers who&amp;rsquo;ve been eyeing some interop scenario but didn&amp;rsquo;t want to learn a C++ build system, this pattern is worth knowing. Write your logic in C#, expose it with &lt;code&gt;[UnmanagedCallersOnly]&lt;/code&gt;, compile with &lt;code&gt;PublishAot&lt;/code&gt;, and load it from wherever you need it.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;The C# Dev Kit team replaced Python/C++ overhead with clean C# code that everyone on the team already knows how to write and debug. The approach is not complicated once you see it, and it&amp;rsquo;s a great example of Native AOT solving a real problem that doesn&amp;rsquo;t get talked about enough.&lt;/p&gt;
&lt;p&gt;If you want the full walkthrough with all the string marshalling helpers, check the &lt;a href="https://devblogs.microsoft.com/dotnet/writing-nodejs-addons-with-dotnet-native-aot/"&gt;original post on the .NET blog&lt;/a&gt;. There&amp;rsquo;s also &lt;a href="https://github.com/microsoft/node-api-dotnet"&gt;node-api-dotnet&lt;/a&gt; if you need a higher-level framework for more complex scenarios.&lt;/p&gt;</content:encoded></item><item><title>VS Code 1.117: Agents Are Getting Their Own Git Branches and I'm Here For It</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/vscode-1-117-agents-autopilot-worktrees/</link><pubDate>Sun, 19 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/vscode-1-117-agents-autopilot-worktrees/</guid><description>VS Code 1.117 ships worktree isolation for agent sessions, persistent Autopilot mode, and subagent support. The agentic coding workflow just got way more real.</description><content:encoded>&lt;p&gt;The line between &amp;ldquo;AI assistant&amp;rdquo; and &amp;ldquo;AI teammate&amp;rdquo; keeps getting thinner. VS Code 1.117 just dropped and the &lt;a href="https://code.visualstudio.com/updates/v1_117"&gt;full release notes&lt;/a&gt; are packed, but the story here is clear: agents are becoming first-class citizens in your dev workflow.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what actually matters.&lt;/p&gt;
&lt;h2 id="autopilot-mode-finally-remembers-your-preference"&gt;Autopilot mode finally remembers your preference&lt;/h2&gt;
&lt;p&gt;Previously, you had to re-enable Autopilot every time you started a new session. Annoying. Now your permission mode persists across sessions, and you can configure the default.&lt;/p&gt;
&lt;p&gt;The Agent Host supports three session configs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Default&lt;/strong&gt; — tools ask for confirmation before running&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bypass&lt;/strong&gt; — auto-approves everything&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Autopilot&lt;/strong&gt; — fully autonomous, answers its own questions and keeps going&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;rsquo;re scaffolding a new .NET project with migrations, Docker, and CI — set it to Autopilot once and forget about it. That preference sticks.&lt;/p&gt;
&lt;h2 id="worktree-and-git-isolation-for-agent-sessions"&gt;Worktree and git isolation for agent sessions&lt;/h2&gt;
&lt;p&gt;This is the big one. Agent sessions now support full worktree and git isolation. That means when an agent works on a task, it gets its own branch and working directory. Your main branch stays untouched.&lt;/p&gt;
&lt;p&gt;Even better — Copilot CLI generates meaningful branch names for these worktree sessions. No more &lt;code&gt;agent-session-abc123&lt;/code&gt;. You get something that actually describes what the agent is doing.&lt;/p&gt;
&lt;p&gt;For .NET developers running multiple feature branches or fixing bugs while a long scaffolding task runs, this is a game changer. You can have an agent building out your API controllers in one worktree while you&amp;rsquo;re debugging a service layer issue in another. No conflicts. No stashing. No mess.&lt;/p&gt;
&lt;h2 id="subagents-and-agent-teams"&gt;Subagents and agent teams&lt;/h2&gt;
&lt;p&gt;The Agent Host Protocol now supports subagents. An agent can spin up other agents to handle parts of a task. Think of it as delegating — your main agent coordinates, and specialized agents handle the pieces.&lt;/p&gt;
&lt;p&gt;This is early, but the potential for .NET workflows is obvious. Imagine one agent handling your EF Core migrations while another sets up your integration tests. We&amp;rsquo;re not fully there yet, but the protocol support landing now means tooling will follow fast.&lt;/p&gt;
&lt;h2 id="terminal-output-auto-included-when-agents-send-input"&gt;Terminal output auto-included when agents send input&lt;/h2&gt;
&lt;p&gt;Small but meaningful. When an agent sends input to the terminal, the terminal output is now automatically included in the context. Before, the agent had to make an extra turn just to read what happened.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve ever watched an agent run &lt;code&gt;dotnet build&lt;/code&gt;, fail, and then take another round-trip just to see the error — that friction is gone. It sees the output immediately and reacts.&lt;/p&gt;
&lt;h2 id="self-updating-agents-app-on-macos"&gt;Self-updating Agents app on macOS&lt;/h2&gt;
&lt;p&gt;The standalone Agents app on macOS now self-updates. No more manually downloading new versions. It just stays current.&lt;/p&gt;
&lt;h2 id="the-smaller-stuff-worth-knowing"&gt;The smaller stuff worth knowing&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;package.json hovers&lt;/strong&gt; now show both the installed version and the latest available. Useful if you manage npm tooling alongside your .NET projects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Images in JSDoc&lt;/strong&gt; comments render correctly in hovers and completions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Copilot CLI sessions&lt;/strong&gt; now indicate whether they were created by VS Code or externally — handy when you&amp;rsquo;re jumping between terminals.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Copilot CLI, Claude Code, and Gemini CLI&lt;/strong&gt; are recognized as shell types. The editor knows what you&amp;rsquo;re running.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-takeaway"&gt;The takeaway&lt;/h2&gt;
&lt;p&gt;VS Code 1.117 isn&amp;rsquo;t a flashy feature dump. It&amp;rsquo;s infrastructure. Worktree isolation, persistent permissions, subagent protocols — these are the building blocks for a workflow where agents handle real, parallel tasks without stepping on your code.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re building with .NET and haven&amp;rsquo;t leaned into the agentic workflow yet, honestly, now&amp;rsquo;s the time to start.&lt;/p&gt;</content:encoded></item><item><title>Foundry's RFT Just Got Cheaper and Smarter — Here's What Changed</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/foundry-fine-tuning-april-2026-rft-graders/</link><pubDate>Sat, 18 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/foundry-fine-tuning-april-2026-rft-graders/</guid><description>Microsoft Foundry shipped three RFT updates this month: global training for o4-mini, new GPT-4.1 model graders, and a best practices guide that'll save you hours of debugging.</description><content:encoded>&lt;p&gt;If you&amp;rsquo;re building .NET apps that rely on fine-tuned models, this month&amp;rsquo;s Foundry updates are worth paying attention to. Reinforcement Fine-Tuning just got more accessible and significantly cheaper.&lt;/p&gt;
&lt;p&gt;The full details are in the &lt;a href="https://devblogs.microsoft.com/foundry/whats-new-in-foundry-finetune-april-2026/"&gt;official announcement&lt;/a&gt;, but here&amp;rsquo;s the practical breakdown.&lt;/p&gt;
&lt;h2 id="global-training-for-o4-mini"&gt;Global Training for o4-mini&lt;/h2&gt;
&lt;p&gt;o4-mini is the go-to model for reasoning-heavy and agentic workloads. The big news: you can now launch fine-tuning jobs from 13+ Azure regions with lower per-token training rates compared to Standard training. Same infrastructure, same quality, broader reach.&lt;/p&gt;
&lt;p&gt;If your team is spread across geographies, this matters. You&amp;rsquo;re no longer pinned to a handful of regions to train.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the REST API call to kick off a global training job:&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;curl -X POST &lt;span class="s2"&gt;&amp;#34;https://&amp;lt;your-resource&amp;gt;.openai.azure.com/openai/fine_tuning/jobs?api-version=2025-04-01-preview&amp;#34;&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; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&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; -H &lt;span class="s2"&gt;&amp;#34;api-key: &lt;/span&gt;&lt;span class="nv"&gt;$AZURE_OPENAI_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&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; -d &lt;span class="s1"&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;model&amp;#34;: &amp;#34;o4-mini&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;training_file&amp;#34;: &amp;#34;&amp;lt;your-training-file-id&amp;gt;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;method&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;type&amp;#34;: &amp;#34;reinforcement&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;reinforcement&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;grader&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;type&amp;#34;: &amp;#34;string_check&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;name&amp;#34;: &amp;#34;answer-check&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;input&amp;#34;: &amp;#34;{{sample.output_text}}&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;reference&amp;#34;: &amp;#34;{{item.reference_answer}}&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;operation&amp;#34;: &amp;#34;eq&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;hyperparameters&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;n_epochs&amp;#34;: 2,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;compute_multiplier&amp;#34;: 1.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;trainingType&amp;#34;: &amp;#34;globalstandard&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That &lt;code&gt;trainingType: globalstandard&lt;/code&gt; flag is the key difference.&lt;/p&gt;
&lt;h2 id="new-model-graders-gpt-41-family"&gt;New Model Graders: GPT-4.1 Family&lt;/h2&gt;
&lt;p&gt;Graders define the reward signal your model optimizes against. Until now, model-based graders were limited to a smaller set of models. Now you get three new options: GPT-4.1, GPT-4.1-mini, and GPT-4.1-nano.&lt;/p&gt;
&lt;p&gt;When should you reach for model graders instead of deterministic ones? When your task output is open-ended, when you need partial credit scoring across multiple dimensions, or when you&amp;rsquo;re building agentic workflows where tool-call correctness depends on semantic context.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the thing &amp;ndash; the tiering strategy is practical:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GPT-4.1-nano&lt;/strong&gt; for initial iterations. Low cost, fast feedback loops.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GPT-4.1-mini&lt;/strong&gt; once your grading rubric is stable and you need higher fidelity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GPT-4.1&lt;/strong&gt; for production grading or complex rubrics where every scoring decision counts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can even mix grader types in a single RFT job. Use string-match for the &amp;ldquo;correct answer&amp;rdquo; dimension and a model grader for evaluating reasoning quality. That flexibility is honestly what makes this useful for real workloads.&lt;/p&gt;
&lt;h2 id="the-rft-data-format-gotcha"&gt;The RFT Data Format Gotcha&lt;/h2&gt;
&lt;p&gt;This trips people up. RFT data format is different from SFT. The last message in each row must be a User or Developer role &amp;ndash; not Assistant. The expected answer goes in a top-level key like &lt;code&gt;reference_answer&lt;/code&gt; that the grader references directly.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve been doing supervised fine-tuning and want to switch to RFT, you need to restructure your training data. Don&amp;rsquo;t skip this step or your jobs will fail silently.&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 calling fine-tuned models from your .NET apps through the Azure OpenAI SDK, cheaper training means you can iterate more aggressively. The model grader options mean you can fine-tune for nuanced tasks &amp;ndash; not just exact-match scenarios. And the best practices guide on &lt;a href="https://github.com/microsoft-foundry/fine-tuning/blob/main/Demos/Agentic_RFT_PrivatePreview/RFT_Best_Practice.md"&gt;GitHub&lt;/a&gt; will save you real debugging time.&lt;/p&gt;
&lt;p&gt;Start small. Ten to a hundred samples. Simple grader. Validate the loop. Then scale.&lt;/p&gt;</content:encoded></item><item><title>Global Azure Spain 2026</title><link>https://thedotnetblog.com/events/global-azure-spain-2026/</link><pubDate>Sat, 18 Apr 2026 00:00:00 +0000</pubDate><guid>https://thedotnetblog.com/events/global-azure-spain-2026/</guid><description>The biggest Azure community event in Spain — a full day of sessions on Azure, AI, data, security, and cloud-native development with 38 speakers across 3 tracks.</description><content:encoded>&lt;p&gt;Global Azure Spain 2026 takes place on &lt;strong&gt;April 18, 2026&lt;/strong&gt; at &lt;strong&gt;Kinépolis Diversia&lt;/strong&gt; in Alcobendas, Madrid. It&amp;rsquo;s the largest community-driven Azure event in Spain, bringing together 38 speakers across 3 parallel tracks covering AI agents, Azure networking, Cosmos DB, Fabric, IoT, security, and much more.&lt;/p&gt;
&lt;p&gt;The event runs from &lt;strong&gt;08:30 to 18:30&lt;/strong&gt; and includes keynote, coffee breaks, lunch, and a closing Q&amp;amp;A session.&lt;/p&gt;
&lt;h2 id="highlights-from-the-agenda"&gt;Highlights from the agenda&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Domando Agentes de IA&lt;/strong&gt;: governance, tools, and APIs with Azure AI Foundry and Azure API Management&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Construyendo agentes con LibreChat en Azure&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How Can I Steal Your Data with Azure Private Endpoints&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stop Building APIs. Forge Agents with Azure&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agentic DevOps Meets IoT: Real-Time Systems with Fabric and GitHub Copilot&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;El regreso de los tamagotchis!&lt;/strong&gt;: multi-agent systems in action&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Foundry Control Plane como plataforma de Agentes global&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rompiendo el perímetro: Zero Trust aplicado en Azure&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="tickets"&gt;Tickets&lt;/h2&gt;
&lt;p&gt;Registration is a symbolic donation — the full ticket price goes directly to &lt;strong&gt;Plan International&lt;/strong&gt;, supporting children&amp;rsquo;s rights and equality worldwide. Limited capacity, so grab your spot early.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.eventbrite.es/e/entradas-global-azure-spain-2026-en-madrid-1981594189564"&gt;Get tickets on Eventbrite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://globalazure.es/"&gt;Event website&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="azure-tour-2026"&gt;Azure Tour 2026&lt;/h2&gt;
&lt;p&gt;Beyond Madrid, the Global Azure Tour 2026 also includes stops in &lt;strong&gt;Zaragoza&lt;/strong&gt;, &lt;strong&gt;Tenerife&lt;/strong&gt;, and &lt;strong&gt;Sevilla&lt;/strong&gt;.&lt;/p&gt;</content:encoded></item><item><title>Your AI Experiments on Azure Are Burning Money — Here's How to Fix That</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/cloud-cost-optimization-ai-workloads-azure/</link><pubDate>Sat, 18 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/cloud-cost-optimization-ai-workloads-azure/</guid><description>AI workloads on Azure can get expensive fast. Let's talk about what actually works for keeping costs under control without slowing down your development.</description><content:encoded>&lt;p&gt;If you&amp;rsquo;re building AI-powered apps on Azure right now, you&amp;rsquo;ve probably noticed something: your cloud bill looks different than it used to. Not just higher — weirder. Spiky. Hard to predict.&lt;/p&gt;
&lt;p&gt;Microsoft just published a great piece on &lt;a href="https://azure.microsoft.com/en-us/blog/cloud-cost-optimization-principles-that-still-matter/"&gt;cloud cost optimization principles that still matter&lt;/a&gt;, and honestly, the timing couldn&amp;rsquo;t be better. Because AI workloads have changed the game when it comes to costs.&lt;/p&gt;
&lt;h2 id="why-ai-workloads-hit-different"&gt;Why AI workloads hit different&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the thing. Traditional .NET workloads are relatively predictable. You know your App Service tier, you know your SQL DTUs, you can estimate monthly spend pretty accurately. AI workloads? Not so much.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;re testing multiple models to see which one fits. You&amp;rsquo;re spinning up GPU-backed infrastructure for fine-tuning. You&amp;rsquo;re making API calls to Azure OpenAI where token consumption varies wildly depending on prompt length and user behavior. Every experiment costs real money, and you might run dozens before you land on the right approach.&lt;/p&gt;
&lt;p&gt;That unpredictability is what makes cost optimization critical — not as an afterthought, but from day one.&lt;/p&gt;
&lt;h2 id="management-vs-optimization--know-the-difference"&gt;Management vs. optimization — know the difference&lt;/h2&gt;
&lt;p&gt;One distinction from the article that I think developers overlook: there&amp;rsquo;s a difference between cost &lt;em&gt;management&lt;/em&gt; and cost &lt;em&gt;optimization&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Management is tracking and reporting. You set up budgets in Azure Cost Management, you get alerts, you see dashboards. That&amp;rsquo;s table stakes.&lt;/p&gt;
&lt;p&gt;Optimization is where you actually make decisions. Do you really need that S3 tier, or would S1 handle your load? Is that always-on compute instance sitting idle on weekends? Could you use spot instances for your training jobs?&lt;/p&gt;
&lt;p&gt;As .NET developers, we tend to focus on the code and leave the infrastructure decisions to &amp;ldquo;the ops team.&amp;rdquo; But if you&amp;rsquo;re deploying to Azure, those decisions are your decisions too.&lt;/p&gt;
&lt;h2 id="what-actually-works"&gt;What actually works&lt;/h2&gt;
&lt;p&gt;Based on the article and my own experience, here&amp;rsquo;s what moves the needle:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Know what you&amp;rsquo;re spending and where.&lt;/strong&gt; Tag your resources. Seriously. If you can&amp;rsquo;t tell which project or experiment is eating your budget, you can&amp;rsquo;t optimize anything. Azure Cost Management with proper tagging is your best friend.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Set guardrails before you experiment.&lt;/strong&gt; Use Azure Policy to restrict expensive SKUs in dev/test environments. Set spending limits on your Azure OpenAI deployments. Don&amp;rsquo;t wait until the bill arrives to realize someone left a GPU cluster running over the weekend.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rightsize continuously.&lt;/strong&gt; That VM you picked during prototyping? It&amp;rsquo;s probably wrong for production. Azure Advisor gives you recommendations — actually look at them. Review monthly, not yearly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Think about lifecycle.&lt;/strong&gt; Dev resources should spin down. Test environments don&amp;rsquo;t need to run 24/7. Use auto-shutdown policies. For AI workloads specifically, consider serverless options where you pay per execution instead of keeping compute warm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Measure value, not just cost.&lt;/strong&gt; This one&amp;rsquo;s easy to forget. A model that costs more but delivers significantly better results might be the right call. The goal isn&amp;rsquo;t to spend the least — it&amp;rsquo;s to spend smart.&lt;/p&gt;
&lt;h2 id="the-takeaway"&gt;The takeaway&lt;/h2&gt;
&lt;p&gt;Cloud cost optimization isn&amp;rsquo;t a one-time cleanup. It&amp;rsquo;s a habit. And with AI workloads making spend less predictable than ever, building that habit early saves you from painful surprises later.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re a .NET developer building on Azure, start treating your cloud bill like you treat your code — review it regularly, refactor when it gets messy, and never deploy without understanding what it&amp;rsquo;s going to cost you.&lt;/p&gt;</content:encoded></item><item><title>Docker Sandbox Lets Copilot Agents Refactor Your Code Without Risking Your Machine</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/copilot-docker-sandbox-agentic-refactoring/</link><pubDate>Fri, 17 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/copilot-docker-sandbox-agentic-refactoring/</guid><description>Docker Sandbox gives GitHub Copilot agents a secure microVM to run wild with refactoring — no permission prompts, no risk to your host. Here's why that changes everything for large-scale .NET modernization.</description><content:encoded>&lt;p&gt;If you&amp;rsquo;ve used Copilot&amp;rsquo;s agent mode for anything beyond small edits, you know the pain. Every file write, every terminal command — another permission prompt. Now imagine running that across 50 projects. Not fun.&lt;/p&gt;
&lt;p&gt;The Azure team just dropped a post about &lt;a href="https://devblogs.microsoft.com/all-things-azure/best-of-both-worlds-for-agentic-refactoring-github-copilot-microvms-via-docker-sandbox/"&gt;Docker Sandbox for GitHub Copilot agents&lt;/a&gt;, and honestly, this is one of the most practical agentic tooling improvements I&amp;rsquo;ve seen. It uses microVMs to give Copilot a fully isolated environment where it can go wild — install packages, run builds, execute tests — without touching your host system.&lt;/p&gt;
&lt;h2 id="what-docker-sandbox-actually-gives-you"&gt;What Docker Sandbox actually gives you&lt;/h2&gt;
&lt;p&gt;The core idea is simple: spin up a lightweight microVM with a full Linux environment, sync your workspace into it, and let the Copilot agent operate freely inside. When it&amp;rsquo;s done, changes sync back.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what makes it more than just &amp;ldquo;run stuff in a container&amp;rdquo;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bidirectional workspace sync&lt;/strong&gt; that preserves absolute paths. Your project structure looks identical inside the sandbox. No path-related build failures.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Private Docker daemon&lt;/strong&gt; running inside the microVM. The agent can build and run containers without ever mounting your host&amp;rsquo;s Docker socket. That&amp;rsquo;s a big deal for security.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTP/HTTPS filtering proxies&lt;/strong&gt; that control what the agent can reach on the network. You decide which registries and endpoints are allowed. Supply chain attacks from a rogue &lt;code&gt;npm install&lt;/code&gt; inside the sandbox? Blocked.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;YOLO mode&lt;/strong&gt; — yes, that&amp;rsquo;s what they call it. The agent runs without permission prompts because it literally cannot damage your host. Every destructive action is contained.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="why-net-developers-should-care"&gt;Why .NET developers should care&lt;/h2&gt;
&lt;p&gt;Think about the modernization work so many teams are facing right now. You have a .NET Framework solution with 30 projects, and you need to move it to .NET 9. That&amp;rsquo;s hundreds of file changes — project files, namespace updates, API replacements, NuGet migrations.&lt;/p&gt;
&lt;p&gt;With Docker Sandbox, you can point a Copilot agent at a project, let it refactor freely inside the microVM, run &lt;code&gt;dotnet build&lt;/code&gt; and &lt;code&gt;dotnet test&lt;/code&gt; to validate, and only accept the changes that actually work. No risk of it accidentally nuking your local dev environment while experimenting.&lt;/p&gt;
&lt;p&gt;The post also describes running a &lt;strong&gt;fleet of parallel agents&lt;/strong&gt; — each in its own sandbox — tackling different projects simultaneously. For large .NET solutions or microservice architectures, that&amp;rsquo;s a massive time saver. One agent per service, all running isolated, all validated independently.&lt;/p&gt;
&lt;h2 id="the-security-angle-matters"&gt;The security angle matters&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the thing most people skip over: when you let an AI agent execute arbitrary commands, you&amp;rsquo;re trusting it with your entire machine. Docker Sandbox flips that model. The agent gets full autonomy inside a throwaway environment. The network proxy ensures it can only pull from approved sources. Your host filesystem, Docker daemon, and credentials stay untouched.&lt;/p&gt;
&lt;p&gt;For teams with compliance requirements — and that&amp;rsquo;s most enterprise .NET shops — this is the difference between &amp;ldquo;we can&amp;rsquo;t use agentic AI&amp;rdquo; and &amp;ldquo;we can adopt it safely.&amp;rdquo;&lt;/p&gt;
&lt;h2 id="takeaway"&gt;Takeaway&lt;/h2&gt;
&lt;p&gt;Docker Sandbox solves the fundamental tension of agentic coding: agents need freedom to be useful, but freedom on your host machine is dangerous. MicroVMs give you both. If you&amp;rsquo;re planning any large-scale .NET refactoring or modernization, this is worth setting up now. The combination of Copilot&amp;rsquo;s code intelligence with a safe execution environment is exactly what production teams have been waiting for.&lt;/p&gt;</content:encoded></item><item><title>Stop Babysitting Your Terminal: Aspire's Detached Mode Changes the Workflow</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/aspire-detached-mode-free-your-terminal/</link><pubDate>Fri, 17 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/aspire-detached-mode-free-your-terminal/</guid><description>Aspire 13.2 lets you run your AppHost in the background and take your terminal back. Combined with new CLI commands and agent support, this is a bigger deal than it sounds.</description><content:encoded>&lt;p&gt;Every time you run an Aspire AppHost, your terminal is gone. Locked. Occupied until you Ctrl+C out of it. Need to run a quick command? Open another tab. Want to check logs? Another tab. It&amp;rsquo;s a small friction that adds up fast.&lt;/p&gt;
&lt;p&gt;Aspire 13.2 fixes this. James Newton-King &lt;a href="https://devblogs.microsoft.com/aspire/aspire-detached-mode-and-process-management/"&gt;wrote up the full details&lt;/a&gt;, and honestly, this is one of those features that immediately changes how you work.&lt;/p&gt;
&lt;h2 id="detached-mode-one-command-terminal-back"&gt;Detached mode: one command, terminal back&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;aspire start
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s the shorthand for &lt;code&gt;aspire run --detach&lt;/code&gt;. Your AppHost boots up in the background and you get your terminal back immediately. No extra tabs. No terminal multiplexer. Just your prompt, ready to go.&lt;/p&gt;
&lt;h2 id="managing-whats-running"&gt;Managing what&amp;rsquo;s running&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the thing — running in the background is only useful if you can actually manage what&amp;rsquo;s out there. Aspire 13.2 ships a full set of CLI commands for exactly that:&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;# List all running AppHosts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aspire ps
&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;# Inspect the state of a specific AppHost&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aspire describe
&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;# Stream logs from a running AppHost&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aspire logs
&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;# Stop a specific AppHost&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aspire stop
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This turns the Aspire CLI into a proper process manager. You can start multiple AppHosts, check their status, tail their logs, and shut them down — all from a single terminal session.&lt;/p&gt;
&lt;h2 id="combine-it-with-isolated-mode"&gt;Combine it with isolated mode&lt;/h2&gt;
&lt;p&gt;Detached mode pairs naturally with isolated mode. Want to run two instances of the same project in the background without port conflicts?&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;aspire start --isolated
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aspire start --isolated
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each gets random ports, separate secrets, and its own lifecycle. Use &lt;code&gt;aspire ps&lt;/code&gt; to see both, &lt;code&gt;aspire stop&lt;/code&gt; to kill whichever you&amp;rsquo;re done with.&lt;/p&gt;
&lt;h2 id="why-this-is-huge-for-coding-agents"&gt;Why this is huge for coding agents&lt;/h2&gt;
&lt;p&gt;This is where it gets really interesting. A coding agent working in your terminal can now:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Start the app with &lt;code&gt;aspire start&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Query its state with &lt;code&gt;aspire describe&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Check logs with &lt;code&gt;aspire logs&lt;/code&gt; to diagnose issues&lt;/li&gt;
&lt;li&gt;Stop it with &lt;code&gt;aspire stop&lt;/code&gt; when done&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All without losing the terminal session. Before detached mode, an agent that ran your AppHost would lock itself out of its own terminal. Now it can start, observe, iterate, and clean up — exactly how you&amp;rsquo;d want an autonomous agent to work.&lt;/p&gt;
&lt;p&gt;The Aspire team leaned into this. Running &lt;code&gt;aspire agent init&lt;/code&gt; sets up an Aspire skill file that teaches agents these commands. So tools like Copilot&amp;rsquo;s coding agent can manage your Aspire workloads out of the box.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Detached mode is a workflow upgrade disguised as a simple flag. You stop context-switching between terminals, agents stop blocking themselves, and the new CLI commands give you real visibility into what&amp;rsquo;s running. It&amp;rsquo;s practical, it&amp;rsquo;s clean, and it makes the daily development loop noticeably smoother.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="https://devblogs.microsoft.com/aspire/aspire-detached-mode-and-process-management/"&gt;full post&lt;/a&gt; for all the details and grab Aspire 13.2 with &lt;code&gt;aspire update --self&lt;/code&gt;.&lt;/p&gt;</content:encoded></item><item><title>Azure MCP Tools Are Now Baked Into Visual Studio 2022 — No Extension Required</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-mcp-tools-built-into-visual-studio-2022/</link><pubDate>Thu, 16 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-mcp-tools-built-into-visual-studio-2022/</guid><description>Azure MCP tools ship as part of the Azure development workload in Visual Studio 2022. Over 230 tools, 45 Azure services, zero extensions to install.</description><content:encoded>&lt;p&gt;If you&amp;rsquo;ve been using the Azure MCP tools in Visual Studio through the separate extension, you know the drill — install the VSIX, restart, hope it doesn&amp;rsquo;t break, manage version mismatches. That friction is gone.&lt;/p&gt;
&lt;p&gt;Yun Jung Choi &lt;a href="https://devblogs.microsoft.com/visualstudio/azure-mcp-tools-now-ship-built-into-visual-studio-2022-no-extension-required/"&gt;announced&lt;/a&gt; that Azure MCP tools now ship directly as part of the Azure development workload in Visual Studio 2022. No extension. No VSIX. No restart dance.&lt;/p&gt;
&lt;h2 id="what-this-actually-means"&gt;What this actually means&lt;/h2&gt;
&lt;p&gt;Starting with Visual Studio 2022 version 17.14.30, the Azure MCP Server is bundled with the Azure development workload. If you already have that workload installed, you just need to toggle it on in GitHub Copilot Chat and you&amp;rsquo;re done.&lt;/p&gt;
&lt;p&gt;Over 230 tools across 45 Azure services — accessible directly from the chat window. List your storage accounts, deploy an ASP.NET Core app, diagnose App Service issues, query Log Analytics — all without opening a browser tab.&lt;/p&gt;
&lt;h2 id="why-this-matters-more-than-it-sounds"&gt;Why this matters more than it sounds&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the thing about developer tooling: every extra step is friction, and friction kills adoption. Having MCP as a separate extension meant version mismatches, installation failures, and one more thing to keep updated. Baking it into the workload means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Single update path&lt;/strong&gt; through the Visual Studio Installer&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No version drift&lt;/strong&gt; between the extension and the IDE&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Always current&lt;/strong&gt; — the MCP Server updates with regular VS releases&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For teams standardizing on Azure, this is a big deal. You install the workload once, enable the tools, and they&amp;rsquo;re there for every session.&lt;/p&gt;
&lt;h2 id="what-you-can-do-with-it"&gt;What you can do with it&lt;/h2&gt;
&lt;p&gt;The tools cover the full development lifecycle through Copilot Chat:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Learn&lt;/strong&gt; — ask about Azure services, best practices, architecture patterns&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Design &amp;amp; develop&lt;/strong&gt; — get service recommendations, configure app code&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deploy&lt;/strong&gt; — provision resources and deploy directly from the IDE&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Troubleshoot&lt;/strong&gt; — query logs, check resource health, diagnose production issues&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A quick example — type this in Copilot Chat:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;List my storage accounts in my current subscription.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Copilot calls the Azure MCP tools behind the scenes, queries your subscriptions, and returns a formatted list with names, locations, and SKUs. No portal needed.&lt;/p&gt;
&lt;h2 id="how-to-enable-it"&gt;How to enable it&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Update to Visual Studio 2022 &lt;strong&gt;17.14.30&lt;/strong&gt; or higher&lt;/li&gt;
&lt;li&gt;Make sure the &lt;strong&gt;Azure development&lt;/strong&gt; workload is installed&lt;/li&gt;
&lt;li&gt;Open GitHub Copilot Chat&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Select tools&lt;/strong&gt; button (the two wrenches icon)&lt;/li&gt;
&lt;li&gt;Toggle &lt;strong&gt;Azure MCP Server&lt;/strong&gt; on&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&amp;rsquo;s it. It stays enabled across sessions.&lt;/p&gt;
&lt;h2 id="one-caveat"&gt;One caveat&lt;/h2&gt;
&lt;p&gt;The tools are disabled by default — you need to opt in. And VS 2026-specific tools aren&amp;rsquo;t available in VS 2022. Tool availability also depends on your Azure subscription permissions, same as the portal.&lt;/p&gt;
&lt;h2 id="the-bigger-picture"&gt;The bigger picture&lt;/h2&gt;
&lt;p&gt;This is part of a clear trend: MCP is becoming the standard way to surface cloud tools in developer IDEs. We&amp;rsquo;ve already seen the &lt;a href="https://devblogs.microsoft.com/azure-sdk/announcing-azure-mcp-server-2-0-stable-release/"&gt;Azure MCP Server 2.0 stable release&lt;/a&gt; and MCP integrations across VS Code and other editors. Having it built into Visual Studio&amp;rsquo;s workload system is the natural progression.&lt;/p&gt;
&lt;p&gt;For us .NET developers who live in Visual Studio, this removes yet another reason to context-switch to the Azure portal. And honestly, the less tab-switching, the better.&lt;/p&gt;</content:encoded></item><item><title>Pin Clustering Finally Lands in .NET MAUI Maps — One Property, Zero Pain</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/maui-maps-pin-clustering-finally/</link><pubDate>Thu, 16 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/maui-maps-pin-clustering-finally/</guid><description>.NET MAUI 11 Preview 3 adds native pin clustering to the Map control. One property, separate clustering groups, and tap handling — all built in.</description><content:encoded>&lt;p&gt;You know that moment when you load a map with a hundred pins and the whole thing turns into an unreadable blob? Yeah, that&amp;rsquo;s been the .NET MAUI Maps experience until now. No more.&lt;/p&gt;
&lt;p&gt;David Ortinau &lt;a href="https://devblogs.microsoft.com/dotnet/pin-clustering-in-dotnet-maui-maps/"&gt;just announced&lt;/a&gt; that .NET MAUI 11 Preview 3 ships pin clustering out of the box on Android and iOS/Mac Catalyst. And the best part — it&amp;rsquo;s ridiculously simple to enable.&lt;/p&gt;
&lt;h2 id="one-property-to-rule-them-all"&gt;One property to rule them all&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;maps:Map&lt;/span&gt; &lt;span class="na"&gt;IsClusteringEnabled=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;True&amp;#34;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. Nearby pins get grouped into clusters with a count badge. Zoom in and they expand. Zoom out and they collapse. The kind of behavior users expect from any modern map — and now you get it with a single property.&lt;/p&gt;
&lt;h2 id="independent-clustering-groups"&gt;Independent clustering groups&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s where it gets interesting. Not all pins should cluster together. Coffee shops and parks are different things, and your map should know that.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ClusteringIdentifier&lt;/code&gt; property lets you separate pins into independent groups:&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;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&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;Pin&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;Label&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Pike Place Coffee&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;Location&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;Location&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;47.6097&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;122.3331&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;ClusteringIdentifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;coffee&amp;#34;&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&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;Pin&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;Label&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Occidental Square&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;Location&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;Location&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;47.6064&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;122.3325&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;ClusteringIdentifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;parks&amp;#34;&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;p&gt;Pins with the same identifier cluster together. Different identifiers form independent clusters even when they&amp;rsquo;re geographically close. No identifier? Default group. Clean and predictable.&lt;/p&gt;
&lt;h2 id="handling-cluster-taps"&gt;Handling cluster taps&lt;/h2&gt;
&lt;p&gt;When a user taps a cluster, you get a &lt;code&gt;ClusterClicked&lt;/code&gt; event with everything you need:&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;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClusterClicked&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;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="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;\n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Label&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;await&lt;/span&gt; &lt;span class="n"&gt;DisplayAlert&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;Cluster ({e.Pins.Count} pins)&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;names&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;OK&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="c1"&gt;// Suppress default zoom-to-cluster behavior:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// e.Handled = true;&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;p&gt;The event args give you &lt;code&gt;Pins&lt;/code&gt; (the pins in the cluster), &lt;code&gt;Location&lt;/code&gt; (the geographic center), and &lt;code&gt;Handled&lt;/code&gt; (set to &lt;code&gt;true&lt;/code&gt; if you want to override the default zoom). Simple, practical, exactly what you&amp;rsquo;d expect.&lt;/p&gt;
&lt;h2 id="platform-details-worth-knowing"&gt;Platform details worth knowing&lt;/h2&gt;
&lt;p&gt;On Android, clustering uses a custom grid-based algorithm that recalculates on zoom changes — no external dependencies. On iOS and Mac Catalyst, it leverages native &lt;code&gt;MKClusterAnnotation&lt;/code&gt; support from MapKit, which means smooth, platform-native animations.&lt;/p&gt;
&lt;p&gt;This is one of those cases where the MAUI team made the right call — lean on the platform where it makes sense.&lt;/p&gt;
&lt;h2 id="why-this-matters"&gt;Why this matters&lt;/h2&gt;
&lt;p&gt;Pin clustering has been one of the most requested features in .NET MAUI (&lt;a href="https://github.com/dotnet/maui/issues/11811"&gt;issue #11811&lt;/a&gt;), and for good reason. Every app that shows locations on a map — delivery tracking, store locators, real estate — needs this. Previously you had to build it yourself or pull in a third-party library. Now it&amp;rsquo;s built in.&lt;/p&gt;
&lt;p&gt;For us .NET developers building cross-platform mobile apps, this is the kind of quality-of-life improvement that makes MAUI a genuinely practical choice for map-heavy scenarios.&lt;/p&gt;
&lt;h2 id="get-started"&gt;Get started&lt;/h2&gt;
&lt;p&gt;Install &lt;a href="https://dotnet.microsoft.com/download/dotnet/11.0"&gt;.NET 11 Preview 3&lt;/a&gt; and update the .NET MAUI workload. The &lt;a href="https://github.com/dotnet/maui-samples/tree/main/10.0/UserInterface/Views/Map/MapDemo/WorkingWithMaps"&gt;Maps sample&lt;/a&gt; includes a new Clustering page you can play with right away.&lt;/p&gt;
&lt;p&gt;Go build something with it — and let your maps finally breathe.&lt;/p&gt;</content:encoded></item><item><title>.NET April 2026 Servicing — Security Patches You Should Apply Today</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/dotnet-april-2026-servicing-security-patches/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/dotnet-april-2026-servicing-security-patches/</guid><description>The April 2026 servicing release patches 6 CVEs across .NET 10, .NET 9, .NET 8, and .NET Framework — including two remote code execution vulnerabilities.</description><content:encoded>&lt;p&gt;The &lt;a href="https://devblogs.microsoft.com/dotnet/dotnet-and-dotnet-framework-april-2026-servicing-updates/"&gt;April 2026 servicing updates&lt;/a&gt; for .NET and .NET Framework are out, and this one includes security fixes you&amp;rsquo;ll want to apply soon. Six CVEs patched, including two remote code execution (RCE) vulnerabilities.&lt;/p&gt;
&lt;h2 id="whats-patched"&gt;What&amp;rsquo;s patched&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the quick summary:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CVE&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Affects&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-26171&lt;/td&gt;
&lt;td&gt;Security Feature Bypass&lt;/td&gt;
&lt;td&gt;.NET 10, 9, 8 + .NET Framework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-32178&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Remote Code Execution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;.NET 10, 9, 8 + .NET Framework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-33116&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Remote Code Execution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;.NET 10, 9, 8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-32203&lt;/td&gt;
&lt;td&gt;Denial of Service&lt;/td&gt;
&lt;td&gt;.NET 10, 9, 8 + .NET Framework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-23666&lt;/td&gt;
&lt;td&gt;Denial of Service&lt;/td&gt;
&lt;td&gt;.NET Framework 3.0–4.8.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-32226&lt;/td&gt;
&lt;td&gt;Denial of Service&lt;/td&gt;
&lt;td&gt;.NET Framework 2.0–4.8.1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The two RCE CVEs (CVE-2026-32178 and CVE-2026-33116) affect the broadest range of .NET versions and should be the priority.&lt;/p&gt;
&lt;h2 id="updated-versions"&gt;Updated versions&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;.NET 10&lt;/strong&gt;: 10.0.6&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.NET 9&lt;/strong&gt;: 9.0.15&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.NET 8&lt;/strong&gt;: 8.0.26&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All are available via the usual channels — &lt;a href="https://dotnet.microsoft.com/download/dotnet/10.0"&gt;dotnet.microsoft.com&lt;/a&gt;, container images on MCR, and Linux package managers.&lt;/p&gt;
&lt;h2 id="what-to-do"&gt;What to do&lt;/h2&gt;
&lt;p&gt;Update your projects and CI/CD pipelines to the latest patch versions. If you&amp;rsquo;re running containers, pull the latest images. If you&amp;rsquo;re on .NET Framework, check the &lt;a href="https://learn.microsoft.com/dotnet/framework/release-notes/release-notes"&gt;.NET Framework release notes&lt;/a&gt; for the corresponding patches.&lt;/p&gt;
&lt;p&gt;For those running .NET 10 in production (it&amp;rsquo;s the current release), 10.0.6 is a mandatory update. Same for .NET 9.0.15 and .NET 8.0.26 if you&amp;rsquo;re on those LTS tracks. Two RCE vulnerabilities are not something you postpone.&lt;/p&gt;</content:encoded></item><item><title>Aspire 13.2 Gets MongoDB EF Core and Azure Data Lake — Two Integrations Worth Trying</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/aspire-132-mongodb-efcore-data-lake/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/aspire-132-mongodb-efcore-data-lake/</guid><description>Aspire 13.2 adds MongoDB Entity Framework Core and Azure Data Lake Storage integrations with zero-config health checks and service discovery. Here's what they look like in practice.</description><content:encoded>&lt;p&gt;Aspire 13.2 just landed with &lt;a href="https://devblogs.microsoft.com/aspire/aspire-new-database-integrations/"&gt;two new database integrations&lt;/a&gt; that are worth your attention: MongoDB Entity Framework Core and Azure Data Lake Storage. If you&amp;rsquo;ve been wanting to use EF Core with MongoDB in an Aspire app, or need to wire up data lake workloads with proper service discovery, this release delivers both.&lt;/p&gt;
&lt;h2 id="mongodb-meets-ef-core-in-aspire"&gt;MongoDB meets EF Core in Aspire&lt;/h2&gt;
&lt;p&gt;This is the one I&amp;rsquo;m most excited about. Aspire has supported MongoDB for a while, but it was always the raw driver — no EF Core, no &lt;code&gt;DbContext&lt;/code&gt;, no LINQ queries against your documents. Now you get the full EF Core experience with MongoDB, plus Aspire&amp;rsquo;s automatic health checks and service discovery.&lt;/p&gt;
&lt;p&gt;Setting it up is the typical Aspire pattern. In your AppHost:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mongodb&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;AddMongoDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;mongodb&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 class="n"&gt;WithDataVolume&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;WithLifetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ContainerLifetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Persistent&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;apiService&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;AddProject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ApiService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;api&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 class="n"&gt;WithReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mongodb&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;Then in your consuming project, add the EF Core integration:&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;dotnet add package Aspire.MongoDB.EntityFrameworkCore
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And register your &lt;code&gt;DbContext&lt;/code&gt;:&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;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddMongoDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;mongodb&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;mydb&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;From there, it&amp;rsquo;s standard EF Core. Define your entities, use your &lt;code&gt;DbContext&lt;/code&gt; like you would with any other provider. The integration handles connection pooling, OpenTelemetry traces, and health checks behind the scenes.&lt;/p&gt;
&lt;p&gt;For .NET developers who&amp;rsquo;ve been using MongoDB with the raw driver and manually wiring up connection strings, this is a nice quality-of-life upgrade. You get the full EF Core abstraction without losing Aspire&amp;rsquo;s service discovery.&lt;/p&gt;
&lt;h2 id="azure-data-lake-storage-joins-the-party"&gt;Azure Data Lake Storage joins the party&lt;/h2&gt;
&lt;p&gt;The second big addition is an &lt;a href="https://aspire.dev/integrations/cloud/azure/azure-storage-datalake/"&gt;Azure Data Lake Storage (ADLS) integration&lt;/a&gt;. If you&amp;rsquo;re building data pipelines, ETL processes, or analytics platforms, you can now wire up Data Lake resources the same way you&amp;rsquo;d wire up any other Aspire dependency.&lt;/p&gt;
&lt;p&gt;In the AppHost:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;storage&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;AddAzureStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;azure-storage&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dataLake&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddDataLake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;data-lake&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fileSystem&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddDataLakeFileSystem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;data-lake-file-system&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;analyticsService&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;AddProject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AnalyticsService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;analytics&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 class="n"&gt;WithReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataLake&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;WithReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileSystem&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;In the consuming project:&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;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddAzureDataLakeServiceClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;data-lake&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;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddAzureDataLakeFileSystemClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;data-lake-file-system&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;No manual connection string management, no credential hunting. Aspire provisions resources and injects them. For those of us building cloud-native .NET apps that touch both operational data and analytics workloads, this makes the data lake feel like a first-class citizen in the Aspire model.&lt;/p&gt;
&lt;h2 id="the-small-fixes-that-matter"&gt;The small fixes that matter&lt;/h2&gt;
&lt;p&gt;Beyond the headline features, there are a few quality-of-life improvements worth noting:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MongoDB connection string fix&lt;/strong&gt; — the forward slash before the database name is now handled correctly. If you&amp;rsquo;ve been working around this, you can remove that workaround&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SQL Server exports&lt;/strong&gt; — &lt;code&gt;Aspire.Hosting.SqlServer&lt;/code&gt; now exports additional server configuration options for finer-grained control&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Emulator updates&lt;/strong&gt; — ServiceBus emulator 2.0.0, App Configuration emulator 1.0.2, and CosmosDB&amp;rsquo;s preview emulator now includes a readiness check&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Azure Managed Redis&lt;/strong&gt; — now defaults to &lt;code&gt;rediss://&lt;/code&gt; (Redis Secure), so connections are encrypted out of the box&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last one is subtle but important — encrypted Redis by default means one less thing to configure in production.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Aspire 13.2 is an incremental release, but the MongoDB EF Core and Data Lake integrations fill real gaps. If you&amp;rsquo;ve been waiting for proper EF Core support with MongoDB in Aspire, or needed Data Lake to be a first-class dependency, &lt;a href="https://get.aspire.dev"&gt;upgrade to 13.2&lt;/a&gt; and give them a spin. The &lt;code&gt;aspire add&lt;/code&gt; command scaffolds everything you need.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="https://aspire.dev/whats-new/aspire-13-2/#-integrations-updates"&gt;full release notes&lt;/a&gt; for more details, and check out the &lt;a href="https://aspire.dev/integrations/gallery/"&gt;integration gallery&lt;/a&gt; for the complete list.&lt;/p&gt;</content:encoded></item><item><title>azd update — One Command to Rule All Your Package Managers</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/azd-update-universal-upgrade-command/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/azd-update-universal-upgrade-command/</guid><description>The Azure Developer CLI now has a universal update command that works regardless of how you installed it — winget, Homebrew, Chocolatey, or install script.</description><content:encoded>&lt;p&gt;You know that &amp;ldquo;A new version of azd is available&amp;rdquo; message that pops up every few weeks? The one you dismiss because you can&amp;rsquo;t remember whether you installed &lt;code&gt;azd&lt;/code&gt; via winget, Homebrew, or that curl script you ran six months ago? Yeah, that&amp;rsquo;s finally fixed.&lt;/p&gt;
&lt;p&gt;Microsoft just shipped &lt;a href="https://devblogs.microsoft.com/azure-sdk/azd-update/"&gt;&lt;code&gt;azd update&lt;/code&gt;&lt;/a&gt; — a single command that updates the Azure Developer CLI to the latest version regardless of how you originally installed it. Windows, macOS, Linux — doesn&amp;rsquo;t matter. One command.&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;How it works&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;azd update
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. If you want early access to new features, you can switch to the daily insiders build:&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;azd update --channel daily
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;azd update --channel stable
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The command detects your current installation method and uses the appropriate update mechanism under the hood. No more &amp;ldquo;wait, did I use winget or choco on this machine?&amp;rdquo;&lt;/p&gt;
&lt;h2 id="the-small-catch"&gt;The small catch&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;azd update&lt;/code&gt; ships starting with version 1.23.x. If you&amp;rsquo;re on an older version, you&amp;rsquo;ll need to do one last manual update using your original installation method. After that, &lt;code&gt;azd update&lt;/code&gt; handles everything going forward.&lt;/p&gt;
&lt;p&gt;Check your current version with &lt;code&gt;azd version&lt;/code&gt;. If you need a fresh install, the &lt;a href="https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd"&gt;install docs&lt;/a&gt; have you covered.&lt;/p&gt;
&lt;h2 id="why-it-matters"&gt;Why it matters&lt;/h2&gt;
&lt;p&gt;This is a small quality-of-life improvement, but for those of us who use &lt;code&gt;azd&lt;/code&gt; daily for deploying AI agents and Aspire apps to Azure, staying current means fewer &amp;ldquo;this bug was already fixed in the latest version&amp;rdquo; moments. One less thing to think about.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="https://devblogs.microsoft.com/azure-sdk/azd-update/"&gt;full announcement&lt;/a&gt; and Jon Gallant&amp;rsquo;s &lt;a href="https://blog.jongallant.com/2026/04/azd-update"&gt;deeper dive&lt;/a&gt; for more context.&lt;/p&gt;</content:encoded></item><item><title>Azure DevOps Server April 2026 Patch — PR Completion Fix and Security Updates</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-devops-server-april-2026-patch/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-devops-server-april-2026-patch/</guid><description>Azure DevOps Server gets Patch 3 with a fix for PR completion failures, improved sign-out validation, and restored GitHub Enterprise Server PAT connections.</description><content:encoded>&lt;p&gt;Quick heads-up for teams running self-hosted Azure DevOps Server: Microsoft released &lt;a href="https://devblogs.microsoft.com/devops/april-patches-for-azure-devops-server/"&gt;Patch 3 for April 2026&lt;/a&gt; with three targeted fixes.&lt;/p&gt;
&lt;h2 id="whats-fixed"&gt;What&amp;rsquo;s fixed&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pull request completion failures&lt;/strong&gt; — a null reference exception during work item auto-completion could cause PR merges to fail. If you&amp;rsquo;ve hit random PR completion errors, this is likely the cause&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sign-out redirect validation&lt;/strong&gt; — improved validation during sign-out to prevent potential malicious redirects. This is a security fix worth applying promptly&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Enterprise Server PAT connections&lt;/strong&gt; — creating Personal Access Token connections to GitHub Enterprise Server was broken, now it&amp;rsquo;s restored&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="how-to-update"&gt;How to update&lt;/h2&gt;
&lt;p&gt;Download &lt;a href="https://aka.ms/devopsserverpatch3"&gt;Patch 3&lt;/a&gt; and run the installer. To verify the patch is applied:&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;&amp;lt;patch-installer&amp;gt;.exe CheckInstall
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you&amp;rsquo;re running Azure DevOps Server on-premises, Microsoft strongly recommends staying on the latest patch for both security and reliability. Check the &lt;a href="https://learn.microsoft.com/azure/devops/server/release-notes/azuredevopsserver?view=azure-devops#azure-devops-server-patch-3-release-date-april-14-2026"&gt;release notes&lt;/a&gt; for full details.&lt;/p&gt;</content:encoded></item><item><title>Azure Smart Tier Is GA — Automatic Blob Storage Cost Optimization Without Lifecycle Rules</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-smart-tier-blob-storage-ga/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-smart-tier-blob-storage-ga/</guid><description>Azure Blob Storage smart tier is now generally available, automatically moving objects between hot, cool, and cold tiers based on actual access patterns — no lifecycle rules needed.</description><content:encoded>&lt;p&gt;If you&amp;rsquo;ve ever spent time tuning Azure Blob Storage lifecycle policies and then watched them fall apart when access patterns shifted, this one&amp;rsquo;s for you. Microsoft just announced the &lt;a href="https://azure.microsoft.com/en-us/blog/optimize-object-storage-costs-automatically-with-smart-tier-now-generally-available/"&gt;general availability of smart tier&lt;/a&gt; for Azure Blob and Data Lake Storage — a fully managed tiering capability that automatically moves objects between hot, cool, and cold tiers based on real usage.&lt;/p&gt;
&lt;h2 id="what-smart-tier-actually-does"&gt;What smart tier actually does&lt;/h2&gt;
&lt;p&gt;The concept is straightforward: smart tier continuously evaluates the last access time of each object in your storage account. Frequently accessed data stays in hot, inactive data moves to cool after 30 days, and then to cold after another 60 days. When data is accessed again, it&amp;rsquo;s promoted back to hot immediately. The cycle restarts.&lt;/p&gt;
&lt;p&gt;No lifecycle rules to configure. No access pattern predictions. No manual tuning.&lt;/p&gt;
&lt;p&gt;During the preview, Microsoft reported that &lt;strong&gt;over 50% of smart-tier-managed capacity automatically shifted to cooler tiers&lt;/strong&gt; based on actual access patterns. That&amp;rsquo;s a meaningful cost reduction for large storage accounts.&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 applications that generate logs, telemetry, analytics data, or any kind of growing data estate — and honestly, who isn&amp;rsquo;t — storage costs add up fast. The traditional approach was writing lifecycle management policies, testing them, and then re-tuning when your app&amp;rsquo;s access patterns changed. Smart tier removes that entire workflow.&lt;/p&gt;
&lt;p&gt;Some practical scenarios where this helps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Application telemetry and logs&lt;/strong&gt; — hot when debugging, rarely accessed after a few weeks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data pipelines and ETL outputs&lt;/strong&gt; — accessed heavily during processing, then mostly cold&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User-generated content&lt;/strong&gt; — recent uploads are hot, older content gradually cools&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Backup and archival data&lt;/strong&gt; — accessed occasionally for compliance, mostly idle&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="setting-it-up"&gt;Setting it up&lt;/h2&gt;
&lt;p&gt;Enabling smart tier is a one-time configuration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;New accounts&lt;/strong&gt;: Select smart tier as the default access tier during storage account creation (zonal redundancy required)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Existing accounts&lt;/strong&gt;: Switch the blob access tier from your current default to smart tier&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Objects smaller than 128 KiB stay in hot and don&amp;rsquo;t incur the monitoring fee. For everything else, you pay standard hot/cool/cold capacity rates with no tier transition charges, no early deletion fees, and no data retrieval costs. A monthly monitoring fee per object covers the orchestration.&lt;/p&gt;
&lt;h2 id="the-tradeoff-to-know-about"&gt;The tradeoff to know about&lt;/h2&gt;
&lt;p&gt;Smart tier&amp;rsquo;s tiering rules are static (30 days → cool, 90 days → cold). If you need custom thresholds — say, moving to cool after 7 days for a specific workload — lifecycle rules are still the way to go. And don&amp;rsquo;t mix both: avoid using lifecycle rules on smart-tier-managed objects, as they can conflict.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;This isn&amp;rsquo;t revolutionary, but it solves a real operational headache. If you manage growing blob storage accounts and you&amp;rsquo;re tired of maintaining lifecycle policies, &lt;a href="https://learn.microsoft.com/en-us/azure/storage/blobs/access-tiers-smart"&gt;enable smart tier&lt;/a&gt; and let Azure handle it. It&amp;rsquo;s available today in nearly all zonal public cloud regions.&lt;/p&gt;</content:encoded></item><item><title>Where Should You Host Your AI Agents on Azure? A Practical Decision Guide</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-ai-agent-hosting-options-guide/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-ai-agent-hosting-options-guide/</guid><description>Azure offers six ways to host AI agents — from raw containers to fully managed Foundry Hosted Agents. Here's how to pick the right one for your .NET workload.</description><content:encoded>&lt;p&gt;If you&amp;rsquo;re building AI agents with .NET right now, you&amp;rsquo;ve probably noticed something: there are a &lt;em&gt;lot&lt;/em&gt; of ways to host them on Azure. Container Apps, AKS, Functions, App Service, Foundry Agents, Foundry Hosted Agents — and they all sound reasonable until you actually need to pick one. Microsoft just published a &lt;a href="https://devblogs.microsoft.com/all-things-azure/hostedagent/"&gt;comprehensive guide to Azure AI agent hosting&lt;/a&gt; that clears this up, and I want to break it down from a practical .NET developer perspective.&lt;/p&gt;
&lt;h2 id="the-six-options-at-a-glance"&gt;The six options at a glance&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s how I&amp;rsquo;d summarize the landscape:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;You manage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Container Apps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full container control without K8s complexity&lt;/td&gt;
&lt;td&gt;Observability, state, lifecycle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AKS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enterprise compliance, multi-cluster, custom networking&lt;/td&gt;
&lt;td&gt;Everything (that&amp;rsquo;s the point)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Azure Functions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Event-driven, short-running agent tasks&lt;/td&gt;
&lt;td&gt;Not much — true serverless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;App Service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple HTTP agents, predictable traffic&lt;/td&gt;
&lt;td&gt;Deployment, scaling config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Foundry Agents&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Code-optional agents via portal/SDK&lt;/td&gt;
&lt;td&gt;Almost nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Foundry Hosted Agents&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Custom framework agents with managed infra&lt;/td&gt;
&lt;td&gt;Your agent code only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The first four are general-purpose compute — you &lt;em&gt;can&lt;/em&gt; run agents on them, but they weren&amp;rsquo;t designed for it. The last two are agent-native: they understand conversations, tool calls, and agent lifecycles as first-class concepts.&lt;/p&gt;
&lt;h2 id="foundry-hosted-agents--the-sweet-spot-for-net-agent-developers"&gt;Foundry Hosted Agents — the sweet spot for .NET agent developers&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s what caught my attention. Foundry Hosted Agents sit right in the middle: you get the flexibility of running your own code (Semantic Kernel, Agent Framework, LangGraph — whatever) but the platform handles infrastructure, observability, and conversation management.&lt;/p&gt;
&lt;p&gt;The key piece is the &lt;strong&gt;Hosting Adapter&lt;/strong&gt; — a thin abstraction layer that bridges your agent framework to the Foundry platform. For Microsoft Agent Framework, it looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;azure.ai.agentserver.agentframework&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;from_agent_framework&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;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatAgent&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;chat_client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;AzureAIAgentClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&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="o"&gt;=&lt;/span&gt;&lt;span class="s2"&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;get_local_time&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;__main__&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;from_agent_framework&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&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;That&amp;rsquo;s your entire hosting story. The adapter handles protocol translation, streaming via server-sent events, conversation history, and OpenTelemetry tracing — all automatically. No custom middleware, no manual plumbing.&lt;/p&gt;
&lt;h2 id="deploying-is-genuinely-simple"&gt;Deploying is genuinely simple&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve deployed agents to Container Apps before and it works, but you end up writing a lot of glue code for state management and observability. With Hosted Agents and &lt;code&gt;azd&lt;/code&gt;, the deployment is:&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 the AI agent extension&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;azd ext install azure.ai.agents
&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;# Init from a template&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;azd ai agent init
&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;# Build, push, deploy — done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;azd up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That single &lt;code&gt;azd up&lt;/code&gt; builds your container, pushes it to ACR, provisions the Foundry project, deploys model endpoints, and starts your agent. Five steps collapsed into one command.&lt;/p&gt;
&lt;h2 id="built-in-conversation-management"&gt;Built-in conversation management&lt;/h2&gt;
&lt;p&gt;This is the part that saves the most time in production. Instead of building your own conversation state store, Hosted Agents handle it natively:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Create a persistent conversation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;conversation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&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="c1"&gt;# First turn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;response1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&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;conversation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&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;extra_body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;agent_reference&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;MyAgent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;agent_reference&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="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Remember: my favorite number is 42.&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Second turn — context is preserved&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;response2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&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;conversation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&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;extra_body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;agent_reference&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;MyAgent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;agent_reference&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="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Multiply my favorite number by 10.&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No Redis. No Cosmos DB session store. No custom middleware for message serialization. The platform just handles it.&lt;/p&gt;
&lt;h2 id="my-decision-framework"&gt;My decision framework&lt;/h2&gt;
&lt;p&gt;After going through all six options, here&amp;rsquo;s my quick mental model:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Do you need zero infrastructure?&lt;/strong&gt; → Foundry Agents (portal/SDK, no containers)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do you have custom agent code but want managed hosting?&lt;/strong&gt; → Foundry Hosted Agents&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do you need event-driven, short-lived agent tasks?&lt;/strong&gt; → Azure Functions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do you need maximum container control without K8s?&lt;/strong&gt; → Container Apps&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do you need strict compliance and multi-cluster?&lt;/strong&gt; → AKS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do you have a simple HTTP agent with predictable traffic?&lt;/strong&gt; → App Service&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For most .NET developers building with Semantic Kernel or Microsoft Agent Framework, Hosted Agents is likely the right starting point. You get scale-to-zero, built-in OpenTelemetry, conversation management, and framework flexibility — without managing Kubernetes or wiring up your own observability stack.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;The agent hosting landscape on Azure is maturing fast. If you&amp;rsquo;re starting a new AI agent project today, I&amp;rsquo;d seriously consider Foundry Hosted Agents before reaching for Container Apps or AKS out of habit. The managed infrastructure saves real time, and the hosting adapter pattern lets you keep your framework choice.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href="https://devblogs.microsoft.com/all-things-azure/hostedagent/"&gt;full guide from Microsoft&lt;/a&gt; and the &lt;a href="https://github.com/microsoft-foundry/foundry-samples/tree/main/samples/python/hosted-agents"&gt;Foundry Samples repo&lt;/a&gt; for working examples.&lt;/p&gt;</content:encoded></item><item><title>Agent Skills in .NET Just Got Seriously Flexible</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/agent-skills-dotnet-three-authoring-patterns/</link><pubDate>Tue, 14 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/agent-skills-dotnet-three-authoring-patterns/</guid><description>The Microsoft Agent Framework now supports three ways to author skills — files, classes, and inline code — all composed through a single provider. Here's why that matters and how to use each one.</description><content:encoded>&lt;p&gt;If you&amp;rsquo;ve been building agents with the Microsoft Agent Framework, you know the drill: you define skills, wire them into a provider, and let the agent figure out which one to invoke. What&amp;rsquo;s new is &lt;em&gt;how&lt;/em&gt; you author those skills — and the flexibility jump is significant.&lt;/p&gt;
&lt;p&gt;The latest update introduces three distinct authoring patterns for agent skills: &lt;strong&gt;file-based&lt;/strong&gt;, &lt;strong&gt;class-based&lt;/strong&gt;, and &lt;strong&gt;inline code-defined&lt;/strong&gt;. All three plug into a single &lt;code&gt;AgentSkillsProviderBuilder&lt;/code&gt;, meaning you can mix and match without any routing logic or special glue code. Let me walk you through each one and when you&amp;rsquo;d reach for it.&lt;/p&gt;
&lt;h2 id="file-based-skills-the-starting-point"&gt;File-based skills: the starting point&lt;/h2&gt;
&lt;p&gt;File-based skills are exactly what they sound like — a directory on disk with a &lt;code&gt;SKILL.md&lt;/code&gt; file, optional scripts, and reference documents. Think of it as the most straightforward way to give your agent new capabilities:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;skills/
└── onboarding-guide/
├── SKILL.md
├── scripts/
│ └── check-provisioning.py
└── references/
└── onboarding-checklist.md
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;SKILL.md&lt;/code&gt; frontmatter declares the skill name and description, and the instructions section tells the agent how to use the scripts and references:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&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;name: onboarding-guide
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;description: &amp;gt;-
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Walk new hires through their first-week setup checklist.
&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## Instructions
&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="k"&gt;1.&lt;/span&gt; Ask for the employee&amp;#39;s name and start date.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;2.&lt;/span&gt; Run &lt;span class="sb"&gt;`scripts/check-provisioning.py`&lt;/span&gt; to verify accounts.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;3.&lt;/span&gt; Walk through &lt;span class="sb"&gt;`references/onboarding-checklist.md`&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;4.&lt;/span&gt; Follow up on incomplete items.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then you wire it up with &lt;code&gt;SubprocessScriptRunner.RunAsync&lt;/code&gt; for script execution:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;skillsProvider&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;AgentSkillsProvider&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;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseDirectory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;skills&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;SubprocessScriptRunner&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&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="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 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 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;GetResponsesClient&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 class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ChatClientAgentOptions&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;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;HRAgent&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;ChatOptions&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="p"&gt;{&lt;/span&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 HR 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;AIContextProviders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;skillsProvider&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;model&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The agent discovers the skill automatically and invokes the provisioning script when it needs to check account status. Clean and simple.&lt;/p&gt;
&lt;h2 id="class-based-skills-ship-via-nuget"&gt;Class-based skills: ship via NuGet&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s where it gets interesting for teams. Class-based skills derive from &lt;code&gt;AgentClassSkill&amp;lt;T&amp;gt;&lt;/code&gt; and use attributes like &lt;code&gt;[AgentSkillResource]&lt;/code&gt; and &lt;code&gt;[AgentSkillScript]&lt;/code&gt; so the framework discovers everything via reflection:&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="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BenefitsEnrollmentSkill&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AgentClassSkill&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BenefitsEnrollmentSkill&lt;/span&gt;&lt;span class="p"&gt;&amp;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="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="n"&gt;AgentSkillFrontmatter&lt;/span&gt; &lt;span class="n"&gt;Frontmatter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;benefits-enrollment&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="s"&gt;&amp;#34;Enroll an employee in health, dental, or vision plans.&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="kd"&gt;protected&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Instructions&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="m"&gt;1.&lt;/span&gt; &lt;span class="n"&gt;Read&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;plans&lt;/span&gt; &lt;span class="n"&gt;resource&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="m"&gt;2.&lt;/span&gt; &lt;span class="n"&gt;Confirm&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="n"&gt;wants&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="m"&gt;3.&lt;/span&gt; &lt;span class="n"&gt;Use&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;enroll&lt;/span&gt; &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;complete&lt;/span&gt; &lt;span class="n"&gt;enrollment&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;&amp;#34;&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&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; [AgentSkillResource(&amp;#34;available-plans&amp;#34;)]&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;Plan options with monthly pricing.&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;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;AvailablePlans&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;##&lt;/span&gt; &lt;span class="n"&gt;Available&lt;/span&gt; &lt;span class="n"&gt;Plans&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2026&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;Health&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Basic&lt;/span&gt; &lt;span class="n"&gt;HMO&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;Premium&lt;/span&gt; &lt;span class="n"&gt;PPO&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;45&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;month&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;Dental&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Standard&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;Enhanced&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;month&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;Vision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Basic&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;month&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;&amp;#34;&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&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; [AgentSkillScript(&amp;#34;enroll&amp;#34;)]&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;Enrolls employee in the specified benefit plan.&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;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Enroll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;employeeId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;planCode&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="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;success&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HrClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EnrollInPlan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employeeId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;planCode&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;return&lt;/span&gt; &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Serialize&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;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employeeId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;planCode&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="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The beauty here is that a team can package this as a NuGet package. You add it to your project, drop it into the builder, and it works alongside your file-based skills with zero coordination:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;skillsProvider&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;AgentSkillsProviderBuilder&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;UseFileSkill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseDirectory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;skills&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 class="n"&gt;UseSkill&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;BenefitsEnrollmentSkill&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;UseFileScriptRunner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SubprocessScriptRunner&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="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;Both skills show up in the agent&amp;rsquo;s system prompt. The agent decides which one to use based on the conversation — no routing code needed.&lt;/p&gt;
&lt;h2 id="inline-skills-the-quick-bridge"&gt;Inline skills: the quick bridge&lt;/h2&gt;
&lt;p&gt;You know that moment when another team is building exactly the skill you need, but it won&amp;rsquo;t ship for a sprint? &lt;code&gt;AgentInlineSkill&lt;/code&gt; is your bridge:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;timeOffSkill&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;AgentInlineSkill&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;time-off-balance&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;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Calculate remaining vacation and sick days.&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;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="m"&gt;1.&lt;/span&gt; &lt;span class="n"&gt;Ask&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="n"&gt;provided&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="m"&gt;2.&lt;/span&gt; &lt;span class="n"&gt;Use&lt;/span&gt; &lt;span class="n"&gt;calculate&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="n"&gt;balance&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="m"&gt;3.&lt;/span&gt; &lt;span class="n"&gt;Present&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt; &lt;span class="n"&gt;clearly&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;&amp;#34;&amp;#34;)
&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;AddScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;calculate-balance&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;employeeId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;leaveType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;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="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;totalDays&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HrDatabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetAnnualAllowance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employeeId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;leaveType&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;int&lt;/span&gt; &lt;span class="n"&gt;daysUsed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HrDatabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetDaysUsed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employeeId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;leaveType&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;int&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;totalDays&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;daysUsed&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;return&lt;/span&gt; &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Serialize&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;employeeId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;leaveType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;totalDays&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;daysUsed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;remaining&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;p&gt;Add it to the builder just like the others:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;skillsProvider&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;AgentSkillsProviderBuilder&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;UseFileSkill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseDirectory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;skills&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 class="n"&gt;UseSkill&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;BenefitsEnrollmentSkill&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;UseSkill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeOffSkill&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;UseFileScriptRunner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SubprocessScriptRunner&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="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;When the NuGet package eventually ships, you swap out the inline skill for the class-based one. The agent doesn&amp;rsquo;t know the difference.&lt;/p&gt;
&lt;p&gt;But inline skills aren&amp;rsquo;t just for bridges. They&amp;rsquo;re also the right choice when you need to generate skills dynamically at runtime — think one skill per business unit loaded from config — or when a script needs to close over local state that doesn&amp;rsquo;t belong in a DI container.&lt;/p&gt;
&lt;h2 id="script-approval-human-in-the-loop"&gt;Script approval: human-in-the-loop&lt;/h2&gt;
&lt;p&gt;For us .NET developers building production agents, this is the part that actually unblocks deployment conversations. Some scripts have real consequences — enrolling someone in benefits, querying production infra. Flip on &lt;code&gt;UseScriptApproval&lt;/code&gt; and the agent pauses before executing any script:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;skillsProvider&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;AgentSkillsProviderBuilder&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;UseFileSkill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseDirectory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;skills&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 class="n"&gt;UseSkill&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;BenefitsEnrollmentSkill&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;UseSkill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeOffSkill&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;UseFileScriptRunner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SubprocessScriptRunner&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="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseScriptApproval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&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;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;When the agent wants to run a script, it returns an approval request instead. Your app collects the decision — approve or reject — and the agent continues accordingly. In regulated environments, this is the difference between &amp;ldquo;we can deploy this&amp;rdquo; and &amp;ldquo;legal says no.&amp;rdquo;&lt;/p&gt;
&lt;h2 id="why-this-combination-matters"&gt;Why this combination matters&lt;/h2&gt;
&lt;p&gt;The real power isn&amp;rsquo;t any single authoring pattern — it&amp;rsquo;s the composition. You can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Start small&lt;/strong&gt; with a file-based skill, iterate on the instructions, and ship it without writing C#&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ship reusable skills&lt;/strong&gt; as NuGet packages that other teams can add with one line&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bridge gaps&lt;/strong&gt; with inline skills when you need something &lt;em&gt;now&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Filter shared skill directories&lt;/strong&gt; with predicates so your agent only loads what it should&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add human oversight&lt;/strong&gt; for scripts that touch production systems&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these compose through &lt;code&gt;AgentSkillsProviderBuilder&lt;/code&gt;. No special routing, no conditional logic, no skill type checks.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Agent skills in .NET now have a genuinely flexible authoring model. Whether you&amp;rsquo;re a solo developer sketching out a prototype with file-based skills or an enterprise team shipping packaged capabilities via NuGet, the patterns fit. And the script approval mechanism makes it production-ready for environments where you need that human checkpoint.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href="https://devblogs.microsoft.com/agent-framework/agent-skills-in-net-three-ways-to-author-one-provider-to-run-them/"&gt;original announcement&lt;/a&gt; for the full walkthrough, the &lt;a href="https://learn.microsoft.com/en-us/agent-framework/agents/skills"&gt;Agent Skills documentation&lt;/a&gt; on Microsoft Learn, and the &lt;a href="https://github.com/microsoft/agent-framework/tree/main/dotnet/samples/02-agents/AgentSkills"&gt;.NET samples on GitHub&lt;/a&gt; to get hands-on.&lt;/p&gt;</content:encoded></item><item><title>Azure MCP Server 2.0 Just Dropped — Self-Hosted Agentic Cloud Automation Is Here</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-mcp-server-2-self-hosted-agentic-cloud/</link><pubDate>Sat, 11 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-mcp-server-2-self-hosted-agentic-cloud/</guid><description>Azure MCP Server 2.0 goes stable with self-hosted remote deployments, 276 tools across 57 Azure services, and enterprise-grade security — here's what matters for .NET developers building agentic workflows.</description><content:encoded>&lt;p&gt;If you&amp;rsquo;ve been building anything with MCP and Azure lately, you probably already know the local experience works well. Plug in an MCP server, let your AI agent talk to Azure resources, move on. But the moment you need to share that setup across a team? That&amp;rsquo;s where things got complicated.&lt;/p&gt;
&lt;p&gt;Not anymore. Azure MCP Server &lt;a href="https://devblogs.microsoft.com/azure-sdk/announcing-azure-mcp-server-2-0-stable-release/"&gt;just hit 2.0 stable&lt;/a&gt;, and the headline feature is exactly what enterprise teams have been asking for: &lt;strong&gt;self-hosted remote MCP server support&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="whats-azure-mcp-server"&gt;What&amp;rsquo;s Azure MCP Server?&lt;/h2&gt;
&lt;p&gt;Quick refresher. Azure MCP Server implements the &lt;a href="https://modelcontextprotocol.io/docs/getting-started/intro"&gt;Model Context Protocol&lt;/a&gt; specification and exposes Azure capabilities as structured, discoverable tools that AI agents can invoke. Think of it as a standardized bridge between your agent and Azure — provisioning, deployment, monitoring, diagnostics, all through one consistent interface.&lt;/p&gt;
&lt;p&gt;The numbers speak for themselves: &lt;strong&gt;276 MCP tools across 57 Azure services&lt;/strong&gt;. That&amp;rsquo;s serious coverage.&lt;/p&gt;
&lt;h2 id="the-big-deal-self-hosted-remote-deployments"&gt;The big deal: self-hosted remote deployments&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the thing. Running MCP locally on your machine is fine for dev and experiments. But in a real team scenario, you need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Shared access for developers and internal agent systems&lt;/li&gt;
&lt;li&gt;Centralized configuration (tenant context, subscription defaults, telemetry)&lt;/li&gt;
&lt;li&gt;Enterprise network and policy boundaries&lt;/li&gt;
&lt;li&gt;Integration into CI/CD pipelines&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Azure MCP Server 2.0 addresses all of this. You can deploy it as a centrally managed internal service with HTTP-based transport, proper authentication, and consistent governance.&lt;/p&gt;
&lt;p&gt;For auth, you get two solid options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Managed Identity&lt;/strong&gt; — when running alongside &lt;a href="https://aka.ms/azmcp/self-host/foundry"&gt;Microsoft Foundry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;On-Behalf-Of (OBO) flow&lt;/strong&gt; — OpenID Connect delegation that calls Azure APIs using the signed-in user&amp;rsquo;s context&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That OBO flow is particularly interesting for us .NET developers. It means your agentic workflows can operate with the user&amp;rsquo;s actual permissions, not some over-privileged service account. Principle of least privilege, built right in.&lt;/p&gt;
&lt;h2 id="security-hardening"&gt;Security hardening&lt;/h2&gt;
&lt;p&gt;This isn&amp;rsquo;t just a feature release — it&amp;rsquo;s a security one too. The 2.0 release adds:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stronger endpoint validation&lt;/li&gt;
&lt;li&gt;Protections against injection patterns in query-oriented tools&lt;/li&gt;
&lt;li&gt;Tighter isolation controls for dev environments&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;rsquo;re going to expose MCP as a shared service, these safeguards matter. A lot.&lt;/p&gt;
&lt;h2 id="where-can-you-use-it"&gt;Where can you use it?&lt;/h2&gt;
&lt;p&gt;The client compatibility story is broad. Azure MCP Server 2.0 works with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IDEs&lt;/strong&gt;: VS Code, Visual Studio, IntelliJ, Eclipse, Cursor&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CLI agents&lt;/strong&gt;: GitHub Copilot CLI, Claude Code&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Standalone&lt;/strong&gt;: local server for simple setups&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Self-hosted remote&lt;/strong&gt;: the new star of 2.0&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Plus there&amp;rsquo;s sovereign cloud support for Azure US Government and Azure operated by 21Vianet, which is critical for regulated deployments.&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 agentic applications with .NET — whether that&amp;rsquo;s Semantic Kernel, Microsoft Agent Framework, or your own orchestration — Azure MCP Server 2.0 gives you a production-ready way to let your agents interact with Azure infrastructure. No custom REST wrappers. No service-specific integration patterns. Just MCP.&lt;/p&gt;
&lt;p&gt;Combined with the &lt;a href="https://devblogs.microsoft.com/azure-sdk/mcp-as-easy-as-1-2-3-introducing-the-fluent-api-for-mcp-apps/"&gt;fluent API for MCP Apps&lt;/a&gt; that dropped a few days ago, the .NET MCP ecosystem is maturing fast.&lt;/p&gt;
&lt;h2 id="getting-started"&gt;Getting started&lt;/h2&gt;
&lt;p&gt;Pick your path:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://aka.ms/azmcp"&gt;GitHub Repo&lt;/a&gt;&lt;/strong&gt; — source code, docs, everything&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://aka.ms/azmcp/download/docker"&gt;Docker Image&lt;/a&gt;&lt;/strong&gt; — containerized deployment&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://aka.ms/azmcp/download/vscode"&gt;VS Code Extension&lt;/a&gt;&lt;/strong&gt; — IDE integration&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://aka.ms/azmcp/self-host"&gt;Self-hosting guide&lt;/a&gt;&lt;/strong&gt; — the 2.0 flagship feature&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Azure MCP Server 2.0 is exactly the kind of infrastructure upgrade that doesn&amp;rsquo;t look flashy in a demo but changes everything in practice. Self-hosted remote MCP with proper auth, security hardening, and sovereign cloud support means MCP is ready for real teams building real agentic workflows on Azure. If you&amp;rsquo;ve been waiting for the &amp;ldquo;enterprise-ready&amp;rdquo; signal — this is it.&lt;/p&gt;</content:encoded></item><item><title>.NET Aspire 13.2 Wants to Be Your AI Agent's Best Friend</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/aspire-agentic-development-build-run-observe/</link><pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/aspire-agentic-development-build-run-observe/</guid><description>Aspire 13.2 goes all-in on agentic development — structured CLI output, isolated runs, auto-healing environments, and full OpenTelemetry data so your AI agents can actually build, run, and observe your apps.</description><content:encoded>&lt;p&gt;You know that moment when your AI coding agent writes some solid code, you get excited, and then it completely falls apart trying to actually &lt;em&gt;run&lt;/em&gt; the thing? Port conflicts, phantom processes, wrong environment variables — suddenly your agent is burning tokens troubleshooting startup issues instead of building features.&lt;/p&gt;
&lt;p&gt;The Aspire team just dropped a &lt;a href="https://devblogs.microsoft.com/aspire/agentic-dev-aspirations/"&gt;really thoughtful post&lt;/a&gt; about exactly this problem, and their answer is compelling: Aspire 13.2 is designed not just for humans, but for AI agents.&lt;/p&gt;
&lt;h2 id="the-problem-is-real"&gt;The problem is real&lt;/h2&gt;
&lt;p&gt;AI agents are incredible at writing code. But shipping a working full-stack app involves way more than generating files. You need to start services in the right order, manage ports, set environment variables, connect databases, and get feedback when things break. Right now, most agents handle all of this through trial-and-error — running commands, reading error output, trying again.&lt;/p&gt;
&lt;p&gt;We layer on Markdown instructions, custom skills, and prompts to try to guide them, but those are unpredictable, can&amp;rsquo;t be compiled, and cost tokens just to parse. The Aspire team nailed the core insight: agents need &lt;strong&gt;compilers and structured APIs&lt;/strong&gt;, not more Markdown.&lt;/p&gt;
&lt;h2 id="aspire-as-agent-infrastructure"&gt;Aspire as agent infrastructure&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s what Aspire 13.2 brings to the agentic development table:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Your entire stack in typed code.&lt;/strong&gt; The AppHost defines your full topology — API, frontend, database, cache — in compilable TypeScript or C#:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createBuilder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;./.modules/aspire.js&amp;#39;&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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;createBuilder&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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;postgres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addPostgres&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;addDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;catalog&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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addRedis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cache&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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&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="nx"&gt;addNodeApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;api&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;./api&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;src/index.ts&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 class="nx"&gt;withHttpEndpoint&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PORT&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 class="nx"&gt;withReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postgres&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="nx"&gt;withReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cache&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&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="nx"&gt;addViteApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;frontend&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;./frontend&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 class="nx"&gt;withReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&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="nx"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;run&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;An agent can read this to understand app topology, add resources, wire up connections, and &lt;em&gt;build to verify&lt;/em&gt;. The compiler tells it immediately if something is wrong. No guessing, no trial-and-error with config files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;One command to rule them all.&lt;/strong&gt; Instead of agents juggling &lt;code&gt;docker compose up&lt;/code&gt;, &lt;code&gt;npm run dev&lt;/code&gt;, and database startup scripts, everything is just &lt;code&gt;aspire start&lt;/code&gt;. All resources launch in the right order, on the right ports, with the right configuration. Long-running processes don&amp;rsquo;t hang the agent either — Aspire manages them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Isolated mode for parallel agents.&lt;/strong&gt; With &lt;code&gt;--isolated&lt;/code&gt;, each Aspire run gets its own random ports and separate user secrets. Got multiple agents working across git worktrees? They won&amp;rsquo;t collide. This is huge for tools like VS Code&amp;rsquo;s background agents that spin up parallel environments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Agent eyes through telemetry.&lt;/strong&gt; Here&amp;rsquo;s where it gets really powerful. The Aspire CLI exposes full OpenTelemetry data during development — traces, metrics, structured logs. Your agent isn&amp;rsquo;t just reading console output and hoping for the best. It can trace a failing request across services, profile slow endpoints, and pinpoint exactly where things break. That&amp;rsquo;s production-grade observability in the development loop.&lt;/p&gt;
&lt;h2 id="the-bowling-bumper-analogy"&gt;The bowling bumper analogy&lt;/h2&gt;
&lt;p&gt;The Aspire team uses a great analogy: think of Aspire as bowling lane bumpers for AI agents. If the agent isn&amp;rsquo;t perfect (and it won&amp;rsquo;t be), the bumpers keep it from throwing gutter balls. The stack definition prevents misconfiguration, the compiler catches errors, the CLI handles process management, and the telemetry provides the feedback loop.&lt;/p&gt;
&lt;p&gt;Pair this with something like Playwright CLI, and your agent can actually &lt;em&gt;use&lt;/em&gt; your app — clicking through flows, checking the DOM, seeing broken things in telemetry, fixing the code, restarting, and testing again. Build, run, observe, fix. That&amp;rsquo;s the autonomous development loop we&amp;rsquo;ve been chasing.&lt;/p&gt;
&lt;h2 id="getting-started"&gt;Getting started&lt;/h2&gt;
&lt;p&gt;New to Aspire? Install the CLI from &lt;a href="https://get.aspire.dev"&gt;get.aspire.dev&lt;/a&gt; and follow the &lt;a href="https://aspire.dev/get-started/first-app"&gt;getting started guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Already using Aspire? Run &lt;code&gt;aspire update --self&lt;/code&gt; to get 13.2, then point your favorite coding agent at your repo. You might be surprised how much further it gets with Aspire&amp;rsquo;s guardrails in place.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Aspire 13.2 isn&amp;rsquo;t just a distributed app framework anymore — it&amp;rsquo;s becoming essential agent infrastructure. Structured stack definitions, one-command startup, isolated parallel runs, and real-time telemetry give AI agents exactly what they need to go from writing code to shipping apps.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="https://devblogs.microsoft.com/aspire/agentic-dev-aspirations/"&gt;full post&lt;/a&gt; from the Aspire team for all the details and demo videos.&lt;/p&gt;</content:encoded></item><item><title>Agentic Platform Engineering Is Getting Real — Git-APE Shows How</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/agentic-platform-engineering-git-ape/</link><pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/agentic-platform-engineering-git-ape/</guid><description>Microsoft's Git-APE project puts agentic platform engineering into practice — using GitHub Copilot agents and Azure MCP to turn natural-language requests into validated cloud infrastructure.</description><content:encoded>&lt;p&gt;Platform engineering has been one of those terms that sounds great in conference talks but usually means &amp;ldquo;we built an internal portal and a Terraform wrapper.&amp;rdquo; The real promise — self-service infrastructure that&amp;rsquo;s actually safe, governed, and fast — has always been a few steps away.&lt;/p&gt;
&lt;p&gt;The Azure team just published &lt;a href="https://devblogs.microsoft.com/all-things-azure/putting-agentic-platform-engineering-to-the-test/"&gt;Part 2 of their agentic platform engineering series&lt;/a&gt;, and this one is all about the hands-on implementation. They call it &lt;strong&gt;Git-APE&lt;/strong&gt; (yes, the acronym is intentional), and it&amp;rsquo;s an open-source project that uses GitHub Copilot agents plus Azure MCP servers to turn natural-language requests into validated, deployed infrastructure.&lt;/p&gt;
&lt;h2 id="what-git-ape-actually-does"&gt;What Git-APE actually does&lt;/h2&gt;
&lt;p&gt;The core idea: instead of developers learning Terraform modules, navigating portal UIs, or filing tickets to a platform team, they talk to a Copilot agent. The agent interprets the intent, generates Infrastructure-as-Code, validates it against policies, and deploys — all within VS Code.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the setup:&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;git clone https://github.com/Azure/git-ape
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; git-ape
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Open the workspace in VS Code, and the agent configuration files are auto-discovered by GitHub Copilot. You interact with the agent directly:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;@git-ape deploy a function app with storage in West Europe
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The agent uses Azure MCP Server under the hood to interact with Azure services. The MCP configuration in VS Code settings enables specific capabilities:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&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="nt"&gt;&amp;#34;azureMcp.serverMode&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;namespace&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="nt"&gt;&amp;#34;azureMcp.enabledServices&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&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="s2"&gt;&amp;#34;deploy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;bestpractices&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;group&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="s2"&gt;&amp;#34;subscription&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;functionapp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;storage&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="s2"&gt;&amp;#34;sql&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;monitor&amp;#34;&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="nt"&gt;&amp;#34;azureMcp.readOnly&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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="why-this-matters"&gt;Why this matters&lt;/h2&gt;
&lt;p&gt;For those of us building on Azure, this shifts the platform engineering conversation from &amp;ldquo;how do we build a portal&amp;rdquo; to &amp;ldquo;how do we describe our guardrails as APIs.&amp;rdquo; When your platform&amp;rsquo;s interface is an AI agent, the quality of your constraints and policies becomes the product.&lt;/p&gt;
&lt;p&gt;The Part 1 blog laid out the theory: well-described APIs, control schemas, and explicit guardrails make platforms agent-ready. Part 2 proves it works by shipping actual tooling. The agent doesn&amp;rsquo;t just blindly generate resources — it validates against best practices, respects naming conventions, and applies your organization&amp;rsquo;s policies.&lt;/p&gt;
&lt;p&gt;Clean-up is just as easy:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;@git-ape destroy my-resource-group
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="my-take"&gt;My take&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ll be honest — this one is more about the pattern than the specific tool. Git-APE itself is a demo/reference architecture. But the underlying idea — agents as the interface to your platform, MCP as the protocol, GitHub Copilot as the host — is where enterprise developer experience is heading.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re a platform team looking at how to make your internal tooling agent-friendly, there&amp;rsquo;s no better starting point. And if you&amp;rsquo;re a .NET developer wondering how this connects to your world: the Azure MCP Server and GitHub Copilot agents work with any Azure workload. Your ASP.NET Core API, your .NET Aspire stack, your containerized microservices — all of it can be the target of an agentic deployment flow.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Git-APE is an early but concrete look at agentic platform engineering in practice. Clone the &lt;a href="https://github.com/Azure/git-ape"&gt;repo&lt;/a&gt;, try the demo, and start thinking about how your platform&amp;rsquo;s APIs and policies would need to look for an agent to safely use them.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="https://devblogs.microsoft.com/all-things-azure/putting-agentic-platform-engineering-to-the-test/"&gt;full post&lt;/a&gt; for the walkthrough and video demos.&lt;/p&gt;</content:encoded></item><item><title>Aspire's Isolated Mode Fixes the Port Conflict Nightmare for Parallel Development</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/aspire-isolated-mode-parallel-instances/</link><pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/aspire-isolated-mode-parallel-instances/</guid><description>Aspire 13.2 introduces --isolated mode: random ports, separate secrets, and zero collisions when running multiple instances of the same AppHost. Perfect for AI agents, worktrees, and parallel workflows.</description><content:encoded>&lt;p&gt;If you&amp;rsquo;ve ever tried running two instances of the same project at the same time, you know the pain. Port 8080 is already in use. Port 17370 is taken. Kill something, restart, juggle environment variables — it&amp;rsquo;s a productivity killer.&lt;/p&gt;
&lt;p&gt;This problem is getting worse, not better. AI agents create git worktrees to work independently. Background agents spin up separate environments. Developers checkout the same repo twice for feature branches. Every one of these scenarios hits the same wall: two instances of the same app fighting over the same ports.&lt;/p&gt;
&lt;p&gt;Aspire 13.2 fixes this with a single flag. James Newton-King from the Aspire team &lt;a href="https://devblogs.microsoft.com/aspire/aspire-isolated-mode-parallel-development/"&gt;wrote up the full details&lt;/a&gt;, and it&amp;rsquo;s one of those &amp;ldquo;why didn&amp;rsquo;t we have this sooner&amp;rdquo; features.&lt;/p&gt;
&lt;h2 id="the-fix---isolated"&gt;The fix: &lt;code&gt;--isolated&lt;/code&gt;&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;aspire run --isolated
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. Each run gets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Random ports&lt;/strong&gt; — no more collisions between instances&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Isolated user secrets&lt;/strong&gt; — connection strings and API keys stay separate per instance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No manual port reassignment. No environment variable juggling. Each run gets a fresh, collision-free environment automatically.&lt;/p&gt;
&lt;h2 id="real-scenarios-where-this-shines"&gt;Real scenarios where this shines&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Multiple checkouts.&lt;/strong&gt; You&amp;rsquo;ve got a feature branch in one directory and a bugfix in another:&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;# Terminal 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/projects/my-app-feature
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aspire run --isolated
&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;# Terminal 2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/projects/my-app-bugfix
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aspire run --isolated
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Both run without conflicts. The dashboard shows what&amp;rsquo;s running and where.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Background agents in VS Code.&lt;/strong&gt; When Copilot Chat&amp;rsquo;s background agent creates a git worktree to work on your code independently, it may need to run your Aspire AppHost. Without &lt;code&gt;--isolated&lt;/code&gt;, that&amp;rsquo;s a port collision with your primary worktree. With it, both instances just work.&lt;/p&gt;
&lt;p&gt;The Aspire skill that ships with &lt;code&gt;aspire agent init&lt;/code&gt; automatically instructs agents to use &lt;code&gt;--isolated&lt;/code&gt; when working in worktrees. So Copilot&amp;rsquo;s background agent should handle this out of the box.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Integration tests alongside development.&lt;/strong&gt; Need to run tests against a live AppHost while continuing to build features? Isolated mode gives each context its own ports and config.&lt;/p&gt;
&lt;h2 id="how-it-works-under-the-hood"&gt;How it works under the hood&lt;/h2&gt;
&lt;p&gt;When you pass &lt;code&gt;--isolated&lt;/code&gt;, the CLI generates a unique instance ID for the run. This drives two behaviors:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Port randomization&lt;/strong&gt; — instead of binding to predictable ports defined in your AppHost config, isolated mode picks random available ports for everything — the dashboard, service endpoints, all of it. Service discovery adjusts automatically, so services find each other regardless of which ports they land on.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Secret isolation&lt;/strong&gt; — each isolated run gets its own user secrets store, keyed by the instance ID. Connection strings and API keys from one run don&amp;rsquo;t leak into another.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Your code doesn&amp;rsquo;t need any changes. Aspire&amp;rsquo;s service discovery resolves endpoints at runtime, so everything connects correctly regardless of port assignment.&lt;/p&gt;
&lt;h2 id="when-to-use-it"&gt;When to use it&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;--isolated&lt;/code&gt; when running multiple instances of the same AppHost simultaneously — whether that&amp;rsquo;s parallel development, automated tests, AI agents, or git worktrees. For single-instance development where you prefer predictable ports, regular &lt;code&gt;aspire run&lt;/code&gt; still works fine.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Isolated mode is a small feature that solves a real, increasingly common problem. As AI-assisted development pushes us toward more parallel workflows — multiple agents, multiple worktrees, multiple contexts — the ability to just spin up another instance without fighting over ports is essential.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="https://devblogs.microsoft.com/aspire/aspire-isolated-mode-parallel-development/"&gt;full post&lt;/a&gt; for all the technical details and try it out with &lt;code&gt;aspire update --self&lt;/code&gt; to get 13.2.&lt;/p&gt;</content:encoded></item><item><title>Building Real-Time Multi-Agent UIs That Don't Feel Like a Black Box</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/ag-ui-real-time-multi-agent-ui-maf/</link><pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/ag-ui-real-time-multi-agent-ui-maf/</guid><description>AG-UI and Microsoft Agent Framework team up to give multi-agent workflows a proper frontend — with real-time streaming, human approvals, and full visibility into what your agents are doing.</description><content:encoded>&lt;p&gt;Here&amp;rsquo;s the thing about multi-agent systems: they look incredible in demos. Three agents passing work around, solving problems, making decisions. Then you try to put it in front of actual users and&amp;hellip; silence. A spinning indicator. No idea which agent is doing what or why the system is paused. That&amp;rsquo;s not a product — that&amp;rsquo;s a trust problem.&lt;/p&gt;
&lt;p&gt;The Microsoft Agent Framework team just published a &lt;a href="https://devblogs.microsoft.com/agent-framework/ag-ui-multi-agent-workflow-demo/"&gt;fantastic walkthrough&lt;/a&gt; on pairing MAF workflows with &lt;a href="https://github.com/ag-ui-protocol/ag-ui"&gt;AG-UI&lt;/a&gt;, an open protocol for streaming agent execution events to a frontend over Server-Sent Events. And honestly? This is the kind of bridge we&amp;rsquo;ve been missing.&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-powered apps, you&amp;rsquo;ve probably hit this wall. Your backend orchestration works great — agents hand off to each other, tools fire, decisions get made. But the frontend has no clue what&amp;rsquo;s happening behind the scenes. AG-UI fixes that by defining a standard protocol for streaming agent events (think &lt;code&gt;RUN_STARTED&lt;/code&gt;, &lt;code&gt;STEP_STARTED&lt;/code&gt;, &lt;code&gt;TOOL_CALL_*&lt;/code&gt;, &lt;code&gt;TEXT_MESSAGE_*&lt;/code&gt;) directly to your UI layer over SSE.&lt;/p&gt;
&lt;p&gt;The demo they built is a customer support workflow with three agents: a triage agent that routes requests, a refund agent that handles money stuff, and an order agent that manages replacements. Each agent has its own tools, and the handoff topology is explicitly defined — no &amp;ldquo;figure it out from the prompt&amp;rdquo; vibes.&lt;/p&gt;
&lt;h2 id="the-handoff-topology-is-the-real-star"&gt;The handoff topology is the real star&lt;/h2&gt;
&lt;p&gt;What caught my eye is how &lt;code&gt;HandoffBuilder&lt;/code&gt; lets you declare a directed routing graph between agents:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HandoffBuilder&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="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ag_ui_handoff_workflow_demo&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;participants&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;triage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;refund&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&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;termination_condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;termination_condition&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&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;builder&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_handoff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;triage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;refund&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Refunds, damaged-item claims...&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_handoff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;triage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Replacement, exchange...&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_handoff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;refund&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Replacement logistics needed after refund.&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_handoff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;triage&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;After replacement/shipping tasks complete.&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each &lt;code&gt;add_handoff&lt;/code&gt; creates a directed edge with a natural-language description. The framework generates handoff tools for each agent based on this topology. So routing decisions are grounded in your orchestration structure, not just whatever the LLM feels like doing. That&amp;rsquo;s a huge deal for production reliability.&lt;/p&gt;
&lt;h2 id="human-in-the-loop-that-actually-works"&gt;Human-in-the-loop that actually works&lt;/h2&gt;
&lt;p&gt;The demo showcases two interrupt patterns that any real-world agent app needs:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tool approval interrupts&lt;/strong&gt; — when an agent calls a tool marked with &lt;code&gt;approval_mode=&amp;quot;always_require&amp;quot;&lt;/code&gt;, the workflow pauses and emits an event. The frontend renders an approval modal with the tool name and arguments. No token-burning retry loops — just a clean pause-approve-resume flow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Information request interrupts&lt;/strong&gt; — when an agent needs more context from the user (like an order ID), it pauses and asks. The frontend shows the question, the user responds, and execution resumes from exactly where it stopped.&lt;/p&gt;
&lt;p&gt;Both patterns stream as standard AG-UI events, so your frontend doesn&amp;rsquo;t need custom logic per agent — it just renders whatever event comes through the SSE connection.&lt;/p&gt;
&lt;h2 id="wiring-it-up-is-surprisingly-simple"&gt;Wiring it up is surprisingly simple&lt;/h2&gt;
&lt;p&gt;The integration between MAF and AG-UI is a single function call:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;agent_framework.ag_ui&lt;/span&gt; &lt;span class="kn"&gt;import&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;AgentFrameworkWorkflow&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;add_agent_framework_fastapi_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="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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FastAPI&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;demo_workflow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AgentFrameworkWorkflow&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;workflow_factory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;_thread_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;create_handoff_workflow&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="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ag_ui_handoff_workflow_demo&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;add_agent_framework_fastapi_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="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;demo_workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/handoff_demo&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;workflow_factory&lt;/code&gt; creates a fresh workflow per thread, so each conversation gets isolated state. The endpoint handles all the SSE plumbing automatically. If you&amp;rsquo;re already using FastAPI (or can add it as a lightweight layer), this is almost zero friction.&lt;/p&gt;
&lt;h2 id="my-take"&gt;My take&lt;/h2&gt;
&lt;p&gt;For us .NET developers, the immediate question is: &amp;ldquo;Can I do this in C#?&amp;rdquo; The Agent Framework is available for both .NET and Python, and the AG-UI protocol is language-agnostic (it&amp;rsquo;s just SSE). So while this specific demo uses Python and FastAPI, the pattern translates directly. You could wire up an ASP.NET Core minimal API with SSE endpoints following the same AG-UI event schema.&lt;/p&gt;
&lt;p&gt;The bigger takeaway is that multi-agent UIs are becoming a first-class concern, not an afterthought. If you&amp;rsquo;re building anything where agents interact with humans — customer support, approval workflows, document processing — this combination of MAF orchestration and AG-UI transparency is the pattern to follow.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;AG-UI + Microsoft Agent Framework gives you the best of both worlds: robust multi-agent orchestration on the backend and real-time visibility on the frontend. No more black-box agent interactions.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href="https://devblogs.microsoft.com/agent-framework/ag-ui-multi-agent-workflow-demo/"&gt;full walkthrough&lt;/a&gt; and the &lt;a href="https://github.com/ag-ui-protocol/ag-ui"&gt;AG-UI protocol repo&lt;/a&gt; to dig deeper.&lt;/p&gt;</content:encoded></item><item><title>Connect Your MCP Servers on Azure Functions to Foundry Agents — Here's How</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/foundry-agents-mcp-servers-azure-functions/</link><pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/foundry-agents-mcp-servers-azure-functions/</guid><description>Build your MCP server once, deploy it to Azure Functions, and connect it to Microsoft Foundry agents with proper auth. Your tools work everywhere — VS Code, Cursor, and now enterprise AI agents.</description><content:encoded>&lt;p&gt;Here&amp;rsquo;s something I love about the MCP ecosystem: you build your server once, and it works everywhere. VS Code, Visual Studio, Cursor, ChatGPT — every MCP client can discover and use your tools. Now, Microsoft is adding another consumer to that list: Foundry agents.&lt;/p&gt;
&lt;p&gt;Lily Ma from the Azure SDK team &lt;a href="https://devblogs.microsoft.com/azure-sdk/give-your-foundry-agent-custom-tools-with-mcp-servers-on-azure-functions/"&gt;published a practical guide&lt;/a&gt; on connecting MCP servers deployed to Azure Functions with Microsoft Foundry agents. If you already have an MCP server, this is pure value-add — no rebuilding required.&lt;/p&gt;
&lt;h2 id="why-this-combination-makes-sense"&gt;Why this combination makes sense&lt;/h2&gt;
&lt;p&gt;Azure Functions gives you scalable infrastructure, built-in auth, and serverless billing for hosting MCP servers. Microsoft Foundry gives you AI agents that can reason, plan, and take actions. Connecting the two means your custom tools — querying a database, calling a business API, running validation logic — become capabilities that enterprise AI agents can discover and use autonomously.&lt;/p&gt;
&lt;p&gt;The key point: your MCP server stays the same. You&amp;rsquo;re just adding Foundry as another consumer. The same tools that work in your VS Code setup now power an AI agent your team or customers interact with.&lt;/p&gt;
&lt;h2 id="authentication-options"&gt;Authentication options&lt;/h2&gt;
&lt;p&gt;This is where the post really adds value. Four auth methods depending on your scenario:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Key-based&lt;/strong&gt; (default)&lt;/td&gt;
&lt;td&gt;Development or servers without Entra auth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Microsoft Entra&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Production with managed identities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OAuth identity passthrough&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Production where each user authenticates individually&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unauthenticated&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dev/testing or public data only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For production, Microsoft Entra with agent identity is the recommended path. OAuth identity passthrough is for when user context matters — the agent prompts users to sign in, and each request carries the user&amp;rsquo;s own token.&lt;/p&gt;
&lt;h2 id="setting-it-up"&gt;Setting it up&lt;/h2&gt;
&lt;p&gt;The high-level flow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Deploy your MCP server to Azure Functions&lt;/strong&gt; — samples available for &lt;a href="https://github.com/Azure-Samples/remote-mcp-functions-dotnet"&gt;.NET&lt;/a&gt;, Python, TypeScript, and Java&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enable built-in MCP authentication&lt;/strong&gt; on your function app&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Get your endpoint URL&lt;/strong&gt; — &lt;code&gt;https://&amp;lt;FUNCTION_APP_NAME&amp;gt;.azurewebsites.net/runtime/webhooks/mcp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add the MCP server as a tool in Foundry&lt;/strong&gt; — navigate to your agent in the portal, add a new MCP tool, provide endpoint and credentials&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Then test it in the Agent Builder playground by sending a prompt that would trigger one of your tools.&lt;/p&gt;
&lt;h2 id="my-take"&gt;My take&lt;/h2&gt;
&lt;p&gt;The composability story here is getting really strong. Build your MCP server once in .NET (or Python, TypeScript, Java), deploy to Azure Functions, and every MCP-compatible client can use it — coding tools, chat apps, and now enterprise AI agents. That&amp;rsquo;s a &amp;ldquo;write once, use everywhere&amp;rdquo; pattern that actually works.&lt;/p&gt;
&lt;p&gt;For .NET developers specifically, the &lt;a href="https://github.com/Azure-Samples/remote-mcp-functions-dotnet"&gt;Azure Functions MCP extension&lt;/a&gt; makes this straightforward. You define your tools as Azure Functions, deploy, and you&amp;rsquo;ve got a production-grade MCP server with all the security and scaling Azure Functions provides.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;If you have MCP tools running on Azure Functions, connecting them to Foundry agents is a quick win — your custom tools become enterprise AI capabilities with proper auth and no code changes to the server itself.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="https://devblogs.microsoft.com/azure-sdk/give-your-foundry-agent-custom-tools-with-mcp-servers-on-azure-functions/"&gt;full guide&lt;/a&gt; for step-by-step instructions on each authentication method, and check the &lt;a href="https://learn.microsoft.com/azure/azure-functions/functions-mcp-foundry-tools?tabs=entra%2Cmcp-extension%2Cfoundry"&gt;detailed docs&lt;/a&gt; for production setups.&lt;/p&gt;</content:encoded></item><item><title>GitHub Copilot's Modernization Assessment Is the Best Migration Tool You're Not Using Yet</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/dotnet-modernization-assessment-github-copilot/</link><pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/dotnet-modernization-assessment-github-copilot/</guid><description>GitHub Copilot's modernization extension doesn't just suggest code changes — it produces a full migration assessment with actionable issues, Azure target comparisons, and a collaborative workflow. Here's why the assessment document is the key to everything.</description><content:encoded>&lt;p&gt;Migrating a legacy .NET Framework app to modern .NET is one of those tasks everyone knows they should do but nobody wants to start. It&amp;rsquo;s never just &amp;ldquo;change the target framework.&amp;rdquo; It&amp;rsquo;s APIs that disappeared, packages that don&amp;rsquo;t exist anymore, hosting models that work completely differently, and a million small decisions about what to containerize, what to rewrite, and what to leave alone.&lt;/p&gt;
&lt;p&gt;Jeffrey Fritz just published a &lt;a href="https://devblogs.microsoft.com/dotnet/your-migrations-source-of-truth-the-modernization-assessment/"&gt;deep dive into GitHub Copilot&amp;rsquo;s modernization assessment&lt;/a&gt;, and honestly? This is the best migration tooling I&amp;rsquo;ve seen for .NET. Not because of the code generation — that&amp;rsquo;s table stakes now. Because of the assessment document it produces.&lt;/p&gt;
&lt;h2 id="its-not-just-a-code-suggestion-engine"&gt;It&amp;rsquo;s not just a code suggestion engine&lt;/h2&gt;
&lt;p&gt;The VS Code extension follows an &lt;strong&gt;Assess → Plan → Execute&lt;/strong&gt; model. The assessment phase analyzes your entire codebase and produces a structured document that captures everything: what needs to change, what Azure resources to provision, what deployment model to use. Everything downstream — infrastructure-as-code, containerization, deployment manifests — flows from what the assessment finds.&lt;/p&gt;
&lt;p&gt;The assessment is stored under &lt;code&gt;.github/modernize/assessment/&lt;/code&gt; in your project. Each run produces an independent report, so you build up a history and can track how your migration posture evolves as you fix issues.&lt;/p&gt;
&lt;h2 id="two-ways-to-start"&gt;Two ways to start&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Recommended Assessment&lt;/strong&gt; — the fast path. Pick from curated domains (Java/.NET Upgrade, Cloud Readiness, Security) and get meaningful results without touching configuration. Great for a first look at where your app stands.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Custom Assessment&lt;/strong&gt; — the targeted path. Configure exactly what to analyze: target compute (App Service, AKS, Container Apps), target OS, containerization analysis. Pick multiple Azure targets to compare migration approaches side-by-side.&lt;/p&gt;
&lt;p&gt;That comparison view is genuinely useful. An app with 3 mandatory issues for App Service might have 7 for AKS. Seeing both helps drive the hosting decision before you commit to a migration path.&lt;/p&gt;
&lt;h2 id="the-issue-breakdown-is-actionable"&gt;The issue breakdown is actionable&lt;/h2&gt;
&lt;p&gt;Each issue comes with a criticality level:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mandatory&lt;/strong&gt; — must fix or migration fails&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Potential&lt;/strong&gt; — might impact migration, needs human judgment&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optional&lt;/strong&gt; — recommended improvements, won&amp;rsquo;t block migration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And each issue links to affected files and line numbers, provides a detailed description of what&amp;rsquo;s wrong and why it matters for your target platform, gives concrete remediation steps (not just &amp;ldquo;fix this&amp;rdquo;), and includes links to official documentation.&lt;/p&gt;
&lt;p&gt;You can hand individual issues to developers and they have everything they need to act. That&amp;rsquo;s the difference between a tool that tells you &amp;ldquo;there&amp;rsquo;s a problem&amp;rdquo; and one that tells you how to solve it.&lt;/p&gt;
&lt;h2 id="the-upgrade-paths-covered"&gt;The upgrade paths covered&lt;/h2&gt;
&lt;p&gt;For .NET specifically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;.NET Framework → .NET 10&lt;/li&gt;
&lt;li&gt;ASP.NET → ASP.NET Core&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each upgrade path has detection rules that know which APIs were removed, which patterns have no direct equivalent, and what security issues need attention.&lt;/p&gt;
&lt;p&gt;For teams managing multiple apps, there&amp;rsquo;s also a CLI that supports multi-repo batch assessments — clone all repos, assess them all, get per-app reports plus an aggregated portfolio view.&lt;/p&gt;
&lt;h2 id="my-take"&gt;My take&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re sitting on legacy .NET Framework apps (and let&amp;rsquo;s be real, most enterprise teams are), this is &lt;em&gt;the&lt;/em&gt; tool to start with. The assessment document alone is worth the time — it turns a vague &amp;ldquo;we should modernize&amp;rdquo; into a concrete, prioritized list of work items with clear paths forward.&lt;/p&gt;
&lt;p&gt;The collaborative workflow is smart too: export assessments, share with your team, import them without re-running. Architecture reviews where the decision-makers aren&amp;rsquo;t the ones running the tools? Covered.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;GitHub Copilot&amp;rsquo;s modernization assessment transforms .NET migration from a scary, undefined project into a structured, trackable process. Start with a recommended assessment to see where you stand, then use custom assessments to compare Azure targets and build your migration plan.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="https://devblogs.microsoft.com/dotnet/your-migrations-source-of-truth-the-modernization-assessment/"&gt;full walkthrough&lt;/a&gt; and grab the &lt;a href="https://aka.ms/ghcp-appmod/vscode-ext"&gt;VS Code extension&lt;/a&gt; to try it on your own codebase.&lt;/p&gt;</content:encoded></item><item><title>MCP Apps Get a Fluent API — Build Rich AI Tool UIs in .NET with Three Steps</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/mcp-fluent-api-azure-functions-dotnet/</link><pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/mcp-fluent-api-azure-functions-dotnet/</guid><description>The new fluent configuration API for MCP Apps on Azure Functions lets you turn any .NET MCP tool into a full app with views, permissions, and CSP policies in just a few lines of code.</description><content:encoded>&lt;p&gt;MCP tools are great for giving AI agents capabilities. But what if your tool needs to show something to the user — a dashboard, a form, an interactive visualization? That&amp;rsquo;s where MCP Apps come in, and they just got a lot easier to build.&lt;/p&gt;
&lt;p&gt;Lilian Kasem from the Azure SDK team &lt;a href="https://devblogs.microsoft.com/azure-sdk/mcp-as-easy-as-1-2-3-introducing-the-fluent-api-for-mcp-apps/"&gt;introduced the new fluent configuration API&lt;/a&gt; for MCP Apps on .NET Azure Functions, and it&amp;rsquo;s the kind of developer experience improvement that makes you wonder why it wasn&amp;rsquo;t always this simple.&lt;/p&gt;
&lt;h2 id="what-are-mcp-apps"&gt;What are MCP Apps?&lt;/h2&gt;
&lt;p&gt;MCP Apps extend the Model Context Protocol by letting tools carry their own UI views, static assets, and security controls. Instead of just returning text, your MCP tool can render full HTML experiences — interactive dashboards, data visualizations, configuration forms — all invocable by AI agents and presented to users by MCP clients.&lt;/p&gt;
&lt;p&gt;The catch was that wiring all this up manually required knowing the MCP spec intimately: &lt;code&gt;ui://&lt;/code&gt; URIs, special mime types, metadata coordination between tools and resources. Not hard, but fiddly.&lt;/p&gt;
&lt;h2 id="the-fluent-api-in-three-steps"&gt;The fluent API in three steps&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Step 1: Define your function.&lt;/strong&gt; Just a standard Azure Functions MCP tool:&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;[Function(nameof(HelloApp))]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;HelloApp&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; [McpToolTrigger(&amp;#34;HelloApp&amp;#34;, &amp;#34;A simple MCP App that says hello.&amp;#34;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ToolInvocationContext&lt;/span&gt; &lt;span class="n"&gt;context&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="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Hello from app&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Step 2: Promote it to an MCP App.&lt;/strong&gt; In your program startup:&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;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConfigureMcpTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;HelloApp&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 class="n"&gt;AsMcpApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;app&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;WithView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;assets/hello-app.html&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 class="n"&gt;WithTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello App&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 class="n"&gt;WithPermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;McpAppPermissions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClipboardWrite&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;McpAppPermissions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClipboardRead&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;WithCsp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csp&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;csp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AllowBaseUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://www.microsoft.com&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 class="n"&gt;ConnectTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://www.microsoft.com&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Step 3: Add your HTML view.&lt;/strong&gt; Create &lt;code&gt;assets/hello-app.html&lt;/code&gt; with whatever UI you need.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it. The fluent API handles all the MCP spec plumbing — generating the synthetic resource function, setting the correct mime type, injecting the metadata that connects your tool to its view.&lt;/p&gt;
&lt;h2 id="the-api-surface-is-well-designed"&gt;The API surface is well-designed&lt;/h2&gt;
&lt;p&gt;A few things I really like:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;View sources are flexible.&lt;/strong&gt; You can serve HTML from files on disk, or embed resources directly in your assembly for self-contained deployments:&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;McpViewSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;assets/my-view.html&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;McpViewSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromEmbeddedResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;MyApp.Resources.view.html&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;&lt;strong&gt;CSP is composable.&lt;/strong&gt; You explicitly allowlist origins your app needs, following least-privilege principles. Call &lt;code&gt;WithCsp&lt;/code&gt; multiple times and origins accumulate:&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="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithCsp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csp&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;csp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnectTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://api.example.com&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 class="n"&gt;LoadResourcesFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://cdn.example.com&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 class="n"&gt;AllowFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://youtube.com&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Visibility control.&lt;/strong&gt; You can make a tool visible to the LLM only, the host UI only, or both. Want a tool that only renders UI and shouldn&amp;rsquo;t be called by the model? Easy:&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="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithVisibility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;McpVisibility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// UI-only, hidden from the model&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="getting-started"&gt;Getting started&lt;/h2&gt;
&lt;p&gt;Add the preview package:&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;dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Mcp --version 1.5.0-preview.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you&amp;rsquo;re already building MCP tools with Azure Functions, this is just a package update. The &lt;a href="https://learn.microsoft.com/azure/azure-functions/scenario-mcp-apps?tabs=bash%2Clinux&amp;amp;pivots=programming-language-csharp"&gt;MCP Apps quickstart&lt;/a&gt; is the best place to start if you&amp;rsquo;re new to the concept.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;MCP Apps are one of the more exciting developments in the AI tooling space — tools that don&amp;rsquo;t just &lt;em&gt;do things&lt;/em&gt; but can &lt;em&gt;show things&lt;/em&gt; to users. The fluent API removes the protocol complexity and lets you focus on what matters: your tool&amp;rsquo;s logic and its UI.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="https://devblogs.microsoft.com/azure-sdk/mcp-as-easy-as-1-2-3-introducing-the-fluent-api-for-mcp-apps/"&gt;full post&lt;/a&gt; for the complete API reference and examples.&lt;/p&gt;</content:encoded></item><item><title>Microsoft Foundry March 2026 — GPT-5.4, Agent Service GA, and the SDK Refresh That Changes Everything</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/microsoft-foundry-march-2026-whats-new/</link><pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/microsoft-foundry-march-2026-whats-new/</guid><description>Microsoft Foundry's March 2026 update is massive: Agent Service hits GA, GPT-5.4 brings reliable reasoning, the azure-ai-projects SDK goes stable across all languages, and Fireworks AI brings open models to Azure.</description><content:encoded>&lt;p&gt;The monthly &amp;ldquo;What&amp;rsquo;s New in Microsoft Foundry&amp;rdquo; posts are usually a mix of incremental improvements and the occasional headline feature. The &lt;a href="https://devblogs.microsoft.com/foundry/whats-new-in-microsoft-foundry-mar-2026/"&gt;March 2026 edition&lt;/a&gt;? It&amp;rsquo;s basically all headline features. Foundry Agent Service goes GA, GPT-5.4 ships for production, the SDK gets a major stable release, and Fireworks AI brings open model inference to Azure. Let me break down what matters for .NET developers.&lt;/p&gt;
&lt;h2 id="foundry-agent-service-is-production-ready"&gt;Foundry Agent Service is production-ready&lt;/h2&gt;
&lt;p&gt;This is the big one. The next-gen agent runtime is generally available — built on the OpenAI Responses API, wire-compatible with OpenAI agents, and open to models from multiple providers. If you&amp;rsquo;re building with the Responses API today, migrating to Foundry adds enterprise security, private networking, Entra RBAC, full tracing, and evaluation on top of your existing agent logic.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;azure.ai.projects&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AIProjectClient&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;azure.ai.projects.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PromptAgentDefinition&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;project_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AIProjectClient&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;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;AZURE_AI_PROJECT_ENDPOINT&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;credential&lt;/span&gt;&lt;span class="o"&gt;=&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&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;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;project_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agents&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_version&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;agent_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;my-enterprise-agent&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;definition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PromptAgentDefinition&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;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;AZURE_AI_MODEL_DEPLOYMENT_NAME&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;instructions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&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="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;p&gt;Key additions: end-to-end private networking, MCP auth expansion (including OAuth passthrough), Voice Live preview for speech-to-speech agents, and hosted agents in 6 new regions.&lt;/p&gt;
&lt;h2 id="gpt-54--reliability-over-raw-intelligence"&gt;GPT-5.4 — reliability over raw intelligence&lt;/h2&gt;
&lt;p&gt;GPT-5.4 isn&amp;rsquo;t about being smarter. It&amp;rsquo;s about being more reliable. Stronger reasoning over long interactions, better instruction adherence, fewer mid-workflow failures, and integrated computer use capabilities. For production agents, that reliability matters way more than benchmark scores.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Pricing (per M tokens)&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.4 (≤272K)&lt;/td&gt;
&lt;td&gt;$2.50 / $15 output&lt;/td&gt;
&lt;td&gt;Production agents, coding, document workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.4 Pro&lt;/td&gt;
&lt;td&gt;$30 / $180 output&lt;/td&gt;
&lt;td&gt;Deep analysis, scientific reasoning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.4 Mini&lt;/td&gt;
&lt;td&gt;Cost-effective&lt;/td&gt;
&lt;td&gt;Classification, extraction, lightweight tool calls&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The smart play is a routing strategy: GPT-5.4 Mini handles high-volume, low-latency work while GPT-5.4 takes the reasoning-heavy requests.&lt;/p&gt;
&lt;h2 id="the-sdk-is-finally-stable"&gt;The SDK is finally stable&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;azure-ai-projects&lt;/code&gt; SDK shipped stable releases across all languages — Python 2.0.0, JS/TS 2.0.0, Java 2.0.0, and .NET 2.0.0 (April 1). The &lt;code&gt;azure-ai-agents&lt;/code&gt; dependency is gone — everything lives under &lt;code&gt;AIProjectClient&lt;/code&gt;. Install with &lt;code&gt;pip install azure-ai-projects&lt;/code&gt; and the package bundles &lt;code&gt;openai&lt;/code&gt; and &lt;code&gt;azure-identity&lt;/code&gt; as direct dependencies.&lt;/p&gt;
&lt;p&gt;For .NET developers, this means a single NuGet package for the full Foundry surface. No more juggling separate agent SDKs.&lt;/p&gt;
&lt;h2 id="fireworks-ai-brings-open-models-to-azure"&gt;Fireworks AI brings open models to Azure&lt;/h2&gt;
&lt;p&gt;Perhaps the most architecturally interesting addition: Fireworks AI processing 13+ trillion tokens daily at ~180K requests/second, now available through Foundry. DeepSeek V3.2, gpt-oss-120b, Kimi K2.5, and MiniMax M2.5 at launch.&lt;/p&gt;
&lt;p&gt;The real story is &lt;strong&gt;bring-your-own-weights&lt;/strong&gt; — upload quantized or fine-tuned weights from anywhere without changing the serving stack. Deploy via serverless pay-per-token or provisioned throughput.&lt;/p&gt;
&lt;h2 id="other-highlights"&gt;Other highlights&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Phi-4 Reasoning Vision 15B&lt;/strong&gt; — multimodal reasoning for charts, diagrams, and document layouts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Evaluations GA&lt;/strong&gt; — out-of-the-box evaluators with continuous production monitoring piped into Azure Monitor&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Priority Processing&lt;/strong&gt; (Preview) — dedicated compute lane for latency-sensitive workloads&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Voice Live&lt;/strong&gt; — speech-to-speech runtime that connects directly to Foundry agents&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tracing GA&lt;/strong&gt; — end-to-end agent trace inspection with sort and filter&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PromptFlow deprecation&lt;/strong&gt; — migration to Microsoft Framework Workflows by January 2027&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;March 2026 is a turning point for Foundry. The Agent Service GA, stable SDKs across all languages, GPT-5.4 for reliable production agents, and open model inference via Fireworks AI — the platform is ready for serious workloads.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="https://devblogs.microsoft.com/foundry/whats-new-in-microsoft-foundry-mar-2026/"&gt;full roundup&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/azure/foundry/quickstarts/get-started-code"&gt;build your first agent&lt;/a&gt; to get started.&lt;/p&gt;</content:encoded></item><item><title>SQL MCP Server — The Right Way to Give AI Agents Database Access</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/sql-mcp-server-data-api-builder/</link><pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/sql-mcp-server-data-api-builder/</guid><description>SQL MCP Server from Data API builder gives AI agents secure, deterministic database access without exposing schemas or relying on NL2SQL. RBAC, caching, multi-database support — all built in.</description><content:encoded>&lt;p&gt;Let&amp;rsquo;s be honest: most database MCP servers available today are terrifying. They take a natural language query, generate SQL on the fly, and run it against your production data. What could go wrong? (Everything. Everything could go wrong.)&lt;/p&gt;
&lt;p&gt;The Azure SQL team just &lt;a href="https://devblogs.microsoft.com/azure-sql/introducing-sql-mcp-server/"&gt;introduced SQL MCP Server&lt;/a&gt;, and it takes a fundamentally different approach. Built as a feature of Data API builder (DAB) 2.0, it gives AI agents structured, deterministic access to database operations — without NL2SQL, without exposing your schema, and with full RBAC at every step.&lt;/p&gt;
&lt;h2 id="why-no-nl2sql"&gt;Why no NL2SQL?&lt;/h2&gt;
&lt;p&gt;This is the most interesting design decision. Models aren&amp;rsquo;t deterministic, and complex queries are the most likely to produce subtle errors. The exact queries users hope AI can generate are also the ones that need the most scrutiny when produced non-deterministically.&lt;/p&gt;
&lt;p&gt;Instead, SQL MCP Server uses an &lt;strong&gt;NL2DAB&lt;/strong&gt; approach. The agent works with Data API builder&amp;rsquo;s entity abstraction layer and built-in query builder to produce accurate, well-formed T-SQL deterministically. Same result for the user, but without the risk of hallucinated JOINs or accidental data exposure.&lt;/p&gt;
&lt;h2 id="seven-tools-not-seven-hundred"&gt;Seven tools, not seven hundred&lt;/h2&gt;
&lt;p&gt;SQL MCP Server exposes exactly seven DML tools, regardless of database size:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;describe_entities&lt;/code&gt; — discover available entities and operations&lt;/li&gt;
&lt;li&gt;&lt;code&gt;create_record&lt;/code&gt; — insert rows&lt;/li&gt;
&lt;li&gt;&lt;code&gt;read_records&lt;/code&gt; — query tables and views&lt;/li&gt;
&lt;li&gt;&lt;code&gt;update_record&lt;/code&gt; — modify rows&lt;/li&gt;
&lt;li&gt;&lt;code&gt;delete_record&lt;/code&gt; — remove rows&lt;/li&gt;
&lt;li&gt;&lt;code&gt;execute_entity&lt;/code&gt; — run stored procedures&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aggregate_records&lt;/code&gt; — aggregation queries&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is smart because context windows are the agent&amp;rsquo;s thinking space. Flooding them with hundreds of tool definitions leaves less room for reasoning. Seven fixed tools keep the agent focused on &lt;em&gt;thinking&lt;/em&gt; rather than &lt;em&gt;navigating&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Each tool can be individually enabled or disabled:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;runtime&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&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="nt"&gt;&amp;#34;mcp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&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="nt"&gt;&amp;#34;enabled&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="nt"&gt;&amp;#34;path&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/mcp&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="nt"&gt;&amp;#34;dml-tools&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&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="nt"&gt;&amp;#34;describe-entities&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="nt"&gt;&amp;#34;create-record&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="nt"&gt;&amp;#34;read-records&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="nt"&gt;&amp;#34;update-record&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="nt"&gt;&amp;#34;delete-record&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="nt"&gt;&amp;#34;execute-entity&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="nt"&gt;&amp;#34;aggregate-records&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="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="getting-started-in-three-commands"&gt;Getting started in three commands&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;dab init &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --database-type mssql &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --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;dab add Customers &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --source dbo.Customers &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --permissions &lt;span class="s2"&gt;&amp;#34;anonymous:*&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;dab start
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s a running SQL MCP Server exposing your Customers table. The entity abstraction layer means you can alias names and columns, limit fields per role, and control exactly what agents see — without exposing internal schema details.&lt;/p&gt;
&lt;h2 id="the-security-story-is-solid"&gt;The security story is solid&lt;/h2&gt;
&lt;p&gt;This is where Data API builder&amp;rsquo;s maturity pays off:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;RBAC at every layer&lt;/strong&gt; — each entity defines which roles can read, create, update, or delete, and which fields are visible&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Azure Key Vault integration&lt;/strong&gt; — connection strings and secrets managed securely&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Microsoft Entra + custom OAuth&lt;/strong&gt; — production-grade authentication&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content Security Policy&lt;/strong&gt; — agents interact through a controlled contract, not raw SQL&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The schema abstraction is particularly important. Your internal table and column names never get exposed to the agent. You define entities, aliases, and descriptions that make sense for the AI interaction — not your database ERD.&lt;/p&gt;
&lt;h2 id="multi-database-and-multi-protocol"&gt;Multi-database and multi-protocol&lt;/h2&gt;
&lt;p&gt;SQL MCP Server supports Microsoft SQL, PostgreSQL, Azure Cosmos DB, and MySQL. And because it&amp;rsquo;s a DAB feature, you get REST, GraphQL, and MCP endpoints simultaneously from the same configuration. Same entity definitions, same RBAC rules, same security — across all three protocols.&lt;/p&gt;
&lt;p&gt;Auto-configuration in DAB 2.0 can even inspect your database and build the configuration dynamically, if you&amp;rsquo;re comfortable with less abstraction for rapid prototyping.&lt;/p&gt;
&lt;h2 id="my-take"&gt;My take&lt;/h2&gt;
&lt;p&gt;This is how enterprise database access for AI agents should work. Not &amp;ldquo;hey LLM, write me some SQL and YOLO it against production.&amp;rdquo; Instead: a well-defined entity layer, deterministic query generation, RBAC at every step, caching, monitoring, and telemetry. It&amp;rsquo;s boring in the best possible way.&lt;/p&gt;
&lt;p&gt;For .NET developers, the integration story is clean — DAB is a .NET tool, the MCP Server runs as a container, and it works with Azure SQL, which most of us are already using. If you&amp;rsquo;re building AI agents that need data access, start here.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;SQL MCP Server is free, open-source, and runs anywhere. It&amp;rsquo;s the prescriptive approach from Microsoft for giving AI agents secure database access. Check out the &lt;a href="https://devblogs.microsoft.com/azure-sql/introducing-sql-mcp-server/"&gt;full post&lt;/a&gt; and the &lt;a href="https://aka.ms/sql/mcp"&gt;documentation&lt;/a&gt; to get started.&lt;/p&gt;</content:encoded></item><item><title>That Visual Studio Floating Windows Setting You Didn't Know About (But Should)</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/visual-studio-floating-windows-powertoys/</link><pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/visual-studio-floating-windows-powertoys/</guid><description>A hidden Visual Studio setting gives you full control over floating windows — independent taskbar entries, proper multi-monitor behavior, and perfect FancyZones integration. One dropdown changes everything.</description><content:encoded>&lt;p&gt;If you use multiple monitors with Visual Studio (and honestly, who doesn&amp;rsquo;t these days), you&amp;rsquo;ve probably experienced the annoyance: floating tool windows disappear when you minimize the main IDE, they always stay on top of everything else, and they don&amp;rsquo;t show up as separate taskbar buttons. It works for some workflows, but for multi-monitor setups it&amp;rsquo;s frustrating.&lt;/p&gt;
&lt;p&gt;Mads Kristensen from the Visual Studio team &lt;a href="https://devblogs.microsoft.com/visualstudio/take-full-control-of-your-floating-windows-in-visual-studio/"&gt;shared a little-known setting&lt;/a&gt; that completely changes how floating windows behave. One dropdown. That&amp;rsquo;s it.&lt;/p&gt;
&lt;h2 id="the-setting"&gt;The setting&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Tools &amp;gt; Options &amp;gt; Environment &amp;gt; Windows &amp;gt; Floating Windows&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The dropdown &amp;ldquo;These floating windows are owned by the main window&amp;rdquo; has three options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;None&lt;/strong&gt; — full independence. Every floating window gets its own taskbar entry and behaves like a normal Windows window.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tool Windows&lt;/strong&gt; (default) — documents float freely, tool windows stay tied to the IDE.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documents and Tool Windows&lt;/strong&gt; — classic Visual Studio behavior, everything tied to the main window.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="why-none-is-the-move-for-multi-monitor-setups"&gt;Why &amp;ldquo;None&amp;rdquo; is the move for multi-monitor setups&lt;/h2&gt;
&lt;p&gt;Set it to &lt;strong&gt;None&lt;/strong&gt; and suddenly all your floating tool windows and documents behave like real Windows applications. They appear in the taskbar, stay visible when you minimize the main Visual Studio window, and stop forcing themselves to the front of everything.&lt;/p&gt;
&lt;p&gt;Combine this with &lt;strong&gt;PowerToys FancyZones&lt;/strong&gt; and it&amp;rsquo;s a game changer. Create custom layouts across your monitors, snap your Solution Explorer to one zone, debugger to another, and code files wherever you want. Everything stays put, everything is independently accessible, and your workspace feels organized instead of chaotic.&lt;/p&gt;
&lt;h2 id="quick-recommendations"&gt;Quick recommendations&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multi-monitor power users&lt;/strong&gt;: Set to &lt;strong&gt;None&lt;/strong&gt;, pair with FancyZones&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Occasional floaters&lt;/strong&gt;: &lt;strong&gt;Tool Windows&lt;/strong&gt; (default) is a solid middle ground&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Traditional workflow&lt;/strong&gt;: &lt;strong&gt;Documents and Tool Windows&lt;/strong&gt; keeps everything classic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pro tip: &lt;strong&gt;Ctrl + double-click&lt;/strong&gt; on any tool window title bar to instantly float or dock it. No restart needed after changing the setting.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s one of those &amp;ldquo;I can&amp;rsquo;t believe I didn&amp;rsquo;t know about this&amp;rdquo; settings. If floating windows in Visual Studio have ever annoyed you, go change this right now.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="https://devblogs.microsoft.com/visualstudio/take-full-control-of-your-floating-windows-in-visual-studio/"&gt;full post&lt;/a&gt; for the details and screenshots.&lt;/p&gt;</content:encoded></item><item><title>VS Code 1.116 — Agents App Gets Keyboard Navigation and File Context Completions</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/vscode-1-116-agents-app-updates/</link><pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/vscode-1-116-agents-app-updates/</guid><description>VS Code 1.116 focuses on Agents app polish — dedicated keybindings, accessibility improvements, file-context completions, and CSS @import link resolution.</description><content:encoded>&lt;p&gt;VS Code 1.116 is the April 2026 release, and while it&amp;rsquo;s lighter than some recent updates, the changes are focused and meaningful — especially if you&amp;rsquo;re using the Agents app daily.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what landed, based on the &lt;a href="https://code.visualstudio.com/updates/v1_116"&gt;official release notes&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="agents-app-improvements"&gt;Agents app improvements&lt;/h2&gt;
&lt;p&gt;The Agents app continues to mature with usability polish that makes a real difference in daily workflows:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dedicated keybindings&lt;/strong&gt; — you can now focus the Changes view, the files tree within Changes, and the Chat Customizations view with dedicated commands and keyboard shortcuts. If you&amp;rsquo;ve been clicking around the Agents app to navigate, this brings full keyboard-driven workflows.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Accessibility help dialog&lt;/strong&gt; — pressing &lt;code&gt;Alt+F1&lt;/code&gt; in the chat input box now opens an accessibility help dialog showing available commands and keybindings. Screen reader users can also control announcement verbosity. Good accessibility benefits everyone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;File-context completions&lt;/strong&gt; — type &lt;code&gt;#&lt;/code&gt; in the Agents app chat to trigger file-context completions scoped to your current workspace. This is one of those small quality-of-life improvements that speeds up every interaction — no more typing full file paths when referencing code.&lt;/p&gt;
&lt;h2 id="css-import-link-resolution"&gt;CSS &lt;code&gt;@import&lt;/code&gt; link resolution&lt;/h2&gt;
&lt;p&gt;A nice one for frontend developers: VS Code now resolves CSS &lt;code&gt;@import&lt;/code&gt; references that use node_modules paths. You can &lt;code&gt;Ctrl+click&lt;/code&gt; through imports like &lt;code&gt;@import &amp;quot;some-module/style.css&amp;quot;&lt;/code&gt; when using bundlers. Small but eliminates a friction point in CSS workflows.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;VS Code 1.116 is about refinement — making the Agents app more navigable, more accessible, and more keyboard-friendly. If you&amp;rsquo;re spending significant time in the Agents app (and I suspect many of us are), these changes add up.&lt;/p&gt;
&lt;p&gt;Check the &lt;a href="https://code.visualstudio.com/updates/v1_116"&gt;full release notes&lt;/a&gt; for the complete list.&lt;/p&gt;</content:encoded></item><item><title>VS Code 1.115 — Background Terminal Notifications, SSH Agent Mode, and More</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/vscode-1-115-agent-improvements/</link><pubDate>Mon, 06 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/vscode-1-115-agent-improvements/</guid><description>VS Code 1.115 brings background terminal notifications for agents, SSH remote agent hosting, file paste in terminals, and session-aware edit tracking. Here's what matters for .NET developers.</description><content:encoded>&lt;p&gt;VS Code 1.115 just &lt;a href="https://code.visualstudio.com/updates/v1_115"&gt;dropped&lt;/a&gt;, and while it&amp;rsquo;s a lighter release in terms of headline features, the agent-related improvements are genuinely useful if you&amp;rsquo;re working with AI coding assistants daily.&lt;/p&gt;
&lt;p&gt;Let me highlight what&amp;rsquo;s actually worth knowing.&lt;/p&gt;
&lt;h2 id="background-terminals-talk-back-to-agents"&gt;Background terminals talk back to agents&lt;/h2&gt;
&lt;p&gt;This is the standout feature. Background terminals now automatically notify agents when commands complete, including the exit code and terminal output. Input prompts in background terminals are also detected and surfaced to the user.&lt;/p&gt;
&lt;p&gt;Why does this matter? If you&amp;rsquo;ve used Copilot&amp;rsquo;s agent mode to run build commands or test suites in the background, you know the pain of &amp;ldquo;did that finish yet?&amp;rdquo; — background terminals were essentially fire-and-forget. Now the agent gets notified when your &lt;code&gt;dotnet build&lt;/code&gt; or &lt;code&gt;dotnet test&lt;/code&gt; completes, sees the output, and can react accordingly. It&amp;rsquo;s a small change that makes agent-driven workflows significantly more reliable.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also a new &lt;code&gt;send_to_terminal&lt;/code&gt; tool that lets agents send commands to background terminals with user confirmation, fixing the issue where &lt;code&gt;run_in_terminal&lt;/code&gt; with a timeout would move terminals to the background and make them read-only.&lt;/p&gt;
&lt;h2 id="ssh-remote-agent-hosting"&gt;SSH remote agent hosting&lt;/h2&gt;
&lt;p&gt;VS Code now supports connecting to remote machines over SSH, automatically installing the CLI and starting it in agent host mode. This means your AI agent sessions can target remote environments directly — useful for .NET developers who build and test on Linux servers or cloud VMs.&lt;/p&gt;
&lt;h2 id="edit-tracking-in-agent-sessions"&gt;Edit tracking in agent sessions&lt;/h2&gt;
&lt;p&gt;File edits made during agent sessions are now tracked and restored, with diffs, undo/redo, and state restoration. If an agent makes changes to your code and something goes wrong, you can see exactly what changed and roll it back. Peace of mind for letting agents modify your codebase.&lt;/p&gt;
&lt;h2 id="browser-tab-awareness-and-other-improvements"&gt;Browser tab awareness and other improvements&lt;/h2&gt;
&lt;p&gt;A few more quality-of-life additions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Browser tab tracking&lt;/strong&gt; — chat can now track and link to browser tabs opened during a session, so agents can reference web pages you&amp;rsquo;re looking at&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;File paste in terminal&lt;/strong&gt; — paste files (including images) into the terminal with Ctrl+V, drag-and-drop, or right-click&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test coverage in minimap&lt;/strong&gt; — test coverage indicators now show in the minimap for a quick visual overview&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pinch-to-zoom on Mac&lt;/strong&gt; — integrated browser supports pinch-to-zoom gestures&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Copilot entitlements in Sessions&lt;/strong&gt; — status bar shows usage info in the Sessions view&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Favicon in Go to File&lt;/strong&gt; — open web pages show favicons in the quick pick list&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;VS Code 1.115 is an incremental release, but the agent improvements — background terminal notifications, SSH agent hosting, and edit tracking — add up to a noticeably smoother experience for AI-assisted development. If you&amp;rsquo;re using Copilot&amp;rsquo;s agent mode for .NET projects, these are the kinds of quality-of-life fixes that reduce friction daily.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href="https://code.visualstudio.com/updates/v1_115"&gt;full release notes&lt;/a&gt; for every detail.&lt;/p&gt;</content:encoded></item><item><title>C# 15 Gets Union Types — and They're Exactly What We've Been Asking For</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/csharp-15-union-types-exhaustive-matching/</link><pubDate>Sun, 05 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/csharp-15-union-types-exhaustive-matching/</guid><description>C# 15 introduces the union keyword — compiler-enforced discriminated unions with exhaustive pattern matching. Here's what they look like, why they matter, and how to try them today.</description><content:encoded>&lt;p&gt;This is the one I&amp;rsquo;ve been waiting for. C# 15 introduces the &lt;code&gt;union&lt;/code&gt; keyword — proper discriminated unions with compiler-enforced exhaustive pattern matching. If you&amp;rsquo;ve ever envied F#&amp;rsquo;s discriminated unions or Rust&amp;rsquo;s enums, you know exactly why this matters.&lt;/p&gt;
&lt;p&gt;Bill Wagner &lt;a href="https://devblogs.microsoft.com/dotnet/csharp-15-union-types/"&gt;published the deep dive&lt;/a&gt; on the .NET blog, and honestly? The design is clean, practical, and very C#. Let me walk you through what&amp;rsquo;s actually here and why it&amp;rsquo;s a bigger deal than it might seem at first glance.&lt;/p&gt;
&lt;h2 id="the-problem-unions-solve"&gt;The problem unions solve&lt;/h2&gt;
&lt;p&gt;Before C# 15, returning &amp;ldquo;one of several possible types&amp;rdquo; from a method was always a compromise:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;object&lt;/code&gt;&lt;/strong&gt; — no constraints, no compiler help, defensive casting everywhere&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Marker interfaces&lt;/strong&gt; — better, but anyone can implement them. The compiler can never consider the set complete&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Abstract base classes&lt;/strong&gt; — same issue, plus the types need a common ancestor&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of these give you what you actually want: a closed set of types where the compiler guarantees you&amp;rsquo;ve handled every case. That&amp;rsquo;s what union types do.&lt;/p&gt;
&lt;h2 id="the-syntax-is-beautifully-simple"&gt;The syntax is beautifully simple&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="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;class&lt;/span&gt; &lt;span class="n"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&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="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;class&lt;/span&gt; &lt;span class="n"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&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="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;class&lt;/span&gt; &lt;span class="n"&gt;Bird&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&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="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;union&lt;/span&gt; &lt;span class="n"&gt;Pet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Bird&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;One line. &lt;code&gt;Pet&lt;/code&gt; can hold a &lt;code&gt;Cat&lt;/code&gt;, a &lt;code&gt;Dog&lt;/code&gt;, or a &lt;code&gt;Bird&lt;/code&gt;. Implicit conversions are generated automatically:&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;Pet&lt;/span&gt; &lt;span class="n"&gt;pet&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;Dog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Rex&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;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="n"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Dog { Name = Rex }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And here&amp;rsquo;s the magic — the compiler enforces exhaustive matching:&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="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pet&lt;/span&gt; &lt;span class="k"&gt;switch&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;Dog&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&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;Cat&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&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;Bird&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&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;p&gt;No discard &lt;code&gt;_&lt;/code&gt; needed. The compiler knows this switch covers every possible case. If you later add a fourth type to the union, every switch expression that doesn&amp;rsquo;t handle it produces a warning. Missing cases caught at build time, not at runtime.&lt;/p&gt;
&lt;h2 id="where-this-gets-practical"&gt;Where this gets practical&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Pet&lt;/code&gt; example is cute, but here&amp;rsquo;s where unions actually shine in real code.&lt;/p&gt;
&lt;h3 id="api-responses-that-return-different-shapes"&gt;API responses that return different shapes&lt;/h3&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="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;union&lt;/span&gt; &lt;span class="n"&gt;ApiResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ApiError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ValidationFailure&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;Now every consumer is forced to handle success, error, and validation failure. No more &amp;ldquo;I forgot to check the error case&amp;rdquo; bugs.&lt;/p&gt;
&lt;h3 id="single-value-or-collection"&gt;Single value or collection&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;OneOrMore&amp;lt;T&amp;gt;&lt;/code&gt; pattern shows how unions can have a body with helper methods:&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="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;union&lt;/span&gt; &lt;span class="n"&gt;OneOrMore&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;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="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;AsEnumerable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="k"&gt;switch&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;T&lt;/span&gt; &lt;span class="n"&gt;single&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;single&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;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;multiple&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;multiple&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="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&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="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Callers pass whichever form is convenient:&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;OneOrMore&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;dotnet&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;OneOrMore&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;moreTags&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="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;csharp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;unions&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;preview&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="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;tag&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsEnumerable&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;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$&amp;#34;[{tag}] &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="c1"&gt;// [dotnet]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="composing-unrelated-types"&gt;Composing unrelated types&lt;/h3&gt;
&lt;p&gt;This is the killer feature over traditional hierarchies. You can union types that have nothing in common — &lt;code&gt;string&lt;/code&gt; and &lt;code&gt;Exception&lt;/code&gt;, &lt;code&gt;int&lt;/code&gt; and &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt;. No common ancestor needed.&lt;/p&gt;
&lt;h2 id="custom-unions-for-existing-libraries"&gt;Custom unions for existing libraries&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a smart design choice: any class or struct with a &lt;code&gt;[Union]&lt;/code&gt; attribute is recognized as a union type, as long as it follows the basic pattern (public constructors for case types and a &lt;code&gt;Value&lt;/code&gt; property). Libraries like OneOf that already provide union-like types can opt into compiler support without rewriting their internals.&lt;/p&gt;
&lt;p&gt;For performance-sensitive scenarios with value types, libraries can implement a non-boxing access pattern with &lt;code&gt;HasValue&lt;/code&gt; and &lt;code&gt;TryGetValue&lt;/code&gt; methods.&lt;/p&gt;
&lt;h2 id="the-bigger-picture"&gt;The bigger picture&lt;/h2&gt;
&lt;p&gt;Union types are part of a broader exhaustiveness story coming to C#:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Union types&lt;/strong&gt; — exhaustive matching over a closed set of types (available now in preview)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Closed hierarchies&lt;/strong&gt; — &lt;code&gt;closed&lt;/code&gt; modifier prevents derived classes outside the defining assembly (proposed)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Closed enums&lt;/strong&gt; — prevents creation of values other than declared members (proposed)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Together, these three features will give C# one of the most comprehensive type-safe pattern matching systems in any mainstream language.&lt;/p&gt;
&lt;h2 id="try-it-today"&gt;Try it today&lt;/h2&gt;
&lt;p&gt;Union types are available in .NET 11 Preview 2:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install the &lt;a href="https://dotnet.microsoft.com/download/dotnet"&gt;.NET 11 Preview SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Target &lt;code&gt;net11.0&lt;/code&gt; in your project&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;&amp;lt;LangVersion&amp;gt;preview&amp;lt;/LangVersion&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One caveat: in Preview 2, you&amp;rsquo;ll need to declare &lt;code&gt;UnionAttribute&lt;/code&gt; and &lt;code&gt;IUnion&lt;/code&gt; in your project since they&amp;rsquo;re not in the runtime yet. Grab &lt;a href="https://github.com/dotnet/docs/blob/e68b5dd1e557b53c45ca43e61b013bc919619fb9/docs/csharp/language-reference/builtin-types/snippets/unions/RuntimePolyfill.cs"&gt;RuntimePolyfill.cs&lt;/a&gt; from the docs repo, or add this:&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;namespace&lt;/span&gt; &lt;span class="nn"&gt;System.Runtime.CompilerServices&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="na"&gt; [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; AllowMultiple = false)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UnionAttribute&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Attribute&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="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IUnion&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="kt"&gt;object?&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&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="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Union types are one of those features that make you wonder how we got by without them. Compiler-enforced exhaustive matching, clean syntax, generic support, and integration with existing pattern matching — it&amp;rsquo;s everything we&amp;rsquo;ve been asking for, done the C# way.&lt;/p&gt;
&lt;p&gt;Try them in .NET 11 Preview 2, break things, and &lt;a href="https://github.com/dotnet/csharplang/discussions/9663"&gt;share your feedback on GitHub&lt;/a&gt;. This is preview, and the C# team is actively listening. Your edge cases and design feedback will shape the final release.&lt;/p&gt;
&lt;p&gt;For the full language reference, check out the &lt;a href="https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/union"&gt;union types docs&lt;/a&gt; and the &lt;a href="https://learn.microsoft.com/dotnet/csharp/language-reference/proposals/unions"&gt;feature specification&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>Aspire 13.2 Ships a Docs CLI — and Your AI Agent Can Use It Too</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/aspire-docs-cli-ai-skills/</link><pubDate>Sat, 04 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/aspire-docs-cli-ai-skills/</guid><description>.NET Aspire 13.2 adds aspire docs — a CLI for searching, browsing, and reading official documentation without leaving your terminal. It also works as a tool for AI agents. Here's why this matters.</description><content:encoded>&lt;p&gt;You know that moment when you&amp;rsquo;re deep in an Aspire AppHost, wiring up integrations, and you need to check exactly which parameters the Redis integration expects? You alt-tab to your browser, hunt through aspire.dev, squint at the API docs, then come back to your editor. Context lost. Flow broken.&lt;/p&gt;
&lt;p&gt;Aspire 13.2 just &lt;a href="https://devblogs.microsoft.com/aspire/aspire-docs-in-your-terminal/"&gt;shipped a fix for that&lt;/a&gt;. The &lt;code&gt;aspire docs&lt;/code&gt; CLI lets you search, browse, and read official Aspire documentation directly from your terminal. And because it&amp;rsquo;s backed by reusable services, AI agents and skills can use the same commands to look up docs instead of hallucinating APIs that don&amp;rsquo;t exist.&lt;/p&gt;
&lt;h2 id="the-problem-this-actually-solves"&gt;The problem this actually solves&lt;/h2&gt;
&lt;p&gt;David Pine nails it in the original post: AI agents were &lt;em&gt;terrible&lt;/em&gt; at helping developers build Aspire apps. They&amp;rsquo;d recommend &lt;code&gt;dotnet run&lt;/code&gt; instead of &lt;code&gt;aspire run&lt;/code&gt;, reference learn.microsoft.com for docs that live on aspire.dev, suggest outdated NuGet packages, and — my personal favorite — hallucinate APIs that don&amp;rsquo;t exist.&lt;/p&gt;
&lt;p&gt;Why? Because Aspire was .NET-specific far longer than it&amp;rsquo;s been polyglot, and LLMs are working off training data that predates the latest features. When you give an AI agent the ability to actually look up the current docs, it stops guessing and starts being useful.&lt;/p&gt;
&lt;h2 id="three-commands-zero-browser-tabs"&gt;Three commands, zero browser tabs&lt;/h2&gt;
&lt;p&gt;The CLI is refreshingly simple:&lt;/p&gt;
&lt;h3 id="list-all-docs"&gt;List all docs&lt;/h3&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;aspire docs list
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Returns every documentation page available on aspire.dev. Need machine-readable output? Add &lt;code&gt;--format Json&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="search-for-a-topic"&gt;Search for a topic&lt;/h3&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;aspire docs search &lt;span class="s2"&gt;&amp;#34;redis&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Searches both titles and content with weighted relevance scoring. Same search engine that powers the documentation tooling internally. You get ranked results with titles, slugs, and relevance scores.&lt;/p&gt;
&lt;h3 id="read-a-full-page-or-just-one-section"&gt;Read a full page (or just one section)&lt;/h3&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;aspire docs get redis-integration
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Streams the full page as markdown to your terminal. Need just one section?&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;aspire docs get redis-integration --section &lt;span class="s2"&gt;&amp;#34;Add Redis resource&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Surgical precision. No scrolling through 500 lines. Just the part you need.&lt;/p&gt;
&lt;h2 id="the-ai-agent-angle"&gt;The AI agent angle&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s where it gets interesting for us developers building with AI tooling. The same &lt;code&gt;aspire docs&lt;/code&gt; commands work as tools for AI agents — through skills, MCP servers, or simple CLI wrappers.&lt;/p&gt;
&lt;p&gt;Instead of your AI assistant making up Aspire APIs based on stale training data, it can call &lt;code&gt;aspire docs search &amp;quot;postgres&amp;quot;&lt;/code&gt;, find the official integration docs, read the right page, and give you the documented approach. Real-time, current documentation — not what the model memorized six months ago.&lt;/p&gt;
&lt;p&gt;The architecture behind this is intentional. The Aspire team built reusable services (&lt;code&gt;IDocsIndexService&lt;/code&gt;, &lt;code&gt;IDocsSearchService&lt;/code&gt;, &lt;code&gt;IDocsFetcher&lt;/code&gt;, &lt;code&gt;IDocsCache&lt;/code&gt;) rather than a one-off integration. That means the same search engine works for humans in the terminal, AI agents in your editor, and automation in your CI pipeline.&lt;/p&gt;
&lt;h2 id="real-world-scenarios"&gt;Real-world scenarios&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Quick terminal lookups:&lt;/strong&gt; You&amp;rsquo;re three files deep and need Redis config parameters. Two commands, ninety seconds, back to 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;aspire docs search &lt;span class="s2"&gt;&amp;#34;redis&amp;#34;&lt;/span&gt; --limit &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aspire docs get redis-integration --section &lt;span class="s2"&gt;&amp;#34;Configuration&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;AI-assisted development:&lt;/strong&gt; Your VS Code skill wraps the CLI commands. You ask &amp;ldquo;Add a PostgreSQL database to my AppHost&amp;rdquo; and the agent looks up the real docs before answering. No hallucinations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CI/CD validation:&lt;/strong&gt; Your pipeline validates AppHost configurations against official documentation programmatically. &lt;code&gt;--format Json&lt;/code&gt; output pipes cleanly to &lt;code&gt;jq&lt;/code&gt; and other tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Custom knowledge bases:&lt;/strong&gt; Building your own AI tooling? Pipe structured JSON output directly into your knowledge base:&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;aspire docs search &lt;span class="s2"&gt;&amp;#34;monitoring&amp;#34;&lt;/span&gt; --format Json &lt;span class="p"&gt;|&lt;/span&gt; jq &lt;span class="s1"&gt;&amp;#39;[.[] | {slug, title, summary}]&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No web scraping. No API keys. Same structured data the docs tooling uses internally.&lt;/p&gt;
&lt;h2 id="the-documentation-is-always-live"&gt;The documentation is always live&lt;/h2&gt;
&lt;p&gt;This is the part I appreciate most. The CLI doesn&amp;rsquo;t download a snapshot — it queries aspire.dev with ETag-based caching. The moment the docs update, your CLI and any skill built on top of it reflects that. No stale copies, no &amp;ldquo;but the wiki said&amp;hellip;&amp;rdquo; moments.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;aspire docs&lt;/code&gt; is one of those small features that solves a real problem cleanly. Humans get terminal-native documentation access. AI agents get a way to stop guessing and start referencing actual docs. And it&amp;rsquo;s all backed by the same source of truth.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re building with .NET Aspire and haven&amp;rsquo;t tried the CLI yet, run &lt;code&gt;aspire docs search &amp;quot;your-topic-here&amp;quot;&lt;/code&gt; and see how it feels. Then consider wrapping those commands into whatever AI skill or automation setup you&amp;rsquo;re using — your agents will thank you.&lt;/p&gt;
&lt;p&gt;Check out &lt;a href="https://davidpine.dev/posts/aspire-docs-mcp-tools/"&gt;David Pine&amp;rsquo;s deep dive&lt;/a&gt; on how the docs tooling came together, and the &lt;a href="https://aspire.dev/reference/cli/commands/aspire-docs/"&gt;official CLI reference&lt;/a&gt; for all the details.&lt;/p&gt;</content:encoded></item><item><title>Microsoft Agent Framework Hits 1.0 — Here's What Actually Matters for .NET Developers</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/agent-framework-1-0-production-ready/</link><pubDate>Fri, 03 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/agent-framework-1-0-production-ready/</guid><description>Microsoft Agent Framework 1.0 is production-ready with stable APIs, multi-agent orchestration, and connectors for every major AI provider. Here's what you need to know as a .NET developer.</description><content:encoded>&lt;p&gt;If you&amp;rsquo;ve been following the Agent Framework journey from the early Semantic Kernel and AutoGen days, this one is significant. Microsoft Agent Framework just &lt;a href="https://devblogs.microsoft.com/agent-framework/microsoft-agent-framework-version-1-0/"&gt;hit version 1.0&lt;/a&gt; — production-ready, stable APIs, long-term support commitment. It&amp;rsquo;s available for both .NET and Python, and it&amp;rsquo;s genuinely ready for real workloads.&lt;/p&gt;
&lt;p&gt;Let me cut through the announcement noise and focus on what matters if you&amp;rsquo;re building AI-powered apps with .NET.&lt;/p&gt;
&lt;h2 id="the-short-version"&gt;The short version&lt;/h2&gt;
&lt;p&gt;Agent Framework 1.0 unifies what used to be Semantic Kernel and AutoGen into a single, open-source SDK. One agent abstraction. One orchestration engine. Multiple AI providers. If you&amp;rsquo;ve been bouncing between Semantic Kernel for enterprise patterns and AutoGen for research-grade multi-agent workflows, you can stop. This is the one SDK now.&lt;/p&gt;
&lt;h2 id="getting-started-is-almost-unfairly-simple"&gt;Getting started is almost unfairly simple&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a working agent in .NET:&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="c1"&gt;// dotnet add package Microsoft.Agents.AI.OpenAI --prerelease&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Agents.AI&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;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Agents.AI.Foundry&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;using&lt;/span&gt; &lt;span class="nn"&gt;Azure.Identity&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="kt"&gt;var&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;AIProjectClient&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 class="s"&gt;&amp;#34;https://your-project.services.ai.azure.com&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 class="n"&gt;GetResponsesClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;gpt-5.3&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 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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;HaikuBot&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;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;You are an upbeat assistant that writes beautifully.&amp;#34;&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&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;Write a haiku about shipping 1.0.&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;That&amp;rsquo;s it. A handful of lines and you have an AI agent running against Azure Foundry. The Python equivalent is equally concise. Add function tools, multi-turn conversations, and streaming as you go — the API surface scales up without getting weird.&lt;/p&gt;
&lt;h2 id="multi-agent-orchestration--this-is-the-real-deal"&gt;Multi-agent orchestration — this is the real deal&lt;/h2&gt;
&lt;p&gt;Single agents are fine for demos, but production scenarios usually need coordination. Agent Framework 1.0 ships with battle-tested orchestration patterns straight from Microsoft Research and AutoGen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sequential&lt;/strong&gt; — agents process in order (writer → reviewer → editor)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Concurrent&lt;/strong&gt; — fan out to multiple agents in parallel, converge results&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Handoff&lt;/strong&gt; — one agent delegates to another based on intent&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Group chat&lt;/strong&gt; — multiple agents discuss and converge on a solution&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Magentic-One&lt;/strong&gt; — the research-grade multi-agent pattern from MSR&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of them support streaming, checkpointing, human-in-the-loop approvals, and pause/resume. The checkpointing part is crucial — long-running workflows survive process restarts. For us .NET developers who&amp;rsquo;ve built durable workflows with Azure Functions, this feels familiar.&lt;/p&gt;
&lt;h2 id="the-features-that-matter-most"&gt;The features that matter most&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s my shortlist of what&amp;rsquo;s worth knowing:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Middleware hooks.&lt;/strong&gt; You know how ASP.NET Core has middleware pipelines? Same concept, but for agent execution. Intercept every stage — add content safety, logging, compliance policies — without touching agent prompts. This is how you make agents enterprise-ready.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pluggable memory.&lt;/strong&gt; Conversational history, persistent key-value state, vector-based retrieval. Choose your backend: Foundry Agent Service, Mem0, Redis, Neo4j, or roll your own. Memory is what turns a stateless LLM call into an agent that actually remembers context.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Declarative YAML agents.&lt;/strong&gt; Define your agent&amp;rsquo;s instructions, tools, memory, and orchestration topology in version-controlled YAML files. Load and run with a single API call. This is a game-changer for teams that want to iterate on agent behavior without redeploying code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A2A and MCP support.&lt;/strong&gt; MCP (Model Context Protocol) lets agents discover and invoke external tools dynamically. A2A (Agent-to-Agent protocol) enables cross-runtime collaboration — your .NET agents can coordinate with agents running in other frameworks. A2A 1.0 support is coming soon.&lt;/p&gt;
&lt;h2 id="the-preview-features-worth-watching"&gt;The preview features worth watching&lt;/h2&gt;
&lt;p&gt;Some features shipped as preview in 1.0 — functional but APIs may evolve:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DevUI&lt;/strong&gt; — a browser-based local debugger for visualizing agent execution, message flows, and tool calls in real time. Think Application Insights, but for agent reasoning.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Copilot SDK and Claude Code SDK&lt;/strong&gt; — use Copilot or Claude as an agent harness directly from your orchestration code. Compose a coding-capable agent alongside your other agents in the same workflow.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agent Harness&lt;/strong&gt; — a customizable local runtime giving agents access to shell, file system, and messaging loops. Think coding agents and automation patterns.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skills&lt;/strong&gt; — reusable domain capability packages that give agents structured capabilities out of the box.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="migrating-from-semantic-kernel-or-autogen"&gt;Migrating from Semantic Kernel or AutoGen&lt;/h2&gt;
&lt;p&gt;If you have existing Semantic Kernel or AutoGen code, there are dedicated migration assistants that analyze your code and generate step-by-step migration plans. The &lt;a href="https://learn.microsoft.com/en-us/agent-framework/migration-guide/from-semantic-kernel"&gt;Semantic Kernel migration guide&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/en-us/agent-framework/migration-guide/from-autogen"&gt;AutoGen migration guide&lt;/a&gt; walk you through everything.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve been on the RC packages, upgrading to 1.0 is just a version bump.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Agent Framework 1.0 is the production milestone that enterprise teams have been waiting for. Stable APIs, multi-provider support, orchestration patterns that actually work at scale, and migration paths from both Semantic Kernel and AutoGen.&lt;/p&gt;
&lt;p&gt;The framework is &lt;a href="https://github.com/microsoft/agent-framework"&gt;fully open source on GitHub&lt;/a&gt;, and you can get started today with &lt;code&gt;dotnet add package Microsoft.Agents.AI&lt;/code&gt;. Check out the &lt;a href="https://learn.microsoft.com/en-us/agent-framework/get-started/"&gt;quickstart guide&lt;/a&gt; and the &lt;a href="https://github.com/microsoft/agent-framework"&gt;samples&lt;/a&gt; to get your hands dirty.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve been waiting for the &amp;ldquo;safe to use in production&amp;rdquo; signal — this is it.&lt;/p&gt;</content:encoded></item><item><title>Aspire 13.2's Dashboard Just Got a Telemetry API — and It Changes Everything</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/aspire-132-dashboard-export-telemetry/</link><pubDate>Thu, 02 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/aspire-132-dashboard-export-telemetry/</guid><description>.NET Aspire 13.2 ships smarter telemetry export, a programmable API for traces and logs, and GenAI visualization improvements. Here's why this matters for your debugging workflow.</description><content:encoded>&lt;p&gt;If you&amp;rsquo;ve been building distributed apps with .NET Aspire, you already know the dashboard is the single best thing about the whole experience. All your traces, logs, and metrics in one place — no external Jaeger, no Seq setup, no &amp;ldquo;let me check the other terminal&amp;rdquo; moments.&lt;/p&gt;
&lt;p&gt;Aspire 13.2 just made it significantly better. James Newton-King &lt;a href="https://devblogs.microsoft.com/aspire/aspire-dashboard-improvements-export-and-telemetry/"&gt;announced the update&lt;/a&gt;, and honestly? The telemetry export and API features alone are worth the upgrade.&lt;/p&gt;
&lt;h2 id="export-telemetry-like-a-sane-person"&gt;Export telemetry like a sane person&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the scenario we&amp;rsquo;ve all lived through: you&amp;rsquo;re debugging a distributed issue, you finally reproduce it after twenty minutes of setup, and now you need to share what happened with your team. Before? Screenshots. Copy-pasting trace IDs. The usual mess.&lt;/p&gt;
&lt;p&gt;Aspire 13.2 adds a proper &lt;strong&gt;Manage logs and telemetry&lt;/strong&gt; dialog where you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clear all telemetry (useful before a repro attempt)&lt;/li&gt;
&lt;li&gt;Export selected telemetry to a ZIP file in standard OTLP/JSON format&lt;/li&gt;
&lt;li&gt;Re-import that ZIP into any Aspire dashboard later&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last part is the killer feature. You reproduce a bug, export the telemetry, attach it to your work item, and your teammate can import it into their own dashboard to see exactly what you saw. No more &amp;ldquo;can you reproduce it on your machine?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Individual traces, spans, and logs also get an &amp;ldquo;Export JSON&amp;rdquo; option in their context menus. Need to share one specific trace? Right-click, copy JSON, paste into your PR description. Done.&lt;/p&gt;
&lt;h2 id="the-telemetry-api-is-the-real-game-changer"&gt;The telemetry API is the real game changer&lt;/h2&gt;
&lt;p&gt;This is what I&amp;rsquo;m most excited about. The dashboard now exposes an HTTP API under &lt;code&gt;/api/telemetry&lt;/code&gt; for querying telemetry data programmatically. Available endpoints:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GET /api/telemetry/resources&lt;/code&gt; — list resources with telemetry&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /api/telemetry/spans&lt;/code&gt; — query spans with filters&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /api/telemetry/logs&lt;/code&gt; — query logs with filters&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /api/telemetry/traces&lt;/code&gt; — list traces&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /api/telemetry/traces/{traceId}&lt;/code&gt; — get all spans for a specific trace&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything comes back in OTLP JSON format. This powers the new &lt;code&gt;aspire agent mcp&lt;/code&gt; and &lt;code&gt;aspire otel&lt;/code&gt; CLI commands, but the real implication is bigger: you can now build tooling, scripts, and AI agent integrations that query your app&amp;rsquo;s telemetry directly.&lt;/p&gt;
&lt;p&gt;Imagine an AI coding agent that can look at your actual distributed traces while debugging. That&amp;rsquo;s not hypothetical anymore — that&amp;rsquo;s what this API enables.&lt;/p&gt;
&lt;h2 id="genai-telemetry-gets-practical"&gt;GenAI telemetry gets practical&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re building AI-powered apps with Semantic Kernel or Microsoft.Extensions.AI, you&amp;rsquo;ll appreciate the improved GenAI telemetry visualizer. Aspire 13.2 adds:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AI tool descriptions rendered as Markdown&lt;/li&gt;
&lt;li&gt;A dedicated GenAI button on the traces page for quick AI trace access&lt;/li&gt;
&lt;li&gt;Better error handling for truncated or non-standard GenAI JSON&lt;/li&gt;
&lt;li&gt;Click-to-highlight navigation between tool definitions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The blog post mentions that VS Code Copilot chat, Copilot CLI, and OpenCode all support configuring an &lt;code&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/code&gt;. Point them at the Aspire dashboard and you can literally watch your AI agents think in real time through telemetry. That&amp;rsquo;s a debugging experience you won&amp;rsquo;t find anywhere else.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Aspire 13.2 takes the dashboard from &amp;ldquo;nice debugging UI&amp;rdquo; to &amp;ldquo;programmable observability platform.&amp;rdquo; The export/import workflow alone saves real time on distributed debugging, and the telemetry API opens the door to AI-assisted diagnostics.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re already on Aspire, upgrade. If you&amp;rsquo;re not — this is a good reason to check out &lt;a href="https://aspire.dev"&gt;aspire.dev&lt;/a&gt; and see what the fuss is about.&lt;/p&gt;</content:encoded></item><item><title>azd Now Lets You Run and Debug AI Agents Locally — Here's What Changed in March 2026</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/azd-march-2026-local-ai-agent-debugging/</link><pubDate>Thu, 02 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/azd-march-2026-local-ai-agent-debugging/</guid><description>The Azure Developer CLI shipped seven releases in March 2026. The highlights: a local run-and-debug loop for AI agents, GitHub Copilot integration in project setup, and Container App Jobs support.</description><content:encoded>&lt;p&gt;Seven releases in one month. That&amp;rsquo;s what the Azure Developer CLI (&lt;code&gt;azd&lt;/code&gt;) team pushed in March 2026, and the headline feature is the one I&amp;rsquo;ve been waiting for: &lt;strong&gt;a local run-and-debug loop for AI agents&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;PC Chan &lt;a href="https://devblogs.microsoft.com/azure-sdk/azure-developer-cli-azd-march-2026/"&gt;published the full roundup&lt;/a&gt;, and while there&amp;rsquo;s a lot in there, let me filter it down to what actually matters for .NET developers building AI-powered apps.&lt;/p&gt;
&lt;h2 id="run-and-debug-ai-agents-without-deploying"&gt;Run and debug AI agents without deploying&lt;/h2&gt;
&lt;p&gt;This is the big one. The new &lt;code&gt;azure.ai.agents&lt;/code&gt; extension adds a set of commands that give you a proper inner-loop experience for AI agents:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;azd ai agent run&lt;/code&gt; — starts your agent locally&lt;/li&gt;
&lt;li&gt;&lt;code&gt;azd ai agent invoke&lt;/code&gt; — sends messages to it (local or deployed)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;azd ai agent show&lt;/code&gt; — displays container status and health&lt;/li&gt;
&lt;li&gt;&lt;code&gt;azd ai agent monitor&lt;/code&gt; — streams container logs in real time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before this, testing an AI agent meant deploying to Microsoft Foundry every time you made a change. Now you can iterate locally, test your agent&amp;rsquo;s behavior, and only deploy when you&amp;rsquo;re ready. If you&amp;rsquo;ve been building agents with the Microsoft Agent Framework or Semantic Kernel, this changes your daily workflow.&lt;/p&gt;
&lt;p&gt;The invoke command works against both local and deployed agents, which means you can use the same testing workflow regardless of where the agent is running. That&amp;rsquo;s the kind of detail that saves you from maintaining two sets of test scripts.&lt;/p&gt;
&lt;h2 id="github-copilot-scaffolds-your-azd-project"&gt;GitHub Copilot scaffolds your azd project&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;azd init&lt;/code&gt; now offers a &amp;ldquo;Set up with GitHub Copilot (Preview)&amp;rdquo; option. Instead of manually answering prompts about your project structure, a Copilot agent scaffolds the configuration for you. It checks for a dirty working directory before modifying anything and asks for MCP server tool consent upfront.&lt;/p&gt;
&lt;p&gt;When a command fails, &lt;code&gt;azd&lt;/code&gt; now offers AI-assisted troubleshooting: pick a category (explain, guidance, troubleshoot, or skip), let the agent suggest a fix, and retry — all without leaving the terminal. For complex infrastructure setups, that&amp;rsquo;s a real time saver.&lt;/p&gt;
&lt;h2 id="container-app-jobs-and-deployment-improvements"&gt;Container App Jobs and deployment improvements&lt;/h2&gt;
&lt;p&gt;A few deployment features worth noting:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Container App Jobs&lt;/strong&gt;: &lt;code&gt;azd&lt;/code&gt; now deploys &lt;code&gt;Microsoft.App/jobs&lt;/code&gt; through the existing &lt;code&gt;host: containerapp&lt;/code&gt; config. Your Bicep template determines whether the target is a Container App or a Job — no extra setup.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Configurable deployment timeouts&lt;/strong&gt;: New &lt;code&gt;--timeout&lt;/code&gt; flag on &lt;code&gt;azd deploy&lt;/code&gt; and a &lt;code&gt;deployTimeout&lt;/code&gt; field in &lt;code&gt;azure.yaml&lt;/code&gt;. No more guessing the default 1200-second limit.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remote build fallback&lt;/strong&gt;: When remote ACR build fails, &lt;code&gt;azd&lt;/code&gt; falls back to local Docker/Podman build automatically.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Local preflight validation&lt;/strong&gt;: Bicep parameters get validated locally before deploying, catching missing params without a round-trip to Azure.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="developer-experience-polish"&gt;Developer experience polish&lt;/h2&gt;
&lt;p&gt;Some smaller improvements that add up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Automatic pnpm/yarn detection&lt;/strong&gt; for JS/TS projects&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pyproject.toml support&lt;/strong&gt; for Python packaging&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Local template directories&lt;/strong&gt; — &lt;code&gt;azd init --template&lt;/code&gt; now accepts filesystem paths for offline iteration&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better error messages&lt;/strong&gt; in &lt;code&gt;--no-prompt&lt;/code&gt; mode — all missing values reported at once with resolution commands&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build environment variables&lt;/strong&gt; injected into all framework build subprocesses (.NET, Node.js, Java, Python)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last one is subtle but important: your .NET build now has access to &lt;code&gt;azd&lt;/code&gt; environment variables, which means you can do build-time configuration injection without extra scripting.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;The local AI agent debugging loop is the star of this release, but the accumulation of deployment improvements and DX polish makes &lt;code&gt;azd&lt;/code&gt; feel more mature than ever. If you&amp;rsquo;re deploying .NET apps to Azure — especially AI agents — this update is worth the install.&lt;/p&gt;
&lt;p&gt;Check the &lt;a href="https://devblogs.microsoft.com/azure-sdk/azure-developer-cli-azd-march-2026/"&gt;full release notes&lt;/a&gt; for every detail, or get started with &lt;a href="https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd"&gt;azd install&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>Azure DevOps Finally Fixes the Markdown Editor UX Everyone Complained About</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-devops-markdown-editor-work-items/</link><pubDate>Thu, 02 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-devops-markdown-editor-work-items/</guid><description>The Azure DevOps Markdown editor for work items gets a clearer preview-vs-edit mode distinction. It's a small change that fixes a genuinely annoying workflow issue.</description><content:encoded>&lt;p&gt;If you use Azure Boards, you&amp;rsquo;ve probably experienced this: you&amp;rsquo;re reading through a work item description, maybe reviewing acceptance criteria, and you accidentally double-click. Boom — you&amp;rsquo;re in edit mode. You didn&amp;rsquo;t want to edit anything. You were just reading.&lt;/p&gt;
&lt;p&gt;Dan Hellem &lt;a href="https://devblogs.microsoft.com/devops/improving-the-markdown-editor-for-work-items/"&gt;announced the fix&lt;/a&gt;, and it&amp;rsquo;s one of those changes that sounds tiny but actually removes real friction from your daily workflow.&lt;/p&gt;
&lt;h2 id="what-changed"&gt;What changed&lt;/h2&gt;
&lt;p&gt;The Markdown editor for work item text fields now opens in &lt;strong&gt;preview mode by default&lt;/strong&gt;. You can read and interact with the content — follow links, review formatting — without worrying about accidentally entering edit mode.&lt;/p&gt;
&lt;p&gt;When you actually want to edit, you click the edit icon at the top of the field. When you&amp;rsquo;re done, you exit back to preview mode explicitly. Simple, intentional, predictable.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it. That&amp;rsquo;s the change.&lt;/p&gt;
&lt;h2 id="why-this-matters-more-than-it-sounds"&gt;Why this matters more than it sounds&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://developercommunity.visualstudio.com/t/Markdown-editor-for-work-item-multi-line/10935496"&gt;community feedback thread&lt;/a&gt; on this was long. The double-click-to-edit behavior was introduced with the Markdown editor back in July 2025, and the complaints started almost immediately. The problem wasn&amp;rsquo;t just accidental edits — it was that the whole interaction felt unpredictable. You never knew if clicking would read or edit.&lt;/p&gt;
&lt;p&gt;For teams that do sprint planning, backlog grooming, or code review with Azure Boards, this kind of micro-friction compounds. Every accidental edit mode entry is a context switch. Every &amp;ldquo;wait, did I change something?&amp;rdquo; moment is wasted attention.&lt;/p&gt;
&lt;p&gt;The new default respects the most common interaction pattern: you read work items far more often than you edit them.&lt;/p&gt;
&lt;h2 id="rollout-status"&gt;Rollout status&lt;/h2&gt;
&lt;p&gt;This is already rolling out to a subset of customers and expanding to everyone over the next two to three weeks. If you&amp;rsquo;re not seeing it yet, you will soon.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Not every improvement needs to be a headline feature. Sometimes the best update is just removing something annoying. This is one of those — a small UX fix that makes Azure Boards feel less hostile to people who just want to read their work items in peace.&lt;/p&gt;</content:encoded></item><item><title>Bookmark Studio Brings Slot-Based Navigation and Sharing to Visual Studio Bookmarks</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/bookmark-studio-visual-studio-extension/</link><pubDate>Thu, 02 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/bookmark-studio-visual-studio-extension/</guid><description>Mads Kristensen's new Bookmark Studio extension adds keyboard-driven slot navigation, a bookmark manager, colors, labels, and export/share capabilities to Visual Studio bookmarks.</description><content:encoded>&lt;p&gt;Bookmarks in Visual Studio have always been&amp;hellip; fine. You set one, you navigate to the next, you forget which bookmark is which. They work, but they&amp;rsquo;ve never been the kind of feature you&amp;rsquo;d call powerful.&lt;/p&gt;
&lt;p&gt;Mads Kristensen just &lt;a href="https://devblogs.microsoft.com/visualstudio/bookmark-studio-evolving-bookmarks-in-visual-studio/"&gt;released Bookmark Studio&lt;/a&gt;, an experimental extension that fills in exactly the gaps you&amp;rsquo;ve probably run into if you use bookmarks regularly.&lt;/p&gt;
&lt;h2 id="slot-based-navigation"&gt;Slot-based navigation&lt;/h2&gt;
&lt;p&gt;The core addition: bookmarks can now be assigned to slots 1–9 and jumped to directly with &lt;code&gt;Alt+Shift+1&lt;/code&gt; through &lt;code&gt;Alt+Shift+9&lt;/code&gt;. New bookmarks automatically get the next available slot, so in most cases, fast navigation works without any setup.&lt;/p&gt;
&lt;p&gt;This sounds simple, but it changes bookmarks from &amp;ldquo;I have some bookmarks somewhere&amp;rdquo; to &amp;ldquo;Slot 3 is my API controller, Slot 5 is the service layer, Slot 7 is the test.&amp;rdquo; That kind of spatial memory makes navigation nearly instant during focused work sessions.&lt;/p&gt;
&lt;h2 id="the-bookmark-manager"&gt;The Bookmark Manager&lt;/h2&gt;
&lt;p&gt;A new tool window shows all your bookmarks in one place with filtering by name, file, location, color, or slot. Double-click or keyboard-navigate to jump to any bookmark.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve ever had more than five or six bookmarks and lost track of which was which, this alone is worth installing the extension.&lt;/p&gt;
&lt;h2 id="organization-with-labels-colors-and-folders"&gt;Organization with labels, colors, and folders&lt;/h2&gt;
&lt;p&gt;Bookmarks can optionally have labels, colors, and be grouped into folders. None of it is required — your current bookmark workflow keeps working. But when you&amp;rsquo;re debugging a complex issue or exploring an unfamiliar codebase, being able to color-code and label your bookmarks adds useful context.&lt;/p&gt;
&lt;p&gt;All metadata is stored per solution, so your bookmark organization persists across sessions.&lt;/p&gt;
&lt;h2 id="export-and-share"&gt;Export and share&lt;/h2&gt;
&lt;p&gt;This is the feature I didn&amp;rsquo;t know I wanted. Bookmark Studio lets you export bookmarks as plain text, Markdown, or CSV. That means you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Include bookmark paths in pull request descriptions&lt;/li&gt;
&lt;li&gt;Share investigation breadcrumbs with teammates&lt;/li&gt;
&lt;li&gt;Move bookmark sets between repos or branches&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bookmarks stop being a solo navigation tool and start being a way to communicate &amp;ldquo;here&amp;rsquo;s the path through this code.&amp;rdquo;&lt;/p&gt;
&lt;h2 id="bookmarks-that-track-code-movement"&gt;Bookmarks that track code movement&lt;/h2&gt;
&lt;p&gt;Bookmark Studio tracks bookmarks relative to the text they&amp;rsquo;re anchored to, so they don&amp;rsquo;t drift to wrong lines as you edit. If you&amp;rsquo;ve ever set bookmarks during a debugging session and had them all point to the wrong lines after a refactor — this fixes that.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Bookmark Studio doesn&amp;rsquo;t reinvent anything. It takes a feature that&amp;rsquo;s been &amp;ldquo;good enough&amp;rdquo; for years and makes it genuinely useful for focused development. Slot navigation, the Bookmark Manager, and export capabilities are the highlights.&lt;/p&gt;
&lt;p&gt;Grab it from the &lt;a href="https://marketplace.visualstudio.com/items?itemName=MadsKristensen.BookmarkStudio"&gt;Visual Studio Marketplace&lt;/a&gt; and give it a try.&lt;/p&gt;</content:encoded></item><item><title>Visual Studio's March Update Lets You Build Custom Copilot Agents — and the find_symbol Tool Is a Big Deal</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/visual-studio-march-2026-custom-copilot-agents/</link><pubDate>Thu, 02 Apr 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/visual-studio-march-2026-custom-copilot-agents/</guid><description>Visual Studio's March 2026 update ships custom Copilot agents, reusable agent skills, a language-aware find_symbol tool, and Copilot-powered profiling from Test Explorer. Here's what matters.</description><content:encoded>&lt;p&gt;Visual Studio just got its most significant Copilot update yet. Mark Downie &lt;a href="https://devblogs.microsoft.com/visualstudio/visual-studio-march-update-build-your-own-custom-agents/"&gt;announced the March release&lt;/a&gt;, and the headline is custom agents — but honestly, the &lt;code&gt;find_symbol&lt;/code&gt; tool buried further down might be the feature that changes your workflow the most.&lt;/p&gt;
&lt;p&gt;Let me break down what&amp;rsquo;s actually here.&lt;/p&gt;
&lt;h2 id="custom-copilot-agents-in-your-repo"&gt;Custom Copilot agents in your repo&lt;/h2&gt;
&lt;p&gt;Want Copilot to follow your team&amp;rsquo;s coding standards, run your build pipeline, or query your internal docs? Now you can build exactly that.&lt;/p&gt;
&lt;p&gt;Custom agents are defined as &lt;code&gt;.agent.md&lt;/code&gt; files that you drop into &lt;code&gt;.github/agents/&lt;/code&gt; in your repository. Each agent gets full access to workspace awareness, code understanding, tools, your preferred model, and MCP connections to external services. They show up in the agent picker alongside the built-in agents.&lt;/p&gt;
&lt;p&gt;This is the same pattern VS Code has been supporting — and it&amp;rsquo;s great to see Visual Studio catch up. For teams that have already built agents for VS Code, your &lt;code&gt;.agent.md&lt;/code&gt; files should work across both IDEs (though tool names can vary, so test them).&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/github/awesome-copilot"&gt;awesome-copilot&lt;/a&gt; repo has community-contributed agent configurations you can use as starting points.&lt;/p&gt;
&lt;h2 id="agent-skills-reusable-instruction-packs"&gt;Agent skills: reusable instruction packs&lt;/h2&gt;
&lt;p&gt;Skills are automatically picked up from &lt;code&gt;.github/skills/&lt;/code&gt; in your repo or &lt;code&gt;~/.copilot/skills/&lt;/code&gt; in your profile. Each skill is a &lt;code&gt;SKILL.md&lt;/code&gt; file following the &lt;a href="https://agentskills.io/specification"&gt;Agent Skills specification&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Think of skills as modular expertise you can mix and match. You might have a skill for your API conventions, another for your testing patterns, and another for your deployment workflow. When a skill activates, it shows up in the chat so you know it&amp;rsquo;s being applied.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve been using skills in VS Code, they work the same way in Visual Studio now.&lt;/p&gt;
&lt;h2 id="find_symbol-language-aware-navigation-for-agents"&gt;find_symbol: language-aware navigation for agents&lt;/h2&gt;
&lt;p&gt;This is where things get really interesting. The new &lt;code&gt;find_symbol&lt;/code&gt; tool gives Copilot&amp;rsquo;s agent mode actual language-service-powered symbol navigation. Instead of searching your code as text, the agent can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Find all references to a symbol across your project&lt;/li&gt;
&lt;li&gt;Access type information, declarations, and scope metadata&lt;/li&gt;
&lt;li&gt;Navigate call sites with full language awareness&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What this means in practice: when you ask Copilot to refactor a method or update a parameter signature across call sites, it can actually see your code&amp;rsquo;s structure. No more &amp;ldquo;the agent changed the method but missed three call sites&amp;rdquo; situations.&lt;/p&gt;
&lt;p&gt;Supported languages include C#, C++, Razor, TypeScript, and anything with a supported LSP extension. For .NET developers, this is a massive improvement — C# codebases with deep type hierarchies and interfaces benefit enormously from symbol-aware navigation.&lt;/p&gt;
&lt;h2 id="profile-tests-with-copilot"&gt;Profile tests with Copilot&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s now a &lt;strong&gt;Profile with Copilot&lt;/strong&gt; command in the Test Explorer context menu. Select a test, click profile, and the Profiling Agent automatically runs it and analyzes performance — combining CPU usage and instrumentation data to deliver actionable insights.&lt;/p&gt;
&lt;p&gt;No more manually configuring profiler sessions, running the test, exporting results, and trying to read a flame graph. The agent does the analysis and tells you what&amp;rsquo;s slow and why. Currently .NET only, which makes sense given Visual Studio&amp;rsquo;s deep .NET diagnostics integration.&lt;/p&gt;
&lt;h2 id="perf-tips-during-live-debugging"&gt;Perf tips during live debugging&lt;/h2&gt;
&lt;p&gt;Performance optimization now happens while you debug, not after. As you step through code, Visual Studio shows execution time and performance signals inline. See a slow line? Click the Perf Tip and ask Copilot for optimization suggestions right there.&lt;/p&gt;
&lt;p&gt;The Profiling Agent captures runtime data automatically — elapsed time, CPU usage, memory behavior — and Copilot uses it to pinpoint hot spots. This keeps performance work as part of your debugging flow instead of a separate task you keep postponing.&lt;/p&gt;
&lt;h2 id="fix-nuget-vulnerabilities-from-solution-explorer"&gt;Fix NuGet vulnerabilities from Solution Explorer&lt;/h2&gt;
&lt;p&gt;When a vulnerability is detected in a NuGet package, you now see a notification with a &lt;strong&gt;Fix with GitHub Copilot&lt;/strong&gt; link directly in Solution Explorer. Click through and Copilot analyzes the vulnerability, recommends the right package updates, and implements them.&lt;/p&gt;
&lt;p&gt;For teams that struggle to keep dependencies up to date (which is basically everyone), this removes the friction of &amp;ldquo;I know there&amp;rsquo;s a vulnerability but figuring out the right update path is a project in itself.&amp;rdquo;&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Custom agents and skills are the headline, but &lt;code&gt;find_symbol&lt;/code&gt; is the sleeper hit — it fundamentally changes how accurate Copilot can be when refactoring .NET code. Combined with live profiling integration and vulnerability fixes, this update makes Visual Studio&amp;rsquo;s AI features feel genuinely practical rather than demo-ready.&lt;/p&gt;
&lt;p&gt;Download &lt;a href="https://visualstudio.microsoft.com/downloads/"&gt;Visual Studio 2026 Insiders&lt;/a&gt; to try it all out.&lt;/p&gt;</content:encoded></item><item><title>KubeCon Europe 2026: What .NET Developers Should Actually Care About</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/kubecon-2026-aks-updates-dotnet-developers/</link><pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/kubecon-2026-aks-updates-dotnet-developers/</guid><description>Microsoft dropped a wall of Kubernetes announcements at KubeCon Europe 2026. Here's the filtered version — only the AKS and cloud-native updates that matter if you're shipping .NET apps.</description><content:encoded>&lt;p&gt;You know that feeling when a massive announcement post drops and you&amp;rsquo;re scrolling through it thinking &amp;ldquo;cool, but what does this actually change for me&amp;rdquo;? That&amp;rsquo;s me every KubeCon season.&lt;/p&gt;
&lt;p&gt;Microsoft just published &lt;a href="https://opensource.microsoft.com/blog/2026/03/24/whats-new-with-microsoft-in-open-source-and-kubernetes-at-kubecon-cloudnativecon-europe-2026/"&gt;their full KubeCon Europe 2026 roundup&lt;/a&gt; — written by Brendan Burns himself — and honestly? There&amp;rsquo;s real substance here. Not just feature checkboxes, but the kind of operational improvements that change how you run things in production.&lt;/p&gt;
&lt;p&gt;Let me break down what actually matters for us .NET developers.&lt;/p&gt;
&lt;h2 id="mtls-without-the-service-mesh-tax"&gt;mTLS without the service mesh tax&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the thing about service meshes: everyone wants the security guarantees, nobody wants the operational overhead. AKS is finally closing that gap.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://aka.ms/aks/application-network"&gt;Azure Kubernetes Application Network&lt;/a&gt; gives you mutual TLS, application-aware authorization, and traffic telemetry — without deploying a full sidecar-heavy mesh. Combined with &lt;a href="https://aka.ms/acns/cilium-mtls"&gt;Cilium mTLS in Advanced Container Networking Services&lt;/a&gt;, you get encrypted pod-to-pod communication using X.509 certificates and SPIRE for identity management.&lt;/p&gt;
&lt;p&gt;What this means in practice: your ASP.NET Core APIs talking to background workers, your gRPC services calling each other — all encrypted and identity-verified at the network level, with zero application code changes. That&amp;rsquo;s huge.&lt;/p&gt;
&lt;p&gt;For teams migrating off &lt;code&gt;ingress-nginx&lt;/code&gt;, there&amp;rsquo;s also &lt;a href="https://aka.ms/aks/app-routing/gateway-api"&gt;Application Routing with Meshless Istio&lt;/a&gt; with full Kubernetes Gateway API support. No sidecars. Standards-based. And they shipped &lt;code&gt;ingress2gateway&lt;/code&gt; tooling for incremental migration.&lt;/p&gt;
&lt;h2 id="gpu-observability-thats-not-an-afterthought"&gt;GPU observability that&amp;rsquo;s not an afterthought&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re running AI inference alongside your .NET services (and let&amp;rsquo;s be honest, who isn&amp;rsquo;t starting to?), you&amp;rsquo;ve probably hit the GPU monitoring blind spot. You&amp;rsquo;d get great CPU/memory dashboards and then&amp;hellip; nothing for GPUs without manual exporter plumbing.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://aka.ms/aks/managed-gpu-metrics"&gt;AKS now surfaces GPU metrics natively&lt;/a&gt; into managed Prometheus and Grafana. Same stack, same dashboards, same alerting pipeline. No custom exporters, no third-party agents.&lt;/p&gt;
&lt;p&gt;On the network side, they added per-flow visibility for HTTP, gRPC, and Kafka traffic with a &lt;a href="https://learn.microsoft.com/en-us/azure/aks/container-network-observability-logs"&gt;one-click Azure Monitor experience&lt;/a&gt;. IPs, ports, workloads, flow direction, policy decisions — all in built-in dashboards.&lt;/p&gt;
&lt;p&gt;And here&amp;rsquo;s the one that made me do a double-take: &lt;a href="https://learn.microsoft.com/en-us/azure/aks/advanced-container-networking-services-overview"&gt;agentic container networking&lt;/a&gt; adds a web UI where you can ask natural-language questions about your cluster&amp;rsquo;s network state. &amp;ldquo;Why is pod X not reaching service Y?&amp;rdquo; → read-only diagnostics from live telemetry. That&amp;rsquo;s genuinely useful at 2 AM.&lt;/p&gt;
&lt;h2 id="cross-cluster-networking-that-doesnt-require-a-phd"&gt;Cross-cluster networking that doesn&amp;rsquo;t require a PhD&lt;/h2&gt;
&lt;p&gt;Multi-cluster Kubernetes has historically been a &amp;ldquo;bring your own networking glue&amp;rdquo; experience. Azure Kubernetes Fleet Manager now ships &lt;a href="https://aka.ms/kubernetes-fleet/networking/cross-cluster"&gt;cross-cluster networking&lt;/a&gt; through managed Cilium cluster mesh:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unified connectivity across AKS clusters&lt;/li&gt;
&lt;li&gt;Global service registry for cross-cluster discovery&lt;/li&gt;
&lt;li&gt;Configuration managed centrally, not repeated per cluster&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;rsquo;re running .NET microservices across regions for resilience or compliance, this replaces a lot of fragile custom plumbing. Service A in West Europe can discover and call Service B in East US through the mesh, with consistent routing and security policies.&lt;/p&gt;
&lt;h2 id="upgrades-that-dont-require-courage"&gt;Upgrades that don&amp;rsquo;t require courage&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s be honest — Kubernetes upgrades in production are stressful. &amp;ldquo;Upgrade and hope&amp;rdquo; has been the de facto strategy for too many teams, and it&amp;rsquo;s the main reason clusters fall behind on versions.&lt;/p&gt;
&lt;p&gt;Two new capabilities change this:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Blue-green agent pool upgrades&lt;/strong&gt; create a parallel node pool with the new configuration. Validate behavior, shift traffic gradually, and keep a clean rollback path. No more in-place mutations on production nodes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Agent pool rollback&lt;/strong&gt; lets you revert a node pool to its previous Kubernetes version and node image after an upgrade goes sideways — without rebuilding the cluster.&lt;/p&gt;
&lt;p&gt;Together, these finally give operators real control over the upgrade lifecycle. For .NET teams, this matters because platform velocity directly controls how fast you can adopt new runtimes, security patches, and networking capabilities.&lt;/p&gt;
&lt;h2 id="ai-workloads-are-becoming-first-class-kubernetes-citizens"&gt;AI workloads are becoming first-class Kubernetes citizens&lt;/h2&gt;
&lt;p&gt;The upstream open-source work is equally important. Dynamic Resource Allocation (DRA) just went GA in Kubernetes 1.36, making GPU scheduling a proper first-class feature instead of a workaround.&lt;/p&gt;
&lt;p&gt;A few projects worth watching:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Project&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/kaito-project/kubeairunway"&gt;AI Runway&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Common Kubernetes API for inference — deploy models without knowing K8s, with HuggingFace discovery and cost estimates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.cncf.io/blog/2026/01/07/holmesgpt-agentic-troubleshooting-built-for-the-cloud-native-era/"&gt;HolmesGPT&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Agentic troubleshooting for cloud-native — now a CNCF Sandbox project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/project-dalec/dalec"&gt;Dalec&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Declarative container image builds with SBOM generation — fewer CVEs at the build stage&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The direction is clear: your .NET API, your Semantic Kernel orchestration layer, and your inference workloads should all run on one consistent platform model. We&amp;rsquo;re getting there.&lt;/p&gt;
&lt;h2 id="where-id-start-this-week"&gt;Where I&amp;rsquo;d start this week&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re evaluating these changes for your team, here&amp;rsquo;s my honest priority list:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Observability first&lt;/strong&gt; — enable GPU metrics and network flow logs in a non-prod cluster. See what you&amp;rsquo;ve been missing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Try blue-green upgrades&lt;/strong&gt; — test the rollback workflow before your next production cluster upgrade. Build confidence in the process.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pilot identity-aware networking&lt;/strong&gt; — pick one internal service path and enable mTLS with Cilium. Measure the overhead (spoiler: it&amp;rsquo;s minimal).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Evaluate Fleet Manager&lt;/strong&gt; — if you run more than two clusters, cross-cluster networking pays for itself in reduced custom glue.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Small experiments, fast feedback. That&amp;rsquo;s always the move.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;KubeCon announcements can be overwhelming, but this batch genuinely moves the needle for .NET teams on AKS. Better networking security without mesh overhead, real GPU observability, safer upgrades, and stronger AI infrastructure foundations.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re already on AKS, this is a great moment to tighten your operational baseline. And if you&amp;rsquo;re planning to move .NET workloads to Kubernetes — the platform just got significantly more production-ready.&lt;/p&gt;</content:encoded></item><item><title>SQL MCP Server, Copilot in SSMS, and a Database Hub with AI Agents: What Actually Matters from SQLCon 2026</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/agentic-ai-microsoft-databases-what-matters/</link><pubDate>Sat, 28 Mar 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/agentic-ai-microsoft-databases-what-matters/</guid><description>Microsoft dropped a stack of database announcements at SQLCon 2026. Here's the stuff that actually matters if you're building AI-powered apps on Azure SQL.</description><content:encoded>&lt;p&gt;Microsoft just kicked off &lt;a href="https://www.microsoft.com/en-us/sql-server/blog/2026/03/18/advancing-agentic-ai-with-microsoft-databases-across-a-unified-data-estate/"&gt;SQLCon 2026 alongside FabCon in Atlanta&lt;/a&gt;, and there&amp;rsquo;s a lot to unpack. The original announcement covers everything from savings plans to enterprise compliance features. I&amp;rsquo;m going to skip the enterprise pricing slides and focus on the pieces that matter if you&amp;rsquo;re a developer building things with Azure SQL and AI.&lt;/p&gt;
&lt;h2 id="sql-mcp-server-is-in-public-preview"&gt;SQL MCP Server is in public preview&lt;/h2&gt;
&lt;p&gt;This is the headline for me. Azure SQL Database Hyperscale now has a &lt;strong&gt;SQL MCP Server&lt;/strong&gt; in public preview that lets you securely connect your SQL data to AI agents and Copilots using the &lt;a href="https://modelcontextprotocol.io/"&gt;Model Context Protocol&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve been following the MCP wave — and honestly, it&amp;rsquo;s hard to miss right now — this is a big deal. Instead of building custom data pipelines to feed your AI agents context from your database, you get a standardized protocol to expose SQL data directly. Your agents can query, reason over, and act on live database information.&lt;/p&gt;
&lt;p&gt;For those of us building AI agents with Semantic Kernel or the Microsoft Agent Framework, this opens up a clean integration path. Your agent needs to check inventory? Look up a customer record? Validate an order? MCP gives it a structured way to do that without you writing bespoke data-fetching code for every scenario.&lt;/p&gt;
&lt;h2 id="github-copilot-in-ssms-22-is-now-ga"&gt;GitHub Copilot in SSMS 22 is now GA&lt;/h2&gt;
&lt;p&gt;If you spend any time in SQL Server Management Studio — and let&amp;rsquo;s be honest, most of us still do — GitHub Copilot is now generally available in SSMS 22. Same Copilot experience you already use in VS Code and Visual Studio, but for T-SQL.&lt;/p&gt;
&lt;p&gt;The practical value here is straightforward: chat-based assistance for writing queries, refactoring stored procedures, troubleshooting performance issues, and handling admin tasks. Nothing revolutionary in concept, but having it right there in SSMS means you don&amp;rsquo;t need to context-switch to another editor just to get AI help with your database work.&lt;/p&gt;
&lt;h2 id="vector-indexes-got-a-serious-upgrade"&gt;Vector indexes got a serious upgrade&lt;/h2&gt;
&lt;p&gt;Azure SQL Database now has faster, more capable vector indexes with full insert, update, and delete support. That means your vector data stays current in real time — no batch reindexing needed.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what&amp;rsquo;s new:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Quantization&lt;/strong&gt; for smaller index sizes without losing too much accuracy&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Iterative filtering&lt;/strong&gt; for more precise results&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tighter query optimizer integration&lt;/strong&gt; for predictable performance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;rsquo;re doing retrieval-augmented generation (RAG) with Azure SQL as your vector store, these improvements are directly useful. You can keep your vectors alongside your relational data in the same database, which simplifies your architecture significantly compared to running a separate vector database.&lt;/p&gt;
&lt;p&gt;The same vector enhancements are also available in SQL database in Fabric, since both run on the same SQL engine underneath.&lt;/p&gt;
&lt;h2 id="database-hub-in-fabric-agentic-management"&gt;Database Hub in Fabric: agentic management&lt;/h2&gt;
&lt;p&gt;This one is more forward-looking, but it&amp;rsquo;s interesting. Microsoft announced the &lt;strong&gt;Database Hub in Microsoft Fabric&lt;/strong&gt; (early access), which gives you a single pane of glass across Azure SQL, Cosmos DB, PostgreSQL, MySQL, and SQL Server via Arc.&lt;/p&gt;
&lt;p&gt;The interesting angle isn&amp;rsquo;t just the unified view — it&amp;rsquo;s the agentic approach to management. AI agents continuously monitor your database estate, surface what changed, explain why it matters, and suggest what to do next. It&amp;rsquo;s a human-in-the-loop model where the agent does the legwork and you make the calls.&lt;/p&gt;
&lt;p&gt;For teams managing more than a handful of databases, this could genuinely reduce the operational noise. Instead of jumping between portals and manually checking metrics, the agent brings the signal to you.&lt;/p&gt;
&lt;h2 id="what-this-means-for-net-developers"&gt;What this means for .NET developers&lt;/h2&gt;
&lt;p&gt;The thread connecting all these announcements is clear: Microsoft is embedding AI agents at every layer of the database stack. Not as a gimmick, but as a practical tooling layer.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re building .NET apps backed by Azure SQL, here&amp;rsquo;s what I&amp;rsquo;d actually do:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Try the SQL MCP Server&lt;/strong&gt; if you&amp;rsquo;re building AI agents. It&amp;rsquo;s the cleanest way to give your agents database access without custom plumbing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enable Copilot in SSMS&lt;/strong&gt; if you haven&amp;rsquo;t already — free productivity win for daily SQL work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Look into vector indexes&lt;/strong&gt; if you&amp;rsquo;re doing RAG and currently running a separate vector store. Consolidating to Azure SQL means one less service to manage.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;The full announcement has more — savings plans, migration assistants, compliance features — but the developer story is in the MCP Server, the vector improvements, and the agentic management layer. These are the pieces that change how you build, not just how you budget.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href="https://www.microsoft.com/en-us/sql-server/blog/2026/03/18/advancing-agentic-ai-with-microsoft-databases-across-a-unified-data-estate/"&gt;full announcement from Shireesh Thota&lt;/a&gt; for the complete picture, and &lt;a href="https://aka.ms/database-hub"&gt;sign up for the Database Hub early access&lt;/a&gt; if you want to try the new management experience.&lt;/p&gt;</content:encoded></item><item><title>Azure DevOps MCP Server Lands in Microsoft Foundry: What This Means for Your AI Agents</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-devops-mcp-server-microsoft-foundry/</link><pubDate>Thu, 26 Mar 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/azure-devops-mcp-server-microsoft-foundry/</guid><description>The Azure DevOps MCP Server is now available in Microsoft Foundry. Connect your AI agents directly to DevOps workflows — work items, repos, pipelines — with a few clicks.</description><content:encoded>&lt;p&gt;MCP (Model Context Protocol) has been having a moment. If you&amp;rsquo;ve been following the AI agent ecosystem, you&amp;rsquo;ve probably noticed MCP servers popping up everywhere — giving agents the ability to interact with external tools and services through a standardized protocol.&lt;/p&gt;
&lt;p&gt;Now the &lt;a href="https://devblogs.microsoft.com/devops/remote-mcp-server-preview-in-microsoft-foundry/"&gt;Azure DevOps MCP Server is available in Microsoft Foundry&lt;/a&gt;, and this is one of those integrations that makes you think about the practical possibilities.&lt;/p&gt;
&lt;h2 id="whats-actually-happening-here"&gt;What&amp;rsquo;s actually happening here&lt;/h2&gt;
&lt;p&gt;Microsoft already released the Azure DevOps MCP Server as a &lt;a href="https://devblogs.microsoft.com/devops/azure-devops-remote-mcp-server-public-preview"&gt;public preview&lt;/a&gt; — that&amp;rsquo;s the MCP server itself. What&amp;rsquo;s new is the Foundry integration. You can now add the Azure DevOps MCP Server to your Foundry agents directly from the tool catalog.&lt;/p&gt;
&lt;p&gt;For those not familiar with Foundry yet: it&amp;rsquo;s Microsoft&amp;rsquo;s unified platform for building and managing AI-powered applications and agents at scale. Model access, orchestration, evaluation, deployment — all in one place.&lt;/p&gt;
&lt;h2 id="setting-it-up"&gt;Setting it up&lt;/h2&gt;
&lt;p&gt;The setup is surprisingly straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In your Foundry agent, go to &lt;strong&gt;Add Tools&lt;/strong&gt; &amp;gt; &lt;strong&gt;Catalog&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Search for &amp;ldquo;Azure DevOps&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Select the Azure DevOps MCP Server (preview) and click &lt;strong&gt;Create&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Enter your organization name and connect&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&amp;rsquo;s it. Your agent now has access to Azure DevOps tools.&lt;/p&gt;
&lt;h2 id="controlling-what-your-agent-can-access"&gt;Controlling what your agent can access&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the part I appreciate: you&amp;rsquo;re not stuck with an all-or-nothing approach. You can specify which tools are available to your agent. So if you only want it to read work items but not touch pipelines, you can configure that. Principle of least privilege, applied to your AI agents.&lt;/p&gt;
&lt;p&gt;This matters for enterprise scenarios where you don&amp;rsquo;t want an agent accidentally triggering a deployment pipeline because someone asked it to &amp;ldquo;help with the release.&amp;rdquo;&lt;/p&gt;
&lt;h2 id="why-this-is-interesting-for-net-teams"&gt;Why this is interesting for .NET teams&lt;/h2&gt;
&lt;p&gt;Think about what this enables in practice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sprint planning assistants&lt;/strong&gt; — agents that can pull work items, analyze velocity data, and suggest sprint capacity&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code review bots&lt;/strong&gt; — agents that understand your PR context because they can actually read your repos and linked work items&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Incident response&lt;/strong&gt; — agents that can create work items, query recent deployments, and correlate bugs with recent changes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Developer onboarding&lt;/strong&gt; — &amp;ldquo;What should I work on?&amp;rdquo; gets a real answer backed by actual project data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For .NET teams already using Azure DevOps for their CI/CD pipelines and project management, having an AI agent that can actually interact with those systems directly is a significant step toward useful automation (not just chatbot-as-a-service).&lt;/p&gt;
&lt;h2 id="the-bigger-mcp-picture"&gt;The bigger MCP picture&lt;/h2&gt;
&lt;p&gt;This is part of a broader trend: MCP servers are becoming the standard way AI agents interact with the outside world. We&amp;rsquo;re seeing them for GitHub, Azure DevOps, databases, SaaS APIs — and Foundry is becoming the hub where these connections all come together.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re building agents in the .NET ecosystem, MCP is worth paying attention to. The protocol is standardized, the tooling is maturing, and the Foundry integration makes it accessible without having to manually wire up server connections.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;The Azure DevOps MCP Server in Foundry is in preview, so expect it to evolve. But the core workflow is solid: connect, configure tool access, and let your agents work with your DevOps data. If you&amp;rsquo;re already in the Foundry ecosystem, this is a few clicks away. Give it a try and see what workflows you can build.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href="https://devblogs.microsoft.com/devops/remote-mcp-server-preview-in-microsoft-foundry/"&gt;full announcement&lt;/a&gt; for the step-by-step setup and more details.&lt;/p&gt;</content:encoded></item><item><title>Background Responses in Microsoft Agent Framework: No More Timeout Anxiety</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/background-responses-agent-framework-long-running-tasks/</link><pubDate>Thu, 26 Mar 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/background-responses-agent-framework-long-running-tasks/</guid><description>Microsoft Agent Framework now lets you offload long-running AI tasks with continuation tokens. Here's how background responses work and why they matter for your .NET agents.</description><content:encoded>&lt;p&gt;If you&amp;rsquo;ve built anything with reasoning models like o3 or GPT-5.2, you know the pain. Your agent starts thinking through a complex task, the client sits there waiting, and somewhere between &amp;ldquo;this is fine&amp;rdquo; and &amp;ldquo;did it crash?&amp;rdquo; your connection times out. All that work? Gone.&lt;/p&gt;
&lt;p&gt;Microsoft Agent Framework just shipped &lt;a href="https://devblogs.microsoft.com/agent-framework/handling-long-running-operations-with-background-responses/"&gt;background responses&lt;/a&gt; — and honestly, this is one of those features that should&amp;rsquo;ve existed from day one.&lt;/p&gt;
&lt;h2 id="the-problem-with-blocking-calls"&gt;The problem with blocking calls&lt;/h2&gt;
&lt;p&gt;In a traditional request-response pattern, your client blocks until the agent finishes. That works fine for quick tasks. But when you&amp;rsquo;re asking a reasoning model to do deep research, multi-step analysis, or generate a 20-page report? You&amp;rsquo;re looking at minutes of wall-clock time. During that window:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTP connections can time out&lt;/li&gt;
&lt;li&gt;Network blips kill the entire operation&lt;/li&gt;
&lt;li&gt;Your user stares at a spinner wondering if anything is happening&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Background responses flip this on its head.&lt;/p&gt;
&lt;h2 id="how-continuation-tokens-work"&gt;How continuation tokens work&lt;/h2&gt;
&lt;p&gt;Instead of blocking, you kick off the agent task and get back a &lt;strong&gt;continuation token&lt;/strong&gt;. Think of it like a claim ticket at a repair shop — you don&amp;rsquo;t stand at the counter waiting, you come back when it&amp;rsquo;s ready.&lt;/p&gt;
&lt;p&gt;The flow is straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Send your request with &lt;code&gt;AllowBackgroundResponses = true&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If the agent supports background processing, you get a continuation token&lt;/li&gt;
&lt;li&gt;Poll on your schedule until the token comes back &lt;code&gt;null&lt;/code&gt; — that means the result is ready&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&amp;rsquo;s the .NET version:&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;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="s"&gt;&amp;#34;https://&amp;lt;myresource&amp;gt;.openai.azure.com&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="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;GetResponsesClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;lt;deployment-name&amp;gt;&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 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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;AgentRunOptions&lt;/span&gt; &lt;span class="n"&gt;options&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&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;AllowBackgroundResponses&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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&gt;&lt;/span&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;AgentResponse&lt;/span&gt; &lt;span class="n"&gt;response&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;Write a detailed market analysis for the Q4 product launch.&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 class="n"&gt;options&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="c1"&gt;// Poll until complete&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContinuationToken&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;null&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&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;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContinuationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContinuationToken&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;response&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="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&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&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="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&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;If the agent completes immediately (simple tasks, models that don&amp;rsquo;t need background processing), no continuation token is returned. Your code just works — no special handling needed.&lt;/p&gt;
&lt;h2 id="streaming-with-resume-the-real-magic"&gt;Streaming with resume: the real magic&lt;/h2&gt;
&lt;p&gt;Polling is fine for fire-and-forget scenarios, but what about when you want real-time progress? Background responses also support streaming with built-in resumption.&lt;/p&gt;
&lt;p&gt;Each streamed update carries its own continuation token. If your connection drops mid-stream, you pick up exactly where you left off:&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;AgentRunOptions&lt;/span&gt; &lt;span class="n"&gt;options&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&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;AllowBackgroundResponses&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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&gt;&lt;/span&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 class="n"&gt;AgentResponseUpdate&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;latestUpdate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&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="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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;Write a detailed market analysis for the Q4 product launch.&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 class="n"&gt;options&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 class="n"&gt;Text&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;latestUpdate&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="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Simulate a network interruption&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Resume from exactly where we left off&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContinuationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;latestUpdate&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;ContinuationToken&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;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="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&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 class="n"&gt;Text&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;p&gt;The agent keeps processing server-side regardless of what&amp;rsquo;s happening with your client. That&amp;rsquo;s built-in fault tolerance without you writing retry logic or circuit breakers.&lt;/p&gt;
&lt;h2 id="when-to-actually-use-this"&gt;When to actually use this&lt;/h2&gt;
&lt;p&gt;Not every agent call needs background responses. For quick completions, you&amp;rsquo;re adding complexity for no reason. But here&amp;rsquo;s where they shine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Complex reasoning tasks&lt;/strong&gt; — multi-step analysis, deep research, anything that makes a reasoning model actually think&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Long content generation&lt;/strong&gt; — detailed reports, multi-part documents, extensive analysis&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unreliable networks&lt;/strong&gt; — mobile clients, edge deployments, flaky corporate VPNs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Async UX patterns&lt;/strong&gt; — submit a task, go do something else, come back for results&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For us .NET developers building enterprise apps, that last one is particularly interesting. Think about a Blazor app where a user requests a complex report — you fire off the agent task, show them a progress indicator, and let them keep working. No WebSocket gymnastics, no custom queue infrastructure, just a token and a poll loop.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Background responses are available now in both .NET and Python through Microsoft Agent Framework. If you&amp;rsquo;re building agents that do anything more complex than simple Q&amp;amp;A, this is worth adding to your toolkit. The continuation token pattern keeps things simple while solving a very real production problem.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href="https://devblogs.microsoft.com/agent-framework/handling-long-running-operations-with-background-responses/"&gt;full documentation&lt;/a&gt; for the complete API reference and more examples.&lt;/p&gt;</content:encoded></item><item><title>Foundry Agent Service is GA: What Actually Matters for .NET Agent Builders</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/foundry-agent-service-ga-what-matters/</link><pubDate>Thu, 26 Mar 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/foundry-agent-service-ga-what-matters/</guid><description>Microsoft's Foundry Agent Service just hit GA with private networking, Voice Live, production evaluations, and an open multi-model runtime. Here's what you need to know.</description><content:encoded>&lt;p&gt;Let&amp;rsquo;s be honest — building an AI agent prototype is the easy part. The hard part is everything after: getting it into production with proper network isolation, running evaluations that actually mean something, handling compliance requirements, and not breaking things at 2 AM.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://devblogs.microsoft.com/foundry/foundry-agent-service-ga/"&gt;Foundry Agent Service just went GA&lt;/a&gt;, and this release is laser-focused on that &amp;ldquo;everything after&amp;rdquo; gap.&lt;/p&gt;
&lt;h2 id="built-on-the-responses-api"&gt;Built on the Responses API&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the headline: the next-gen Foundry Agent Service is built on the OpenAI Responses API. If you&amp;rsquo;re already building with that wire protocol, migrating to Foundry is minimal code changes. What you gain: enterprise security, private networking, Entra RBAC, full tracing, and evaluation — on top of your existing agent logic.&lt;/p&gt;
&lt;p&gt;The architecture is intentionally open. You&amp;rsquo;re not locked to one model provider or one orchestration framework. Use DeepSeek for planning, OpenAI for generation, LangGraph for orchestration — the runtime handles the consistency layer.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;azure.ai.projects&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AIProjectClient&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;azure.ai.projects.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PromptAgentDefinition&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="k"&gt;with&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;DefaultAzureCredential&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;credential&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;AIProjectClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;AZURE_AI_PROJECT_ENDPOINT&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;credential&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;project_client&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;project_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_openai_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;openai_client&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;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;project_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agents&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_version&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;agent_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;my-enterprise-agent&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;definition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PromptAgentDefinition&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;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;AZURE_AI_MODEL_DEPLOYMENT_NAME&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;instructions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&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="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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;conversation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&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;conversation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&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="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;What are best practices for building AI agents?&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;extra_body&lt;/span&gt;&lt;span class="o"&gt;=&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="s2"&gt;&amp;#34;agent_reference&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;agent_reference&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="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output_text&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;blockquote&gt;
&lt;p&gt;If you&amp;rsquo;re coming from the &lt;code&gt;azure-ai-agents&lt;/code&gt; package, agents are now first-class operations on &lt;code&gt;AIProjectClient&lt;/code&gt; in &lt;code&gt;azure-ai-projects&lt;/code&gt;. Drop the standalone pin and use &lt;code&gt;get_openai_client()&lt;/code&gt; to drive responses.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="private-networking-the-enterprise-blocker-removed"&gt;Private networking: the enterprise blocker removed&lt;/h2&gt;
&lt;p&gt;This is the feature that unblocks enterprise adoption. Foundry now supports full end-to-end private networking with BYO VNet:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No public egress&lt;/strong&gt; — agent traffic never touches the public internet&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Container/subnet injection&lt;/strong&gt; into your network for local communication&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tool connectivity included&lt;/strong&gt; — MCP servers, Azure AI Search, Fabric data agents all operate over private paths&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last point is critical. It&amp;rsquo;s not just inference calls that stay private — every tool invocation and retrieval call stays inside your network boundary too. For teams operating under data classification policies that prohibit external routing, this is what was missing.&lt;/p&gt;
&lt;h2 id="mcp-authentication-done-right"&gt;MCP authentication done right&lt;/h2&gt;
&lt;p&gt;MCP server connections now support the full spectrum of auth patterns:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Auth method&lt;/th&gt;
&lt;th&gt;When to use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Key-based&lt;/td&gt;
&lt;td&gt;Simple shared access for org-wide internal tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Entra Agent Identity&lt;/td&gt;
&lt;td&gt;Service-to-service; the agent authenticates as itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Entra Managed Identity&lt;/td&gt;
&lt;td&gt;Per-project isolation; no credential management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OAuth Identity Passthrough&lt;/td&gt;
&lt;td&gt;User-delegated access; agent acts on behalf of users&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;OAuth Identity Passthrough is the interesting one. When users need to grant an agent access to their personal data — their OneDrive, their Salesforce org, a SaaS API scoped by user — the agent acts on their behalf with standard OAuth flows. No shared system identity pretending to be everyone.&lt;/p&gt;
&lt;h2 id="voice-live-speech-to-speech-without-the-plumbing"&gt;Voice Live: speech-to-speech without the plumbing&lt;/h2&gt;
&lt;p&gt;Adding voice to an agent used to mean stitching together STT, LLM, and TTS — three services, three latency hops, three billing surfaces, all synchronized by hand. &lt;strong&gt;Voice Live&lt;/strong&gt; collapses that into a single managed API with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Semantic voice activity and end-of-turn detection (understands meaning, not just silence)&lt;/li&gt;
&lt;li&gt;Server-side noise suppression and echo cancellation&lt;/li&gt;
&lt;li&gt;Barge-in support (users can interrupt mid-response)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Voice interactions go through the same agent runtime as text. Same evaluators, same traces, same cost visibility. For customer support, field service, or accessibility scenarios, this replaces what previously required a custom audio pipeline.&lt;/p&gt;
&lt;h2 id="evaluations-from-checkbox-to-continuous-monitoring"&gt;Evaluations: from checkbox to continuous monitoring&lt;/h2&gt;
&lt;p&gt;This is where Foundry gets serious about production quality. The evaluation system now has three layers:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Out-of-the-box evaluators&lt;/strong&gt; — coherence, relevance, groundedness, retrieval quality, safety. Connect to a dataset or live traffic and get scores back.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Custom evaluators&lt;/strong&gt; — encode your own business logic, tone standards, and domain-specific compliance rules.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Continuous evaluation&lt;/strong&gt; — Foundry samples live production traffic, runs your evaluator suite, and surfaces results through dashboards. Set Azure Monitor alerts for when groundedness drops or safety thresholds breach.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Everything publishes to Azure Monitor Application Insights. Agent quality, infrastructure health, cost, and app telemetry — all in one place.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;eval_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;evals&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&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="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Agent Quality Evaluation&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;data_source_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;DataSourceConfigCustom&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="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;custom&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;item_schema&lt;/span&gt;&lt;span class="o"&gt;=&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="s2"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;object&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="s2"&gt;&amp;#34;properties&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;query&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;string&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="s2"&gt;&amp;#34;required&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;query&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;include_sample_schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&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;testing_criteria&lt;/span&gt;&lt;span class="o"&gt;=&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="s2"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;azure_ai_evaluator&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="s2"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;fluency&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="s2"&gt;&amp;#34;evaluator_name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;builtin.fluency&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="s2"&gt;&amp;#34;initialization_parameters&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&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="s2"&gt;&amp;#34;deployment_name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;AZURE_AI_MODEL_DEPLOYMENT_NAME&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="s2"&gt;&amp;#34;data_mapping&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&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="s2"&gt;&amp;#34;query&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;{{item.query}}&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="s2"&gt;&amp;#34;response&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;{{sample.output_text}}&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="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="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="six-new-regions-for-hosted-agents"&gt;Six new regions for hosted agents&lt;/h2&gt;
&lt;p&gt;Hosted agents are now available in East US, North Central US, Sweden Central, Southeast Asia, Japan East, and more. This matters for data residency requirements and for compressing latency when your agent runs close to its data sources.&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;Even though the code samples in the GA announcement are Python-first, the underlying infrastructure is language-agnostic — and the .NET SDK for &lt;code&gt;azure-ai-projects&lt;/code&gt; follows the same patterns. The Responses API, the evaluation framework, the private networking, the MCP auth — all of this is available from .NET.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve been waiting for AI agents to go from &amp;ldquo;cool demo&amp;rdquo; to &amp;ldquo;I can actually ship this at work,&amp;rdquo; this GA release is the signal. Private networking, proper auth, continuous evaluation, and production monitoring are the pieces that were missing.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Foundry Agent Service is available now. Install the SDK, open &lt;a href="https://ai.azure.com"&gt;the portal&lt;/a&gt;, and start building. The &lt;a href="https://learn.microsoft.com/azure/foundry/quickstarts/get-started-code"&gt;quickstart guide&lt;/a&gt; takes you from zero to a running agent in minutes.&lt;/p&gt;
&lt;p&gt;For the full technical deep-dive with all code samples, check the &lt;a href="https://devblogs.microsoft.com/foundry/foundry-agent-service-ga/"&gt;GA announcement&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>From Laptop to Production: Deploying AI Agents to Microsoft Foundry with Two Commands</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/deploy-ai-agents-foundry-azd-two-commands/</link><pubDate>Thu, 26 Mar 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/deploy-ai-agents-foundry-azd-two-commands/</guid><description>The Azure Developer CLI now has 'azd ai agent' commands that take your AI agent from local dev to a live Foundry endpoint in minutes. Here's the full workflow.</description><content:encoded>&lt;p&gt;You know that gap between &amp;ldquo;it works on my machine&amp;rdquo; and &amp;ldquo;it&amp;rsquo;s deployed and serving traffic&amp;rdquo;? For AI agents, that gap has been painfully wide. You need to provision resources, deploy models, wire up identity, set up monitoring — and that&amp;rsquo;s before anyone can actually call your agent.&lt;/p&gt;
&lt;p&gt;The Azure Developer CLI just made this a &lt;a href="https://devblogs.microsoft.com/azure-sdk/azd-ai-agent-end-to-end/"&gt;two-command affair&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="the-new-azd-ai-agent-workflow"&gt;The new &lt;code&gt;azd ai agent&lt;/code&gt; workflow&lt;/h2&gt;
&lt;p&gt;Let me walk through what this actually looks like. You have an AI agent project — let&amp;rsquo;s say a hotel concierge agent. It works locally. You want it running on Microsoft Foundry.&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;azd ai agent init
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;azd up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. Two commands. &lt;code&gt;azd ai agent init&lt;/code&gt; scaffolds the infrastructure-as-code in your repo, and &lt;code&gt;azd up&lt;/code&gt; provisions everything on Azure and publishes your agent. You get a direct link to your agent in the Foundry portal.&lt;/p&gt;
&lt;h2 id="what-happens-under-the-hood"&gt;What happens under the hood&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;init&lt;/code&gt; command generates real, inspectable Bicep templates in your repo:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;Foundry Resource&lt;/strong&gt; (top-level container)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Foundry Project&lt;/strong&gt; (where your agent lives)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Model deployment&lt;/strong&gt; configuration (GPT-4o, etc.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Managed identity&lt;/strong&gt; with proper RBAC role assignments&lt;/li&gt;
&lt;li&gt;&lt;code&gt;azure.yaml&lt;/code&gt; for the service map&lt;/li&gt;
&lt;li&gt;&lt;code&gt;agent.yaml&lt;/code&gt; with agent metadata and environment variables&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;rsquo;s the key part: you own all of this. It&amp;rsquo;s versioned Bicep in your repo. You can inspect it, customize it, and commit it alongside your agent code. No magic black boxes.&lt;/p&gt;
&lt;h2 id="the-dev-inner-loop"&gt;The dev inner loop&lt;/h2&gt;
&lt;p&gt;What I really like is the local development story. When you&amp;rsquo;re iterating on agent logic, you don&amp;rsquo;t want to redeploy every time you change a prompt:&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;azd ai agent run
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This starts your agent locally. Pair it with &lt;code&gt;azd ai agent invoke&lt;/code&gt; to send test prompts, and you&amp;rsquo;ve got a tight feedback loop. Edit code, restart, invoke, repeat.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;invoke&lt;/code&gt; command is smart about routing too — when a local agent is running, it targets that automatically. When it&amp;rsquo;s not, it hits the remote endpoint.&lt;/p&gt;
&lt;h2 id="real-time-monitoring"&gt;Real-time monitoring&lt;/h2&gt;
&lt;p&gt;This is the feature that sold me. Once your agent is deployed:&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;azd ai agent monitor --follow
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Every request and response flowing through your agent streams to your terminal in real time. For debugging production issues, this is invaluable. No digging through log analytics, no waiting for metrics to aggregate — you see what&amp;rsquo;s happening right now.&lt;/p&gt;
&lt;h2 id="the-full-command-set"&gt;The full command set&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the quick reference:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;azd ai agent init&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scaffold a Foundry agent project with IaC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;azd up&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Provision Azure resources and deploy the agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;azd ai agent invoke&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send prompts to the remote or local agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;azd ai agent run&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run the agent locally for development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;azd ai agent monitor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stream real-time logs from the published agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;azd ai agent show&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check agent health and status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;azd down&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clean up all Azure resources&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="why-this-matters-for-net-developers"&gt;Why this matters for .NET developers&lt;/h2&gt;
&lt;p&gt;Even though the sample in the announcement is Python-based, the infrastructure story is language-agnostic. Your .NET agent gets the same Bicep scaffolding, the same managed identity setup, the same monitoring pipeline. And if you&amp;rsquo;re already using &lt;code&gt;azd&lt;/code&gt; for your .NET Aspire apps or Azure deployments, this fits right into your existing workflow.&lt;/p&gt;
&lt;p&gt;The deployment gap for AI agents has been one of the biggest friction points in the ecosystem. Going from a working prototype to a production endpoint with proper identity, networking, and monitoring shouldn&amp;rsquo;t require a week of DevOps work. Now it requires two commands and a few minutes.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;azd ai agent&lt;/code&gt; is available now. If you&amp;rsquo;ve been putting off deploying your AI agents because the infrastructure setup felt like too much work, give this a shot. Check out the &lt;a href="https://devblogs.microsoft.com/azure-sdk/azd-ai-agent-end-to-end/"&gt;full walkthrough&lt;/a&gt; for the complete step-by-step including frontend chat app integration.&lt;/p&gt;</content:encoded></item><item><title>VS Code 1.112: What .NET Developers Should Actually Care About</title><link>https://thedotnetblog.com/posts/emiliano-montesdeoca/vscode-1-112-dotnet-developers/</link><pubDate>Thu, 26 Mar 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/posts/emiliano-montesdeoca/vscode-1-112-dotnet-developers/</guid><description>VS Code 1.112 just dropped and it's packed with agent upgrades, an integrated browser debugger, MCP sandboxing, and monorepo support. Here's what actually matters if you're building with .NET.</description><content:encoded>&lt;p&gt;VS Code 1.112 just landed, and honestly? This one hits different if you&amp;rsquo;re spending your days in .NET land. There&amp;rsquo;s a lot in the &lt;a href="https://code.visualstudio.com/updates/v1_112"&gt;official release notes&lt;/a&gt;, but let me save you some scrolling and focus on what actually matters for us.&lt;/p&gt;
&lt;h2 id="copilot-cli-just-got-way-more-useful"&gt;Copilot CLI just got way more useful&lt;/h2&gt;
&lt;p&gt;The big theme this release is &lt;strong&gt;agent autonomy&lt;/strong&gt; — giving Copilot more room to do its thing without you babysitting every step.&lt;/p&gt;
&lt;h3 id="message-steering-and-queueing"&gt;Message steering and queueing&lt;/h3&gt;
&lt;p&gt;You know that moment when Copilot CLI is halfway through a task and you realize you forgot to mention something? Before, you had to wait. Now you can just send messages while a request is still running — either to steer the current response or queue up follow-up instructions.&lt;/p&gt;
&lt;p&gt;This is huge for those longer &lt;code&gt;dotnet&lt;/code&gt; scaffolding tasks where you&amp;rsquo;re watching Copilot set up a project and think &amp;ldquo;oh wait, I also need MassTransit in there.&amp;rdquo;&lt;/p&gt;
&lt;h3 id="permission-levels"&gt;Permission levels&lt;/h3&gt;
&lt;p&gt;This is the one I&amp;rsquo;m most excited about. Copilot CLI sessions now support three permission levels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Default Permissions&lt;/strong&gt; — the usual flow where tools ask for confirmation before running&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bypass Approvals&lt;/strong&gt; — auto-approves everything and retries on errors&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Autopilot&lt;/strong&gt; — goes fully autonomous: approves tools, answers its own questions, and keeps going until the task is done&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;rsquo;re doing something like scaffolding a new ASP.NET Core API with Entity Framework, migrations, and a Docker setup — Autopilot mode means you describe what you want and go grab a coffee. It&amp;rsquo;ll figure it out.&lt;/p&gt;
&lt;p&gt;You can enable Autopilot with the &lt;code&gt;chat.autopilot.enabled&lt;/code&gt; setting.&lt;/p&gt;
&lt;h3 id="preview-changes-before-delegation"&gt;Preview changes before delegation&lt;/h3&gt;
&lt;p&gt;When you delegate a task to Copilot CLI, it creates a worktree. Before, if you had uncommitted changes, you had to check Source Control to see what would be affected. Now the Chat view shows pending changes right there before you decide whether to copy, move, or ignore them.&lt;/p&gt;
&lt;p&gt;Small thing, but it saves you from that &amp;ldquo;wait, what did I have staged?&amp;rdquo; moment.&lt;/p&gt;
&lt;h2 id="debug-web-apps-without-leaving-vs-code"&gt;Debug web apps without leaving VS Code&lt;/h2&gt;
&lt;p&gt;The integrated browser now supports &lt;strong&gt;full debugging&lt;/strong&gt;. You can set breakpoints, step through code, and inspect variables — all inside VS Code. No more switching to Edge DevTools.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a new &lt;code&gt;editor-browser&lt;/code&gt; debug type, and if you already have existing &lt;code&gt;msedge&lt;/code&gt; or &lt;code&gt;chrome&lt;/code&gt; launch configurations, migrating is as simple as changing the &lt;code&gt;type&lt;/code&gt; field in your &lt;code&gt;launch.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&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="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;editor-browser&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="nt"&gt;&amp;#34;request&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;launch&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="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Debug Blazor App&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="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://localhost:5001&amp;#34;&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;p&gt;For Blazor developers, this is a game changer. You&amp;rsquo;re already running &lt;code&gt;dotnet watch&lt;/code&gt; in the terminal — now your debugging stays in the same window too.&lt;/p&gt;
&lt;p&gt;The browser also got independent zoom levels (finally), proper right-click context menus, and the zoom is remembered per website.&lt;/p&gt;
&lt;h2 id="mcp-server-sandboxing"&gt;MCP server sandboxing&lt;/h2&gt;
&lt;p&gt;This one matters more than you might think. If you&amp;rsquo;re using MCP servers — maybe you&amp;rsquo;ve set up a custom one for your Azure resources or database queries — they&amp;rsquo;ve been running with the same permissions as your VS Code process. That means full access to your filesystem, network, everything.&lt;/p&gt;
&lt;p&gt;Now you can sandbox them. In your &lt;code&gt;mcp.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&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="nt"&gt;&amp;#34;servers&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&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="nt"&gt;&amp;#34;my-azure-tools&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&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="nt"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;node&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="nt"&gt;&amp;#34;args&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;./mcp-server.js&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="nt"&gt;&amp;#34;sandboxEnabled&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="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;p&gt;When a sandboxed server needs access to something it doesn&amp;rsquo;t have, VS Code prompts you to grant permission. Much better than the &amp;ldquo;hope nobody does anything weird&amp;rdquo; approach.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Sandboxing is available on macOS and Linux for now. Windows support is coming — remote scenarios like WSL do work though.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="monorepo-customizations-discovery"&gt;Monorepo customizations discovery&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re working in a monorepo (and let&amp;rsquo;s be honest, many enterprise .NET solutions end up as one), this solves a real pain point.&lt;/p&gt;
&lt;p&gt;Previously, if you opened a subfolder of your repo, VS Code wouldn&amp;rsquo;t find your &lt;code&gt;copilot-instructions.md&lt;/code&gt;, &lt;code&gt;AGENTS.md&lt;/code&gt;, or custom skills sitting at the repository root. Now with the &lt;code&gt;chat.useCustomizationsInParentRepositories&lt;/code&gt; setting, it walks up to the &lt;code&gt;.git&lt;/code&gt; root and discovers everything.&lt;/p&gt;
&lt;p&gt;This means your team can share agent instructions, prompt files, and custom tools across all projects in a monorepo without everyone having to open the root folder.&lt;/p&gt;
&lt;h2 id="troubleshoot-for-agent-debugging"&gt;/troubleshoot for agent debugging&lt;/h2&gt;
&lt;p&gt;Ever set up custom instructions or skills and wonder why they&amp;rsquo;re not being picked up? The new &lt;code&gt;/troubleshoot&lt;/code&gt; skill reads agent debug logs and tells you what happened — which tools were used or skipped, why instructions didn&amp;rsquo;t load, and what&amp;rsquo;s causing slow responses.&lt;/p&gt;
&lt;p&gt;Enable it with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&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="nt"&gt;&amp;#34;github.copilot.chat.agentDebugLog.enabled&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="nt"&gt;&amp;#34;github.copilot.chat.agentDebugLog.fileLogging.enabled&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;p&gt;Then just type &lt;code&gt;/troubleshoot why is my custom skill not loading?&lt;/code&gt; in chat.&lt;/p&gt;
&lt;p&gt;You can also export and import these debug logs now, which is great for sharing with your team when something isn&amp;rsquo;t working as expected.&lt;/p&gt;
&lt;h2 id="image-and-binary-file-support"&gt;Image and binary file support&lt;/h2&gt;
&lt;p&gt;Agents can now read image files from disk and binary files natively. The binary files are presented in hexdump format, and image outputs (like screenshots from the integrated browser) show up in a carousel view.&lt;/p&gt;
&lt;p&gt;For .NET developers, think: paste a screenshot of a UI bug into chat and have the agent understand what&amp;rsquo;s wrong, or have it analyze the output of a Blazor component rendering.&lt;/p&gt;
&lt;h2 id="automatic-symbol-references"&gt;Automatic symbol references&lt;/h2&gt;
&lt;p&gt;Small quality-of-life improvement: when you copy a symbol name (a class, method, etc.) and paste it into chat, VS Code now automatically converts it to a &lt;code&gt;#sym:Name&lt;/code&gt; reference. This gives the agent full context about that symbol without you having to manually add it.&lt;/p&gt;
&lt;p&gt;If you want plain text instead, use &lt;code&gt;Ctrl+Shift+V&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="plugins-can-now-be-enableddisabled"&gt;Plugins can now be enabled/disabled&lt;/h2&gt;
&lt;p&gt;Previously, disabling an MCP server or plugin meant uninstalling it. Now you can toggle them on and off — both globally and per-workspace. Right-click in the Extensions view or the Customizations view and you&amp;rsquo;re done.&lt;/p&gt;
&lt;p&gt;Plugins from npm and pypi can also auto-update now, though they&amp;rsquo;ll ask for approval first since updates mean running new code on your machine.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;VS Code 1.112 is clearly pushing hard on the agent experience — more autonomy, better debugging, tighter security. For .NET developers, the integrated browser debugging and Copilot CLI improvements are the standout features.&lt;/p&gt;
&lt;p&gt;If you haven&amp;rsquo;t tried running a full Copilot CLI session in Autopilot mode for a .NET project yet, this release is a good time to start. Just remember to set your permissions and let it cook.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://code.visualstudio.com/updates/v1_112"&gt;Download VS Code 1.112&lt;/a&gt; or update from within VS Code via &lt;strong&gt;Help &amp;gt; Check for Updates&lt;/strong&gt;.&lt;/p&gt;</content:encoded></item><item><title>Emiliano Montesdeoca</title><link>https://thedotnetblog.com/authors/emiliano-montesdeoca/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://thedotnetblog.com/authors/emiliano-montesdeoca/</guid><description/><content:encoded/></item><item><title>Write for The .NET Blog</title><link>https://thedotnetblog.com/contribute/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://thedotnetblog.com/contribute/</guid><description>Share your knowledge with the .NET community. Learn how to join as an author and submit your first post.</description><content:encoded>&lt;p&gt;The .NET Blog is a community-driven publication where developers share insights, tutorials, and stories about .NET, Azure, AI, and cloud-native development. &lt;strong&gt;We welcome contributions from developers of all levels&lt;/strong&gt; — whether you&amp;rsquo;re writing your first technical post or you&amp;rsquo;re a seasoned speaker.&lt;/p&gt;
&lt;h2 id="how-to-join"&gt;How to Join&lt;/h2&gt;
&lt;p&gt;Everything lives on GitHub and follows a pull-request workflow. Here&amp;rsquo;s how to get started:&lt;/p&gt;
&lt;h3 id="1-fork-the-repository"&gt;1. Fork the Repository&lt;/h3&gt;
&lt;p&gt;Head to &lt;a href="https://github.com/thedotnetblog/blog"&gt;github.com/thedotnetblog/blog&lt;/a&gt; and fork it to your GitHub account.&lt;/p&gt;
&lt;h3 id="2-create-your-author-profile"&gt;2. Create Your Author Profile&lt;/h3&gt;
&lt;p&gt;If this is your first contribution, add yourself as an author by creating a folder at &lt;code&gt;content/authors/your-username/&lt;/code&gt; with an &lt;code&gt;index.md&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Your Name&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;your-username&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Your role or title&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;A short bio about yourself.&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/img/authors/your-avatar.jpg&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;socials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;GitHub&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://github.com/your-username&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Twitter&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://x.com/your-username&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Add your avatar image (square, at least 200×200px) to &lt;code&gt;static/img/authors/&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="3-write-your-post"&gt;3. Write Your Post&lt;/h3&gt;
&lt;p&gt;Create a new folder at &lt;code&gt;content/posts/your-username/your-post-slug/&lt;/code&gt; and add an &lt;code&gt;index.md&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Your Post Title&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ld"&gt;2025-01-01&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;your-username&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;A one-sentence description of your post.&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;dotnet&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;azure&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;Your post content in Markdown...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="4-open-a-pull-request"&gt;4. Open a Pull Request&lt;/h3&gt;
&lt;p&gt;Push your changes to your fork and open a pull request against the &lt;code&gt;main&lt;/code&gt; branch. Our team will review it and provide feedback within a few days.&lt;/p&gt;
&lt;h2 id="what-were-looking-for"&gt;What We&amp;rsquo;re Looking For&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tutorials&lt;/strong&gt; — step-by-step guides on .NET, Azure, AI, Blazor, Aspire, and more&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deep dives&lt;/strong&gt; — in-depth explorations of a technology, pattern, or architecture&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Community stories&lt;/strong&gt; — your experience building with .NET in production&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Event recaps&lt;/strong&gt; — summaries of conferences, meetups, or webinars&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="guidelines"&gt;Guidelines&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Content should be technical and relevant to the .NET ecosystem&lt;/li&gt;
&lt;li&gt;Code examples must be accurate and tested against a real project&lt;/li&gt;
&lt;li&gt;Include a meaningful description and at least one relevant tag&lt;/li&gt;
&lt;li&gt;Posts are automatically translated into all supported languages&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="get-in-touch"&gt;Get in Touch&lt;/h2&gt;
&lt;p&gt;Open an issue on &lt;a href="https://github.com/thedotnetblog/blog/issues"&gt;GitHub&lt;/a&gt; or reach out on &lt;a href="https://x.com/thedotnetblog"&gt;X / Twitter&lt;/a&gt;. We&amp;rsquo;d love to have you as part of the community!&lt;/p&gt;</content:encoded></item></channel></rss>