{"id":4952,"date":"2026-08-27T17:12:13","date_gmt":"2026-08-27T17:12:13","guid":{"rendered":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2026\/08\/27\/how-uno-platform-uses-net-mcp-and-ai-to-build-high-quality-apps\/"},"modified":"2026-08-27T17:12:13","modified_gmt":"2026-08-27T17:12:13","slug":"how-uno-platform-uses-net-mcp-and-ai-to-build-high-quality-apps","status":"publish","type":"post","link":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2026\/08\/27\/how-uno-platform-uses-net-mcp-and-ai-to-build-high-quality-apps\/","title":{"rendered":"How Uno Platform uses .NET, MCP, and AI to build high quality apps"},"content":{"rendered":"<blockquote>\n<p>This is a guest post by Sam Basu. Sam is a technologist, author, speaker, Microsoft MVP and Developer Advocate for Uno Platform.<\/p>\n<\/blockquote>\n<p>If you\u2019ve spent any time building software in the last couple of years, you\u2019ve felt the shift. AI is no longer a novelty sitting on the sidelines \u2013 it\u2019s right there in the editor, the terminal, the build pipeline. And for .NET developers, this moment is particularly exciting. The ecosystem is deep, the tooling is stellar, and AI just keeps getting better at navigating both.<\/p>\n<p>But raw AI power and grounded, contextual AI are two very different things. An AI agent will happily write you a settings page for a cross-platform .NET app. It will compile. It will pass review if you only read it. And it will still be wrong in ways you cannot see until the app is running in front of you. Closing that gap is the problem we set out to solve at Uno Platform.<\/p>\n<p>The developers who will get the most out of this era aren\u2019t the ones prompting the hardest \u2013 they\u2019re the ones giving AI the right context to actually do the job well. The focus is on quality \u2013 how can we provide AI all the guardrails to be successful and be able to validate its own work, and tooling that makes .NET developers productive from the start. Let\u2019s unpack.<\/p>\n<h2>Why MCP, and why we ended up with two servers<\/h2>\n<p>The obvious first move is context stuffing: shovel the docs into the prompt,<br \/>\nadd a long instructions file, hope for the best. It fails for a reason that<br \/>\nis clear in hindsight. Documentation is large, the useful slice is small and<br \/>\nquery-dependent, and no amount of prompt real estate substitutes for the<br \/>\nagent being able to look something up at the moment it has the question.<\/p>\n<p><a href=\"https:\/\/modelcontextprotocol.io\/\">Model Context Protocol<\/a> solves the lookup<br \/>\nproblem. It does not solve the verification problem. Knowing what the API<br \/>\nshould be does not tell an agent whether the layout it just wrote actually<br \/>\nrenders. Those are two different jobs with two different lifetimes, and that<br \/>\ndistinction is why we ended up with two servers rather than one.<\/p>\n<p>The split we landed on maps to those two lifetimes.<\/p>\n<h3>The docs server: grounding<\/h3>\n<p>The docs server is publicly hosted at <code>https:\/\/mcp.platform.uno\/v1<\/code>, speaks<br \/>\nHTTP, authenticates with OAuth, and is stateless. It answers <em>what is true<br \/>\nabout this framework right now<\/em> \u2013 a question whose answer changes when we<br \/>\nship, not when your app runs.<\/p>\n<ul>\n<li><code>uno_platform_docs_search<\/code> \u2013 search official documentation and return the<br \/>\nmost relevant results<\/li>\n<li><code>uno_platform_docs_fetch<\/code> \u2013 fetch a full documentation page as markdown<\/li>\n<li><code>uno_platform_agent_rules_init<\/code> \u2013 initialize the agent session with rules<br \/>\nfor working against a running app<\/li>\n<li><code>uno_platform_usage_rules_init<\/code> \u2013 load common API usage rules<\/li>\n<\/ul>\n<p>It also ships two prompts: <code>\/new<\/code> to scaffold an app with current best<br \/>\npractices, and <code>\/init<\/code> to prime an existing conversation before adding a<br \/>\nfeature to an existing codebase.<\/p>\n<p>The design property that matters is that this server is versioned with our<br \/>\ndocumentation, not with the developer\u2019s SDK. Correct a doc page and every<br \/>\nagent everywhere gets the correction on its next call. That is a very<br \/>\ndifferent maintenance story from shipping guidance inside a NuGet package,<br \/>\nand it is the main reason we host it rather than distribute it.<\/p>\n<h3>The app server: eyes and hands<\/h3>\n<p>The app server is the opposite in every dimension. It ships as a .NET tool<br \/>\nlaunched over stdio, runs on the developer\u2019s machine as a bridge to the Uno<br \/>\nDevServer, is stateful, and belongs to exactly one session. It answers <em>what<br \/>\nis actually happening right now<\/em>.<\/p>\n<p>It gives an agent four capabilities. It can <strong>run<\/strong> the app \u2013<br \/>\n<code>uno_app_start<\/code> launches in debug mode with Hot Reload enabled, so the agent<br \/>\ncontrols the whole lifecycle rather than waiting for a human to press F5. It<br \/>\ncan <strong>see<\/strong> \u2013 <code>uno_app_get_screenshot<\/code> for pixels, and<br \/>\n<code>uno_app_visualtree_snapshot<\/code> for an XML snapshot of the visual tree. It can<br \/>\n<strong>act<\/strong> \u2013 <code>uno_app_pointer_click<\/code>, <code>uno_app_key_press<\/code>, <code>uno_app_type_text<\/code>,<br \/>\nand <code>uno_app_element_peer_action<\/code> to invoke automation peers directly. And<br \/>\nit can <strong>check itself<\/strong> \u2013 <code>uno_health<\/code> reports the status of the bridge and<br \/>\nits connection, because an agent that cannot tell \u201cthe app is broken\u201d from<br \/>\n\u201cmy connection dropped\u201d will confidently debug the wrong thing.<\/p>\n<p><img data-opt-id=251052761  fetchpriority=\"high\" decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2026\/08\/uno-mcp-tools-ide.webp\" alt=\"MCP tool list showing Uno app and docs servers registered in an IDE\" \/><\/p>\n<p><em>Both servers, side by side, as the agent sees them.<\/em><\/p>\n<p>The visual tree tool is the one that earns its keep. Screenshots tell a<br \/>\nmodel that something looks wrong; the XML tree tells it which element is at<br \/>\nfault and what its properties are. Pixels are for detection, structure is<br \/>\nfor diagnosis, and an agent needs both.<\/p>\n<p>There is one detail in that tool list worth calling out: read the<br \/>\ndescription on <code>uno_app_pointer_click<\/code> and it says <em>prefer<br \/>\n<code>uno_app_element_peer_action<\/code><\/em>. That preference lives in the tool<br \/>\ndescription itself rather than in documentation nobody loads, because<br \/>\ncoordinate clicking is brittle across window sizes and DPI while automation<br \/>\npeers are stable. More on why that matters below.<\/p>\n<h2>Building it: the MCP C# SDK in production<\/h2>\n<p>Both servers are written in C# on the<br \/>\n<a href=\"https:\/\/github.com\/modelcontextprotocol\/csharp-sdk\">official MCP C# SDK<\/a>,<br \/>\nwhich Microsoft maintains in collaboration with the community. Two things<br \/>\nwe would tell any .NET team starting the same work.<\/p>\n<p><strong>Pick your transport from your topology.<\/strong> The docs server is HTTP because<br \/>\nit is a hosted multi-tenant service that needs OAuth. The app server is<br \/>\nstdio because it is a child process on one developer\u2019s machine talking to<br \/>\none running app. The topology decides the transport; there is not much of a<br \/>\nchoice to agonize over once you have written the constraints down.<\/p>\n<p><strong>Your tool definitions are a permanent tax on the context window.<\/strong> Every<br \/>\ntool name, description, and input schema is loaded before the model does any<br \/>\nwork. Our docs server costs about 6.4k tokens and the app server about 1.5k<br \/>\n\u2013 for comparison, the built-in GitHub MCP server in the same session costs<br \/>\nabout 5.2k. That is real budget spent before a single question is answered,<br \/>\nand it is why terse, high-signal tool descriptions are not a style<br \/>\npreference.<\/p>\n<p><img data-opt-id=1505048306  fetchpriority=\"high\" decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2026\/08\/uno-mcp-tools-cli.webp\" alt=\"Copilot CLI listing MCP servers with transport, token cost, and auth\" \/><\/p>\n<p><em>The same servers in GitHub Copilot CLI. Note the token cost per server.<\/em><\/p>\n<p>That second point has a corollary: <strong>tool descriptions are prompts, not<br \/>\ndocumentation.<\/strong> A tool the model never selects may as well not exist, and<br \/>\nthe only lever you have over selection is the wording. This is why<br \/>\n<code>uno_app_pointer_click<\/code> explicitly tells the model to prefer the<br \/>\nautomation-peer tool instead \u2013 that is not documenting a preference, it is<br \/>\nsteering a decision at the moment it is made.<\/p>\n<h2>Generating code and validating it are different problems<\/h2>\n<p>Here is the hot take: AI can write UI code faster than any human team, and<br \/>\nit cannot tell whether what it wrote is correct. As agentic workflows become<br \/>\nnormal, that asymmetry is the bottleneck. Generation got cheap. Verification<br \/>\ndid not.<\/p>\n<p>Web developers already solved their half of this. Playwright drives a real<br \/>\nbrowser, so an agent working on a web app can check its own work. There has<br \/>\nbeen no equivalent for a native cross-platform .NET app running on Windows,<br \/>\nmacOS, Linux, iOS, Android, or WebAssembly \u2013 the app is a black box the<br \/>\nmoment it launches.<\/p>\n<p>The app server is our answer to that:<br \/>\n<a href=\"https:\/\/platform.uno\/blog\/playwright-for-dotnet-apps\/\">Playwright-style UI automation for .NET apps<\/a>.<br \/>\nThe agent writes a change, the app hot reloads, the agent takes a<br \/>\nscreenshot, reads the visual tree, clicks through the flow, and decides for<br \/>\nitself whether the change did what was asked. When it did not, the agent<br \/>\nfixes it before handing anything back.<\/p>\n<p>Code is cheap. Software is not. This is how you hold both truths at once.<\/p>\n<h2>Skills: giving the agent the \u201chow\u201d<\/h2>\n<p>MCP tools give an agent the <em>what<\/em>. They do not say when to reach for which<br \/>\none, or in what order, or what \u201cdone\u201d looks like. That is what Skills are<br \/>\nfor.<\/p>\n<p>The cooking analogy holds up well here. <strong>MCP tools are ingredients<\/strong> \u2013<br \/>\natomic, each does one thing. <strong>Skills are recipe cards<\/strong> \u2013 the reusable<br \/>\ninstructions for combining ingredients into something worth eating. <strong>The<br \/>\nagent is the cook<\/strong>, choosing a recipe and adapting it to what is actually<br \/>\nin the kitchen.<\/p>\n<p>Our <a href=\"https:\/\/github.com\/unoplatform\/studio\/tree\/main\/skills\">Skills library<\/a><br \/>\nis organized by the thing you are actually doing: MVUX state and feeds,<br \/>\nnavigation, theming, the Uno Toolkit controls, and testing. The one that<br \/>\ncloses the loop is <code>uno-testing-ui<\/code>, which automates UI testing through the<br \/>\napp server \u2013 the Skill knows the order to drive the tools in, so the agent<br \/>\ndoes not have to work it out from first principles every session.<\/p>\n<p>Grounded documentation, a live app it can inspect, and curated procedure for<br \/>\nthe workflows that matter: that combination is what we mean by contextual<br \/>\nAI.<\/p>\n<p><img data-opt-id=1575002765  data-opt-src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2026\/08\/uno-skills-library.webp\"  decoding=\"async\" src=\"data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%20100%%20100%%22%20width%3D%22100%%22%20height%3D%22100%%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20width%3D%22100%%22%20height%3D%22100%%22%20fill%3D%22transparent%22%2F%3E%3C%2Fsvg%3E\" alt=\"Uno Platform Skills listed as toggleable plugins inside an AI agent session\" \/><\/p>\n<p><em>Skills install as plugins, available to any MCP-compatible agent.<\/em><\/p>\n<h2>What it adds up to<\/h2>\n<p>The most interesting thing we built with all of this is not a feature list,<br \/>\nit is a compiler running where a compiler has no business running.<\/p>\n<p><a href=\"https:\/\/platform.uno\/blog\/introducing-uno-platform-studio-3-0-ai-native-productivity-platform-for-enterprise-net-applications\/\">Uno Platform Studio 3.0<\/a><br \/>\ngenerates a full cross-platform .NET app entirely in the browser. Behind the<br \/>\nprompt box, a specialized agent orchestrated by<br \/>\n<a href=\"https:\/\/learn.microsoft.com\/agent-framework\/overview\/\">Microsoft Agent Framework<\/a><br \/>\nplans and executes the work across parallel steps and multi-turn<br \/>\nconversations. A full <a href=\"https:\/\/github.com\/dotnet\/roslyn\">Roslyn<\/a> workspace<br \/>\nthen compiles what the agent writes, loads the generated assemblies,<br \/>\nresolves NuGet changes, and hot reloads the result into the running app \u2013<br \/>\nall in the browser, while you watch. The docs server keeps the agent\u2019s<br \/>\nknowledge current. The app server lets it check its own work. The Skills<br \/>\nkeep it on the rails.<\/p>\n<p>That is Roslyn, Microsoft Agent Framework, and the MCP C# SDK doing work<br \/>\nthat would have been a research project a few years ago, and the entire<br \/>\nstack is .NET.<\/p>\n<p><img data-opt-id=1753922303  data-opt-src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2026\/08\/uno-studio-generation.webp\"  decoding=\"async\" src=\"data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%20100%%20100%%22%20width%3D%22100%%22%20height%3D%22100%%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20width%3D%22100%%22%20height%3D%22100%%22%20fill%3D%22transparent%22%2F%3E%3C%2Fsvg%3E\" alt=\"Uno Platform Studio generating a CRM dashboard app in a browser with an agent panel\" \/><\/p>\n<p><em>Prompt on the right, compiled and running .NET app on the left. Not a mockup.<\/em><\/p>\n<p>The practical consequence for a team is that the agent stops being a fast<br \/>\ntypist. It knows your design system, it validates its own output against a<br \/>\nrunning app, and it follows workflows you chose. That is a different<br \/>\nproposition from writing code faster.<\/p>\n<p>The generated .NET app is fully interactive in the browser, along with page navigation and<br \/>\nPreviews to work on app UI in isolation. Developers can iterate on app UI with the<br \/>\nAgent or manually with Hot Design in the browser \u2013 the changes are immediately visible<br \/>\nwith Hot Reload. There is no barrier to entry \u2013 developers can start in the browser,<br \/>\niterate on app UI with Agent or Hot Design, and drop down to local IDE\/CLI with same<br \/>\ntools, when ready.<\/p>\n<p><img data-opt-id=1061108472  data-opt-src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2026\/08\/uno-studio-design-scaled.webp\"  decoding=\"async\" src=\"data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%20100%%20100%%22%20width%3D%22100%%22%20height%3D%22100%%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20width%3D%22100%%22%20height%3D%22100%%22%20fill%3D%22transparent%22%2F%3E%3C%2Fsvg%3E\" alt=\"Uno Platform Studio in edit mode\" \/><\/p>\n<h2>Why we work upstream<\/h2>\n<p>None of this would be buildable on a foundation we could not influence, and<br \/>\nthat is the honest reason we invest where we do.<\/p>\n<p>We co-maintain<br \/>\n<a href=\"https:\/\/github.com\/mono\/SkiaSharp\">SkiaSharp<\/a> alongside Microsoft\u2019s .NET<br \/>\nteam. SkiaSharp is the 2D graphics API underneath a large share of .NET<br \/>\ncharting, custom controls, and data visualization \u2013 it is built on Google\u2019s<br \/>\nSkia, the same engine in Chrome and Android \u2013 and it is what Uno Platform<br \/>\nrenders with. Becoming a<br \/>\n<a href=\"https:\/\/platform.uno\/blog\/skiasharp-4-co-maintainer-announcement\/\">co-maintainer<\/a><br \/>\nformalized years of investment ahead of<br \/>\n<a href=\"https:\/\/platform.uno\/blog\/announcing-skiasharp-4-0\/\">SkiaSharp 4.0<\/a>, the<br \/>\nlargest release the project has had in years.<\/p>\n<p>We also work directly on the .NET runtime through a<br \/>\n<a href=\"https:\/\/platform.uno\/blog\/announcing-unoplatform-microsoft-dotnet-collaboration\/\">formal collaboration with the Microsoft .NET team<\/a>,<br \/>\ncontributing to .NET for Android and .NET for iOS bindings and to AOT in<br \/>\n.NET 10.<\/p>\n<p>The pattern is the same one this whole post describes: the further upstream<br \/>\nyou fix something, the more people never have to think about it again.<\/p>\n<h2>Wrap up<\/h2>\n<p>If you are building an MCP server for your own .NET stack, the two things we<br \/>\nwould pass along are these. Split your servers by lifetime, not by feature \u2013<br \/>\nknowledge that changes when you ship does not belong in the same process as<br \/>\nstate that changes when the app runs. And spend real time on your tool<br \/>\ndescriptions, because they are prompts, and a tool the model never selects<br \/>\nmay as well not exist.<\/p>\n<p>The rest is ordinary .NET. The MCP C# SDK, Roslyn, Microsoft Agent<br \/>\nFramework, and a graphics stack we help maintain, doing work that is<br \/>\nanything but ordinary.<\/p>\n<p>Try the Uno Platform MCP servers at<br \/>\n<a href=\"https:\/\/aka.platform.uno\/mcp\">aka.platform.uno\/mcp<\/a>.<\/p>\n<p>The post <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/how-uno-platform-uses-dotnet-mcp-ai-to-build-high-quality-apps\/\">How Uno Platform uses .NET, MCP, and AI to build high quality apps<\/a> appeared first on <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\">.NET Blog<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>This is a guest post by Sam Basu. Sam is a technologist, author, speaker, Microsoft MVP and Developer Advocate for [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":4953,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[7],"tags":[],"class_list":["post-4952","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet"],"_links":{"self":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/4952","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/comments?post=4952"}],"version-history":[{"count":0,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/4952\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media\/4953"}],"wp:attachment":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media?parent=4952"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/categories?post=4952"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/tags?post=4952"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}