<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Sayok Bose</title>
    <description>The latest articles on Forem by Sayok Bose (@sayokbose91).</description>
    <link>https://forem.com/sayokbose91</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3818098%2Ffc3044a7-2b13-45d9-a2d1-812dcdb7730e.jpg</url>
      <title>Forem: Sayok Bose</title>
      <link>https://forem.com/sayokbose91</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/sayokbose91"/>
    <language>en</language>
    <item>
      <title>Debugging Is a Lost Art. Juniors Never Had It. Seniors Traded It. AI Faked It. Cool Story.</title>
      <dc:creator>Sayok Bose</dc:creator>
      <pubDate>Wed, 18 Mar 2026 13:55:14 +0000</pubDate>
      <link>https://forem.com/sayokbose91/debugging-is-a-lost-art-juniors-never-had-it-seniors-traded-it-ai-faked-it-cool-story-c5n</link>
      <guid>https://forem.com/sayokbose91/debugging-is-a-lost-art-juniors-never-had-it-seniors-traded-it-ai-faked-it-cool-story-c5n</guid>
      <description>&lt;p&gt;&lt;em&gt;How AI quietly broke two things on our .NET team at the same time. In opposite directions. And nobody noticed.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Juniors ship fast and cannot debug anything. Seniors are slower to debug than they used to be. The AI writes immaculate code with the error handling of a sleep-deprived intern. And everyone is too busy to care until 2am.&lt;/p&gt;




&lt;h2&gt;
  
  
  2013 vs 2026
&lt;/h2&gt;

&lt;p&gt;In 2013, joining a .NET team meant one thing.&lt;/p&gt;

&lt;p&gt;You got stuck. You stayed stuck. You Googled the same NullReferenceException four times. You finally understood it. You never forgot.&lt;/p&gt;

&lt;p&gt;In 2026, joining a .NET team means opening a chat window.&lt;/p&gt;

&lt;p&gt;Which is faster. Genuinely. No sarcasm. The AI is great.&lt;/p&gt;

&lt;p&gt;But somewhere between then and now, something got lost. And we did not notice until we really needed it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Juniors: They Never Had to Struggle
&lt;/h2&gt;

&lt;p&gt;AI removed the productive suffering.&lt;/p&gt;

&lt;p&gt;Now a junior hits a wall, asks the AI, gets a fix in ten seconds, ships. Great velocity. Zero scar tissue.&lt;/p&gt;

&lt;p&gt;Ask them to trace a value through .NET middleware they did not write. Ask them to explain why an async deadlock only happens under load. Ask them to open the debugger and step through something cold.&lt;/p&gt;

&lt;p&gt;You can see the exact moment the confidence leaves their face.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"I have trained you well. Now debug this StackOverflowException in production without me."&lt;/em&gt;&lt;br&gt;
-- The AI, logging off at exactly the wrong moment&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The muscle never got built. Not their fault. Ours. We handed them a Ferrari before they knew how to drive.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuv56fzruzpu8dzs14nqo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuv56fzruzpu8dzs14nqo.png" alt="Debugging it yourself vs describing the error to AI and hoping" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Seniors: They Traded It In
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before AI:&lt;/strong&gt; Senior hits a weird EF Core bug. Reads the stack trace. Holds the whole call chain in their head. Finds it in twelve minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After eight months of AI:&lt;/strong&gt; Senior pastes the stack trace. AI says check the DbContext lifetime. Senior checks. Fixed in four minutes.&lt;/p&gt;

&lt;p&gt;Still fast. But notice what did not happen.&lt;/p&gt;

&lt;p&gt;The senior did not &lt;em&gt;debug&lt;/em&gt;. The senior &lt;em&gt;supervised&lt;/em&gt; debugging. That is a different skill. And it atrophies quietly.&lt;/p&gt;

