<?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>DEV Community: Mahmoud Nabil</title>
    <description>The latest articles on DEV Community by Mahmoud Nabil (@emahmoudnabil).</description>
    <link>https://dev.to/emahmoudnabil</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4049816%2Fc004ee48-481f-449f-904e-f281b2154521.jpg</url>
      <title>DEV Community: Mahmoud Nabil</title>
      <link>https://dev.to/emahmoudnabil</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/emahmoudnabil"/>
    <language>en</language>
    <item>
      <title>I built an interactive HTML export for .NET dependency graphs, because my AI agent kept guessing wrong</title>
      <dc:creator>Mahmoud Nabil</dc:creator>
      <pubDate>Mon, 27 Jul 2026 16:10:29 +0000</pubDate>
      <link>https://dev.to/emahmoudnabil/i-built-an-interactive-html-export-for-net-dependency-graphs-because-my-ai-agent-kept-guessing-2nd1</link>
      <guid>https://dev.to/emahmoudnabil/i-built-an-interactive-html-export-for-net-dependency-graphs-because-my-ai-agent-kept-guessing-2nd1</guid>
      <description>&lt;p&gt;Every time I asked Claude Code or Copilot something like &lt;em&gt;"what breaks if I rename this method"&lt;/em&gt; on a mid-sized .NET solution, it would grep through files, miss call sites that go through interfaces, and burn a lot of tokens getting a partial answer. On a solution with a few dozen projects, that's slow and often just wrong: the agent doesn't see the whole picture, so it can't reason about it correctly.&lt;/p&gt;

&lt;p&gt;That's the problem &lt;a href="https://github.com/EMahmoudNabil/slnmap" rel="noopener noreferrer"&gt;Slnmap&lt;/a&gt; is built to solve. It uses Roslyn (the actual C# compiler) to build a semantic graph of your solution, stores it locally in SQLite, and exposes it over MCP so any compatible agent can query it directly instead of guessing from whatever files happen to be open.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I actually use the most: &lt;code&gt;slnmap viz&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Tool calls are great for agents. They're not great for &lt;em&gt;me&lt;/em&gt;, sitting there wondering what an unfamiliar codebase actually looks like before I start asking an agent to change it.&lt;/p&gt;

&lt;p&gt;So Slnmap also ships a &lt;code&gt;viz&lt;/code&gt; command that exports the whole graph as a single self-contained HTML file. No server, no account, no CDN calls. Open it in a browser, pan and zoom, click a symbol, and see everything connected to it collapse into view.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;slnmap viz &lt;span class="nt"&gt;--project&lt;/span&gt; MyProject.csproj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason this matters more than it sounds: it's the one artifact in this whole workflow that isn't just for the agent. You can drop the HTML file in a PR description, or send it to a teammate who doesn't have the repo checked out, and they get the same interactive view you do. Every other Roslyn-based MCP server I looked at while building this outputs data for an agent to consume. None of them give you something to look at.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw649c0laozawp6uobss4.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw649c0laozawp6uobss4.png" alt="slnmap viz interactive HTML graph export showing a .NET solution's dependency structure" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A concrete example
&lt;/h2&gt;

&lt;p&gt;Say you're about to touch &lt;code&gt;IBasketService&lt;/code&gt;. Instead of grepping five files and hoping you found every caller, you ask the agent (or run the query yourself):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What breaks if I change IBasketService?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Slnmap follows both the interface's callers &lt;strong&gt;and&lt;/strong&gt; its concrete implementations, across every project in the solution, in one query. On &lt;a href="https://github.com/dotnet-architecture/eShopOnWeb" rel="noopener noreferrer"&gt;eShopOnWeb&lt;/a&gt; (10 projects), querying an interface with 18 dependents comes back in about &lt;strong&gt;270ms end-to-end over MCP&lt;/strong&gt; (median of 3 runs — full methodology is in &lt;a href="https://github.com/EMahmoudNabil/slnmap/blob/main/BENCHMARKS.md" rel="noopener noreferrer"&gt;BENCHMARKS.md&lt;/a&gt; if you want to check the setup yourself).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the tool surface is small on purpose
&lt;/h2&gt;

&lt;p&gt;If you go looking, there are a few other Roslyn-based MCP servers out there, some with a much larger surface of tools than Slnmap's. I went the other way on purpose. Every tool Slnmap exposes is read-only and narrow — find a symbol, trace its callers, check impact, list implementations — on the bet that an agent (or a person) gets more value from a small set of tools that answer a specific question well than from a large API surface that tries to do everything.&lt;/p&gt;

&lt;p&gt;I'll be honest: I don't know yet whether that bet holds up as codebases and usage scale. It's a real open question, not a marketing line.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it doesn't do
&lt;/h2&gt;

&lt;p&gt;It doesn't call out anywhere. Slnmap reads your source with Roslyn and writes one local SQLite file — nothing else. It's open source now, so that's not just a claim: you can read the code or watch the process yourself and confirm nothing leaves the machine.&lt;/p&gt;

&lt;p&gt;It also doesn't guess. Every answer comes from the compiler's own understanding of your code, not from string matching or heuristics on the source text — which is the same reason it catches call sites that go through an interface instead of missing them like grep does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--global&lt;/span&gt; Slnmap
slnmap analyze path/to/YourSolution.sln
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then point any MCP-compatible client at it (Claude Code, Cursor, etc.) — setup instructions are in the &lt;a href="https://github.com/EMahmoudNabil/slnmap" rel="noopener noreferrer"&gt;README&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/EMahmoudNabil/slnmap" rel="noopener noreferrer"&gt;https://github.com/EMahmoudNabil/slnmap&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;NuGet: &lt;a href="https://www.nuget.org/packages/Slnmap" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/Slnmap&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Site: &lt;a href="https://slnmap.dev" rel="noopener noreferrer"&gt;https://slnmap.dev&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's MIT licensed. If you've hit the "grep vs. semantic index" tradeoff yourself, or tried the narrow-tools-vs-many-tools design in a different direction, I'd genuinely like to hear how you approached it.&lt;/p&gt;

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