<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[System Shogun]]></title><description><![CDATA[Learn all about system design, software architecture, cloud architecture, and AI in one place.]]></description><link>https://systemshogun.com</link><image><url>https://substackcdn.com/image/fetch/$s_!EGQ-!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5048a823-9128-48de-ac3a-c0e4d523f053_800x800.png</url><title>System Shogun</title><link>https://systemshogun.com</link></image><generator>Substack</generator><lastBuildDate>Sun, 14 Jun 2026 17:50:03 GMT</lastBuildDate><atom:link href="https://systemshogun.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[System Shogun]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[systemshogun@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[systemshogun@substack.com]]></itunes:email><itunes:name><![CDATA[Kaloyan Drenski]]></itunes:name></itunes:owner><itunes:author><![CDATA[Kaloyan Drenski]]></itunes:author><googleplay:owner><![CDATA[systemshogun@substack.com]]></googleplay:owner><googleplay:email><![CDATA[systemshogun@substack.com]]></googleplay:email><googleplay:author><![CDATA[Kaloyan Drenski]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[CQRS Visualized]]></title><description><![CDATA[Learn CQRS with some diagrams]]></description><link>https://systemshogun.com/p/cqrs-visualized</link><guid isPermaLink="false">https://systemshogun.com/p/cqrs-visualized</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Sun, 31 May 2026 07:36:38 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!REEQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6163eb23-05fd-414b-a060-b4729e9e082f_914x350.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most explanations of CQRS drown you in definitions. This one shows you the <em>one<br>moment</em> that makes it click: you save something, read it back immediately, and it<br>isn't there yet. That gap is the whole pattern. Let's earn it.</p><p>(You&#8217;ll find a working demo in C# /.NET at the end)</p><h2><strong>The problem</strong></h2><p>In a normal CRUD app, one model does two jobs: it enforces business rules on writes<br><em>and</em> gets reshaped for every screen on reads. That&#8217;s fine until the jobs fight &#8212;<br>your entity bloats, your queries grow joins, and you can&#8217;t make reads faster without<br>risking writes.</p><p><strong>CQRS</strong> (Command Query Responsibility Segregation) splits them in two:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!REEQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6163eb23-05fd-414b-a060-b4729e9e082f_914x350.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!REEQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6163eb23-05fd-414b-a060-b4729e9e082f_914x350.png 424w, https://substackcdn.com/image/fetch/$s_!REEQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6163eb23-05fd-414b-a060-b4729e9e082f_914x350.png 848w, https://substackcdn.com/image/fetch/$s_!REEQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6163eb23-05fd-414b-a060-b4729e9e082f_914x350.png 1272w, https://substackcdn.com/image/fetch/$s_!REEQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6163eb23-05fd-414b-a060-b4729e9e082f_914x350.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!REEQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6163eb23-05fd-414b-a060-b4729e9e082f_914x350.png" width="914" height="350" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6163eb23-05fd-414b-a060-b4729e9e082f_914x350.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:350,&quot;width&quot;:914,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:24842,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/199944202?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6163eb23-05fd-414b-a060-b4729e9e082f_914x350.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!REEQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6163eb23-05fd-414b-a060-b4729e9e082f_914x350.png 424w, https://substackcdn.com/image/fetch/$s_!REEQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6163eb23-05fd-414b-a060-b4729e9e082f_914x350.png 848w, https://substackcdn.com/image/fetch/$s_!REEQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6163eb23-05fd-414b-a060-b4729e9e082f_914x350.png 1272w, https://substackcdn.com/image/fetch/$s_!REEQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6163eb23-05fd-414b-a060-b4729e9e082f_914x350.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The write side owns <strong>correctness</strong>. The read side owns <strong>speed</strong>. Domain events<br>keep them in sync. Reads never touch the write model's joins &#8212; they hit pre-shaped<br>views, so they're trivial and scale on their own.</p><blockquote><p>Two things people get wrong: CQRS is <em>not</em> two databases (that's an option, not<br>the definition), and adding a message bus is <em>not</em> CQRS. The defining move is<br><strong>separate read and write models.</strong></p></blockquote><h2><strong>The choice that defines everything: when does the read side catch up?</strong></h2><p>This is where it gets interesting. You decide <em>when</em> the projection runs.</p><p><strong>Synchronous</strong> &#8212; run it inline, before the write returns. Reads are always fresh:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FV1j!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b68267-2bbb-4947-932f-d206634f7031_930x498.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FV1j!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b68267-2bbb-4947-932f-d206634f7031_930x498.png 424w, https://substackcdn.com/image/fetch/$s_!FV1j!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b68267-2bbb-4947-932f-d206634f7031_930x498.png 848w, https://substackcdn.com/image/fetch/$s_!FV1j!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b68267-2bbb-4947-932f-d206634f7031_930x498.png 1272w, https://substackcdn.com/image/fetch/$s_!FV1j!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b68267-2bbb-4947-932f-d206634f7031_930x498.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FV1j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b68267-2bbb-4947-932f-d206634f7031_930x498.png" width="930" height="498" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/90b68267-2bbb-4947-932f-d206634f7031_930x498.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:498,&quot;width&quot;:930,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:29296,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/199944202?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b68267-2bbb-4947-932f-d206634f7031_930x498.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FV1j!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b68267-2bbb-4947-932f-d206634f7031_930x498.png 424w, https://substackcdn.com/image/fetch/$s_!FV1j!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b68267-2bbb-4947-932f-d206634f7031_930x498.png 848w, https://substackcdn.com/image/fetch/$s_!FV1j!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b68267-2bbb-4947-932f-d206634f7031_930x498.png 1272w, https://substackcdn.com/image/fetch/$s_!FV1j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b68267-2bbb-4947-932f-d206634f7031_930x498.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Asynchronous</strong> &#8212; drop the event on a queue and return instantly; a background<br>worker projects it <em>later</em>. Fast writes, but the read side serves <strong>stale data</strong><br>until it catches up:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jFr3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bb8a3e1-bd6c-48a2-bd78-206baa63e2e8_1166x672.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jFr3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bb8a3e1-bd6c-48a2-bd78-206baa63e2e8_1166x672.png 424w, https://substackcdn.com/image/fetch/$s_!jFr3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bb8a3e1-bd6c-48a2-bd78-206baa63e2e8_1166x672.png 848w, https://substackcdn.com/image/fetch/$s_!jFr3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bb8a3e1-bd6c-48a2-bd78-206baa63e2e8_1166x672.png 1272w, https://substackcdn.com/image/fetch/$s_!jFr3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bb8a3e1-bd6c-48a2-bd78-206baa63e2e8_1166x672.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jFr3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bb8a3e1-bd6c-48a2-bd78-206baa63e2e8_1166x672.png" width="1166" height="672" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3bb8a3e1-bd6c-48a2-bd78-206baa63e2e8_1166x672.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:672,&quot;width&quot;:1166,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:43508,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/199944202?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bb8a3e1-bd6c-48a2-bd78-206baa63e2e8_1166x672.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jFr3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bb8a3e1-bd6c-48a2-bd78-206baa63e2e8_1166x672.png 424w, https://substackcdn.com/image/fetch/$s_!jFr3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bb8a3e1-bd6c-48a2-bd78-206baa63e2e8_1166x672.png 848w, https://substackcdn.com/image/fetch/$s_!jFr3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bb8a3e1-bd6c-48a2-bd78-206baa63e2e8_1166x672.png 1272w, https://substackcdn.com/image/fetch/$s_!jFr3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bb8a3e1-bd6c-48a2-bd78-206baa63e2e8_1166x672.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>That red window is <strong>eventual consistency</strong> &#8212; the cost of CQRS at scale.</p><p>Here's what <code>GET /tickets</code> returns over time after closing a ticket at t=0:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4eOK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73587934-cc90-47be-9a14-3590607658c4_1305x523.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4eOK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73587934-cc90-47be-9a14-3590607658c4_1305x523.png 424w, https://substackcdn.com/image/fetch/$s_!4eOK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73587934-cc90-47be-9a14-3590607658c4_1305x523.png 848w, https://substackcdn.com/image/fetch/$s_!4eOK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73587934-cc90-47be-9a14-3590607658c4_1305x523.png 1272w, https://substackcdn.com/image/fetch/$s_!4eOK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73587934-cc90-47be-9a14-3590607658c4_1305x523.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4eOK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73587934-cc90-47be-9a14-3590607658c4_1305x523.png" width="1305" height="523" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/73587934-cc90-47be-9a14-3590607658c4_1305x523.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:523,&quot;width&quot;:1305,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:28061,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/199944202?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73587934-cc90-47be-9a14-3590607658c4_1305x523.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4eOK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73587934-cc90-47be-9a14-3590607658c4_1305x523.png 424w, https://substackcdn.com/image/fetch/$s_!4eOK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73587934-cc90-47be-9a14-3590607658c4_1305x523.png 848w, https://substackcdn.com/image/fetch/$s_!4eOK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73587934-cc90-47be-9a14-3590607658c4_1305x523.png 1272w, https://substackcdn.com/image/fetch/$s_!4eOK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73587934-cc90-47be-9a14-3590607658c4_1305x523.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>The trade-off, in one table</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qQUo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a8518e-3e59-49be-ab99-2f211be3e501_764x210.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qQUo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a8518e-3e59-49be-ab99-2f211be3e501_764x210.png 424w, https://substackcdn.com/image/fetch/$s_!qQUo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a8518e-3e59-49be-ab99-2f211be3e501_764x210.png 848w, https://substackcdn.com/image/fetch/$s_!qQUo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a8518e-3e59-49be-ab99-2f211be3e501_764x210.png 1272w, https://substackcdn.com/image/fetch/$s_!qQUo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a8518e-3e59-49be-ab99-2f211be3e501_764x210.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qQUo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a8518e-3e59-49be-ab99-2f211be3e501_764x210.png" width="764" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/93a8518e-3e59-49be-ab99-2f211be3e501_764x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:764,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:20945,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/199944202?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a8518e-3e59-49be-ab99-2f211be3e501_764x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qQUo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a8518e-3e59-49be-ab99-2f211be3e501_764x210.png 424w, https://substackcdn.com/image/fetch/$s_!qQUo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a8518e-3e59-49be-ab99-2f211be3e501_764x210.png 848w, https://substackcdn.com/image/fetch/$s_!qQUo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a8518e-3e59-49be-ab99-2f211be3e501_764x210.png 1272w, https://substackcdn.com/image/fetch/$s_!qQUo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a8518e-3e59-49be-ab99-2f211be3e501_764x210.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2><strong>So when do you actually use CQRS?</strong></h2><p><strong>Use it</strong> when reads and writes genuinely diverge: different shapes, wildly<br>different load (reads &#8811; writes), or a complex domain where query concerns pollute<br>your business rules. Think product catalogs, social feeds, dashboards, ledgers.</p><p><strong>Skip it</strong> for simple symmetric CRUD &#8212; it&#8217;s pure overhead. And if you can&#8217;t<br>tolerate stale reads <em>and</em> can&#8217;t design the UX around them, stay synchronous or<br>stay CRUD.</p><p>The mature move isn&#8217;t &#8220;use CQRS.&#8221; It&#8217;s knowing that the write side is your truth,<br>the read side is a rebuildable cache, and <strong>choosing how fresh that cache must be &#8212;<br>on purpose.</strong></p><p><strong>Demo:</strong> <a href="https://github.com/kaldren/SystemDesignLab/tree/main/01-CQRS">https://github.com/kaldren/SystemDesignLab/tree/main/01-CQRS</a></p><p>This repository follows an AI-first approach and is designed to evolve into a platform for learning, experimentation, and technical content creation. The goal is to leverage AI to streamline the development of Proof of Concepts (POCs) while automatically generating supporting artifacts such as documentation, architectural notes, and blog content.</p>]]></content:encoded></item><item><title><![CDATA[Microsoft Agent Framework Workflows & Observability Demo]]></title><description><![CDATA[Learn how to build agentic workflows with telemetry using Aspire, Microsoft Agent Framework, and Microsoft Foundry]]></description><link>https://systemshogun.com/p/microsoft-agent-framework-workflows</link><guid isPermaLink="false">https://systemshogun.com/p/microsoft-agent-framework-workflows</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Tue, 26 May 2026 12:02:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/tnsFlZijTMY" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today we&#8217;re going to see how to build workflows with FAN-OUT / FAN-IN architecture using <a href="https://learn.microsoft.com/en-us/agent-framework/overview/">Microsoft Agent Framework</a>. We&#8217;re also going to add telemetry using <a href="https://aspire.dev/">Aspire</a>.</p><p>If you prefer video - here&#8217;s a quick walkthrough:</p><div id="youtube2-tnsFlZijTMY" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;tnsFlZijTMY&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/tnsFlZijTMY?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>Here&#8217;s the architecture:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FMDQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb01636fe-47e9-41fc-96c0-04d93877dd08_2398x2124.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FMDQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb01636fe-47e9-41fc-96c0-04d93877dd08_2398x2124.png 424w, https://substackcdn.com/image/fetch/$s_!FMDQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb01636fe-47e9-41fc-96c0-04d93877dd08_2398x2124.png 848w, https://substackcdn.com/image/fetch/$s_!FMDQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb01636fe-47e9-41fc-96c0-04d93877dd08_2398x2124.png 1272w, https://substackcdn.com/image/fetch/$s_!FMDQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb01636fe-47e9-41fc-96c0-04d93877dd08_2398x2124.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FMDQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb01636fe-47e9-41fc-96c0-04d93877dd08_2398x2124.png" width="1456" height="1290" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b01636fe-47e9-41fc-96c0-04d93877dd08_2398x2124.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1290,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:266381,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/199289309?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb01636fe-47e9-41fc-96c0-04d93877dd08_2398x2124.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FMDQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb01636fe-47e9-41fc-96c0-04d93877dd08_2398x2124.png 424w, https://substackcdn.com/image/fetch/$s_!FMDQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb01636fe-47e9-41fc-96c0-04d93877dd08_2398x2124.png 848w, https://substackcdn.com/image/fetch/$s_!FMDQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb01636fe-47e9-41fc-96c0-04d93877dd08_2398x2124.png 1272w, https://substackcdn.com/image/fetch/$s_!FMDQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb01636fe-47e9-41fc-96c0-04d93877dd08_2398x2124.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><br>We have 2 executors - one that takes input, and one that aggregates the output, and 3 agents - one that translates text, one that summarizes it, and one that extracts keywords. We are using <a href="https://azure.microsoft.com/en-us/products/ai-foundry">Microsoft Foundry</a> for LLMs and<a href="https://aspire.dev/"> Aspire Dashboard</a> for local telemetry.</p><p>Here&#8217;s the code:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;ceedad02-27d6-411e-bbf1-55419e4c228c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">// Program.cs

using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using OpenTelemetry;
using OpenTelemetry.Logs;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using System.Diagnostics;

/// &lt;summary&gt;
/// Demonstrates a fan-out workflow where a single text document is dispatched
/// in parallel to three specialist agents.
/// &lt;/summary&gt;
public static class Program
{
    private const string SourceName = "Workflows.DocumentFanOut";
    private static readonly ActivitySource s_activitySource = new(SourceName);

    private static async Task Main()
    {
        var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT")
            ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");

        var deploymentName =
            Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME")
            ?? "gpt-4o-mini";

        var resourceBuilder = ResourceBuilder
            .CreateDefault()
            .AddService(
                serviceName: "DocumentFanOut",
                serviceVersion: "1.0.0");

        using var tracerProvider = Sdk.CreateTracerProviderBuilder()
            .SetResourceBuilder(resourceBuilder)
            .AddSource(SourceName)
            .AddOtlpExporter()
            .Build();

        using var loggerFactory = LoggerFactory.Create(builder =&gt;
        {
            builder
                .SetMinimumLevel(LogLevel.Trace)
                .AddConsole()
                .AddOpenTelemetry(logging =&gt;
                {
                    logging.SetResourceBuilder(resourceBuilder);
                    logging.IncludeFormattedMessage = true;
                    logging.IncludeScopes = true;
                    logging.ParseStateValues = true;

                    logging.AddOtlpExporter();
                });
        });

        var logger = loggerFactory.CreateLogger(SourceName);

#pragma warning disable OPENAI001
        var chatClient = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential())
            .GetProjectOpenAIClient()
            .GetProjectResponsesClient()
            .AsIChatClient(deploymentName)
            .AsBuilder()
            .UseOpenTelemetry(
                sourceName: SourceName,
                configure: cfg =&gt; cfg.EnableSensitiveData = true)
            .Build();
#pragma warning restore OPENAI001

        using var activity = s_activitySource.StartActivity("main");

        logger.LogInformation(
            "Application started. TraceId: {TraceId}",
            activity?.TraceId);

        var translator = new ChatClientAgent(
            chatClient,
            name: "Translator",
            instructions: "You are a professional translator. Translate the user's document into Spanish. Output only the translated text, no commentary."
        ).BindAsExecutor(new AIAgentHostOptions { ForwardIncomingMessages = false });

        var summarizer = new ChatClientAgent(
            chatClient,
            name: "Summarizer",
            instructions: "You are a concise summarizer. Produce exactly 3 bullet points that capture the key ideas of the document."
        ).BindAsExecutor(new AIAgentHostOptions { ForwardIncomingMessages = false });

        var keywordExtractor = new ChatClientAgent(
            chatClient,
            name: "KeywordExtractor",
            instructions: "You are a keyword-extraction specialist. Return the top 5 most important keywords from the document as a numbered list."
        ).BindAsExecutor(new AIAgentHostOptions { ForwardIncomingMessages = false });

        var documentInput = new DocumentInputExecutor();
        var aggregator = new ResultsAggregatorExecutor();

        var workflow = new WorkflowBuilder(documentInput)
            .AddFanOutEdge(documentInput, [translator, summarizer, keywordExtractor])
            .AddFanInBarrierEdge([translator, summarizer, keywordExtractor], aggregator)
            .WithOutputFrom(aggregator)
            .WithOpenTelemetry(
                configure: cfg =&gt; cfg.EnableSensitiveData = true,
                activitySource: s_activitySource)
            .Build();

        const string document = """
            Photosynthesis is the process by which green plants, algae, and some bacteria
            convert light energy into chemical energy stored in glucose. Using sunlight,
            water absorbed from the soil, and carbon dioxide from the air, plants produce
            oxygen as a byproduct. This process occurs primarily in the chloroplasts, where
            the pigment chlorophyll captures light. Photosynthesis is fundamental to life
            on Earth, forming the base of nearly all food chains and maintaining atmospheric
            oxygen levels.
            """;

        Console.WriteLine("=== Input document ===");
        Console.WriteLine(document);
        Console.WriteLine(new string('=', 60));

        logger.LogInformation("Starting fan-out workflow");

        await using StreamingRun run =
            await InProcessExecution.RunStreamingAsync(workflow, input: document);

        await foreach (WorkflowEvent evt in run.WatchStreamAsync())
        {
            switch (evt)
            {
                case WorkflowOutputEvent output:
                    logger.LogInformation(
                        "Workflow completed. Results:{NewLine}{Output}",
                        Environment.NewLine,
                        output.Data);
                    break;

                case WorkflowErrorEvent error:
                    logger.LogError(error.Exception, "Workflow error");
                    break;

                case ExecutorFailedEvent failed:
                    logger.LogError(
                        "Executor '{ExecutorId}' failed: {Data}",
                        failed.ExecutorId,
                        failed.Data);
                    break;
            }
        }

        logger.LogInformation("Application finished");
    }
}</code></pre></div><p><a href="https://learn.microsoft.com/en-us/agent-framework/workflows/executors?pivots=programming-language-csharp">Executors </a>are the fundamental building blocks that process messages in a workflow. They are autonomous processing units that receive typed messages, perform operations, and can produce output messages or events.</p><pre><code>// Executors.cs

using System.Text;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.AI;