&lt;p&gt;The reflex goes. You do not notice it leaving. You just notice one day that reading a raw stack trace feels harder than it used to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Act Three: 2am. Production is down. AI is rate limited.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will leave that one to your imagination.&lt;/p&gt;




&lt;h2&gt;
  
  
  The AI: Crime Scene, No Witnesses
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"An error occurred."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// groundbreaking stuff&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AN. ERROR. OCCURRED.&lt;/p&gt;

&lt;p&gt;Not which order. Not which customer. Not whether the charge went through before it exploded -- which is literally the only thing anyone needs to know at 2am.&lt;/p&gt;

&lt;p&gt;Here is what a human writes on a Friday when they are scared:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaymentGatewayException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsTransient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Do NOT retry blindly -- charge may have gone through. Check idempotency key first.&lt;/span&gt;
    &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Gateway failure for Order {OrderId}. Charged: {WasCharged}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChargeAttempted&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One gets debugged in twenty minutes. The other gets debugged while someone asks if the customer was charged twice.&lt;/p&gt;

&lt;p&gt;Guess which one the AI writes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frnxychansuhvdz5pc6pg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frnxychansuhvdz5pc6pg.png" alt="Wait nobody knows why this catch block exists -- Always has been" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Nobody Read The PR Either
&lt;/h2&gt;

&lt;p&gt;Open diff. Looks like code we would write. Tests pass. LGTM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// perfectly formatted. reviewed in 30 seconds. approved.&lt;/span&gt;
&lt;span class="c1"&gt;// contains a classic EF Core N+1 that will destroy the DB under any real load.&lt;/span&gt;
&lt;span class="c1"&gt;// but sure. LGTM.&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;OrderDto&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Items&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderItems&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderId&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// new query. per order. every time. forever.&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbzj26sdfo1ndculudp26.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbzj26sdfo1ndculudp26.png" alt="Cant have an N plus 1 problem if you never read past the happy path" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Junior shipped it. Senior approved it. AI has no idea what N+1 means emotionally. Database is about to have a very bad day.&lt;/p&gt;




&lt;h2&gt;
  
  
  And Then There Is The Pressure
&lt;/h2&gt;

&lt;p&gt;The sprint does not stop. The tickets do not stop. The stand-up is in twenty minutes.&lt;/p&gt;

&lt;p&gt;So the junior asks the AI because asking a senior feels like interrupting someone who is drowning. The senior approves the PR quickly because they have five more tickets. Nobody writes the why comment because the ticket closes today and tomorrow is already full.&lt;/p&gt;

&lt;p&gt;Nobody is doing anything wrong. Everyone is just doing the only thing the system has time for.&lt;/p&gt;

&lt;p&gt;It is not a skill problem. It is a system problem wearing a skill problem's coat.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Move fast and break things"&lt;/em&gt;&lt;br&gt;
-- Someone who never had to debug the things that got broken&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Only Checklist A Human Reviewer Needs In 2026
&lt;/h2&gt;

&lt;p&gt;The linters catch the N+1. The analyzers catch the missing CancellationToken. Let the tools do the tools job.&lt;/p&gt;

&lt;p&gt;Here is what only a human can check.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; [ ] Does this solve the RIGHT problem, not just the one in the ticket?
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Does this contradict a decision made in another service six months ago?
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Is this shortcut acceptable given what is coming next quarter?
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Will the on-call person understand what went wrong from the logs alone?
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Is this junior developing a habit we should address, not just fix?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five questions. All of them require someone who was in the meeting, knows the history, or has been burned before.&lt;/p&gt;

&lt;p&gt;The actual value of a human reviewer in 2026 is not spotting the N+1.&lt;/p&gt;

&lt;p&gt;It is knowing we tried this exact pattern in 2023 and it took down prod.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pitfalls To Avoid
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Using AI to fix bugs that AI wrote.&lt;/strong&gt; Junior hits a bug in AI code, asks the AI, moves on. The mental model never forms. Same bug, different form, three months later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trusting green tests as proof of correctness.&lt;/strong&gt; AI tests the behaviour it implemented. If it implemented the wrong behaviour, the tests pass. Enthusiastically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Letting the PR description review the code for you.&lt;/strong&gt; AI writes great PR descriptions. So great that reviewers read the description and skim the diff. The description says what it does. The code is where it goes wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;The juniors are fast. The seniors are comfortable. The AI is confident.&lt;/p&gt;

&lt;p&gt;Somewhere in the middle is the production incident nobody has the instincts to fix at speed anymore.&lt;/p&gt;

&lt;p&gt;The lost art is not gone. It just needs a process around it -- not heroism and overtime.&lt;/p&gt;

&lt;p&gt;Start with the two checklists. Ship them this sprint.&lt;/p&gt;

&lt;p&gt;Drop a comment if you have hit this differently. Genuinely asking.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>ai</category>
      <category>codereview</category>
      <category>programming</category>
    </item>
    <item>
      <title>Your Company's APIs Are Collecting Dust. Agents Are Starving. We Played Matchmaker.</title>
      <dc:creator>Sayok Bose</dc:creator>
      <pubDate>Wed, 11 Mar 2026 08:36:15 +0000</pubDate>
      <link>https://forem.com/sayokbose91/your-companys-apis-are-collecting-dust-agents-are-starving-we-played-matchmaker-1275</link>
      <guid>https://forem.com/sayokbose91/your-companys-apis-are-collecting-dust-agents-are-starving-we-played-matchmaker-1275</guid>
      <description>&lt;p&gt;&lt;em&gt;We added one project. Agents got jobs. Nobody checked if they wanted them.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Your company has APIs. AI agents want to use them. The agents have no idea they exist. We wrapped ours in MCP in an afternoon, touched zero existing business logic, and suddenly an agent was doing work that used to take a human four browser tabs and a spreadsheet with too many conditional formats. This is that story.&lt;/p&gt;




&lt;h2&gt;
  
  
  Okay So Picture This
&lt;/h2&gt;

&lt;p&gt;You have spent years building APIs. Real ones. With actual business logic. Carefully written handlers, domain models, the whole clean architecture setup your team is quietly proud of.&lt;/p&gt;

&lt;p&gt;And then AI agents show up.&lt;/p&gt;

&lt;p&gt;They want to automate things. They are very enthusiastic about it. And they cannot find a single one of your endpoints because — and we cannot stress this enough — &lt;strong&gt;agents do not browse Swagger docs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;They need tools. Exposed in a protocol they speak. Without that, your entire API portfolio might as well not exist.&lt;/p&gt;

&lt;p&gt;We found this out the fun way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.memegen.link%2Fimages%2Fwoman-cat%2FLeadership_demanding_AI_on_all_our_APIs%2FOur_APIs_with_no_MCP_layer.png%2520align%3D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.memegen.link%2Fimages%2Fwoman-cat%2FLeadership_demanding_AI_on_all_our_APIs%2FOur_APIs_with_no_MCP_layer.png%2520align%3D" alt="woman-cat meme" width="931" height="600"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  MCP. What Is It. Why Do You Care.
&lt;/h2&gt;

&lt;p&gt;Model Context Protocol. Anthropic open-sourced it. Think USB-C for AI tools.&lt;/p&gt;

&lt;p&gt;Before MCP, connecting an AI to your API meant writing a custom wrapper. Every. Single. Time. Different model? New wrapper. New team? New wrapper. It was miserable and everyone pretended it was fine.&lt;/p&gt;

&lt;p&gt;MCP says: one standard. One server. Any agent that speaks the protocol discovers your tools automatically and knows what to call and with what parameters.&lt;/p&gt;