/// &lt;summary&gt;
/// Receives the raw document string and broadcasts it as a &lt;see cref=&#8221;ChatMessage&#8221;/&gt;
/// followed by a &lt;see cref=&#8221;TurnToken&#8221;/&gt; to all downstream agents simultaneously.
/// &lt;/summary&gt;
[SendsMessage(typeof(ChatMessage))]
[SendsMessage(typeof(TurnToken))]
internal sealed class DocumentInputExecutor() : Executor&lt;string&gt;(&#8221;DocumentInput&#8221;)
{
    public override async ValueTask HandleAsync(
        string document,
        IWorkflowContext context,
        CancellationToken cancellationToken = default)
    {
        // Broadcast the document to all fan-out targets.
        await context.SendMessageAsync(new ChatMessage(ChatRole.User, document), cancellationToken: cancellationToken);
        // TurnToken kicks off each receiving agent&#8217;s inference turn.
        await context.SendMessageAsync(new TurnToken(emitEvents: false), cancellationToken: cancellationToken);
    }
}

/// &lt;summary&gt;
/// Collects the &lt;see cref=&#8221;ChatMessage&#8221;/&gt; lists produced by each agent after the
/// fan-in barrier fires, then emits a single formatted string as the workflow output.
/// &lt;/summary&gt;
[YieldsOutput(typeof(string))]
internal sealed class ResultsAggregatorExecutor() : Executor&lt;List&lt;ChatMessage&gt;&gt;(&#8221;ResultsAggregator&#8221;)
{
    private readonly List&lt;ChatMessage&gt; _buffer = [];

    /// &lt;summary&gt;
    /// Each call receives all messages produced by one agent during its turn.
    /// &lt;/summary&gt;
    public override ValueTask HandleAsync(
        List&lt;ChatMessage&gt; messages,
        IWorkflowContext context,
        CancellationToken cancellationToken = default)
    {
        _buffer.AddRange(messages);
        return default;
    }

    /// &lt;summary&gt;
    /// Called once all agent replies for this super-step have been delivered.
    /// Formats and yields the combined output.
    /// &lt;/summary&gt;
    protected override ValueTask OnMessageDeliveryFinishedAsync(
        IWorkflowContext context,
        CancellationToken cancellationToken = default)
    {
        var sb = new StringBuilder();

        foreach (var msg in _buffer)
        {
            sb.AppendLine($&#8221;--- {msg.AuthorName} ---&#8221;);
            sb.AppendLine(msg.Text);
            sb.AppendLine();
        }

        _buffer.Clear();

        return context.YieldOutputAsync(sb.ToString(), cancellationToken);
    }
}
</code></pre><p>Here&#8217;s the console output from Aspire:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8N-S!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4ee71fd-0043-4876-baac-f33dc6fbedeb_1097x855.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8N-S!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4ee71fd-0043-4876-baac-f33dc6fbedeb_1097x855.png 424w, https://substackcdn.com/image/fetch/$s_!8N-S!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4ee71fd-0043-4876-baac-f33dc6fbedeb_1097x855.png 848w, https://substackcdn.com/image/fetch/$s_!8N-S!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4ee71fd-0043-4876-baac-f33dc6fbedeb_1097x855.png 1272w, https://substackcdn.com/image/fetch/$s_!8N-S!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4ee71fd-0043-4876-baac-f33dc6fbedeb_1097x855.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8N-S!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4ee71fd-0043-4876-baac-f33dc6fbedeb_1097x855.png" width="1097" height="855" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c4ee71fd-0043-4876-baac-f33dc6fbedeb_1097x855.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:855,&quot;width&quot;:1097,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:132104,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/199289309?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4ee71fd-0043-4876-baac-f33dc6fbedeb_1097x855.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8N-S!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4ee71fd-0043-4876-baac-f33dc6fbedeb_1097x855.png 424w, https://substackcdn.com/image/fetch/$s_!8N-S!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4ee71fd-0043-4876-baac-f33dc6fbedeb_1097x855.png 848w, https://substackcdn.com/image/fetch/$s_!8N-S!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4ee71fd-0043-4876-baac-f33dc6fbedeb_1097x855.png 1272w, https://substackcdn.com/image/fetch/$s_!8N-S!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4ee71fd-0043-4876-baac-f33dc6fbedeb_1097x855.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here are the traces:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VCi7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb8e73e-fcd7-45e4-afd2-d853af987e74_1165x1253.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VCi7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb8e73e-fcd7-45e4-afd2-d853af987e74_1165x1253.png 424w, https://substackcdn.com/image/fetch/$s_!VCi7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb8e73e-fcd7-45e4-afd2-d853af987e74_1165x1253.png 848w, https://substackcdn.com/image/fetch/$s_!VCi7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb8e73e-fcd7-45e4-afd2-d853af987e74_1165x1253.png 1272w, https://substackcdn.com/image/fetch/$s_!VCi7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb8e73e-fcd7-45e4-afd2-d853af987e74_1165x1253.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VCi7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb8e73e-fcd7-45e4-afd2-d853af987e74_1165x1253.png" width="1165" height="1253" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7bb8e73e-fcd7-45e4-afd2-d853af987e74_1165x1253.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1253,&quot;width&quot;:1165,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:106202,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/199289309?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb8e73e-fcd7-45e4-afd2-d853af987e74_1165x1253.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!VCi7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb8e73e-fcd7-45e4-afd2-d853af987e74_1165x1253.png 424w, https://substackcdn.com/image/fetch/$s_!VCi7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb8e73e-fcd7-45e4-afd2-d853af987e74_1165x1253.png 848w, https://substackcdn.com/image/fetch/$s_!VCi7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb8e73e-fcd7-45e4-afd2-d853af987e74_1165x1253.png 1272w, https://substackcdn.com/image/fetch/$s_!VCi7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb8e73e-fcd7-45e4-afd2-d853af987e74_1165x1253.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>That&#8217;s it! For more - read the docs and start experimenting!</p>]]></content:encoded></item><item><title><![CDATA[Build Observable MCP Agents with Microsoft Agent Framework & Foundry]]></title><description><![CDATA[Learn how to build basic Agent with MCP and OpenTelemetry on Microsoft Foundry]]></description><link>https://systemshogun.com/p/build-observable-mcp-agents-with</link><guid isPermaLink="false">https://systemshogun.com/p/build-observable-mcp-agents-with</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Fri, 22 May 2026 16:17:26 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!UL8Z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff446f5d8-4b05-42b1-a846-c8c19f2528ba_2296x1249.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today I&#8217;m going to show you how you can create an agent inside Microsoft Foundry using Microsoft Agent Framework, add MCP and use OpenTelemetry.</p><p>Prerequisites:</p><ul><li><p>Azure Resource Group</p></li><li><p>Microsoft Foundry Resource &amp; Project</p></li><li><p>Create model for the inference (I used gpt-4o)</p></li><li><p>Configure environment variables</p></li><li><p>Application Insights Resource</p></li></ul><p></p><p>NuGet Packages:</p><ul><li><p>Azure.AI.Projects.Agents (2.1.0-beta.2)</p></li><li><p>Azure.Identity (latest stable)</p></li><li><p>Microsoft.Agents.AI.Foundry (1.6.2-preview.260521.1)</p></li><li><p>Azure.AI.OpenAI (2.1.0)<br></p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;b22df79e-77af-474c-b020-f7bc91c8fd0b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI.Foundry;
using OpenAI.Responses;

var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT")
    ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
var model = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o";

const string AgentName = "MicrosoftLearnAgent";
const string AgentInstructions = "You answer questions by searching the Microsoft Learn content only through MCP. If you can't find an answer - just say so! Don't ever use your general knowledge!";

#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
var mcpTool = ResponseTool.CreateMcpTool(
    serverLabel: "microsoft_learn",
    serverUri: new Uri("https://learn.microsoft.com/api/mcp"),
    toolCallApprovalPolicy: new McpToolCallApprovalPolicy(GlobalMcpToolCallApprovalPolicy.NeverRequireApproval));
#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.


var aiProjectClient = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential());

var connectionString = aiProjectClient.Telemetry.GetApplicationInsightsConnectionString();
Environment.SetEnvironmentVariable("APPLICATIONINSIGHTS_CONNECTION_STRING", connectionString);

var agentVersion = await aiProjectClient.AgentAdministrationClient.CreateAgentVersionAsync(
    AgentName,
    new ProjectsAgentVersionCreationOptions(
        new DeclarativeAgentDefinition(model)
        {
            Instructions = AgentInstructions,
            Tools = { mcpTool }
        }));

#pragma warning disable OPENAI001
FoundryAgent agent = aiProjectClient.AsAIAgent(agentVersion);
#pragma warning restore OPENAI001

Console.WriteLine(await agent.RunAsync("What are Local MCP Tools"));</code></pre></div><p><br>&#8230; and here&#8217;s the telemetry inside AppInsights. As you can see it is executing the mcp tool.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UL8Z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff446f5d8-4b05-42b1-a846-c8c19f2528ba_2296x1249.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UL8Z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff446f5d8-4b05-42b1-a846-c8c19f2528ba_2296x1249.png 424w, https://substackcdn.com/image/fetch/$s_!UL8Z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff446f5d8-4b05-42b1-a846-c8c19f2528ba_2296x1249.png 848w, https://substackcdn.com/image/fetch/$s_!UL8Z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff446f5d8-4b05-42b1-a846-c8c19f2528ba_2296x1249.png 1272w, https://substackcdn.com/image/fetch/$s_!UL8Z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff446f5d8-4b05-42b1-a846-c8c19f2528ba_2296x1249.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UL8Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff446f5d8-4b05-42b1-a846-c8c19f2528ba_2296x1249.png" width="1456" height="792" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f446f5d8-4b05-42b1-a846-c8c19f2528ba_2296x1249.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:792,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:256032,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/198860928?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff446f5d8-4b05-42b1-a846-c8c19f2528ba_2296x1249.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UL8Z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff446f5d8-4b05-42b1-a846-c8c19f2528ba_2296x1249.png 424w, https://substackcdn.com/image/fetch/$s_!UL8Z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff446f5d8-4b05-42b1-a846-c8c19f2528ba_2296x1249.png 848w, https://substackcdn.com/image/fetch/$s_!UL8Z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff446f5d8-4b05-42b1-a846-c8c19f2528ba_2296x1249.png 1272w, https://substackcdn.com/image/fetch/$s_!UL8Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff446f5d8-4b05-42b1-a846-c8c19f2528ba_2296x1249.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>That&#8217;s it! Short and sweet!</p>]]></content:encoded></item><item><title><![CDATA[Build a Smart Call Center with AI on Azure]]></title><description><![CDATA[Architecture & PoC of a smart call center on Microsoft Azure with LangGraph]]></description><link>https://systemshogun.com/p/build-a-smart-call-center-with-ai</link><guid isPermaLink="false">https://systemshogun.com/p/build-a-smart-call-center-with-ai</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Fri, 17 Apr 2026 12:51:59 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/9NWp9DCUgC8" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Customer calls are one of the most valuable sources of business insight.</p><p>The question is how well you capture and use that information.</p><p>What if every call could be turned into structured data and used by agentic AI systems where agents actively handle tasks, fix issues, and support decision-making?</p><p>There are many ways to do it, but here&#8217;s a simple architecture on Azure:</p><p>&#120284;&#120315;&#120308;&#120306;&#120320;&#120321;&#120310;&#120316;&#120315; &amp; &#120291;&#120319;&#120316;&#120304;&#120306;&#120320;&#120320;&#120310;&#120315;&#120308;</p><p>Audio &#8594; Blob &#8594; Event Grid &#8594; Queue &#8594; Worker</p><p>&#120296;&#120315;&#120305;&#120306;&#120319;&#120320;&#120321;&#120302;&#120315;&#120305;&#120310;&#120315;&#120308;</p><p>- Content Understanding (turns audio into structured data)</p><p>&#120279;&#120302;&#120321;&#120302; &amp; &#120293;&#120306;&#120321;&#120319;&#120310;&#120306;&#120323;&#120302;&#120313;</p><p>- PostgreSQL (chat history, other structured data)</p><p>- Azure AI Search (RAG)</p><p>&#120276;&#120284;</p><p>- Microsoft Foundry + LangGraph (multi-agent orchestration)</p><p>&#120296;&#120284; / &#120276;&#120291;&#120284;</p><p>- Container Apps (exposes the system to users / clients)</p><p>Nothing fancy individually, but together, it turns raw calls into something agents can query, analyze, and act on in real workflows.</p><p>Full walkthrough + POC app:</p><div id="youtube2-9NWp9DCUgC8" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;9NWp9DCUgC8&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/9NWp9DCUgC8?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Build AI Apps with Microsoft Foundry, Microsoft Agent Framework, and PostgreSQL]]></title><description><![CDATA[Learn how to build awesome AI apps with Microsoft Foundry, Microsoft Agent Framework, and PostgreSQL]]></description><link>https://systemshogun.com/p/build-ai-apps-with-microsoft-foundry</link><guid isPermaLink="false">https://systemshogun.com/p/build-ai-apps-with-microsoft-foundry</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Sat, 21 Mar 2026 05:47:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/eldhIGRLxIQ" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div id="youtube2-eldhIGRLxIQ" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;eldhIGRLxIQ&quot;,&quot;startTime&quot;:&quot;1s&quot;,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/eldhIGRLxIQ?start=1s&amp;rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Winter Is Coming… Guard Your Architecture in the AI Era]]></title><description><![CDATA[Practical Steps to Protect Your Architecture Long-Term in the AI Era]]></description><link>https://systemshogun.com/p/winter-is-coming-guard-your-architecture</link><guid isPermaLink="false">https://systemshogun.com/p/winter-is-coming-guard-your-architecture</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Sun, 01 Mar 2026 06:29:59 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/1e8b18fe-3f77-40cd-9be5-f58f0861741e_586x882.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a Solution Architect, my daily job consists of working closely with the business to understand their needs, gathering both functional and non-functional requirements, and designing solutions that translate those requirements into reliable, scalable systems.</p><p>Lately, a big part of my job has been figuring out how to leverage AI&#8217;s benefits without compromising the overall solution architecture and turning the system into unreliable, hard to maintain mess. Many teams are being pushed to deliver faster using AI, often under tighter deadlines. If this isn&#8217;t handled carefully, it can quickly lead to architectural erosion, growing complexity, and systems that you want to stay away from.</p><p>Teams are already feeling the impact. When guardrails are missing, structure erodes and boundaries blur. We&#8217;re even seeing large systems struggle under constant rushed changes with AI. Frequent Windows update issues are a reminder of how fragile complex software (especially written with AI) can become when complexity grows faster than control.</p><p>The challenge isn&#8217;t speed versus quality. It&#8217;s learning how to move fast without weakening the architecture that keeps the system stable.</p><p>In today&#8217;s post, I&#8217;m focusing specifically on greenfield projects. I&#8217;m going to walk through a few practical steps you can follow to ensure AI accelerates your development without slowly destroying your solution and making it unmaintainable.</p><p>I will cover brownfield systems separately, because the strategy there is different and deserves its own discussion.</p><h2>Build Good Architecture from the Start (Greenfield)</h2><p>If you&#8217;re working on a greenfield project, you have a rare advantage as you control the foundation. There&#8217;s no legacy, no accidental complexity, no historical shortcuts. That also means the responsibility is entirely yours.</p><p>Start with clear understanding of functional and non-functional requirements. Design system boundaries intentionally. Define coding standards, layering rules, and architectural principles before the first large batch of AI-generated code enters the repository.</p><p>AI can help you build and move faster but only when you have stable foundations.</p><p>In greenfield projects, the discipline you apply at the beginning determines maintainability years later.</p><h2>We&#8217;ve Got the Architecture&#8230; Now It&#8217;s Time to Protect It</h2><p>Designing a solid architecture is only the first step. Once development begins and AI starts generating code at scale, protection becomes critical.</p><p>Architecture should not live only in your head or in a diagram. It needs to be documented, enforced, and protected with automated checks, review processes, and clear boundaries.</p><p>Now let&#8217;s see some basic steps that will help us protect the solution from architectural erosion.</p><h2>Use GitHub Copilot to Protect the Architecture</h2><p>AI should not only generate code. It should help enforce the architecture you designed. Instead of treating AI as a shortcut to write tons of code, treat it as an extension of your governance model as well.</p><p>If you give AI clear rules, boundaries, and responsibilities, it can become part of your quality control system rather than a source of entropy.</p><p>A recent 2025 study (<a href="https://arxiv.org/pdf/2507.11538">https://arxiv.org/pdf/2507.11538</a>) showed that as the number of instructions increases, AI models start missing requirements and skipping constraints. In other words, the more rules you throw at them, the more likely they are to ignore some of them. <strong>You should always keep this in mind.</strong></p><p>This has direct implications for how we document architecture and even how we use the tools that I am about to show you in a second.</p><p>If your repository contains hundreds of loosely structured rules, long narrative explanations, and scattered constraints, the model will not reliably follow all of them. Overloading it with instructions reduces compliance.</p><p>That is why architectural guidance for AI must be:</p><ul><li><p>Short</p></li><li><p>Explicit</p></li><li><p>Structured</p></li><li><p>Prioritized</p></li></ul><p>Concise, well-organized rules outperform long, dense documents. If we want AI to protect the architecture, we must write instructions that it can realistically follow.</p><p>Now let&#8217;s look at some easy-to-follow steps that will help us protect the architectural integrity of our solution.</p><h2>Custom Instructions</h2><p>Start with a <code>copilot-instructions.md</code> file. This document defines your project&#8217;s coding standards and architectural rules in a concise format.</p><p>Include things like:</p><ul><li><p>Folder structure expectations</p></li><li><p>Layering rules</p></li><li><p>Dependency constraints</p></li><li><p>Naming conventions</p></li><li><p>Required patterns</p></li></ul><p>When Copilot reads this before generating code, it aligns with your architecture instead of inventing its own structure.</p><p><strong>Keep it short. If it&#8217;s too long, it won&#8217;t be followed consistently.</strong></p><p>Here&#8217;s an example of such a document.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;13fc6e62-0caf-4e34-ad2d-81a155248a1e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext"># Project: Protect Arch Demo

This project follows **Clean Architecture** principles. All contributions &#8212; human or AI &#8212; must respect the layered dependency rules.

## Architecture Layers (innermost &#8594; outermost)

1. **Domain** (`src/Domain`) &#8212; Entities and repository interfaces. Zero dependencies on other project layers.
2. **Application** (`src/Application`) &#8212; Use cases / business logic. Depends only on Domain.
3. **Infrastructure** (`src/Infrastructure`) &#8212; Data access, external services. Depends on Domain and Application.
4. **Api** (`src/Api`) &#8212; HTTP layer, DI composition root. Depends on Application and Infrastructure.

## Dependency Rules &#8212; NEVER violate these

| Layer          | May reference               | Must NEVER reference             |
| -------------- | --------------------------- | -------------------------------- |
| Domain         | (nothing)                   | Application, Infrastructure, Api |
| Application    | Domain                      | Infrastructure, Api              |
| Infrastructure | Domain, Application         | Api                              |
| Api            | Application, Infrastructure | &#8212;                                |

## Coding Guidelines

- Use **C# 12** with file-scoped namespaces.
- Keep Domain entities as plain POCOs &#8212; no framework dependencies.
- Repository **interfaces** live in `Domain/Interfaces`; **implementations** live in `Infrastructure/Repositories`.
- Use cases live in `Application/UseCases` and accept interfaces via constructor injection.
- The Api project is the only place that configures DI and middleware &#8212; never register services elsewhere.
- All new code must pass the architecture tests in `tests/ArchitectureTests` (run with `dotnet test`).
</code></pre></div><h2>Agent Skills</h2><p>Agent Skills let you package reusable capabilities, like instructions, scripts, templates, and supporting resources that Copilot can load when needed. Only the skill&#8217;s <strong>name and description</strong> are available by default. The full instructions and resources are loaded <strong>on demand</strong>, when the task matches the skill&#8217;s purpose.</p><p>This is critical for two reasons:</p><ol><li><p>It keeps context clean and lightweight.</p></li><li><p>It prevents instruction overload, which reduces model reliability.</p></li></ol><p>A skill can encapsulate workflows such as:</p><ul><li><p>Architecture validation</p></li><li><p>Test generation</p></li><li><p>Refactoring checks</p></li><li><p>Security review</p></li><li><p>Deployment validation</p></li></ul><p>Unlike custom instructions, which are always applied and define your structural rules, Agent Skills are task-specific. They activate only when relevant.</p><p>Here&#8217;s an example of a skill for clean architecture review:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;4efb8ca0-4edf-43b5-99a2-b3cefcbc1123&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">---
name: clean-architecture-review
description: 'Reviews .NET code for Clean Architecture dependency violations. Use when reviewing code changes, pull requests, or validating that new code respects layer boundaries in a Clean Architecture project.'
---

# Clean Architecture Review

You are a specialist in Clean Architecture for .NET projects. When this skill is activated, perform a thorough review of the code for dependency rule violations.

## Architecture Layers

The project uses four layers (innermost &#8594; outermost):

1. **Domain** (`src/Domain`) &#8212; Entities, value objects, repository interfaces. **Zero** dependencies on other layers.
2. **Application** (`src/Application`) &#8212; Use cases and business logic. Depends only on Domain.
3. **Infrastructure** (`src/Infrastructure`) &#8212; Data access, external services. Depends on Domain and Application.
4. **Api** (`src/Api`) &#8212; HTTP endpoints, DI composition root. Depends on Application and Infrastructure.

## Review Checklist

For each source file, check the following:

### 1. Layer Identification

Determine which layer a file belongs to based on its path prefix:

- `src/Domain/` &#8594; Domain
- `src/Application/` &#8594; Application
- `src/Infrastructure/` &#8594; Infrastructure
- `src/Api/` &#8594; Api

### 2. Forbidden Dependencies

Check all `using` directives and type references:

| Layer          | Forbidden Dependencies           |
| -------------- | -------------------------------- |
| Domain         | Application, Infrastructure, Api |
| Application    | Infrastructure, Api              |
| Infrastructure | Api                              |
| Api            | (no restrictions)                |

### 3. Structural Rules

- Interfaces in `Domain/Interfaces/` must **only** be interfaces
- Repository implementations must live in `Infrastructure/Repositories/`
- Use cases must live in `Application/UseCases/`
- DI registration must only happen in `src/Api/Program.cs`

## How to Run Verification

Use the [architecture test script](./run-arch-tests.ps1) to execute automated verification:

```powershell
.\run-arch-tests.ps1
```

Or run directly:

```
dotnet test tests/ArchitectureTests --verbosity normal
```

## Output Format

Report findings as:

```
&#9989; PASS &#8212; No architecture violations found
```

or

```
&#10060; VIOLATION in [file path]
   Layer: [layer name]
   References: [forbidden namespace]
   Rule: [which rule is broken]
   Fix: [how to fix it]
```</code></pre></div><h2>Custom Agents</h2><p>Custom agents let you define AI roles with clear responsibilities.</p><p>Instead of one generic assistant, you can create:</p><ul><li><p>A Developer Agent</p></li><li><p>An Architecture Review Agent</p></li><li><p>A Testing Agent</p></li><li><p>A Documentation Agent</p></li></ul><p>You can generate code with one agent and hand it off to another for validation.</p><p>That handoff mirrors real engineering teams. Responsibilities are separated. Validation is intentional. AI becomes structured, not improvisational.<br><br>Here&#8217;s an example of an <strong>Architecture Guardian </strong>agent:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;15ae9641-8a2e-4f49-8156-b36bb7f199ee&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">---
name: arch-guardian
description: 'Architecture Guardian &#8212; reviews code for Clean Architecture violations'
tools:
  - search
  - read/readFile
---

# Architecture Guardian

You are the **Architecture Guardian** for this project. Your sole purpose is to protect the Clean Architecture boundaries.

## Your Personality

You are a vigilant castle guard. The architecture is your castle, and each layer is a wall. You never let a dependency sneak through the wrong gate.

## What You Do

When asked to review code or validate the architecture:

1. **Identify the layer** each file belongs to based on its path:
   - `src/Domain/` &#8594; Domain layer
   - `src/Application/` &#8594; Application layer
   - `src/Infrastructure/` &#8594; Infrastructure layer
   - `src/Api/` &#8594; Api layer

2. **Check `using` statements and references** for forbidden dependencies:
   - Domain &#8594; must NOT reference Application, Infrastructure, or Api
   - Application &#8594; must NOT reference Infrastructure or Api
   - Infrastructure &#8594; must NOT reference Api

3. **Check structural rules**:
   - Interfaces in `Domain/Interfaces/` must be interfaces (not classes)
   - Repository implementations must live in `Infrastructure/Repositories/`
   - Use cases must live in `Application/UseCases/`
   - DI registration must only happen in `src/Api/`

4. **Run the architecture tests** to confirm:

   ```
   dotnet test tests/ArchitectureTests --verbosity normal
   ```

5. **Report findings** clearly &#8212; list each violation with the file, line, and which rule was broken.

## Important

- NEVER suggest code that violates these rules.
- If asked to write code, always place it in the correct layer.
- If you find violations, provide corrected code that moves the logic to the proper layer.
</code></pre></div><h2>Hooks (Preview)</h2><p>Hooks allow you to execute custom logic automatically when specific events occur during AI-assisted workflows. This feature is currently in preview, but it introduces an important architectural capability.</p><p>Instead of relying only on instructions, you can attach enforcement mechanisms to lifecycle events inside the development flow.</p><p>Hooks can trigger scripts or commands at defined moments &#8212; for example before or after AI generates code.</p><p>You can use hooks to:</p><ul><li><p>Validate that required architectural patterns are present after generation</p></li><li><p>Run boundary-check scripts when a new file is created</p></li><li><p>Enforce dependency direction rules automatically</p></li><li><p>Trigger linting or architectural validation commands</p></li><li><p>Block unsafe patterns before changes are finalized</p></li></ul><p>Hooks turn architecture from documentation into execution.</p><p>Instead of trusting that AI follows your rules, you verify compliance programmatically.</p><p>Even though the feature is still in preview, it represents a major step toward embedding architectural governance directly into AI-assisted development workflows.<br></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tYKY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tYKY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png 424w, https://substackcdn.com/image/fetch/$s_!tYKY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png 848w, https://substackcdn.com/image/fetch/$s_!tYKY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png 1272w, https://substackcdn.com/image/fetch/$s_!tYKY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tYKY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png" width="537" height="232" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:232,&quot;width&quot;:537,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:16263,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/189433015?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tYKY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png 424w, https://substackcdn.com/image/fetch/$s_!tYKY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png 848w, https://substackcdn.com/image/fetch/$s_!tYKY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png 1272w, https://substackcdn.com/image/fetch/$s_!tYKY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;18499ab5-adfa-47d0-a996-6cf218dd5e8d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">{
  "hooks": {
    "PostToolUse": [
      {
        "type": "command",
        "command": "bash .github/hooks/scripts/arch-test.sh",
        "windows": "powershell -ExecutionPolicy Bypass -File .github/hooks/scripts/arch-test.ps1",
        "timeout": 60
      }
    ]
  }
}
</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;5229e91c-541f-41c7-933d-d4165c0f8a43&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">
#!/usr/bin/env pwsh
# PostToolUse hook &#8212; runs architecture tests after Copilot edits files
# Only triggers when the tool is "editFiles" (i.e. Copilot wrote code)

$input_json = [Console]::In.ReadToEnd() | ConvertFrom-Json

$tool = $input_json.tool_name

# Only run after file-editing tools
if ($tool -notin @("editFiles", "create_file", "replace_string_in_file", "write_to_file", "insert_edit")) {
    # Not a file edit &#8212; skip silently
    @{ continue = $true } | ConvertTo-Json
    exit 0
}

# Run architecture tests
$testResult = dotnet test tests/ArchitectureTests --no-restore --verbosity quiet 2&gt;&amp;1

if ($LASTEXITCODE -ne 0) {
    $output = @{
        continue           = $true
        hookSpecificOutput = @{
            hookEventName     = "PostToolUse"
            additionalContext = "ARCHITECTURE VIOLATION DETECTED! The architecture tests failed after your edit. Please review and fix the dependency rule violation. Test output: $($testResult -join "`n")"
        }
    }
    $output | ConvertTo-Json -Depth 3
    exit 0
}

@{ continue = $true } | ConvertTo-Json
exit 0</code></pre></div><h2>GitHub Copilot Code Review Agent</h2><p>Generating code is only half of the equation. The real protection happens during review.</p><p>GitHub Copilot&#8217;s code review agent brings AI into the pull request workflow. Instead of using AI only at the moment of creation, you can use it as an automated reviewer that analyzes changes before they reach your main branch.</p><p>From an architectural perspective, this is critical.</p><p>The review agent can:</p><ul><li><p>Detect violations of architectural boundaries</p></li><li><p>Flag missing required patterns</p></li><li><p>Identify risky dependency changes</p></li><li><p>Suggest improvements aligned with project conventions</p></li><li><p>Surface potential security or maintainability issues</p></li></ul><p>This transforms AI from a code accelerator into a quality gate participant.</p><p>When combined with branch protection rules and required status checks, the Copilot review agent becomes part of your governance layer. Every pull request passes through automated architectural scrutiny before human approval.</p><p>This mirrors how mature engineering organizations operate:</p><ul><li><p>Developer writes code</p></li><li><p>Automated systems validate it</p></li><li><p>Reviewers enforce standards</p></li><li><p>Only then does it reach production</p></li></ul><p>In a greenfield project, integrating AI into the review stage is one of the most effective ways to prevent architectural erosion while still benefiting from rapid generation.</p><h2>AI-Governed Systems in 2026 and Onward</h2><p>This is what a modern solution should start to look like.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ib1y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ib1y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png 424w, https://substackcdn.com/image/fetch/$s_!ib1y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png 848w, https://substackcdn.com/image/fetch/$s_!ib1y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png 1272w, https://substackcdn.com/image/fetch/$s_!ib1y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ib1y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png" width="585" height="890" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:890,&quot;width&quot;:585,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:79873,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/189433015?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ib1y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png 424w, https://substackcdn.com/image/fetch/$s_!ib1y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png 848w, https://substackcdn.com/image/fetch/$s_!ib1y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png 1272w, https://substackcdn.com/image/fetch/$s_!ib1y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Not just <code>src</code> and <code>tests</code>, but explicit architectural guardrails built into the repository itself. Dedicated folders for agents, skills, hooks, prompts, and enforcement scripts. Architecture is not something we design at the beginning and then forget &#8212; it needs mechanisms that continuously enforce boundaries as the system evolves. And what better way to achieve that than a well-structured AI governance process with a human in the loop?</p><h2>Final Thoughts</h2><p>None of the steps discussed here are complex or exotic. They are practical, easy to follow, and accessible to any team starting a greenfield project.</p><ul><li><p>Define your architecture clearly in the beginning.</p></li><li><p>Document it concisely.</p></li><li><p>Use custom instructions to encode your standards.</p></li><li><p>Leverage Agent Skills and Custom Agents to structure AI behavior.</p></li><li><p>Add hooks to automatically validate architectural rules when different AI agent events occur.</p></li><li><p>Enforce everything through pull requests, automated tests, and quality gates.</p></li></ul><p>If a team wants to move fast with AI while still building a system that remains understandable, maintainable, and scalable years from now, these steps are a great starting point.</p>]]></content:encoded></item><item><title><![CDATA[What Happens When You Open ChatGPT in Your Browser?]]></title><description><![CDATA[From the initial request in your browser to the response]]></description><link>https://systemshogun.com/p/what-happens-when-you-open-chatgpt</link><guid isPermaLink="false">https://systemshogun.com/p/what-happens-when-you-open-chatgpt</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Sat, 07 Feb 2026 09:00:45 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!U-a6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When you type <strong>chat.com</strong> in your browser and press <strong>Enter</strong>, a lot happens in milliseconds. In today&#8217;s post I am going to try and explain it with a system diagram that I&#8217;ve built for this purpose.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!U-a6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!U-a6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png 424w, https://substackcdn.com/image/fetch/$s_!U-a6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png 848w, https://substackcdn.com/image/fetch/$s_!U-a6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png 1272w, https://substackcdn.com/image/fetch/$s_!U-a6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!U-a6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png" width="1456" height="858" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:858,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:254367,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/186829366?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!U-a6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png 424w, https://substackcdn.com/image/fetch/$s_!U-a6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png 848w, https://substackcdn.com/image/fetch/$s_!U-a6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png 1272w, https://substackcdn.com/image/fetch/$s_!U-a6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>DNS Lookup</h3><p>Domain names exist so humans can remember websites more easily. It&#8217;s much easier to remember <strong>chat.com</strong> than <strong>104.18.41.60</strong>, right? But since every computer on the internet communicates using <strong>IP addresses</strong>, we need a way to translate a domain name into an IP address. That&#8217;s where <strong>DNS</strong> comes in.</p><p>Your browser first asks a simple question:<br><strong>&#8220;What&#8217;s the IP address of chat.com?&#8221;</strong></p><p>It checks caches (browser, OS, router, ISP).<br><br>If there&#8217;s no cached answer, it queries <a href="https://www.cloudflare.com/learning/dns/what-is-dns/">DNS </a>by traversing the DNS hierarchy: first the root servers, then the top-level domain servers (.com), and finally the authoritative name server, which contains the IP address for chat.com.</p><p>This IP is <strong><a href="https://en.wikipedia.org/wiki/Anycast">Anycast</a></strong>, meaning the same IP is advertised from many locations globally.</p><h4>What is Anycast?</h4><p>Anycast is a network addressing and routing method in which incoming requests can be routed to multiple different locations, or &#8220;nodes.&#8221; With Anycast, the same IP address is advertised from servers in multiple geographic locations. This approach is commonly used by CDNs, such as <strong>Cloudflare</strong>, and provides benefits related to security, reliability, and performance.</p><p>For example, a hacker performing a <a href="https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/">DDoS </a>attack would require significantly more resources to have a meaningful impact, because the traffic is distributed across many servers and locations rather than concentrated on a single endpoint (<a href="https://en.wikipedia.org/wiki/Unicast">Unicast</a>).</p><p>And of course, another benefit of CDNs in general is also enabled by Anycast. You are routed as close as possible to resources such as images, videos, and text. Basically, Anycast routes your traffic (via BGP, as explained later) to the nearest server that can handle your request. So, if you open <strong>chat.com</strong>, you will hit a server that is closest to you and therefore experience lower latency and overall good performance. For example, if you are in France, your request will not be routed to the US, but rather to a nearby location, such as Paris.</p><p>So, at the end of the DNS lookup process, we get an IP address such as <strong>104.18.41.60</strong>, which belongs to <strong>Cloudflare</strong>, and not directly to OpenAI (ChatGPT).</p><h3>Border Gateway Protocol (BGP)</h3><p>BGP is the routing protocol of the internet. A hero almost no one talks about, yet it is the protocol that keeps the internet reliable by selecting paths between networks based on routing policies. To better understand how this works, let&#8217;s briefly talk about <a href="https://en.wikipedia.org/wiki/Autonomous_system_(Internet)">autonomous systems</a>.</p><h4>Autonomous systems</h4><p>The internet is a network of hundreds of thousands of smaller networks called <strong>autonomous systems (ASes)</strong>, each operated by a single organization (e.g., Cloudflare) and consisting of its own set of routers.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wFD5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wFD5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png 424w, https://substackcdn.com/image/fetch/$s_!wFD5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png 848w, https://substackcdn.com/image/fetch/$s_!wFD5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png 1272w, https://substackcdn.com/image/fetch/$s_!wFD5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wFD5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png" width="619" height="426" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d064e517-6bd1-4b81-902c-6fa348b48282_619x426.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:426,&quot;width&quot;:619,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:52747,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/186829366?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wFD5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png 424w, https://substackcdn.com/image/fetch/$s_!wFD5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png 848w, https://substackcdn.com/image/fetch/$s_!wFD5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png 1272w, https://substackcdn.com/image/fetch/$s_!wFD5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Basically, what BGP does is select a route from source to destination based on routing policies between autonomous systems, rather than simply choosing the path with the fewest hops. In practice, these policies can include factors such as business relationships, costs, and traffic agreements between ASes. But at a high level, the idea is simple: BGP determines how traffic moves between networks on the internet.</p><h3>TCP</h3><p>Once we have the source IP, destination IP, and port (443 in our case, because that&#8217;s what TLS uses), we are ready to start a TCP connection. This is handled underneath by the operating system (OS). The OS creates a TCP connection by adding the required headers&#8212;such as source IP, destination IP, and ports&#8212;and sending the packets over the network.</p><p>The process is a three-step procedure (commonly called the <strong><a href="https://www.geeksforgeeks.org/computer-networks/tcp-3-way-handshake-process/">three-way handshake</a></strong>), where the client and the server acknowledge each other and establish an ongoing connection between them.</p><h3>TLS</h3><p>Since we don&#8217;t want our chat messages to be visible to third parties, we establish an <strong>encrypted channel</strong> over TCP. This is done using <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS</a>, which relies on symmetric encryption algorithms such as <a href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard">AES </a>to encrypt all data exchanged between the client and the server, including the chat messages. This works by negotiating a <a href="https://www.cloudflare.com/learning/ssl/what-is-a-session-key/">symmetric session key</a> that is then used by both parties for encryption and decryption.</p><h3>HTTP</h3><p>Now that we have a secure (TLS) and ongoing (TCP) session, we are ready to start communicating with ChatGPT. To do that, we rely on the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Overview">HTTP protocol</a>. Since we want to load the ChatGPT page in our browser, we first send an HTTP GET request to <strong>chat.com</strong>.</p><p>All of this communication is encrypted using the negotiated symmetric key, and the request is sent not directly to ChatGPT&#8217;s infrastructure, but to <strong>Cloudflare</strong>, because Cloudflare sits in front of ChatGPT to protect its infrastructure, provide CDN capabilities, and handle traffic at the edge for the duration of the session. Remember, you never communicate directly with ChatGPT&#8217;s web servers. </p><p>Once the request is received and validated, it is forwarded to one of ChatGPT&#8217;s many web servers, which processes it. The web server then returns the website resources&#8212;such as HTML, CSS, JavaScript, and images&#8212;to Cloudflare&#8217;s servers, which in turn send the response back to your browser.</p><h3>Browser</h3><p>When the browser receives the response from Cloudflare, it parses the HTML, CSS, and JavaScript files and builds the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model">DOM</a>. The final step is rendering the page, where the browser renders the visual elements&#8212;that&#8217;s how you see ChatGPT in your browser.</p><h3>Thank you</h3><p>Now you know what happens when you open ChatGPT in your browser and all the steps that take place in milliseconds, almost like magic. If you found this post useful, please subscribe and share it with others.</p>]]></content:encoded></item><item><title><![CDATA[A Simple Architecture That Works for 90% of Applications]]></title><description><![CDATA[You&#8217;ll also learn how requests travel across the internet using the BGP protocol and autonomous systems that are rarely talked about.]]></description><link>https://systemshogun.com/p/a-simple-architecture-that-works</link><guid isPermaLink="false">https://systemshogun.com/p/a-simple-architecture-that-works</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Sat, 31 Jan 2026 07:36:18 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/J-GgXhU5iXE" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today we&#8217;ll go over the system architecture that&#8217;s used in ~90% of the applications today. If you want a slightly deeper dive, please consider watching this short video that I made.</p><div id="youtube2-J-GgXhU5iXE" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;J-GgXhU5iXE&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/J-GgXhU5iXE?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h3>THE BASICS&#8230;</h3><p>Let&#8217;s start with the basics. A typical architecture usually has a <strong>Client</strong> for the user interface (UI), a <strong>Server </strong>for the business logic, and a <strong>Database</strong> to store state.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0zu1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0zu1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png 424w, https://substackcdn.com/image/fetch/$s_!0zu1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png 848w, https://substackcdn.com/image/fetch/$s_!0zu1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png 1272w, https://substackcdn.com/image/fetch/$s_!0zu1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0zu1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png" width="1456" height="565" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:565,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:155767,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/186052838?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0zu1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png 424w, https://substackcdn.com/image/fetch/$s_!0zu1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png 848w, https://substackcdn.com/image/fetch/$s_!0zu1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png 1272w, https://substackcdn.com/image/fetch/$s_!0zu1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is an architecture we still see today, and it was predominant in the early days of the internet. However, it has a <strong>major flaw</strong> &#8212; it lacks mechanisms that improve <strong>security</strong> and <strong>availability</strong>.</p><p>Since there&#8217;s nothing in front of your web server to protect it, your web server is publicly visible and an easy target for all kinds of attacks, most notably<a href="https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/"> distributed denial-of-service (DDoS)</a> attacks.</p><p>Another major issue is the lack of availability. What happens, for example, if your single web server goes down? Your clients won&#8217;t be happy, and your business might be at risk.</p><p>Of course, whether the lack of availability mechanisms is an issue or not depends entirely on the type of application you have. If this is just a simple web app containing your CV, for example, then this is okay. If this is banking software or a medical system, it is not.</p><p>Security, on the other hand, should never be a compromise when your system is exposed to the internet. Yes, a hacked banking system is far more damaging than someone hacking your CV website, but even a personal attack can affect you, your finances, and your reputation.</p><p>Let&#8217;s now fix these issues.</p><h3>MEET THE REVERSE PROXY</h3><p>There&#8217;s one service that helps mitigate the flaws of the architecture described above. Some call it a <strong>reverse proxy</strong>, others a <strong>load balancer</strong> or <strong>gateway</strong>. And while they are not always the same thing, they usually solve overlapping problems.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!530O!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!530O!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png 424w, https://substackcdn.com/image/fetch/$s_!530O!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png 848w, https://substackcdn.com/image/fetch/$s_!530O!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png 1272w, https://substackcdn.com/image/fetch/$s_!530O!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!530O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png" width="1204" height="844" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:844,&quot;width&quot;:1204,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:139987,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/186052838?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!530O!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png 424w, https://substackcdn.com/image/fetch/$s_!530O!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png 848w, https://substackcdn.com/image/fetch/$s_!530O!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png 1272w, https://substackcdn.com/image/fetch/$s_!530O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>That&#8217;s it. One service that will improve your security tremendously and help you reach higher availability. But wait a minute, we now have <strong>two </strong>web servers, instead of one. Yes, that&#8217;s because we do want to utilize the <strong>load balancing </strong>feature of our reverse proxy. </p><p>Now, unlike in the first example above, <strong>if and when</strong> your server goes down, for example <strong>Server A</strong>, all the traffic will be automatically redirected to <strong>Server B</strong>. And just like that your business will no longer suffer and thus you&#8217;ve improved your overall availability.</p><p>And now you have protection from attacks, load balancing, caching, and TLS/SSL termination&#8212;features that usually come built in with these services in the cloud.</p><h3>Internet Routing</h3><p>And finally, I&#8217;m going to spend just a short amount of time on internet routing and try to answer the question of how a request reaches your load balancer in the first place.</p><p>Many people think it&#8217;s all thanks to the<a href="https://www.cloudflare.com/learning/dns/what-is-dns/"> Domain Name System (DNS)</a>, and while the DNS is extremely important part, there&#8217;s another protocol, without which the internet simply won&#8217;t work and that&#8217;s the <a href="https://aws.amazon.com/what-is/border-gateway-protocol/">Border Gateway Protocol</a> which basically finds the best route for your request based on latency, cost, geographic location, and others.</p><p>What we call &#8220;the internet&#8221; is a network of many large networks connected together, called autonomous systems. An <a href="https://www.cloudflare.com/learning/network-layer/what-is-an-autonomous-system/">autonomous system (AS)</a> is a large network&#8212;or a group of networks&#8212;operated under a single routing policy, and every device connected to the internet ultimately connects through one of these systems.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yO6X!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yO6X!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png 424w, https://substackcdn.com/image/fetch/$s_!yO6X!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png 848w, https://substackcdn.com/image/fetch/$s_!yO6X!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png 1272w, https://substackcdn.com/image/fetch/$s_!yO6X!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yO6X!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png" width="1456" height="620" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:620,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:276441,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/186052838?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!yO6X!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png 424w, https://substackcdn.com/image/fetch/$s_!yO6X!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png 848w, https://substackcdn.com/image/fetch/$s_!yO6X!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png 1272w, https://substackcdn.com/image/fetch/$s_!yO6X!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Each autonomous system uses BGP to announce which IP address ranges it is responsible for and which other autonomous systems it connects to. BGP routers collect this information from ASes around the world and build routing tables that determine how traffic should flow from one AS to another. When a packet arrives, a router consults its routing table to decide which AS the packet should be forwarded to next.</p><p>Because there are so many autonomous systems globally, BGP routers constantly update their routing tables. As networks go offline, new networks come online, or ASes change the IP ranges they advertise, these changes are propagated via BGP so routers can continuously adjust their routing decisions.</p><p>I simplified the diagram above, but if you&#8217;ve read this far, I hope you now have a better idea of what happens when you visit a website&#8212;or your own app&#8212;and how and why it all happens so fast. I&#8217;m definitely going to make a video and a blog post on BGP and how it works, and I&#8217;ll write about it here as well, so I&#8217;ll keep you posted.</p><p>Thank you for reading this.</p>]]></content:encoded></item><item><title><![CDATA[My Experiment Building Distributed Online Store in Aspire and .NET]]></title><description><![CDATA[How I built a distributed e-commerce application using Aspire and .NET]]></description><link>https://systemshogun.com/p/my-experiment-building-distributed</link><guid isPermaLink="false">https://systemshogun.com/p/my-experiment-building-distributed</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Wed, 21 Jan 2026 06:41:43 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!a_w7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Lately, I&#8217;ve been experimenting around with <a href="https://aspire.dev/">Aspire </a>by building a simple e-commerce distributed application that consists of a few microservices: Payments, Orders, Notifications, Invoices, Catalog, Shipping, Cart, and a single Web UI. </p><p>Each one of the microservice has its own SQL database (except for Cart, which uses Redis), all of which containerized. The idea of my experiment was to see how easy it really makes the life of a developer that&#8217;s been assigned to work on a distributed system with lots of moving parts.</p><p>Here&#8217;s what the solution structure looks like.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!a_w7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!a_w7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png 424w, https://substackcdn.com/image/fetch/$s_!a_w7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png 848w, https://substackcdn.com/image/fetch/$s_!a_w7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png 1272w, https://substackcdn.com/image/fetch/$s_!a_w7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!a_w7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png" width="673" height="627" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:627,&quot;width&quot;:673,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55584,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/184958773?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!a_w7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png 424w, https://substackcdn.com/image/fetch/$s_!a_w7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png 848w, https://substackcdn.com/image/fetch/$s_!a_w7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png 1272w, https://substackcdn.com/image/fetch/$s_!a_w7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>And here&#8217;s the architecture diagram that represents the action flow.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qui8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qui8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png 424w, https://substackcdn.com/image/fetch/$s_!qui8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png 848w, https://substackcdn.com/image/fetch/$s_!qui8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png 1272w, https://substackcdn.com/image/fetch/$s_!qui8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qui8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png" width="1456" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:367705,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/184958773?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qui8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png 424w, https://substackcdn.com/image/fetch/$s_!qui8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png 848w, https://substackcdn.com/image/fetch/$s_!qui8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png 1272w, https://substackcdn.com/image/fetch/$s_!qui8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The flow starts with the user paying for the items they have in their shopping cart (Redis). Once the payment goes through, a call to Order microservice creates a new order with all the required information. Once the new order is created in the database the service pushes a new <strong>OrderCreated</strong> event to notify the whole system. Now, this is just a quick and dirty. In a real scenario we should consider something like <a href="https://microservices.io/patterns/data/saga.html">The Saga Pattern</a> to ensure that we don&#8217;t leave our system in a corrupt state (e.g., the insert in the order database failed, but we pushed an event for the new order)</p><p>To implement the event-based communication flow I used a library called <a href="https://particular.net/nservicebus">NServiceBus</a>. It is a paid solution, but available for free as long as your solution is not reaching a production environment (e.g., free for Dev, Test, QA, UAT).</p><p>Now, what exactly is <strong>Aspire</strong>?</p><p>Think of Aspire as a production-ready platform (from Microsoft) that allows you to build, test, deploy, debug, and run distributed applications. It provides built-in tooling for local development, containerization, orchestration, and observability, making it much easier to work with distributed systems.</p><p>Aspire really makes things so much easier. For example, see how easily I&#8217;ve added <a href="https://www.rabbitmq.com/">RabbitMQ</a> as a messaging broker to my solution.</p><pre><code>// RabbitMQ
var messaging = builder
    .AddRabbitMQ("messaging")
    .WithManagementPlugin(15672)
    .WithLifetime(ContainerLifetime.Persistent);</code></pre><p>Here&#8217;s my SQL database.</p><pre><code>// SQL Server
var sql = builder
    .AddSqlServer("sql")
    .WithLifetime(ContainerLifetime.Persistent);</code></pre><p>And that&#8217;s my Redis cache.</p><pre><code>// Redis
var cartcache = builder.AddRedis("cartcache")
    .WithLifetime(ContainerLifetime.Persistent);</code></pre><p>And this is how I&#8217;m creating the Orders microservice, its own SQL database, and then injecting the RabbitMQ messaging that we declared above to be used in it for event-based communication.</p><pre><code>// Catalog
var catalogDb = sql.AddDatabase("CatalogDb", "eCommerceDemo.Catalog");
var catalog = builder.AddProject&lt;Projects.eCommerceDemo_Catalog&gt;("catalog")
    .WithReference(catalogDb)
    .WaitFor(catalogDb)
    .WithReference(messaging)
    .WaitFor(messaging);</code></pre><p>Tell me that isn&#8217;t easy!</p><p>If you&#8217;re curious about how this looks in action you should check my latest video where I give a detailed walkthrough. You&#8217;ll see how a single order creation triggers other services and we&#8217;ll even send an email.</p><div id="youtube2-1EAZrxnOXiA" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;1EAZrxnOXiA&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/1EAZrxnOXiA?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[You Probably Don't Need SignalR in .NET 10 (use Server-Sent Events)]]></title><description><![CDATA[Learn how to implement Server-Sent Events in .NET 10 (code demo)]]></description><link>https://systemshogun.com/p/you-probably-dont-need-signalr-in</link><guid isPermaLink="false">https://systemshogun.com/p/you-probably-dont-need-signalr-in</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Wed, 24 Dec 2025 15:47:48 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ZZBx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>Do you need SignalR?</h3><p>If your app only needs <strong>server-to-browser</strong> notifications, then you really don&#8217;t need the heavy <a href="https://dotnet.microsoft.com/en-us/apps/aspnet/signalr">SignalR</a> and all the packages, rules and protocols it comes with. You need <a href="https://en.wikipedia.org/wiki/Server-sent_events">Server-Sent events</a>, and in .NET 10 they are extremely easy to implement. </p><p>In today&#8217;s blog post I&#8217;m going to show you how you can get started.</p><p>Today&#8217;s demo shows how to build real-time UI updates in .NET 10 <strong>without SignalR</strong>, using plain Server-Sent Events. An Azure Function simulates a weather sensor writing data to the database, while a Minimal API streams the updates live to a Blazor WASM frontend.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZZBx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZZBx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png 424w, https://substackcdn.com/image/fetch/$s_!ZZBx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png 848w, https://substackcdn.com/image/fetch/$s_!ZZBx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png 1272w, https://substackcdn.com/image/fetch/$s_!ZZBx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZZBx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png" width="1456" height="357" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:357,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:93322,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182508428?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZZBx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png 424w, https://substackcdn.com/image/fetch/$s_!ZZBx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png 848w, https://substackcdn.com/image/fetch/$s_!ZZBx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png 1272w, https://substackcdn.com/image/fetch/$s_!ZZBx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>The solution consists of three projects:</p><ul><li><p>Blazor WASM (for UI)</p></li><li><p>.NET 10 Web API (for SSE)</p></li><li><p>.NET 10 Function App (for updating SQL database with fictious weather data)</p></li><li><p>SQL Database (for storing sensor / weather data)</p></li></ul><p>Required packages (for database manipulation):</p><ul><li><p>Microsoft.EntityFrameworkCore</p></li><li><p>Microsoft.EntityFrameworkCore.SqlServer</p></li></ul><p>I&#8217;m only going to share core code snippets here. See the full solution on GitHub <a href="https://github.com/kaldren/ServerSentEventsDemo">here</a>. </p><h3>Database</h3><p>Our database has only one table called <code>WeatherForecasts</code> with basic structure (<code>Id </code>for PK, <code>CapturedAt</code>, <code>TemperatureC </code>and <code>Summary</code>)</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LCKK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LCKK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png 424w, https://substackcdn.com/image/fetch/$s_!LCKK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png 848w, https://substackcdn.com/image/fetch/$s_!LCKK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png 1272w, https://substackcdn.com/image/fetch/$s_!LCKK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LCKK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png" width="1456" height="257" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:257,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:16022,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182508428?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LCKK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png 424w, https://substackcdn.com/image/fetch/$s_!LCKK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png 848w, https://substackcdn.com/image/fetch/$s_!LCKK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png 1272w, https://substackcdn.com/image/fetch/$s_!LCKK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Function App (SSE.WeatherSensor)</h3><p>Our Function App runs every 10 seconds and inserts weather data into the database, simulating a live sensor.</p><p>Here&#8217;s what the table looks like after it&#8217;s populated with weather data generated by the Function App.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MztH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MztH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png 424w, https://substackcdn.com/image/fetch/$s_!MztH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png 848w, https://substackcdn.com/image/fetch/$s_!MztH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png 1272w, https://substackcdn.com/image/fetch/$s_!MztH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MztH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png" width="701" height="345" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:345,&quot;width&quot;:701,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:31533,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182508428?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MztH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png 424w, https://substackcdn.com/image/fetch/$s_!MztH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png 848w, https://substackcdn.com/image/fetch/$s_!MztH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png 1272w, https://substackcdn.com/image/fetch/$s_!MztH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>API (SSE.API)</h3><p>This is the API endpoint that implements SSE. It polls the database every 5 seconds and streams the latest weather data to connected clients (our Blazor WASM).</p><pre><code>app.MapGet("api/weatherforecast", (HttpContext context, SSEDbContext dbContext) =&gt;
{

    async IAsyncEnumerable&lt;WeatherForecast&gt; GetWeatherForecastStream([EnumeratorCancellation] CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            var latestForecast = await dbContext.WeatherForecasts
                .OrderByDescending(wf =&gt; wf.CapturedAt)
                .FirstOrDefaultAsync(cancellationToken);

            if (latestForecast != null)
            {
                yield return latestForecast;
            }

            await Task.Delay(5000, cancellationToken);
        }
    }

    if (context.Request.Headers["Accept"] == "text/event-stream")
    {
        return Results.ServerSentEvents(GetWeatherForecastStream(context.RequestAborted));
    }
    else
    {
        return Results.BadRequest("Unsupported Accept header. Use 'text/event-stream'.");
    }
});</code></pre><h3>UI (SSE.Web)</h3><p>This is our client app that subscribes to the SSE endpoint and renders the live weather updates in real time.</p><pre><code>@page "/weather"
@using System.Net.ServerSentEvents
@using System.Text.Json
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@inject HttpClient Http

&lt;h3&gt;Live Weather Sensor Feed&lt;/h3&gt;

@if (forecast == null &amp;&amp; string.IsNullOrEmpty(errorMessage))
{
    &lt;p&gt;&lt;em&gt;Waiting for sensor data...&lt;/em&gt;&lt;/p&gt;
}
else {
    &lt;table class="table"&gt;
        &lt;thead&gt;
            &lt;tr&gt;
                &lt;th&gt;Time (Local)&lt;/th&gt;
                &lt;th&gt;Temp (C)&lt;/th&gt;
                &lt;th&gt;Summary&lt;/th&gt;
            &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;

            &lt;tr style="border: 2px solid black;"&gt;
                &lt;td&gt;@forecast.CapturedAt.ToLocalTime().ToString("HH:mm:ss")&lt;/td&gt;
                &lt;td&gt;@forecast.TemperatureC&#176;C&lt;/td&gt;
                &lt;td&gt;@forecast.Summary&lt;/td&gt;
            &lt;/tr&gt;
        &lt;/tbody&gt;
    &lt;/table&gt;
}


@if (!string.IsNullOrEmpty(errorMessage))
{
    &lt;div class="alert alert-danger"&gt;@errorMessage&lt;/div&gt;
}

@code {
    private WeatherForecast forecast;
    private string? errorMessage;

    public class WeatherForecast
    {
        public int Id { get; set; }
        public DateTimeOffset CapturedAt { get; set; }
        public int TemperatureC { get; set; }
        public string? Summary { get; set; }
    }

    protected override async Task OnInitializedAsync()
    {
        try
        {
            using var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:7194/api/weatherforecast");
            request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("text/event-stream"));

            using var response = await Http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
            using var stream = await response.Content.ReadAsStreamAsync();

            var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
            var parser = SseParser.Create(stream, (type, data) =&gt;
                JsonSerializer.Deserialize&lt;WeatherForecast&gt;(data, options));

            await foreach (var item in parser.EnumerateAsync())
            {
                if (item.Data is not null)
                {
                    forecast = item.Data;

                    await InvokeAsync(StateHasChanged);
                }
            }
        }
        catch (Exception ex)
        {
            errorMessage = $"Connection lost: {ex.Message}";
            await InvokeAsync(StateHasChanged);
        }
    }
}</code></pre><h3>Demo</h3><p>Here&#8217;s the whole solution in action.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hvIz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hvIz!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif 424w, https://substackcdn.com/image/fetch/$s_!hvIz!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif 848w, https://substackcdn.com/image/fetch/$s_!hvIz!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif 1272w, https://substackcdn.com/image/fetch/$s_!hvIz!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hvIz!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif" width="1456" height="444" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:444,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6177494,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182508428?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hvIz!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif 424w, https://substackcdn.com/image/fetch/$s_!hvIz!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif 848w, https://substackcdn.com/image/fetch/$s_!hvIz!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif 1272w, https://substackcdn.com/image/fetch/$s_!hvIz!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>TL;DR:</strong> If your use case is <strong>server-to-browser real-time updates only</strong>, you probably don&#8217;t need SignalR &#8212; SSE is simpler, cheaper, and easier to maintain. Make sure to consider it for your next project.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!n9V2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!n9V2!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif 424w, https://substackcdn.com/image/fetch/$s_!n9V2!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif 848w, https://substackcdn.com/image/fetch/$s_!n9V2!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif 1272w, https://substackcdn.com/image/fetch/$s_!n9V2!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!n9V2!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:12372827,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182508428?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!n9V2!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif 424w, https://substackcdn.com/image/fetch/$s_!n9V2!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif 848w, https://substackcdn.com/image/fetch/$s_!n9V2!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif 1272w, https://substackcdn.com/image/fetch/$s_!n9V2!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div>]]></content:encoded></item><item><title><![CDATA[Stop Building Monoliths! Build Modular Monoliths Instead]]></title><description><![CDATA[Intro to Modular Monolith Architecture]]></description><link>https://systemshogun.com/p/stop-building-monoliths-build-modular</link><guid isPermaLink="false">https://systemshogun.com/p/stop-building-monoliths-build-modular</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Mon, 22 Dec 2025 07:18:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!BWwR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today we&#8217;ll talk about <strong>Modular Monolith architecture</strong> &#8212; what it is, the problems it solves, and why I believe it&#8217;s the right starting point for many modern applications. We&#8217;ll also walk through a few diagrams and a real demo app I&#8217;ve built to see how this looks in an actual codebase.</p><h2>What is a Modular Monolith?</h2><p>A Modular Monolith is a software architecture that runs as a <strong>single process</strong>, just like a traditional monolith. The difference is that instead of one large, tangled codebase, the application is <strong>explicitly split into well-defined modules</strong>.</p><p>Each module represents a <strong>separate domain</strong> and owns its own logic. The boundaries are clear, enforced in code, and intentional &#8212; not just folders pretending to be architecture, but separate class libraries.</p><h2>What is a Module?</h2><p>A module is a <strong>self-contained logical unit</strong> that represents a specific business capability (for example, Payments or Reviews). Its sole purpose is to model that specific business domain &#8212; exactly how you would design a microservice.</p><p>Each module behaves like a <strong>separate application</strong> focused on a single business use case. It has its own API layer and its own data store (a separate schema or even a separate database). In other words, it&#8217;s an independent application that happens to live <strong>inside the monolith</strong>, and it communicates with other modules <strong>only through its public API</strong>.</p><p>And this is the key point: even though all modules run inside a <strong>single process</strong>, they are designed so that, if needed, they can later be <strong>extracted into independent microservices</strong> with minimal friction.</p><p>Here&#8217;s how a typical module looks like.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0ypK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0ypK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png 424w, https://substackcdn.com/image/fetch/$s_!0ypK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png 848w, https://substackcdn.com/image/fetch/$s_!0ypK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png 1272w, https://substackcdn.com/image/fetch/$s_!0ypK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0ypK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png" width="1041" height="185" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:185,&quot;width&quot;:1041,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:19759,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182301692?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0ypK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png 424w, https://substackcdn.com/image/fetch/$s_!0ypK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png 848w, https://substackcdn.com/image/fetch/$s_!0ypK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png 1272w, https://substackcdn.com/image/fetch/$s_!0ypK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Each module exposes its own <strong>public API</strong>, and that is the <strong>only</strong> way other modules are allowed to access its logic. This is extremely important because it enforces <strong>loose coupling</strong>, making modules easier to extract later &#8212; which is at the core of the Modular Monolith&#8217;s benefits.</p><p>In addition, every module owns its <strong>own data store</strong> &#8212; either a separate schema within a shared database or a completely separate database. Data ownership is strict and enforced at the module boundary.</p><p>In short, think of each module as an independent API that just happens to live inside the same application.</p><h2>Why use Modular Monolith?</h2><p>A Modular Monolith is powerful because it lets you <strong>start simple</strong>, while still enforcing <strong>clear boundaries</strong> that allow the system to scale later if &#8212; and only if &#8212; it actually becomes necessary.</p><p>You get the simplicity of a monolith today, but by designing proper modules, you&#8217;re deliberately preparing for a future where parts of the system may need to be <strong>distributed into microservices</strong>. If that time comes, the transition is far less painful than trying to break apart a traditional, tightly coupled monolith.</p><p>Yes, you <em>can</em> start directly with a microservices architecture. But in most cases, that&#8217;s a <strong>bad idea</strong>, even when you&#8217;re convinced you&#8217;ll eventually need it (this is exactly what <a href="https://martinfowler.com/bliki/MonolithFirst.html">Monolith First by Martin Fowler</a> argues).</p><p>By building a Modular Monolith, you get several concrete benefits that help you <strong>ship faster and at lower cost</strong> &#8212; which, in today&#8217;s enterprise world, is non-negotiable (everyone wants it yesterday):</p><ul><li><p><strong>Easy to start with</strong> &#8212; it&#8217;s still a monolith</p></li><li><p><strong>Simple CI/CD</strong> &#8212; a single deployable unit</p></li><li><p><strong>Clear boundaries</strong> &#8212; making future migration to microservices significantly easier</p></li><li><p><strong>Easier to scale when needed</strong> &#8212; both technically and organizationally</p></li><li><p><strong>Better maintainability and team ownership</strong> &#8212; teams work on modules without stepping on each other</p></li></ul><h2>Real Estate Platform</h2><p>Let&#8217;s take a fictional real estate platform as an example &#8212; a place where people list their homes for others to buy. In this scenario, a Modular Monolith solution could look like this.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BWwR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BWwR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png 424w, https://substackcdn.com/image/fetch/$s_!BWwR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png 848w, https://substackcdn.com/image/fetch/$s_!BWwR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png 1272w, https://substackcdn.com/image/fetch/$s_!BWwR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BWwR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png" width="1456" height="664" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:664,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:132900,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182301692?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!BWwR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png 424w, https://substackcdn.com/image/fetch/$s_!BWwR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png 848w, https://substackcdn.com/image/fetch/$s_!BWwR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png 1272w, https://substackcdn.com/image/fetch/$s_!BWwR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>At this point, it&#8217;s important to understand what this diagram <strong>does not</strong> represent.</p><p>This is <strong>not</strong> a microservices architecture. All modules run inside a <strong>single process</strong>, are deployed together, and share the same runtime. There is no network communication between them, no service discovery, no distributed tracing, and no operational overhead.</p><p>Yet, from a <strong>design perspective</strong>, each module behaves as if it were a microservice.</p><p>Each module:</p><ul><li><p>Owns its <strong>business logic</strong></p></li><li><p>Exposes a <strong>public API</strong></p></li><li><p>Owns its <strong>data</strong></p></li><li><p>Has <strong>no direct access</strong> to other modules&#8217; internals or databases</p></li></ul><p>This is what gives the Modular Monolith its power: <strong>strong boundaries without distribution</strong>.</p><h2>Module-to-Module Communication</h2><p>So how do modules talk to each other? Let&#8217;s take <strong>Listings</strong> and <strong>Reviews</strong> as an example.</p><p>Imagine we&#8217;re building a page that needs to display a specific listing (for example, a house on XYZ Street) along with all the reviews people have left for it.</p><p>The Listings module should not reach directly into the Reviews database, and the Reviews module should not query Listings data directly. Instead, communication happens strictly through each module&#8217;s public API.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZOWt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZOWt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png 424w, https://substackcdn.com/image/fetch/$s_!ZOWt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png 848w, https://substackcdn.com/image/fetch/$s_!ZOWt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png 1272w, https://substackcdn.com/image/fetch/$s_!ZOWt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZOWt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png" width="1402" height="620" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:620,&quot;width&quot;:1402,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:105371,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182301692?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZOWt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png 424w, https://substackcdn.com/image/fetch/$s_!ZOWt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png 848w, https://substackcdn.com/image/fetch/$s_!ZOWt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png 1272w, https://substackcdn.com/image/fetch/$s_!ZOWt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The application first calls the <strong>Listings</strong> module to fetch the listing data by <code>listingId</code>. Then, using the same identifier, it calls the <strong>Reviews</strong> module through its public API to retrieve all reviews associated with that listing.</p><p>Both modules:</p><ul><li><p>Access <strong>only their own database</strong></p></li><li><p>Expose <strong>explicit read models</strong> through their APIs</p></li><li><p>Remain completely unaware of each other&#8217;s internal implementation</p></li></ul><p>Even though these calls happen <strong>inside the same process</strong>, they are treated exactly like service-to-service calls. No shared repositories. No cross-module ORM access.</p><p>This discipline is what keeps the system modular.</p><p>If, at some point, the Reviews module needs to be extracted into a separate microservice, this flow remains largely unchanged &#8212; the in-process API call simply becomes a network call. The contract stays the same.</p><p>This is the core idea behind Modular Monolith communication: <strong>design for distribution, without paying the distribution cost upfront</strong>.</p><h2>Modular Monolith Solution</h2><p>To make this concrete, here&#8217;s how this looks in a <strong>real codebase</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!B6LO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!B6LO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png 424w, https://substackcdn.com/image/fetch/$s_!B6LO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png 848w, https://substackcdn.com/image/fetch/$s_!B6LO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png 1272w, https://substackcdn.com/image/fetch/$s_!B6LO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!B6LO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png" width="366" height="923" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f650946b-1737-4bc6-97b2-c79912b53294_366x923.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:923,&quot;width&quot;:366,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:45515,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182301692?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!B6LO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png 424w, https://substackcdn.com/image/fetch/$s_!B6LO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png 848w, https://substackcdn.com/image/fetch/$s_!B6LO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png 1272w, https://substackcdn.com/image/fetch/$s_!B6LO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The solution is structured around a <strong>Modules</strong> folders, where each module represents a distinct business capability &#8212; Accounts, Listings, Reviews, and so on.</p><p>Each module is fully self-contained and separate class library that follows the same internal structure built around <a href="https://devblogs.microsoft.com/ise/next-level-clean-architecture-boilerplate/">Clean Architecture</a>:</p><ul><li><p><strong>API</strong> &#8212; the module&#8217;s public surface, exposed to other modules</p></li><li><p><strong>Application</strong> &#8212; use cases, orchestration, and business workflows</p></li><li><p><strong>Domain</strong> &#8212; core business logic and domain models</p></li><li><p><strong>Infrastructure</strong> &#8212; data access, external integrations, persistence</p></li></ul><p>This is not accidental. Every module is treated as a <strong>mini application</strong> with clear ownership and explicit boundaries.</p><p>The <strong>Shared</strong> projects contain only truly cross-cutting concerns &#8212; things like the database abstraction, shared kernel primitives, or mediator implementation (a custom one I built since <a href="https://www.jimmybogard.com/automapper-and-mediatr-going-commercial/">MediatR</a> is now commercial). There is no shared business logic between modules.</p><p>At the top level, there&#8217;s a single <strong>API host</strong> that wires everything together and a single <strong>Web UI</strong> consuming the system &#8212; reinforcing the fact that this is still a <strong>single deployable unit</strong>, running in a <strong>single process</strong>.</p><p>This structure enforces discipline:</p><ul><li><p>Modules don&#8217;t reach into each other&#8217;s internals</p></li><li><p>Data access stays inside module boundaries</p></li><li><p>Communication happens only through public APIs</p></li></ul><p>And most importantly, if one of these modules ever needs to become a standalone microservice, most of the work is already done &#8212; the boundaries, contracts, and responsibilities are already in place.</p><p>That&#8217;s the real power of a Modular Monolith: <strong>microservice-level design, without microservice-level complexity</strong>.</p><p>Lastly, let&#8217;s take a deeper look at one of the modules &#8212; the <strong>Listings</strong> module.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WnON!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WnON!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png 424w, https://substackcdn.com/image/fetch/$s_!WnON!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png 848w, https://substackcdn.com/image/fetch/$s_!WnON!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png 1272w, https://substackcdn.com/image/fetch/$s_!WnON!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WnON!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png" width="424" height="759" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:759,&quot;width&quot;:424,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:41516,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182301692?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WnON!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png 424w, https://substackcdn.com/image/fetch/$s_!WnON!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png 848w, https://substackcdn.com/image/fetch/$s_!WnON!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png 1272w, https://substackcdn.com/image/fetch/$s_!WnON!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>At first glance, you&#8217;ll notice that the module follows the same internal structure as the others: <strong>API</strong>, <strong>Application</strong>, <strong>Domain</strong>, and <strong>Infrastructure</strong>. This is intentional and consistent across all modules, reinforcing clear boundaries and ownership.</p><h3>API</h3><p>The <strong>API</strong> project is the module&#8217;s public entry point. It exposes endpoints (for example, <code>ListingsEndpoints.cs</code>) and defines how the outside world &#8212; including other modules &#8212; can interact with Listings. Nothing outside the module talks directly to its internals.</p><h3>Application</h3><p>The <strong>Application</strong> layer contains the use cases and orchestration logic. This is where the module&#8217;s behavior lives &#8212; not in controllers, not in infrastructure.</p><p>The important part here is the <strong>Features</strong> folder.</p><p>Each feature (like <code>CreateListing</code>, <code>GetListingById</code>, or <code>GetUserListings</code>) is implemented as a <strong>vertical slice</strong> &#8212; meaning everything related to that use case lives together. This follows <strong>Jimmy Bogard&#8217;s Vertical Slice Architecture</strong>.</p><p>Instead of grouping code by technical concerns (controllers, services, handlers), we group it by <strong>business intent</strong>.</p><p>Each feature typically contains:</p><ul><li><p>The request/command</p></li><li><p>The handler</p></li><li><p>Validation</p></li><li><p>Any feature-specific logic</p></li></ul><p>This makes features easy to understand, easy to modify, and easy to delete.</p><h3>Domain</h3><p>The <strong>Domain</strong> project contains the core business logic:</p><ul><li><p>Entities</p></li><li><p>Value Objects</p></li><li><p>Domain Events</p></li></ul><p>This layer is pure business logic. It has no dependencies on infrastructure, frameworks, or external concerns. It represents the heart of the Listings domain.</p><h3>Infrastructure</h3><p>The <strong>Infrastructure</strong> project handles everything related to persistence and external systems &#8212; for example, database access and ORM configuration. This is the only place where the module touches technical details like EF Core.</p><h3>Why this works so well</h3><p>This combination &#8212; <strong>Modular Monolith + Clean boundaries + Vertical Slice Architecture</strong> &#8212; gives you:</p><ul><li><p>Strong separation between business domains</p></li><li><p>Highly focused, readable features</p></li><li><p>Minimal coupling between modules</p></li><li><p>A structure that scales without becoming unmanageable</p></li></ul><p>Each module is cohesive. Each feature is explicit. And the entire system remains a <strong>single, simple deployable unit</strong>.</p><h2>Final Words</h2><p>I hope this post helps you appreciate the Modular Monolith approach &#8212; and gives you a clear idea of how to build one.</p><p>I strongly believe you should always start small and avoid overengineering. Every large company started small, and they only scaled when it became necessary. Don&#8217;t start with expensive decisions. Build iteratively.</p><p>Don&#8217;t begin with the idea of building the next Netflix. Start with a basic streaming app, then add one feature, then another &#8212; but keep it simple. Once you see real demand, <em>then</em> you can justify adding complexity. Not before.</p><p>This is exactly why Modular Monolith fits my mindset so well. It keeps you on the right track: a single-process application, built with clear module boundaries around real business capabilities. And if your product succeeds and you truly need to scale out, you can evolve the right modules into microservices with far less pain.</p><p></p>]]></content:encoded></item><item><title><![CDATA[Maybe Microsoft Finally Got It Right with Microsoft Agent Framework]]></title><description><![CDATA[Let's talk about Microsoft's NEWEST agentic framework &#8212; Microsoft Agent Framework]]></description><link>https://systemshogun.com/p/maybe-microsoft-finally-got-it-right</link><guid isPermaLink="false">https://systemshogun.com/p/maybe-microsoft-finally-got-it-right</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Sun, 21 Dec 2025 06:23:41 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/v92-htX8lBg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I mean&#8230; c&#8217;mon. Microsoft has shipped <em>a lot</em> of &#8220;next big thing&#8221; AI frameworks lately.<br>First it was Semantic Kernel. Then AutoGen showed up. Every few months, there&#8217;s a new framework, a new promise, and a new &#8220;this is the future&#8221; narrative.</p><p>And now we have <a href="https://learn.microsoft.com/en-us/agent-framework/overview/agent-framework-overview">Microsoft Agent Framework</a>, which, according to Microsoft is&#8230; the best agentic framework that combines the strengths of Semantic Kernel and AutoGen.</p><p>I&#8217;ve heard that before. But as techies and architects, we need to be aware of what&#8217;s coming so we can prepare and make informed decisions about solutions at work. </p><p>In my newest YouTube video, I show a quick demo where we use the Microsoft Agent Framework and the A2A protocol to enable communication between agents, using tools to persist state in Cosmos DB. </p><p>Who knows if this is really the path forward for Microsoft? At the very least, it&#8217;s fun&#8212;and since it&#8217;s Sunday, why not give it a try?</p><div id="youtube2-v92-htX8lBg" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;v92-htX8lBg&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/v92-htX8lBg?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Stop Asking One LLM. Ask a Council Instead.]]></title><description><![CDATA[An overview of the LLM Council repo - a vibe coded project from Andrej Karpathy where you send prompts to a board of AIs]]></description><link>https://systemshogun.com/p/stop-asking-one-llm-ask-a-council</link><guid isPermaLink="false">https://systemshogun.com/p/stop-asking-one-llm-ask-a-council</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Sat, 06 Dec 2025 06:56:57 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/5ee077e3-287f-417b-a30b-44cd88525ff2_1280x720.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently I stumbled upon a fascinating repo from Andrej Karpathy: <strong><a href="https://github.com/karpathy/llm-council">LLM Council</a></strong>.</p><p>It&#8217;s a lightweight local web app that looks like ChatGPT, but instead of relying on a single model, it lets multiple LLMs answer your question, critique each other, and then produce a final, consolidated result.</p><p>Here&#8217;s the core idea, quoting the repo:</p><blockquote><p>Instead of asking a question to your favorite LLM provider (OpenAI GPT-5.1, Google Gemini, Anthropic Claude, xAI Grok, etc.), you can group them into your &#8220;LLM Council&#8221;.</p></blockquote><p>Your query is sent to all of them through <a href="https://openrouter.ai/">OpenRouter</a>, they each give an answer, they review and rank each other&#8217;s work, and finally a Chairman LLM produces the final response.</p><p>And honestly &#8212; it&#8217;s brilliant.</p><h2><strong>Why This Is Cool</strong></h2><p>When you ask a single LLM a question, you&#8217;re trusting one model&#8217;s reasoning, training data, biases, and blind spots.</p><p>With <em>LLM Council</em>, a few things change:</p><ul><li><p>Multiple models answer the same question independently.</p></li><li><p>They then <strong>evaluate each other&#8217;s responses</strong>.</p></li><li><p>The system picks a <strong>&#8220;Chairman LLM&#8221; </strong>(you choose which) to read all answers and critiques, and then write a final, improved response.</p></li></ul><p>It&#8217;s like having a panel of experts debate the prompt before speaking to you.</p><h2><strong>Why This is Cool?</strong></h2><p>LLMs behave differently:</p><ul><li><p>Some are great with code (Claude).</p></li><li><p>Some are great coders and overall thinkers (GPT-5.1).</p></li><li><p>Some are creative and fast (Gemini).</p></li><li><p>Some are cheap and fast (Mixtral, Qwen, etc.).</p></li></ul><p>Instead of guessing which one is best for each prompt, LLM Council lets them <strong>collaborate</strong>.</p><p>As models become more specialized, this &#8220;debate &#8594; critique &#8594; consolidate&#8221; pattern becomes extremely powerful.</p><p>It&#8217;s basically a tiny research lab running on your laptop.</p><h2><strong>Trying It Out</strong></h2><p>The repo is simple to clone and run. Basically, you just follow the README and you&#8217;re done. You&#8217;ll need an <strong>OpenRouter API key</strong> &#8212; everything else works out of the box.</p><h2><strong>Demo</strong></h2><h3><strong>Running It on Windows</strong></h3><p>If you&#8217;re on Windows, there&#8217;s one extra step: the repo uses a <code>start.sh</code> shell script.<br>Windows doesn&#8217;t run <code>.sh</code> files natively, so you need a shell environment.</p><p>I used <strong><a href="https://git-scm.com/install/">Git Bash</a></strong> (included with Git for Windows) to run the script:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sRgU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sRgU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png 424w, https://substackcdn.com/image/fetch/$s_!sRgU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png 848w, https://substackcdn.com/image/fetch/$s_!sRgU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png 1272w, https://substackcdn.com/image/fetch/$s_!sRgU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sRgU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png" width="578" height="367" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:367,&quot;width&quot;:578,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:25468,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/180861958?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sRgU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png 424w, https://substackcdn.com/image/fetch/$s_!sRgU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png 848w, https://substackcdn.com/image/fetch/$s_!sRgU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png 1272w, https://substackcdn.com/image/fetch/$s_!sRgU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Mac and Linux users don&#8217;t need this &#8212; both platforms support shell scripts out of the box.</p><p>Here&#8217;s a quick demo of how this works with a prompt &#8220;How to become a great solution architect? I want to be able to build systems like Netflix and Uber.&#8221;</p><div id="youtube2--72SM6Fe10w" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;-72SM6Fe10w&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/-72SM6Fe10w?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Upload Files to Your API from a Copilot Studio Agent]]></title><description><![CDATA[Learn how to upload files from your Copilot Studio agent to your own API]]></description><link>https://systemshogun.com/p/upload-files-to-your-api-from-a-copilot</link><guid isPermaLink="false">https://systemshogun.com/p/upload-files-to-your-api-from-a-copilot</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Tue, 25 Nov 2025 10:31:23 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!q5eM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Copilot Studio has hundreds of built-in integrations, yet there is virtually no documentation on how to upload files to your own API. You can easily send files to SharePoint or Google Drive, but the moment you want to send one to your own backend, you&#8217;re on your own.</p><p>That&#8217;s why in today&#8217;s post, I&#8217;ll walk you through how I managed to make it work and, hopefully, help you apply it in your own projects.</p><h3>The API</h3><p>First, here&#8217;s how my API looks. It receives the file from Copilot Studio, performs basic validation, and uploads it to a storage account.</p><pre><code>    [HttpPost(&#8221;upload&#8221;)]
    public async Task&lt;IActionResult&gt; Upload([FromBody] UploadFileRequest request)
    {
        if (string.IsNullOrWhiteSpace(request.ContentBytes))
            return BadRequest(&#8221;contentBytes is missing&#8221;);

        if (string.IsNullOrWhiteSpace(request.Name))
            return BadRequest(&#8221;name is missing&#8221;);

        // Handle possible data:image/png;base64,...
        var base64 = request.ContentBytes.Contains(&#8217;,&#8217;)
            ? request.ContentBytes.Split(&#8217;,&#8217;).Last()
            : request.ContentBytes;

        byte[] fileBytes;

        try
        {
            fileBytes = Convert.FromBase64String(base64);
        }
        catch
        {
            return BadRequest(&#8221;Invalid base64 content&#8221;);
        }

        var container = _blobServiceClient.GetBlobContainerClient(&#8221;uploadedfiles&#8221;);
        await container.CreateIfNotExistsAsync();

        var blob = container.GetBlobClient(request.Name);

        using var stream = new MemoryStream(fileBytes);
        await blob.UploadAsync(stream, overwrite: true);

        return Ok(new
        {
            FileName = request.Name,
            Size = fileBytes.Length,
            Status = &#8220;Uploaded&#8221;
        });
    }
}</code></pre><p>Here&#8217;s my <strong>UploadFileRequest</strong><code> request </code>model. This is the format I am sending from Copilot Studio, so it needs strictly match it.</p><pre><code>public class UploadFileRequest
{
    public string ContentBytes { get; set; } = string.Empty;
    public string Name { get; set; } = string.Empty;
}</code></pre><h3>Copilot Studio Flow</h3><p>Next, I created a flow for the whole file upload process. The flow expects a file, then it runs an HTTP post against my API, attaching the file content and the file name.</p><p><strong>contentBytes</strong>: <code>triggerBody()?[&#8217;file&#8217;][&#8217;content&#8217;]</code></p><p>To add it, use the F(x) icon on the right.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5fY3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5fY3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png 424w, https://substackcdn.com/image/fetch/$s_!5fY3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png 848w, https://substackcdn.com/image/fetch/$s_!5fY3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png 1272w, https://substackcdn.com/image/fetch/$s_!5fY3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5fY3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png" width="794" height="204" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:204,&quot;width&quot;:794,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:20540,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/179894331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!5fY3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png 424w, https://substackcdn.com/image/fetch/$s_!5fY3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png 848w, https://substackcdn.com/image/fetch/$s_!5fY3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png 1272w, https://substackcdn.com/image/fetch/$s_!5fY3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Then add it here and save it.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CsLw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CsLw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png 424w, https://substackcdn.com/image/fetch/$s_!CsLw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png 848w, https://substackcdn.com/image/fetch/$s_!CsLw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png 1272w, https://substackcdn.com/image/fetch/$s_!CsLw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CsLw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png" width="489" height="259" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:259,&quot;width&quot;:489,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5974,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/179894331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CsLw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png 424w, https://substackcdn.com/image/fetch/$s_!CsLw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png 848w, https://substackcdn.com/image/fetch/$s_!CsLw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png 1272w, https://substackcdn.com/image/fetch/$s_!CsLw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Do the same for the name below.</p><p><strong>name</strong>: <code>triggerBody()?[&#8217;file&#8217;]?[&#8217;name&#8217;]</code></p><p>In the end, I added a condition that checks the response from the file upload. If it returns <strong>200</strong>, it uses the <strong>Respond to agent</strong> action to confirm the file was uploaded successfully. Otherwise, it uses the same action to indicate that the upload failed.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!q5eM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!q5eM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png 424w, https://substackcdn.com/image/fetch/$s_!q5eM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png 848w, https://substackcdn.com/image/fetch/$s_!q5eM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png 1272w, https://substackcdn.com/image/fetch/$s_!q5eM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!q5eM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png" width="966" height="1238" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1238,&quot;width&quot;:966,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:143219,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/179894331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!q5eM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png 424w, https://substackcdn.com/image/fetch/$s_!q5eM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png 848w, https://substackcdn.com/image/fetch/$s_!q5eM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png 1272w, https://substackcdn.com/image/fetch/$s_!q5eM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Agent</h3><p>Now that we have the flow created, we need to create an agent. I named mine <strong>File Upload Agent</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TIjv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TIjv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png 424w, https://substackcdn.com/image/fetch/$s_!TIjv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png 848w, https://substackcdn.com/image/fetch/$s_!TIjv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png 1272w, https://substackcdn.com/image/fetch/$s_!TIjv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TIjv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png" width="1312" height="365" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:365,&quot;width&quot;:1312,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:47296,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/179894331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!TIjv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png 424w, https://substackcdn.com/image/fetch/$s_!TIjv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png 848w, https://substackcdn.com/image/fetch/$s_!TIjv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png 1272w, https://substackcdn.com/image/fetch/$s_!TIjv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Topic</h3><p>The last step is to create a topic that gets triggered any time a person mentions that they want to upload a file.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aHN3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aHN3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png 424w, https://substackcdn.com/image/fetch/$s_!aHN3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png 848w, https://substackcdn.com/image/fetch/$s_!aHN3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png 1272w, https://substackcdn.com/image/fetch/$s_!aHN3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aHN3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png" width="1363" height="373" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:373,&quot;width&quot;:1363,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:51999,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/179894331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!aHN3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png 424w, https://substackcdn.com/image/fetch/$s_!aHN3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png 848w, https://substackcdn.com/image/fetch/$s_!aHN3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png 1272w, https://substackcdn.com/image/fetch/$s_!aHN3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Name it <strong>File Upload</strong></p><p>Below is the topic setup. It defines what this topic is about &#8212; in our case, file uploads &#8212; and what will trigger it.<br>When the user asks for a file to be uploaded, the agent responds with a question prompting them to attach the file in the chat.<br>In the next step, we call the flow created earlier. As input, we set <strong>File content (Record)</strong> to <strong>First(System.Activity.Attachments)</strong>, which grabs the first attached file. If you want to process all files, simply remove <code>First</code>.<br>For the output, we assign the response to a variable called <strong>OutputMsg</strong> and display it inside a <strong>Message </strong>element.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QVWP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QVWP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png 424w, https://substackcdn.com/image/fetch/$s_!QVWP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png 848w, https://substackcdn.com/image/fetch/$s_!QVWP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png 1272w, https://substackcdn.com/image/fetch/$s_!QVWP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QVWP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png" width="346" height="1061" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1061,&quot;width&quot;:346,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:70268,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/179894331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QVWP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png 424w, https://substackcdn.com/image/fetch/$s_!QVWP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png 848w, https://substackcdn.com/image/fetch/$s_!QVWP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png 1272w, https://substackcdn.com/image/fetch/$s_!QVWP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>And that&#8217;s it. If you follow the same approach, you&#8217;ll be able to upload files from Copilot Studio (directly in the agent chat) to your own API.</p><p>While researching how to do this, I couldn&#8217;t find a single resource or piece of documentation on it, so I hope this helps others as well.</p>]]></content:encoded></item><item><title><![CDATA[Azure Cosmos DB Partitioning Fundamentals]]></title><description><![CDATA[Learn the fundamentals of partitioning in Azure Cosmos DB (NoSQL API)]]></description><link>https://systemshogun.com/p/azure-cosmos-db-partitioning-fundamentals</link><guid isPermaLink="false">https://systemshogun.com/p/azure-cosmos-db-partitioning-fundamentals</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Mon, 03 Nov 2025 22:08:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hBs-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today&#8217;s post is a quick introduction to partitioning with <a href="https://azure.microsoft.com/en-us/products/cosmos-db">Azure Cosmos DB</a>.</p><p>Azure Cosmos DB is a fully managed Platform as a Service (PaaS) and serves as Microsoft&#8217;s flagship distributed database solution.</p><p>How you design the partitioning strategy for your Cosmos DB is undoubtedly one of the most critical architectural decisions you&#8217;ll make when building a solution on Microsoft Azure&#8217;s highly scalable, globally distributed NoSQL database.</p><h4><strong>Request Units (RUs)</strong></h4><p>Each operation you perform in Cosmos DB &#8212; whether it&#8217;s a <strong>read</strong>, <strong>write</strong>, <strong>update</strong>, or <strong>query</strong> &#8212; consumes a certain number of <strong><a href="https://learn.microsoft.com/en-us/azure/cosmos-db/request-units">Request Units (RUs)</a></strong>.</p><p>RUs represent a normalized measure of system resources such as <strong>CPU, memory, and IOPS (input/output operations per second)</strong> required to perform a given operation. This abstraction makes performance predictable &#8212; instead of worrying about the underlying infrastructure, you only manage RUs.</p><p>The amount of RUs consumed depends on several factors:</p><ul><li><p><strong>Item size</strong> &#8211; larger JSON documents consume more RUs.</p></li><li><p><strong>Operation type</strong> &#8211; writes and queries typically cost more than point reads.</p></li><li><p><strong>Query complexity</strong> &#8211; filters, cross-partition queries, and sorting increase RU consumption.</p></li><li><p><strong>Indexing</strong> &#8211; since Cosmos DB automatically indexes all properties by default, write operations cost more if you have large or deeply nested documents.</p></li></ul><p>You provision throughput in RUs per second (e.g., 400 RU/s), which defines how much performance capacity your container or database can handle. If your workload exceeds that capacity, Cosmos DB starts to throttle requests, returning a <strong>429 (Request Rate Too Large)</strong> response until throughput is available again.</p><p>You can monitor RU consumption using the Azure Portal, SDK, or query metrics to identify and optimize expensive operations.</p><h3><strong>Basic Database Structure</strong></h3><p>In the Azure Cosmos DB NoSQL API, the data model consists of three main components &#8212; <strong>database</strong>, <strong>container(s)</strong>, and <strong>item(s)</strong> &#8212; as illustrated in the diagram below.</p><ul><li><p>A <strong>database</strong> is the top-level logical namespace that groups one or more containers.</p></li><li><p>A <strong>container</strong> is the fundamental unit of scalability and throughput. It holds a collection of JSON documents (items) and defines the <strong>partition key</strong> used to distribute data across partitions.</p></li><li><p>An <strong>item</strong> is a single JSON document stored within a container. Each item must include an <code>id</code> property and a value for the partition key.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TtzI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TtzI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png 424w, https://substackcdn.com/image/fetch/$s_!TtzI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png 848w, https://substackcdn.com/image/fetch/$s_!TtzI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png 1272w, https://substackcdn.com/image/fetch/$s_!TtzI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TtzI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png" width="455" height="553" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:553,&quot;width&quot;:455,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:22701,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/177918296?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!TtzI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png 424w, https://substackcdn.com/image/fetch/$s_!TtzI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png 848w, https://substackcdn.com/image/fetch/$s_!TtzI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png 1272w, https://substackcdn.com/image/fetch/$s_!TtzI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>What is Partitioning?</h4><p>Partitioning literally means <em>dividing into parts</em>. In Azure Cosmos DB, partitioning is a core mechanism that enables your application to scale efficiently and maintain consistent performance at any size.</p><p>Whenever you create a container, you must define a <strong>partition key</strong> &#8212; a property within your JSON documents that determines how data is distributed across partitions. The partition key choice directly affects scalability, query performance, and cost efficiency.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hBs-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hBs-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png 424w, https://substackcdn.com/image/fetch/$s_!hBs-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png 848w, https://substackcdn.com/image/fetch/$s_!hBs-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png 1272w, https://substackcdn.com/image/fetch/$s_!hBs-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hBs-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png" width="1221" height="805" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:805,&quot;width&quot;:1221,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:423598,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/177918296?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hBs-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png 424w, https://substackcdn.com/image/fetch/$s_!hBs-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png 848w, https://substackcdn.com/image/fetch/$s_!hBs-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png 1272w, https://substackcdn.com/image/fetch/$s_!hBs-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Each partition key value creates a virtual bucket called a <strong>logical partition</strong>, which contains all items that share the same partition key value. Choosing the right partition key is critical to achieving balanced data distribution. Ideally, the partition key should contain a value that never changes and one that has <strong>high cardinality</strong> &#8212; meaning it should have many unique values to evenly distribute data and requests across multiple logical partitions.</p><p>Otherwise, you may end up with what&#8217;s known as a <strong>hot partition</strong> &#8212; a single logical partition that stores a disproportionately large share of your data or handles most of the requests. This can lead to uneven throughput consumption, throttling, and degraded performance as that single partition becomes a bottleneck for your workload.</p><p>A <strong>logical partition</strong> resides within a <strong>physical partition</strong>. Depending on your data volume and throughput, a container can span multiple physical partitions. Each physical partition hosts one or more logical partitions, and Azure Cosmos DB automatically manages this distribution behind the scenes to maintain performance and scalability.</p><p>Now, let&#8217;s dive a bit deeper into partitioning strategy and discuss how to choose an effective partition key. </p><h4>Partitioning Strategy</h4><p>Let&#8217;s imagine we&#8217;re building a simple e-commerce website, similar to Amazon, where we have <strong>products</strong>, <strong>categories</strong>, and <strong>reviews</strong>. When users visit the site, they see a list of categories, select one, and then browse the products that belong to it.</p><p>For the sake of simplicity, we won&#8217;t attempt to replicate Amazon&#8217;s full complexity here &#8212; instead, we&#8217;ll focus on a simplified example that helps illustrate the fundamentals of partitioning in Azure Cosmos DB.</p><p>So, based on the structure we just defined, we&#8217;ll have three containers &#8212; one for <strong>products</strong>, one for <strong>categories</strong>, and one for <strong>reviews</strong>.</p><p>Now comes the interesting (and often challenging) part: choosing the right partition key for each container. The partition key determines how data is distributed and accessed, so picking the right one is essential for achieving scalability, balanced throughput, and optimal query performance.</p><p><strong>Application accessing patterns</strong></p><p>Whenever you choose a partition key, your starting point should always be how your application accesses the data. The way users query and interact with your data model should guide your partitioning strategy.</p><p>For our fictitious e-commerce app, when a user visits the website, they first see a list of categories and select one. After choosing a category, they&#8217;re shown a list of products that belong to it. From there, they can click on a specific product to view its details, add it to the cart, or purchase it.</p><p>Now, based on this, let&#8217;s choose the right partition key for each container, starting with the simplest one - categories.</p><p><strong>categories</strong></p><p>This one is easy. Since we don&#8217;t have that many categories (tens and not thousands) we&#8217;ll use the <strong>id</strong> as a partition key for it. What this means is that for each category we&#8217;ll have a single logical partition.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qeiD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qeiD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png 424w, https://substackcdn.com/image/fetch/$s_!qeiD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png 848w, https://substackcdn.com/image/fetch/$s_!qeiD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png 1272w, https://substackcdn.com/image/fetch/$s_!qeiD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qeiD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png" width="1162" height="808" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:808,&quot;width&quot;:1162,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:427859,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/177918296?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qeiD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png 424w, https://substackcdn.com/image/fetch/$s_!qeiD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png 848w, https://substackcdn.com/image/fetch/$s_!qeiD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png 1272w, https://substackcdn.com/image/fetch/$s_!qeiD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>products</strong></p><p>You might be tempted to use the product&#8217;s ID<em> </em>as the partition key here as well &#8212; it offers high cardinality and would evenly distribute data across partitions. While that&#8217;s not necessarily wrong, remember that our partitioning strategy is driven by how the application accesses data, not just by distribution.</p><p>In our e-commerce example, users typically browse products by category, not by individual product ID. That&#8217;s why a better choice for the Products container is <strong>CategoryID</strong> as the partition key &#8212; it aligns with the most common access pattern (fetching all products within a category) while still providing good distribution and query performance.</p><p>This approach keeps queries efficient, since the data you need (all products in a given category) is co-located within the same logical partition. As a result, Cosmos DB doesn&#8217;t have to perform a cross-partition query, which improves latency, throughput efficiency (RUs), and overall performance.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xnZj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xnZj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png 424w, https://substackcdn.com/image/fetch/$s_!xnZj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png 848w, https://substackcdn.com/image/fetch/$s_!xnZj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png 1272w, https://substackcdn.com/image/fetch/$s_!xnZj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xnZj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png" width="1221" height="805" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:805,&quot;width&quot;:1221,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:473799,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/177918296?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xnZj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png 424w, https://substackcdn.com/image/fetch/$s_!xnZj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png 848w, https://substackcdn.com/image/fetch/$s_!xnZj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png 1272w, https://substackcdn.com/image/fetch/$s_!xnZj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>reviews</strong></p><p>This one should be straightforward. In any e-commerce platform, <strong>reviews</strong> naturally belong to the <strong>product</strong> they describe &#8212; users leave feedback directly under the product page for others to see.</p><p>To maintain high cardinality and maximize query efficiency, we&#8217;ll use the <strong>ProductID</strong> as the partition key for the Reviews container. This way, all reviews for a given product are stored within the same logical partition, allowing the application to retrieve them with a single, efficient query &#8212; no cross-partition scans, no unnecessary RU consumption, and minimal latency.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PUUK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PUUK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png 424w, https://substackcdn.com/image/fetch/$s_!PUUK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png 848w, https://substackcdn.com/image/fetch/$s_!PUUK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png 1272w, https://substackcdn.com/image/fetch/$s_!PUUK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PUUK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png" width="1265" height="805" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:805,&quot;width&quot;:1265,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:476908,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/177918296?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PUUK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png 424w, https://substackcdn.com/image/fetch/$s_!PUUK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png 848w, https://substackcdn.com/image/fetch/$s_!PUUK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png 1272w, https://substackcdn.com/image/fetch/$s_!PUUK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For example, whenever you&#8217;re fetching a product&#8217;s reviews, you&#8217;ll run a query like this:</p><pre><code>SELECT * 
FROM r 
WHERE r.ProductId = "123"</code></pre><p>This query retrieves all reviews for the product with ID <strong>123</strong>. Since all reviews for a given product reside in the same logical partition, Cosmos DB executes this as a single-partition query, ensuring low latency and efficient RU consumption.</p><h4>Thanks</h4><p>That&#8217;s a quick overview of the Cosmos DB partitioning fundamentals and the practical approach I personally use when designing data models. These are the core concepts that, once mastered, make it much easier to build scalable, high-performing solutions in Azure.</p><h4>YouTube</h4><p>Here&#8217;s a 10-minute video where I explain all this + a code demo.</p><div id="youtube2-TfR0I7QB4MY" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;TfR0I7QB4MY&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/TfR0I7QB4MY?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>If you want to really go deep into this topic, here&#8217;s links that I recommend:</p><ul><li><p><a href="https://learn.microsoft.com/en-us/azure/cosmos-db/partitioning-overview">Partitioning and horizontal scaling - Azure Cosmos DB | Microsoft Learn</a></p></li><li><p><a href="https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/model-partition-example">Model and Partition Data using a Real-World Example - Azure Cosmos DB for NoSQL | Microsoft Learn</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Private Container Communication in Azure Container Apps]]></title><description><![CDATA[Learn the basics of private containers communication with Azure Container Apps]]></description><link>https://systemshogun.com/p/private-container-communication-in</link><guid isPermaLink="false">https://systemshogun.com/p/private-container-communication-in</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Tue, 30 Sep 2025 07:10:12 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!qamL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today I am going to show you how you can deploy containers to Azure Container Apps (ACA) that can communicate with each other <em>privately </em>using <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview?tabs=bicep">Bicep</a>. </p><p>Most of the tutorials out there concentrate on building and deploying public containers that can be accessed from everyone over the internet. This approach, however, is not ideal and is rarely used in real production environments. </p><p>That&#8217;s why today I will show you how you can deploy two containers, one accessible from the internet (the web app UI) and another one that&#8217;s only accessible by the first container (the backend).</p><p>Keep in mind that this is not a full-blown tutorial. You should clone the repo and study it on your own. You can find the source code of my demo here: <a href="https://github.com/kaldren/acr-private-demo">kaldren/acr-private-demo</a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qamL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qamL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png 424w, https://substackcdn.com/image/fetch/$s_!qamL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png 848w, https://substackcdn.com/image/fetch/$s_!qamL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png 1272w, https://substackcdn.com/image/fetch/$s_!qamL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qamL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png" width="370" height="599" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:599,&quot;width&quot;:370,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:35951,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/174315096?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qamL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png 424w, https://substackcdn.com/image/fetch/$s_!qamL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png 848w, https://substackcdn.com/image/fetch/$s_!qamL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png 1272w, https://substackcdn.com/image/fetch/$s_!qamL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>What is Azure Container Apps</h3><p>Azure Container Apps is a serverless platform for deploying and running containers. It is built on top of Kubernetes, and it abstracts away most of its complexity for you. Basically, things like scaling, container orchestration, configuration and server updates are all done for you by Azure.</p><h3>Bicep</h3><p>For this demo I&#8217;ve decided to use Bicep to deploy my infrastructure. You can check the source code for a more detailed walkthrough.</p><h3>Container Communication in Azure Container Apps</h3><p>There are a few ways for containers to talk to each other in ACA.</p><ul><li><p>Default fully qualified domain name (FQDN)</p></li><li><p>A custom domain name</p></li><li><p>The container app name, for instance <code>http://&lt;APP_NAME&gt;</code> for internal requests</p></li><li><p>A Dapr URL</p></li></ul><p>Today I will show you how the third option above, connecting via the app name for internal requests. </p><p>Let&#8217;s take a look at the bicep files now.</p><h3>Repo Walkthrough</h3><h4>Azure Container Registry</h4><p>First, to be able to deploy containers in Azure via ACA you need to have <a href="https://azure.microsoft.com/en-us/products/container-registry">Azure Container Registry</a> (ACR). This service can build and store your container images. Once the image is in the ACR your Container App can pull from it and use the image to build and deploy a container instance.</p><pre><code>@description(&#8217;Name of the Container Registry&#8217;)
param containerRegistryName string

resource containerRegistry &#8216;Microsoft.ContainerRegistry/registries@2025-05-01-preview&#8217; = {
  name: containerRegistryName
  location: resourceGroup().location
  identity: {
    type: &#8216;SystemAssigned&#8217;
  }
  sku: {
    name: &#8216;Basic&#8217;
  }
  properties: {
    adminUserEnabled: true
  }
}

output containerRegistry object = {
  name: containerRegistry.name
  endpoint: containerRegistry.properties.loginServer
}</code></pre><h4>Managed Identity</h4><p>Once we deploy the ACR we need to create another resource - a <a href="https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview">Managed identity</a>. We will then assign the role <a href="https://learn.microsoft.com/en-us/azure/container-registry/container-registry-rbac-built-in-roles-overview?tabs=registries-configured-with-rbac-registry-abac-repository-permissions">AcrPull </a>to it for the ACR that we created above. This will allow it to pull container images from the registry. We will then assign this identity to our container apps which would allow them to pull the images from the registry themselves.</p><pre><code>param containerRegistryName string

resource managedIdentity &#8216;Microsoft.ManagedIdentity/userAssignedIdentities@2025-01-31-preview&#8217; = {
  location: resourceGroup().location
  name: &#8216;mi-containerapp&#8217;
}

// Set ArcPull role assignment for the managed identity on the ACR
resource acrPull &#8216;Microsoft.Authorization/roleAssignments@2022-04-01&#8217; = {
  name: guid(managedIdentity.id, &#8216;AcrPull&#8217;)
  scope: containerRegistry
  properties: {
    roleDefinitionId: subscriptionResourceId(
      &#8216;Microsoft.Authorization/roleDefinitions&#8217;,
      &#8216;7f951dda-4ed3-4680-a7ca-43fe172d538d&#8217; // AcrPull role ID
    )
    principalId: managedIdentity.properties.principalId
    principalType: &#8216;ServicePrincipal&#8217;
  }
}

output managedIdentityId string = managedIdentity.id

resource containerRegistry &#8216;Microsoft.ContainerRegistry/registries@2025-05-01-preview&#8217; existing = {
  name: containerRegistryName
}</code></pre><h4>Azure Container Apps</h4><p>We will deploy two containers - one for the frontend and one for the backend.</p><pre><code>param location string = resourceGroup().location
param containerAppEnvironmentName string
param containerAppName string
param containerRegistryName string
param imageName string
param imageTag string

resource containerAppEnvironment &#8216;Microsoft.App/managedEnvironments@2025-02-02-preview&#8217; = {
  name: containerAppEnvironmentName
  location: location
  properties: {}
}

resource containerApp &#8216;Microsoft.App/containerApps@2022-03-01&#8217; ={
  name: containerAppName
  location: location
  <strong>identity</strong>: {
    type: &#8216;SystemAssigned, UserAssigned&#8217;
    userAssignedIdentities: {
      &#8216;${managedIdentity.id}&#8217;: {}
    }
  }
  properties:{
    managedEnvironmentId: containerAppEnvironment.id
    configuration: {
      <strong>ingress</strong>: {
        targetPort: 9696
        external: true
      }
      <strong>registries</strong>: [
        {
          server: &#8216;${containerRegistry.name}.azurecr.io&#8217;
          identity: managedIdentity.id
        }
      ]
    }
    template: {
      containers: [
        {
          image: &#8216;${containerRegistry.name}.azurecr.io/${imageName}:${imageTag}&#8217;
          name: imageName
        }
      ]
    }
  }
}

resource containerRegistry &#8216;Microsoft.ContainerRegistry/registries@2025-05-01-preview&#8217; existing = {
  name: containerRegistryName
}

resource managedIdentity &#8216;Microsoft.ManagedIdentity/userAssignedIdentities@2025-01-31-preview&#8217; existing = {
  name: &#8216;mi-containerapp&#8217;
}</code></pre><p>The most important sections (in bold) to note are:</p><ul><li><p>identity section: Here we assign the managed identity created above to the app</p></li><li><p>registry section: We use the identity to authenticate with the ACR</p></li><li><p>ingress section: We allow incoming <strong>external</strong> requests (from the internet) because the web app needs to be accessible from everyone</p></li></ul><p>Here&#8217;s the deployed <strong>frontend</strong> container app.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!S_Hl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!S_Hl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png 424w, https://substackcdn.com/image/fetch/$s_!S_Hl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png 848w, https://substackcdn.com/image/fetch/$s_!S_Hl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png 1272w, https://substackcdn.com/image/fetch/$s_!S_Hl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!S_Hl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png" width="1351" height="945" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:945,&quot;width&quot;:1351,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:122210,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/174315096?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!S_Hl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png 424w, https://substackcdn.com/image/fetch/$s_!S_Hl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png 848w, https://substackcdn.com/image/fetch/$s_!S_Hl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png 1272w, https://substackcdn.com/image/fetch/$s_!S_Hl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Now, let&#8217;s take a look at the <strong>backend</strong> container app.</p><pre><code>param location string = resourceGroup().location
param containerAppEnvironmentName string
param containerAppName string
param containerRegistryName string
param imageName string
param imageTag string

resource containerAppEnvironment &#8216;Microsoft.App/managedEnvironments@2025-02-02-preview&#8217; = {
  name: containerAppEnvironmentName
  location: location
  properties: {}
}

resource containerApp &#8216;Microsoft.App/containerApps@2022-03-01&#8217; ={
  name: containerAppName
  location: location
  identity: {
    type: &#8216;SystemAssigned, UserAssigned&#8217;
    userAssignedIdentities: {
      &#8216;${managedIdentity.id}&#8217;: {}
    }
  }
  properties:{
    managedEnvironmentId: containerAppEnvironment.id
    configuration: {
      ingress: {
        targetPort: 8686
        external: false
      }
      registries: [
        {
          server: &#8216;${containerRegistry.name}.azurecr.io&#8217;
          identity: managedIdentity.id
        }
      ]
    }
    template: {
      containers: [
        {
          image: &#8216;${containerRegistry.name}.azurecr.io/${imageName}:${imageTag}&#8217;
          name: imageName
        }
      ]
    }
  }
}

resource containerRegistry &#8216;Microsoft.ContainerRegistry/registries@2025-05-01-preview&#8217; existing = {
  name: containerRegistryName
}

resource managedIdentity &#8216;Microsoft.ManagedIdentity/userAssignedIdentities@2025-01-31-preview&#8217; existing = {
  name: &#8216;mi-containerapp&#8217;
}</code></pre><p>The only difference here is the <strong>external</strong> <strong>ingress</strong> is set to <strong>false</strong>. That&#8217;s because we don&#8217;t want this container to be accessible from the internet but only from the network inside the <a href="https://learn.microsoft.com/en-us/azure/container-apps/environment">Azure Container App Environment</a>.</p><p>Here&#8217;s how the <strong>backend</strong> container app looks like.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!And5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!And5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png 424w, https://substackcdn.com/image/fetch/$s_!And5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png 848w, https://substackcdn.com/image/fetch/$s_!And5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png 1272w, https://substackcdn.com/image/fetch/$s_!And5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!And5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png" width="1294" height="964" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:964,&quot;width&quot;:1294,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:123675,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/174315096?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!And5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png 424w, https://substackcdn.com/image/fetch/$s_!And5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png 848w, https://substackcdn.com/image/fetch/$s_!And5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png 1272w, https://substackcdn.com/image/fetch/$s_!And5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h4>main.bicep</h4><p>Here we add all bicep files as modules. For example:</p><pre><code>module acr &#8216;modules/container-registry.bicep&#8217; = {
  name: &#8216;acr-${randomSuffix}&#8217;
  params: {
    containerRegistryName: containerRegistryName
  }
  dependsOn: [
    rg
  ]
  scope: resourceGroup(resourceGroupName)
}</code></pre><p>(Check the repo for the full file content)</p><h4>deploy.sh</h4><p>I&#8217;ve written a shell script that does the deployment for me. Here&#8217;s part of it.</p><pre><code># az login -- Might need to login first. Uncomment if needed.

az deployment sub create \
  --name rgDeployment \
  --location eastus \
  --parameters params/main.dev.bicepparam deployContainerApp=false

# # Login to ACR
ACR_LOGIN_SERVER=$(az acr show -n $ACR_NAME --query loginServer -o tsv)
az acr build --registry $ACR_NAME \
  --image $BACKEND_IMAGE:$BACKEND_TAG \
  --file ../backend/Dockerfile ../backend

ACR_LOGIN_SERVER=$(az acr show -n $ACR_NAME --query loginServer -o tsv)
az acr build --registry $ACR_NAME \
  --image $FRONTEND_IMAGE:$FRONTEND_TAG \
  --file ../frontend/Dockerfile ../frontend

az deployment sub create \
  --name rgDeployment \
  --location eastus \
  --parameters params/main.dev.bicepparam deployContainerApp=true</code></pre><p>What this does is it is first deploying the ACR and managed identity resources without the container apps (see the <strong>deployContainerApp</strong> is set to false). That&#8217;s because I want to build and deploy the images for the frontend and backend first, so they are available for when we start deploying the container apps. </p><h4>Dockerfile</h4><p>Nothing fancy here. We just set the version to <strong>Python 3.13</strong>, install <strong>requirements.txt</strong> and run the apps on ports 8686 and 9696.</p><pre><code>FROM python:3.13
WORKDIR /app
ENV HELLO_MESSAGE=&#8221;Hello from the backend!&#8221;
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8686
CMD [&#8221;uvicorn&#8221;, &#8220;main:app&#8221;, &#8220;--host&#8221;, &#8220;0.0.0.0&#8221;, &#8220;--port&#8221;, &#8220;8686&#8221;]</code></pre><pre><code>FROM python:3.13
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 9696
# Run streamlit app on container&#8217;s port 9696
CMD [&#8221;streamlit&#8221;, &#8220;run&#8221;, &#8220;main.py&#8221;, &#8220;--server.port=9696&#8221;, &#8220;--server.address=0.0.0.0&#8221;]</code></pre><h4>compose.yaml</h4><p>This one I used for local development and testing.</p><h4>Talking Containers</h4><p>Let&#8217;s take a quick look at the frontend code to see how, once we deploy the infrastructure, we are able to connect to the backend container via the private network and not the public internet.</p><pre><code>import streamlit as st
import requests

st.title(&#8221;Container Demo UI&#8221;)
st.text(&#8221;This text will change if the backend is called successfully.&#8221;)

API_URL = &#8220;http://<strong>app-backend-dev</strong>&#8221;

def call_hi_get():
    try:
        response = requests.get(f&#8221;{API_URL}/hi&#8221;)
        if response.status_code == 200:
            st.text(response.json().get(&#8221;message&#8221;, &#8220;No message in response&#8221;))
    except requests.RequestException as e:
        st.text(f&#8221;Error reaching backend: {e}&#8221;)
    
if st.button(&#8221;Call /hi endpoint!&#8221;):
    response = call_hi_get()</code></pre><p>Here we&#8217;re using the <strong>app name</strong>, which in this case is <strong>app-backend-dev. </strong>We&#8217;re not using any FQDNs or FQDNs with internal in their url (e.g. https://app-frontend-dev.<strong>internal</strong>.alabala-xxx.eastus.azurecontainerapps.io) - I&#8217;ve seen this approach presented by folks, but I really, really hate it, because it is ugly and it requires you to figure out the actual FQDN first. </p><p>This approach is much cleaner - it relies on the service discovery system of ACA, which is much cleaner solution. We don&#8217;t have to care what the FQDN is.</p><h4>Azure</h4><p>Here&#8217;s our infrastructure in Azure.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JRrR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JRrR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png 424w, https://substackcdn.com/image/fetch/$s_!JRrR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png 848w, https://substackcdn.com/image/fetch/$s_!JRrR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png 1272w, https://substackcdn.com/image/fetch/$s_!JRrR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JRrR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png" width="254" height="242" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:242,&quot;width&quot;:254,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:9823,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/174315096?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!JRrR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png 424w, https://substackcdn.com/image/fetch/$s_!JRrR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png 848w, https://substackcdn.com/image/fetch/$s_!JRrR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png 1272w, https://substackcdn.com/image/fetch/$s_!JRrR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If you want to learn more, then clone the repo, take a look and try it on your own.</p><p>Thanks.</p>]]></content:encoded></item><item><title><![CDATA[Build Remote MCP Server with Azure Container Apps]]></title><description><![CDATA[Learn how to build a simple remote MCP server with Microsoft Azure Container Apps and test it with Claude Desktop]]></description><link>https://systemshogun.com/p/build-remote-mcp-server-with-azure</link><guid isPermaLink="false">https://systemshogun.com/p/build-remote-mcp-server-with-azure</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Mon, 15 Sep 2025 11:11:43 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!6jT6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today we&#8217;re going to take a quick look at how we can build a simple <em>remote </em>MCP server using Python, deploy it to <a href="https://azure.microsoft.com/en-us/products/container-apps">Azure Container Apps</a> and integrate it with <a href="https://claude.ai/download">Claude Desktop</a>. All of the examples I&#8217;ve seen online are for <em>local </em>MCP server using stdio, which is fine for a demo, but in reality, you&#8217;re most likely going to use remote MCP servers. That&#8217;s why I decided to dig into it and write a quick blog post about it.</p><h3>What is MCP?</h3><p>MCP stands for <a href="https://modelcontextprotocol.io/docs/getting-started/intro">Model Context Protocol</a>, and it is an open-source standard for connecting your AI applications to external systems. </p><p>Those systems can live on your own machine, like your filesystem, or be deployed thousands of kilometers away in some distant server, like the <a href="https://learn.microsoft.com/en-us/azure/developer/azure-mcp-server/">Azure MCP server</a>.</p><p>In a nutshell, MCP allows you to easily integrate your AI application with other sources, like data stores, APIs, and etc.,</p><h3>How MCP works?</h3><p>MCP uses the client-server architecture that we are all familiar with. There are three main actors involved in the whole process:</p><ol><li><p><strong>MCP Host</strong></p><p><strong>An AI application, powered by language model that supports MCP.</strong> Examples of such applications are Claude Desktop, Claude Code, GitHub Copilot.</p><p>Keep in mind that for this to work <strong>the language model needs to support tool calling</strong> and not every model supports this. Also, I&#8217;m saying language model and not large language model (LLM), because nothing stops a small language model (SLM) from being able to use MCP, if the model supports it.</p></li><li><p><strong>MCP Client</strong></p><p>An <strong>MCP Host</strong> creates a client, which then connects to an <strong>MCP Server</strong>. Each MCP Server has its own dedicated client.</p></li><li><p><strong>MCP Server</strong></p><p>This is what provides the extended functionalities to your AI application. This is basically what we called &#8220;external system&#8221; earlier. <strong>Think of it as an API that your AI-powered application can call.</strong> And like we said, it can live on the same machine as your AI application or anywhere on the internet.</p></li></ol><p>The following is a basic diagram of the whole process.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6jT6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6jT6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png 424w, https://substackcdn.com/image/fetch/$s_!6jT6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png 848w, https://substackcdn.com/image/fetch/$s_!6jT6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png 1272w, https://substackcdn.com/image/fetch/$s_!6jT6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6jT6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png" width="403" height="341" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e700dc39-3402-4b03-9f08-4dd765573136_403x341.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:341,&quot;width&quot;:403,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:7525,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6jT6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png 424w, https://substackcdn.com/image/fetch/$s_!6jT6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png 848w, https://substackcdn.com/image/fetch/$s_!6jT6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png 1272w, https://substackcdn.com/image/fetch/$s_!6jT6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>One thing to note is the <strong>two-way connection</strong> between the <strong>MCP Client</strong> and the <strong>MCP Server</strong>. That&#8217;s because the protocol allows for both the client AND the server to send messages to each other.</p><h3>Features of MCP</h3><p>So, what does MCP gives us? There are a few options available.</p><ol><li><p><strong>Tools</strong></p><p>These are functions provided by the <strong>MCP server</strong> that an LLM can call. You can think of them like API endpoints if you want. These functions could be calling a database, another API or executing any kind of business logic or integration. Tools are without question the most powerful feature of MCP.</p></li><li><p><strong>Resources</strong></p><p>Read-only data sources that supply contextual information, such as file contents, database schemas, or API documentation.</p></li><li><p><strong>Prompts</strong></p><p>Pre-built templates for guiding the model in using specific tools and resources.</p></li></ol><p>These are all features that the <strong>MCP Server</strong> provides. There are also features that the MCP Client provides and that&#8217;s why the connection above is bidirectional. However, these are not that interesting for our understanding of MCP and for this demo, so if you want to learn more, <a href="https://modelcontextprotocol.io/docs/getting-started/intro">please read the docs</a>, which are really good.</p><p>Now, let&#8217;s build our remote MCP server.</p><p><strong>requirements.txt</strong></p><pre><code>fastmcp</code></pre><p>We&#8217;re going to be using <a href="https://gofastmcp.com/getting-started/welcome">FastMCP</a> to build our server.</p><p><strong>main.py</strong></p><pre><code>from fastmcp import FastMCP

mcp = FastMCP("Demo MCP")

@mcp.tool
def call_the_ceo() -&gt; str:
    return "Calling the CEO... DONE!"

@mcp.tool
def send_email(to: str, subject: str, body: str) -&gt; str:
    return f"Email sent to {to} with subject '{subject}' and body '{body}'"

@mcp.tool
def weather_info(location: str) -&gt; str:
    return f"The weather in {location} is sunny with a high of 999&#176;F."

if __name__ == "__main__":
    mcp.run()</code></pre><p>We defined 3 tools. One that calls the CEO, another one that sends email and third one for weather info.</p><p><strong>Dockerfile</strong></p><pre><code># Use official Python image
FROM python:3.12-slim

# Set working directory
WORKDIR /app

# Copy requirements and source code
COPY requirements.txt ./
COPY main.py ./

# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Expose port
EXPOSE 8000

# Set entrypoint
CMD ["fastmcp", "run", "./main.py:mcp", "--transport", "http", "--host", "0.0.0.0", "--port", "8000"]</code></pre><p>Since we&#8217;re going to use containers, we need Dockerfile. We&#8217;re first going to run this locally using Docker Desktop and test it with a tool called <a href="https://modelcontextprotocol.io/legacy/tools/inspector">MCP Inspector</a>. Then we will push this to <a href="https://azure.microsoft.com/en-us/products/container-registry">Azure Container Registry</a> and then run it on Container Apps.</p><p>Make sure you install the packages inside virtual environment and then run it. </p><pre><code>uv venv
uv pip install -r .\requirements.txt
.venv\Scripts\activate
</code></pre><p>Now, let&#8217;s build our image.</p><pre><code>docker build -t mcp-server-demo .</code></pre><p>Run the image</p><pre><code>docker run --name mcp-server-demo --rm -p 8000:8000 `
   -e HOST=0.0.0.0 -e PORT=8000 `
   mcp-server-demo</code></pre><p>Now, let&#8217;s run the MCP Inspector. Inside the project folder, run the following.</p><pre><code>npx @modelcontextprotocol/inspector fastmcp run .\main.py</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hbRo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hbRo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png 424w, https://substackcdn.com/image/fetch/$s_!hbRo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png 848w, https://substackcdn.com/image/fetch/$s_!hbRo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png 1272w, https://substackcdn.com/image/fetch/$s_!hbRo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hbRo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png" width="855" height="529" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:529,&quot;width&quot;:855,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:31550,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hbRo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png 424w, https://substackcdn.com/image/fetch/$s_!hbRo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png 848w, https://substackcdn.com/image/fetch/$s_!hbRo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png 1272w, https://substackcdn.com/image/fetch/$s_!hbRo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Click <strong>Connect</strong> and if all done correctly, you should see the following.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fago!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fago!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png 424w, https://substackcdn.com/image/fetch/$s_!fago!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png 848w, https://substackcdn.com/image/fetch/$s_!fago!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png 1272w, https://substackcdn.com/image/fetch/$s_!fago!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fago!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png" width="1456" height="759" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:759,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:73844,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fago!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png 424w, https://substackcdn.com/image/fetch/$s_!fago!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png 848w, https://substackcdn.com/image/fetch/$s_!fago!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png 1272w, https://substackcdn.com/image/fetch/$s_!fago!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Click on <strong>Tools</strong> and you should see all the tools that we have exposed. You can test each one of them if you want by providing the required parameters and clicking on <strong>Run Tool</strong>.</p><p>That&#8217;s great, we&#8217;ve tested the MCP server, and we saw it working <em>locally</em>. Now, let&#8217;s deploy this to Container Apps and use it <em>remotely</em>.</p><h3><strong>Deploy to Container Apps (Steps):</strong></h3><ol><li><p>Create resource group in Azure</p></li><li><p>Create Azure Container Registry</p></li><li><p>Log into your container registry using <a href="https://learn.microsoft.com/en-us/cli/azure/?view=azure-cli-latest">azure cli</a></p><pre><code>az acr login --name <strong>[YOUR AZURE CONTAINER REGISTRY NAME]</strong></code></pre></li><li><p>Tag your container image</p><pre><code>docker tag mcp-server-demo <strong>[YOUR CONTAINER REGISTRY NAME]</strong>.azurecr.io/mcp-server-demo:latest</code></pre></li><li><p>Push to Azure Container Registry</p><pre><code>docker push <strong>[YOUR CONTAINER REGISTRY NAME]</strong>.azurecr.io/mcp-server-demo:latest</code></pre></li><li><p>Create Container App and in the set-up steps add the image you just pushed. Also, don&#8217;t forget to enable external traffic (<strong>Ingress</strong> option).</p></li><li><p>Once deployed, copy your container app&#8217;s URL</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eZ85!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eZ85!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png 424w, https://substackcdn.com/image/fetch/$s_!eZ85!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png 848w, https://substackcdn.com/image/fetch/$s_!eZ85!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png 1272w, https://substackcdn.com/image/fetch/$s_!eZ85!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eZ85!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png" width="1456" height="367" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:367,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:79452,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!eZ85!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png 424w, https://substackcdn.com/image/fetch/$s_!eZ85!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png 848w, https://substackcdn.com/image/fetch/$s_!eZ85!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png 1272w, https://substackcdn.com/image/fetch/$s_!eZ85!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p></li><li><p>Open Claude Desktop (assuming you have it installed)</p></li><li><p>On the left screen at the bottom click on your user and then to <strong>Settings</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!txKY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!txKY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png 424w, https://substackcdn.com/image/fetch/$s_!txKY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png 848w, https://substackcdn.com/image/fetch/$s_!txKY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png 1272w, https://substackcdn.com/image/fetch/$s_!txKY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!txKY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png" width="271" height="329" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:329,&quot;width&quot;:271,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:15365,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!txKY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png 424w, https://substackcdn.com/image/fetch/$s_!txKY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png 848w, https://substackcdn.com/image/fetch/$s_!txKY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png 1272w, https://substackcdn.com/image/fetch/$s_!txKY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p></li><li><p>Click on <strong>Developer </strong>and then on <strong>Edit Config</strong></p></li><li><p>Open <strong>claude_desktop_config.json </strong>if it didn&#8217;t open automatically</p></li><li><p>Add the following snipper and replace <strong>[YOUR CONTAINER APP URL] </strong>with your actual container app url you copied earlier.</p><pre><code>{
  "mcpServers": {
    "custom-mcp": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "[YOUR CONTAINER APP URL]"
      ]
    }
  }
}
</code></pre></li><li><p>Install custom-mcp package (the one we used above)</p><pre><code>npm i -g mcp-remote</code></pre></li><li><p>Open Claude Desktop</p></li></ol><p>Now, if you&#8217;ve followed along (and if I didn&#8217;t miss any step) you shouldn&#8217;t see any errors. Also, you should see the remote MCP server by clicking on the bar icon. Make sure it is enabled.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cJge!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cJge!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png 424w, https://substackcdn.com/image/fetch/$s_!cJge!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png 848w, https://substackcdn.com/image/fetch/$s_!cJge!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png 1272w, https://substackcdn.com/image/fetch/$s_!cJge!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cJge!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png" width="866" height="625" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:625,&quot;width&quot;:866,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55953,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!cJge!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png 424w, https://substackcdn.com/image/fetch/$s_!cJge!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png 848w, https://substackcdn.com/image/fetch/$s_!cJge!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png 1272w, https://substackcdn.com/image/fetch/$s_!cJge!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Now, let&#8217;s test this with the following prompt.</p><pre><code>call the ceo</code></pre><p>If everything was done correctly, you should see something similar to the following.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XZqx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XZqx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png 424w, https://substackcdn.com/image/fetch/$s_!XZqx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png 848w, https://substackcdn.com/image/fetch/$s_!XZqx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png 1272w, https://substackcdn.com/image/fetch/$s_!XZqx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XZqx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png" width="523" height="397" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:397,&quot;width&quot;:523,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:37847,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XZqx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png 424w, https://substackcdn.com/image/fetch/$s_!XZqx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png 848w, https://substackcdn.com/image/fetch/$s_!XZqx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png 1272w, https://substackcdn.com/image/fetch/$s_!XZqx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Choose either <strong>Allow always</strong> as we trust our own MCP server.</p><p>Here&#8217;s the result:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!e4vp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!e4vp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png 424w, https://substackcdn.com/image/fetch/$s_!e4vp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png 848w, https://substackcdn.com/image/fetch/$s_!e4vp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png 1272w, https://substackcdn.com/image/fetch/$s_!e4vp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!e4vp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png" width="745" height="236" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:236,&quot;width&quot;:745,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:27646,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!e4vp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png 424w, https://substackcdn.com/image/fetch/$s_!e4vp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png 848w, https://substackcdn.com/image/fetch/$s_!e4vp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png 1272w, https://substackcdn.com/image/fetch/$s_!e4vp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>As you can see this actually worked. Now, let&#8217;s ask about the weather.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FAo_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FAo_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png 424w, https://substackcdn.com/image/fetch/$s_!FAo_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png 848w, https://substackcdn.com/image/fetch/$s_!FAo_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png 1272w, https://substackcdn.com/image/fetch/$s_!FAo_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FAo_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png" width="731" height="284" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:284,&quot;width&quot;:731,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:50237,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FAo_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png 424w, https://substackcdn.com/image/fetch/$s_!FAo_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png 848w, https://substackcdn.com/image/fetch/$s_!FAo_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png 1272w, https://substackcdn.com/image/fetch/$s_!FAo_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We can see that it says it&#8217;s 999F, which is exactly what we put in our code above. It works!</p><p>Now, let&#8217;s send an email, but let&#8217;s see how the LLM can provide a better user experience on its own, without us explicitly programming it to do so. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Xeui!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Xeui!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png 424w, https://substackcdn.com/image/fetch/$s_!Xeui!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png 848w, https://substackcdn.com/image/fetch/$s_!Xeui!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png 1272w, https://substackcdn.com/image/fetch/$s_!Xeui!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Xeui!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png" width="714" height="249" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9ea90a2e-6879-4491-af55-41950d54518a_714x249.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:249,&quot;width&quot;:714,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:41308,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Xeui!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png 424w, https://substackcdn.com/image/fetch/$s_!Xeui!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png 848w, https://substackcdn.com/image/fetch/$s_!Xeui!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png 1272w, https://substackcdn.com/image/fetch/$s_!Xeui!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here, if I wanted to send email directly, I would&#8217;ve provided the parameters. But instead, I said that I want to send an email and the LLM knows what parameters the tool needs in order for the email to be sent - email address, subject and body. And it asks me for that. Smart, and cool!</p><p>Now, let&#8217;s send the email.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3WNg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3WNg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png 424w, https://substackcdn.com/image/fetch/$s_!3WNg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png 848w, https://substackcdn.com/image/fetch/$s_!3WNg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png 1272w, https://substackcdn.com/image/fetch/$s_!3WNg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3WNg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png" width="733" height="248" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:248,&quot;width&quot;:733,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:43520,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3WNg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png 424w, https://substackcdn.com/image/fetch/$s_!3WNg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png 848w, https://substackcdn.com/image/fetch/$s_!3WNg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png 1272w, https://substackcdn.com/image/fetch/$s_!3WNg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Honestly, I&#8217;m too tired to fix any grammar mistakes at this point and am not going to use AI to do that either. I hope you found this quick demo useful!</p><p>Until next time!</p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Build MCP Server with Claude Desktop and Python]]></title><description><![CDATA[Try MCP by building a server and trying it as quickly as possible using Claude Desktop]]></description><link>https://systemshogun.com/p/build-mcp-server-with-claude-desktop</link><guid isPermaLink="false">https://systemshogun.com/p/build-mcp-server-with-claude-desktop</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Fri, 15 Aug 2025 06:02:03 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/fd3774a7-50b6-40db-a7a3-ba006de58669_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>MCP (<a href="https://modelcontextprotocol.io/docs/getting-started/intro">Model Context Protocol</a>) is gaining a lot of attention lately. It&#8217;s an open-source protocol that enables large language models (LLMs) to call external functions and enrich their context with data from sources like databases, APIs, and file systems.</p><p>Today, I&#8217;ll show you a quick way to experiment with it. For more details, check the official documentation.</p><h3>Prerequisites</h3><p>To follow along, make sure you have the following installed:</p><ul><li><p><a href="https://www.python.org/downloads/">Python</a></p></li><li><p><a href="https://claude.ai/download">Claude Desktop</a></p></li><li><p><a href="https://github.com/astral-sh/uv">uv</a></p></li></ul><h3>Step 1: Set up the project</h3><h4>Windows</h4><pre><code># Create a new directory for our project
uv init fakedb
cd fakedb

# Create virtual environment and activate it
uv venv
.venv\Scripts\activate

# Install dependencies
uv add mcp[cli]

# Create our server file
code fakedb.py</code></pre><h4>macOS</h4><pre><code># Create a new directory for our project
uv init fakedb
cd fakedb

# Create virtual environment and activate it
uv venv
source .venv/bin/activate

# Install dependencies
uv add "mcp[cli]"

# Create our server file
touch fakedb.py</code></pre><h3>Step 2: Build a simple MCP server</h3><p>Let&#8217;s add an endpoint to our MCP server that retrieves user information by user ID.</p><p><code>fakedb.py</code></p><p>This script creates a simple MCP server named <code>fakedb</code> with one tool, <code>get_user_info</code>. The tool takes a user ID, looks it up in a hardcoded dictionary (<code>fake_db</code>), and returns the matching user&#8217;s name and age. You could make this more interesting by integrating a real database and performing an actual query, but for simplicity, we&#8217;re keeping it in-memory here.</p><pre><code>from typing import Any
from mcp.server.fastmcp import FastMCP

# Initialize FastMCP server
mcp = FastMCP("fakedb")

@mcp.tool()
def get_user_info(user_id: str) -&gt; dict[str, Any] | None:
    """Get user information from the database.

    Args:
        user_id: The ID of the user to retrieve.
    """

    # Here we're simulating a real database lookup 
    # (you can try with a real one to make it more interesting)
    fake_db = {
        "1234": {"name": "Alice", "age": 30},
        "5678": {"name": "Bob", "age": 25},
    }
    return fake_db.get(user_id)

if __name__ == "__main__":
    # Initialize and run the server
    mcp.run(transport='stdio')</code></pre><h3>Step 3: Connect to Claude Desktop</h3><h4>Windows</h4><p>Open:</p><pre><code>%APPDATA%\Claude\claude_desktop_config.json</code></pre><h4>macOS</h4><p>Open:</p><pre><code>~/Library/Application Support/Claude/claude_desktop_config.json</code></pre><p>Replace the file contents with:</p><pre><code>{
  "mcpServers": {
    "userDatabase": {
      "command": "uv",
      "args": [
        "--directory",
        "&lt;&lt;FULL PATH TO YOUR PROJECT DIRECTORY&gt;&gt;",
        "run",
        "fakedb.py"
      ]
    }
  }
}</code></pre><p>This configuration creates a new MCP server connection called <strong>userDatabase</strong>. The <code>command</code> and <code>args</code> simply run your MCP server.</p><p>Restart Claude Desktop, and you should see your new MCP server with one endpoint, called <strong>get_user_info</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tby5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tby5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png 424w, https://substackcdn.com/image/fetch/$s_!tby5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png 848w, https://substackcdn.com/image/fetch/$s_!tby5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png 1272w, https://substackcdn.com/image/fetch/$s_!tby5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tby5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png" width="1452" height="1182" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1182,&quot;width&quot;:1452,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:132981,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/170978183?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tby5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png 424w, https://substackcdn.com/image/fetch/$s_!tby5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png 848w, https://substackcdn.com/image/fetch/$s_!tby5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png 1272w, https://substackcdn.com/image/fetch/$s_!tby5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lc6T!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lc6T!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png 424w, https://substackcdn.com/image/fetch/$s_!lc6T!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png 848w, https://substackcdn.com/image/fetch/$s_!lc6T!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png 1272w, https://substackcdn.com/image/fetch/$s_!lc6T!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lc6T!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png" width="1456" height="618" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:618,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:74534,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/170978183?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lc6T!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png 424w, https://substackcdn.com/image/fetch/$s_!lc6T!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png 848w, https://substackcdn.com/image/fetch/$s_!lc6T!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png 1272w, https://substackcdn.com/image/fetch/$s_!lc6T!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Step 4: Test it in Claude</h3><p>Now, if you ask Claude for user information (for example, &#8220;<strong>Give me all you have about user with id 1234 in tabular format</strong>&#8221;), the LLM will:</p><ol><li><p>Detect the <code>get_user_info</code> tool.</p></li><li><p>Call it with the provided <code>user_id</code>.</p></li><li><p>Return the result in a neat, tabular format, exactly how we defined it.</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Hysk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Hysk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png 424w, https://substackcdn.com/image/fetch/$s_!Hysk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png 848w, https://substackcdn.com/image/fetch/$s_!Hysk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png 1272w, https://substackcdn.com/image/fetch/$s_!Hysk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Hysk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png" width="1456" height="743" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:743,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:105551,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/170978183?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Hysk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png 424w, https://substackcdn.com/image/fetch/$s_!Hysk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png 848w, https://substackcdn.com/image/fetch/$s_!Hysk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png 1272w, https://substackcdn.com/image/fetch/$s_!Hysk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>You can see from the image above the <strong>prompt</strong>, <strong>the tool</strong> that was used and <strong>the output</strong>.</p><p>So this is it! A quick and neat way to test what MCP is and how it works without writing lots of complex code.</p><p>Thanks for reading!</p>]]></content:encoded></item><item><title><![CDATA[Using PostgreSQL as a Vector Database for RAG]]></title><description><![CDATA[Did you know you can use PostgreSQL, the mighty relational database, as your vector store too?]]></description><link>https://systemshogun.com/p/using-postgresql-as-a-vector-database</link><guid isPermaLink="false">https://systemshogun.com/p/using-postgresql-as-a-vector-database</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Sat, 09 Aug 2025 05:08:04 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/a4350915-d1bf-4e29-94b4-f390cad97386_1740x1218.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Did you know you can use <a href="https://www.postgresql.org/">PostgreSQL</a>, the mighty relational database, as your vector store too? That means you don't need to rely on two separate products if you want to implement <a href="https://en.wikipedia.org/wiki/Retrieval-augmented_generation">RAG </a>in a solution that already uses this open-source database.</p><p>Here&#8217;s a quick overview of how you can make it happen.</p><p>To keep things simple, I&#8217;ve used <a href="https://console.cloud.timescale.com/">TigerCloud</a> to spin up a PostgreSQL instance in the cloud.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rfL6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rfL6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png 424w, https://substackcdn.com/image/fetch/$s_!rfL6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png 848w, https://substackcdn.com/image/fetch/$s_!rfL6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png 1272w, https://substackcdn.com/image/fetch/$s_!rfL6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rfL6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png" width="1456" height="347" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:347,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:118527,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/170185659?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!rfL6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png 424w, https://substackcdn.com/image/fetch/$s_!rfL6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png 848w, https://substackcdn.com/image/fetch/$s_!rfL6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png 1272w, https://substackcdn.com/image/fetch/$s_!rfL6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p></p><p>Now, back to code.</p><p><code>general.txt</code></p><p>For the purpose of this demo, I&#8217;ve used Python&#8217;s Q&amp;A section in their documentation that you can see here: <a href="https://docs.python.org/3/faq/general.html#id2">General Python FAQ &#8212; Python 3.13.5 documentation</a></p><p>You can either copy the text from that page, or click <a href="https://docs.python.org/3/archives/python-3.13-docs-text.zip">Download</a> to download the whole docs, then go to <code>faq </code>folder and take the <code>general.txt</code> from there and put it in your solution.</p><p><code>requirements.txt</code></p><p>Here are all the packages I needed:</p><pre><code>openai
pandas
numpy
psycopg2-binary
pgvector
dotenv</code></pre><p><code>.env</code></p><p>For the demo I had to get my OpenAI key as well as the connection string from TigerCloud:</p><pre><code>OPENAI_API_KEY=&lt;YOUR OPENAI API KEY HERE&gt;
TIMESCALE_CONNECTION_STRING=&lt;YOUR TIMESCALE CONNECTION STRING HERE&gt;</code></pre><p><code>utils.py</code></p><pre><code>import numpy as np
import openai
from pgvector.psycopg2 import register_vector

# Function to process input with retrieval of most similar documents from the database
def process_input_with_retrieval(user_input, conn):
    delimiter = "```"

    #Step 1: Get documents related to the user input from database
    related_docs = get_top3_similar_docs(get_embeddings(user_input), conn)

    # Step 2: Get completion from OpenAI API
    # Set system message to help set appropriate tone and context for model
    system_message = f"""
    You are a friendly chatbot. \
    You can answer questions about Python, the programming language. \
    You respond in a concise, technically credible tone. Always use ONLY the data from the context and DO NOT PROVIDE any additional information. If you can't find the answer in the context, say "I don't know". Even if you find something that you think is not correct, but if it is in the context - say it.
    """

    # Prepare messages to pass to model
    # We use a delimiter to help the model understand the where the user_input starts and ends
    messages = [
        {"role": "system", "content": system_message},
        {"role": "user", "content": f"{delimiter}{user_input}{delimiter}"},
        {"role": "assistant", "content": f"Relevant Python information: \n {related_docs[0][0]} \n {related_docs[1][0]} {related_docs[2][0]}"}   
    ]

    final_response = get_completion_from_messages(messages)
    return final_response

# Helper function: get text completion from OpenAI API
def get_completion_from_messages(messages, model="gpt-4o", temperature=0, max_tokens=1000):
    openai_client = openai.OpenAI()
    response = openai_client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=temperature, 
        max_tokens=max_tokens, 
    )
    return response.choices[0].message.content

# Helper function: Get top 3 most similar documents from the database
def get_top3_similar_docs(query_embedding, conn):
    embedding_array = np.array(query_embedding)
    # Register pgvector extension
    register_vector(conn)
    cur = conn.cursor()
    # Get the top 3 most similar documents using the KNN &lt;=&gt; operator
    cur.execute("SELECT content FROM embeddings ORDER BY embedding &lt;=&gt; %s LIMIT 3", (embedding_array,))
    top3_docs = cur.fetchall()
    return top3_docs

# Load text file content (duh!)
def load_text_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        return file.read()

# Function to chunk text into smaller pieces
def chunk_text(text, num_chunks):
    """
    Split text into a specified number of chunks.
    
    Args:
        text (str): The text to be chunked
        num_chunks (int): Number of chunks to create
        
    Returns:
        list: List of text chunks
    """
    if num_chunks &lt;= 0:
        raise ValueError("Number of chunks must be greater than 0")
    
    if not text:
        return [""] * num_chunks
    
    text_length = len(text)
    chunk_size = text_length // num_chunks
    remainder = text_length % num_chunks
    
    chunks = []
    start = 0
    
    for i in range(num_chunks):
        # Add one extra character to the first 'remainder' chunks
        current_chunk_size = chunk_size + (1 if i &lt; remainder else 0)
        end = start + current_chunk_size
        chunks.append(text[start:end])
        start = end
    
    return chunks

# Function to get embeddings for a given text using OpenAI API
def get_embeddings(text):
    openai_client = openai.OpenAI()
    response = openai_client.embeddings.create(
        model="text-embedding-3-small",
        input = text.replace("\n"," ")
    )
    return response.data[0].embedding</code></pre><p><code>main.py</code></p><pre><code>from dotenv import load_dotenv, find_dotenv
import os
import openai
import pandas as pd
import numpy as np
import psycopg2
from utils import chunk_text, get_embeddings, load_text_file, process_input_with_retrieval
from psycopg2.extras import execute_values
from pgvector.psycopg2 import register_vector
_ = load_dotenv(find_dotenv()) 
openai.api_key  = os.environ['OPENAI_API_KEY'] 

new_list = []
text = load_text_file('general.txt')

# You can change the number of chunks here based on your needs and the size of the text
chunks = chunk_text(text, 5)

for i in range(len(chunks)):
    embedding = get_embeddings(chunks[i])
    new_list.append([chunks[i], embedding])

df = pd.DataFrame(new_list, columns=['content', 'embedding'])

print(f"Created DataFrame with {len(df)} rows")

connection_string  = os.environ['TIMESCALE_CONNECTION_STRING']

# Connect to PostgreSQL database in Timescale using connection string
conn = psycopg2.connect(connection_string)
cur = conn.cursor()

#install pgvector
cur.execute("CREATE EXTENSION IF NOT EXISTS vector;")
conn.commit()

#install pgvectorscale
cur.execute("CREATE EXTENSION IF NOT EXISTS vectorscale CASCADE;")
conn.commit()

# Create table to store embeddings and metadata
table_create_command = """
-- Remove table embeddings if it exists
DROP TABLE IF EXISTS embeddings;
CREATE TABLE embeddings (
            id bigserial primary key, 
            content text,
            embedding vector(1536)
            );
            """

cur.execute(table_create_command)
cur.close()
conn.commit()


#Batch insert embeddings and metadata from dataframe into PostgreSQL database
register_vector(conn)
cur = conn.cursor()

# Prepare the list of tuples to insert
data_list = [(row['content'], np.array(row['embedding'])) for index, row in df.iterrows()]

# Use execute_values to perform batch insertion
execute_values(cur, "INSERT INTO embeddings (content, embedding) VALUES %s", data_list)

# Commit after we insert all embeddings
conn.commit()

# Create an index on the data for faster retrieval
cur.execute('CREATE INDEX embedding_idx ON embeddings USING diskann (embedding);')
conn.commit()

input = 'Is Python portable?'

response = process_input_with_retrieval(input, conn)
print(input)
print(response)</code></pre><p>Here&#8217;s the outcome</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Z6KJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png 424w, https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png 848w, https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png 1272w, https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png" width="1456" height="78" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:78,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:19830,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/170185659?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png 424w, https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png 848w, https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png 1272w, https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>You can play around with this by changing the input, the data, etc.</p><p>All in all, it works great. And the fact that you are using one of the most widely used relational databases as your vector store for RAG is super convenient.</p><p>Thanks:</p><ul><li><p><a href="https://www.tigerdata.com/blog/postgresql-as-a-vector-database-using-pgvector">PostgreSQL as a Vector Database: A Pgvector Tutorial | TigerData</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Multi-Agent App with Azure AI Foundry Agent Service]]></title><description><![CDATA[Build Multi-Agent app using Azure AI Foundry Agent Service]]></description><link>https://systemshogun.com/p/multi-agent-app-with-azure-ai-foundry</link><guid isPermaLink="false">https://systemshogun.com/p/multi-agent-app-with-azure-ai-foundry</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Mon, 04 Aug 2025 08:08:48 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b2ec290b-194b-49d2-9f9b-60568d2dfb2e_420x300.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Last Sunday, I decided to explore the new <strong><a href="https://learn.microsoft.com/en-us/azure/ai-foundry/agents/overview">Azure AI Foundry Agent Service</a></strong> and built a small multi-agent onboarding app powered behind the scenes by three coordinated agents.</p><p>This blog post is meant to give you a high-level overview of how I approached it, not a full step-by-step tutorial. </p><p>You can watch the video at the end to see the app in action.</p><h3><strong>The Onboarding App</strong></h3><p>I built a simple onboarding web app for, you guessed it, onboarding new employees in a company. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LlaZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LlaZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png 424w, https://substackcdn.com/image/fetch/$s_!LlaZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png 848w, https://substackcdn.com/image/fetch/$s_!LlaZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png 1272w, https://substackcdn.com/image/fetch/$s_!LlaZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LlaZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png" width="1318" height="788" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:788,&quot;width&quot;:1318,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:53800,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/169990941?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LlaZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png 424w, https://substackcdn.com/image/fetch/$s_!LlaZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png 848w, https://substackcdn.com/image/fetch/$s_!LlaZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png 1272w, https://substackcdn.com/image/fetch/$s_!LlaZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The app takes three inputs: the employee&#8217;s full name, email, and department. Behind the scenes, it creates three agents:</p><ul><li><p><code>department_rag_agent</code> uses Azure AI Search to perform a vector-based RAG (see <a href="https://learn.microsoft.com/en-us/azure/search/retrieval-augmented-generation-overview?tabs=docs">RAG and generative AI - Azure AI Search</a>) search and find relevant onboarding information for the specified department (for example, Software Engineering).</p></li><li><p><code>discord_agent</code> is responsible for sending a welcome message on Discord, which includes the employee&#8217;s details and any department-specific information returned by the RAG agent.</p></li><li><p><code>onboarding_agent</code> acts as the orchestrator. It first invokes the RAG agent to gather relevant information, then passes the results to the Discord agent to send the final message.</p></li></ul><p>The entire process is coordinated automatically once the onboarding form is submitted.</p><p>Here&#8217;s the end result message in Discord:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sOlp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sOlp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png 424w, https://substackcdn.com/image/fetch/$s_!sOlp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png 848w, https://substackcdn.com/image/fetch/$s_!sOlp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png 1272w, https://substackcdn.com/image/fetch/$s_!sOlp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sOlp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png" width="1456" height="1382" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1382,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:244619,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/169990941?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sOlp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png 424w, https://substackcdn.com/image/fetch/$s_!sOlp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png 848w, https://substackcdn.com/image/fetch/$s_!sOlp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png 1272w, https://substackcdn.com/image/fetch/$s_!sOlp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Project Structure Overview</h3><p>This project showcases a multi-agent onboarding flow using Azure AI Foundry Agent Service. The main folders are:</p><ul><li><p><code>src/api/</code> &#8211; Contains the FastAPI backend with agent logic, tools, and utilities.</p></li><li><p><code>func-api/</code> &#8211; An Azure Function App that exposes an endpoint for sending Discord messages. This is needed because connected agents do not support local function calls (see <a href="https://learn.microsoft.com/en-us/azure/ai-foundry/agents/how-to/connected-agents?pivots=python">How to use connected agents - Azure AI Foundry | Microsoft Learn</a>).</p></li><li><p><code>web/</code> &#8211; A React.js frontend that serves as the UI for triggering the onboarding flow.</p></li></ul><h3>Environment Setup</h3><ul><li><p><strong>Azure Function App</strong> (for webhook to Discord)</p></li><li><p><strong>Azure AI Search</strong> (for RAG)</p></li><li><p><strong>Azure OpenAI</strong> (for embedding model)</p></li><li><p><strong>Azure AI Foundry</strong> project (for LLM)</p></li></ul><p>To run the app locally or deploy it yourself, you&#8217;ll need a <code>.env</code> file with the following variables:</p><pre><code># Azure AI Foundry
PROJECT_ENDPOINT=   # Your Azure AI Foundry project endpoint
MODEL_DEPLOYMENT_NAME=   # The model deployment name used for your agents (e.g., gpt-4o-mini)

# Azure AI Search
AZURE_SEARCH_ENDPOINT=   # Endpoint of your Azure AI Search resource
AZURE_SEARCH_API_KEY=    # API key for your Azure AI Search instance
AZURE_SEARCH_INDEX_NAME= # Name of the index used for department RAG search</code></pre><p>Also, you need to make sure you connect your Azure AI Search resource with your AI Foundry (see <a href="https://learn.microsoft.com/en-us/azure/ai-foundry/how-to/connections-add?pivots=fdp-project">How to add a new connection in Azure AI Foundry portal - Azure AI Foundry | Microsoft Learn</a>)</p><h3><strong>Final Thoughts</strong></h3><p>This was a fun Sunday experiment that gave me a hands-on introduction to Azure AI Foundry Agent Service (seriously, Microsoft, please give it a shorter and cooler name). With multi-agent coordination, Azure AI Search, and Discord integration via Azure Functions and an OpenAPI spec, I built a simple but practical onboarding automation flow that helped me understand what this framework is currently capable of.</p><p>Keep in mind that this framework is still new and actively evolving, so things may change quickly.</p><p>Here&#8217;s a demo of the app as well as quick walkthrough of the solution</p><div id="youtube2-0YLFdzMoMCo" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;0YLFdzMoMCo&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/0YLFdzMoMCo?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h3>Resources</h3><ul><li><p><a href="https://github.com/kaldren/multiagent-foundry-ai-agents">Github Repo</a></p></li><li><p><a href="https://learn.microsoft.com/en-us/azure/ai-foundry/agents/overview">What is Azure AI Foundry Agent Service?</a></p></li></ul>]]></content:encoded></item></channel></rss>