&lt;p&gt;You write a description. The agent reads it. The agent does the thing. No human in the middle explaining the API like it is a new intern's first day.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"You had the power all along." — Glinda the Good Witch, definitely talking to your Application layer&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Part Where Clean Architecture Accidentally Saved Us
&lt;/h2&gt;

&lt;p&gt;So here is the thing about Clean Architecture that nobody puts on the conference slides.&lt;/p&gt;

&lt;p&gt;The REST API? It is just a delivery mechanism. It sits on the outside. It is not the brain. It is the postman. You could swap it out and the business logic would not notice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      Domain
        ↑
    Application       ← the actual brain
     ↑       ↑
   REST     MCP       ← both just taking orders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We added the MCP server at the same level as the REST API. Both of them talk to the same existing handlers. Neither knows the other exists. The entire business logic stayed completely untouched.&lt;/p&gt;

&lt;p&gt;We were smug about this for the rest of the day. Rightly so.&lt;/p&gt;




&lt;h2&gt;
  
  
  And Vertical Slicing Made Each Feature a Free Tool
&lt;/h2&gt;

&lt;p&gt;Each feature in our codebase is already a self-contained slice. Handler, request, response — all together, all neat.&lt;/p&gt;

&lt;p&gt;The REST controller calls it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GetProductDetails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;GetProductDetails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;ProductId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP tool calls the exact same handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;McpServerTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Gets product details including price and stock level."&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GetProductDetails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetProductDetails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The product ID"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GetProductDetails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;GetProductDetails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;ProductId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same handler. Same response. Three new lines of code.&lt;/p&gt;

&lt;p&gt;The feature slice was already a unit. We just gave it a new front door.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.memegen.link%2Fimages%2Fkhaby-lame%2FBuild_custom_AI_wrappers_for_every_single_API%2FJust_add_one_MCP_project.png%2520align%3D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.memegen.link%2Fimages%2Fkhaby-lame%2FBuild_custom_AI_wrappers_for_every_single_API%2FJust_add_one_MCP_project.png%2520align%3D" alt="khaby-lame meme" width="601" height="600"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Whole Server In One Screenshot Worth Of Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// your entire app, already wired&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMcpServer&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;WithHttpTransport&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;WithToolsFromAssembly&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapMcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;AddApplication&lt;/code&gt; — same call your REST API makes. Every handler, every repo, every service. All of it just... there.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;WithToolsFromAssembly&lt;/code&gt; — scans for &lt;code&gt;[McpServerTool]&lt;/code&gt; methods, reads the descriptions, builds the schemas. You write a description. It does the rest. It is almost annoying how easy it is.&lt;/p&gt;

&lt;p&gt;We ended up with 10 tools. All existing logic. The whole class is 126 lines and most of it is whitespace and closing braces.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Every Enterprise Should Be Mildly Panicking Right Now
&lt;/h2&gt;

&lt;p&gt;Most big companies have not ten APIs but fifty. Microservices, internal data tools, report generators, ETL jobs with REST wrappers, legacy systems being held together by prayer and a Java 8 runtime.&lt;/p&gt;

&lt;p&gt;All of it. Invisible to agents.&lt;/p&gt;

&lt;p&gt;The MCP layer pattern means none of that investment gets binned. One project. Write some descriptions. Your entire API surface becomes agent-accessible. An agent can now chain your tools, pull data, run models, and summarise findings — without a human opening four apps and doing the copy-paste shuffle.&lt;/p&gt;

&lt;p&gt;The companies that win here are not rebuilding from scratch. They are the ones who look at what they already have and ask "what would it take to put MCP in front of this?"&lt;/p&gt;

&lt;p&gt;Turns out the answer is: an afternoon.&lt;/p&gt;




&lt;h2&gt;
  
  
  MCPJam: Swagger UI But Make It MCP
&lt;/h2&gt;

&lt;p&gt;Before wiring up a real agent we needed to poke at the tools. Enter MCPJam.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 127.0.0.1:6274:6274 mcpjam/mcp-inspector
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;localhost:6274&lt;/code&gt;. Connect to your server. Click a tool. Fill in params. See the response. That is genuinely all there is to it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffiles.catbox.moe%2F1qqh2v.png%2520align%3D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffiles.catbox.moe%2F1qqh2v.png%2520align%3D" alt="MCPJam showing the tool list for our MCP server" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It finds your broken tools immediately. Much better than finding them two hours into an agent debugging session at 6pm.&lt;/p&gt;




&lt;h2&gt;
  
  
  Things That Did Not Work Immediately (Quick Version)
&lt;/h2&gt;

&lt;p&gt;NuGet returned 401 because our private feed had never heard of the MCP packages. One &lt;code&gt;nuget.config&lt;/code&gt; with packageSourceMapping. Ten minutes. Done.&lt;/p&gt;

&lt;p&gt;MCP Inspector kept throwing 400s with Streamable HTTP. We spent 45 minutes on this. Forty. Five. Minutes. Switched to SSE and it worked in 30 seconds. We do not talk about those 45 minutes.&lt;/p&gt;

&lt;p&gt;Docker Desktop had claimed port 6274 before MCPJam could. Docker was blocking the thing that runs in Docker. The irony was not appreciated.&lt;/p&gt;

&lt;p&gt;None of these were MCP problems. All entirely self-inflicted.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bit That Will Save You At 11pm
&lt;/h2&gt;

&lt;p&gt;Our API has environment config files for each deployment. We linked them into the MCP project via MSBuild instead of copying them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Content&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"..\MyApi\appsettings.staging.json"&lt;/span&gt;
         &lt;span class="na"&gt;Link=&lt;/span&gt;&lt;span class="s"&gt;"appsettings.staging.json"&lt;/span&gt;
         &lt;span class="na"&gt;CopyToOutputDirectory=&lt;/span&gt;&lt;span class="s"&gt;"PreserveNewest"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One source of truth. Right config picked up automatically. Zero "why is MCP talking to prod while we are on staging" incidents. You are welcome.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons (The Ones We Actually Learned, Not The Ones That Sound Good)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Good architecture pays off in ways you did not plan for. Adding a new delivery mechanism to a clean codebase is an afternoon. Adding it to a mess is a project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The MCP layer has zero business logic. Zero. If you put an &lt;code&gt;if&lt;/code&gt; statement in a tool method we will find out.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tool descriptions are load-bearing. An agent with a vague description will call the wrong tool with complete confidence and a smile.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MCPJam first. Always. No exceptions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sort nuget.config before you need it. NuGet 401 on a Friday is a personality-altering experience.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Part 2 Is Coming. And It Is Not Pretty.
&lt;/h2&gt;

&lt;p&gt;We made our APIs agent-ready. The agents showed up.&lt;/p&gt;

&lt;p&gt;We were not ready.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.memegen.link%2Fimages%2Ffine%2FOur_API_after_10_agents_discovered_it%2FThis_is_fine.png%2520align%3D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.memegen.link%2Fimages%2Ffine%2FOur_API_after_10_agents_discovered_it%2FThis_is_fine.png%2520align%3D" alt="fine meme" width="617" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Turns out there is a difference between "an agent &lt;em&gt;can&lt;/em&gt; call your API" and "your API &lt;em&gt;survives&lt;/em&gt; an agent calling it." Rate limits. Retries that fire four times in parallel. Costs that make your finance team ask questions you do not want to answer. And logs that tell you absolutely nothing useful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next up: We Let Agents Loose on Our APIs. Here's What Broke First.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Stay tuned. It gets worse before it gets better.&lt;/p&gt;




&lt;p&gt;Building on existing APIs and trying to figure out how to make them agent-ready without a six-month rewrite? This is the answer. Drop a comment. Tell us what broke differently for you. We genuinely want to know.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>dotnet</category>
      <category>ai</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
